rtld/arm: fix initial-exec (IE) thread-local storage relocation

net/frr[89] revealed an interesting edge-case on arm when dynamically
linking a shared library that declares more than one static TLS variable
with at least one  using the "initial-exec" TLS model. In the case
of frr[89], this library was libfrr.so which essentially does the
following:

	#include <stdio.h>

	#include "lib.h"

	static __thread int *a
		__attribute__((tls_model("initial-exec")));

	void lib_test()
	{
		static __thread int b = -1;

		printf("&a = %p\n", &a);
		printf(" a = %p\n", a);

		printf("\n");

		printf("&b = %p\n", &b);
		printf(" b = %d\n", b);
	}

Allocates a file scoped `static __thread` pointer with
tls_model("initial-exec") and later a block scoped TLS int. Notice in
the above minimal reproducer, `b == -1`. The relocation process does
the wrong thing and ends up pointing both `a` and `b` at the same place
in memory.

The output of the above in the broken state is:

	&a = 0x4009c018
	 a = 0xffffffff

	&b = 0x4009c018
	 b = -1

With the patch applied, the output becomes:

	&a = 0x4009c01c
	 a = 0x0

	&b = 0x4009c018
	 b = -1

Reviewed by:	kib
Sponsored by:	Rubicon Communications, LLC ("Netgate")
Differential Revision:	https://reviews.freebsd.org/D42415/
This commit is contained in:
R. Christian McDonald 2023-11-03 13:56:58 +01:00 committed by Kristof Provost
parent 6c3ae01cc7
commit 98fd69f009

View file

@ -280,10 +280,13 @@ reloc_nonplt_object(Obj_Entry *obj, const Elf_Rel *rel, SymCache *cache,
return -1;
tmp = (Elf_Addr)def->st_value + defobj->tlsoffset;
if (__predict_true(RELOC_ALIGNED_P(where)))
if (__predict_true(RELOC_ALIGNED_P(where))) {
tmp += *where;
*where = tmp;
else
} else {
tmp += load_ptr(where);
store_ptr(where, tmp);
}
dbg("TLS_TPOFF32 %s in %s --> %p",
obj->strtab + obj->symtab[symnum].st_name,
obj->path, (void *)tmp);