[PATCH] elf: Re-initialise static TLS after .tdata relocation (BZ 34164)

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Tue May 26 22:50:10 GMT 2026



On 26/05/26 19:47, H.J. Lu wrote:
> On Wed, May 27, 2026 at 6:39 AM Adhemerval Zanella Netto
> <adhemerval.zanella@linaro.org> wrote:
>>
>>
>>
>> On 26/05/26 18:36, H.J. Lu wrote:
>>> On Wed, May 27, 2026 at 2:52 AM Adhemerval Zanella
>>> <adhemerval.zanella@linaro.org> wrote:
>>>>
>>>> The af34b1376a37fa27e1de9d869ed9493fc569bfa6 changed the .tdata
>>>> setup from:
>>>>
>>>>   relocation loop (applies relocations to .tdata in DSO memory)
>>>>   _dl_allocate_tls_init copies relocated .tdata -> main thread TLS
>>>>
>>>> to a new order:
>>>>
>>>>   _dl_allocate_tls_init copies unrelocated .tdata -> main thread TLS
>>>>   relocation loop (relocates .tdata in DSO memory, but the TLS block
>>>>   has stale copies)
>>>>
>>>> This broke file-scope thread-local initialised with the address of a
>>>> function (for instance the cache structs in libmpfr).
>>>
>>> This is a regression.  Please file a glibc bug report.
>>
>> I think there is no need to file a bug report, it is a regression for
>> an unreleased version and it is really related to BZ#34164.
> 
> If it is related to BZ #34164, please update BZ #34164 and mention it
> in the commit message.

It is with af34b1376a37fa27e1de9d869ed9493fc569bfa6 mention, the commit that
introduced this regression.  I think the commit message has all the information
to track when, why, and how the issue was introduced.

> 
>>>
>>>> Fix it by splitting ELF_DYNAMIC_RELOCATE inside
>>>> _dl_relocate_object_no_relro into the non-IRELATIVE and IRELATIVE
>>>> sub-passes (similar as done on static-pie startup by b75ad99d45b)
>>>> and call _dl_init_static_tls between them.  By the time the IFUNC
>>>> pass fires, .tdata is fully relocated.
>>>>
>>>> Checked on x86_64-linux-gnu and aarch64-linux-gnu.
>>>> ---
>>>>  elf/Makefile                  |  3 +++
>>>>  elf/dl-reloc.c                | 24 ++++++++++++++++++--
>>>>  elf/tst-tls-tdata-reloc-lib.c | 42 +++++++++++++++++++++++++++++++++++
>>>>  elf/tst-tls-tdata-reloc.c     | 32 ++++++++++++++++++++++++++
>>>>  4 files changed, 99 insertions(+), 2 deletions(-)
>>>>  create mode 100644 elf/tst-tls-tdata-reloc-lib.c
>>>>  create mode 100644 elf/tst-tls-tdata-reloc.c
>>>>
>>>> diff --git a/elf/Makefile b/elf/Makefile
>>>> index d7bab52cd97..a58a902451c 100644
>>>> --- a/elf/Makefile
>>>> +++ b/elf/Makefile
>>>> @@ -1297,6 +1297,7 @@ tests += \
>>>>    tst-ifunc-resolver-protector \
>>>>    tst-ifunc-tls-init \
>>>>    tst-ifunc-tls-write \
>>>> +  tst-tls-tdata-reloc \
>>>>    # tests
>>>>  # Note: sysdeps/x86_64/ifuncmain8.c uses ifuncmain8.
>>>>  tests-internal += \
>>>> @@ -1365,6 +1366,7 @@ modules-names += \
>>>>    tst-ifunc-tls-init-lib1 \
>>>>    tst-ifunc-tls-init-lib2 \
>>>>    tst-ifunc-tls-write-lib \
>>>> +  tst-tls-tdata-reloc-lib \
>>>>    # modules-names
>>>>  ifeq (no,$(with-lld))
>>>>  modules-names += ifuncmod5
>>>> @@ -2518,6 +2520,7 @@ $(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-tls-tdata-reloc: $(objpfx)tst-tls-tdata-reloc-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 91497b38397..15a6a4cffef 100644
>>>> --- a/elf/dl-reloc.c
>>>> +++ b/elf/dl-reloc.c
>>>> @@ -269,9 +269,29 @@ _dl_relocate_object_no_relro (struct link_map *l, struct r_scope_elem *scope[],
>>>>      }
>>>>
>>>>    {
>>>> -    /* Do the actual relocation of the object's GOT and other data.  */
>>>> +    /* Do the actual relocation of the object's GOT and other data.
>>>>
>>>> -    ELF_DYNAMIC_RELOCATE (l, scope, lazy, consider_profiling, skip_ifunc);
>>>> +       Process the non-IRELATIVE pass first so .tdata is fully relocated
>>>> +       (including R_*_RELATIVE / R_*_64 fixups for TLS initialisers, e.g. a
>>>> +       file-scope thread-local initialised with the address of a function),
>>>> +       then refresh the static TLS slot before the IRELATIVE pass runs the
>>>> +       IFUNC resolvers.  Without this, a resolver would see the unrelocated
>>>> +       initialiser bytes that were placed into the slot by the early
>>>> +       _dl_allocate_tls_init.  */
>>>> +    ELF_DYNAMIC_RELOCATE_NOIFUNC (l, scope, lazy, consider_profiling);
>>>> +
>>>> +#ifdef SHARED
>>>> +    /* Re-initialise the static TLS slot with the .tdata so the IRELATIVE
>>>> +       pass observes a fully-relocated initialiser image.  Skipped for objects
>>>> +       without static TLS or before the main thread TCB has been set up.  */
>>>> +    if (l->l_tls_blocksize != 0
>>>> +       && __rtld_tls_init_tp_called
>>>> +       && l->l_tls_offset != NO_TLS_OFFSET
>>>> +       && l->l_tls_offset != FORCED_DYNAMIC_TLS_OFFSET)
>>>> +      _dl_init_static_tls (l);
>>>> +#endif
>>>> +
>>>> +    ELF_DYNAMIC_RELOCATE_IFUNC (l, scope, lazy, skip_ifunc);
>>>>
>>>>      if ((consider_profiling || consider_symbind)
>>>>         && l->l_info[DT_PLTRELSZ] != NULL)
>>>> diff --git a/elf/tst-tls-tdata-reloc-lib.c b/elf/tst-tls-tdata-reloc-lib.c
>>>> new file mode 100644
>>>> index 00000000000..73aa58b0722
>>>> --- /dev/null
>>>> +++ b/elf/tst-tls-tdata-reloc-lib.c
>>>> @@ -0,0 +1,42 @@
>>>> +/* Shared library for tst-tls-tdata-reloc.
>>>> +   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/>.  */
>>>> +
>>>> +#define EXPECTED 0x12345678
>>>> +
>>>> +static int
>>>> +callee (void)
>>>> +{
>>>> +  return EXPECTED;
>>>> +}
>>>> +
>>>> +typedef struct
>>>> +{
>>>> +  int (*func) (void);
>>>> +} cache_t;
>>>> +
>>>> +/* General-dynamic TLS: the initialiser of '.func' produces an
>>>> +   R_*_RELATIVE on .tdata.  */
>>>> +__thread cache_t cache = { .func = callee };
>>>> +
>>>> +int
>>>> +call_through_tls (void)
>>>> +{
>>>> +  if (cache.func == 0)
>>>> +    return -1;
>>>> +  return cache.func ();
>>>> +}
>>>> diff --git a/elf/tst-tls-tdata-reloc.c b/elf/tst-tls-tdata-reloc.c
>>>> new file mode 100644
>>>> index 00000000000..1d38726e364
>>>> --- /dev/null
>>>> +++ b/elf/tst-tls-tdata-reloc.c
>>>> @@ -0,0 +1,32 @@
>>>> +/* Check that .tdata relocations are applied to the static TLS slot.
>>>> +   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/>.  */
>>>> +
>>>> +#include <support/check.h>
>>>> +
>>>> +#define EXPECTED 0x12345678
>>>> +
>>>> +extern int call_through_tls (void);
>>>> +
>>>> +static int
>>>> +do_test (void)
>>>> +{
>>>> +  TEST_COMPARE (call_through_tls (), EXPECTED);
>>>> +  return 0;
>>>> +}
>>>> +
>>>> +#include <support/test-driver.c>
>>>> --
>>>> 2.43.0
>>>>
>>>
>>>
>>
> 
> 



More information about the Libc-alpha mailing list