[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