aboutsummaryrefslogtreecommitdiff
path: root/elf
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2025-01-16 20:02:42 +0100
committerFlorian Weimer <fweimer@redhat.com>2025-01-16 20:02:42 +0100
commitcbd9fd236981717d3d4ee942986ea912e9707c32 (patch)
treef6ea10053a17a49c2dcf73c4b23396e960186816 /elf
parent7f784ffc173b5a2166ff846fd003a2264d614456 (diff)
downloadglibc-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.c50
-rw-r--r--elf/dl-tls_block_align.h70
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;
+}