Просмотр исходного кода

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>
ramin 3 дней назад
Родитель
Сommit
4a5d432347
1 измененных файлов с 5 добавлено и 1 удалено
  1. 5 1
      ldso/ldso/aarch64/elfinterp.c

+ 5 - 1
ldso/ldso/aarch64/elfinterp.c

@@ -216,7 +216,11 @@ _dl_do_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope,
 #if defined USE_TLS && USE_TLS
 		case R_AARCH64_TLS_TPREL:
 			CHECK_STATIC_TLS ((struct link_map *) tls_tpnt);
-			*reloc_addr = (symbol_addr + tls_tpnt->l_tls_offset);
+			/* A local TLS symbol has no symbol table entry, so the
+			   offset inside the module's block travels in the
+			   addend and symbol_addr is zero.  */
+			*reloc_addr = (symbol_addr + rpnt->r_addend
+				       + tls_tpnt->l_tls_offset);
 			break;
 		case R_AARCH64_TLSDESC:
 				{