[lld] dbd0ad3 - [LLD][ELF] Add support for INPUT_SECTION_FLAGS

Peter Smith via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 21 02:15:04 PST 2020


Author: Peter Smith
Date: 2020-01-21T10:05:26Z
New Revision: dbd0ad33668ea7b6d5c2c5db1fa290cc08ab99d9

URL: https://github.com/llvm/llvm-project/commit/dbd0ad33668ea7b6d5c2c5db1fa290cc08ab99d9
DIFF: https://github.com/llvm/llvm-project/commit/dbd0ad33668ea7b6d5c2c5db1fa290cc08ab99d9.diff

LOG: [LLD][ELF] Add support for INPUT_SECTION_FLAGS

The INPUT_SECTION_FLAGS linker script command is used to constrain the
section pattern matching to sections that match certain combinations of
flags.

There are two ways to express the constraint.
withFlags: Section must have these flags.
withoutFlags: Section must not have these flags.

The syntax of the command is:
INPUT_SECTION_FLAGS '(' sect_flag_list ')'
sect_flag_list: NAME
| sect_flag_list '&' NAME

Where NAME matches a section flag name such as SHF_EXECINSTR, or the
integer value of a section flag. If the first character of NAME is ! then
it means must not contain flag.

We do not support the rare case of { INPUT_SECTION_FLAGS(flags) filespec }
where filespec has no input section description like (.text).

As an example from the ld man page:
SECTIONS {
  .text : { INPUT_SECTION_FLAGS (SHF_MERGE & SHF_STRINGS) *(.text) }
  .text2 :  { INPUT_SECTION_FLAGS (!SHF_WRITE) *(.text) }
}
.text will match sections called .text that have both the SHF_MERGE and
SHF_STRINGS flag.
.text2 will match sections called .text that don't have the SHF_WRITE flag.

The flag names accepted are the generic to all targets and SHF_ARM_PURECODE
as it is very useful to filter all the pure code sections into a single
program header that can be marked execute never.

fixes PR44265

Differential Revision: https://reviews.llvm.org/D72756

Added: 
    lld/test/ELF/input-section-flags-diag1.test
    lld/test/ELF/input-section-flags-diag2.test
    lld/test/ELF/input-section-flags-diag3.test
    lld/test/ELF/input-section-flags-keep.s
    lld/test/ELF/input-section-flags.s

Modified: 
    lld/ELF/LinkerScript.cpp
    lld/ELF/LinkerScript.h
    lld/ELF/ScriptParser.cpp

Removed: 
    


################################################################################
diff  --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index b0d60bc32a9f..aaa7744a7809 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -335,7 +335,9 @@ bool LinkerScript::shouldKeep(InputSectionBase *s) {
   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 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
         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/LinkerScript.h b/lld/ELF/LinkerScript.h
index 25a14e08dade..d57301cf3524 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -155,8 +155,10 @@ struct SectionPattern {
 };
 
 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 @@ struct InputSectionDescription : BaseCommand {
   // they were created in. This is used to insert newly created ThunkSections
   // into Sections at the end of a createThunks() pass.
   std::vector<std::pair<ThunkSection *, uint32_t>> 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/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index fd8de3b54bd7..f62a0d133afd 100644
--- 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 <cassert>
 #include <limits>
 #include <vector>
@@ -91,10 +92,13 @@ class ScriptParser final : ScriptLexer {
   OutputSection *readOutputSectionDescription(StringRef outSec);
   std::vector<BaseCommand *> readOverlay();
   std::vector<StringRef> readOutputSectionPhdrs();
+  std::pair<uint64_t, uint64_t> readInputSectionFlags();
   InputSectionDescription *readInputSectionDescription(StringRef tok);
   StringMatcher readFilePatterns();
   std::vector<SectionPattern> 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 @@ std::vector<SectionPattern> ScriptParser::readInputSectionsList() {
 //
 // <section-list> is parsed by readInputSectionsList().
 InputSectionDescription *
-ScriptParser::readInputSectionRules(StringRef filePattern) {
-  auto *cmd = make<InputSectionDescription>(filePattern);
+ScriptParser::readInputSectionRules(StringRef filePattern, uint64_t withFlags,
+                                    uint64_t withoutFlags) {
+  auto *cmd =
+      make<InputSectionDescription>(filePattern, withFlags, withoutFlags);
   expect("(");
 
   while (!errorCount() && !consume(")")) {
@@ -694,15 +700,23 @@ InputSectionDescription *
 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 @@ OutputSection *ScriptParser::readOverlaySectionDescription() {
       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 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef outSec) {
       // 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<InputSectionDescription>(tok);
       isd->sectionPatterns.push_back({{}, StringMatcher({"*"})});
       cmd->sectionCommands.push_back(isd);
@@ -1102,6 +1124,63 @@ ByteCommand *ScriptParser::readByteCommand(StringRef tok) {
   return make<ByteCommand>(e, size, commandString);
 }
 
+static llvm::Optional<uint64_t> parseFlag(StringRef tok) {
+  if (llvm::Optional<uint64_t> asInt = parseInt(tok))
+    return asInt;
+#define CASE_ENT(enum) #enum, ELF::enum
+  return StringSwitch<llvm::Optional<uint64_t>>(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 '(' <flags> ')' list of section flags in
+// INPUT_SECTION_FLAGS '(' <flags> ')' in the
+// following form:
+// <flags> ::= <flag>
+//           | <flags> & flag
+// <flag>  ::= Recognized Flag Name, or Integer value of flag.
+// If the first character of <flag> 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<uint64_t, uint64_t> 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<uint64_t> 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
index 000000000000..329f17c86ec7
--- /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
index 000000000000..a83ab23d7040
--- /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
index 000000000000..2d16030fc10f
--- /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
index 000000000000..51891892940d
--- /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
index 000000000000..0c8e31c77b0d
--- /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"


        


More information about the llvm-commits mailing list