Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2195605/?format=api
{ "id": 2195605, "url": "http://patchwork.ozlabs.org/api/patches/2195605/?format=api", "web_url": "http://patchwork.ozlabs.org/project/glibc/patch/20260211135944.2066869-2-fberat@redhat.com/", "project": { "id": 41, "url": "http://patchwork.ozlabs.org/api/projects/41/?format=api", "name": "GNU C Library", "link_name": "glibc", "list_id": "libc-alpha.sourceware.org", "list_email": "libc-alpha@sourceware.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260211135944.2066869-2-fberat@redhat.com>", "list_archive_url": null, "date": "2026-02-11T13:59:43", "name": "[v4,1/2] elf(tls): Add debug logging for TLS operations", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "ad14a5704ea0abf301117ca9a8ed9a44f9c8aaa3", "submitter": { "id": 84672, "url": "http://patchwork.ozlabs.org/api/people/84672/?format=api", "name": "Frédéric Bérat", "email": "fberat@redhat.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/glibc/patch/20260211135944.2066869-2-fberat@redhat.com/mbox/", "series": [ { "id": 491846, "url": "http://patchwork.ozlabs.org/api/series/491846/?format=api", "web_url": "http://patchwork.ozlabs.org/project/glibc/list/?series=491846", "date": "2026-02-11T13:59:44", "name": "Enhancing LD_DEBUG with TLS logging and category exclusion support", "version": 4, "mbox": "http://patchwork.ozlabs.org/series/491846/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2195605/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2195605/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "libc-alpha@sourceware.org" ], "Delivered-To": [ "patchwork-incoming@legolas.ozlabs.org", "libc-alpha@sourceware.org" ], "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=TgOzyHiC;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org;\n receiver=patchwork.ozlabs.org)", "sourceware.org;\n\tdkim=pass (1024-bit key,\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=TgOzyHiC", "sourceware.org; dmarc=pass (p=quarantine dis=none)\n header.from=redhat.com", "sourceware.org; spf=pass smtp.mailfrom=redhat.com", "server2.sourceware.org;\n arc=none smtp.remote-ip=170.10.129.124" ], "Received": [ "from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fB0Sz56Kyz1xpY\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 12 Feb 2026 01:00:51 +1100 (AEDT)", "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id AD9F44BA2E14\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 11 Feb 2026 14:00:49 +0000 (GMT)", "from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.129.124])\n by sourceware.org (Postfix) with ESMTP id B415C4BA2E10\n for <libc-alpha@sourceware.org>; Wed, 11 Feb 2026 13:59:58 +0000 (GMT)", "from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-21-4XNzhIdFPGaPA5kf8gJFKg-1; Wed,\n 11 Feb 2026 08:59:55 -0500", "from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id 1F87C195605F; Wed, 11 Feb 2026 13:59:54 +0000 (UTC)", "from Nymeria-redhat.redhat.com (unknown [10.44.33.163])\n by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with\n ESMTPS\n id 5C4E7180066F; Wed, 11 Feb 2026 13:59:52 +0000 (UTC)" ], "DKIM-Filter": [ "OpenDKIM Filter v2.11.0 sourceware.org AD9F44BA2E14", "OpenDKIM Filter v2.11.0 sourceware.org B415C4BA2E10" ], "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org B415C4BA2E10", "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org B415C4BA2E10", "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1770818403; cv=none;\n b=CbtkzSD0KjRoWfGBhp7UnC4KBeE7BT5f5p/gHUxm8SupDujUTcCOp/Rnkok3E3IpsyO0TeFOBVNaFP8GwI09KoUZvc40Mq+x7ImLDYU9OOws7NiaGelTdLh9ntF9sEoGTo0xFkG8PgyaV5+2MYPH2G8X152WkvwEEpJt6/2IMcM=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1770818403; c=relaxed/simple;\n bh=e017FjF9fvCUxOdrHC421dWSWKdImTz3GbJbyZAtsAY=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=OOhPw5IROUkLEimGpybP0l6daNtnQ0OBreGZmZ8wixeBGSun583Jz+TDHUBT5w/C46+O1ps+clBVcwa8t2o3mcxZQIDz79ssbfWWzSId9MCxntcB2uGbQRGmqy5MWDwIBDENbChz907BotqJzogNXi7uTFV5iWSZhKDaoTeYu7A=", "ARC-Authentication-Results": "i=1; server2.sourceware.org", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1770818398;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=HV61zqFNiBgswjx25mqFtNs+dz6b8LUr0BdfBT6IoTM=;\n b=TgOzyHiCzcMyFfSt+HC9sfqYEh0ARSMoCGty7Wwtm+PH3I6eeNlRqG6ED881UYd3uvu2Pv\n NW3DTm1QjTkJYpObWlRklcWEsFArN/UfjQUa55tNBOXzmR6yk1+njKRMPTH1i41aD3x/92\n EqMnwAPvMoCDBuFbo3T3b0oMUUcBvNI=", "X-MC-Unique": "4XNzhIdFPGaPA5kf8gJFKg-1", "X-Mimecast-MFC-AGG-ID": "4XNzhIdFPGaPA5kf8gJFKg_1770818394", "From": "=?utf-8?b?RnLDqWTDqXJpYyBCw6lyYXQ=?= <fberat@redhat.com>", "To": "libc-alpha@sourceware.org, fweimer@redhat.com,\n adhemerval.zanella@linaro.org", "Subject": "[PATCH v4 1/2] elf(tls): Add debug logging for TLS operations", "Date": "Wed, 11 Feb 2026 14:59:43 +0100", "Message-ID": "<20260211135944.2066869-2-fberat@redhat.com>", "In-Reply-To": "<20260211135944.2066869-1-fberat@redhat.com>", "References": "<20260211135944.2066869-1-fberat@redhat.com>", "MIME-Version": "1.0", "X-Scanned-By": "MIMEDefang 3.4.1 on 10.30.177.93", "X-Mimecast-Spam-Score": "0", "X-Mimecast-MFC-PROC-ID": "5cto0I1zGpMMtakttRtHUrUQP-7h1LOAtYs6SEmO6Yw_1770818394", "X-Mimecast-Originator": "redhat.com", "Content-Transfer-Encoding": "8bit", "content-type": "text/plain; charset=\"US-ASCII\"; x-default=true", "X-BeenThere": "libc-alpha@sourceware.org", "X-Mailman-Version": "2.1.30", "Precedence": "list", "List-Id": "Libc-alpha mailing list <libc-alpha.sourceware.org>", "List-Unsubscribe": "<https://sourceware.org/mailman/options/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=unsubscribe>", "List-Archive": "<https://sourceware.org/pipermail/libc-alpha/>", "List-Post": "<mailto:libc-alpha@sourceware.org>", "List-Help": "<mailto:libc-alpha-request@sourceware.org?subject=help>", "List-Subscribe": "<https://sourceware.org/mailman/listinfo/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=subscribe>", "Errors-To": "libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org" }, "content": "This commit introduces extensive debug logging for thread-local storage\n(TLS) operations within the dynamic linker. When `LD_DEBUG=tls` is\nenabled, messages are printed for:\n- TLS module assignment and release.\n- DTV (Dynamic Thread Vector) resizing events.\n- TLS block allocations and deallocations.\n- `__tls_get_addr` slow path events (DTV updates, lazy allocations, and\n static TLS usage).\n\nThe log format is standardized to use a \"tls: \" prefix and identifies\nmodules using the \"modid %lu\" convention. To aid in debugging\nmultithreaded applications, thread-specific logs include the Thread\nControl Block (TCB) address to identify the context of the operation.\n\nA new test module `tst-tls-debug-mod.c` and a corresponding shell script\n`tst-tls-debug-recursive.sh` have been added. Additionally, the existing\n`tst-dl-debug-tid` NPTL test has been updated to verify these TLS debug\nmessages in a multithreaded context.\n---\nv4:\n- Replace __func__ prefix with \"tls: \" to provide more meaningful\n context.\n- Clarify TLS log messages (e.g., \"TLS initialized\") to avoid ambiguity.\n- Remove TCB information from global events (modid assign/release).\n- Fix Lmid_t printing to use %ld (signed long).\n- Add missing glibc headers and descriptive comments to new test files.\n- Update tst-tls-debug-recursive.sh and tst-dl-debug-tid.sh to match the\n new format.\n\n elf/Makefile | 15 ++++++\n elf/dl-close.c | 5 ++\n elf/dl-tls.c | 83 +++++++++++++++++++++++++++++-----\n elf/rtld.c | 5 ++\n elf/tst-tls-debug-recursive.sh | 83 ++++++++++++++++++++++++++++++++++\n nptl/Makefile | 5 +-\n nptl/allocatestack.c | 10 ++--\n nptl/nptl-stack.c | 6 +--\n nptl/pthread_create.c | 2 +-\n nptl/tst-dl-debug-tid.c | 13 ++++++\n nptl/tst-dl-debug-tid.sh | 33 +++++++++++---\n nptl/tst-tls-debug-mod.c | 26 +++++++++++\n sysdeps/x86_64/dl-tls.c | 33 ++++++++++++--\n 13 files changed, 288 insertions(+), 31 deletions(-)\n create mode 100755 elf/tst-tls-debug-recursive.sh\n create mode 100644 nptl/tst-tls-debug-mod.c\n\n\nbase-commit: 60a3b44f603f1410a6fd2a63f96da65095269bad", "diff": "diff --git a/elf/Makefile b/elf/Makefile\nindex 396e97b5e7..9d4dfcf4ae 100644\n--- a/elf/Makefile\n+++ b/elf/Makefile\n@@ -3539,3 +3539,18 @@ $(objpfx)tst-origin.out: tst-origin.sh $(objpfx)tst-origin\n \t$(evaluate-test)\n \n $(objpfx)tst-dlopen-sgid.out: $(objpfx)tst-dlopen-sgid-mod.so\n+\n+\n+ifeq ($(run-built-tests),yes)\n+tests-special += $(objpfx)tst-tls-debug-recursive.out\n+\n+$(objpfx)tst-tls-debug-recursive.out: tst-tls-debug-recursive.sh \\\n+\t\t\t $(objpfx)tst-recursive-tls \\\n+\t\t\t $(objpfx)tst-recursive-tlsmallocmod.so \\\n+\t\t\t $(patsubst %,$(objpfx)tst-recursive-tlsmod%.so, \\\n+\t\t\t\t0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)\n+\t$(SHELL) $< $(common-objpfx) '$(test-wrapper-env)' \\\n+\t\t '$(rtld-prefix)' '$(run_program_env)' \\\n+\t\t $(objpfx)tst-recursive-tls > $@; \\\n+\t$(evaluate-test)\n+endif\ndiff --git a/elf/dl-close.c b/elf/dl-close.c\nindex fca877534e..92bce07c7a 100644\n--- a/elf/dl-close.c\n+++ b/elf/dl-close.c\n@@ -74,6 +74,11 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp,\n if (__glibc_likely (old_map != NULL))\n \t{\n \t /* Mark the entry as unused. These can be read concurrently. */\n+\t if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n+\t _dl_debug_printf (\n+\t\t\"tls: release modid %lu from %s [%ld]\\n\",\n+\t\t(unsigned long int) idx, DSO_FILENAME (old_map->l_name),\n+\t\t(long int) old_map->l_ns);\n \t atomic_store_relaxed (&listp->slotinfo[idx - disp].gen,\n \t\t\t\tGL(dl_tls_generation) + 1);\n \t atomic_store_relaxed (&listp->slotinfo[idx - disp].map, NULL);\ndiff --git a/elf/dl-tls.c b/elf/dl-tls.c\nindex 8cef809261..5bfcb184ed 100644\n--- a/elf/dl-tls.c\n+++ b/elf/dl-tls.c\n@@ -220,6 +220,12 @@ _dl_assign_tls_modid (struct link_map *l)\n }\n \n l->l_tls_modid = result;\n+\n+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n+ _dl_debug_printf (\"tls: assign modid %lu to %s [%ld]\\n\",\n+\t\t (unsigned long int) result,\n+\t\t DSO_FILENAME (l->l_name),\n+\t\t (long int) l->l_ns);\n }\n \n \n@@ -538,7 +544,7 @@ _dl_allocate_tls_storage (void)\n if (result == NULL)\n free (allocated);\n else if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n- _dl_debug_printf (\"TCB allocated: 0x%lx\\n\", (unsigned long int) result);\n+ _dl_debug_printf (\"tls: allocate TCB 0x%lx\\n\", (unsigned long int) result);\n \n _dl_tls_allocate_end ();\n return result;\n@@ -551,13 +557,18 @@ extern dtv_t _dl_static_dtv[];\n #endif\n \n static dtv_t *\n-_dl_resize_dtv (dtv_t *dtv, size_t max_modid)\n+_dl_resize_dtv (dtv_t *dtv, size_t max_modid, void *tcb)\n {\n /* Resize the dtv. */\n dtv_t *newp;\n size_t newsize = max_modid + DTV_SURPLUS;\n size_t oldsize = dtv[-1].counter;\n \n+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n+ _dl_debug_printf (\"tls: DTV resized for TCB 0x%lx: oldsize=%lu, newsize=%lu\\n\",\n+\t\t (unsigned long int) tcb,\n+\t\t (unsigned long int) oldsize, (unsigned long int) newsize);\n+\n _dl_tls_allocate_begin ();\n if (dtv == GL(dl_initial_dtv))\n {\n@@ -626,7 +637,7 @@ _dl_allocate_tls_init (void *result, bool main_thread)\n if (dtv[-1].counter < GL(dl_tls_max_dtv_idx))\n {\n /* Resize the dtv. */\n- dtv = _dl_resize_dtv (dtv, GL(dl_tls_max_dtv_idx));\n+ dtv = _dl_resize_dtv (dtv, GL(dl_tls_max_dtv_idx), result);\n \n /* Install this new dtv in the thread data structures. */\n INSTALL_DTV (result, &dtv[-1]);\n@@ -717,9 +728,14 @@ rtld_hidden_def (_dl_allocate_tls_init)\n void *\n _dl_allocate_tls (void *mem)\n {\n- return _dl_allocate_tls_init (mem == NULL\n-\t\t\t\t? _dl_allocate_tls_storage ()\n-\t\t\t\t: allocate_dtv (mem), false);\n+ void *result = _dl_allocate_tls_init (mem == NULL\n+\t\t\t\t\t? _dl_allocate_tls_storage ()\n+\t\t\t\t\t: allocate_dtv (mem), false);\n+ if (__glibc_unlikely (result != NULL\n+\t\t\t&& (GLRO (dl_debug_mask) & DL_DEBUG_TLS)))\n+ _dl_debug_printf (\"tls: TLS initialized for TCB 0x%lx\\n\",\n+\t\t (unsigned long int) result);\n+ return result;\n }\n rtld_hidden_def (_dl_allocate_tls)\n \n@@ -728,14 +744,22 @@ void\n _dl_deallocate_tls (void *tcb, bool dealloc_tcb)\n {\n if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n- _dl_debug_printf (\"TCB deallocating: 0x%lx (dealloc_tcb=%d)\\n\",\n+ _dl_debug_printf (\"tls: deallocate TCB 0x%lx (dealloc_tcb=%d)\\n\",\n \t\t (unsigned long int) tcb, dealloc_tcb);\n \n dtv_t *dtv = GET_DTV (tcb);\n \n /* We need to free the memory allocated for non-static TLS. */\n for (size_t cnt = 0; cnt < dtv[-1].counter; ++cnt)\n- free (dtv[1 + cnt].pointer.to_free);\n+ {\n+ if (dtv[1 + cnt].pointer.to_free != NULL\n+\t && __glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n+\t_dl_debug_printf (\n+\t \"tls: deallocate block 0x%lx for modid %lu; TCB=0x%lx\\n\",\n+\t (unsigned long int) dtv[1 + cnt].pointer.to_free,\n+\t (unsigned long int) (1 + cnt), (unsigned long int) tcb);\n+ free (dtv[1 + cnt].pointer.to_free);\n+ }\n \n /* The array starts with dtv[-1]. */\n if (dtv != GL(dl_initial_dtv))\n@@ -790,6 +814,12 @@ allocate_and_init (struct link_map *map)\n (map->l_tls_align, map->l_tls_blocksize);\n if (result.val == NULL)\n oom ();\n+ else if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n+ _dl_debug_printf (\"tls: allocate block 0x%lx for modid %lu; size=%lu, TCB=0x%lx\\n\",\n+\t\t (unsigned long int) result.to_free,\n+\t\t (unsigned long int) map->l_tls_modid,\n+\t\t (unsigned long int) map->l_tls_blocksize,\n+\t\t (unsigned long int) THREAD_SELF);\n \n /* Initialize the memory. */\n memset (__mempcpy (result.val, map->l_tls_initimage,\n@@ -891,7 +921,7 @@ _dl_update_slotinfo (unsigned long int req_modid, size_t new_gen)\n \t\t continue;\n \n \t\t /* Resizing the dtv aborts on failure: bug 16134. */\n-\t\t dtv = _dl_resize_dtv (dtv, max_modid);\n+\t\t dtv = _dl_resize_dtv (dtv, max_modid, THREAD_SELF);\n \n \t\t assert (modid <= dtv[-1].counter);\n \n@@ -912,6 +942,12 @@ _dl_update_slotinfo (unsigned long int req_modid, size_t new_gen)\n \t\t least some dynamic TLS usage by interposed mallocs. */\n \t if (dtv[modid].pointer.to_free != NULL)\n \t\t{\n+\t\t if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n+\t\t _dl_debug_printf (\n+\t\t\t\"tls: DTV update for TCB 0x%lx: modid %lu deallocated block 0x%lx\\n\",\n+\t\t\t(unsigned long int) THREAD_SELF,\n+\t\t\t(unsigned long int) modid,\n+\t\t\t(unsigned long int) dtv[modid].pointer.to_free);\n \t\t _dl_tls_allocate_begin ();\n \t\t free (dtv[modid].pointer.to_free);\n \t\t _dl_tls_allocate_end ();\n@@ -1004,6 +1040,11 @@ tls_get_addr_tail (tls_index *ti, dtv_t *dtv, struct link_map *the_map)\n \t dtv[ti->ti_module].pointer.to_free = NULL;\n \t dtv[ti->ti_module].pointer.val = p;\n \n+\t if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n+\t _dl_debug_printf (\"tls: modid %lu using static TLS; TCB=0x%lx\\n\",\n+\t\t\t (unsigned long int) ti->ti_module,\n+\t\t\t (unsigned long int) THREAD_SELF);\n+\n \t return tls_get_addr_adjust (p, ti);\n \t}\n else\n@@ -1059,18 +1100,28 @@ __tls_get_addr (tls_index *ti)\n {\n if (_dl_tls_allocate_active ()\n \t && ti->ti_module < _dl_tls_initial_modid_limit)\n+\t{\n \t /* This is a reentrant __tls_get_addr call, but we can\n \t satisfy it because it's an initially-loaded module ID.\n \t These TLS slotinfo slots do not change, so the\n \t out-of-date generation counter does not matter. However,\n \t if not in a TLS update, still update_get_addr below, to\n \t get off the slow path eventually. */\n-\t;\n+\t if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n+\t _dl_debug_printf (\"tls: modid %lu reentrant usage; TCB=0x%lx\\n\",\n+\t\t\t (unsigned long int) ti->ti_module,\n+\t\t\t (unsigned long int) THREAD_SELF);\n+\t}\n else\n \t{\n \t /* Update DTV up to the global generation, see CONCURRENCY NOTES\n \t in _dl_update_slotinfo. */\n \t gen = atomic_load_acquire (&GL(dl_tls_generation));\n+\t if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n+\t _dl_debug_printf (\n+\t\t\"tls: modid %lu update DTV to generation %lu; TCB=0x%lx\\n\",\n+\t\t(unsigned long int) ti->ti_module, (unsigned long int) gen,\n+\t\t(unsigned long int) THREAD_SELF);\n \t return update_get_addr (ti, gen);\n \t}\n }\n@@ -1078,7 +1129,13 @@ __tls_get_addr (tls_index *ti)\n void *p = dtv[ti->ti_module].pointer.val;\n \n if (__glibc_unlikely (p == TLS_DTV_UNALLOCATED))\n- return tls_get_addr_tail (ti, dtv, NULL);\n+ {\n+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n+\t_dl_debug_printf (\"tls: modid %lu lazy allocation; TCB=0x%lx\\n\",\n+\t\t\t (unsigned long int) ti->ti_module,\n+\t\t\t (unsigned long int) THREAD_SELF);\n+ return tls_get_addr_tail (ti, dtv, NULL);\n+ }\n \n return tls_get_addr_adjust (p, ti);\n }\n@@ -1149,6 +1206,10 @@ _dl_tls_initial_modid_limit_setup (void)\n \tbreak;\n }\n _dl_tls_initial_modid_limit = idx;\n+\n+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n+ _dl_debug_printf (\"tls: initial modid limit set to %lu\\n\",\n+\t\t (unsigned long int) idx);\n }\n \n \ndiff --git a/elf/rtld.c b/elf/rtld.c\nindex 29e7a4ddfa..1fc32ebefa 100644\n--- a/elf/rtld.c\n+++ b/elf/rtld.c\n@@ -1192,6 +1192,11 @@ rtld_setup_main_map (struct link_map *main_map)\n \n \t /* This image gets the ID one. */\n \t GL(dl_tls_max_dtv_idx) = main_map->l_tls_modid = 1;\n+\t if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n+\t _dl_debug_printf (\"tls: assign modid %lu to %s [%ld]\\n\",\n+\t\t\t\t(unsigned long int) main_map->l_tls_modid,\n+\t\t\t\tDSO_FILENAME (main_map->l_name),\n+\t\t\t\t(long int) main_map->l_ns);\n \t }\n \tbreak;\n \ndiff --git a/elf/tst-tls-debug-recursive.sh b/elf/tst-tls-debug-recursive.sh\nnew file mode 100755\nindex 0000000000..083e716f72\n--- /dev/null\n+++ b/elf/tst-tls-debug-recursive.sh\n@@ -0,0 +1,83 @@\n+#!/bin/sh\n+# Test for TLS logging in dynamic linker.\n+# Copyright (C) 2026 Free Software Foundation, Inc.\n+# This file is part of the GNU C Library.\n+#\n+# The GNU C Library is free software; you can redistribute it and/or\n+# modify it under the terms of the GNU Lesser General Public\n+# License as published by the Free Software Foundation; either\n+# version 2.1 of the License, or (at your option) any later version.\n+#\n+# The GNU C Library is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+# Lesser General Public License for more details.\n+#\n+# You should have received a copy of the GNU Lesser General Public\n+# License along with the GNU C Library; if not, see\n+# <https://www.gnu.org/licenses/>.\n+\n+# This script runs the tst-tls-debug-recursive test case and verifies its\n+# LD_DEBUG=tls output. It checks for various TLS-related messages\n+# to ensure the dynamic linker's TLS logging is working correctly.\n+\n+set -e\n+common_objpfx=\"$1\"\n+test_wrapper_env=\"$2\"\n+rtld_prefix=\"$3\"\n+run_program_env=\"$4\"\n+test_program=\"$5\"\n+\n+debug_output=\"${common_objpfx}elf/tst-tls-debug-recursive.debug\"\n+rm -f \"${debug_output}\".*\n+\n+# Run the test program with LD_DEBUG=tls.\n+eval \"${test_wrapper_env}\" LD_DEBUG=tls LD_DEBUG_OUTPUT=\"${debug_output}\" \\\n+ \"${rtld_prefix}\" \"${test_program}\"\n+\n+debug_output=$(ls \"${debug_output}\".*)\n+\n+fail=0\n+\n+# Check for expected messages\n+if ! grep -q 'tls: DTV resized for TCB 0x.*: oldsize' \"${debug_output}\"; then\n+ echo \"FAIL: DTV resized message not found\"\n+ fail=1\n+fi\n+\n+if ! grep -q 'tls: DTV update for TCB 0x.*: modid .* deallocated block' \"${debug_output}\"; then\n+ echo \"FAIL: module deallocated during DTV update message not found\"\n+ fail=1\n+fi\n+\n+if ! grep -q 'tls: assign modid .* to' \"${debug_output}\"; then\n+ echo \"FAIL: module assigned message not found\"\n+ fail=1\n+fi\n+\n+if ! grep -q 'tls: allocate block .* for modid .* size=.*, TCB=0x' \"${debug_output}\"; then\n+ echo \"FAIL: module allocated message not found\"\n+ fail=1\n+fi\n+\n+if ! grep -q 'tls: modid .* update DTV to generation .* TCB=0x' \"${debug_output}\"; then\n+ echo \"FAIL: update DTV message not found\"\n+ fail=1\n+fi\n+\n+if ! grep -q 'tls: initial modid limit set to' \"${debug_output}\"; then\n+ echo \"FAIL: initial modid limit message not found\"\n+ fail=1\n+fi\n+\n+if [ $fail -ne 0 ]; then\n+ echo \"Test FAILED\"\n+ cat \"${debug_output}\"\n+ rm -f \"${debug_output}\"\n+ exit 1\n+fi\n+\n+echo \"Test PASSED\"\n+cat \"${debug_output}\"\n+rm -f \"${debug_output}\"\n+exit 0\ndiff --git a/nptl/Makefile b/nptl/Makefile\nindex 08b8ba8a31..85f95dd0cf 100644\n--- a/nptl/Makefile\n+++ b/nptl/Makefile\n@@ -266,6 +266,7 @@ CFLAGS-tst-thread-exit-clobber.o = -std=gnu++11\n LDLIBS-tst-thread-exit-clobber = -lstdc++\n CFLAGS-tst-minstack-throw.o = -std=gnu++11\n LDLIBS-tst-minstack-throw = -lstdc++\n+LDLIBS-tst-dl-debug-tid = $(libdl)\n \n tests = \\\n tst-attr2 \\\n@@ -485,6 +486,7 @@ modules-names = \\\n tst-audit-threads-mod2 \\\n tst-compat-forwarder-mod \\\n tst-stack4mod \\\n+ tst-tls-debug-mod \\\n tst-tls3mod \\\n tst-tls5mod \\\n tst-tls5moda \\\n@@ -710,7 +712,8 @@ tst-stackguard1-ARGS = --command \"$(host-test-program-cmd) --child\"\n tst-stackguard1-static-ARGS = --command \"$(objpfx)tst-stackguard1-static --child\"\n \n ifeq ($(run-built-tests),yes)\n-$(objpfx)tst-dl-debug-tid.out: tst-dl-debug-tid.sh $(objpfx)tst-dl-debug-tid\n+$(objpfx)tst-dl-debug-tid.out: tst-dl-debug-tid.sh $(objpfx)tst-dl-debug-tid \\\n+\t\t\t $(objpfx)tst-tls-debug-mod.so\n \t$(SHELL) $< $(common-objpfx) '$(test-wrapper-env)' '$(rtld-prefix)' \\\n \t '$(run-program-env)' \\\n \t $(objpfx)tst-dl-debug-tid > $@; $(evaluate-test)\ndiff --git a/nptl/allocatestack.c b/nptl/allocatestack.c\nindex d3a9dbd3d2..b2ecb00113 100644\n--- a/nptl/allocatestack.c\n+++ b/nptl/allocatestack.c\n@@ -117,7 +117,7 @@ get_cached_stack (size_t *sizep, void **memp)\n lll_unlock (GL (dl_stack_cache_lock), LLL_PRIVATE);\n \n if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n- GLRO (dl_debug_printf) (\"TLS TCB reused from cache: 0x%lx\\n\",\n+ GLRO (dl_debug_printf) (\"tls: TCB reused from cache: 0x%lx\\n\",\n \t\t\t (unsigned long int) result);\n \n /* Report size and location of the stack to the caller. */\n@@ -436,9 +436,9 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,\n \n if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n \tGLRO (dl_debug_printf) (\n-\t \"TCB for user-supplied stack created: 0x%lx, stack=0x%lx, size=%lu\\n\",\n-\t (unsigned long int) pd, (unsigned long int) pd->stackblock,\n-\t (unsigned long int) pd->stackblock_size);\n+\t \"tls: TCB created (user-supplied stack); stack=0x%lx, size=%lu, TCB=0x%lx\\n\",\n+\t (unsigned long int) pd->stackblock,\n+\t (unsigned long int) pd->stackblock_size, (unsigned long int) pd);\n \n /* This is at least the second thread. */\n pd->header.multiple_threads = 1;\n@@ -561,7 +561,7 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,\n \t pd->setxid_futex = -1;\n \n \t if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n-\t GLRO (dl_debug_printf) (\"TCB for new stack allocated: 0x%lx\\n\",\n+\t GLRO (dl_debug_printf) (\"tls: TCB allocated (new stack): 0x%lx\\n\",\n \t\t\t\t (unsigned long int) pd);\n \n \t /* Allocate the DTV for this thread. */\ndiff --git a/nptl/nptl-stack.c b/nptl/nptl-stack.c\nindex 4ae081c55e..d327c7f7be 100644\n--- a/nptl/nptl-stack.c\n+++ b/nptl/nptl-stack.c\n@@ -77,7 +77,7 @@ __nptl_free_stacks (size_t limit)\n \n \t if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n \t GLRO (dl_debug_printf) (\n-\t\t\"TCB cache full, deallocating: TID=%ld, TCB=0x%lx\\n\",\n+\t\t\"tls: TCB deallocating from full cache; TID=%ld, TCB=0x%lx\\n\",\n \t\t(long int) curr->tid, (unsigned long int) curr);\n \n \t /* Free the memory associated with the ELF TLS. */\n@@ -104,7 +104,7 @@ queue_stack (struct pthread *stack)\n /* The 'stack' parameter is a pointer to the TCB (struct pthread),\n not just the stack. */\n if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n- GLRO (dl_debug_printf) (\"TCB deallocated into cache: TID=%ld, TCB=0x%lx\\n\",\n+ GLRO (dl_debug_printf) (\"tls: TCB deallocated into cache; TID=%ld, TCB=0x%lx\\n\",\n \t\t\t (long int) stack->tid, (unsigned long int) stack);\n \n /* We unconditionally add the stack to the list. The memory may\n@@ -139,7 +139,7 @@ __nptl_deallocate_stack (struct pthread *pd)\n \t the TLS memory. */\n if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n \tGLRO (dl_debug_printf) (\n-\t \"TCB for user-supplied stack deallocated: TID=%ld, TCB=0x%lx\\n\",\n+\t \"tls: TCB deallocated (user-supplied stack); TID=%ld, TCB=0x%lx\\n\",\n \t (long int) pd->tid, (unsigned long int) pd);\n /* Free the memory associated with the ELF TLS. */\n _dl_deallocate_tls (TLS_TPADJ (pd), false);\ndiff --git a/nptl/pthread_create.c b/nptl/pthread_create.c\nindex 6e7e0c6435..9a0cefb0f5 100644\n--- a/nptl/pthread_create.c\n+++ b/nptl/pthread_create.c\n@@ -367,7 +367,7 @@ start_thread (void *arg)\n }\n \n if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n- GLRO (dl_debug_printf) (\"Thread starting: TID=%ld, TCB=0x%lx\\n\",\n+ GLRO (dl_debug_printf) (\"tls: thread starting; TID=%ld, TCB=0x%lx\\n\",\n \t\t\t (long int) pd->tid, (unsigned long int) pd);\n \n /* Initialize resolver state pointer. */\ndiff --git a/nptl/tst-dl-debug-tid.c b/nptl/tst-dl-debug-tid.c\nindex b530d2170a..faa3b795b5 100644\n--- a/nptl/tst-dl-debug-tid.c\n+++ b/nptl/tst-dl-debug-tid.c\n@@ -27,12 +27,25 @@\n #include <support/xthread.h>\n #include <stdio.h>\n #include <unistd.h>\n+#include <dlfcn.h>\n+#include <support/xdlfcn.h>\n+#include <support/check.h>\n \n static void *\n thread_function (void *arg)\n {\n if (arg)\n pthread_barrier_wait ((pthread_barrier_t *) arg);\n+\n+ /* Load a module with TLS to verify allocation/deallocation logs. */\n+ void *h = xdlopen (\"tst-tls-debug-mod.so\", RTLD_NOW);\n+\n+ /* Call a function that accesses TLS. */\n+ int (*fp) (void) = (int (*) (void)) xdlsym (h, \"in_dso\");\n+ TEST_COMPARE (fp (), 0);\n+\n+ xdlclose (h);\n+\n return NULL;\n }\n \ndiff --git a/nptl/tst-dl-debug-tid.sh b/nptl/tst-dl-debug-tid.sh\nindex 12a4aa2b34..a748865a25 100644\n--- a/nptl/tst-dl-debug-tid.sh\n+++ b/nptl/tst-dl-debug-tid.sh\n@@ -39,7 +39,7 @@ eval \"${test_wrapper_env}\" LD_DEBUG=tls LD_DEBUG_OUTPUT=\"${debug_output}\" \\\n \n debug_output=$(ls \"${debug_output}\".*)\n # Check for the \"Thread starting\" message.\n-if ! grep -q 'Thread starting: TID=' \"${debug_output}\"; then\n+if ! grep -q 'tls: thread starting; TID=.*, TCB=0x' \"${debug_output}\"; then\n echo \"error: 'Thread starting' message not found\"\n cat \"${debug_output}\"\n exit 1\n@@ -47,10 +47,10 @@ fi\n \n # Check that we have a message where the PID (from prefix) is different\n # from the TID (in the message). This indicates a worker thread log.\n-if ! grep 'Thread starting: TID=' \"${debug_output}\" | awk -F '[ \\t:]+' '{\n- sub(/,/, \"\", $4);\n- sub(/TID=/, \"\", $4);\n- if ($1 != $4)\n+if ! grep 'tls: thread starting; TID=.*, TCB=0x' \"${debug_output}\" | awk -F '[ \\t:]+' '{\n+ sub(/TID=/, \"\", $5);\n+ sub(/,/, \"\", $5);\n+ if ($1 != $5)\n exit 0;\n exit 1\n }'; then\n@@ -60,12 +60,33 @@ if ! grep 'Thread starting: TID=' \"${debug_output}\" | awk -F '[ \\t:]+' '{\n fi\n \n # We expect messages from thread creation and destruction.\n-if ! grep -q 'TCB allocated\\|TCB deallocating\\|TCB reused\\|TCB deallocated' \\\n+if ! grep -q 'tls: allocate TCB 0x\\|tls: deallocate TCB 0x\\|tls: TCB reused from cache\\|tls: TCB deallocated' \\\n \"${debug_output}\"; then\n echo \"error: Expected TCB allocation/deallocation message not found\"\n cat \"${debug_output}\"\n exit 1\n fi\n \n+# Check for TLS module ID assignment.\n+if ! grep -q 'tls: assign modid .* to' \"${debug_output}\"; then\n+ echo \"error: Expected 'modid ... assigned to' message not found\"\n+ cat \"${debug_output}\"\n+ exit 1\n+fi\n+\n+# Check for TLS block allocation.\n+if ! grep -q 'tls: allocate block .* for modid .* size=.*, TCB=0x' \"${debug_output}\"; then\n+ echo \"error: Expected 'modid ... allocated' message not found\"\n+ cat \"${debug_output}\"\n+ exit 1\n+fi\n+\n+# TLS block deallocation might be skipped due to DTV surplus.\n+if grep -q 'tls: deallocate block .* for modid .* TCB=0x' \"${debug_output}\"; then\n+ echo \"INFO: module deallocated message found\"\n+else\n+ echo \"INFO: module deallocated message not found (may be due to DTV surplus)\"\n+fi\n+\n cat \"${debug_output}\"\n rm -f \"${debug_output}\"\ndiff --git a/nptl/tst-tls-debug-mod.c b/nptl/tst-tls-debug-mod.c\nnew file mode 100644\nindex 0000000000..0786d3755c\n--- /dev/null\n+++ b/nptl/tst-tls-debug-mod.c\n@@ -0,0 +1,26 @@\n+/* Test for TLS logging in dynamic linker.\n+ Copyright (C) 2026 Free Software Foundation, Inc.\n+ This file is part of the GNU C Library.\n+\n+ The GNU C Library is free software; you can redistribute it and/or\n+ modify it under the terms of the GNU Lesser General Public\n+ License as published by the Free Software Foundation; either\n+ version 2.1 of the License, or (at your option) any later version.\n+\n+ The GNU C Library is distributed in the hope that it will be useful,\n+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ Lesser General Public License for more details.\n+\n+ You should have received a copy of the GNU Lesser General Public\n+ License along with the GNU C Library; if not, see\n+ <https://www.gnu.org/licenses/>. */\n+\n+__thread int tls_var __attribute__ ((tls_model (\"global-dynamic\")));\n+\n+int\n+in_dso (void)\n+{\n+ tls_var = 42;\n+ return tls_var - 42;\n+}\ndiff --git a/sysdeps/x86_64/dl-tls.c b/sysdeps/x86_64/dl-tls.c\nindex a1877eeaed..6aee0972bb 100644\n--- a/sysdeps/x86_64/dl-tls.c\n+++ b/sysdeps/x86_64/dl-tls.c\n@@ -41,11 +41,36 @@ __tls_get_addr_slow (tls_index *ti)\n dtv_t *dtv = THREAD_DTV ();\n \n size_t gen = atomic_load_acquire (&GL(dl_tls_generation));\n- if (__glibc_unlikely (dtv[0].counter != gen)\n+ if (__glibc_unlikely (dtv[0].counter != gen))\n+ {\n /* See comment in __tls_get_addr in elf/dl-tls.c. */\n- && !(_dl_tls_allocate_active ()\n- && ti->ti_module < _dl_tls_initial_modid_limit))\n- return update_get_addr (ti, gen);\n+ if (_dl_tls_allocate_active ()\n+\t && ti->ti_module < _dl_tls_initial_modid_limit)\n+\t{\n+\t if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n+\t _dl_debug_printf (\n+\t\t\"tls: modid %lu reentrant usage; TCB=0x%lx\\n\",\n+\t\t(unsigned long int) ti->ti_module,\n+\t\t(unsigned long int) THREAD_SELF);\n+\t}\n+ else\n+\t{\n+\t if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n+\t _dl_debug_printf (\n+\t\t\"tls: modid %lu update DTV to generation %lu; TCB=0x%lx\\n\",\n+\t\t(unsigned long int) ti->ti_module, (unsigned long int) gen,\n+\t\t(unsigned long int) THREAD_SELF);\n+\t return update_get_addr (ti, gen);\n+\t}\n+ }\n+\n+ if (__glibc_unlikely (dtv[ti->ti_module].pointer.val == TLS_DTV_UNALLOCATED))\n+ {\n+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_TLS))\n+\t_dl_debug_printf (\"tls: modid %lu lazy allocation; TCB=0x%lx\\n\",\n+\t\t\t (unsigned long int) ti->ti_module,\n+\t\t\t (unsigned long int) THREAD_SELF);\n+ }\n \n return tls_get_addr_tail (ti, dtv, NULL);\n }\n", "prefixes": [ "v4", "1/2" ] }