diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -126,14 +126,14 @@ // 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/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -103,10 +103,11 @@ 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 @@ 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/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -108,7 +108,7 @@ Expr readConstant(); Expr getPageSize(); - uint64_t readMemoryAssignment(StringRef, StringRef, StringRef); + Expr readMemoryAssignment(StringRef, StringRef, StringRef); std::pair readMemoryAttributes(); Expr combine(StringRef op, Expr l, Expr r); @@ -1302,7 +1302,7 @@ 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 @@ 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 @@ 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 @@ } 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(tok, origin, length, flags, negFlags); diff --git a/lld/docs/ELF/linker_script.rst b/lld/docs/ELF/linker_script.rst new file mode 100644 --- /dev/null +++ b/lld/docs/ELF/linker_script.rst @@ -0,0 +1,34 @@ +Output section description +========================== + +The description of an output section looks like: + +:: + + section [address] [(type)] : [AT(lma)] [ALIGN(section_align)] [SUBALIGN](subsection_align)] { + output-section-command + ... + } [>region] [AT>lma_region] [:phdr ...] [=fillexp] [,] + +Output section address +====================== + +When an *OutputSection* *S* has ``address``, LLD will set sh_addr to ``address``. + +The ELF specification says: + +> The value of sh_addr must be congruent to 0, modulo the value of sh_addralign. + +The presence of ``address`` can cause the condition unsatisfied. LLD will warn. +GNU ld from Binutils 2.35 onwards will reduce sh_addralign so that +sh_addr=0 (modulo sh_addralign). + +Output section alignment +======================== + +sh_addralign of an *OutputSection* *S* is the maximum of +``ALIGN(section_align)`` and the maximum alignment of the input sections in +*S*. + +When an *OutputSection* *S* has both ``address`` and ``ALIGN(section_align)``, +GNU ld will set sh_addralign to ``ALIGN(section_align)``. diff --git a/lld/test/ELF/linkerscript/memory-err.s b/lld/test/ELF/linkerscript/memory-err.s --- 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 --- 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: