diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -324,10 +324,6 @@ // Sections that will be warned/errored by --orphan-handling. std::vector orphanSections; - - // Sections whose addresses are not equal to their addrExpr values. - std::vector> - changedSectionAddresses; }; extern LinkerScript *script; diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -777,18 +777,13 @@ ctx->outSec = sec; uint64_t pos = advance(0, 1); - if (sec->addrExpr && !sec->alignExpr) { + if (sec->addrExpr && script->hasSectionsCommand) { // The alignment is ignored. ctx->outSec->addr = pos; } else { - // If ALIGN is specified, advance sh_addr according to ALIGN and ignore the - // maximum of input section alignments. - // - // When no SECTIONS command is given, sec->alignExpr is set to the maximum - // of input section alignments. - uint32_t align = - sec->alignExpr ? sec->alignExpr().getValue() : ctx->outSec->alignment; - ctx->outSec->addr = advance(0, align); + // ctx->outSec->alignment is the max of ALIGN and the maximum of input + // section alignments. + ctx->outSec->addr = advance(0, ctx->outSec->alignment); expandMemoryRegions(ctx->outSec->addr - pos); } } @@ -854,10 +849,7 @@ expandMemoryRegion(ctx->memRegion, dot - ctx->memRegion->curPos, ctx->memRegion->name, sec->name); - uint64_t oldDot = dot; switchTo(sec); - if (sec->addrExpr && oldDot != dot) - changedSectionAddresses.push_back({sec, oldDot}); ctx->lmaOffset = 0; @@ -1129,7 +1121,6 @@ auto deleter = std::make_unique(); ctx = deleter.get(); errorOnMissingSection = true; - changedSectionAddresses.clear(); switchTo(aether); SymbolAssignmentMap oldValues = getSymbolAssignmentValues(sectionCommands); diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1638,16 +1638,14 @@ } } - // If a SECTIONS command is given, addrExpr, if set, is the specified output - // section address. Warn if the computed value is different from the actual - // address. - if (!script->hasSectionsCommand) - return; - for (auto changed : script->changedSectionAddresses) { - const OutputSection *os = changed.first; - warn("start of section " + os->name + " changes from 0x" + - utohexstr(changed.second) + " to 0x" + utohexstr(os->addr)); - } + // If addrExpr is set, the address may not be a multiple of the alignment. + // Warn because this is error-prone. + for (BaseCommand *cmd : script->sectionCommands) + if (auto *os = dyn_cast(cmd)) + if (os->addr % os->alignment != 0) + warn("address (0x" + Twine::utohexstr(os->addr) + ") of section " + + os->name + " is not a multiple of alignment (" + + Twine(os->alignment) + ")"); } static void finalizeSynthetic(SyntheticSection *sec) { diff --git a/lld/docs/ELF/linker_script.rst b/lld/docs/ELF/linker_script.rst --- a/lld/docs/ELF/linker_script.rst +++ b/lld/docs/ELF/linker_script.rst @@ -16,3 +16,38 @@ possible. We reserve the right to make different implementation choices where it is appropriate for LLD. Intentional deviations will be documented in this file. + +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/lma-align.test b/lld/test/ELF/linkerscript/lma-align.test --- a/lld/test/ELF/linkerscript/lma-align.test +++ b/lld/test/ELF/linkerscript/lma-align.test @@ -5,21 +5,21 @@ # RUN: ld.lld -T %s %t.o -o %t 2>&1 | FileCheck --check-prefix=WARN %s --implicit-check-not=warning: # RUN: llvm-readelf -S -l %t | FileCheck %s -# WARN: warning: start of section .data changes from 0x11001 to 0x11010 -# WARN: warning: start of section .bss changes from 0x11021 to 0x11040 +# WARN: warning: address (0x11001) of section .data is not a multiple of alignment (32) +# WARN: warning: address (0x11021) of section .bss is not a multiple of alignment (64) # CHECK: Name Type Address Off Size ES Flg Lk Inf Al # CHECK-NEXT: NULL 0000000000000000 000000 000000 00 0 0 0 # CHECK-NEXT: .text PROGBITS 0000000000001000 001000 000001 00 AX 0 0 4 # CHECK-NEXT: .data.rel.ro PROGBITS 0000000000011000 002000 000001 00 WA 0 0 16 -# CHECK-NEXT: .data PROGBITS 0000000000011010 002010 000011 00 WA 0 0 32 -# CHECK-NEXT: .bss NOBITS 0000000000011040 002040 000001 00 WA 0 0 64 +# CHECK-NEXT: .data PROGBITS 0000000000011001 002001 000020 00 WA 0 0 32 +# CHECK-NEXT: .bss NOBITS 0000000000011021 002021 000001 00 WA 0 0 64 # CHECK: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align # CHECK-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x000001 0x000001 R E 0x1000 # CHECK-NEXT: LOAD 0x002000 0x0000000000011000 0x0000000000001010 0x000001 0x000001 RW 0x1000 -# CHECK-NEXT: LOAD 0x002010 0x0000000000011010 0x0000000000001020 0x000011 0x000011 RW 0x1000 -# CHECK-NEXT: LOAD 0x002040 0x0000000000011040 0x0000000000011040 0x000000 0x000001 RW 0x1000 +# CHECK-NEXT: LOAD 0x002001 0x0000000000011001 0x0000000000001020 0x000020 0x000020 RW 0x1000 +# CHECK-NEXT: LOAD 0x002021 0x0000000000011021 0x0000000000011021 0x000000 0x000001 RW 0x1000 MEMORY { ROM : ORIGIN = 0x1000, LENGTH = 1K diff --git a/lld/test/ELF/linkerscript/section-address-align.test b/lld/test/ELF/linkerscript/section-address-align.test new file mode 100644 --- /dev/null +++ b/lld/test/ELF/linkerscript/section-address-align.test @@ -0,0 +1,34 @@ +# REQUIRES: x86 +## Test ALIGN when specifying the output section address. + +# RUN: echo '.globl _start; _start: ret; \ +# RUN: .data.rel.ro; .balign 8; .byte 0; \ +# RUN: .data; .byte 0; \ +# RUN: .bss; .balign 32; .byte 0' | \ +# RUN: llvm-mc -filetype=obj -triple=x86_64 - -o %t.o +# RUN: ld.lld -T %s %t.o -o %t 2>&1 | FileCheck --check-prefix=WARN %s --implicit-check-not=warning: +# RUN: llvm-readelf -S %t | FileCheck %s + +# WARN: warning: address (0x10004) of section .data.rel.ro is not a multiple of alignment (8) +# WARN: warning: address (0x20008) of section .bss is not a multiple of alignment (32) + +# CHECK: Name Type Address Off Size ES Flg Lk Inf Al +# CHECK-NEXT: NULL 0000000000000000 000000 000000 00 0 0 0 +# CHECK-NEXT: .text PROGBITS 0000000000010000 001000 000001 00 AX 0 0 4 +# CHECK-NEXT: .data.rel.ro PROGBITS 0000000000010004 001004 000005 00 WA 0 0 8 +# CHECK-NEXT: .data PROGBITS 0000000000020000 002000 000001 00 WA 0 0 1 +# CHECK-NEXT: .bss NOBITS 0000000000020008 002001 000019 00 WA 0 0 32 + +SECTIONS { + .text 0x10000 : { *(.text) } + ## The output .data.rel.ro starts at 0x10004. + ## The input .data.rel.ro starts at 0x10008 and ends at 0x10009. + ## sh_size(.data.rel.ro) = 0x10009-0x10004 = 0x5. + .data.rel.ro ALIGN(4) : { *(.data.rel.ro) } + + .data 0x20000 : { *(.data) } + ## The output .bss starts at 0x20008. + ## The input .bss starts at 0x20020 and ends at 0x20021. + ## sh_size(.bss) = 0x20021-0x20008 = 0x19. + .bss ALIGN(8) : { *(.bss) } +} diff --git a/lld/test/ELF/linkerscript/section-align2.test b/lld/test/ELF/linkerscript/section-align2.test --- a/lld/test/ELF/linkerscript/section-align2.test +++ b/lld/test/ELF/linkerscript/section-align2.test @@ -2,7 +2,9 @@ ## Test ALIGN and its interaction with explicit output section addresses. # RUN: echo '.globl _start; _start: ret; .data.rel.ro; .balign 8; .byte 0; .data; .byte 0; \ -# RUN: .section .data2,"aw"; .balign 8; .byte 0; .bss; .balign 32; .byte 0' | \ +# RUN: .section .data2,"aw"; .balign 8; .byte 0; \ +# RUN: .section .data3,"aw"; .balign 32; .byte 0; \ +# RUN: .bss; .balign 32; .byte 0' | \ # RUN: llvm-mc -filetype=obj -triple=aarch64 - -o %t.o # RUN: ld.lld -T %s %t.o -o %t 2>&1 | FileCheck --check-prefix=WARN %s --implicit-check-not=warning: # RUN: llvm-readelf -S %t | FileCheck %s @@ -10,16 +12,18 @@ ## Check we don't warn in the absence of SECTIONS. # RUN: ld.lld --fatal-warnings -Ttext=0x10000 %t.o -o /dev/null -# WARN: warning: start of section .data.rel.ro changes from 0x10004 to 0x10010 -# WARN: warning: start of section .bss changes from 0x20009 to 0x20010 +# WARN: warning: address (0x10004) of section .data.rel.ro is not a multiple of alignment (16) +# WARN: warning: address (0x20001) of section .data2 is not a multiple of alignment (8) +# WARN: warning: address (0x20021) of section .bss is not a multiple of alignment (32) # CHECK: Name Type Address Off Size ES Flg Lk Inf Al # CHECK-NEXT: NULL 0000000000000000 000000 000000 00 0 0 0 # CHECK-NEXT: .text PROGBITS 0000000000010000 010000 000004 00 AX 0 0 4 -# CHECK-NEXT: .data.rel.ro PROGBITS 0000000000010010 010010 000001 00 WA 0 0 16 +# CHECK-NEXT: .data.rel.ro PROGBITS 0000000000010004 010004 000005 00 WA 0 0 16 # CHECK-NEXT: .data PROGBITS 0000000000020000 020000 000001 00 WA 0 0 1 # CHECK-NEXT: .data2 PROGBITS 0000000000020001 020001 000008 00 WA 0 0 8 -# CHECK-NEXT: .bss NOBITS 0000000000020010 020009 000011 00 WA 0 0 32 +# CHECK-NEXT: .data3 PROGBITS 0000000000020020 020020 000001 00 WA 0 0 32 +# CHECK-NEXT: .bss NOBITS 0000000000020021 020021 000020 00 WA 0 0 32 SECTIONS { .text 0x10000 : { *(.text) } @@ -30,6 +34,9 @@ ## The output section address is set without ALIGN. sh_addr is set to Dot, ignoring alignment. ## sh_addralign is the maximum of input section alignments, 8. .data2 . : { *(.data2) } + ## sh_addr is aligned to 32. + ## The input section has a larger alignment and is thus preceded by a gap. + .data3 : ALIGN(16) { *(.data3) } ## sh_addr is aligned to 16. ## The input section has a larger alignment and is thus preceded by a gap. .bss . : ALIGN(16) { *(.bss) }