diff options
author | Florian Weimer <fweimer@redhat.com> | 2025-01-16 20:02:42 +0100 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2025-01-16 20:02:42 +0100 |
commit | cbd9fd236981717d3d4ee942986ea912e9707c32 (patch) | |
tree | f6ea10053a17a49c2dcf73c4b23396e960186816 /elf | |
parent | 7f784ffc173b5a2166ff846fd003a2264d614456 (diff) | |
download | glibc-cbd9fd236981717d3d4ee942986ea912e9707c32.zip glibc-cbd9fd236981717d3d4ee942986ea912e9707c32.tar.gz glibc-cbd9fd236981717d3d4ee942986ea912e9707c32.tar.bz2 |
Consolidate TLS block allocation for static binaries with ld.so
Use the same code to compute the TLS block size and its alignment.
The code in elf/dl-tls.c is linked in anyway for all binaries
due to the reference to _dl_tls_static_surplus_init.
It is not possible to call _dl_allocate_tls_storage directly
because malloc is not available in the static case. (The
dynamic linker uses the minimal malloc at this stage.) Therefore,
split _dl_tls_block_size_with_pre and _dl_tls_block_align from
_dl_allocate_tls_storage, and call those new functions from
__libc_setup_tls.
This fixes extra TLS allocation for the static case, and apparently
some pre-existing bugs as well (the independent recomputation of
TLS block sizes in init_static_tls looks rather suspect).
Fixes commit 0e411c5d3098982d67cd2d7a233eaa6c977a1869 ("Add generic
'extra TLS'").
Diffstat (limited to 'elf')
-rw-r--r-- | elf/dl-tls.c | 50 | ||||
-rw-r--r-- | elf/dl-tls_block_align.h | 70 |
2 files changed, 77 insertions, 43 deletions
diff --git a/elf/dl-tls.c b/elf/dl-tls.c index 647deaf..8306a39 100644 --- a/elf/dl-tls.c +++ b/elf/dl-tls.c @@ -28,6 +28,7 @@ #include <tls.h> #include <dl-tls.h> #include <ldsodefs.h> +#include <dl-tls_block_align.h> #if PTHREAD_IN_LIBC # include <list.h> @@ -237,7 +238,6 @@ _dl_count_modids (void) } -#ifdef SHARED void _dl_determine_tlsoffset (void) { @@ -446,7 +446,6 @@ _dl_determine_tlsoffset (void) /* The alignment requirement for the static TLS block. */ GLRO (dl_tls_static_align) = max_align; } -#endif /* SHARED */ static void * allocate_dtv (void *result) @@ -508,55 +507,20 @@ tcb_to_pointer_to_free_location (void *tcb) void * _dl_allocate_tls_storage (void) { - void *result; - size_t size = GLRO (dl_tls_static_size); - -#if TLS_DTV_AT_TP - /* Memory layout is: - [ TLS_PRE_TCB_SIZE ] [ TLS_TCB_SIZE ] [ TLS blocks ] - ^ This should be returned. */ - size += TLS_PRE_TCB_SIZE; -#endif - - /* Reserve space for the required alignment and the pointer to the - original allocation. */ - size_t alignment = GLRO (dl_tls_static_align); + size_t size = _dl_tls_block_size_with_pre (); - /* Perform the allocation. */ + /* Perform the allocation. Reserve space for alignment storage of + the pointer that will have to be freed. */ _dl_tls_allocate_begin (); - void *allocated = malloc (size + alignment + sizeof (void *)); + void *allocated = malloc (size + GLRO (dl_tls_static_align) + + sizeof (void *)); if (__glibc_unlikely (allocated == NULL)) { _dl_tls_allocate_end (); return NULL; } - /* Perform alignment and allocate the DTV. */ -#if TLS_TCB_AT_TP - /* The TCB follows the TLS blocks, which determine the alignment. - (TCB alignment requirements have been taken into account when - calculating GLRO (dl_tls_static_align).) */ - void *aligned = (void *) roundup ((uintptr_t) allocated, alignment); - result = aligned + size - TLS_TCB_SIZE; - - /* Clear the TCB data structure. We can't ask the caller (i.e. - libpthread) to do it, because we will initialize the DTV et al. */ - memset (result, '\0', TLS_TCB_SIZE); -#elif TLS_DTV_AT_TP - /* Pre-TCB and TCB come before the TLS blocks. The layout computed - in _dl_determine_tlsoffset assumes that the TCB is aligned to the - TLS block alignment, and not just the TLS blocks after it. This - can leave an unused alignment gap between the TCB and the TLS - blocks. */ - result = (void *) roundup - (sizeof (void *) + TLS_PRE_TCB_SIZE + (uintptr_t) allocated, - alignment); - - /* Clear the TCB data structure and TLS_PRE_TCB_SIZE bytes before - it. We can't ask the caller (i.e. libpthread) to do it, because - we will initialize the DTV et al. */ - memset (result - TLS_PRE_TCB_SIZE, '\0', TLS_PRE_TCB_SIZE + TLS_TCB_SIZE); -#endif + void *result = _dl_tls_block_align (size, allocated); /* Record the value of the original pointer for later deallocation. */ diff --git a/elf/dl-tls_block_align.h b/elf/dl-tls_block_align.h new file mode 100644 index 0000000..82016f3 --- /dev/null +++ b/elf/dl-tls_block_align.h @@ -0,0 +1,70 @@ +/* Computation of TLS block size and its alignment. + Copyright (C) 2002-2025 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/>. */ + +/* Compute the size of the TLS block for memory allocation. */ +static size_t +_dl_tls_block_size_with_pre (void) +{ + size_t size = GLRO (dl_tls_static_size); + +#if TLS_DTV_AT_TP + /* Memory layout is: + [ TLS_PRE_TCB_SIZE ] [ TLS_TCB_SIZE ] [ TLS blocks ] + ^ This should be returned. */ + size += TLS_PRE_TCB_SIZE; +#endif + return size; +} + +/* SIZE must be the value returned by _dl_tls_block_size_with_pre. + ALLOCATED must point to at least SIZE + GLRO (dl_tls_static_align) + bytes. */ +static void * +_dl_tls_block_align (size_t size, void *allocated) +{ + void *result; + size_t alignment = GLRO (dl_tls_static_align); + +#if TLS_TCB_AT_TP + /* The TCB follows the TLS blocks, which determine the alignment. + (TCB alignment requirements have been taken into account when + calculating GLRO (dl_tls_static_align).) */ + void *aligned = (void *) roundup ((uintptr_t) allocated, alignment); + result = aligned + size - TLS_TCB_SIZE; + + /* Clear the TCB data structure. We can't ask the caller (i.e. + libpthread) to do it, because we will initialize the DTV et al. */ + memset (result, '\0', TLS_TCB_SIZE); +#elif TLS_DTV_AT_TP + /* Pre-TCB and TCB come before the TLS blocks. The layout computed + in _dl_determine_tlsoffset assumes that the TCB is aligned to the + TLS block alignment, and not just the TLS blocks after it. This + can leave an unused alignment gap between the TCB and the TLS + blocks. */ + result = (void *) roundup + (sizeof (void *) + TLS_PRE_TCB_SIZE + (uintptr_t) allocated, + alignment); + + /* Clear the TCB data structure and TLS_PRE_TCB_SIZE bytes before + it. We can't ask the caller (i.e. libpthread) to do it, because + we will initialize the DTV et al. */ + memset (result - TLS_PRE_TCB_SIZE, '\0', TLS_PRE_TCB_SIZE + TLS_TCB_SIZE); +#endif + + return result; +} |