From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id QaakFat0cGpoKQIAWB0awg (envelope-from ) for ; Mon, 03 Aug 2026 06:59:55 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=PHFMgJsx; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 503A11E09E; Mon, 03 Aug 2026 06:59:55 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-5.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=unavailable autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 858051E099 for ; Mon, 03 Aug 2026 06:59:53 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id B5A334BA2E1F for ; Mon, 3 Aug 2026 10:59:52 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B5A334BA2E1F Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=PHFMgJsx Received: from mail-pl1-x62c.google.com (mail-pl1-x62c.google.com [IPv6:2607:f8b0:4864:20::62c]) by sourceware.org (Postfix) with ESMTPS id 336094BA540B for ; Mon, 3 Aug 2026 10:59:27 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 336094BA540B Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 336094BA540B Authentication-Results: sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::62c ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1785754767; cv=none; b=JZuq8IP64PFBxDKyahpaIMl80t5NyD+cjqpjdksIYlz0FjJcY2SIFZToHYmqyACFwYj+4EYjkGPt1i9p0kpjFAZ6aZOLnVIGytMjcW00xdYMjzVmSvzQizzIkVZYF01bafFBf8X9nbnWGGuLEPinFf5hiC0II5h+Kd6L37m4VDk= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1785754767; c=relaxed/simple; bh=XH/l2gVqTJWG7M4Q/xDX0N7CTzu3ovCGoBh28EH9rOo=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=x7TSWLMR58qMqzUai9UYFrNj9Vwzd33cyehGrIZi0JXFDBiDuVwoUEKyJYddie11B2vt8gIO0drBOvXF5rfiEXqHceRhwZWS6hCZZu2tf02PzezvMbYfmWINvP6TkxOMNP/L7rfyp26LVu9SFLlNpCsYb33KEcot0tqDjgQX73o= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=PHFMgJsx DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 336094BA540B Received: by mail-pl1-x62c.google.com with SMTP id d9443c01a7336-2cc7e86e7aeso29946245ad.2 for ; Mon, 03 Aug 2026 03:59:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785754766; x=1786359566; darn=sourceware.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=6DHZdXhEd5koIDCqX5TmPYyagAEOt6LQ+p11IzVIB7Q=; b=PHFMgJsxO1/dj5RIWvbNFAvJWfTFG3Yrw9MDNX0gtX5+zofW71pCJGJl4sh4a6pkuB BzZ49wNkNjRL0afMn4mkj3LWXD3fZnwfNaduCTRRXpVI+xmuaAcdMlhMRmxQZUXozhgT EuraE8v1njl80WjtXm/WyxaJFcfh68X1OxGHwMDel5G1LMoW65TlQbKOm/g907ytxR+e AO0WEM7uv+AUQvHp3DMNVXPZR39+QH3AYTbx/jplR0BjT88IsqDMZ0/WI35ADGkKzjtJ QjrsT3uFHqTJPGosjjPnvagT7lJQ+5Sezof2eC7NJIKuCmhk3x26ILR5HSsAl3cTZ7+p KWbg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785754766; x=1786359566; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=6DHZdXhEd5koIDCqX5TmPYyagAEOt6LQ+p11IzVIB7Q=; b=DL1ktxPRUR4vF65/kMykbuhbqwWTDcwAhih1p8Co4Hp1kFJIXL4wm1bmgyTQQhSC9D cf5Ej97YW9zm9RW3N9gFVnF7ZQuixr2qz6A8yXGfjXOqg/d9uKbzwJuwH9eerMRDAn5v nwpsuBLk69SylCW2NpmFACNmnAcYwxtUy6Cibfu5BvpnxgSMNz5tPYc9zganlYh447Gf 6yz/Fua5S9a5iw2W+N0dZTzFThcUYBSfsrUSY/Moh+Cr5QPocIVmCV1BQEqG7gL57iXR pT0GfkIn0XStNMqGZScopLCwU8ATaylFPgWaK13p0A1c/Kxow70XC+zG9hOFtt0FNF1y dUXg== X-Gm-Message-State: AOJu0YyP0qkKFbO99X6CWsusj6GM/B2zwuVZAx8B5WAaRjSMvODpdBHO SUSBGri3+8MyVQe0MpqZqV5/H95rRfhoiJE7vRmVmCBRFKtCgMJLgahL X-Gm-Gg: AR+sD13AUrWzQMRbtgeWfTZy7bUtx9qHpmHZIoBrU0dSUmIVCP6hYigpVAY90sBEb+a OKDVHX78iHqtRLMQqLqCBpuiJJnr4JTLNImLij06szcvTazDMrrffSusVu1YoiHRAKEhCJKhw74 hDOl53DciEeMU8Kt2+gz82oz672QhChCsSJj0O7XvQ5a2j8KtN34FMfpoLqfNSkyH6wUw+zb0rN ujf7U3x2Atx63NYYSs7WRYnI0Sl6TkDtAQzsLhHGJUXkYw9JPMktHOWEl7NojEpCPd37Epd7f8b AaXnGaJW2dU8DG6hfzEe2uk9hu9q6nRZ3xtam8U2Wv45WBlO2wnHej+HIgyyhCcEc0sEj3BUEA5 uOtPx+G9o3NSrp9DD938GtngKeIxJ4wQ6KxZ0uC70yUeEKBh7tN/aGWblMoa7hOl/NivKY9Ahh/ WJYBuf65Hv7U0Soy3AARBlV/NEuRKi6gOT+/SR6jdPVtV6AZRZDwnH32q8dbrVT1j5o3hF26l1k cbUAG6n1RQxhh8wDOS/B+Sc/A== X-Received: by 2002:a17:902:da81:b0:2cc:ed57:c7c with SMTP id d9443c01a7336-2d0523f5489mr99580635ad.32.1785754766043; Mon, 03 Aug 2026 03:59:26 -0700 (PDT) Received: from dhcp-9-123-6-89.bl1-in.ibm.com ([129.41.58.1]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2d04ae5a95asm35933615ad.18.2026.08.03.03.59.23 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Mon, 03 Aug 2026 03:59:25 -0700 (PDT) From: Aditya Vidyadhar Kamath To: ulrich.weigand@de.ibm.com, simon.marchi@polymtl.ca, tom@tromey.com Cc: gdb-patches@sourceware.org, Aditya.Kamath1@ibm.com, sangamesh.swamy@in.ibm.com, PRAJWAL.B.MEHENDARKAR@ibm.com Subject: [PATCH v1 2/2] Add TLS support for shared libraries in AIX Date: Mon, 3 Aug 2026 16:29:12 +0530 Message-ID: <20260803105911.74857-2-akamath996@gmail.com> X-Mailer: git-send-email 2.43.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org From: Aditya Kamath This patch adds support to debug thread local variables defined in shared libraries in AIX. Sample debug output of this patch is as below Thread 3 hit Breakpoint 1, thread_runner (arg=0x2) at tls_main.c:36 36 volatile int bp_here = 0; (void)bp_here; $3 = 20 $4 = 40 thread 2: my_tls_var=20 lib_tls_var=40 [Thread 1 (tid 101646715) (id 1) exited] [Thread 515 (tid 88015327) (id 3) exited] [Inferior 1 (process 21758254) exited normally] where lib_tls_var=40 is a variable from a thread library. For initial-exec (R_TLS_IE): read the runtime TP-relative offset from the .loader section TOC slot written by the AIX loader, returning it with bit 0 set as a sentinel so get_thread_local_address() uses it directly without re-adding the static symbol value. For global-dynamic/local-dynamic (R_TLSM): scan the .loader section for the R_TLSM relocation, read the 8-byte mod_id from the inferior's TOC, and dereference it as a TP-relative pointer to the per-thread block, then add the intra-module variable offset. Local-exec (main executable) continues to use the existing tp + offset path. This patch implements the same as mentioned above. --- gdb/rs6000-aix-tdep.c | 205 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 179 insertions(+), 26 deletions(-) diff --git a/gdb/rs6000-aix-tdep.c b/gdb/rs6000-aix-tdep.c index aa68654ff41..db81c91524c 100644 --- a/gdb/rs6000-aix-tdep.c +++ b/gdb/rs6000-aix-tdep.c @@ -40,6 +40,11 @@ #include "trad-frame.h" #include "frame-unwind.h" #include "inferior.h" +#include "bfd.h" +#include "coff/internal.h" +#include "libcoff.h" +#include "coff/xcoff.h" +#include "libxcoff.h" /* If the kernel has to deliver a signal, it pushes a sigcontext structure on the stack and then calls the signal handler, passing @@ -69,6 +74,9 @@ /* Minimum possible text address in AIX. */ #define AIX_TEXT_SEGMENT_BASE 0x10000000 +/* R_TLSM relocation type stored in the low byte of internal_ldrel.l_rtype. */ +#define XCOFF_R_TLSM 0x24 + struct rs6000_aix_reg_vrreg_offset { int vr0_offset; @@ -1358,45 +1366,165 @@ rs6000_aix_core_xfer_shared_libraries_aix (struct gdbarch *gdbarch, /* For AIX, use the rs6000_aix_fetch_tls_load_module_address gdbarch method. - Thread-local variables accessed via a simple TP-relative offset (the Local - Exec and Initial Exec TLS models) are only valid for the main executable and - for shared libraries that were pulled in at program startup. Variables - belonging to a library loaded later via dlopen() have their storage - allocated dynamically; their addresses must be resolved through the DTV - (Dynamic Thread Vector) and cannot be computed with a plain TP offset. + AIX 64-bit TLS defines four access models, they are: + + local-exec: + The TLS variable belongs to the main executable. + + initial-exec: + The TLS variable may be in the main executable or a shared library + that was present at program startup (i.e. not dlopen'd). + + local-dynamic: + The TLS variable is in the same module but it may be a shared library. + + global-dynamic: + The general case for shared libraries. The compiler generates a + per-module TOC entry pair: + TOC[mod_entry] -- filled with "mod_id" by the R_TLSM loader relocation + TOC[off_entry] -- filled with the intra-module variable offset by R_TLS + mod_id is a signed TP-relative offset to the per-thread block pointer + for this module's TLS storage: + per_thread_block_ptr = *(thread_pointer + mod_id) + variable_addr = per_thread_block_ptr + xcoff_symbol_value - Until full DTV-based lookup is implemented, reject any objfile that is a - shared library (OBJF_SHARED) which has been dlopen'd and only - allow the main executable (OBJF_MAINLINE). Returning 0 here causes - rs6000_aix_get_thread_local_address() to be called with lm_addr == 0, - which it treats as the signal to use the TP-relative path. */ + In practice, local-exec and initial-exec both resolve via a plain + TP-relative offset (lm_addr == 0). local-dynamic and global-dynamic + both require the mod_id indirection (lm_addr != 0) and are handled + in rs6000_aix_fetch_tls_load_module_address (). + + To obtain mod_id for a shared library we scan the library's loader + section for the first R_TLSM relocation, read the virtual address of the + TOC entry it targets, apply the objfile's data-section relocation to get + the runtime address, and then read the 8-byte mod_id from the inferior. */ static CORE_ADDR rs6000_aix_fetch_tls_load_module_address (struct objfile *objfile) { - /* TLS variables from shared libraries cannot be directly fetched - via the thread pointer if they were loaded by dlopen(). */ - if (objfile->flags & OBJF_SHARED) + /* Main executable so return 0. */ + if (!(objfile->flags & OBJF_SHARED)) + return 0; + + bfd *abfd = objfile->obfd.get (); + if (abfd == nullptr) throw_error (TLS_GENERIC_ERROR, - _("TLS lookup via thread pointer is not supported for " - "shared library \"%s\"; full DTV-based lookup is not " - "yet implemented for AIX"), + _("Cannot resolve TLS for \"%s\": no BFD"), objfile_name (objfile)); - return 0; + /* Read the .loader section, which contains the header, symbol table, + and relocation table for the shared library. */ + asection *loader_sec = bfd_get_section_by_name (abfd, ".loader"); + if (loader_sec == nullptr) + throw_error (TLS_GENERIC_ERROR, + _("Cannot resolve TLS for \"%s\": no .loader section"), + objfile_name (objfile)); + + bfd_size_type loader_size = bfd_section_size (loader_sec); + gdb::byte_vector loader_buf (loader_size); + if (!bfd_get_section_contents (abfd, loader_sec, loader_buf.data (), + 0, loader_size)) + throw_error (TLS_GENERIC_ERROR, + _("Cannot resolve TLS for \"%s\": cannot read .loader section"), + objfile_name (objfile)); + + /* Parse the loader header to find the relocation table offset and count. */ + struct internal_ldhdr ldhdr; + bfd_xcoff_swap_ldhdr_in (abfd, loader_buf.data (), &ldhdr); + + bfd_vma reloc_start = bfd_xcoff_loader_reloc_offset (abfd, &ldhdr); + bfd_size_type reloc_size = bfd_xcoff_ldrelsz (abfd); + + if (reloc_start + ldhdr.l_nreloc * reloc_size > loader_size) + throw_error (TLS_GENERIC_ERROR, + _("Cannot resolve TLS for \"%s\": .loader section truncated"), + objfile_name (objfile)); + + /* Scan loader relocations for TLS reloc types. + l_rtype is a 16-bit field: high byte = reloc size, low byte = reloc type + + R_TLS_IE (0x21) = initial-exec: the TOC slot holds the complete + TP-relative address of the variable written at load time by the + AIX loader. For shared libraries this value differs from the + static XCOFF symbol value. We must read the runtime TOC slot. + Bit 0 of lm_addr is set as a sentinel to distinguish this path + + R_TLSM (0x24) = global-dynamic / local-dynamic: the TOC slot holds + mod_id, a signed TP-relative offset to the per-thread block pointer. + get_thread_local_address() then dereferences it and adds offset. */ + + const gdb_byte *reloc_ptr = loader_buf.data () + reloc_start; + for (size_t i = 0; i < ldhdr.l_nreloc; i++, reloc_ptr += reloc_size) + { + struct internal_ldrel ldrel; + bfd_xcoff_swap_ldrel_in (abfd, reloc_ptr, &ldrel); + + int rtype = ldrel.l_rtype & 0xff; + if (rtype != R_TLS_IE && rtype != XCOFF_R_TLSM) + continue; + + /* ldrel.l_vaddr is the static (pre-relocation) address of the TOC + entry the loader fills with the TLS value. Apply the .data section + relocation offset to get the runtime TOC slot address. */ + CORE_ADDR toc_addr = ldrel.l_vaddr + objfile->data_section_offset (); + + /* Read the 8-byte value the AIX loader wrote into the TOC slot. */ + gdb_byte buf[8]; + if (target_read_memory (toc_addr, buf, sizeof buf) != 0) + throw_error (TLS_GENERIC_ERROR, + _("Cannot resolve TLS for \"%s\": " + "failed to read TLS TOC slot from inferior"), + objfile_name (objfile)); + + CORE_ADDR toc_val + = extract_signed_integer (buf, sizeof buf, BFD_ENDIAN_BIG); + + /* A zero or positive value means the loader has not yet written the + TLS offset (storage not yet allocated). */ + if ((LONGEST) toc_val >= 0) + throw_error (TLS_NOT_ALLOCATED_YET_ERROR, + _("TLS storage not yet allocated for \"%s\""), + objfile_name (objfile)); + + if (rtype == R_TLS_IE) + { + /* initial-exec: return runtime TP-relative offset with bit 0 set + so get_thread_local_address() uses it directly without adding + the symbol offset (which is the static .tdata offset, not the + runtime TP offset for a shared library). */ + return (CORE_ADDR) toc_val | 1; + } + + /* R_TLSM: return mod_id as-is (bit 0 is always 0). */ + return (CORE_ADDR) toc_val; + } + + throw_error (TLS_GENERIC_ERROR, + _("Cannot resolve TLS for \"%s\": " + "no TLS relocation (R_TLS_IE or R_TLSM) found " + "in .loader section"), + objfile_name (objfile)); } /* Use the AIX get_thread_local_address gdbarch function. - On 64-bit AIX the thread pointer is in R13. For the Local Exec TLS model - (used by the main executable) the XCOFF symbol value is a signed - TP-relative offset, so the per-thread address is: + On 64-bit AIX the thread pointer is in R13. + + lm_addr == 0 (local-exec, main executable): + The XCOFF symbol value is a signed TP-relative offset baked in at + link time: + address = tp + (int64_t) offset - address = thread_pointer + (int64_t) offset + lm_addr has bit 0 set (initial-exec, shared library): + The full runtime TP-relative offset was read from the R_TLS_IE TOC + slot by rs6000_aix_fetch_tls_load_module_address(). Do not add the + symbol offset again: + address = tp + (int64_t)(lm_addr & ~1) - This only works correctly when the variable's storage is allocated - statically relative to the thread pointer, which is guaranteed for the - main executable. */ + lm_addr != 0 and bit 0 clear (global-dynamic / local-dynamic): + lm_addr is mod_id from the R_TLSM TOC slot a signed TP-relative + offset to the per-thread block pointer. Dereference it, then add + the intra-module symbol offset: + address = *(tp + (int64_t) lm_addr) + (int64_t) offset */ static CORE_ADDR rs6000_aix_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid, @@ -1420,7 +1548,32 @@ rs6000_aix_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid, throw_error (TLS_GENERIC_ERROR, _("Unable to fetch thread pointer for TLS lookup")); - return tp + (CORE_ADDR)(int64_t) offset; + if (lm_addr == 0) + { + /* local-exec (main executable): XCOFF symbol value is TP-relative. */ + return tp + (CORE_ADDR)(int64_t) offset; + } + else if (lm_addr & 1) + { + /* initial-exec (shared library): lm_addr holds the full runtime + TP-relative offset from the R_TLS_IE TOC slot, with bit 0 set as + a sentinel. Strip the sentinel and compute the address directly. */ + return tp + (CORE_ADDR)(int64_t)(lm_addr & ~(CORE_ADDR)1); + } + else + { + /* global-dynamic / local-dynamic: lm_addr is mod_id from R_TLSM. + Dereference the per-thread block pointer, then add the symbol offset. */ + CORE_ADDR region_ptr_addr = tp + (CORE_ADDR)(int64_t) lm_addr; + gdb_byte buf[8]; + if (target_read_memory (region_ptr_addr, buf, sizeof buf) != 0) + throw_error (TLS_GENERIC_ERROR, + _("Cannot read TLS region pointer for thread-local lookup")); + + CORE_ADDR region_base + = extract_unsigned_integer (buf, sizeof buf, BFD_ENDIAN_BIG); + return region_base + (CORE_ADDR)(int64_t) offset; + } } static void -- 2.51.2