Fangrui Song 7b6a89f346
[ELF] Detect convergence of output section addresses
Some linker scripts don't converge. https://reviews.llvm.org/D66279
("[ELF] Make LinkerScript::assignAddresses iterative") detected
convergence of symbol assignments.

This patch detects convergence of output section addresses. While input
sections might also have convergence issues, they are less common as
expressions that could cause convergence issues typically involve output
sections and symbol assignments.

GNU ld has an error `non constant or forward reference address expression for section` that
correctly rejects
```
SECTIONS {
  .text ADDR(.data)+0x1000 : { *(.text) }
  .data : { *(.data) }
}
```

but not the following variant:
```
SECTIONS {
  .text foo : { *(.text) }
  .data : { *(.data) }
  foo = ADDR(.data)+0x1000;
}
```

Our approach consistently rejects both cases.

Link: https://discourse.llvm.org/t/lld-and-layout-convergence/79232

Pull Request: https://github.com/llvm/llvm-project/pull/93888
2024-05-31 09:31:15 -07:00

78 lines
3.0 KiB
ArmAsm

# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
## Check bad `ORIGIN`.
# RUN: echo 'MEMORY { ram (rwx) : XYZ = 0x8000 } }' > %t.script
# RUN: not ld.lld -o /dev/null -T %t.script %t.o 2>&1 | FileCheck --check-prefix=ERR1 %s
# ERR1: {{.*}}.script:1: expected one of: ORIGIN, org, or o
## Check bad `LENGTH`.
# RUN: echo 'MEMORY { ram (rwx) : ORIGIN = 0x8000, XYZ = 256K } }' > %t.script
# RUN: not ld.lld -o /dev/null -T %t.script %t.o 2>&1 | FileCheck --check-prefix=ERR2 %s
# ERR2: {{.*}}.script:1: expected one of: LENGTH, len, or l
## Check duplicate regions.
# RUN: echo 'MEMORY { ram (rwx) : o = 8, l = 256K ram (rx) : o = 0, l = 256K }' > %t.script
# RUN: not ld.lld -o /dev/null -T %t.script %t.o 2>&1 | FileCheck --check-prefix=ERR3 %s
# ERR3: {{.*}}.script:1: region 'ram' already defined
## Check no region available.
# RUN: echo 'MEMORY { ram (!rx) : ORIGIN = 0x8000, LENGTH = 256K } \
# RUN: SECTIONS { \
# RUN: .text : { *(.text) } \
# RUN: .data : { *(.data) } > ram \
# RUN: }' > %t.script
# RUN: not ld.lld -o /dev/null -T %t.script %t.o 2>&1 | FileCheck --check-prefix=ERR4 %s
# ERR4: error: no memory region specified for section '.text'
## Check undeclared region.
# RUN: echo 'SECTIONS { .text : { *(.text) } > ram }' > %t.script
# RUN: not ld.lld -o /dev/null -T %t.script %t.o 2>&1 | FileCheck --check-prefix=ERR5 %s
# ERR5: error: memory region 'ram' not declared
# RUN: echo 'SECTIONS { .text : { *(.text) } AT> ram }' > %t.script
# RUN: not ld.lld -o /dev/null -T %t.script %t.o 2>&1 | FileCheck --check-prefix=ERR5 %s
## Check region overflow.
# RUN: echo 'MEMORY { ram (rwx) : ORIGIN = 0, LENGTH = 2K } \
# RUN: SECTIONS { \
# RUN: .text : { *(.text) } > ram \
# RUN: .data : { *(.data) } > ram \
# RUN: }' > %t.script
# RUN: not ld.lld -o /dev/null -T %t.script %t.o 2>&1 | FileCheck --check-prefix=ERR6 %s
# ERR6: error: section '.data' will not fit in region 'ram': overflowed by 2049 bytes
## Check invalid region attributes.
# RUN: echo "MEMORY { ram (abc) : ORIGIN = 8000, LENGTH = 256K } }" > %t.script
# RUN: not ld.lld -o /dev/null -T %t.script %t.o 2>&1 | FileCheck --check-prefix=ERR7 %s
# 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 = ., 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 = 4094 } \
# 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=NOT_CONVERGE %s
# NOT_CONVERGE: error: address (0x14) of section '.text' does not converge
nop
.data
.zero 4096