From patchwork Wed May 20 20:07:10 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella Netto X-Patchwork-Id: 135368 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id BB5894BB5903 for ; Wed, 20 May 2026 20:08:01 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org BB5894BB5903 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256 header.s=google header.b=NWnQPcyc X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-vs1-xe2a.google.com (mail-vs1-xe2a.google.com [IPv6:2607:f8b0:4864:20::e2a]) by sourceware.org (Postfix) with ESMTPS id CE7354BB5903 for ; Wed, 20 May 2026 20:07:26 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org CE7354BB5903 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=linaro.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org CE7354BB5903 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::e2a ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1779307647; cv=none; b=uinoyLSP3jn2jSsJyUoCKImmVce4+N9TTOdV7JhB3mkKJcdlXeQdO74JlD3qo6OL0NxQU7MegOFEuekHxVcc35lW0G0XQRR2+r8WMa/WBvxX/rVBA0RikJVGv+6Lti9D/iW2ylYX8a8twFdGOW1s5BdxBooqChauro/pfGA9xWE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1779307647; c=relaxed/simple; bh=aBSjYcroBRgt1wdCUKWrtaXTWRQ6b9h9JOjhfphs+OE=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=F+Gkub1MwskdLBNTO6u8DYYl3LhMFBMfQvyjVuFPQnqV/Xoi2Ebl1HbIMLNWXwME1LbkEU4jZv07oZOKJhQ8Br5ZKoTl5Pk1ma26iDd4TaCh02XYPXIE4CnTSsZIrpqCt1mhc9Dl1gEV+FuAUJwUcaT2j+hpNq22mzxgXQ6f20w= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256 header.s=google header.b=NWnQPcyc DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org CE7354BB5903 Received: by mail-vs1-xe2a.google.com with SMTP id ada2fe7eead31-628086439b6so4752593137.2 for ; Wed, 20 May 2026 13:07:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1779307646; x=1779912446; darn=sourceware.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=PrYzvoyYRTHaDYR78ha0YhRBWIT/HNXQBVBVfa1uHHQ=; b=NWnQPcycdQd6npssBiek9DL1zAkx36fjZdT8GxwSpR73ucu/qUFwPC9sXpU65l08yF TTKa97gRlvrxE4HcL5+CmABStfaYNy8xj+k3KXOPbdo1IEPREDmvma+a6M9UsB8VOVYA LCBFCC4sSOETEZGn2xpitpSQrvZhiadJ01ISWcgZ4WNo++JWmlxEfZOMP7GTqA/KSkVu w/fNzdIHGJAgT6aqwyvcx0LFXFHGghn5ICybnzQs+p6yNIGFLkjqrZGhDAu9jmV3SCnM Nxt8sJpBs5dYlImp90bMVwDF2E0iEC3hZRWcN/ABssgZ5dDB8iVlWYBCegsmRggPw57I Mx+g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779307646; x=1779912446; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=PrYzvoyYRTHaDYR78ha0YhRBWIT/HNXQBVBVfa1uHHQ=; b=LMepfrfWrPzZTq8svAcCf8q8rGjOHOE+YWTowIk2rDMqOF+hYD2ExjowWaGDKjfNRW 0ZKVNIKYcbR0HiogVZ5W/BoJmkp3aZyajn9wLNOQvuqZ5O5waTi7plALBuDuX1lkmU0x uvNieIQW0vvv5yvmC+tohau4W3shXjMPXGjLcyLk7S8P7BfDu/vN1sveEBQTiW4P+kIS VK+SZWX6a+l5JfuvLFXh5//Fr2RIaQqMka4j5Kih0imJssGOmHZHevRMgN1Vo50HhqUt AJyhZENv3pe4Qrir+yShrRMzvpZFy5Y4JYLL1yLdMPAuQISkcmbs4hepr7fi+jJrh4su zJqA== X-Gm-Message-State: AOJu0YyQf8ipDqQwB5ztQfBMme60Sysxa43ap+jMnRX8EM+kCJnWWTV4 AF6Wxt8erOc69jaqJYfNpakpTWZ8TxrM4y1k6Mvji4veNS1V3Hgl4xDwYLGri/BjWv5xADvjFvw MSPgF X-Gm-Gg: Acq92OHuHPqU+JMvt+bQGhDInA7EiktQO7E0VKezGKQ5L7GUR5ZfHXVwanH4MJVe0qa xqM9f5fgylLBocTllZTdB8hzKDZakx68nxCa4IA+jXeHJnGPl74v0IDQ96skPzekAtLQdwjy/rU EIyhre+1w1hVL87MUOIIs0PNU/dUWcAK2WbNpLgERv9FSwy189vwbtAp6DZS3CsGy7Y9rDKHhr0 B/hgWfFSkkOZX0P5UqnH4YfrVWWser6zMJmOGDkVGUPVtj15OjnWkfBkXzCxoXhftY6Z1rxo/sT i7guqg/BMaQLvA/oVN2lHrisrDQeSsnBC84H4Vt9IUeqOkKMZdrSALmASKyvrUKrOccQ/7IZd9p 23fGOsNHGpjrxXDSU+gqNWjrkMEdNh5/3LBkMJjgPbb8fPv6iZhX0P248L4H7I+m570FVjDvfDY ZEaZKrgwuBKq4NrVCpTQbL4WdrK/+gkUjYcUy/0jMxvwZf3w== X-Received: by 2002:a05:6102:5110:b0:631:81d6:e158 with SMTP id ada2fe7eead31-6739b40a849mr53079137.27.1779307645714; Wed, 20 May 2026 13:07:25 -0700 (PDT) Received: from mandiga.. ([2804:1b3:a7c1:d905:fd82:5d3f:8372:aad6]) by smtp.gmail.com with ESMTPSA id ada2fe7eead31-63cea473caasm10202534137.12.2026.05.20.13.07.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 May 2026 13:07:25 -0700 (PDT) From: Adhemerval Zanella To: libc-alpha@sourceware.org Cc: "H . J . Lu" Subject: [PATCH] elf: Initialize static TLS before relocation processing Date: Wed, 20 May 2026 17:07:10 -0300 Message-ID: <20260520200720.1241978-1-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.43.0 MIME-Version: 1.0 X-Spam-Status: No, score=-12.6 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org An IFUNC resolver firing during dynamic linker relocation reads its DSO's __thread storage from a zero-filled slot: init_tls() allocates the static TLS block zero-filled, but .tdata is not copied in until the trailing _dl_allocate_tls_init at the end of dl_main, long after the per-object phase 2 resolvers from commit 63b31c05a8a901 have run. A resolver that *writes* TLS is even worse off -- the write is clobbered by that same trailing copy. dl_main (elf/rtld.c): populate the DTV slotinfo, bump dl_tls_generation, and call _dl_allocate_tls_init right after init_tls(), before the relocation loop. _dl_try_allocate_static_tls (elf/dl-reloc.c): drop the "defer-if-not-relocated" branch and always run _dl_init_static_tls inline, so a CHECK_STATIC_TLS allocation triggered mid-relocation initialises the slot before the same object's phase 2 fires. The new tests cheks some scenariosn: elf/tst-ifunc-tls-init resolver reads its DSO's IE TLS. elf/tst-ifunc-tls-init-dlopen same, via dlopen. elf/tst-ifunc-tls-write resolver write to TLS must survive to main. Checked on aarch64-linux-gnu, x86_64-linux-gnu, and i686-linux-gnu. --- elf/Makefile | 10 ++++ elf/dl-reloc.c | 27 +++++---- elf/rtld.c | 31 ++++++---- elf/tst-ifunc-tls-init-dlopen-lib.c | 56 ++++++++++++++++++ elf/tst-ifunc-tls-init-lib-skeleton.c | 57 +++++++++++++++++++ elf/tst-ifunc-tls-init-lib1.c | 1 + elf/tst-ifunc-tls-init-lib2.c | 1 + elf/tst-ifunc-tls-init.c | 81 +++++++++++++++++++++++++++ elf/tst-ifunc-tls-write-lib.c | 54 ++++++++++++++++++ elf/tst-ifunc-tls-write.c | 34 +++++++++++ 10 files changed, 328 insertions(+), 24 deletions(-) create mode 100644 elf/tst-ifunc-tls-init-dlopen-lib.c create mode 100644 elf/tst-ifunc-tls-init-lib-skeleton.c create mode 100644 elf/tst-ifunc-tls-init-lib1.c create mode 100644 elf/tst-ifunc-tls-init-lib2.c create mode 100644 elf/tst-ifunc-tls-init.c create mode 100644 elf/tst-ifunc-tls-write-lib.c create mode 100644 elf/tst-ifunc-tls-write.c diff --git a/elf/Makefile b/elf/Makefile index aef13b73ca5..ff42eb32d41 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -1268,6 +1268,8 @@ tests += \ tst-ifunc-plt-bindnow \ tst-ifunc-plt-dlopen \ tst-ifunc-plt-dlopen-bindnow \ + tst-ifunc-tls-init \ + tst-ifunc-tls-write \ # tests # Note: sysdeps/x86_64/ifuncmain8.c uses ifuncmain8. tests-internal += \ @@ -1332,6 +1334,9 @@ modules-names += \ ifuncmod6 \ tst-ifunc-plt-dep \ tst-ifunc-plt-lib \ + tst-ifunc-tls-init-lib1 \ + tst-ifunc-tls-init-lib2 \ + tst-ifunc-tls-write-lib \ # modules-names ifeq (no,$(with-lld)) modules-names += ifuncmod5 @@ -2468,6 +2473,11 @@ $(objpfx)tst-ifunc-plt-dlopen.out: \ $(objpfx)tst-ifunc-plt-dlopen-bindnow.out: \ $(objpfx)tst-ifunc-plt-lib.so $(objpfx)tst-ifunc-plt-dep.so +$(objpfx)tst-ifunc-tls-init: $(objpfx)tst-ifunc-tls-init-lib1.so +$(objpfx)tst-ifunc-tls-init.out: \ + $(objpfx)tst-ifunc-tls-init-lib2.so +$(objpfx)tst-ifunc-tls-write: $(objpfx)tst-ifunc-tls-write-lib.so + $(objpfx)tst-unique1.out: $(objpfx)tst-unique1mod1.so \ $(objpfx)tst-unique1mod2.so diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c index f1a432ac099..67299400cad 100644 --- a/elf/dl-reloc.c +++ b/elf/dl-reloc.c @@ -107,22 +107,21 @@ _dl_try_allocate_static_tls (struct link_map *map, bool optional) # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" #endif - /* If the object is not yet relocated we cannot initialize the - static TLS region. Delay it. */ - if (map->l_real->l_relocated) - { + /* Initialise the static TLS region, the map may not yet be l_relocated (a + TLS reloc inside the relocation loop triggered the allocation), but + _dl_init_static_tls only writes .tdata into the static TLS slot, which is + independent of relocation state. + Doing this inline ensures any IFUNC resolver that fires laterin the same + object's relocation pass sees an initialised TLS slot, and the + post-relocation TLS init loop in dl_open_worker_begin becomes a no-op for + this map. */ #ifdef SHARED - /* Update the DTV of the current thread. Note: GL(dl_load_tls_lock) - is held here so normal load of the generation counter is valid. */ - if (__builtin_expect (THREAD_DTV()[0].counter != GL(dl_tls_generation), - 0)) - (void) _dl_update_slotinfo (map->l_tls_modid, GL(dl_tls_generation)); + /* Update the DTV of the current thread. Note: GL(dl_load_tls_lock) + is held here so normal load of the generation counter is valid. */ + if (__glibc_unlikely (THREAD_DTV()[0].counter != GL(dl_tls_generation))) + _dl_update_slotinfo (map->l_tls_modid, GL(dl_tls_generation)); #endif - - _dl_init_static_tls (map); - } - else - map->l_need_tls_init = 1; + _dl_init_static_tls (map); return 0; } diff --git a/elf/rtld.c b/elf/rtld.c index e926ec73e49..12e1b4dd71f 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -2263,6 +2263,26 @@ dl_main (const ElfW(Phdr) *phdr, rtld_timer_accum (&relocate_time, start); } + /* Populate the DTV slotinfo and copy each TLS module's into thestatic TLS + block *before* the relocation loop. IFUNC resolvers fired during phase 2 + of the per-object two-phase scheme therefore observe initialised TLS. */ + if (__rtld_tls_init_tp_called) + { + unsigned int i = main_map->l_searchlist.r_nlist; + while (i-- > 0) + { + struct link_map *l = main_map->l_initfini[i]; + if (l->l_tls_blocksize != 0) + _dl_add_to_slotinfo (l, true); + } + /* _dl_add_to_slotinfo records gen = dl_tls_generation + 1, and + _dl_allocate_tls_init asserts gen <= dl_tls_generation, so bump + the generation before init. */ + if (GL(dl_tls_max_dtv_idx) > 0) + ++GL(dl_tls_generation); + _dl_allocate_tls_init (tcbp, true); + } + RTLD_TIMING_VAR (start); rtld_timer_start (&start); { @@ -2286,10 +2306,6 @@ dl_main (const ElfW(Phdr) *phdr, _dl_relocate_object (l, l->l_scope, GLRO(dl_lazy) ? RTLD_LAZY : 0, consider_profiling); - - /* Add object to slot information data if necessary. */ - if (l->l_tls_blocksize != 0 && __rtld_tls_init_tp_called) - _dl_add_to_slotinfo (l, true); } } rtld_timer_stop (&relocate_time, start); @@ -2310,12 +2326,7 @@ dl_main (const ElfW(Phdr) *phdr, || count_modids != _dl_count_modids ()) ++GL(dl_tls_generation); - /* Now that we have completed relocation, the initializer data - for the TLS blocks has its final values and we can copy them - into the main thread's TLS area, which we allocated above. - Note: thread-local variables must only be accessed after completing - the next step. */ - _dl_allocate_tls_init (tcbp, true); + /* TLS .tdata copy moved before the relocation loop above. */ /* And finally install it for the main thread. */ if (! __rtld_tls_init_tp_called) diff --git a/elf/tst-ifunc-tls-init-dlopen-lib.c b/elf/tst-ifunc-tls-init-dlopen-lib.c new file mode 100644 index 00000000000..b3e587a3f40 --- /dev/null +++ b/elf/tst-ifunc-tls-init-dlopen-lib.c @@ -0,0 +1,56 @@ +/* Shared library for tst-ifunc-tls-init-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 + . */ + +/* Same as tst-ifunc-tls-init-lib.c, but exercises the dlopen path. */ + +#define SENTINEL 0x5A5A1234 + +static volatile __thread int sentinel + __attribute__ ((tls_model ("initial-exec"))) = SENTINEL; +static volatile int last_seen_sentinel; + +static int +impl_ok (void) +{ + return SENTINEL; +} + +static int +impl_bad (void) +{ + return 0; +} + +int +get_last_seen_sentinel (void) +{ + return last_seen_sentinel; +} + +static int (* +resolver (void)) (void) +{ + int s = sentinel; + last_seen_sentinel = s; + return s == SENTINEL ? impl_ok : impl_bad; +} + +int ifunc_tls (void) __attribute__ ((ifunc ("resolver"))); + +/* Force a non-PLT relocation against the IFUNC symbol. */ +int (*fptr) (void) = ifunc_tls; diff --git a/elf/tst-ifunc-tls-init-lib-skeleton.c b/elf/tst-ifunc-tls-init-lib-skeleton.c new file mode 100644 index 00000000000..d8b2a4f0f10 --- /dev/null +++ b/elf/tst-ifunc-tls-init-lib-skeleton.c @@ -0,0 +1,57 @@ +/* Test that static-TLS initialisation works correctly with IFUNC resolvers. + 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 + . */ + +/* The initial-exec TLS model keeps the access path to a single TP-relative + load, so the test is sensitive to whether the static TLS block has been + populated rather than to any __tls_get_addr / DTV-update timing. */ + +#define SENTINEL 0x5A5A1234 + +/* The 'volatile' avoids constant fold optimization in impl_ok. */ +static volatile __thread int sentinel + __attribute__ ((tls_model ("initial-exec"))) = SENTINEL; +static volatile int last_seen_sentinel; + +static int +impl_ok (void) +{ + return SENTINEL; +} + +static int +impl_bad (void) +{ + return 0; +} + +int +get_last_seen_sentinel (void) +{ + return last_seen_sentinel; +} + +static int (*resolver (void)) (void) +{ + int s = sentinel; + last_seen_sentinel = s; + return s == SENTINEL ? impl_ok : impl_bad; +} +int ifunc_tls (void) __attribute__ ((ifunc ("resolver"))); + +/* Force a non-PLT relocation against the IFUNC symbol. */ +int (*fptr) (void) = ifunc_tls; diff --git a/elf/tst-ifunc-tls-init-lib1.c b/elf/tst-ifunc-tls-init-lib1.c new file mode 100644 index 00000000000..ed9db110b1a --- /dev/null +++ b/elf/tst-ifunc-tls-init-lib1.c @@ -0,0 +1 @@ +#include "tst-ifunc-tls-init-lib-skeleton.c" diff --git a/elf/tst-ifunc-tls-init-lib2.c b/elf/tst-ifunc-tls-init-lib2.c new file mode 100644 index 00000000000..ed9db110b1a --- /dev/null +++ b/elf/tst-ifunc-tls-init-lib2.c @@ -0,0 +1 @@ +#include "tst-ifunc-tls-init-lib-skeleton.c" diff --git a/elf/tst-ifunc-tls-init.c b/elf/tst-ifunc-tls-init.c new file mode 100644 index 00000000000..8b996b7c60c --- /dev/null +++ b/elf/tst-ifunc-tls-init.c @@ -0,0 +1,81 @@ +/* Check if static-TLS variables are correctly intialized in IFUNC resolvers. + 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 + . */ + +/* Checks if a IFUNC resolver sees if a TLS blocker is properly initialized. + The tst-ifunc-tls-init-lib.so carries: + + - a thread-local 'sentinel' initialised to SENTINEL. + - an IFUNC 'ifunc_tls' whose resolver picks impl_ok when sentinel reads + as SENTINEL and impl_bad (returning 0) otherwise. + - an IFUNC-backed function-pointer global 'fptr' whose initialiser + produces a non-PLT relocation. */ + +#include +#include + +#define SENTINEL 0x5A5A1234 + +extern int ifunc_tls (void); +extern int (*fptr) (void); +extern int get_last_seen_sentinel (void); + +static void +test_tls_ifunc (int (*check_sentinel)(void), + int (*check_fptr)(void), + int (*check_ifunc_tls)(void)) +{ + /* Primary check: 'get_last_seen_sentinel' returns the value of the DSO's + thread-local 'sentinel' as observed by the resolver at the moment it ran + for the IFUNC reloc that initialised fptr. The getter is a regular + PLT-resolved function in the DSO, so the read of the diagnostic global + does NOT go through a COPY relocation that could overwrite the resolver's + write. */ + TEST_COMPARE (check_sentinel (), SENTINEL); + + /* Secondary check: fptr is set during IFUNC resolver call, then copied into + the exe's. Returns SENTINEL only if the resolver picked impl_ok. */ + TEST_VERIFY (check_fptr != NULL); + TEST_COMPARE (check_fptr (), SENTINEL); + + /* Sanity check: issue the ifunc. */ + TEST_COMPARE (check_ifunc_tls (), SENTINEL); +} + +static int +do_test (void) +{ + test_tls_ifunc (get_last_seen_sentinel, fptr, ifunc_tls); + + /* Same as before, but check the dlopen path. */ + void *handle = xdlopen ("tst-ifunc-tls-init-lib2.so", + RTLD_LAZY | RTLD_LOCAL); + + int (*get_last_seen_sentinel_dlopen) (void) + = xdlsym (handle, "get_last_seen_sentinel"); + int (**fptr_dlopen) (void) = xdlsym (handle, "fptr"); + int (*ifunc_tls_dlopen) (void) = xdlsym (handle, "ifunc_tls"); + + test_tls_ifunc (get_last_seen_sentinel_dlopen, *fptr_dlopen, + ifunc_tls_dlopen); + + xdlclose (handle); + + return 0; +} + +#include diff --git a/elf/tst-ifunc-tls-write-lib.c b/elf/tst-ifunc-tls-write-lib.c new file mode 100644 index 00000000000..ef1dff90e01 --- /dev/null +++ b/elf/tst-ifunc-tls-write-lib.c @@ -0,0 +1,54 @@ +/* Shared library for tst-ifunc-tls-write. + 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 + . */ + +/* The DSO defines an initial-exec thread-local 'counter' initialised to + SENTINEL, the IFUNC resolver (via the non-PLT IFUNC reloc on 'fptr') + overwrites counter with MARKER, and the test then reads counter back + through a getter. */ + +#define SENTINEL 0x5A5A1234u +#define MARKER 0x32125A5Au + +/* The 'volatile' avoids constant fold optimization in impl_ok. */ +static volatile __thread unsigned int counter + __attribute__ ((tls_model ("initial-exec"))) = SENTINEL; + +static unsigned int +impl (void) +{ + return 0; +} + +static unsigned int (*resolver (void)) (void) +{ + counter = MARKER; + return impl; +} +unsigned int ifunc_write (void) __attribute__ ((ifunc ("resolver"))); + +/* Force the resolver rather than lazy bind on first call, which would + re-write counter after the test reads it). Using a COPY'd fptr also lets + the test verify the resolver ran without making a PLT call that would + itself fire the resolver again. */ +unsigned int (*fptr) (void) = ifunc_write; + +unsigned int +get_counter (void) +{ + return counter; +} diff --git a/elf/tst-ifunc-tls-write.c b/elf/tst-ifunc-tls-write.c new file mode 100644 index 00000000000..8c3711c161c --- /dev/null +++ b/elf/tst-ifunc-tls-write.c @@ -0,0 +1,34 @@ +/* Check if static-TLS variables are correctly intialized in IFUNC resolvers. + 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 + . */ + +#include + +#define MARKER 0x32125A5Au + +extern unsigned int (*fptr) (void); +extern unsigned int get_counter (void); + +static int +do_test (void) +{ + TEST_VERIFY (fptr != NULL); + TEST_COMPARE (get_counter (), MARKER); + return 0; +} + +#include