diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -155,8 +155,10 @@ }; struct InputSectionDescription : BaseCommand { - InputSectionDescription(StringRef filePattern) - : BaseCommand(InputSectionKind), filePat(filePattern) {} + InputSectionDescription(StringRef filePattern, uint64_t withFlags = 0, + uint64_t withoutFlags = 0) + : BaseCommand(InputSectionKind), filePat(filePattern), + withFlags(withFlags), withoutFlags(withoutFlags) {} static bool classof(const BaseCommand *c) { return c->kind == InputSectionKind; @@ -180,6 +182,10 @@ // they were created in. This is used to insert newly created ThunkSections // into Sections at the end of a createThunks() pass. std::vector> thunkSections; + + // SectionPatterns can be filtered with the INPUT_SECTION_FLAGS command. + uint64_t withFlags; + uint64_t withoutFlags; }; // Represents BYTE(), SHORT(), LONG(), or QUAD(). diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -335,7 +335,9 @@ for (InputSectionDescription *id : keptSections) if (id->filePat.match(filename)) for (SectionPattern &p : id->sectionPatterns) - if (p.sectionPat.match(s->name)) + if (p.sectionPat.match(s->name) && + (s->flags & id->withFlags) == id->withFlags && + (s->flags & id->withoutFlags) == 0) return true; return false; } @@ -431,7 +433,10 @@ continue; std::string filename = getFilename(sec->file); - if (!cmd->filePat.match(filename) || pat.excludedFilePat.match(filename)) + if (!cmd->filePat.match(filename) || + pat.excludedFilePat.match(filename) || + (sec->flags & cmd->withFlags) != cmd->withFlags || + (sec->flags & cmd->withoutFlags) != 0) continue; ret.push_back(sec); diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -30,6 +30,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +#include "llvm/Support/ScopedPrinter.h" #include #include #include @@ -91,10 +92,13 @@ OutputSection *readOutputSectionDescription(StringRef outSec); std::vector readOverlay(); std::vector readOutputSectionPhdrs(); + std::pair readInputSectionFlags(); InputSectionDescription *readInputSectionDescription(StringRef tok); StringMatcher readFilePatterns(); std::vector readInputSectionsList(); - InputSectionDescription *readInputSectionRules(StringRef filePattern); + InputSectionDescription *readInputSectionRules(StringRef filePattern, + uint64_t withFlags, + uint64_t withoutFlags); unsigned readPhdrType(); SortSectionPolicy readSortKind(); SymbolAssignment *readProvideHidden(bool provide, bool hidden); @@ -657,8 +661,10 @@ // // is parsed by readInputSectionsList(). InputSectionDescription * -ScriptParser::readInputSectionRules(StringRef filePattern) { - auto *cmd = make(filePattern); +ScriptParser::readInputSectionRules(StringRef filePattern, uint64_t withFlags, + uint64_t withoutFlags) { + auto *cmd = + make(filePattern, withFlags, withoutFlags); expect("("); while (!errorCount() && !consume(")")) { @@ -694,15 +700,23 @@ ScriptParser::readInputSectionDescription(StringRef tok) { // Input section wildcard can be surrounded by KEEP. // https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep + uint64_t withFlags = 0; + uint64_t withoutFlags = 0; if (tok == "KEEP") { expect("("); - StringRef filePattern = next(); - InputSectionDescription *cmd = readInputSectionRules(filePattern); + if (consume("INPUT_SECTION_FLAGS")) + std::tie(withFlags, withoutFlags) = readInputSectionFlags(); + InputSectionDescription *cmd = + readInputSectionRules(next(), withFlags, withoutFlags); expect(")"); script->keptSections.push_back(cmd); return cmd; } - return readInputSectionRules(tok); + if (tok == "INPUT_SECTION_FLAGS") { + std::tie(withFlags, withoutFlags) = readInputSectionFlags(); + tok = next(); + } + return readInputSectionRules(tok, withFlags, withoutFlags); } void ScriptParser::readSort() { @@ -781,9 +795,14 @@ script->createOutputSection(next(), getCurrentLocation()); cmd->inOverlay = true; expect("{"); - while (!errorCount() && !consume("}")) - cmd->sectionCommands.push_back(readInputSectionRules(next())); - cmd->phdrs = readOutputSectionPhdrs(); + while (!errorCount() && !consume("}")) { + uint64_t withFlags = 0; + uint64_t withoutFlags = 0; + if (consume("INPUT_SECTION_FLAGS")) + std::tie(withFlags, withoutFlags) = readInputSectionFlags(); + cmd->sectionCommands.push_back( + readInputSectionRules(next(), withFlags, withoutFlags)); + } return cmd; } @@ -841,6 +860,9 @@ // We have a file name and no input sections description. It is not a // commonly used syntax, but still acceptable. In that case, all sections // from the file will be included. + // FIXME: GNU ld permits INPUT_SECTION_FLAGS to be used here. We do not + // handle this case here as it will already have been matched by the + // case above. auto *isd = make(tok); isd->sectionPatterns.push_back({{}, StringMatcher({"*"})}); cmd->sectionCommands.push_back(isd); @@ -1102,6 +1124,63 @@ return make(e, size, commandString); } +static llvm::Optional parseFlag(StringRef tok) { + if (llvm::Optional asInt = parseInt(tok)) + return asInt; +#define CASE_ENT(enum) #enum, ELF::enum + return StringSwitch>(tok) + .Case(CASE_ENT(SHF_WRITE)) + .Case(CASE_ENT(SHF_ALLOC)) + .Case(CASE_ENT(SHF_EXECINSTR)) + .Case(CASE_ENT(SHF_MERGE)) + .Case(CASE_ENT(SHF_STRINGS)) + .Case(CASE_ENT(SHF_INFO_LINK)) + .Case(CASE_ENT(SHF_LINK_ORDER)) + .Case(CASE_ENT(SHF_OS_NONCONFORMING)) + .Case(CASE_ENT(SHF_GROUP)) + .Case(CASE_ENT(SHF_TLS)) + .Case(CASE_ENT(SHF_COMPRESSED)) + .Case(CASE_ENT(SHF_EXCLUDE)) + .Case(CASE_ENT(SHF_ARM_PURECODE)) + .Default(None); +#undef CASE_ENT +} + +// Reads the '(' ')' list of section flags in +// INPUT_SECTION_FLAGS '(' ')' in the +// following form: +// ::= +// | & flag +// ::= Recognized Flag Name, or Integer value of flag. +// If the first character of is a ! then this means without flag, +// otherwise with flag. +// Example: SHF_EXECINSTR & !SHF_WRITE means with flag SHF_EXECINSTR and +// without flag SHF_WRITE. +std::pair ScriptParser::readInputSectionFlags() { + uint64_t withFlags = 0; + uint64_t withoutFlags = 0; + expect("("); + while (!errorCount()) { + StringRef tok = unquote(next()); + bool without = tok.consume_front("!"); + if (llvm::Optional flag = parseFlag(tok)) { + if (without) + withoutFlags |= *flag; + else + withFlags |= *flag; + } else { + setError("unrecognised flag: " + tok); + } + if (consume(")")) + break; + if (!consume("&")) { + next(); + setError("expected & or )"); + } + } + return std::make_pair(withFlags, withoutFlags); +} + StringRef ScriptParser::readParenLiteral() { expect("("); bool orig = inExpr; diff --git a/lld/test/ELF/input-section-flags-diag1.test b/lld/test/ELF/input-section-flags-diag1.test new file mode 100644 --- /dev/null +++ b/lld/test/ELF/input-section-flags-diag1.test @@ -0,0 +1,13 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64 /dev/null -o %t.o +# RUN: not ld.lld -shared %t.o -o /dev/null --script %s 2>&1 | FileCheck -strict-whitespace %s + +## Check that the section flag is recognized. + +SECTIONS { + .text : { INPUT_SECTION_FLAGS(UNKNOWN_FLAG) *(.text) } +} + +# CHECK: unrecognised flag: UNKNOWN_FLAG +# CHECK-NEXT: >>> .text : { INPUT_SECTION_FLAGS(UNKNOWN_FLAG) *(.text) } +# CHECK-NEXT: >>> ^ diff --git a/lld/test/ELF/input-section-flags-diag2.test b/lld/test/ELF/input-section-flags-diag2.test new file mode 100644 --- /dev/null +++ b/lld/test/ELF/input-section-flags-diag2.test @@ -0,0 +1,13 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64 /dev/null -o %t.o +# RUN: not ld.lld -shared %t.o -o /dev/null --script %s 2>&1 | FileCheck -strict-whitespace %s + +## Check that we start with a flag + +SECTIONS { + .text : { INPUT_SECTION_FLAGS(& SHF_ALLOC) *(.text) } +} + +# CHECK: unrecognised flag: & +# CHECK-NEXT: >>> .text : { INPUT_SECTION_FLAGS(& SHF_ALLOC) *(.text) } +# CHECK-NEXT: >>> ^ diff --git a/lld/test/ELF/input-section-flags-diag3.test b/lld/test/ELF/input-section-flags-diag3.test new file mode 100644 --- /dev/null +++ b/lld/test/ELF/input-section-flags-diag3.test @@ -0,0 +1,13 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64 /dev/null -o %t.o +# RUN: not ld.lld -shared %t.o -o /dev/null --script %s 2>&1 | FileCheck -strict-whitespace %s + +## Check that flags are separated by & + +SECTIONS { + .text : { INPUT_SECTION_FLAGS(SHF_ALLOC SHF_EXECINSTR) *(.text) } +} + +// CHECK: expected & or ) +// CHECK-NEXT: >>> .text : { INPUT_SECTION_FLAGS(SHF_ALLOC SHF_EXECINSTR) *(.text) } +// CHECK-NEXT: >>> ^ diff --git a/lld/test/ELF/input-section-flags-keep.s b/lld/test/ELF/input-section-flags-keep.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/input-section-flags-keep.s @@ -0,0 +1,27 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o +# RUN: echo "SECTIONS { \ +# RUN: . = SIZEOF_HEADERS; \ +# RUN: .keep : { KEEP( INPUT_SECTION_FLAGS(!SHF_WRITE) *(.sec*)) } \ +# RUN: }" > %t.script +# RUN: ld.lld --gc-sections -o %t --script %t.script %t.o +# RUN: llvm-readobj --symbols %t | FileCheck %s + +## Check that INPUT_SECTION_FLAGS can be used within KEEP, and affects what +## is kept. +# CHECK: Name: keep +# CHECK-NOT: NAME: collect +.text +.global _start +_start: + .long 0 + +.section .sec1, "a" +.global keep +keep: + .long 1 + +.section .sec2, "aw" +.global collect +collect: + .long 2 diff --git a/lld/test/ELF/input-section-flags.s b/lld/test/ELF/input-section-flags.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/input-section-flags.s @@ -0,0 +1,115 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o + +## Test the INPUT_SECTION_FLAGS feature. It prefixes an input section list and +## restricts matches to sections that have the required flags and do not have +## any of the must not have flags. + +## Uniquely identify each .sec section by flag alone, with .text going into +## to the SHF_EXECINSTR requiring .outsec2 +# RUN: echo "SECTIONS { \ +# RUN: .outsec1 : { INPUT_SECTION_FLAGS(SHF_ALLOC & !SHF_EXECINSTR & \ +# RUN: !SHF_WRITE & !SHF_MERGE) *(.sec.*) } \ +# RUN: .outsec2 : { INPUT_SECTION_FLAGS(SHF_ALLOC & SHF_EXECINSTR & !SHF_WRITE\ +# RUN: & !SHF_MERGE) *(.sec.* .text) } \ +# RUN: .outsec3 : { INPUT_SECTION_FLAGS(SHF_WRITE) *(.sec.*) } \ +# RUN: .outsec4 : { INPUT_SECTION_FLAGS(SHF_MERGE & !SHF_STRINGS) *(.sec.*) } \ +# RUN: .outsec5 : { INPUT_SECTION_FLAGS(SHF_STRINGS) *(.sec.*) } \ +# RUN: } " > %t.script +# RUN: ld.lld -o %t1 --script %t.script %t.o +# RUN: llvm-readobj --symbols %t1 | FileCheck %s +# CHECK: Name: _start +# CHECK: Section: .outsec2 +# CHECK: Name: s1 +# CHECK: Section: .outsec1 +# CHECK: Name: s2 +# CHECK: Section: .outsec2 +# CHECK: Name: s3 +# CHECK: Section: .outsec3 +# CHECK: Name: s4 +# CHECK: Section: .outsec4 +# CHECK: Name: s5 +# CHECK: Section: .outsec5 + +## Same test but using OVERLAY. +# RUN: echo "SECTIONS { \ +# RUN: OVERLAY 0x1000 : AT ( 0x4000 ) { \ +# RUN: .outsec1 { INPUT_SECTION_FLAGS(SHF_ALLOC & !SHF_EXECINSTR & \ +# RUN: !SHF_WRITE & !SHF_MERGE) *(.sec.*) }\ +# RUN: .outsec2 { INPUT_SECTION_FLAGS(SHF_ALLOC & SHF_EXECINSTR & !SHF_WRITE \ +# RUN: & !SHF_MERGE) *(.sec.* .text) } \ +# RUN: .outsec3 { INPUT_SECTION_FLAGS(SHF_WRITE) *(.sec.*) } \ +# RUN: .outsec4 { INPUT_SECTION_FLAGS(SHF_MERGE & !SHF_STRINGS) *(.sec.*) } \ +# RUN: .outsec5 { INPUT_SECTION_FLAGS(SHF_STRINGS) *(.sec.*) } \ +# RUN: } } " > %t2.script + +# RUN: ld.lld -o %t2 --script %t2.script %t.o +# RUN: llvm-readobj --symbols %t2 | FileCheck %s + +## Same test but using hex representations of the flags. +# RUN: echo "SECTIONS { \ +# RUN: .outsec1 : { INPUT_SECTION_FLAGS(0x2 & !0x4 & !0x1 & !0x10) *(.sec.*) }\ +# RUN: .outsec2 : { INPUT_SECTION_FLAGS(0x2 & 0x4 & !0x1 & !0x10) \ +# RUN: *(.sec.* .text) } \ +# RUN: .outsec3 : { INPUT_SECTION_FLAGS(0x1) *(.sec.*) } \ +# RUN: .outsec4 : { INPUT_SECTION_FLAGS(0x10 & !0x20) *(.sec.*) } \ +# RUN: .outsec5 : { INPUT_SECTION_FLAGS(0x20) *(.sec.*) } \ +# RUN: } " > %t3.script + +# RUN: ld.lld -o %t3 --script %t3.script %t.o +# RUN: llvm-readobj --symbols %t3 | FileCheck %s + +## Check that we can handle multiple InputSectionDescriptions in a single +## OutputSection +# RUN: echo "SECTIONS { \ +# RUN: .outsec1 : { INPUT_SECTION_FLAGS(SHF_ALLOC & !SHF_EXECINSTR & \ +# RUN: !SHF_WRITE & !SHF_MERGE) *(.sec.*) ; \ +# RUN: INPUT_SECTION_FLAGS(SHF_ALLOC & SHF_EXECINSTR & !SHF_WRITE\ +# RUN: & !SHF_MERGE) *(.sec.* *.text) }\ +# RUN: } " > %t4.script + +# RUN: ld.lld -o %t4 --script %t4.script %t.o +# RUN: llvm-readobj --symbols %t4 | FileCheck --check-prefix MULTIPLE %s + +# MULTIPLE: Name: _start +# MULTIPLE: Section: .outsec1 +# MULTIPLE: Name: s1 +# MULTIPLE: Section: .outsec1 +# MULTIPLE: Name: s2 +# MULTIPLE: Section: .outsec1 +# MULTIPLE: Name: s3 +# MULTIPLE: Section: .sec.aw +# MULTIPLE: Name: s4 +# MULTIPLE: Section: .sec.aM +# MULTIPLE: Name: s5 +# MULTIPLE: Section: .sec.aMS + + .text + .global _start +_start: + nop + + .section .sec.a, "a", @progbits + .globl s1 +s1: + .long 1 + + .section .sec.ax, "ax", @progbits + .globl s2 +s2: + .long 2 + + .section .sec.aw, "aw", @progbits + .globl s3 +s3: + .long 3 + + .section .sec.aM, "aM", @progbits, 4 + .globl s4 +s4: + .long 4 + + .section .sec.aMS, "aMS", @progbits, 1 + .globl s5 +s5: + .asciz "a"