aboutsummaryrefslogtreecommitdiff
path: root/elf/dl-tls_block_align.h
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/dl-tls_block_align.h
parent7f784ffc173b5a2166ff846fd003a2264d614456 (diff)
downloadglibc-cbd9fd236981717d3d4ee942986ea912e9707c32.tar.xz
glibc-cbd9fd236981717d3d4ee942986ea912e9707c32.zip
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/dl-tls_block_align.h')
-rw-r--r--elf/dl-tls_block_align.h70
1 files changed, 70 insertions, 0 deletions
diff --git a/elf/dl-tls_block_align.h b/elf/dl-tls_block_align.h
new file mode 100644
index 0000000000..82016f3bb5
--- /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;
+}