ldso: aarch64 dropped the addend of a TPREL relocation
R_AARCH64_TLS_TPREL was resolved as symbol_addr + l_tls_offset, without
the addend that every other case in the same function adds. For a global
symbol that costs nothing, because the linker leaves the addend at zero
there. For a local one it is the whole answer: a static __thread variable
has no symbol table entry, so symbol_addr is zero and the offset inside
the module's TLS block travels in the addend. Every such variable
therefore resolved to the start of its module's block.
tst-tlsmod7.so makes it plain. local2 sits at 0x58:
53: 0000000000000058 16 TLS LOCAL DEFAULT 13 local2
1ffa0 R_AARCH64_TLS_TPREL64 58
and a1 sits at 0. So f6a, which returns &local2, handed back a1 -- the
test asks for {19,20,21} and got {4,5,6}. The same in tst-tlsmod10.so
with a2. tst-tls10 and tst-tls11 then abort, at -O0 and at -O2.
glibc's sysdeps/aarch64/dl-machine.h adds the addend, uClibc-ng's riscv32
and riscv64 add it, and the R_AARCH64_TLS_DTPREL case right below adds it
too -- so aarch64's TPREL was the odd one out.
Reproduced under qemu-aarch64 against a freshly built NPTL sysroot: both
tests abort with the loader as it was and pass with the addend added, and
an instrumented copy shows f5a, f6a, f9a and f10a all returning their own
variables again.
Signed-off-by: Ramin Moussavi <lordrasmus@gmail.com>