Overwrite target for x86_64_v2
Update patch-git.lua to handle AlmaLinux branches correctly Add support for AlmaLinux import UBI format
This commit is contained in:
commit
1d0c7dface
180
glibc-RHEL-149709.patch
Normal file
180
glibc-RHEL-149709.patch
Normal file
@ -0,0 +1,180 @@
|
||||
commit d8997716a1ca22cf038eac86ed286830ba9818cc
|
||||
Author: DJ Delorie <dj@redhat.com>
|
||||
Date: Wed Apr 1 17:52:25 2026 -0400
|
||||
|
||||
nss: fix __get_default_domain logic
|
||||
|
||||
Fix logic bug in __nss_get_default_domain that prevents
|
||||
proper initialization.
|
||||
|
||||
Because this function is not exposed, the test case must link
|
||||
against the object directly.
|
||||
|
||||
Bug origin commit: 64d1e08ea822bf47cb2796ad0f727136227f983c
|
||||
|
||||
Co-authored-by: Florian Weimer <fweimer@redhat.com>
|
||||
Reviewed-by: Frédéric Bérat <fberat@redhat.com>
|
||||
|
||||
diff --git a/nss/Makefile b/nss/Makefile
|
||||
index 400ee3d5eefe3bd1..a9d0715d885ddb0d 100644
|
||||
--- a/nss/Makefile
|
||||
+++ b/nss/Makefile
|
||||
@@ -317,6 +317,7 @@ tests := \
|
||||
test-netdb \
|
||||
test-rpcent \
|
||||
testgrp \
|
||||
+ tst-default-domain \
|
||||
tst-fgetsgent_r \
|
||||
tst-getaddrinfo \
|
||||
tst-getaddrinfo2 \
|
||||
@@ -510,6 +511,8 @@ endif
|
||||
$(objpfx)tst-nss-files-alias-leak.out: $(objpfx)libnss_files.so
|
||||
$(objpfx)tst-nss-files-alias-truncated.out: $(objpfx)libnss_files.so
|
||||
|
||||
+$(objpfx)tst-default-domain: $(objpfx)nisdomain.os
|
||||
+
|
||||
tst-nss-gai-hv2-canonname-ENV = \
|
||||
MALLOC_TRACE=$(objpfx)tst-nss-gai-hv2-canonname.mtrace \
|
||||
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
|
||||
diff --git a/nss/nss_compat/nisdomain.c b/nss/nss_compat/nisdomain.c
|
||||
index 1c9d6423d3dc7e74..90b227f131ac787c 100644
|
||||
--- a/nss/nss_compat/nisdomain.c
|
||||
+++ b/nss/nss_compat/nisdomain.c
|
||||
@@ -36,7 +36,7 @@ __nss_get_default_domain (char **outdomain)
|
||||
|
||||
__libc_lock_lock (domainname_lock);
|
||||
|
||||
- if (domainname[0] != '\0')
|
||||
+ if (domainname[0] == '\0')
|
||||
{
|
||||
if (getdomainname (domainname, MAXDOMAINNAMELEN) < 0)
|
||||
result = errno;
|
||||
diff --git a/nss/tst-default-domain.c b/nss/tst-default-domain.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..2fa9153d8802d4c3
|
||||
--- /dev/null
|
||||
+++ b/nss/tst-default-domain.c
|
||||
@@ -0,0 +1,123 @@
|
||||
+/* Basic test of __nss_get_default_domain
|
||||
+ 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 <unistd.h>
|
||||
+#include <string.h>
|
||||
+#include <dlfcn.h>
|
||||
+
|
||||
+#include "nss_compat/nisdomain.h"
|
||||
+
|
||||
+#include <support/test-driver.h>
|
||||
+#include <support/support.h>
|
||||
+#include <support/namespace.h>
|
||||
+#include <support/check.h>
|
||||
+
|
||||
+char unset_domain[] = "unset_domain";
|
||||
+char new_domain[] = "new_domain";
|
||||
+
|
||||
+/* This function checks the __nss_get_default_domain() function in
|
||||
+ nss_compat/nssdomain.c. Because this is an internal function to
|
||||
+ libnss_compat.so, the Makefile will link that object to this test
|
||||
+ case directly. */
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ char *domain_name;
|
||||
+ char buf[1024];
|
||||
+
|
||||
+ /* We need to be in a network namespace so we can change the domain
|
||||
+ name without interfering with the host system. */
|
||||
+ support_become_root ();
|
||||
+ support_enter_network_namespace ();
|
||||
+ if (!support_in_uts_namespace ())
|
||||
+ return EXIT_UNSUPPORTED;
|
||||
+
|
||||
+ /* First pass: set an empty domain and make sure it's returned
|
||||
+ correctly. This should not be cached. */
|
||||
+
|
||||
+ /* Set the domain name to a known value. */
|
||||
+ TEST_VERIFY (setdomainname ("", 0) == 0);
|
||||
+
|
||||
+ /* Make sure it got set. */
|
||||
+ TEST_VERIFY (getdomainname (buf, sizeof(buf)) == 0);
|
||||
+ TEST_COMPARE_STRING (buf, "");
|
||||
+
|
||||
+ /* Set this to a known "unknown" value so we can detect if it's not
|
||||
+ changed. */
|
||||
+ domain_name = unset_domain;
|
||||
+
|
||||
+ /* This is the function we're testing. */
|
||||
+ TEST_VERIFY (__nss_get_default_domain (& domain_name) == 0);
|
||||
+
|
||||
+ /* Make sure the correct domain name is returned. */
|
||||
+ TEST_VERIFY (domain_name != NULL);
|
||||
+ TEST_COMPARE_STRING (domain_name, "");
|
||||
+
|
||||
+ /* Second pass: set a non-empty domain and make sure it's returned
|
||||
+ correctly. This works because the empty domain is not
|
||||
+ cached. */
|
||||
+
|
||||
+ /* Set the domain name to a known value. */
|
||||
+ TEST_VERIFY (setdomainname (new_domain, strlen (new_domain)) == 0);
|
||||
+
|
||||
+ /* Make sure it got set. */
|
||||
+ TEST_VERIFY (getdomainname (buf, sizeof(buf)) == 0);
|
||||
+ TEST_COMPARE_STRING (buf, new_domain);
|
||||
+
|
||||
+ /* Set this to a known "unknown" value so we can detect if it's not
|
||||
+ changed. */
|
||||
+ domain_name = unset_domain;
|
||||
+
|
||||
+ /* This is the function we're testing. */
|
||||
+ TEST_VERIFY (__nss_get_default_domain (& domain_name) == 0);
|
||||
+
|
||||
+ /* Make sure the correct domain name is returned. */
|
||||
+ TEST_VERIFY (domain_name != NULL);
|
||||
+ TEST_COMPARE_STRING (domain_name, new_domain);
|
||||
+
|
||||
+ /* The function caches the name, so check it twice. */
|
||||
+ TEST_VERIFY (__nss_get_default_domain (& domain_name) == 0);
|
||||
+
|
||||
+ TEST_VERIFY (domain_name != NULL);
|
||||
+ TEST_COMPARE_STRING (domain_name, new_domain);
|
||||
+
|
||||
+ /* Third pass: set an empty domain again but expect the cached
|
||||
+ value. */
|
||||
+
|
||||
+ /* Set the domain name to a known value. */
|
||||
+ TEST_VERIFY (setdomainname ("", 0) == 0);
|
||||
+
|
||||
+ /* Make sure it got set. */
|
||||
+ TEST_VERIFY (getdomainname (buf, sizeof(buf)) == 0);
|
||||
+ TEST_COMPARE_STRING (buf, "");
|
||||
+
|
||||
+ /* Set this to a known "unknown" value so we can detect if it's not
|
||||
+ changed. */
|
||||
+ domain_name = unset_domain;
|
||||
+
|
||||
+ /* This is the function we're testing. */
|
||||
+ TEST_VERIFY (__nss_get_default_domain (& domain_name) == 0);
|
||||
+
|
||||
+ TEST_VERIFY (domain_name != NULL);
|
||||
+ TEST_COMPARE_STRING (domain_name, new_domain);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
369
glibc-RHEL-159427-1.patch
Normal file
369
glibc-RHEL-159427-1.patch
Normal file
@ -0,0 +1,369 @@
|
||||
commit 332f8e62afef53492dd8285490bcf7aeef18c80a
|
||||
Author: Frédéric Bérat <fberat@redhat.com>
|
||||
Date: Fri Sep 5 16:14:38 2025 +0200
|
||||
|
||||
tls: Add debug logging for TLS and TCB management
|
||||
|
||||
Introduce the `DL_DEBUG_TLS` debug mask to enable detailed logging for
|
||||
Thread-Local Storage (TLS) and Thread Control Block (TCB) management.
|
||||
|
||||
This change integrates a new `tls` option into the `LD_DEBUG`
|
||||
environment variable, allowing developers to trace:
|
||||
- TCB allocation, deallocation, and reuse events in `dl-tls.c`,
|
||||
`nptl/allocatestack.c`, and `nptl/nptl-stack.c`.
|
||||
- Thread startup events, including the TID and TCB address, in
|
||||
`nptl/pthread_create.c`.
|
||||
|
||||
A new test, `tst-dl-debug-tid`, has been added to validate the
|
||||
functionality of this new debug logging, ensuring that relevant messages
|
||||
are correctly generated for both main and worker threads.
|
||||
|
||||
This enhances the debugging capabilities for diagnosing issues related
|
||||
to TLS allocation and thread lifecycle within the dynamic linker.
|
||||
|
||||
Reviewed-by: DJ Delorie <dj@redhat.com>
|
||||
|
||||
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
|
||||
index b13e752358a059a4..85db1969b921d9e6 100644
|
||||
--- a/elf/dl-tls.c
|
||||
+++ b/elf/dl-tls.c
|
||||
@@ -497,6 +497,8 @@ _dl_allocate_tls_storage (void)
|
||||
result = allocate_dtv (result);
|
||||
if (result == NULL)
|
||||
free (allocated);
|
||||
+ else if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf ("TCB allocated: 0x%lx\n", (unsigned long int) result);
|
||||
|
||||
_dl_tls_allocate_end ();
|
||||
return result;
|
||||
@@ -685,6 +687,10 @@ rtld_hidden_def (_dl_allocate_tls)
|
||||
void
|
||||
_dl_deallocate_tls (void *tcb, bool dealloc_tcb)
|
||||
{
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf ("TCB deallocating: 0x%lx (dealloc_tcb=%d)\n",
|
||||
+ (unsigned long int) tcb, dealloc_tcb);
|
||||
+
|
||||
dtv_t *dtv = GET_DTV (tcb);
|
||||
|
||||
/* We need to free the memory allocated for non-static TLS. */
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index 8b189a87f76e7e08..c9082bb31643d9ab 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -2469,10 +2469,12 @@ process_dl_debug (struct dl_main_state *state, const char *dl_debug)
|
||||
DL_DEBUG_VERSIONS | DL_DEBUG_IMPCALLS },
|
||||
{ LEN_AND_STR ("scopes"), "display scope information",
|
||||
DL_DEBUG_SCOPES },
|
||||
+ { LEN_AND_STR ("tls"), "display TLS structures processing",
|
||||
+ DL_DEBUG_TLS },
|
||||
{ LEN_AND_STR ("all"), "all previous options combined",
|
||||
DL_DEBUG_LIBS | DL_DEBUG_RELOC | DL_DEBUG_FILES | DL_DEBUG_SYMBOLS
|
||||
| DL_DEBUG_BINDINGS | DL_DEBUG_VERSIONS | DL_DEBUG_IMPCALLS
|
||||
- | DL_DEBUG_SCOPES },
|
||||
+ | DL_DEBUG_SCOPES | DL_DEBUG_TLS },
|
||||
{ LEN_AND_STR ("statistics"), "display relocation statistics",
|
||||
DL_DEBUG_STATISTICS },
|
||||
{ LEN_AND_STR ("unused"), "determined unused DSOs",
|
||||
diff --git a/nptl/Makefile b/nptl/Makefile
|
||||
index 4d3271ba71f0bc65..89e2b8c4523714ab 100644
|
||||
--- a/nptl/Makefile
|
||||
+++ b/nptl/Makefile
|
||||
@@ -373,6 +373,7 @@ tests-container = tst-pthread-getattr
|
||||
tests-internal := \
|
||||
tst-barrier5 \
|
||||
tst-cond22 \
|
||||
+ tst-dl-debug-tid \
|
||||
tst-mutex8 \
|
||||
tst-mutex8-static \
|
||||
tst-mutexpi8 \
|
||||
@@ -559,6 +560,7 @@ xtests-static += tst-setuid1-static
|
||||
|
||||
ifeq ($(run-built-tests),yes)
|
||||
tests-special += \
|
||||
+ $(objpfx)tst-dl-debug-tid.out \
|
||||
$(objpfx)tst-oddstacklimit.out \
|
||||
# tests-special
|
||||
ifeq ($(build-shared),yes)
|
||||
@@ -693,6 +695,11 @@ tst-stackguard1-ARGS = --command "$(host-test-program-cmd) --child"
|
||||
tst-stackguard1-static-ARGS = --command "$(objpfx)tst-stackguard1-static --child"
|
||||
|
||||
ifeq ($(run-built-tests),yes)
|
||||
+$(objpfx)tst-dl-debug-tid.out: tst-dl-debug-tid.sh $(objpfx)tst-dl-debug-tid
|
||||
+ $(SHELL) $< $(common-objpfx) '$(test-wrapper)' '$(rtld-prefix)' \
|
||||
+ '$(test-wrapper-env)' '$(run-program-env)' \
|
||||
+ $(objpfx)tst-dl-debug-tid > $@; $(evaluate-test)
|
||||
+
|
||||
$(objpfx)tst-oddstacklimit.out: $(objpfx)tst-oddstacklimit $(objpfx)tst-basic1
|
||||
$(test-program-prefix) $< --command '$(host-test-program-cmd)' > $@; \
|
||||
$(evaluate-test)
|
||||
diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
|
||||
index d9adb5856cefa533..b26ebc8680d7280a 100644
|
||||
--- a/nptl/allocatestack.c
|
||||
+++ b/nptl/allocatestack.c
|
||||
@@ -116,6 +116,10 @@ get_cached_stack (size_t *sizep, void **memp)
|
||||
/* Release the lock early. */
|
||||
lll_unlock (GL (dl_stack_cache_lock), LLL_PRIVATE);
|
||||
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ GLRO (dl_debug_printf) ("TLS TCB reused from cache: 0x%lx\n",
|
||||
+ (unsigned long int) result);
|
||||
+
|
||||
/* Report size and location of the stack to the caller. */
|
||||
*sizep = result->stackblock_size;
|
||||
*memp = result->stackblock;
|
||||
@@ -293,6 +297,12 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
|
||||
stack cache nor will the memory (except the TLS memory) be freed. */
|
||||
pd->user_stack = true;
|
||||
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ GLRO (dl_debug_printf) (
|
||||
+ "TCB for user-supplied stack created: 0x%lx, stack=0x%lx, size=%lu\n",
|
||||
+ (unsigned long int) pd, (unsigned long int) pd->stackblock,
|
||||
+ (unsigned long int) pd->stackblock_size);
|
||||
+
|
||||
/* This is at least the second thread. */
|
||||
pd->header.multiple_threads = 1;
|
||||
|
||||
@@ -427,6 +437,10 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
|
||||
/* Don't allow setxid until cloned. */
|
||||
pd->setxid_futex = -1;
|
||||
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ GLRO (dl_debug_printf) ("TCB for new stack allocated: 0x%lx\n",
|
||||
+ (unsigned long int) pd);
|
||||
+
|
||||
/* Allocate the DTV for this thread. */
|
||||
if (_dl_allocate_tls (TLS_TPADJ (pd)) == NULL)
|
||||
{
|
||||
diff --git a/nptl/nptl-stack.c b/nptl/nptl-stack.c
|
||||
index 396f2261411aa079..f634d6a29fbdd624 100644
|
||||
--- a/nptl/nptl-stack.c
|
||||
+++ b/nptl/nptl-stack.c
|
||||
@@ -75,6 +75,11 @@ __nptl_free_stacks (size_t limit)
|
||||
/* Account for the freed memory. */
|
||||
GL (dl_stack_cache_actsize) -= curr->stackblock_size;
|
||||
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ GLRO (dl_debug_printf) (
|
||||
+ "TCB cache full, deallocating: TID=%ld, TCB=0x%lx\n",
|
||||
+ (long int) curr->tid, (unsigned long int) curr);
|
||||
+
|
||||
/* Free the memory associated with the ELF TLS. */
|
||||
_dl_deallocate_tls (TLS_TPADJ (curr), false);
|
||||
|
||||
@@ -96,6 +101,12 @@ static inline void
|
||||
__attribute ((always_inline))
|
||||
queue_stack (struct pthread *stack)
|
||||
{
|
||||
+ /* The 'stack' parameter is a pointer to the TCB (struct pthread),
|
||||
+ not just the stack. */
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ GLRO (dl_debug_printf) ("TCB deallocated into cache: TID=%ld, TCB=0x%lx\n",
|
||||
+ (long int) stack->tid, (unsigned long int) stack);
|
||||
+
|
||||
/* We unconditionally add the stack to the list. The memory may
|
||||
still be in use but it will not be reused until the kernel marks
|
||||
the stack as not used anymore. */
|
||||
@@ -123,8 +134,16 @@ __nptl_deallocate_stack (struct pthread *pd)
|
||||
if (__glibc_likely (! pd->user_stack))
|
||||
(void) queue_stack (pd);
|
||||
else
|
||||
- /* Free the memory associated with the ELF TLS. */
|
||||
- _dl_deallocate_tls (TLS_TPADJ (pd), false);
|
||||
+ {
|
||||
+ /* User-provided stack. We must not free it. But we must free
|
||||
+ the TLS memory. */
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ GLRO (dl_debug_printf) (
|
||||
+ "TCB for user-supplied stack deallocated: TID=%ld, TCB=0x%lx\n",
|
||||
+ (long int) pd->tid, (unsigned long int) pd);
|
||||
+ /* Free the memory associated with the ELF TLS. */
|
||||
+ _dl_deallocate_tls (TLS_TPADJ (pd), false);
|
||||
+ }
|
||||
|
||||
lll_unlock (GL (dl_stack_cache_lock), LLL_PRIVATE);
|
||||
}
|
||||
diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
|
||||
index a6254b1204ccfc80..b836a3ca9216245d 100644
|
||||
--- a/nptl/pthread_create.c
|
||||
+++ b/nptl/pthread_create.c
|
||||
@@ -364,6 +364,10 @@ start_thread (void *arg)
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ GLRO (dl_debug_printf) ("Thread starting: TID=%ld, TCB=0x%lx\n",
|
||||
+ (long int) pd->tid, (unsigned long int) pd);
|
||||
+
|
||||
/* Initialize resolver state pointer. */
|
||||
__resp = &pd->res;
|
||||
|
||||
diff --git a/nptl/tst-dl-debug-tid.c b/nptl/tst-dl-debug-tid.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..231fa43516b233b3
|
||||
--- /dev/null
|
||||
+++ b/nptl/tst-dl-debug-tid.c
|
||||
@@ -0,0 +1,69 @@
|
||||
+/* Test for thread ID logging in dynamic linker.
|
||||
+ Copyright (C) 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/>. */
|
||||
+
|
||||
+/* This test checks that the dynamic linker correctly logs thread creation
|
||||
+ and destruction. It creates a detached thread followed by a joinable
|
||||
+ thread to exercise different code paths. A barrier is used to ensure
|
||||
+ the detached thread has started before the joinable one is created,
|
||||
+ making the test more deterministic. The tst-dl-debug-tid.sh shell script
|
||||
+ wrapper then verifies the LD_DEBUG output. */
|
||||
+
|
||||
+#include <pthread.h>
|
||||
+#include <support/xthread.h>
|
||||
+#include <stdio.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+static void *
|
||||
+thread_function (void *arg)
|
||||
+{
|
||||
+ if (arg)
|
||||
+ pthread_barrier_wait ((pthread_barrier_t *) arg);
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ pthread_t thread1;
|
||||
+ pthread_attr_t attr;
|
||||
+ pthread_barrier_t barrier;
|
||||
+
|
||||
+ pthread_barrier_init (&barrier, NULL, 2);
|
||||
+
|
||||
+ /* A detached thread.
|
||||
+ * Deallocation is done by the thread itself upon exit. */
|
||||
+ xpthread_attr_init (&attr);
|
||||
+ xpthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
|
||||
+ /* We don't need the thread handle for the detached thread. */
|
||||
+ xpthread_create (&attr, thread_function, &barrier);
|
||||
+ xpthread_attr_destroy (&attr);
|
||||
+
|
||||
+ /* Wait for the detached thread to be executed. */
|
||||
+ pthread_barrier_wait (&barrier);
|
||||
+ pthread_barrier_destroy (&barrier);
|
||||
+
|
||||
+ /* A joinable thread.
|
||||
+ * Deallocation is done by the main thread in pthread_join. */
|
||||
+ thread1 = xpthread_create (NULL, thread_function, NULL);
|
||||
+
|
||||
+ xpthread_join (thread1);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
diff --git a/nptl/tst-dl-debug-tid.sh b/nptl/tst-dl-debug-tid.sh
|
||||
new file mode 100644
|
||||
index 0000000000000000..93d27134a09eaca7
|
||||
--- /dev/null
|
||||
+++ b/nptl/tst-dl-debug-tid.sh
|
||||
@@ -0,0 +1,72 @@
|
||||
+#!/bin/sh
|
||||
+# Test for thread ID logging in dynamic linker.
|
||||
+# Copyright (C) 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/>.
|
||||
+
|
||||
+# This script runs the tst-dl-debug-tid test case and verifies its
|
||||
+# LD_DEBUG output. It checks for thread creation/destruction messages
|
||||
+# to ensure the dynamic linker's thread-aware logging is working.
|
||||
+
|
||||
+set -e
|
||||
+
|
||||
+# Arguments are from Makefile.
|
||||
+common_objpfx="$1"
|
||||
+test_wrapper="$2"
|
||||
+rtld_prefix="$3"
|
||||
+test_wrapper_env="$4"
|
||||
+run_program_env="$5"
|
||||
+test_program="$6"
|
||||
+
|
||||
+debug_output="${common_objpfx}elf/tst-dl-debug-tid.debug"
|
||||
+rm -f "${debug_output}".*
|
||||
+
|
||||
+# Run the test program with LD_DEBUG=tls.
|
||||
+eval "${test_wrapper_env}" LD_DEBUG=tls LD_DEBUG_OUTPUT="${debug_output}" \
|
||||
+ "${test_wrapper}" "${rtld_prefix}" "${test_program}"
|
||||
+
|
||||
+debug_output=$(ls "${debug_output}".*)
|
||||
+# Check for the "Thread starting" message.
|
||||
+if ! grep -q 'Thread starting: TID=' "${debug_output}"; then
|
||||
+ echo "error: 'Thread starting' message not found"
|
||||
+ cat "${debug_output}"
|
||||
+ exit 1
|
||||
+fi
|
||||
+
|
||||
+# Check that we have a message where the PID (from prefix) is different
|
||||
+# from the TID (in the message). This indicates a worker thread log.
|
||||
+if ! grep 'Thread starting: TID=' "${debug_output}" | awk -F '[ \t:]+' '{
|
||||
+ sub(/,/, "", $5);
|
||||
+ sub(/TID=/, "", $5);
|
||||
+ if ($1 != $5)
|
||||
+ exit 0;
|
||||
+ exit 1
|
||||
+}'; then
|
||||
+ echo "error: No 'Thread starting' message from a worker thread found"
|
||||
+ cat "${debug_output}"
|
||||
+ exit 1
|
||||
+fi
|
||||
+
|
||||
+# We expect messages from thread creation and destruction.
|
||||
+if ! grep -q 'TCB allocated\|TCB deallocating\|TCB reused\|TCB deallocated' \
|
||||
+ "${debug_output}"; then
|
||||
+ echo "error: Expected TCB allocation/deallocation message not found"
|
||||
+ cat "${debug_output}"
|
||||
+ exit 1
|
||||
+fi
|
||||
+
|
||||
+cat "${debug_output}"
|
||||
+rm -f "${debug_output}"
|
||||
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
|
||||
index 5512c83d536874cb..3b10639e0a8a4de6 100644
|
||||
--- a/sysdeps/generic/ldsodefs.h
|
||||
+++ b/sysdeps/generic/ldsodefs.h
|
||||
@@ -547,8 +547,9 @@ struct rtld_global_ro
|
||||
#define DL_DEBUG_STATISTICS (1 << 7)
|
||||
#define DL_DEBUG_UNUSED (1 << 8)
|
||||
#define DL_DEBUG_SCOPES (1 << 9)
|
||||
-/* These two are used only internally. */
|
||||
+/* DL_DEBUG_HELP is only used internally. */
|
||||
#define DL_DEBUG_HELP (1 << 10)
|
||||
+#define DL_DEBUG_TLS (1 << 11)
|
||||
|
||||
/* Platform name. */
|
||||
EXTERN const char *_dl_platform;
|
||||
107
glibc-RHEL-159427-2.patch
Normal file
107
glibc-RHEL-159427-2.patch
Normal file
@ -0,0 +1,107 @@
|
||||
commit d4d472366ba69df7b14eba22a75f887b99855d70
|
||||
Author: Frédéric Bérat <fberat@redhat.com>
|
||||
Date: Thu Oct 2 19:15:29 2025 +0200
|
||||
|
||||
docs: Add dynamic linker environment variable docs
|
||||
|
||||
The Dynamic Linker chapter now includes a new section detailing
|
||||
environment variables that influence its behavior.
|
||||
|
||||
This new section documents the `LD_DEBUG` environment variable,
|
||||
explaining how to enable debugging output and listing its various
|
||||
keywords like `libs`, `reloc`, `files`, `symbols`, `bindings`,
|
||||
`versions`, `scopes`, `tls`, `all`, `statistics`, `unused`, and `help`.
|
||||
|
||||
It also documents `LD_DEBUG_OUTPUT`, which controls where the debug
|
||||
output is written, allowing redirection to a file with the process ID
|
||||
appended.
|
||||
|
||||
This provides users with essential information for controlling and
|
||||
debugging the dynamic linker.
|
||||
|
||||
Reviewed-by: DJ Delorie <dj@redhat.com>
|
||||
|
||||
diff --git a/manual/dynlink.texi b/manual/dynlink.texi
|
||||
index 86917c7452ca9f96..be6b0193339d3863 100644
|
||||
--- a/manual/dynlink.texi
|
||||
+++ b/manual/dynlink.texi
|
||||
@@ -14,6 +14,8 @@ Dynamic linkers are sometimes called @dfn{dynamic loaders}.
|
||||
|
||||
@menu
|
||||
* Dynamic Linker Invocation:: Explicit invocation of the dynamic linker.
|
||||
+* Dynamic Linker Environment Variables:: Environment variables that control the
|
||||
+ dynamic linker.
|
||||
* Dynamic Linker Introspection:: Interfaces for querying mapping information.
|
||||
@end menu
|
||||
|
||||
@@ -348,6 +350,70 @@ probed CPU are omitted. Nothing is printed if the system does not
|
||||
support the XGETBV instruction.
|
||||
@end table
|
||||
|
||||
+@node Dynamic Linker Environment Variables
|
||||
+@section Dynamic Linker Environment Variables
|
||||
+
|
||||
+The behavior of the dynamic linker can be modified through various environment
|
||||
+variables.
|
||||
+
|
||||
+@table @code
|
||||
+@item LD_DEBUG
|
||||
+@cindex @code{LD_DEBUG} environment variable
|
||||
+The @env{LD_DEBUG} environment variable can be set to a comma-separated list
|
||||
+of keywords to enable debugging output from the dynamic linker. Setting it to
|
||||
+@code{help} will display a list of all available keywords. The output is
|
||||
+written to standard output by default.
|
||||
+
|
||||
+@table @code
|
||||
+@item libs
|
||||
+Display library search paths.
|
||||
+
|
||||
+@item reloc
|
||||
+Display relocation processing.
|
||||
+
|
||||
+@item files
|
||||
+Display progress for input file processing.
|
||||
+
|
||||
+@item symbols
|
||||
+Display symbol table processing.
|
||||
+
|
||||
+@item bindings
|
||||
+Display information about symbol binding.
|
||||
+
|
||||
+@item versions
|
||||
+Display version dependencies.
|
||||
+
|
||||
+@item scopes
|
||||
+Display scope information.
|
||||
+
|
||||
+@item tls
|
||||
+Display information about Thread-Local Storage (TLS) handling, including TCB
|
||||
+allocation, deallocation, and reuse. This is useful for debugging issues
|
||||
+related to thread creation and lifecycle.
|
||||
+
|
||||
+@item all
|
||||
+All previous options combined.
|
||||
+
|
||||
+@item statistics
|
||||
+Display relocation statistics.
|
||||
+
|
||||
+@item unused
|
||||
+Determined unused DSOs.
|
||||
+
|
||||
+@item help
|
||||
+Display a help message with all available options and exit.
|
||||
+@end table
|
||||
+
|
||||
+@item LD_DEBUG_OUTPUT
|
||||
+@cindex @code{LD_DEBUG_OUTPUT} environment variable
|
||||
+If @env{LD_DEBUG} is set, the output is written to standard output by
|
||||
+default. If @env{LD_DEBUG_OUTPUT} is set, the output is written to the
|
||||
+file specified by its value, with the process ID appended. For example, if
|
||||
+@env{LD_DEBUG_OUTPUT} is set to @file{/tmp/glibc.debug}, the output will be
|
||||
+written to a file named @file{/tmp/glibc.debug.12345}, where @code{12345} is
|
||||
+the process ID.
|
||||
+@end table
|
||||
+
|
||||
@node Dynamic Linker Introspection
|
||||
@section Dynamic Linker Introspection
|
||||
|
||||
70
glibc-RHEL-159427-3.patch
Normal file
70
glibc-RHEL-159427-3.patch
Normal file
@ -0,0 +1,70 @@
|
||||
commit 20092f2ef601aef57cc184cbacd7cab39bba5a25
|
||||
Author: Yury Khrustalev <yury.khrustalev@arm.com>
|
||||
Date: Mon Dec 1 10:09:14 2025 +0000
|
||||
|
||||
nptl: tests: Fix test-wrapper use in tst-dl-debug-tid.sh
|
||||
|
||||
Test wrapper script was used twice: once to run the test
|
||||
command and second time within the text command which
|
||||
seems unnecessary and results in false errors when running
|
||||
this test.
|
||||
|
||||
Fixes 332f8e62afef53492dd8285490bcf7aeef18c80a
|
||||
|
||||
Reviewed-by: Frédéric Bérat <fberat@redhat.com>
|
||||
|
||||
diff --git a/nptl/Makefile b/nptl/Makefile
|
||||
index 89e2b8c4523714ab..44bc59a2d3fea89f 100644
|
||||
--- a/nptl/Makefile
|
||||
+++ b/nptl/Makefile
|
||||
@@ -696,8 +696,8 @@ tst-stackguard1-static-ARGS = --command "$(objpfx)tst-stackguard1-static --child
|
||||
|
||||
ifeq ($(run-built-tests),yes)
|
||||
$(objpfx)tst-dl-debug-tid.out: tst-dl-debug-tid.sh $(objpfx)tst-dl-debug-tid
|
||||
- $(SHELL) $< $(common-objpfx) '$(test-wrapper)' '$(rtld-prefix)' \
|
||||
- '$(test-wrapper-env)' '$(run-program-env)' \
|
||||
+ $(SHELL) $< $(common-objpfx) '$(test-wrapper-env)' '$(rtld-prefix)' \
|
||||
+ '$(run-program-env)' \
|
||||
$(objpfx)tst-dl-debug-tid > $@; $(evaluate-test)
|
||||
|
||||
$(objpfx)tst-oddstacklimit.out: $(objpfx)tst-oddstacklimit $(objpfx)tst-basic1
|
||||
diff --git a/nptl/tst-dl-debug-tid.sh b/nptl/tst-dl-debug-tid.sh
|
||||
index 93d27134a09eaca7..9ee31ac4f241f0b4 100644
|
||||
--- a/nptl/tst-dl-debug-tid.sh
|
||||
+++ b/nptl/tst-dl-debug-tid.sh
|
||||
@@ -25,18 +25,17 @@ set -e
|
||||
|
||||
# Arguments are from Makefile.
|
||||
common_objpfx="$1"
|
||||
-test_wrapper="$2"
|
||||
+test_wrapper_env="$2"
|
||||
rtld_prefix="$3"
|
||||
-test_wrapper_env="$4"
|
||||
-run_program_env="$5"
|
||||
-test_program="$6"
|
||||
+run_program_env="$4"
|
||||
+test_program="$5"
|
||||
|
||||
debug_output="${common_objpfx}elf/tst-dl-debug-tid.debug"
|
||||
rm -f "${debug_output}".*
|
||||
|
||||
# Run the test program with LD_DEBUG=tls.
|
||||
eval "${test_wrapper_env}" LD_DEBUG=tls LD_DEBUG_OUTPUT="${debug_output}" \
|
||||
- "${test_wrapper}" "${rtld_prefix}" "${test_program}"
|
||||
+ "${rtld_prefix}" "${test_program}"
|
||||
|
||||
debug_output=$(ls "${debug_output}".*)
|
||||
# Check for the "Thread starting" message.
|
||||
@@ -49,9 +48,9 @@ fi
|
||||
# Check that we have a message where the PID (from prefix) is different
|
||||
# from the TID (in the message). This indicates a worker thread log.
|
||||
if ! grep 'Thread starting: TID=' "${debug_output}" | awk -F '[ \t:]+' '{
|
||||
- sub(/,/, "", $5);
|
||||
- sub(/TID=/, "", $5);
|
||||
- if ($1 != $5)
|
||||
+ sub(/,/, "", $4);
|
||||
+ sub(/TID=/, "", $4);
|
||||
+ if ($1 != $4)
|
||||
exit 0;
|
||||
exit 1
|
||||
}'; then
|
||||
681
glibc-RHEL-159427-4.patch
Normal file
681
glibc-RHEL-159427-4.patch
Normal file
@ -0,0 +1,681 @@
|
||||
commit 1e47dbcce4d5cf2ce71377e729014e454a3e15ae
|
||||
Author: Frédéric Bérat <fberat@redhat.com>
|
||||
Date: Fri Dec 12 16:19:43 2025 +0100
|
||||
|
||||
elf(tls): Add debug logging for TLS operations
|
||||
|
||||
This commit introduces extensive debug logging for thread-local storage
|
||||
(TLS) operations within the dynamic linker. When `LD_DEBUG=tls` is
|
||||
enabled, messages are printed for:
|
||||
- TLS module assignment and release.
|
||||
- DTV (Dynamic Thread Vector) resizing events.
|
||||
- TLS block allocations and deallocations.
|
||||
- `__tls_get_addr` slow path events (DTV updates, lazy allocations, and
|
||||
static TLS usage).
|
||||
|
||||
The log format is standardized to use a "tls: " prefix and identifies
|
||||
modules using the "modid %lu" convention. To aid in debugging
|
||||
multithreaded applications, thread-specific logs include the Thread
|
||||
Control Block (TCB) address to identify the context of the operation.
|
||||
|
||||
A new test module `tst-tls-debug-mod.c` and a corresponding shell script
|
||||
`tst-tls-debug-recursive.sh` have been added. Additionally, the existing
|
||||
`tst-dl-debug-tid` NPTL test has been updated to verify these TLS debug
|
||||
messages in a multithreaded context.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
Conflicts:
|
||||
elf/Makefile
|
||||
(fixup context)
|
||||
elf/dl-tls.c
|
||||
(missing 5e249192cac7354af02a7347a0d8c984e0c88ed3 downstream)
|
||||
sysdeps/x86_64/dl-tls.c
|
||||
(missing 5e249192cac7354af02a7347a0d8c984e0c88ed3 downstream)
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index 73c426ae9c1da05c..8651a96bf260416c 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -1352,6 +1352,7 @@ $(objpfx)tst-glibcelf.out: tst-glibcelf.py elf.h $(..)/scripts/glibcelf.py \
|
||||
|
||||
ifeq ($(run-built-tests),yes)
|
||||
tests-special += $(objpfx)tst-tls-allocation-failure-static-patched.out
|
||||
+tests-special += $(objpfx)tst-tls-debug-recursive.out
|
||||
endif
|
||||
|
||||
# The test requires shared _and_ PIE because the executable
|
||||
@@ -3339,3 +3340,15 @@ tst-tls22-mod1.so-no-z-defs = yes
|
||||
tst-tls22-mod1-gnu2.so-no-z-defs = yes
|
||||
tst-tls22-mod2.so-no-z-defs = yes
|
||||
tst-tls22-mod2-gnu2.so-no-z-defs = yes
|
||||
+
|
||||
+ifeq ($(run-built-tests),yes)
|
||||
+$(objpfx)tst-tls-debug-recursive.out: tst-tls-debug-recursive.sh \
|
||||
+ $(objpfx)tst-recursive-tls \
|
||||
+ $(objpfx)tst-recursive-tlsmallocmod.so \
|
||||
+ $(patsubst %,$(objpfx)tst-recursive-tlsmod%.so, \
|
||||
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
|
||||
+ $(SHELL) $< $(common-objpfx) '$(test-wrapper-env)' \
|
||||
+ '$(rtld-prefix)' '$(run_program_env)' \
|
||||
+ $(objpfx)tst-recursive-tls > $@; \
|
||||
+ $(evaluate-test)
|
||||
+endif
|
||||
diff --git a/elf/dl-close.c b/elf/dl-close.c
|
||||
index fb27a1231c1c5b66..708e88c68f7d5482 100644
|
||||
--- a/elf/dl-close.c
|
||||
+++ b/elf/dl-close.c
|
||||
@@ -74,6 +74,11 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp,
|
||||
if (__glibc_likely (old_map != NULL))
|
||||
{
|
||||
/* Mark the entry as unused. These can be read concurrently. */
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf (
|
||||
+ "tls: release modid %lu from %s [%ld]\n",
|
||||
+ (unsigned long int) idx, DSO_FILENAME (old_map->l_name),
|
||||
+ (long int) old_map->l_ns);
|
||||
atomic_store_relaxed (&listp->slotinfo[idx - disp].gen,
|
||||
GL(dl_tls_generation) + 1);
|
||||
atomic_store_relaxed (&listp->slotinfo[idx - disp].map, NULL);
|
||||
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
|
||||
index 85db1969b921d9e6..41f76bff9a1bbebe 100644
|
||||
--- a/elf/dl-tls.c
|
||||
+++ b/elf/dl-tls.c
|
||||
@@ -209,6 +209,12 @@ _dl_assign_tls_modid (struct link_map *l)
|
||||
}
|
||||
|
||||
l->l_tls_modid = result;
|
||||
+
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf ("tls: assign modid %lu to %s [%ld]\n",
|
||||
+ (unsigned long int) result,
|
||||
+ DSO_FILENAME (l->l_name),
|
||||
+ (long int) l->l_ns);
|
||||
}
|
||||
|
||||
|
||||
@@ -498,7 +504,7 @@ _dl_allocate_tls_storage (void)
|
||||
if (result == NULL)
|
||||
free (allocated);
|
||||
else if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
- _dl_debug_printf ("TCB allocated: 0x%lx\n", (unsigned long int) result);
|
||||
+ _dl_debug_printf ("tls: allocate TCB 0x%lx\n", (unsigned long int) result);
|
||||
|
||||
_dl_tls_allocate_end ();
|
||||
return result;
|
||||
@@ -511,13 +517,18 @@ extern dtv_t _dl_static_dtv[];
|
||||
#endif
|
||||
|
||||
static dtv_t *
|
||||
-_dl_resize_dtv (dtv_t *dtv, size_t max_modid)
|
||||
+_dl_resize_dtv (dtv_t *dtv, size_t max_modid, void *tcb)
|
||||
{
|
||||
/* Resize the dtv. */
|
||||
dtv_t *newp;
|
||||
size_t newsize = max_modid + DTV_SURPLUS;
|
||||
size_t oldsize = dtv[-1].counter;
|
||||
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf ("tls: DTV resized for TCB 0x%lx: oldsize=%lu, newsize=%lu\n",
|
||||
+ (unsigned long int) tcb,
|
||||
+ (unsigned long int) oldsize, (unsigned long int) newsize);
|
||||
+
|
||||
_dl_tls_allocate_begin ();
|
||||
if (dtv == GL(dl_initial_dtv))
|
||||
{
|
||||
@@ -586,7 +597,7 @@ _dl_allocate_tls_init (void *result, bool main_thread)
|
||||
if (dtv[-1].counter < GL(dl_tls_max_dtv_idx))
|
||||
{
|
||||
/* Resize the dtv. */
|
||||
- dtv = _dl_resize_dtv (dtv, GL(dl_tls_max_dtv_idx));
|
||||
+ dtv = _dl_resize_dtv (dtv, GL(dl_tls_max_dtv_idx), result);
|
||||
|
||||
/* Install this new dtv in the thread data structures. */
|
||||
INSTALL_DTV (result, &dtv[-1]);
|
||||
@@ -677,9 +688,14 @@ rtld_hidden_def (_dl_allocate_tls_init)
|
||||
void *
|
||||
_dl_allocate_tls (void *mem)
|
||||
{
|
||||
- return _dl_allocate_tls_init (mem == NULL
|
||||
- ? _dl_allocate_tls_storage ()
|
||||
- : allocate_dtv (mem), false);
|
||||
+ void *result = _dl_allocate_tls_init (mem == NULL
|
||||
+ ? _dl_allocate_tls_storage ()
|
||||
+ : allocate_dtv (mem), false);
|
||||
+ if (__glibc_unlikely (result != NULL
|
||||
+ && (GLRO (dl_debug_mask) & DL_DEBUG_TLS)))
|
||||
+ _dl_debug_printf ("tls: TLS initialized for TCB 0x%lx\n",
|
||||
+ (unsigned long int) result);
|
||||
+ return result;
|
||||
}
|
||||
rtld_hidden_def (_dl_allocate_tls)
|
||||
|
||||
@@ -688,14 +704,22 @@ void
|
||||
_dl_deallocate_tls (void *tcb, bool dealloc_tcb)
|
||||
{
|
||||
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
- _dl_debug_printf ("TCB deallocating: 0x%lx (dealloc_tcb=%d)\n",
|
||||
+ _dl_debug_printf ("tls: deallocate TCB 0x%lx (dealloc_tcb=%d)\n",
|
||||
(unsigned long int) tcb, dealloc_tcb);
|
||||
|
||||
dtv_t *dtv = GET_DTV (tcb);
|
||||
|
||||
/* We need to free the memory allocated for non-static TLS. */
|
||||
for (size_t cnt = 0; cnt < dtv[-1].counter; ++cnt)
|
||||
- free (dtv[1 + cnt].pointer.to_free);
|
||||
+ {
|
||||
+ if (dtv[1 + cnt].pointer.to_free != NULL
|
||||
+ && __glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf (
|
||||
+ "tls: deallocate block 0x%lx for modid %lu; TCB=0x%lx\n",
|
||||
+ (unsigned long int) dtv[1 + cnt].pointer.to_free,
|
||||
+ (unsigned long int) (1 + cnt), (unsigned long int) tcb);
|
||||
+ free (dtv[1 + cnt].pointer.to_free);
|
||||
+ }
|
||||
|
||||
/* The array starts with dtv[-1]. */
|
||||
if (dtv != GL(dl_initial_dtv))
|
||||
@@ -767,6 +791,12 @@ allocate_and_init (struct link_map *map)
|
||||
(map->l_tls_align, map->l_tls_blocksize);
|
||||
if (result.val == NULL)
|
||||
oom ();
|
||||
+ else if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf ("tls: allocate block 0x%lx for modid %lu; size=%lu, TCB=0x%lx\n",
|
||||
+ (unsigned long int) result.to_free,
|
||||
+ (unsigned long int) map->l_tls_modid,
|
||||
+ (unsigned long int) map->l_tls_blocksize,
|
||||
+ (unsigned long int) THREAD_SELF);
|
||||
|
||||
/* Initialize the memory. */
|
||||
memset (__mempcpy (result.val, map->l_tls_initimage,
|
||||
@@ -868,7 +898,7 @@ _dl_update_slotinfo (unsigned long int req_modid, size_t new_gen)
|
||||
continue;
|
||||
|
||||
/* Resizing the dtv aborts on failure: bug 16134. */
|
||||
- dtv = _dl_resize_dtv (dtv, max_modid);
|
||||
+ dtv = _dl_resize_dtv (dtv, max_modid, THREAD_SELF);
|
||||
|
||||
assert (modid <= dtv[-1].counter);
|
||||
|
||||
@@ -889,6 +919,12 @@ _dl_update_slotinfo (unsigned long int req_modid, size_t new_gen)
|
||||
least some dynamic TLS usage by interposed mallocs. */
|
||||
if (dtv[modid].pointer.to_free != NULL)
|
||||
{
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf (
|
||||
+ "tls: DTV update for TCB 0x%lx: modid %lu deallocated block 0x%lx\n",
|
||||
+ (unsigned long int) THREAD_SELF,
|
||||
+ (unsigned long int) modid,
|
||||
+ (unsigned long int) dtv[modid].pointer.to_free);
|
||||
_dl_tls_allocate_begin ();
|
||||
free (dtv[modid].pointer.to_free);
|
||||
_dl_tls_allocate_end ();
|
||||
@@ -969,6 +1005,11 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map)
|
||||
dtv[GET_ADDR_MODULE].pointer.to_free = NULL;
|
||||
dtv[GET_ADDR_MODULE].pointer.val = p;
|
||||
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf ("tls: modid %lu using static TLS; TCB=0x%lx\n",
|
||||
+ (unsigned long int) GET_ADDR_MODULE,
|
||||
+ (unsigned long int) THREAD_SELF);
|
||||
+
|
||||
return (char *) p + GET_ADDR_OFFSET;
|
||||
}
|
||||
else
|
||||
@@ -1024,18 +1065,28 @@ __tls_get_addr (GET_ADDR_ARGS)
|
||||
{
|
||||
if (_dl_tls_allocate_active ()
|
||||
&& GET_ADDR_MODULE < _dl_tls_initial_modid_limit)
|
||||
+ {
|
||||
/* This is a reentrant __tls_get_addr call, but we can
|
||||
satisfy it because it's an initially-loaded module ID.
|
||||
These TLS slotinfo slots do not change, so the
|
||||
out-of-date generation counter does not matter. However,
|
||||
if not in a TLS update, still update_get_addr below, to
|
||||
get off the slow path eventually. */
|
||||
- ;
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf ("tls: modid %lu reentrant usage; TCB=0x%lx\n",
|
||||
+ (unsigned long int) GET_ADDR_MODULE,
|
||||
+ (unsigned long int) THREAD_SELF);
|
||||
+ }
|
||||
else
|
||||
{
|
||||
/* Update DTV up to the global generation, see CONCURRENCY NOTES
|
||||
in _dl_update_slotinfo. */
|
||||
gen = atomic_load_acquire (&GL(dl_tls_generation));
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf (
|
||||
+ "tls: modid %lu update DTV to generation %lu; TCB=0x%lx\n",
|
||||
+ (unsigned long int) GET_ADDR_MODULE, (unsigned long int) gen,
|
||||
+ (unsigned long int) THREAD_SELF);
|
||||
return update_get_addr (GET_ADDR_PARAM, gen);
|
||||
}
|
||||
}
|
||||
@@ -1043,7 +1094,13 @@ __tls_get_addr (GET_ADDR_ARGS)
|
||||
void *p = dtv[GET_ADDR_MODULE].pointer.val;
|
||||
|
||||
if (__glibc_unlikely (p == TLS_DTV_UNALLOCATED))
|
||||
- return tls_get_addr_tail (GET_ADDR_PARAM, dtv, NULL);
|
||||
+ {
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf ("tls: modid %lu lazy allocation; TCB=0x%lx\n",
|
||||
+ (unsigned long int) GET_ADDR_MODULE,
|
||||
+ (unsigned long int) THREAD_SELF);
|
||||
+ return tls_get_addr_tail (GET_ADDR_PARAM, dtv, NULL);
|
||||
+ }
|
||||
|
||||
return (char *) p + GET_ADDR_OFFSET;
|
||||
}
|
||||
@@ -1113,6 +1170,10 @@ _dl_tls_initial_modid_limit_setup (void)
|
||||
break;
|
||||
}
|
||||
_dl_tls_initial_modid_limit = idx;
|
||||
+
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf ("tls: initial modid limit set to %lu\n",
|
||||
+ (unsigned long int) idx);
|
||||
}
|
||||
|
||||
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index c9082bb31643d9ab..8b09d8389a7050d2 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -1237,6 +1237,11 @@ rtld_setup_main_map (struct link_map *main_map)
|
||||
|
||||
/* This image gets the ID one. */
|
||||
GL(dl_tls_max_dtv_idx) = main_map->l_tls_modid = 1;
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf ("tls: assign modid %lu to %s [%ld]\n",
|
||||
+ (unsigned long int) main_map->l_tls_modid,
|
||||
+ DSO_FILENAME (main_map->l_name),
|
||||
+ (long int) main_map->l_ns);
|
||||
}
|
||||
break;
|
||||
|
||||
diff --git a/elf/tst-recursive-tlsmodN.c b/elf/tst-recursive-tlsmodN.c
|
||||
index bb7592aee6ed347e..921980605cf32a38 100644
|
||||
--- a/elf/tst-recursive-tlsmodN.c
|
||||
+++ b/elf/tst-recursive-tlsmodN.c
|
||||
@@ -19,10 +19,10 @@
|
||||
/* Compiled with VAR and FUNC set via -D. FUNC requires some
|
||||
relocation against TLS variable VAR. */
|
||||
|
||||
-__thread int VAR;
|
||||
+__thread char VAR[32768];
|
||||
|
||||
int
|
||||
FUNC (void)
|
||||
{
|
||||
- return VAR;
|
||||
+ return VAR[0];
|
||||
}
|
||||
diff --git a/elf/tst-tls-debug-recursive.sh b/elf/tst-tls-debug-recursive.sh
|
||||
new file mode 100755
|
||||
index 0000000000000000..083e716f7216ffd5
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-tls-debug-recursive.sh
|
||||
@@ -0,0 +1,83 @@
|
||||
+#!/bin/sh
|
||||
+# Test for TLS logging in dynamic linker.
|
||||
+# 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/>.
|
||||
+
|
||||
+# This script runs the tst-tls-debug-recursive test case and verifies its
|
||||
+# LD_DEBUG=tls output. It checks for various TLS-related messages
|
||||
+# to ensure the dynamic linker's TLS logging is working correctly.
|
||||
+
|
||||
+set -e
|
||||
+common_objpfx="$1"
|
||||
+test_wrapper_env="$2"
|
||||
+rtld_prefix="$3"
|
||||
+run_program_env="$4"
|
||||
+test_program="$5"
|
||||
+
|
||||
+debug_output="${common_objpfx}elf/tst-tls-debug-recursive.debug"
|
||||
+rm -f "${debug_output}".*
|
||||
+
|
||||
+# Run the test program with LD_DEBUG=tls.
|
||||
+eval "${test_wrapper_env}" LD_DEBUG=tls LD_DEBUG_OUTPUT="${debug_output}" \
|
||||
+ "${rtld_prefix}" "${test_program}"
|
||||
+
|
||||
+debug_output=$(ls "${debug_output}".*)
|
||||
+
|
||||
+fail=0
|
||||
+
|
||||
+# Check for expected messages
|
||||
+if ! grep -q 'tls: DTV resized for TCB 0x.*: oldsize' "${debug_output}"; then
|
||||
+ echo "FAIL: DTV resized message not found"
|
||||
+ fail=1
|
||||
+fi
|
||||
+
|
||||
+if ! grep -q 'tls: DTV update for TCB 0x.*: modid .* deallocated block' "${debug_output}"; then
|
||||
+ echo "FAIL: module deallocated during DTV update message not found"
|
||||
+ fail=1
|
||||
+fi
|
||||
+
|
||||
+if ! grep -q 'tls: assign modid .* to' "${debug_output}"; then
|
||||
+ echo "FAIL: module assigned message not found"
|
||||
+ fail=1
|
||||
+fi
|
||||
+
|
||||
+if ! grep -q 'tls: allocate block .* for modid .* size=.*, TCB=0x' "${debug_output}"; then
|
||||
+ echo "FAIL: module allocated message not found"
|
||||
+ fail=1
|
||||
+fi
|
||||
+
|
||||
+if ! grep -q 'tls: modid .* update DTV to generation .* TCB=0x' "${debug_output}"; then
|
||||
+ echo "FAIL: update DTV message not found"
|
||||
+ fail=1
|
||||
+fi
|
||||
+
|
||||
+if ! grep -q 'tls: initial modid limit set to' "${debug_output}"; then
|
||||
+ echo "FAIL: initial modid limit message not found"
|
||||
+ fail=1
|
||||
+fi
|
||||
+
|
||||
+if [ $fail -ne 0 ]; then
|
||||
+ echo "Test FAILED"
|
||||
+ cat "${debug_output}"
|
||||
+ rm -f "${debug_output}"
|
||||
+ exit 1
|
||||
+fi
|
||||
+
|
||||
+echo "Test PASSED"
|
||||
+cat "${debug_output}"
|
||||
+rm -f "${debug_output}"
|
||||
+exit 0
|
||||
diff --git a/nptl/Makefile b/nptl/Makefile
|
||||
index 44bc59a2d3fea89f..fcb0582e3a1c2b1d 100644
|
||||
--- a/nptl/Makefile
|
||||
+++ b/nptl/Makefile
|
||||
@@ -273,6 +273,7 @@ CFLAGS-tst-thread-exit-clobber.o = -std=gnu++11
|
||||
LDLIBS-tst-thread-exit-clobber = -lstdc++
|
||||
CFLAGS-tst-minstack-throw.o = -std=gnu++11
|
||||
LDLIBS-tst-minstack-throw = -lstdc++
|
||||
+LDLIBS-tst-dl-debug-tid = $(libdl)
|
||||
|
||||
tests = \
|
||||
tst-attr2 \
|
||||
@@ -483,6 +484,7 @@ modules-names = \
|
||||
tst-compat-forwarder-mod \
|
||||
tst-execstack-threads-mod \
|
||||
tst-stack4mod \
|
||||
+ tst-tls-debug-mod \
|
||||
tst-tls3mod \
|
||||
tst-tls5mod \
|
||||
tst-tls5moda \
|
||||
@@ -695,7 +697,8 @@ tst-stackguard1-ARGS = --command "$(host-test-program-cmd) --child"
|
||||
tst-stackguard1-static-ARGS = --command "$(objpfx)tst-stackguard1-static --child"
|
||||
|
||||
ifeq ($(run-built-tests),yes)
|
||||
-$(objpfx)tst-dl-debug-tid.out: tst-dl-debug-tid.sh $(objpfx)tst-dl-debug-tid
|
||||
+$(objpfx)tst-dl-debug-tid.out: tst-dl-debug-tid.sh $(objpfx)tst-dl-debug-tid \
|
||||
+ $(objpfx)tst-tls-debug-mod.so
|
||||
$(SHELL) $< $(common-objpfx) '$(test-wrapper-env)' '$(rtld-prefix)' \
|
||||
'$(run-program-env)' \
|
||||
$(objpfx)tst-dl-debug-tid > $@; $(evaluate-test)
|
||||
diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
|
||||
index b26ebc8680d7280a..2876fd7fe306c639 100644
|
||||
--- a/nptl/allocatestack.c
|
||||
+++ b/nptl/allocatestack.c
|
||||
@@ -117,7 +117,7 @@ get_cached_stack (size_t *sizep, void **memp)
|
||||
lll_unlock (GL (dl_stack_cache_lock), LLL_PRIVATE);
|
||||
|
||||
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
- GLRO (dl_debug_printf) ("TLS TCB reused from cache: 0x%lx\n",
|
||||
+ GLRO (dl_debug_printf) ("tls: TCB reused from cache: 0x%lx\n",
|
||||
(unsigned long int) result);
|
||||
|
||||
/* Report size and location of the stack to the caller. */
|
||||
@@ -299,9 +299,9 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
|
||||
|
||||
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
GLRO (dl_debug_printf) (
|
||||
- "TCB for user-supplied stack created: 0x%lx, stack=0x%lx, size=%lu\n",
|
||||
- (unsigned long int) pd, (unsigned long int) pd->stackblock,
|
||||
- (unsigned long int) pd->stackblock_size);
|
||||
+ "tls: TCB created (user-supplied stack); stack=0x%lx, size=%lu, TCB=0x%lx\n",
|
||||
+ (unsigned long int) pd->stackblock,
|
||||
+ (unsigned long int) pd->stackblock_size, (unsigned long int) pd);
|
||||
|
||||
/* This is at least the second thread. */
|
||||
pd->header.multiple_threads = 1;
|
||||
@@ -438,7 +438,7 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
|
||||
pd->setxid_futex = -1;
|
||||
|
||||
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
- GLRO (dl_debug_printf) ("TCB for new stack allocated: 0x%lx\n",
|
||||
+ GLRO (dl_debug_printf) ("tls: TCB allocated (new stack): 0x%lx\n",
|
||||
(unsigned long int) pd);
|
||||
|
||||
/* Allocate the DTV for this thread. */
|
||||
diff --git a/nptl/nptl-stack.c b/nptl/nptl-stack.c
|
||||
index f634d6a29fbdd624..3993a7a90d14d80d 100644
|
||||
--- a/nptl/nptl-stack.c
|
||||
+++ b/nptl/nptl-stack.c
|
||||
@@ -77,7 +77,7 @@ __nptl_free_stacks (size_t limit)
|
||||
|
||||
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
GLRO (dl_debug_printf) (
|
||||
- "TCB cache full, deallocating: TID=%ld, TCB=0x%lx\n",
|
||||
+ "tls: TCB deallocating from full cache; TID=%ld, TCB=0x%lx\n",
|
||||
(long int) curr->tid, (unsigned long int) curr);
|
||||
|
||||
/* Free the memory associated with the ELF TLS. */
|
||||
@@ -104,7 +104,7 @@ queue_stack (struct pthread *stack)
|
||||
/* The 'stack' parameter is a pointer to the TCB (struct pthread),
|
||||
not just the stack. */
|
||||
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
- GLRO (dl_debug_printf) ("TCB deallocated into cache: TID=%ld, TCB=0x%lx\n",
|
||||
+ GLRO (dl_debug_printf) ("tls: TCB deallocated into cache; TID=%ld, TCB=0x%lx\n",
|
||||
(long int) stack->tid, (unsigned long int) stack);
|
||||
|
||||
/* We unconditionally add the stack to the list. The memory may
|
||||
@@ -139,7 +139,7 @@ __nptl_deallocate_stack (struct pthread *pd)
|
||||
the TLS memory. */
|
||||
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
GLRO (dl_debug_printf) (
|
||||
- "TCB for user-supplied stack deallocated: TID=%ld, TCB=0x%lx\n",
|
||||
+ "tls: TCB deallocated (user-supplied stack); TID=%ld, TCB=0x%lx\n",
|
||||
(long int) pd->tid, (unsigned long int) pd);
|
||||
/* Free the memory associated with the ELF TLS. */
|
||||
_dl_deallocate_tls (TLS_TPADJ (pd), false);
|
||||
diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
|
||||
index b836a3ca9216245d..381ed68c05f58a1e 100644
|
||||
--- a/nptl/pthread_create.c
|
||||
+++ b/nptl/pthread_create.c
|
||||
@@ -365,7 +365,7 @@ start_thread (void *arg)
|
||||
}
|
||||
|
||||
if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
- GLRO (dl_debug_printf) ("Thread starting: TID=%ld, TCB=0x%lx\n",
|
||||
+ GLRO (dl_debug_printf) ("tls: thread starting; TID=%ld, TCB=0x%lx\n",
|
||||
(long int) pd->tid, (unsigned long int) pd);
|
||||
|
||||
/* Initialize resolver state pointer. */
|
||||
diff --git a/nptl/tst-dl-debug-tid.c b/nptl/tst-dl-debug-tid.c
|
||||
index 231fa43516b233b3..f3b443f9786a9a99 100644
|
||||
--- a/nptl/tst-dl-debug-tid.c
|
||||
+++ b/nptl/tst-dl-debug-tid.c
|
||||
@@ -27,12 +27,25 @@
|
||||
#include <support/xthread.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
+#include <dlfcn.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
+#include <support/check.h>
|
||||
|
||||
static void *
|
||||
thread_function (void *arg)
|
||||
{
|
||||
if (arg)
|
||||
pthread_barrier_wait ((pthread_barrier_t *) arg);
|
||||
+
|
||||
+ /* Load a module with TLS to verify allocation/deallocation logs. */
|
||||
+ void *h = xdlopen ("tst-tls-debug-mod.so", RTLD_NOW);
|
||||
+
|
||||
+ /* Call a function that accesses TLS. */
|
||||
+ int (*fp) (void) = (int (*) (void)) xdlsym (h, "in_dso");
|
||||
+ TEST_COMPARE (fp (), 0);
|
||||
+
|
||||
+ xdlclose (h);
|
||||
+
|
||||
return NULL;
|
||||
}
|
||||
|
||||
diff --git a/nptl/tst-dl-debug-tid.sh b/nptl/tst-dl-debug-tid.sh
|
||||
index 9ee31ac4f241f0b4..8708ac9f2ea25c9e 100644
|
||||
--- a/nptl/tst-dl-debug-tid.sh
|
||||
+++ b/nptl/tst-dl-debug-tid.sh
|
||||
@@ -39,7 +39,7 @@ eval "${test_wrapper_env}" LD_DEBUG=tls LD_DEBUG_OUTPUT="${debug_output}" \
|
||||
|
||||
debug_output=$(ls "${debug_output}".*)
|
||||
# Check for the "Thread starting" message.
|
||||
-if ! grep -q 'Thread starting: TID=' "${debug_output}"; then
|
||||
+if ! grep -q 'tls: thread starting; TID=.*, TCB=0x' "${debug_output}"; then
|
||||
echo "error: 'Thread starting' message not found"
|
||||
cat "${debug_output}"
|
||||
exit 1
|
||||
@@ -47,10 +47,10 @@ fi
|
||||
|
||||
# Check that we have a message where the PID (from prefix) is different
|
||||
# from the TID (in the message). This indicates a worker thread log.
|
||||
-if ! grep 'Thread starting: TID=' "${debug_output}" | awk -F '[ \t:]+' '{
|
||||
- sub(/,/, "", $4);
|
||||
- sub(/TID=/, "", $4);
|
||||
- if ($1 != $4)
|
||||
+if ! grep 'tls: thread starting; TID=.*, TCB=0x' "${debug_output}" | awk -F '[ \t:]+' '{
|
||||
+ sub(/TID=/, "", $5);
|
||||
+ sub(/,/, "", $5);
|
||||
+ if ($1 != $5)
|
||||
exit 0;
|
||||
exit 1
|
||||
}'; then
|
||||
@@ -60,12 +60,33 @@ if ! grep 'Thread starting: TID=' "${debug_output}" | awk -F '[ \t:]+' '{
|
||||
fi
|
||||
|
||||
# We expect messages from thread creation and destruction.
|
||||
-if ! grep -q 'TCB allocated\|TCB deallocating\|TCB reused\|TCB deallocated' \
|
||||
+if ! grep -q 'tls: allocate TCB 0x\|tls: deallocate TCB 0x\|tls: TCB reused from cache\|tls: TCB deallocated' \
|
||||
"${debug_output}"; then
|
||||
echo "error: Expected TCB allocation/deallocation message not found"
|
||||
cat "${debug_output}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
+# Check for TLS module ID assignment.
|
||||
+if ! grep -q 'tls: assign modid .* to' "${debug_output}"; then
|
||||
+ echo "error: Expected 'modid ... assigned to' message not found"
|
||||
+ cat "${debug_output}"
|
||||
+ exit 1
|
||||
+fi
|
||||
+
|
||||
+# Check for TLS block allocation.
|
||||
+if ! grep -q 'tls: allocate block .* for modid .* size=.*, TCB=0x' "${debug_output}"; then
|
||||
+ echo "error: Expected 'modid ... allocated' message not found"
|
||||
+ cat "${debug_output}"
|
||||
+ exit 1
|
||||
+fi
|
||||
+
|
||||
+# TLS block deallocation might be skipped due to DTV surplus.
|
||||
+if grep -q 'tls: deallocate block .* for modid .* TCB=0x' "${debug_output}"; then
|
||||
+ echo "INFO: module deallocated message found"
|
||||
+else
|
||||
+ echo "INFO: module deallocated message not found (may be due to DTV surplus)"
|
||||
+fi
|
||||
+
|
||||
cat "${debug_output}"
|
||||
rm -f "${debug_output}"
|
||||
diff --git a/nptl/tst-tls-debug-mod.c b/nptl/tst-tls-debug-mod.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..0308c8a3e2d76fe1
|
||||
--- /dev/null
|
||||
+++ b/nptl/tst-tls-debug-mod.c
|
||||
@@ -0,0 +1,26 @@
|
||||
+/* Test for TLS logging in dynamic linker.
|
||||
+ 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/>. */
|
||||
+
|
||||
+__thread char tls_var[32768] __attribute__ ((tls_model ("global-dynamic")));
|
||||
+
|
||||
+int
|
||||
+in_dso (void)
|
||||
+{
|
||||
+ tls_var[0] = 42;
|
||||
+ return tls_var[0] - 42;
|
||||
+}
|
||||
diff --git a/sysdeps/x86_64/dl-tls.c b/sysdeps/x86_64/dl-tls.c
|
||||
index b3c1e4fcd7813b8b..8e550fc7d8efe2ff 100644
|
||||
--- a/sysdeps/x86_64/dl-tls.c
|
||||
+++ b/sysdeps/x86_64/dl-tls.c
|
||||
@@ -41,11 +41,36 @@ __tls_get_addr_slow (GET_ADDR_ARGS)
|
||||
dtv_t *dtv = THREAD_DTV ();
|
||||
|
||||
size_t gen = atomic_load_acquire (&GL(dl_tls_generation));
|
||||
- if (__glibc_unlikely (dtv[0].counter != gen)
|
||||
+ if (__glibc_unlikely (dtv[0].counter != gen))
|
||||
+ {
|
||||
/* See comment in __tls_get_addr in elf/dl-tls.c. */
|
||||
- && !(_dl_tls_allocate_active ()
|
||||
- && GET_ADDR_MODULE < _dl_tls_initial_modid_limit))
|
||||
- return update_get_addr (GET_ADDR_PARAM, gen);
|
||||
+ if (_dl_tls_allocate_active ()
|
||||
+ && GET_ADDR_MODULE < _dl_tls_initial_modid_limit)
|
||||
+ {
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf (
|
||||
+ "tls: modid %lu reentrant usage; TCB=0x%lx\n",
|
||||
+ (unsigned long int) GET_ADDR_MODULE,
|
||||
+ (unsigned long int) THREAD_SELF);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf (
|
||||
+ "tls: modid %lu update DTV to generation %lu; TCB=0x%lx\n",
|
||||
+ (unsigned long int) GET_ADDR_MODULE, (unsigned long int) gen,
|
||||
+ (unsigned long int) THREAD_SELF);
|
||||
+ return update_get_addr (GET_ADDR_PARAM, gen);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (__glibc_unlikely (dtv[GET_ADDR_MODULE].pointer.val == TLS_DTV_UNALLOCATED))
|
||||
+ {
|
||||
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))
|
||||
+ _dl_debug_printf ("tls: modid %lu lazy allocation; TCB=0x%lx\n",
|
||||
+ (unsigned long int) GET_ADDR_MODULE,
|
||||
+ (unsigned long int) THREAD_SELF);
|
||||
+ }
|
||||
|
||||
return tls_get_addr_tail (GET_ADDR_PARAM, dtv, NULL);
|
||||
}
|
||||
177
glibc-RHEL-159427-5.patch
Normal file
177
glibc-RHEL-159427-5.patch
Normal file
@ -0,0 +1,177 @@
|
||||
commit 9181dc6eb6084da95d8c14d9defe96189fd0360d
|
||||
Author: Frédéric Bérat <berat.fred@gmail.com>
|
||||
Date: Tue Jan 27 23:07:17 2026 +0100
|
||||
|
||||
feat(rtld): Allow LD_DEBUG category exclusion
|
||||
|
||||
Adds support for excluding specific categories from `LD_DEBUG` output.
|
||||
|
||||
The `LD_DEBUG` environment variable now accepts category names prefixed
|
||||
with a dash (`-`) to disable their debugging output. This allows users
|
||||
to enable broad categories (e.g., `all`) while suppressing verbose or
|
||||
irrelevant information from specific sub-categories (e.g., `-tls`).
|
||||
|
||||
The `process_dl_debug` function in `rtld.c` has been updated to parse
|
||||
these exclusion options and unset the corresponding bits in
|
||||
`GLRO(dl_debug_mask)`. The `LD_DEBUG=help` output has also been updated
|
||||
to document this new functionality. A new test `tst-dl-debug-exclude.sh`
|
||||
is added to verify the correct behavior of category exclusion.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index 8651a96bf260416c..46bf893b8f6eb537 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -1353,6 +1353,7 @@ $(objpfx)tst-glibcelf.out: tst-glibcelf.py elf.h $(..)/scripts/glibcelf.py \
|
||||
ifeq ($(run-built-tests),yes)
|
||||
tests-special += $(objpfx)tst-tls-allocation-failure-static-patched.out
|
||||
tests-special += $(objpfx)tst-tls-debug-recursive.out
|
||||
+tests-special += $(objpfx)tst-dl-debug-exclude.out
|
||||
endif
|
||||
|
||||
# The test requires shared _and_ PIE because the executable
|
||||
@@ -3351,4 +3352,14 @@ $(objpfx)tst-tls-debug-recursive.out: tst-tls-debug-recursive.sh \
|
||||
'$(rtld-prefix)' '$(run_program_env)' \
|
||||
$(objpfx)tst-recursive-tls > $@; \
|
||||
$(evaluate-test)
|
||||
+
|
||||
+$(objpfx)tst-dl-debug-exclude.out: tst-dl-debug-exclude.sh \
|
||||
+ $(objpfx)tst-recursive-tls \
|
||||
+ $(objpfx)tst-recursive-tlsmallocmod.so \
|
||||
+ $(patsubst %,$(objpfx)tst-recursive-tlsmod%.so, \
|
||||
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
|
||||
+ $(SHELL) $< $(common-objpfx) '$(test-wrapper-env)' \
|
||||
+ '$(rtld-prefix)' '$(run_program_env)' \
|
||||
+ $(objpfx)tst-recursive-tls > $@; \
|
||||
+ $(evaluate-test)
|
||||
endif
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index 8b09d8389a7050d2..e18f70349b337173 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -2501,11 +2501,18 @@ process_dl_debug (struct dl_main_state *state, const char *dl_debug)
|
||||
&& dl_debug[len] != ',' && dl_debug[len] != ':')
|
||||
++len;
|
||||
|
||||
+ bool exclude = *dl_debug == '-';
|
||||
+ const char *name = exclude ? dl_debug + 1 : dl_debug;
|
||||
+ size_t name_len = exclude ? len - 1 : len;
|
||||
+
|
||||
for (cnt = 0; cnt < ndebopts; ++cnt)
|
||||
- if (debopts[cnt].len == len
|
||||
- && memcmp (dl_debug, debopts[cnt].name, len) == 0)
|
||||
+ if (debopts[cnt].len == name_len
|
||||
+ && memcmp (name, debopts[cnt].name, name_len) == 0)
|
||||
{
|
||||
- GLRO(dl_debug_mask) |= debopts[cnt].mask;
|
||||
+ if (exclude)
|
||||
+ GLRO(dl_debug_mask) &= ~debopts[cnt].mask;
|
||||
+ else
|
||||
+ GLRO(dl_debug_mask) |= debopts[cnt].mask;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2547,7 +2554,8 @@ Valid options for the LD_DEBUG environment variable are:\n\n");
|
||||
|
||||
_dl_printf ("\n\
|
||||
To direct the debugging output into a file instead of standard output\n\
|
||||
-a filename can be specified using the LD_DEBUG_OUTPUT environment variable.\n");
|
||||
+a filename can be specified using the LD_DEBUG_OUTPUT environment variable.\n\
|
||||
+Categories can be excluded by prefixing them with a dash (-).\n");
|
||||
_exit (0);
|
||||
}
|
||||
}
|
||||
diff --git a/elf/tst-dl-debug-exclude.sh b/elf/tst-dl-debug-exclude.sh
|
||||
new file mode 100644
|
||||
index 0000000000000000..9837ffcea8e07933
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-dl-debug-exclude.sh
|
||||
@@ -0,0 +1,87 @@
|
||||
+#!/bin/sh
|
||||
+# Test for LD_DEBUG category exclusion.
|
||||
+# 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/>.
|
||||
+
|
||||
+# This script verifies the LD_DEBUG category exclusion functionality.
|
||||
+# It checks that:
|
||||
+# 1. Categories can be excluded using the '-' prefix.
|
||||
+# 2. Options are processed sequentially, meaning the last specified
|
||||
+# option for a category (enable or exclude) takes precedence.
|
||||
+
|
||||
+set -e
|
||||
+
|
||||
+common_objpfx="$1"
|
||||
+test_wrapper_env="$2"
|
||||
+rtld_prefix="$3"
|
||||
+run_program_env="$4"
|
||||
+test_program="$5"
|
||||
+
|
||||
+debug_output="${common_objpfx}elf/tst-dl-debug-exclude.debug"
|
||||
+rm -f "${debug_output}".*
|
||||
+
|
||||
+# Run the test program with LD_DEBUG=all,-tls.
|
||||
+# We expect general logs but no TLS logs.
|
||||
+eval "${test_wrapper_env}" LD_DEBUG=all,-tls LD_DEBUG_OUTPUT="${debug_output}" \
|
||||
+ "${rtld_prefix}" "${test_program}"
|
||||
+
|
||||
+fail=0
|
||||
+
|
||||
+# 1. Check that general logs are present (e.g., file loading)
|
||||
+if ! grep -q 'file=' "${debug_output}".*; then
|
||||
+ echo "FAIL: 'file=' message not found (LD_DEBUG=all failed)"
|
||||
+ fail=1
|
||||
+fi
|
||||
+
|
||||
+# 2. Check that TLS logs are NOT present
|
||||
+if grep -q 'tls: ' "${debug_output}".*; then
|
||||
+ echo "FAIL: TLS message found (exclusion of -tls failed)"
|
||||
+ fail=1
|
||||
+fi
|
||||
+
|
||||
+rm -f "${debug_output}".*
|
||||
+# 3. Check for LD_DEBUG=all,-tls,tls (ordering verification)
|
||||
+# We expect TLS logs to BE present
|
||||
+eval "${test_wrapper_env}" LD_DEBUG=all,-tls,tls LD_DEBUG_OUTPUT="${debug_output}" \
|
||||
+ "${rtld_prefix}" "${test_program}"
|
||||
+
|
||||
+if ! grep -q 'tls: ' "${debug_output}".*; then
|
||||
+ echo "FAIL: TLS message not found (ordering -tls,tls failed)"
|
||||
+ fail=1
|
||||
+fi
|
||||
+
|
||||
+rm -f "${debug_output}".*
|
||||
+# 4. Check for LD_DEBUG=tls,-tls
|
||||
+# We expect TLS logs to NOT be present
|
||||
+eval "${test_wrapper_env}" LD_DEBUG=tls,-tls LD_DEBUG_OUTPUT="${debug_output}" \
|
||||
+ "${rtld_prefix}" "${test_program}"
|
||||
+
|
||||
+if grep -q 'tls: ' "${debug_output}".*; then
|
||||
+ echo "FAIL: TLS message found (ordering tls,-tls failed)"
|
||||
+ fail=1
|
||||
+fi
|
||||
+
|
||||
+if [ $fail -ne 0 ]; then
|
||||
+ echo "Test FAILED"
|
||||
+ cat "${debug_output}".*
|
||||
+ rm -f "${debug_output}".*
|
||||
+ exit 1
|
||||
+fi
|
||||
+
|
||||
+echo "Test PASSED"
|
||||
+rm -f "${debug_output}".*
|
||||
+exit 0
|
||||
321
glibc-RHEL-162885.patch
Normal file
321
glibc-RHEL-162885.patch
Normal file
@ -0,0 +1,321 @@
|
||||
commit d6f08d1cf027f4eb2ba289a6cc66853722d4badc
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Thu Apr 16 19:13:43 2026 +0200
|
||||
|
||||
Use pending character state in IBM1390, IBM1399 character sets (CVE-2026-4046)
|
||||
|
||||
Follow the example in iso-2022-jp-3.c and use the __count state
|
||||
variable to store the pending character. This avoids restarting
|
||||
the conversion if the output buffer ends between two 4-byte UCS-4
|
||||
code points, so that the assert reported in the bug can no longer
|
||||
happen.
|
||||
|
||||
Even though the fix is applied to ibm1364.c, the change is only
|
||||
effective for the two HAS_COMBINED codecs for IBM1390, IBM1399.
|
||||
|
||||
The test case was mostly auto-generated using
|
||||
claude-4.6-opus-high-thinking, and composer-2-fast shows up in the
|
||||
log as well. During review, gpt-5.4-xhigh flagged that the original
|
||||
version of the test case was not exercising the new character
|
||||
flush logic.
|
||||
|
||||
This fixes bug 33980.
|
||||
|
||||
Assisted-by: LLM
|
||||
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
|
||||
|
||||
diff --git a/iconvdata/Makefile b/iconvdata/Makefile
|
||||
index 7196a8744bb66e8c..090ba929b1ec646e 100644
|
||||
--- a/iconvdata/Makefile
|
||||
+++ b/iconvdata/Makefile
|
||||
@@ -76,7 +76,7 @@ tests = bug-iconv1 bug-iconv2 tst-loading tst-e2big tst-iconv4 bug-iconv4 \
|
||||
tst-iconv6 bug-iconv5 bug-iconv6 tst-iconv7 bug-iconv8 bug-iconv9 \
|
||||
bug-iconv10 bug-iconv11 bug-iconv12 tst-iconv-big5-hkscs-to-2ucs4 \
|
||||
bug-iconv13 bug-iconv14 bug-iconv15 \
|
||||
- tst-iconv-iso-2022-cn-ext
|
||||
+ tst-iconv-iso-2022-cn-ext tst-bug33980
|
||||
ifeq ($(have-thread-library),yes)
|
||||
tests += bug-iconv3
|
||||
endif
|
||||
@@ -333,6 +333,8 @@ $(objpfx)bug-iconv15.out: $(addprefix $(objpfx), $(gconv-modules)) \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
$(objpfx)tst-iconv-iso-2022-cn-ext.out: $(addprefix $(objpfx), $(gconv-modules)) \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
+$(objpfx)tst-bug33980.out: $(addprefix $(objpfx), $(gconv-modules)) \
|
||||
+ $(addprefix $(objpfx),$(modules.so))
|
||||
|
||||
$(objpfx)iconv-test.out: run-iconv-test.sh \
|
||||
$(addprefix $(objpfx), $(gconv-modules)) \
|
||||
diff --git a/iconvdata/ibm1364.c b/iconvdata/ibm1364.c
|
||||
index d6c8ce7f682aa64d..47970008f7ef31ec 100644
|
||||
--- a/iconvdata/ibm1364.c
|
||||
+++ b/iconvdata/ibm1364.c
|
||||
@@ -67,12 +67,29 @@
|
||||
|
||||
/* Since this is a stateful encoding we have to provide code which resets
|
||||
the output state to the initial state. This has to be done during the
|
||||
- flushing. */
|
||||
+ flushing. For the to-internal direction (FROM_DIRECTION is true),
|
||||
+ there may be a pending character that needs flushing. */
|
||||
#define EMIT_SHIFT_TO_INIT \
|
||||
if ((data->__statep->__count & ~7) != sb) \
|
||||
{ \
|
||||
if (FROM_DIRECTION) \
|
||||
- data->__statep->__count &= 7; \
|
||||
+ { \
|
||||
+ uint32_t ch = data->__statep->__count >> 7; \
|
||||
+ if (__glibc_unlikely (ch != 0)) \
|
||||
+ { \
|
||||
+ if (__glibc_unlikely (outend - outbuf < 4)) \
|
||||
+ status = __GCONV_FULL_OUTPUT; \
|
||||
+ else \
|
||||
+ { \
|
||||
+ put32 (outbuf, ch); \
|
||||
+ outbuf += 4; \
|
||||
+ /* Clear character and db bit. */ \
|
||||
+ data->__statep->__count &= 7; \
|
||||
+ } \
|
||||
+ } \
|
||||
+ else \
|
||||
+ data->__statep->__count &= 7; \
|
||||
+ } \
|
||||
else \
|
||||
{ \
|
||||
/* We are not in the initial state. To switch back we have \
|
||||
@@ -99,11 +116,13 @@
|
||||
*curcsp = save_curcs
|
||||
|
||||
|
||||
-/* Current codeset type. */
|
||||
+/* Current codeset type. The bit is stored in the __count variable of
|
||||
+ the conversion state. If the db bit is set, bit 7 and above store
|
||||
+ a pending UCS-4 code point if non-zero. */
|
||||
enum
|
||||
{
|
||||
- sb = 0,
|
||||
- db = 64
|
||||
+ sb = 0, /* Single byte mode. */
|
||||
+ db = 64 /* Double byte mode. */
|
||||
};
|
||||
|
||||
|
||||
@@ -119,21 +138,29 @@ enum
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
- /* This is a combined character. Make sure we have room. */ \
|
||||
- if (__glibc_unlikely (outptr + 8 > outend)) \
|
||||
- { \
|
||||
- result = __GCONV_FULL_OUTPUT; \
|
||||
- break; \
|
||||
- } \
|
||||
- \
|
||||
const struct divide *cmbp \
|
||||
= &DB_TO_UCS4_COMB[ch - __TO_UCS4_COMBINED_MIN]; \
|
||||
assert (cmbp->res1 != 0 && cmbp->res2 != 0); \
|
||||
\
|
||||
put32 (outptr, cmbp->res1); \
|
||||
outptr += 4; \
|
||||
- put32 (outptr, cmbp->res2); \
|
||||
- outptr += 4; \
|
||||
+ \
|
||||
+ /* See whether we have room for the second character. */ \
|
||||
+ if (outend - outptr >= 4) \
|
||||
+ { \
|
||||
+ put32 (outptr, cmbp->res2); \
|
||||
+ outptr += 4; \
|
||||
+ } \
|
||||
+ else \
|
||||
+ { \
|
||||
+ /* Otherwise store only the first character now, and \
|
||||
+ put the second one into the queue. */ \
|
||||
+ curcs |= cmbp->res2 << 7; \
|
||||
+ inptr += 2; \
|
||||
+ /* Tell the caller why we terminate the loop. */ \
|
||||
+ result = __GCONV_FULL_OUTPUT; \
|
||||
+ break; \
|
||||
+ } \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
@@ -153,7 +180,20 @@ enum
|
||||
#define LOOPFCT FROM_LOOP
|
||||
#define BODY \
|
||||
{ \
|
||||
- uint32_t ch = *inptr; \
|
||||
+ uint32_t ch; \
|
||||
+ \
|
||||
+ ch = curcs >> 7; \
|
||||
+ if (__glibc_unlikely (ch != 0)) \
|
||||
+ { \
|
||||
+ put32 (outptr, ch); \
|
||||
+ outptr += 4; \
|
||||
+ /* Remove the pending character, but preserve state bits. */ \
|
||||
+ curcs &= (1 << 7) - 1; \
|
||||
+ continue; \
|
||||
+ } \
|
||||
+ \
|
||||
+ /* Otherwise read the next input byte. */ \
|
||||
+ ch = *inptr; \
|
||||
\
|
||||
if (__builtin_expect (ch, 0) == SO) \
|
||||
{ \
|
||||
diff --git a/iconvdata/tst-bug33980.c b/iconvdata/tst-bug33980.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..c9693e0efebe4eae
|
||||
--- /dev/null
|
||||
+++ b/iconvdata/tst-bug33980.c
|
||||
@@ -0,0 +1,153 @@
|
||||
+/* Test for bug 33980: combining characters in IBM1390/IBM1399.
|
||||
+ 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 <alloc_buffer.h>
|
||||
+#include <errno.h>
|
||||
+#include <iconv.h>
|
||||
+#include <stdbool.h>
|
||||
+#include <string.h>
|
||||
+
|
||||
+#include <support/check.h>
|
||||
+#include <support/next_to_fault.h>
|
||||
+#include <support/support.h>
|
||||
+
|
||||
+/* Run iconv in a loop with a small output buffer of OUTBUFSIZE bytes
|
||||
+ starting at OUTBUF. OUTBUF should be right before an unmapped page
|
||||
+ so that writing past the end will fault. Skip SHIFT bytes at the
|
||||
+ start of the input and output, to exercise different buffer
|
||||
+ alignment. TRUNCATE indicates skipped bytes at the end of
|
||||
+ input (0 and 1 a valid). */
|
||||
+static void
|
||||
+test_one (const char *encoding, unsigned int shift, unsigned int truncate,
|
||||
+ char *outbuf, size_t outbufsize)
|
||||
+{
|
||||
+ /* In IBM1390 and IBM1399, the DBCS code 0xECB5 expands to two
|
||||
+ Unicode code points when translated. */
|
||||
+ static char input[] =
|
||||
+ {
|
||||
+ /* 8 letters X. */
|
||||
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
|
||||
+ /* SO, 0xECB5, SI: shift to DBCS, special character, shift back. */
|
||||
+ 0x0e, 0xec, 0xb5, 0x0f
|
||||
+ };
|
||||
+
|
||||
+ /* Expected output after UTF-8 conversion. */
|
||||
+ static char expected[] =
|
||||
+ {
|
||||
+ 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X',
|
||||
+ /* U+304B (HIRAGANA LETTER KA). */
|
||||
+ 0xe3, 0x81, 0x8b,
|
||||
+ /* U+309A (COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK). */
|
||||
+ 0xe3, 0x82, 0x9a
|
||||
+ };
|
||||
+
|
||||
+ iconv_t cd = iconv_open ("UTF-8", encoding);
|
||||
+ TEST_VERIFY_EXIT (cd != (iconv_t) -1);
|
||||
+
|
||||
+ char result_storage[64];
|
||||
+ struct alloc_buffer result_buf
|
||||
+ = alloc_buffer_create (result_storage, sizeof (result_storage));
|
||||
+
|
||||
+ char *inptr = &input[shift];
|
||||
+ size_t inleft = sizeof (input) - shift - truncate;
|
||||
+
|
||||
+ while (inleft > 0)
|
||||
+ {
|
||||
+ char *outptr = outbuf;
|
||||
+ size_t outleft = outbufsize;
|
||||
+ size_t inleft_before = inleft;
|
||||
+
|
||||
+ size_t ret = iconv (cd, &inptr, &inleft, &outptr, &outleft);
|
||||
+ size_t produced = outptr - outbuf;
|
||||
+ alloc_buffer_copy_bytes (&result_buf, outbuf, produced);
|
||||
+
|
||||
+ if (ret == (size_t) -1 && errno == E2BIG)
|
||||
+ {
|
||||
+ if (produced == 0 && inleft == inleft_before)
|
||||
+ {
|
||||
+ /* Output buffer too small to make progress. This is
|
||||
+ expected for very small output buffer sizes. */
|
||||
+ TEST_VERIFY_EXIT (outbufsize < 3);
|
||||
+ break;
|
||||
+ }
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (ret == (size_t) -1)
|
||||
+ FAIL_EXIT1 ("%s (outbufsize %zu): iconv: %m", encoding, outbufsize);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* Flush any pending state (e.g. a buffered combined character).
|
||||
+ With outbufsize < 3, we could not store the first character, so
|
||||
+ the second character did not become pending, and there is nothing
|
||||
+ to flush. */
|
||||
+ {
|
||||
+ char *outptr = outbuf;
|
||||
+ size_t outleft = outbufsize;
|
||||
+
|
||||
+ size_t ret = iconv (cd, NULL, NULL, &outptr, &outleft);
|
||||
+ TEST_VERIFY_EXIT (ret == 0);
|
||||
+ size_t produced = outptr - outbuf;
|
||||
+ alloc_buffer_copy_bytes (&result_buf, outbuf, produced);
|
||||
+
|
||||
+ /* Second flush does not provide more data. */
|
||||
+ outptr = outbuf;
|
||||
+ outleft = outbufsize;
|
||||
+ ret = iconv (cd, NULL, NULL, &outptr, &outleft);
|
||||
+ TEST_VERIFY_EXIT (ret == 0);
|
||||
+ TEST_VERIFY (outptr == outbuf);
|
||||
+ }
|
||||
+
|
||||
+ TEST_VERIFY_EXIT (!alloc_buffer_has_failed (&result_buf));
|
||||
+ size_t result_used
|
||||
+ = sizeof (result_storage) - alloc_buffer_size (&result_buf);
|
||||
+
|
||||
+ if (outbufsize >= 3)
|
||||
+ {
|
||||
+ TEST_COMPARE (inleft, 0);
|
||||
+ TEST_COMPARE (result_used, sizeof (expected) - shift);
|
||||
+ TEST_COMPARE_BLOB (result_storage, result_used,
|
||||
+ &expected[shift], sizeof (expected) - shift);
|
||||
+ }
|
||||
+ else
|
||||
+ /* If the buffer is too small, only the leading X could be converted. */
|
||||
+ TEST_COMPARE (result_used, 8 - shift);
|
||||
+
|
||||
+ TEST_VERIFY_EXIT (iconv_close (cd) == 0);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ struct support_next_to_fault ntf
|
||||
+ = support_next_to_fault_allocate (8);
|
||||
+
|
||||
+ for (int shift = 0; shift <= 8; ++shift)
|
||||
+ for (int truncate = 0; truncate < 2; ++truncate)
|
||||
+ for (size_t outbufsize = 1; outbufsize <= 8; outbufsize++)
|
||||
+ {
|
||||
+ char *outbuf = ntf.buffer + ntf.length - outbufsize;
|
||||
+ test_one ("IBM1390", shift, truncate, outbuf, outbufsize);
|
||||
+ test_one ("IBM1399", shift, truncate, outbuf, outbufsize);
|
||||
+ }
|
||||
+
|
||||
+ support_next_to_fault_free (&ntf);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
@ -2357,7 +2357,7 @@ update_gconv_modules_cache ()
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Tue May 19 2026 Eduard Abdullin <eabdullin@almalinux.org> - 2.39-121.alma.1
|
||||
* Tue May 26 2026 Eduard Abdullin <eabdullin@almalinux.org> - 2.39-124.alma.1
|
||||
- Overwrite target for x86_64_v2
|
||||
- Update patch-git.lua to handle AlmaLinux branches correctly
|
||||
- Add support for AlmaLinux import UBI format
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
3e6989278e3b421cc69180f93e6b3ab73b2ce5fe
|
||||
8f42d24c37304d4db88237907d86b220cf0ecf3c
|
||||
v1
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user