mirror of
git://sourceware.org/git/glibc.git
synced 2026-09-08 23:58:31 +08:00
An IFUNC resolver in a DSO that is being loaded by dlopen is allowed
to read its own TLS storage during the resolver call. After
af34b1376a ("elf: Initialize static TLS before relocation processing
(BZ 34164)") that works for the initial-exec model on every supported
architecture.
However, it does not work for the dynamic-TLS path (on both -mtls-dialect mode,
if the ABI supports both). Both lookup paths index the calling thread's
DTV by the new module's l_tls_modid and, on miss, walk
GL(dl_tls_dtv_slotinfo_list) to discover the module and lazily allocate
its TLS block. The just-loaded DSO is however not yet in that list when
its resolver fires, so the lookup faults inside dlopen. This is the
direct dlopen analog of BZ 34164.
The solution is to reorder dl_open_worker_begin so the slotinfo install
happens before the relocation pass. The new order is:
1. resize_scopes, resize_tls_slotinfo, add_to_global_resize
(unchanged, still recoverable).
2. update_tls_slotinfo: register the new modules in slotinfo, bump
dl_tls_generation, initialise their static TLS images.
3. Relocate the new objects. IFUNC resolvers can now read their
own DSO's __thread storage via any TLS model.
4. Demarcation point.
5. update_scopes, _dl_find_object_update.
Checked on aarch64-linux-gnu and x86_64-linux-gnu.
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
90 lines
2.8 KiB
C
90 lines
2.8 KiB
C
/* Check if dynamic-TLS variables (global-dynamic, local-dynamic) are
|
|
correctly initialised in IFUNC resolvers reached via dlopen.
|
|
Copyright (C) 2026 Free Software Foundation, Inc.
|
|
This file is part of the GNU C Library.
|
|
|
|
The GNU C Library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with the GNU C Library; if not, see
|
|
<https://www.gnu.org/licenses/>. */
|
|
|
|
/* This test dlopens three modules covering the dynamic-TLS access variants:
|
|
|
|
gd-lib file-local 'sentinel' + tls_model("global-dynamic")
|
|
ld-lib file-local 'sentinel' + tls_model("local-dynamic")
|
|
gd-global-lib external 'sentinel' + tls_model("global-dynamic")
|
|
|
|
In each case the IFUNC resolver's read of 'sentinel' must traverse the
|
|
dynamic-TLS resolution path at the moment the resolver fires during
|
|
dlopen-time relocation.
|
|
|
|
Each variant additionally calls the resolved IFUNC from a thread spawned
|
|
after dlopen, to verify per-thread DTV propagation for the newly-loaded
|
|
module. */
|
|
|
|
#include <support/check.h>
|
|
#include <support/xdlfcn.h>
|
|
#include <support/xthread.h>
|
|
|
|
#define SENTINEL 0x5A5A1234
|
|
|
|
struct ifunc_handles
|
|
{
|
|
int (*get_last_seen_sentinel) (void);
|
|
int (**fptr) (void);
|
|
int (*ifunc_tls) (void);
|
|
};
|
|
|
|
static void *
|
|
ifunc_caller (void *arg)
|
|
{
|
|
struct ifunc_handles *h = arg;
|
|
TEST_COMPARE ((*h->fptr) (), SENTINEL);
|
|
TEST_COMPARE (h->ifunc_tls (), SENTINEL);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
test_lib (const char *soname)
|
|
{
|
|
void *handle = xdlopen (soname, RTLD_LAZY | RTLD_LOCAL);
|
|
|
|
struct ifunc_handles h;
|
|
h.get_last_seen_sentinel = xdlsym (handle, "get_last_seen_sentinel");
|
|
h.fptr = xdlsym (handle, "fptr");
|
|
h.ifunc_tls = xdlsym (handle, "ifunc_tls");
|
|
|
|
TEST_COMPARE (h.get_last_seen_sentinel (), SENTINEL);
|
|
|
|
TEST_VERIFY (*h.fptr != NULL);
|
|
TEST_COMPARE ((*h.fptr) (), SENTINEL);
|
|
TEST_COMPARE (h.ifunc_tls (), SENTINEL);
|
|
|
|
/* From a thread spawned *after* the dlopen, which exercises DTV propagation
|
|
for the new module into a fresh TCB. */
|
|
pthread_t consumer = xpthread_create (NULL, ifunc_caller, &h);
|
|
xpthread_join (consumer);
|
|
|
|
xdlclose (handle);
|
|
}
|
|
|
|
static int
|
|
do_test (void)
|
|
{
|
|
test_lib ("tst-ifunc-tls-init-gd-lib.so");
|
|
test_lib ("tst-ifunc-tls-init-ld-lib.so");
|
|
test_lib ("tst-ifunc-tls-init-gd-global-lib.so");
|
|
return 0;
|
|
}
|
|
|
|
#include <support/test-driver.c>
|