[BOLT] Incorrect ADRP encoding on TLSDESC -> IE using mold
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
The TLSDESC stub looks like this with corresponding relocs:
```
Slot 1: adrp x0, :tlsdesc:var ; R_AARCH64_TLSDESC_ADR_PAGE21
Slot 2: ldr x1, [x0, :tlsdesc:var] ; R_AARCH64_TLSDESC_LD64_LO12
Slot 3: add x0, x0, :tlsdesc:var ; R_AARCH64_TLSDESC_ADD_LO12
Slot 4: blr x1 ; R_AARCH64_TLSDESC_CALL
```
Mold transforms this to IE under right conditions. However it has somewhat similar pattern as TLSDESC->LE we noted in https://github.com/llvm/llvm-project/issues/190366. This is how mold transforms the stub:
```
Slot 1: nop ;R_AARCH64_TLSDESC_ADR_PAGE21
Slot 2: nop ;R_AARCH64_TLSDESC_LD64_LO12
Slot 3: adrp x0, :gottprel: ;R_AARCH64_TLSDESC_ADD_LO12
Slot 4: ldr x0, [x0, ...] ;R_AARCH64_TLSDESC_CALL
```
The stale reloc on which the ADRP lands (R_AARCH64_TLSDESC_ADD_LO12) is not correctly handled for this transformation. Both TLSDESC_ADD_LO12 and TLSDESC_ADR_PAGE21 pass through adjustRelocation() to the __BOLT_got_zero fallback path,
which encodes the ADRP as __BOLT_got_zero + Rel.Value. For lld's stale reloc (TLSDESC_ADR_PAGE21), Rel.Value contains the GOT slot address, so this works. For mold's stale reloc (TLSDESC_ADD_LO12), Rel.Value is the TLS offset (0 for the
first variable), causing the ADRP to target page 0 instead of the GOT page.
For us to handle this correctly, we can return nullopt in adjustRelocation() to skip this reloc, but then symbolize it in tryAddingSymbolicOperand() using PC relative value after we handle stale TLS relocs.
Here is a test that shows the issue, synthesized to use lld and skips mold.
```
# REQUIRES: system-linux
#
# Check that BOLT handles TLSDESC -> IE relaxation with stale
# R_AARCH64_TLSDESC_ADD_LO12 on an ADRP (mold linker layout), while
# not regressing the normal GOTTPREL ADRP handling (lld layout).
#
# Build with correct GOTTPREL relocations, then swap the first
# GOTTPREL_PAGE21 to TLSDESC_ADD_LO12 to simulate mold's stale reloc.
#
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-linux %s -o %t.o
# RUN: ld.lld --emit-relocs -shared %t.o -o %t.so
# RUN: obj2yaml %t.so | sed '0,/R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21/{s/R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21/R_AARCH64_TLSDESC_ADD_LO12/}' | sed '0,/R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21/{s/R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21/R_AARCH64_TLSDESC_ADR_PAGE21/}' | yaml2obj -o %t.mold.so
# RUN: llvm-bolt %t.mold.so -o %t.bolt 2>&1 | FileCheck %s
# RUN: llvm-objdump -d --section=.text %t.bolt | FileCheck %s --check-prefix=DISASM
# CHECK-NOT: BOLT-ERROR
# Both ADRP instructions must target the GOT page, not page 0
# DISASM: <_start>:
# DISASM-NEXT: adrp x0, 0x{{[1-9][0-9a-f]*}}
# DISASM: adrp x0, 0x{{[1-9][0-9a-f]*}}
.text
.globl _start
.type _start, %function
_start:
// First TLS access - will become mold-style (TLSDESC_ADD_LO12 on ADRP)
adrp x0, :gottprel:tls_var
ldr x0, [x0, :gottprel_lo12:tls_var]
mrs x8, tpidr_el0
add x19, x8, x0
// Second TLS access - stays as lld-style (GOTTPREL on ADRP)
adrp x0, :gottprel:tls_var
ldr x0, [x0, :gottprel_lo12:tls_var]
mrs x8, tpidr_el0
add x20, x8, x0
ret
.size _start, .-_start
.section .tbss,"awT",@nobits
.globl tls_var
.type tls_var, @tls_object
tls_var:
.word 0
.size tls_var, 4
```
This shared lib has both styles of stubs. First is mold, second is lld. The nops by themselves
do not matter so I made a proper IE executable and replaced the reloc to simulate stale reloc.
```
a.so: file format elf64-littleaarch64
Disassembly of section .text:
Disassembly of section .text:
00000000000102b8 <_start>:
102b8: 90000080 adrp x0, 20000 <_start+0xfd48>
102b8: R_AARCH64_TLSDESC_ADD_LO12 tls_var
102bc: f941c800 ldr x0, [x0, #912]
102bc: R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC tls_var
102c0: d53bd048 mrs x8, tpidr_el0
102c4: 8b000113 add x19, x8, x0
102c8: 90000080 adrp x0, 20000 <_start+0xfd48>
102c8: R_AARCH64_TLSDESC_ADR_PAGE21 tls_var
102cc: f941c800 ldr x0, [x0, #912]
102cc: R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC tls_var
102d0: d53bd048 mrs x8, tpidr_el0
102d4: 8b000114 add x20, x8, x0
102d8: d65f03c0 ret
```
The disassembly shows that we use GOT page 0 for accessing this TLS variable.
```
a.bad: file format elf64-littleaarch64
Disassembly of section .bolt.org.text:
00000000000102b8 <.bolt.org.text>:
102b8: 90000080 adrp x0, 20000
102bc: f941c800 ldr x0, [x0, #912]
102c0: d53bd048 mrs x8, tpidr_el0
102c4: 8b000113 add x19, x8, x0
102c8: 90000080 adrp x0, 20000
102cc: f941c800 ldr x0, [x0, #912]
102d0: d53bd048 mrs x8, tpidr_el0
102d4: 8b000114 add x20, x8, x0
102d8: d65f03c0 ret
Disassembly of section .text:
0000000000400000 <_start>:
400000: 90ffe000 adrp x0, 0 <=========== BAD got address (0), should be 0x20000
400004: f941c800 ldr x0, [x0, #912]
400008: d53bd048 mrs x8, tpidr_el0
40000c: 8b000113 add x19, x8, x0
400010: 90ffe100 adrp x0, 20000
400014: f941c800 ldr x0, [x0, #912]
400018: d53bd048 mrs x8, tpidr_el0
40001c: 8b000114 add x20, x8, x0
400020: d65f03c0 ret
readelf -SW a.bad | grep got
[ 9] .got PROGBITS 0000000000020390 000390 000008 00 WA 0 0 8
```
I have a change that I will send PR for, I can see that if we correctly symbolize it,
we can reach the variable using the true GOT address:
```
a.good: file format elf64-littleaarch64
Disassembly of section .bolt.org.text:
00000000000102b8 <.bolt.org.text>:
102b8: 90000080 adrp x0, 20000
102bc: f941c800 ldr x0, [x0, #912]
102c0: d53bd048 mrs x8, tpidr_el0
102c4: 8b000113 add x19, x8, x0
102c8: 90000080 adrp x0, 20000
102cc: f941c800 ldr x0, [x0, #912]
102d0: d53bd048 mrs x8, tpidr_el0
102d4: 8b000114 add x20, x8, x0
102d8: d65f03c0 ret
Disassembly of section .text:
0000000000400000 <_start>:
400000: 90ffe100 adrp x0, 20000
400004: f941c800 ldr x0, [x0, #912]
400008: d53bd048 mrs x8, tpidr_el0
40000c: 8b000113 add x19, x8, x0
400010: 90ffe100 adrp x0, 20000
400014: f941c800 ldr x0, [x0, #912]
400018: d53bd048 mrs x8, tpidr_el0
40001c: 8b000114 add x20, x8, x0
400020: d65f03c0 ret
readelf -SW a.good |grep got
[ 9] .got PROGBITS 0000000000020390 000390 000008 00 WA 0 0 8
```
Contributor guide
Assessment
This issue has not been assessed yet.