[lld] 92b5b98 - [ELF] Postpone evaluation of ORIGIN/LENGTH in a MEMORY command

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 9 08:32:00 PDT 2020


Author: Fangrui Song
Date: 2020-03-09T08:31:41-07:00
New Revision: 92b5b980d2e3ed2abddf116c18d163488b62235f

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

LOG: [ELF] Postpone evaluation of ORIGIN/LENGTH in a MEMORY command

```
createFiles(args)
 readDefsym
 readerLinkerScript(*mb)
  ...
   readMemory
    readMemoryAssignment("ORIGIN", "org", "o") // eagerly evaluated
target = getTarget();
link(args)
 writeResult<ELFT>()
  ...
   finalizeSections()
    script->processSymbolAssignments()
     addSymbol(cmd) // with this patch, evaluated here
```

readMemoryAssignment eagerly evaluates ORIGIN/LENGTH and returns an uint64_t.
This patch postpones the evaluation to make

* --defsym and symbol assignments
* `CONSTANT(COMMONPAGESIZE)` (requires a non-null `lld::elf::target`)

work. If the expression somehow requires interaction with memory
regions, the circular dependency may cause the expression to evaluate to
a strange value. See the new test added to memory-err.s

Reviewed By: grimar

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

Added: 
    

Modified: 
    lld/ELF/LinkerScript.cpp
    lld/ELF/LinkerScript.h
    lld/ELF/ScriptParser.cpp
    lld/test/ELF/linkerscript/memory-err.s
    lld/test/ELF/linkerscript/memory.s

Removed: 
    


################################################################################
diff  --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index ceebb03d88f8..0aca7c132c87 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -103,10 +103,11 @@ OutputSection *LinkerScript::getOrCreateOutputSection(StringRef name) {
 static void expandMemoryRegion(MemoryRegion *memRegion, uint64_t size,
                                StringRef regionName, StringRef secName) {
   memRegion->curPos += size;
-  uint64_t newSize = memRegion->curPos - memRegion->origin;
-  if (newSize > memRegion->length)
+  uint64_t newSize = memRegion->curPos - (memRegion->origin)().getValue();
+  uint64_t length = (memRegion->length)().getValue();
+  if (newSize > length)
     error("section '" + secName + "' will not fit in region '" + regionName +
-          "': overflowed by " + Twine(newSize - memRegion->length) + " bytes");
+          "': overflowed by " + Twine(newSize - length) + " bytes");
 }
 
 void LinkerScript::expandMemoryRegions(uint64_t size) {
@@ -1101,7 +1102,7 @@ void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &phdrs) {
 LinkerScript::AddressState::AddressState() {
   for (auto &mri : script->memoryRegions) {
     MemoryRegion *mr = mri.second;
-    mr->curPos = mr->origin;
+    mr->curPos = (mr->origin)().getValue();
   }
 }
 

diff  --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index 32b33b82f98e..2eb98b694de7 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -126,14 +126,14 @@ enum class ConstraintKind { NoConstraint, ReadOnly, ReadWrite };
 // target memory. Instances of the struct are created by parsing the
 // MEMORY command.
 struct MemoryRegion {
-  MemoryRegion(StringRef name, uint64_t origin, uint64_t length, uint32_t flags,
+  MemoryRegion(StringRef name, Expr origin, Expr length, uint32_t flags,
                uint32_t negFlags)
       : name(std::string(name)), origin(origin), length(length), flags(flags),
         negFlags(negFlags) {}
 
   std::string name;
-  uint64_t origin;
-  uint64_t length;
+  Expr origin;
+  Expr length;
   uint32_t flags;
   uint32_t negFlags;
   uint64_t curPos = 0;

diff  --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index 81864207a7ed..11e87197a9a2 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -108,7 +108,7 @@ class ScriptParser final : ScriptLexer {
   Expr readConstant();
   Expr getPageSize();
 
-  uint64_t readMemoryAssignment(StringRef, StringRef, StringRef);
+  Expr readMemoryAssignment(StringRef, StringRef, StringRef);
   std::pair<uint32_t, uint32_t> readMemoryAttributes();
 
   Expr combine(StringRef op, Expr l, Expr r);
@@ -1302,7 +1302,7 @@ Expr ScriptParser::readPrimary() {
       setError("memory region not defined: " + name);
       return [] { return 0; };
     }
-    return [=] { return script->memoryRegions[name]->length; };
+    return script->memoryRegions[name]->length;
   }
   if (tok == "LOADADDR") {
     StringRef name = readParenLiteral();
@@ -1329,7 +1329,7 @@ Expr ScriptParser::readPrimary() {
       setError("memory region not defined: " + name);
       return [] { return 0; };
     }
-    return [=] { return script->memoryRegions[name]->origin; };
+    return script->memoryRegions[name]->origin;
   }
   if (tok == "SEGMENT_START") {
     expect("(");
@@ -1519,14 +1519,14 @@ std::vector<SymbolVersion> ScriptParser::readVersionExtern() {
   return ret;
 }
 
-uint64_t ScriptParser::readMemoryAssignment(StringRef s1, StringRef s2,
-                                            StringRef s3) {
+Expr ScriptParser::readMemoryAssignment(StringRef s1, StringRef s2,
+                                        StringRef s3) {
   if (!consume(s1) && !consume(s2) && !consume(s3)) {
     setError("expected one of: " + s1 + ", " + s2 + ", or " + s3);
-    return 0;
+    return [] { return 0; };
   }
   expect("=");
-  return readExpr()().getValue();
+  return readExpr();
 }
 
 // Parse the MEMORY command as specified in:
@@ -1550,9 +1550,9 @@ void ScriptParser::readMemory() {
     }
     expect(":");
 
-    uint64_t origin = readMemoryAssignment("ORIGIN", "org", "o");
+    Expr origin = readMemoryAssignment("ORIGIN", "org", "o");
     expect(",");
-    uint64_t length = readMemoryAssignment("LENGTH", "len", "l");
+    Expr length = readMemoryAssignment("LENGTH", "len", "l");
 
     // Add the memory region to the region map.
     MemoryRegion *mr = make<MemoryRegion>(tok, origin, length, flags, negFlags);

diff  --git a/lld/test/ELF/linkerscript/memory-err.s b/lld/test/ELF/linkerscript/memory-err.s
index ac8d77ead7a4..214b618f4ec0 100644
--- a/lld/test/ELF/linkerscript/memory-err.s
+++ b/lld/test/ELF/linkerscript/memory-err.s
@@ -52,19 +52,22 @@
 # ERR7: {{.*}}.script:1: invalid memory region attribute
 
 # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: echo "MEMORY { name : ORIGIN = DATA_SEGMENT_RELRO_END; }" > %t.script
-# RUN: not ld.lld -shared -o /dev/null -T %t.script %t.o 2>&1 | FileCheck %s
-# CHECK: error: {{.*}}.script:1: unable to calculate page size
-
-# RUN: echo 'MEMORY { name : ORIGIN = CONSTANT(COMMONPAGESIZE); }' > %t.script
-# RUN: not ld.lld -shared -o /dev/null -T %t.script %t.o 2>&1 | FileCheck --check-prefix=ERR_PAGESIZE %s
-# ERR_PAGESIZE: error: {{.*}}.script:1: unable to calculate page size
-
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: echo 'MEMORY { name : ORIGIN = .; }' > %t.script
+# RUN: echo 'MEMORY { name : ORIGIN = ., LENGTH = 1 }' > %t.script
 # RUN: not ld.lld -shared -o /dev/null -T %t.script %t.o 2>&1 | FileCheck --check-prefix=ERR_DOT %s
 # ERR_DOT: error: {{.*}}.script:1: unable to get location counter value
 
+## ORIGIN/LENGTH can be simple symbolic expressions. If the expression
+## requires interaction with memory regions, it may fail.
+
+# RUN: echo 'MEMORY { ram : ORIGIN = symbol, LENGTH = 4097 } \
+# RUN: SECTIONS { \
+# RUN:   .text : { *(.text) } > ram \
+# RUN:   symbol = .; \
+# RUN:   .data : { *(.data) } > ram \
+# RUN: }' > %t.script
+# RUN: not ld.lld -T %t.script %t.o -o /dev/null 2>&1 | FileCheck --check-prefix=ERR_OVERFLOW %s
+# ERR_OVERFLOW: error: section '.text' will not fit in region 'ram': overflowed by 18446744073709547518 bytes
+
 nop
 
 .data

diff  --git a/lld/test/ELF/linkerscript/memory.s b/lld/test/ELF/linkerscript/memory.s
index 3111ef60dde9..e3c19bc3d03a 100644
--- a/lld/test/ELF/linkerscript/memory.s
+++ b/lld/test/ELF/linkerscript/memory.s
@@ -46,6 +46,32 @@
 # ATTRS:      [ 1] .text PROGBITS 0000000080000000 001000 000001
 # ATTRS-NEXT: [ 2] .data PROGBITS 0000000000000000 002000 001000
 
+## ORIGIN/LENGTH support expressions with symbol assignments.
+# RUN: echo 'MEMORY { ram : ORIGIN = symbol, LENGTH = 4097 } \
+# RUN: SECTIONS { \
+# RUN:   .text : { *(.text) } > ram \
+# RUN:   .data : { *(.data) } > ram \
+# RUN: }' > %t.script
+# RUN: ld.lld -T %t.script %t --defsym symbol=0x5000 -o %t.relro
+# RUN: llvm-readelf -S %t.relro | FileCheck --check-prefix=RELRO %s
+# RUN: echo 'symbol = 0x5000;' > %t1.script
+# RUN: ld.lld -T %t.script -T %t1.script %t -o %t.relro2
+# RUN: llvm-readelf -S %t.relro2 | FileCheck --check-prefix=RELRO %s
+
+# RELRO:      [ 1] .text PROGBITS 0000000000005000 001000 000001
+# RELRO-NEXT: [ 2] .data PROGBITS 0000000000005001 001001 001000
+
+# RUN: echo 'MEMORY { ram : ORIGIN = CONSTANT(COMMONPAGESIZE), LENGTH = CONSTANT(COMMONPAGESIZE)+1 } \
+# RUN: SECTIONS { \
+# RUN:   .text : { *(.text) } > ram \
+# RUN:   .data : { *(.data) } > ram \
+# RUN: }' > %t.script
+# RUN: ld.lld -T %t.script %t -o %t.pagesize
+# RUN: llvm-readelf -S %t.pagesize | FileCheck --check-prefix=PAGESIZE %s
+
+# PAGESIZE:      [ 1] .text PROGBITS 0000000000001000 001000 000001
+# PAGESIZE-NEXT: [ 2] .data PROGBITS 0000000000001001 001001 001000
+
 .text
 .global _start
 _start:


        


More information about the llvm-commits mailing list