Re: [PATCH 1/5] glibc: Perform rseq(2) registration at C startup and thread creation (v10)
From: Mathieu Desnoyers
Date: Thu May 30 2019 - 17:00:24 EST
----- On May 29, 2019, at 11:45 AM, Mathieu Desnoyers mathieu.desnoyers@xxxxxxxxxxxx wrote:
> ----- On May 27, 2019, at 3:27 PM, Mathieu Desnoyers
> mathieu.desnoyers@xxxxxxxxxxxx wrote:
>> ----- On May 27, 2019, at 7:19 AM, Florian Weimer fweimer@xxxxxxxxxx wrote:
>>> Furthermore, the reference to ELF constructors is misleading. I believe
>>> the code you added to __libc_start_main to initialize __rseq_handled and
>>> register __seq_abi with the kernel runs *after* ELF constructors have
>>> executed (and not at all if the main program is written in Go, alas).
>>> All initialization activity for the shared case needs to happen in
>>> elf/rtld.c or called from there, probably as part of the security
>>> initialization code or thereabouts.
>> in elf/rtld.c:dl_main() we have the following code:
>> /* We do not initialize any of the TLS functionality unless any of the
>> initial modules uses TLS. This makes dynamic loading of modules with
>> TLS impossible, but to support it requires either eagerly doing setup
>> now or lazily doing it later. Doing it now makes us incompatible with
>> an old kernel that can't perform TLS_INIT_TP, even if no TLS is ever
>> used. Trying to do it lazily is too hairy to try when there could be
>> multiple threads (from a non-TLS-using libpthread). */
>> bool was_tls_init_tp_called = tls_init_tp_called;
>> if (tcbp == NULL)
>> tcbp = init_tls ();
>> If I understand your point correctly, I should move the rseq_init() and
>> rseq_register_current_thread() for the SHARED case just after this
>> initialization, otherwise calling those from LIBC_START_MAIN() is too
>> late and it runs after initial modules constructors (or not at all for
>> Go). However, this means glibc will start using TLS internally. I'm
>> concerned that this is not quite in line with the above comment which
>> states that TLS is not initialized if no initial modules use TLS.
>> For the !SHARED use-case, if my understanding is correct, I should keep
>> rseq_init() and rseq_register_current_thread() calls within LIBC_START_MAIN().
> I've moved the rseq initialization for SHARED case to the very end of
> elf/rtld.c:init_tls(), and get the following error on make check:
> Generating locale am_ET.UTF-8: this might take a while...
> Inconsistency detected by ld.so: get-dynamic-info.h: 143: elf_get_dynamic_info:
> Assertion `info[DT_FLAGS] == NULL || (info[DT_FLAGS]->d_un.d_val &
> ~DF_BIND_NOW) == 0' failed!
> Charmap: "UTF-8" Inputfile: "am_ET" Outputdir: "am_ET.UTF-8" failed
> /bin/sh: 4: cannot create
> Directory nonexistent
> This error goes away if I comment out the call to rseq_register_current_thread
> which touches the __rseq_abi __thread variable and issues a system call.
> Currently, the __rseq_abi __thread variable is within
> sysdeps/unix/sysv/linux/rseq-sym.c, which is added to the
> sysdep_routines within sysdeps/unix/sysv/linux/Makefile. I
> suspect it may need to be moved elsewhere.
> Any thoughts on how to solve this ?
I found that it's because touching a __thread variable from
ld-linux-x86-64.so.2 ends up setting the DF_STATIC_TLS flag
for that .so, which is really not expected.
Even if I tweak the assert to make it more lenient there,
touching the __thread variable ends up triggering a SIGFPE.
So rather than touching the TLS from ld-linux-x86-64.so.2,
I've rather experimented with moving the rseq initialization
for both SHARED and !SHARED cases to a library constructor
Are you aware of any downside to this approach ?
diff --git a/csu/libc-start.c b/csu/libc-start.c
index 5d9c3675fa..9755ed5467 100644
@@ -22,6 +22,7 @@
@@ -81,6 +82,14 @@ apply_irel (void)
+void __rseq_libc_init (void)
+ rseq_init ();
+ /* Register rseq ABI to the kernel. */
+ (void) rseq_register_current_thread ();
# ifdef LIBC_START_DISABLE_INLINE