[lld] 8cdf1c1 - [ELF] Support the "read-only" memory region attribute

Igor Kudrin via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 23 21:18:20 PST 2021


Author: Igor Kudrin
Date: 2021-11-24T12:17:09+07:00
New Revision: 8cdf1c1edb937b192d162f299127fad8d9dc0faa

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

LOG: [ELF] Support the "read-only" memory region attribute

The attribute 'r' allows (or disallows for the negative case) read-only
sections, i.e. ones without the SHF_WRITE flag, to be assigned to the
memory region. Before the patch, lld could put a section in the wrong
region or fail with "error: no memory region specified for section".

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

Added: 
    lld/test/ELF/linkerscript/memory-attr.test

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 40127baf40d6b..5e09e5dde39d1 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -977,7 +977,7 @@ LinkerScript::findMemoryRegion(OutputSection *sec, MemoryRegion *hint) {
   // See if a region can be found by matching section flags.
   for (auto &pair : memoryRegions) {
     MemoryRegion *m = pair.second;
-    if ((m->flags & sec->flags) && (m->negFlags & sec->flags) == 0)
+    if (m->compatibleWith(sec->flags))
       return {m, nullptr};
   }
 

diff  --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index b366da4f274ee..7faa49a794f9a 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -132,16 +132,32 @@ enum class ConstraintKind { NoConstraint, ReadOnly, ReadWrite };
 // MEMORY command.
 struct MemoryRegion {
   MemoryRegion(StringRef name, Expr origin, Expr length, uint32_t flags,
-               uint32_t negFlags)
+               uint32_t invFlags, uint32_t negFlags, uint32_t negInvFlags)
       : name(std::string(name)), origin(origin), length(length), flags(flags),
-        negFlags(negFlags) {}
+        invFlags(invFlags), negFlags(negFlags), negInvFlags(negInvFlags) {}
 
   std::string name;
   Expr origin;
   Expr length;
+  // A section can be assigned to the region if any of these ELF section flags
+  // are set...
   uint32_t flags;
+  // ... or any of these flags are not set.
+  // For example, the memory region attribute "r" maps to SHF_WRITE.
+  uint32_t invFlags;
+  // A section cannot be assigned to the region if any of these ELF section
+  // flags are set...
   uint32_t negFlags;
+  // ... or any of these flags are not set.
+  // For example, the memory region attribute "!r" maps to SHF_WRITE.
+  uint32_t negInvFlags;
   uint64_t curPos = 0;
+
+  bool compatibleWith(uint32_t secFlags) const {
+    if ((secFlags & negFlags) || (~secFlags & negInvFlags))
+      return false;
+    return (secFlags & flags) || (~secFlags & invFlags);
+  }
 };
 
 // This struct represents one section match pattern in SECTIONS() command.

diff  --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index ad3b3e61ad594..cc18a59da2369 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -113,7 +113,8 @@ class ScriptParser final : ScriptLexer {
   Expr getPageSize();
 
   Expr readMemoryAssignment(StringRef, StringRef, StringRef);
-  std::pair<uint32_t, uint32_t> readMemoryAttributes();
+  void readMemoryAttributes(uint32_t &flags, uint32_t &invFlags,
+                            uint32_t &negFlags, uint32_t &negInvFlags);
 
   Expr combine(StringRef op, Expr l, Expr r);
   Expr readExpr();
@@ -1614,9 +1615,11 @@ void ScriptParser::readMemory() {
     }
 
     uint32_t flags = 0;
+    uint32_t invFlags = 0;
     uint32_t negFlags = 0;
+    uint32_t negInvFlags = 0;
     if (consume("(")) {
-      std::tie(flags, negFlags) = readMemoryAttributes();
+      readMemoryAttributes(flags, invFlags, negFlags, negInvFlags);
       expect(")");
     }
     expect(":");
@@ -1626,7 +1629,8 @@ void ScriptParser::readMemory() {
     Expr length = readMemoryAssignment("LENGTH", "len", "l");
 
     // Add the memory region to the region map.
-    MemoryRegion *mr = make<MemoryRegion>(tok, origin, length, flags, negFlags);
+    MemoryRegion *mr = make<MemoryRegion>(tok, origin, length, flags, invFlags,
+                                          negFlags, negInvFlags);
     if (!script->memoryRegions.insert({tok, mr}).second)
       setError("region '" + tok + "' already defined");
   }
@@ -1635,30 +1639,34 @@ void ScriptParser::readMemory() {
 // This function parses the attributes used to match against section
 // flags when placing output sections in a memory region. These flags
 // are only used when an explicit memory region name is not used.
-std::pair<uint32_t, uint32_t> ScriptParser::readMemoryAttributes() {
-  uint32_t flags = 0;
-  uint32_t negFlags = 0;
+void ScriptParser::readMemoryAttributes(uint32_t &flags, uint32_t &invFlags,
+                                        uint32_t &negFlags,
+                                        uint32_t &negInvFlags) {
   bool invert = false;
 
   for (char c : next().lower()) {
-    uint32_t flag = 0;
-    if (c == '!')
+    if (c == '!') {
       invert = !invert;
-    else if (c == 'w')
-      flag = SHF_WRITE;
+      std::swap(flags, negFlags);
+      std::swap(invFlags, negInvFlags);
+      continue;
+    }
+    if (c == 'w')
+      flags |= SHF_WRITE;
     else if (c == 'x')
-      flag = SHF_EXECINSTR;
+      flags |= SHF_EXECINSTR;
     else if (c == 'a')
-      flag = SHF_ALLOC;
-    else if (c != 'r')
+      flags |= SHF_ALLOC;
+    else if (c == 'r')
+      invFlags |= SHF_WRITE;
+    else
       setError("invalid memory region attribute");
+  }
 
-    if (invert)
-      negFlags |= flag;
-    else
-      flags |= flag;
+  if (invert) {
+    std::swap(flags, negFlags);
+    std::swap(invFlags, negInvFlags);
   }
-  return {flags, negFlags};
 }
 
 void elf::readLinkerScript(MemoryBufferRef mb) {

diff  --git a/lld/test/ELF/linkerscript/memory-attr.test b/lld/test/ELF/linkerscript/memory-attr.test
new file mode 100644
index 0000000000000..5ea6e77ee2540
--- /dev/null
+++ b/lld/test/ELF/linkerscript/memory-attr.test
@@ -0,0 +1,75 @@
+REQUIRES: x86
+
+RUN: split-file %s %ts
+RUN: llvm-mc -filetype=obj -triple=x86_64 %ts/s -o %t.o
+
+## Check assigning sections to memory regions by attributes.
+
+#--- s
+  .text
+  .zero 8
+
+  .rodata
+  .zero 8
+
+  .data
+  .zero 8
+
+#--- t1
+## Check memory region attribute 'a'.
+
+# RUN: ld.lld -o %t1 -T %ts/t1 %t.o
+# RUN: llvm-readelf -S %t1 | FileCheck %s --check-prefix=TEST1
+
+# TEST1:      Name     Type      Address
+# TEST1:      .text    PROGBITS  0000000000002000
+# TEST1-NEXT: .rodata  PROGBITS  0000000000002008
+# TEST1-NEXT: .data    PROGBITS  0000000000002010
+
+MEMORY
+{
+  ## All sections have SHF_ALLOC attribute, so no one can be added here.
+  NOMEM (rwx!a) : org = 0x1000, l = 1K
+  ## All sections are assigned to this memory region.
+  MEM   (a)     : org = 0x2000, l = 1K
+}
+
+SECTIONS
+{
+  .text   : { *(.text) }
+  .rodata : { *(.rodata) }
+  .data   : { *(.data) }
+}
+
+#--- t2
+## Check that memory region attributes 'r', 'w', and 'x' are supported both in
+## positive and negative forms.
+
+# RUN: ld.lld -o %t2 -T %ts/t2 %t.o
+# RUN: llvm-readelf -S %t2 | FileCheck %s --check-prefix=TEST2
+
+# TEST2:      Name     Type      Address
+# TEST2:      .text    PROGBITS  0000000000004000
+# TEST2-NEXT: .rodata  PROGBITS  0000000000003000
+# TEST2-NEXT: .data    PROGBITS  0000000000002000
+
+MEMORY
+{
+  ## While all sections are allocatable, '.text' and '.rodata' are read-only and
+  ## '.data' is writable, so no sections should be assigned to this region.
+  NOMEM (a!rw) : org = 0x1000, l = 1K
+  ## Only writable section '.data' is allowed here.
+  RAM   (w)    : org = 0x2000, l = 1K
+  ## Executable sections are not allowed here, so only '.rodata' should be
+  ## assigned to the region.
+  ROM   (r!x)  : org = 0x3000, l = 1K
+  ## An executable section '.text' comes here.
+  EXEC  (x)    : org = 0x4000, l = 1K
+}
+
+SECTIONS
+{
+  .text   : { *(.text) }
+  .rodata : { *(.rodata) }
+  .data   : { *(.data) }
+}


        


More information about the llvm-commits mailing list