| Issue |
171475
|
| Summary |
[ELF][AArch64][PAC] .relr.auth.dyn -> .rela.dyn movement is unsound
|
| Labels |
lld,
lld:ELF
|
| Assignees |
|
| Reporter |
jrtc27
|
The current implementation moves dynamic relocations *after* the relocation sections have been finalised, which means their sizes change. For some sections this is supported, but for relocation sections this is dodgy. In particular, there are four issues.
Firstly, __rela_iplt_end's value is not updated to include the extra relocations. Secondly, if .rela.dyn is initially empty, __rela_iplt_start/end don't even point to .rela.dyn, they point to the ELF header. Both these issues are perhaps not the end of the world, because __rela_iplt_start/end are only for static PDEs, and there are no equivalent encapsulation symbols as far as I know for .relr.dyn or .relr.auth.dyn, so that LLD configuration is a stupid one to use (though I suppose if one were serious about RELR one would define encapsulation symbols for the latter; the former isn't going to be present today in static PDEs in any context I can think of).
However, the third and fourth issues applies to any dynamic object (non-static executable, or DSO). The size of .dynamic is determined once, at finalise time, and the number of dynamic entries varies based on which of .relr.auth.dyn and .rela.dyn are present. For the third issue, if .rela.dyn is initially empty at finalise time, and a non-trivial subset of .relr.auth.dyn (i.e. at least one but not all) are moved to .rela.dyn, the number of dynamic tags emitted by writeTo will grow, but the section size will not. This means the tags spill off the end of the section into whatever sections follow (if any); llvm-readelf won't show these because it stops based on the section size. For the fourth issue, if both .rela.dyn and .relr.auth.dyn are initially non-empty, and all of .relr.auth.dyn can be moved to .rela.dyn, then the number of dynamic tags emitted by writeTo will shrink, but the section size will not, so it will be padded with a bunch of redundant DT_NULLs. This is probably harmless though?
Although these are all corner cases that probably don't occur in the real world, they're trivial to write tests for to reproduce them, so I'm not sure the implementation is robust enough to be exposing without jumping through hoops.
Test cases for each of these are below (testing the broken behaviour, so currently pass, rather than testing for the correct behaviour, which would make them currently fail).
### Stale __rela_iplt_end
```asm
# REQUIRES: aarch64
# RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
# RUN: ld.lld -static -z pack-relative-relocs %t.o -o %t
# RUN: llvm-readelf -S -s %t | FileCheck %s
# CHECK: .rela.dyn RELA 00000000002001c8 0001c8 000030 18 A 0 0 8
# CHECK: 00000000002001c8 0 NOTYPE LOCAL HIDDEN 1 __rela_iplt_start
## XXX: Only covers 0x18 bytes for one relocation, not 0x30 bytes for both
# CHECK: 00000000002001e0 0 NOTYPE LOCAL HIDDEN 1 __rela_iplt_end
adrp x0, __rela_iplt_start
adrp x0, __rela_iplt_end
.data
.balign 8
foo:
## Will be moved to .rela.dyn
.quad foo+0x100000000@AUTH(da,42)
## Extra relocation in a section with alignment 1 to force it into .rela.dyn
## up-front.
.section .data.rel.ro, "aw"
.quad foo@AUTH(da,42)
```
### __rela_iplt_start/end point to ELF header
```asm
# REQUIRES: aarch64
# RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
# RUN: ld.lld -static -z pack-relative-relocs %t.o -o %t
# RUN: llvm-readelf -S -s %t | FileCheck %s
# CHECK: .rela.dyn RELA 0000000000200158 000158 000018 18 A 0 0 8
## XXX: ELF header's address
# CHECK: 0000000000200000 0 NOTYPE LOCAL HIDDEN 1 __rela_iplt_start
# CHECK: 0000000000200000 0 NOTYPE LOCAL HIDDEN 1 __rela_iplt_end
adrp x0, __rela_iplt_start
adrp x0, __rela_iplt_end
.data
.balign 8
foo:
## Will be moved to .rela.dyn
.quad foo+0x100000000@AUTH(da,42)
```
### .dynamic grows
```asm
# REQUIRES: aarch64
# RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
# RUN: ld.lld -pie -z pack-relative-relocs %t.o -o %t
# RUN: llvm-readelf -S -d -s %t | FileCheck %s
# RUN: llvm-readelf -S -d -s %t 2>&1 >/dev/null | FileCheck %s --check-prefix=WARN
# CHECK: .rela.dyn RELA 0000000000000248 000248 000018 18 A 0 0 8
# CHECK: .relr.auth.dyn AARCH64_AUTH_RELR 0000000000000260 000260 000008 08 A 0 0 8
# CHECK: .dynamic DYNAMIC 0000000000020268 000268 0000c0 10 WA 4 0 8
## XXX: Spilled into next section (llvm-readelf output truncated to size in
## section header). GNU_HASH, HASH and NULL pushed off the end by RELA, RELASZ
## and RELAENT.
# CHECK: Dynamic section at offset 0x268 contains 12 entries:
# CHECK: Tag Type Name/Value
# CHECK: 0x000000006ffffffb (FLAGS_1) PIE
# CHECK: 0x0000000000000015 (DEBUG) 0x0
# CHECK: 0x0000000000000007 (RELA) 0x248
# CHECK: 0x0000000000000008 (RELASZ) 24 (bytes)
# CHECK: 0x0000000000000009 (RELAENT) 24 (bytes)
# CHECK: 0x0000000070000012 (AARCH64_AUTH_RELR) 0x260
# CHECK: 0x0000000070000011 (AARCH64_AUTH_RELRSZ) 8 (bytes)
# CHECK: 0x0000000070000013 (AARCH64_AUTH_RELRENT) 8 (bytes)
# CHECK: 0x0000000000000006 (SYMTAB) 0x200
# CHECK: 0x000000000000000b (SYMENT) 24 (bytes)
# CHECK: 0x0000000000000005 (STRTAB) 0x244
# CHECK: 0x000000000000000a (STRSZ) 1 (bytes)
# XXX: HASH's d_val overwrote null symbol's st_name
# WARN: warning: {{.*}}: st_name (0x234) is past the end of the string table of size 0x11
# CHECK: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND <?>
.data
.balign 8
foo:
## Can stay in .relr.auth.dyn
.quad foo@AUTH(da,42)
## Will be moved to .rela.dyn
.quad foo+0x100000000@AUTH(da,42)
```
### .dynamic shrinks
```asm
# REQUIRES: aarch64
# RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
# RUN: ld.lld -pie -z pack-relative-relocs %t.o -o %t
# RUN: llvm-readelf -S -d -s -x.dynamic %t | FileCheck %s
# CHECK: .rela.dyn RELA 0000000000000248 000248 000030 18 A 1 0 8
# CHECK: .relr.auth.dyn AARCH64_AUTH_RELR 0000000000000278 000278 000000 08 A 0 0 8
# CHECK: .text PROGBITS 0000000000010278 000278 000000 00 AX 0 0 4
# CHECK: .data.rel.ro PROGBITS 0000000000020278 000278 000008 00 WA 0 0 1
## XXX: Claims 15 entries but only 12 present (extra NULLs)
# CHECK: .dynamic DYNAMIC 0000000000020280 000280 0000f0 10 WA 4 0 8
# CHECK: Dynamic section at offset 0x280 contains 12 entries:
# CHECK: Tag Type Name/Value
# CHECK: 0x000000006ffffffb (FLAGS_1) PIE
# CHECK: 0x0000000000000015 (DEBUG) 0x0
# CHECK: 0x0000000000000007 (RELA) 0x248
# CHECK: 0x0000000000000008 (RELASZ) 48 (bytes)
# CHECK: 0x0000000000000009 (RELAENT) 24 (bytes)
# CHECK: 0x0000000000000006 (SYMTAB) 0x200
# CHECK: 0x000000000000000b (SYMENT) 24 (bytes)
# CHECK: 0x0000000000000005 (STRTAB) 0x244
# CHECK: 0x000000000000000a (STRSZ) 1 (bytes)
# CHECK: 0x000000006ffffef5 (GNU_HASH) 0x218
# CHECK: 0x0000000000000004 (HASH) 0x234
# CHECK: 0x0000000000000000 (NULL) 0x0
## All 4 NULLs, not just 1 (see above)
# CHECK: 0x00020330 00000000 00000000 00000000 00000000 ................
# CHECK: 0x00020340 00000000 00000000 00000000 00000000 ................
# CHECK: 0x00020350 00000000 00000000 00000000 00000000 ................
# CHECK: 0x00020360 00000000 00000000 00000000 00000000 ................
.data
.balign 8
foo:
## Will be moved to .rela.dyn
.quad foo+0x100000000@AUTH(da,42)
## Extra relocation in a section with alignment 1 to force it into .rela.dyn
## up-front.
.section .data.rel.ro, "aw"
.quad foo@AUTH(da,42)
```
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs