diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -761,9 +761,21 @@ void LinkerScript::switchTo(OutputSection *sec) { ctx->outSec = sec; - uint64_t before = advance(0, 1); - ctx->outSec->addr = advance(0, ctx->outSec->alignment); - expandMemoryRegions(ctx->outSec->addr - before); + uint64_t pos = advance(0, 1); + if (sec->addrExpr && !sec->alignExpr) { + // 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); + expandMemoryRegions(ctx->outSec->addr - pos); + } } // This function searches for a memory region to place the given output diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -2236,7 +2236,10 @@ const PhdrEntry *prev; auto pageAlign = [&](const PhdrEntry *p) { OutputSection *cmd = p->firstSec; - if (cmd && !cmd->addrExpr) { + if (!cmd) + return; + cmd->alignExpr = [align = cmd->alignment]() { return align; }; + if (!cmd->addrExpr) { // Prefer advancing to align(dot, maxPageSize) + dot%maxPageSize to avoid // padding in the file contents. // 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 @@ -1,5 +1,6 @@ # REQUIRES: x86 -# RUN: echo 'ret; .data.rel.ro; .balign 16; .byte 0; .data; .byte 0; .bss; .byte 0' | \ +# RUN: echo '.globl _start; _start: ret; .data.rel.ro; .balign 16; .byte 0; \ +# RUN: .data; .balign 32; .byte 0; .bss; .byte 0' | \ # RUN: llvm-mc -filetype=obj -triple=x86_64 - -o %t.o # RUN: ld.lld -T %s %t.o -o %t # RUN: llvm-readelf -S -l %t | FileCheck %s @@ -8,13 +9,13 @@ # 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 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: 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 0x000001 0x000001 RW 0x1000 +# CHECK-NEXT: LOAD 0x002010 0x0000000000011010 0x0000000000001020 0x000011 0x000011 RW 0x1000 # CHECK-NEXT: LOAD 0x002040 0x0000000000011040 0x0000000000011040 0x000000 0x000001 RW 0x1000 MEMORY { diff --git a/lld/test/ELF/linkerscript/outsections-addr.s b/lld/test/ELF/linkerscript/outsections-addr.s --- a/lld/test/ELF/linkerscript/outsections-addr.s +++ b/lld/test/ELF/linkerscript/outsections-addr.s @@ -83,9 +83,9 @@ #CHECK: Flags [ #CHECK: SHF_ALLOC #CHECK: ] -#CHECK: Address: 0x5010 -#CHECK: Offset: 0x4010 -#CHECK: Size: 8 +#CHECK: Address: 0x5001 +#CHECK: Offset: 0x4001 +#CHECK: Size: 23 #CHECK: Link: 0 #CHECK: Info: 0 #CHECK: AddressAlignment: 16 diff --git a/lld/test/ELF/linkerscript/section-align2.test b/lld/test/ELF/linkerscript/section-align2.test new file mode 100644 --- /dev/null +++ b/lld/test/ELF/linkerscript/section-align2.test @@ -0,0 +1,30 @@ +# REQUIRES: aarch64 +## 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: llvm-mc -filetype=obj -triple=aarch64 - -o %t.o +# RUN: ld.lld -T %s %t.o -o %t 2>&1 | count 0 +# RUN: llvm-readelf -S %t | FileCheck %s + +# 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 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 + +SECTIONS { + .text 0x10000 : { *(.text) } + ## sh_addr is aligned to 16. + .data.rel.ro . : ALIGN(16) { *(.data.rel.ro) } + + .data 0x20000 : { *(.data) } + ## 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 16. + ## The input section has a larger alignment and is thus preceded by a gap. + .bss . : ALIGN(16) { *(.bss) } +}