From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id /5FlCuI3mWp8jCMAWB0awg (envelope-from ) for ; Thu, 03 Sep 2026 05:03:30 -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=Me1+mdlC; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id D07A11E166; Thu, 03 Sep 2026 05:03:29 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-2.1 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,RCVD_IN_SBL_CSS autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [IPv6:2620:52:6:3111::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 DACAA1E09B for ; Thu, 03 Sep 2026 05:03:22 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 7D26B4BA902E for ; Thu, 3 Sep 2026 09:03:20 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 7D26B4BA902E 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=Me1+mdlC Received: from mail-pg1-x536.google.com (mail-pg1-x536.google.com [IPv6:2607:f8b0:4864:20::536]) by sourceware.org (Postfix) with ESMTPS id 64C5C4BA7999 for ; Thu, 3 Sep 2026 09:02:51 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 64C5C4BA7999 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 64C5C4BA7999 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::536 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1788426171; cv=none; b=cyMEdHXEGmBzUS9alnszQ1Cn0FD8tpy5jgTVVuI+AK+6Lcl5fFP7dNYM4EZVGAtczcZeXJeNg3mFaGcd6XGG0A1YKzk3Rz8f+HlGwJU/B8wQ70whvs3m/3y380Q4s8ZSEVb/xNT389NvD+vqrRFWNQ0LsBkzN2ldPKG9MaPUZnE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1788426171; c=relaxed/simple; bh=4rZWjFNAtBIzyIACJJEdTjMcDZDtPbPn9mfaAaGlQaw=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=d8bgNmtWZgqdQeRWeNNtPTtLCOUVTi/ELL9vOEQ/R7t8zfTAzsmOT8AQiI7bk3lKgJLuejrrbXJcbvoyN0phprZo4Y7RJnmhA9Gw7UMtXPs2kKbGmdZBBEqSPMWjeOvV55RMLhfQkUCpo1aUK/Xlzt7vzDW1SiBp+yz2WOaJGsA= 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=Me1+mdlC DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 64C5C4BA7999 Received: by mail-pg1-x536.google.com with SMTP id 41be03b00d2f7-cc1a4c62804so2072923a12.3 for ; Thu, 03 Sep 2026 02:02:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788426170; x=1789030970; 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=8BfF2gpdmYBhUVt8JdjSPfNjUSCmEXffRTOLNEw7nWw=; b=Me1+mdlCz556WTRK3AUrBD9mBc5Czpsqe75haa1Zr2sQeUpZQnq9SokE5b6rvij1X+ NCqrb1cRNIinbriPjWNssDQIH0WdkBNirsWBzoz1ogEv9XphOaSdI1lqEr/KmyINr0Gg RADNBgb762tPICZjBzWNOzT78FXvIZy1+Uj5w0s+AHnLuMAefzWonzlaOjVwFYnwpeRu 80+z7ENWM2KrVT6DeH/QQHwneTNB9V1yCMx/1vSsm6R894I8BR8EHtik+LwWAFLtE3Ay 6BJsXlYK/zcmlM8+f8EHjjSS5eJfshWZpgd61Jl9beNrwULPGs1awuPYMElknJOAKNjx ASkg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788426170; x=1789030970; 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=8BfF2gpdmYBhUVt8JdjSPfNjUSCmEXffRTOLNEw7nWw=; b=ZCQMErwRU1X0RoqwYtUQA7We5wyGIdsFmiXyA0XIYBAZTjDXTr9dvx1DuqVnPwPsbc mph2CyZI5IWFUbNK7n7l1ry83XE2ekvNJAxD2N7P4lTsRfHMwqbCvSyzHDGRq5ayI7fS pOqvbHplC2tNO1RRl6w4fSgHruN2+T8zhnR5sU7kfoJI/Sgy1V7A9E+AnjDViykx7zY3 FCW+qEy3ihbbaIp13E0yegL4q+tVIVC9tpET9Goc16GQUlLEQQvaMRNG/HUqLIwYFNjt Wzw6e8ciVtocsoldwJU6oC3LegslWpVx4TZ56R3fN/DapgFAGOW9xbtQRL55Ycg1Mi+J rA6w== X-Gm-Message-State: AFuF++k8w9E9a3SSNCwX1jG4vMK7I4Cn8BWglL1ROCgys29csLgAgnhd tSzV8QRxBH0FiOOowvAYW0urnTm7tgDrJC7QnIq7NBr7QjUROHimY6H+ X-Gm-Gg: AYBFou0bM69LRX5VGJd8Q+HOmVJKK4mX5ofeSfE4ixWjOyeiLbw9HgSSiPVjFT73/xy FbESP0B57299d+wlvAolha4cjHKufxwgZUSDP+O+vVegr4EKZoQQJcis1V65Y8qo1SsbEr/sQR9 5PI2AA0SeKOnkegsCYtidzgqohE3in5Euh/uCvOS0GFWdrgmyPaZFbO7D7yB0bBmUkB0Intr51/ 4ai9Br2+2FJLpGda85hqP3doRJJZiCsi6/jSrbVL1P6TMEP5Cw68snCMhJgL1rEkNDVpo1ckOK0 cecmhJ5pX35wk0oyAViWOeqPWpYOhFq7RnC72CfFFh1bm+sV3D48wBM2goCAtrJfEC8XxMSXLc1 3q8FprZOQvaxwMY71fYymmDBkB1OkjXY5mnMEVZLCO4PGmePLKPCIkC4536+J0RYPpSxF0G7PDF myL9uZr2zKUFXL4MqnpKbTTktidG8feM9YNTs43BCHHkhx2E6R6uUXfijKzwfe1QXmISTbxyG6R eMpgG1YcRA+ZK57dQ== X-Received: by 2002:a17:90b:28cd:b0:398:e96a:8999 with SMTP id 98e67ed59e1d1-39aee0e6633mr15897161a91.16.1788426169821; Thu, 03 Sep 2026 02:02:49 -0700 (PDT) Received: from localhost.localdomain ([122.171.17.193]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-33255d39d21sm5428631eec.16.2026.09.03.02.02.46 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Thu, 03 Sep 2026 02:02:49 -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, Aditya Vidyadhar Kamath Subject: [PATCH v2 2/2] This patch adds support to debug thread local variables defined in shared libraries in AIX. Date: Thu, 3 Sep 2026 14:26:15 +0530 Message-ID: <20260903085614.39532-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 Vidyadhar Kamath 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. There are 4 models for TLS varialbles in AIX, Local exec means the TLS variable is in main executable and defined there only. Initial exec means the TLS variable is in a library and is linked in start up. Global dynamic means TLS variable is in shared module and loaded when we dlopen Local dynamic means TLS varialbe is in shared module it is local to the shared library code. So local dynamic variables the compiler can group together for a shared look up. This local-dymanic case is handled in rs6000_aix_compute_tls_address () 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 | 509 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 498 insertions(+), 11 deletions(-) diff --git a/gdb/rs6000-aix-tdep.c b/gdb/rs6000-aix-tdep.c index 76763f08e08..c4bc01c381e 100644 --- a/gdb/rs6000-aix-tdep.c +++ b/gdb/rs6000-aix-tdep.c @@ -40,6 +40,10 @@ #include "trad-frame.h" #include "frame-unwind.h" #include "inferior.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 +73,40 @@ /* Minimum possible text address in AIX. */ #define AIX_TEXT_SEGMENT_BASE 0x10000000 +/* XCOFF TLS relocation types (low byte of internal_ldrel.l_rtype). + + The AIX loader fills TLS TOC slots as follows: + + R_TLS (0x20, global-dynamic): the loader writes the signed TP-relative + offset for the variable. For multi-module programs this value is only + meaningful relative to the module's own TLS block base; to get the true + address the thread_vector must be consulted via __tls_get_addr. + + R_TLS_IE (0x21, initial-exec): the loader writes the signed TP-relative + offset directly. address = tp + toc_val is always correct here. + + R_TLS_LD (0x22, local-dynamic): the loader writes the module-relative + offset of the variable within the module's TLS block. + + R_TLS_LE (0x23, local-exec): resolved statically by the linker; no + TOC slot is emitted at runtime. + + R_TLSM (0x24): the loader writes the module-id for the module + that owns the variable. Used with R_TLS. + + R_TLSML (0x25): the loader writes the module-id for the current module. + Used with R_TLS_LD one slot per module, shared by all + local-dynamic variables in that module. + +*/ + +#define XCOFF_R_TLS 0x20 +#define XCOFF_R_TLS_IE 0x21 +#define XCOFF_R_TLS_LD 0x22 +#define XCOFF_R_TLS_LE 0x23 +#define XCOFF_R_TLSM 0x24 +#define XCOFF_R_TLSML 0x25 + struct rs6000_aix_reg_vrreg_offset { int vr0_offset; @@ -84,6 +122,27 @@ static struct rs6000_aix_reg_vrreg_offset rs6000_aix_vrreg_offset = 560 /* vrsave_offset */ }; +/* Description of a single TLS relocation found by scanning the .loader + section. Used as the return value of rs6000_aix_find_tls_reloc. */ + +struct aix_tls_reloc_info +{ + /* The TLS model - XCOFF_R_TLS or XCOFF_R_TLS_IE or XCOFF_R_TLS_LD, + or XCOFF_R_TLS_LE. */ + int rtype = -1; + + /* Runtime address of the primary TOC slot. */ + CORE_ADDR toc_addr = 0; + + /* For global-dynamic: runtime address of the paired R_TLSM slot + that holds the module-id. 0 if not found. */ + CORE_ADDR modid_toc_addr = 0; + + /* For local-dynamic: runtime address of the R_TLSML slot that + holds the module-id for the whole module. 0 if not found. */ + CORE_ADDR modid_ld_addr = 0; +}; + static int rs6000_aix_get_vrreg_offset (ppc_gdbarch_tdep *tdep, const struct rs6000_aix_reg_vrreg_offset *offsets, @@ -1361,21 +1420,369 @@ rs6000_aix_core_xfer_shared_libraries_aix (struct gdbarch *gdbarch, 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 lm_addr == 0. */ + if (!(objfile->flags & OBJF_SHARED)) + return 0; + + /* Return the objfile pointer as the lm_addr token. The per-variable + TOC-slot lookup is done in rs6000_aix_get_thread_local_address where + the static symbol value is also available. */ + if (objfile->obfd.get () == 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"), - objfile_name (objfile)); + _("Cannot resolve TLS for \"%s\": no BFD"), + objfile_name (objfile)); - return 0; + return (CORE_ADDR)(uintptr_t) objfile; +} + +/* Helper to read an 8-byte slot from the inferior at RUNTIME_ADDR. + Returns the value as a signed 64-bit integer, or throws TLS_GENERIC_ERROR + on a memory read failure. */ + +static int64_t +rs6000_aix_read_tls_slot (CORE_ADDR runtime_addr, const char *objfile_name_str) +{ + gdb_byte buf[8]; + if (target_read_memory (runtime_addr, buf, sizeof buf) != 0) + throw_error (TLS_GENERIC_ERROR, + _("Cannot resolve TLS for \"%s\": " + "failed to read TLS TOC slot at %s from inferior"), + objfile_name_str, + core_addr_to_string (runtime_addr)); + return (int64_t) extract_unsigned_integer (buf, sizeof buf, BFD_ENDIAN_BIG); +} + +/* Read the AIX thread entry for module MOD_ID and return the base address of + that module's TLS block. On 64-bit AIX the pointer is stored at the + address pointed to by the thread pointer (tp): + thread_ptr = *(uint64_t *) tp + tls_base = thread_ptr[mod_id] + Each entry is a single 8-byte pointer. Returns 0 if the slot has not + been allocated yet (throws TLS_NOT_ALLOCATED_YET_ERROR in that case). */ + +static CORE_ADDR +rs6000_aix_thread_vec_lookup (ULONGEST tp, uint64_t mod_id, + const char *objfile_name_str) +{ + /* The thread vector pointer lives at *tp on AIX 64-bit. */ + gdb_byte buf[8]; + if (target_read_memory ((CORE_ADDR) tp, buf, sizeof buf) != 0) + throw_error (TLS_GENERIC_ERROR, + _("Cannot resolve TLS for \"%s\": " + "failed to read thread vector pointer at tp=%s"), + objfile_name_str, + core_addr_to_string ((CORE_ADDR) tp)); + + CORE_ADDR thread_ptr + = (CORE_ADDR) extract_unsigned_integer (buf, sizeof buf, BFD_ENDIAN_BIG); + + if (thread_ptr == 0) + throw_error (TLS_NOT_ALLOCATED_YET_ERROR, + _("TLS storage not yet allocated for \"%s\""), + objfile_name_str); + + /* thread_ptr[mod_id] is the TLS block base for this module. */ + CORE_ADDR thread_entry_addr = thread_ptr + mod_id * sizeof (uint64_t); + if (target_read_memory (thread_entry_addr, buf, sizeof buf) != 0) + throw_error (TLS_GENERIC_ERROR, + _("Cannot resolve TLS for \"%s\": " + "failed to read thread_vec[%s] at %s"), + objfile_name_str, + pulongest (mod_id), + core_addr_to_string (thread_entry_addr)); + + CORE_ADDR tls_base + = (CORE_ADDR) extract_unsigned_integer (buf, sizeof buf, BFD_ENDIAN_BIG); + + if (tls_base == 0) + throw_error (TLS_NOT_ALLOCATED_YET_ERROR, + _("TLS storage not yet allocated for \"%s\" (thread vector[%s] == 0)"), + objfile_name_str, pulongest (mod_id)); + + return tls_base; } -/* Implement the get_thread_local_address gdbarch method for AIX. +/* Scan the .loader section of ABFD for a TLS relocation whose loader symbol + value matches SYM_OFFSET. Returns an aix_tls_reloc_info describing what was + found, or one with rtype == -1 if no matching relocation exists. + + For global-dynamic (R_TLS), also walks the reloc table looking for the + paired R_TLSM entry that has the same l_vaddr as the R_TLS slot minus + one pointer-width, which is how the AIX ABI lays out the two-word + {modid, offset} structure in the TOC. + + For local-dynamic (R_TLS_LD), locates the corresponding R_TLSML reloc + which is always self-referencing -- l_symndx == the TOC entry's own csect + index in the same object. */ + +static aix_tls_reloc_info +rs6000_aix_find_tls_reloc (bfd *abfd, + CORE_ADDR data_slide, + const gdb::byte_vector &loader_buf, + CORE_ADDR sym_offset) +{ + aix_tls_reloc_info result; + + struct internal_ldhdr ldhdr; + bfd_xcoff_swap_ldhdr_in (abfd, loader_buf.data (), &ldhdr); + + bfd_size_type loader_size = loader_buf.size (); + bfd_vma sym_start = bfd_xcoff_loader_symbol_offset (abfd, &ldhdr); + bfd_size_type symsz = bfd_xcoff_ldsymsz (abfd); + bfd_vma reloc_start = bfd_xcoff_loader_reloc_offset (abfd, &ldhdr); + bfd_size_type relsz = bfd_xcoff_ldrelsz (abfd); + + if (reloc_start + ldhdr.l_nreloc * relsz > loader_size) + return result; + + /* Find the primary TLS reloc for this variable. */ + const gdb_byte *reloc_ptr = loader_buf.data () + reloc_start; + for (size_t i = 0; i < ldhdr.l_nreloc; i++, reloc_ptr += relsz) + { + struct internal_ldrel ldrel; + bfd_xcoff_swap_ldrel_in (abfd, reloc_ptr, &ldrel); + + int rtype = ldrel.l_rtype & 0xff; + if (rtype != XCOFF_R_TLS && rtype != XCOFF_R_TLS_IE + && rtype != XCOFF_R_TLS_LD) + continue; + + /* l_symndx values 0..2 are section references; loader symbol indices + start at 3. */ + if (ldrel.l_symndx < 3 + || (bfd_vma)(ldrel.l_symndx - 3) >= ldhdr.l_nsyms) + continue; + + bfd_vma ldsym_off = sym_start + (bfd_vma)(ldrel.l_symndx - 3) * symsz; + if (ldsym_off + symsz > loader_size) + continue; + + struct internal_ldsym ldsym; + bfd_xcoff_swap_ldsym_in (abfd, loader_buf.data () + ldsym_off, &ldsym); + + + /* Match: the loader symbol's l_value equals the XCOFF static symbol + value that GDB received as 'sym_offset'. For R_TLS and R_TLS_IE + this is the TP-relative offset baked in at link time. For R_TLS_LD + this is the within-module variable offset. */ + if ((CORE_ADDR)(int64_t) ldsym.l_value != sym_offset) + continue; + + result.rtype = rtype; + result.toc_addr = ldrel.l_vaddr + data_slide; + break; + } + + if (result.rtype == -1) + return result; /* No matching reloc found. */ + + /* Find the paired R_TLSM slot. + The AIX ABI places the two-word {modid, offset} GD descriptor in the + TOC at consecutive 8-byte slots. The R_TLSM reloc targets the first + word = modid and the R_TLS reloc targets the second word = offset. + So the R_TLSM slot lives at result.toc_addr - 8. We verify this by + scanning for an R_TLSM reloc with l_vaddr == result.toc_addr - 8. */ + if (result.rtype == XCOFF_R_TLS) + { + bfd_vma expected_tlsm_vaddr = (result.toc_addr - data_slide) - 8; + reloc_ptr = loader_buf.data () + reloc_start; + for (size_t i = 0; i < ldhdr.l_nreloc; i++, reloc_ptr += relsz) + { + struct internal_ldrel ldrel; + bfd_xcoff_swap_ldrel_in (abfd, reloc_ptr, &ldrel); + + if ((ldrel.l_rtype & 0xff) != XCOFF_R_TLSM) + continue; + if (ldrel.l_vaddr == expected_tlsm_vaddr) + { + result.modid_toc_addr = ldrel.l_vaddr + data_slide; + break; + } + } + } + + /* For local-dynamic is to find the R_TLSML reloc in the same + object. There is exactly one R_TLSML per module; it is self-targeting + i.e. the loader symbol it references is the TOC entry itself. */ + if (result.rtype == XCOFF_R_TLS_LD) + { + reloc_ptr = loader_buf.data () + reloc_start; + for (size_t i = 0; i < ldhdr.l_nreloc; i++, reloc_ptr += relsz) + { + struct internal_ldrel ldrel; + bfd_xcoff_swap_ldrel_in (abfd, reloc_ptr, &ldrel); + + if ((ldrel.l_rtype & 0xff) != XCOFF_R_TLSML) + continue; + + result.modid_ld_addr = ldrel.l_vaddr + data_slide; + break; + } + } + + return result; +} - On 64-bit AIX the thread pointer (TP) is in R13. For variables in +/* Cross-objfile fallback for the variable's owning objfile has no matching TLS + loader reloc (Ex: the variable is only accessed via __tls_get_addr from + code, not via a loader-section TOC slot). Search every other loaded objfile + for an R_TLS_IE or R_TLS reloc whose loader symbol l_value matches OFFSET. + + On AIX with GCC, the main executable typically has R_TLS_IE relocs for + cross-module TLS variables, even when the defining .so has no matching + R_TLS / R_TLSM loader relocs for them. The R_TLS_IE TOC slot value is + the signed TP-relative offset, valid as: address = tp + toc_val. + + Returns a filled aix_tls_reloc_info, or one with rtype==-1 if not found. */ + +static aix_tls_reloc_info +rs6000_aix_scan_all_objfiles (CORE_ADDR offset) +{ + for (objfile &candidate : current_program_space->objfiles ()) + { + if (candidate.obfd.get () == nullptr) + continue; + + bfd *abfd = candidate.obfd.get (); + asection *loader_sec = bfd_get_section_by_name (abfd, ".loader"); + if (loader_sec == nullptr) + continue; + + 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)) + continue; + + aix_tls_reloc_info ri + = rs6000_aix_find_tls_reloc (abfd, + candidate.data_section_offset (), + loader_buf, offset); + if (ri.rtype != -1) + return ri; + } + + aix_tls_reloc_info empty; + return empty; +} + +/* This is a function for TLS resolution for a variable in OBJFILE when no loader reloc + was found in the owning .so or in any other loaded module. + + On AIX the R_TLSM (module-id) slot is sometimes left as zero by the loader, + so above functions cannot be used. As a final fallback an attempt to + resolve via the runtime __tls_get_addr mechanism: + + 1. Verify __tls_get_addr is present which confirms TLS-capable runtime. + 2. Find any R_TLSM or R_TLSML slot in the owning .so that is non-zero and + use its module-id to walk the thread vector. + 3. If all R_TLSM/R_TLSML slots are zero (loader did not fill them), throw + TLS_NOT_ALLOCATED_YET_ERROR. */ + +static CORE_ADDR +rs6000_aix_compute_tls_address (struct objfile *objfile, + ULONGEST tp, + CORE_ADDR offset) +{ + if (lookup_minimal_symbol (current_program_space, "__tls_get_addr").minsym + == nullptr) + throw_error (TLS_GENERIC_ERROR, + _("Cannot resolve TLS for \"%s\": " + "no loader relocation found and __tls_get_addr not " + "present in inferior"), + objfile_name (objfile)); + + bfd *abfd = objfile->obfd.get (); + 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"), + objfile_name (objfile)); + + 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 relsz = bfd_xcoff_ldrelsz (abfd); + CORE_ADDR data_slide = objfile->data_section_offset (); + + const gdb_byte *reloc_ptr = loader_buf.data () + reloc_start; + for (size_t i = 0; i < ldhdr.l_nreloc; i++, reloc_ptr += relsz) + { + struct internal_ldrel ldrel; + bfd_xcoff_swap_ldrel_in (abfd, reloc_ptr, &ldrel); + int rt = ldrel.l_rtype & 0xff; + if (rt != XCOFF_R_TLSM && rt != XCOFF_R_TLSML) + continue; + + CORE_ADDR modid_addr = ldrel.l_vaddr + data_slide; + uint64_t mod_id + = (uint64_t) rs6000_aix_read_tls_slot (modid_addr, + objfile_name (objfile)); + if (mod_id == 0) + continue; /* loader left this slot empty */ + + CORE_ADDR tls_base = rs6000_aix_thread_vec_lookup (tp, mod_id, + objfile_name (objfile)); + return tls_base + offset; + } + + /* Every R_TLSM/R_TLSML slot is zero -- TLS has not been initialised yet. */ + throw_error (TLS_NOT_ALLOCATED_YET_ERROR, + _("TLS storage not yet allocated for \"%s\""), + objfile_name (objfile)); +} + +/* Scan the .loader section of OBJFILE and return TLS relocation information + for the variable with static symbol value SYM_OFFSET. Throws on hard + errors; returns an aix_tls_reloc_info with rtype == -1 if not found + (caller must decide what to do). */ + +static aix_tls_reloc_info +rs6000_aix_scan_loader (struct objfile *objfile, CORE_ADDR sym_offset) +{ + bfd *abfd = objfile->obfd.get (); + + 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"), + objfile_name (objfile)); + + { + 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 relsz = bfd_xcoff_ldrelsz (abfd); + if (reloc_start + ldhdr.l_nreloc * relsz > loader_size) + throw_error (TLS_GENERIC_ERROR, + _("Cannot resolve TLS for \"%s\": " + ".loader section truncated"), + objfile_name (objfile)); + } + + return rs6000_aix_find_tls_reloc (abfd, + objfile->data_section_offset (), + loader_buf, sym_offset); +} + +/* On 64-bit AIX the thread pointer (TP) is in R13. For variables in the main executable (lm_addr == 0) the XCOFF symbol value is a signed TP-relative offset baked in at link time: address = tp + (int64_t) offset */ @@ -1405,8 +1812,88 @@ rs6000_aix_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid, if (lm_addr == 0) return tp + (CORE_ADDR)(int64_t) offset; + /* Shared-library models: lm_addr encodes the objfile pointer. */ + struct objfile *objfile = (struct objfile *)(uintptr_t) lm_addr; + aix_tls_reloc_info ri = rs6000_aix_scan_loader (objfile, offset); + + if (ri.rtype == -1) + { + /* No loader reloc in the owning .so. Search every other objfile + (typically the main exe has R_TLS_IE for cross-module vars). */ + ri = rs6000_aix_scan_all_objfiles (offset); + if (ri.rtype == -1) + return rs6000_aix_compute_tls_address (objfile, tp, offset); + } + + if (ri.rtype == XCOFF_R_TLS_IE) + { + /* initial-exec: tp + signed TP-relative offset from TOC slot. */ + int64_t tp_offset = rs6000_aix_read_tls_slot (ri.toc_addr, + objfile_name (objfile)); + if (tp_offset == 0) + throw_error (TLS_NOT_ALLOCATED_YET_ERROR, + _("TLS storage not yet allocated for \"%s\""), + objfile_name (objfile)); + return tp + (CORE_ADDR) tp_offset; + } + + if (ri.rtype == XCOFF_R_TLS) + { + /* global-dynamic case only. + The AIX loader sometimes leaves R_TLSM as zero; in that case + TOC[R_TLS] itself holds the TP-relative offset (equivalent to + initial-exec), so use tp + toc_val as the fallback. */ + if (ri.modid_toc_addr != 0) + { + uint64_t mod_id = (uint64_t) rs6000_aix_read_tls_slot ( + ri.modid_toc_addr, objfile_name (objfile)); + if (mod_id != 0) + { + int64_t var_offset = rs6000_aix_read_tls_slot ( + ri.toc_addr, objfile_name (objfile)); + CORE_ADDR tls_base = rs6000_aix_thread_vec_lookup (tp, mod_id, + objfile_name (objfile)); + return tls_base + (CORE_ADDR) var_offset; + } + } + + /* R_TLSM not found or zero: fall back to tp + TOC[R_TLS]. */ + int64_t tp_offset = rs6000_aix_read_tls_slot (ri.toc_addr, + objfile_name (objfile)); + if (tp_offset == 0) + throw_error (TLS_NOT_ALLOCATED_YET_ERROR, + _("TLS storage not yet allocated for \"%s\""), + objfile_name (objfile)); + return tp + (CORE_ADDR) tp_offset; + } + + if (ri.rtype == XCOFF_R_TLS_LD) + { + /* local-dynamic: TOC[R_TLSML] holds the module-id. */ + if (ri.modid_ld_addr != 0) + { + uint64_t mod_id = (uint64_t) rs6000_aix_read_tls_slot ( + ri.modid_ld_addr, objfile_name (objfile)); + if (mod_id != 0) + { + int64_t var_offset = rs6000_aix_read_tls_slot ( + ri.toc_addr, objfile_name (objfile)); + CORE_ADDR tls_base = rs6000_aix_thread_vec_lookup (tp, mod_id, + objfile_name (objfile)); + return tls_base + (CORE_ADDR) var_offset; + } + } + + /* R_TLSML is zero: last resort. */ + return rs6000_aix_compute_tls_address (objfile, tp, offset); + } + + /* R_TLS_LE in a shared library should not occur; the linker rejects it. */ throw_error (TLS_GENERIC_ERROR, - _("TLS in shared libraries not yet supported on AIX")); + _("Cannot resolve TLS for \"%s\": " + "unexpected TLS relocation type 0x%x in shared library"), + objfile_name (objfile), ri.rtype); + } static void -- 2.51.2