THREAD_SET_STACK_GUARD reads the thread pointer via THREAD_SELF
(__builtin_thread_pointer), which the compiler treats as invariant. In the
static startup path the thread pointer is installed by the __libc_setup_tls
call that immediately precedes the guard store, so the read must stay below
it. The existing barrier only clobbered the access registers a0/a1, which
creates no dependency on the call, so the compiler could move the whole
barrier and read above __libc_setup_tls.
This is sensitive to instruction scheduling and recent TLS startup
changes exposed it on s390x.
Add a "memory" clobber to the barrier so it is tied to the call's memory
effects and cannot be hoisted above it. The macro is shared with the
dynamic loader, so both startup paths are covered.
I checked on s390x-linux-gnu build for arch5, arch8, arch9, and arch11
by running the elf tests on qemu system (kernel 6.1.0).
Reviewed-by: Stefan Liebler <stli@linux.ibm.com>
#define THREAD_SET_STACK_GUARD(value) \
do \
{ \
- __asm__ __volatile__ ("" : : : "a0", "a1"); \
+ __asm__ __volatile__ ("" : : : "a0", "a1", "memory"); \
THREAD_SETMEM (THREAD_SELF, header.stack_guard, value); \
} \
while (0)