From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id afl4G56DomrHbzwAWB0awg (envelope-from ) for ; Thu, 10 Sep 2026 06:17:02 -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=HxQlbxrZ; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 6BBCF1E09E; Thu, 10 Sep 2026 06:17:02 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=0.9 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,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=no 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 306581E091 for ; Thu, 10 Sep 2026 06:17:01 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 4BBB24BA23F1 for ; Thu, 10 Sep 2026 10:17:00 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 4BBB24BA23F1 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=HxQlbxrZ Received: from mail-pj1-x102a.google.com (mail-pj1-x102a.google.com [IPv6:2607:f8b0:4864:20::102a]) by sourceware.org (Postfix) with ESMTPS id C28F94BA2E3C for ; Thu, 10 Sep 2026 10:16:31 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C28F94BA2E3C 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 C28F94BA2E3C Authentication-Results: sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::102a ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1789035392; cv=none; b=V8mmD6txq8k0X7QAGsRGq0dgbrRCIeVrQWxR6/6V1S2lBoYPfiJdSyppHA097rLszbYBonk2X8SLGvBgHPk5IzcDjdzYxYgs5vTykd4Ihwe1X6pXdoRBURlTWkUMwuqmJl6mVl0XmhxYSZUCrFkdXxvSlj5sMqlvtwlG192Ztas= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1789035392; c=relaxed/simple; bh=/cWaYj3ReKl/Z7gvBhbFcgEBbMdp0viWUoB/hrIVqo8=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=ZcjbVjAotksBT71wQUnRob1JngNSMSPg4xAq2m6CJilMIsWZARucf91SeN7+SipbNUGfm5HLdsJt8+urt9W65AptIQ7Xw6O/dqlfwJw2odS5ct4J5HoPqrFjhH1wjIfjunLWUsKLEvvljV16LRjjQUjeotuU6C7ur8R57izzEhU= 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=HxQlbxrZ DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C28F94BA2E3C Received: by mail-pj1-x102a.google.com with SMTP id 98e67ed59e1d1-398b1e63c49so642179a91.0 for ; Thu, 10 Sep 2026 03:16:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789035390; x=1789640190; 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=TaqPUs6lg/5JFS3415UuVeuBJ7T9goEh6tWKR9rmvSI=; b=HxQlbxrZ4/utj1GyP7Hd9oim525ZAbLU3Jn+sQ2sKDY8NdDQ2d4HiRJZER0mtnz/xp /+aHbMJDceAXpSFRFGbEZDdWX7Xr3o0ViNYA82FSDqy8HrwOJX4o9nMfUjxAE32d1ZgK iFvD9N5CVDUp1+2moLrVvxuwEAb9uI61FOnWy+5WETC6CzckCfDvj7oaq5ZKGQWI0RNx QP4QFoveLrM4MzeFPN2gPApI1vSN7JUusB/oQ2iDDPZXnThmefoS0Z0oyrbr5teROcda Q6U8o3HVSBPMSlpI6kKsxzLGUy8SfgXiUkkFNp5uUtUCzsb3NCe2SrlYkMovrs2Ooo0/ lQhA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789035390; x=1789640190; 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=TaqPUs6lg/5JFS3415UuVeuBJ7T9goEh6tWKR9rmvSI=; b=VecuecIhzEwcxNd/DzE3IU9xqhF5Dd7fAp4Z6pf4vI5lCzKNwxgSUVy5ROCayJU1Ar xV6QcfyOWbkiKXl4JUs3XMVgeByi0qSMJsU3nUEUE0M25BqzZfUnY3h9rnIqHiKlwvzk aDAL3Z9uD4jyRNdvk8IzFNFeWEXuc4ewyNeB83+kRUXtICG/b2PKXfdYl+3DclhwxS2D v74fhzDLbMeZ7hTPsFLH3sRuMGojyWnkB0xXSxtam8S8omBc4p29Zwrdgn2WrcBgtKFD O4+JrLvtBCsfukT00WLEyPoRQym7i21+9+mSHb7aHkq/6WU3ZJhdbRqbAy3W/cXoppkz 548Q== X-Gm-Message-State: AFuF++k6eJPfwcOUML8u4E0xDdah9vo2PsL64GmmHK0nwx7oKGUaFbEj TuufpUXjDd7aOS5X13GU7uoxemndBtmK9NHrtUScaolCb8GTqj6DD8Vs X-Gm-Gg: AYBFou0gZ1RVmUBqPJUGUOI4L2/qKFPCQu12Ea/qJxShMZAiun7oXrnZC7b+Rbg1i38 JsOEdAOpHFVp2F8wKPIxCMrTLBlq4wr3VtEiAHronj5jlfiNSYjaHXyari34NQVUkUSTJcoFPCJ bMf55la36ZdA3y0xDSV+m9xrZiHJY0d7qw7jV/O4LOY2OAzWVxei2Lb15vhMwr0zyiwiflgqeoC BWJzEW6Mqtjuai9sZG6kTM141bVh4j9bEmoYvure/cWAAZk1pnihpb7iL8Flh/9dBQehIQAhOoq m8COBFyZE7fZ6NzOFEtCJP0eYhYHn7UM5R6OzexZTaRZIg9Dy76Rwtmz40ReH4OPvbMmFx4+kCA kjyoWKnQrZL3BzOOkLscmJaXpOeJHMICHYlJrYAbo0HC+Er+FAOKzqyLQaD0PHiB0a38A3bhF2q rs0COtzCbPsBjc8XrvyOenc9K1/jMaw/Gs5slibbtU4E7Y/FJsYme8MsZkWTE4mx25JYys+5V5B iZdOq0vTaqkQRpfQuk1ylGXvTiy X-Received: by 2002:a17:90b:1d51:b0:398:9beb:a2b4 with SMTP id 98e67ed59e1d1-39d77b762f4mr4334740a91.22.1789035390274; Thu, 10 Sep 2026 03:16:30 -0700 (PDT) Received: from localhost.localdomain ([122.171.22.218]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-1434c745a09sm18320937c88.7.2026.09.10.03.16.27 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Thu, 10 Sep 2026 03:16:29 -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 v4 2/3] Add support for TLS variables in shared libraries on AIX. Date: Thu, 10 Sep 2026 15:45:54 +0530 Message-ID: <20260910101553.85926-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 after applying 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. Module-id resolution for shared libraries uses two strategies: 1. R_TLSML (local-dynamic): there is exactly one R_TLSML reloc per module, and the loader always writes that module's own id into the corresponding TOC slot. Read it from the inferior and return it. 2. R_TLSM (global-dynamic): each R_TLSM relocation in any loaded module's .loader section references the library that exports the named TLS symbol. Scan R_TLSM relocs across all loaded objfiles for each, resolve the loader symbol name and check whether the library of interest exports it. If so, read the TOC slot value, that is the library's module-id. Matching is done by symbol name, not by symbol value, so two libraries that both define a TLS variable at within-module offset 0 are distinguished correctly. The module-id is cached in a per-objfile structure so the .loader scan is paid only once per shared library. Module-id 0 is a valid AIX loader assignment in initial-exec model and is not treated as "not yet allocated". The main executable is distinguished from a module-id-0 library by returning the out-of-band sentinel XCOFF_MODID_MAIN_EXE (UINT64_MAX) instead of 0. the address computation is done using 3 cases, they are: - XCOFF_MODID_MAIN_EXE: local-exec where the XCOFF symbol value is the signed TP-relative offset directly; address = tp + offset. - module-id 0 is initial-execed shared library where the AIX loader merges all initial-exec modules into one contiguous TLS segment and adjusts each variable's TP-relative offset accordingly. The link-time XCOFF symbol value does not necessarily equal the runtime offset. The patch scans the library's R_TLS slots and matches by l_value (unique within one module) to find the slot the loader filled with the actual runtime TP-relative offset, then computes address = tp + runtime_offset. - module-id n > 0 is global-dynamic where we iterate the per-thread thread vector: tls_base = thread_vector[n]; address = tls_base + offset. --- gdb/rs6000-aix-tdep.c | 455 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 438 insertions(+), 17 deletions(-) diff --git a/gdb/rs6000-aix-tdep.c b/gdb/rs6000-aix-tdep.c index 76763f08e08..8b6a79f158d 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,28 @@ static struct rs6000_aix_reg_vrreg_offset rs6000_aix_vrreg_offset = 560 /* vrsave_offset */ }; +/* Sentinel returned by rs6000_aix_fetch_tls_load_module_address for the main + executable. AIX module IDs are small non-negative integers, so UINT64_MAX + is safely outside the range the loader ever uses. */ +#define XCOFF_MODID_MAIN_EXE ((CORE_ADDR) UINT64_MAX) + +/* Per-objfile cache for the AIX TLS module-id. + + fetch_tls_load_module_address scans the .loader section once and caches + the result so subsequent TLS lookups in the same shared library are cheap. + + mod_id == 0 is a real module-id (initial-exec); it does not mean + "not yet resolved". resolved == true is the flag for that. */ + +struct aix_tls_objfile_data +{ + bool resolved = false; + uint64_t mod_id = 0; +}; + +static const registry::key + aix_tls_objfile_data_key; + static int rs6000_aix_get_vrreg_offset (ppc_gdbarch_tdep *tdep, const struct rs6000_aix_reg_vrreg_offset *offsets, @@ -1356,29 +1416,362 @@ rs6000_aix_core_xfer_shared_libraries_aix (struct gdbarch *gdbarch, offset, len, 0); } -/* For AIX, use the rs6000_aix_fetch_tls_load_module_address gdbarch method. */ +/* Helper to read an 8-byte unsigned slot from the inferior at RUNTIME_ADDR. + Returns the value, or throws TLS_GENERIC_ERROR on memory read failure. */ + +static uint64_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 (uint64_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 thread vector pointer lives at *tp: + thread_vec_ptr = *(uint64_t *) tp + tls_base = thread_vec_ptr[mod_id] (each entry is one 8-byte pointer) + Throws TLS_NOT_ALLOCATED_YET_ERROR if the slot is not yet filled. */ + +static CORE_ADDR +rs6000_aix_thread_vec_lookup (ULONGEST tp, uint64_t mod_id, + const char *objfile_name_str) +{ + 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_vec_ptr + = (CORE_ADDR) extract_unsigned_integer (buf, sizeof buf, BFD_ENDIAN_BIG); + + if (thread_vec_ptr == 0) + throw_error (TLS_NOT_ALLOCATED_YET_ERROR, + _("TLS storage not yet allocated for \"%s\""), + objfile_name_str); + + CORE_ADDR entry_addr = thread_vec_ptr + mod_id * sizeof (uint64_t); + if (target_read_memory (entry_addr, buf, sizeof buf) != 0) + throw_error (TLS_GENERIC_ERROR, + _("Cannot resolve TLS for \"%s\": " + "failed to read thread_vector[%s] at %s"), + objfile_name_str, + pulongest (mod_id), + core_addr_to_string (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; +} + +/* Return true if OBJFILE exports a minimal symbol named NAME. Used by + rs6000_aix_fetch_tls_load_module_address to match R_TLSM relocations + to their owning library by symbol name rather than by offset value. */ + +static bool +rs6000_aix_objfile_exports_symbol (struct objfile *objfile, const char *name) +{ + for (minimal_symbol *msym : objfile->msymbols ()) + if (strcmp (msym->linkage_name (), name) == 0) + return true; + return false; +} + +/* Scan the .loader section of OBJFILE to determine the AIX TLS module-id + for that shared library. The module-id is the index into the per-thread + thread vector that the AIX runtime fills at load time. + + Two strategies are tried in order: + + 1. R_TLSML (local-dynamic): there is exactly one R_TLSML reloc per + module, and the loader always writes that module's own id into the + corresponding TOC slot. If found, read the slot value from the + inferior and return it. + + 2. R_TLSM (global-dynamic): each R_TLSM reloc references the module + that *exports* the TLS symbol named in the accompanying loader + symbol table entry. Scan every R_TLSM reloc across all loaded + objfiles; for each, check whether OBJFILE exports the referenced + symbol name. If it does, the TOC slot value for that reloc is + OBJFILE's module-id. + + Returns the module-id, or throws TLS_NOT_ALLOCATED_YET_ERROR if the + loader has not filled any relevant slot yet. */ + +static uint64_t +rs6000_aix_find_module_id (struct objfile *obj) +{ + const char *oname = objfile_name (obj); + bfd *abfd = obj->obfd.get (); + + /* ---- Strategy 1: R_TLSML in the owning .so ---- */ + { + asection *loader_sec = bfd_get_section_by_name (abfd, ".loader"); + if (loader_sec != nullptr) + { + 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)) + { + 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 = obj->data_section_offset (); + + const gdb_byte *rp = loader_buf.data () + reloc_start; + for (size_t i = 0; i < ldhdr.l_nreloc; i++, rp += relsz) + { + struct internal_ldrel ldrel; + bfd_xcoff_swap_ldrel_in (abfd, rp, &ldrel); + if ((ldrel.l_rtype & 0xff) != XCOFF_R_TLSML) + continue; + + /* R_TLSML: the loader writes this module's own id here. + mod_id == 0 is valid (initial-exec). */ + CORE_ADDR slot_addr = ldrel.l_vaddr + data_slide; + uint64_t mod_id = rs6000_aix_read_tls_slot (slot_addr, oname); + return mod_id; + } + } + } + } + + /* ---- Strategy 2: R_TLSM scan across all objfiles ---- */ + for (struct objfile &candidate : current_program_space->objfiles ()) + { + if (candidate.obfd.get () == nullptr) + continue; + + bfd *cabfd = candidate.obfd.get (); + asection *loader_sec = bfd_get_section_by_name (cabfd, ".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 (cabfd, loader_sec, loader_buf.data (), + 0, loader_size)) + continue; + + struct internal_ldhdr ldhdr; + bfd_xcoff_swap_ldhdr_in (cabfd, loader_buf.data (), &ldhdr); + bfd_vma sym_start = bfd_xcoff_loader_symbol_offset (cabfd, &ldhdr); + bfd_size_type symsz = bfd_xcoff_ldsymsz (cabfd); + bfd_vma reloc_start = bfd_xcoff_loader_reloc_offset (cabfd, &ldhdr); + bfd_size_type relsz = bfd_xcoff_ldrelsz (cabfd); + CORE_ADDR data_slide = candidate.data_section_offset (); + /* String table immediately follows the loader symbol table. */ + const char *strtab + = (ldhdr.l_stoff < loader_size + ? (const char *) loader_buf.data () + ldhdr.l_stoff + : nullptr); + + const gdb_byte *rp = loader_buf.data () + reloc_start; + for (size_t i = 0; i < ldhdr.l_nreloc; i++, rp += relsz) + { + struct internal_ldrel ldrel; + bfd_xcoff_swap_ldrel_in (cabfd, rp, &ldrel); + if ((ldrel.l_rtype & 0xff) != XCOFF_R_TLSM) + continue; + + /* Indices 0..2 are the special .text/.data/.bss section refs. */ + 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 (cabfd, + loader_buf.data () + ldsym_off, &ldsym); + + /* Names up to SYMNMLEN bytes are stored inline; longer ones + are in the string table. */ + char nambuf[SYMNMLEN + 1]; + const char *sym_name; + if (ldsym._l._l_l._l_zeroes != 0) + { + memcpy (nambuf, ldsym._l._l_name, SYMNMLEN); + nambuf[SYMNMLEN] = '\0'; + sym_name = nambuf; + } + else if (strtab != nullptr + && ldsym._l._l_l._l_offset < ldhdr.l_stlen) + sym_name = strtab + ldsym._l._l_l._l_offset; + else + continue; + + /* If OBJ exports this symbol then the loader wrote OBJ's + module-id into this R_TLSM slot. First match wins. */ + if (!rs6000_aix_objfile_exports_symbol (obj, sym_name)) + continue; + + /* mod_id == 0 is valid (initial-exec); return it as-is. */ + CORE_ADDR slot_addr = ldrel.l_vaddr + data_slide; + uint64_t mod_id = rs6000_aix_read_tls_slot (slot_addr, oname); + return mod_id; + } + } + + throw_error (TLS_GENERIC_ERROR, + _("Cannot resolve TLS for \"%s\": " + "no R_TLSML or R_TLSM relocation found for this module"), + oname); +} + +/* Implement the fetch_tls_load_module_address gdbarch hook for AIX. + + For the main executable return XCOFF_MODID_MAIN_EXE so that + get_thread_local_address can tell it apart from a shared library whose + module-id happens to be 0. For shared libraries, scan the .loader + section for the module-id the AIX loader wrote into the R_TLSML or + R_TLSM slot, cache it per-objfile, and return it. */ 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) + /* The main executable uses local-exec; no thread-vector lookup needed. */ + if (!(objfile->flags & OBJF_SHARED)) + return XCOFF_MODID_MAIN_EXE; + + 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 cached result if available. */ + aix_tls_objfile_data &cache + = aix_tls_objfile_data_key.try_emplace (objfile); + if (cache.resolved) + return (CORE_ADDR) cache.mod_id; + + /* Scan .loader section to determine the real module-id. */ + uint64_t mod_id = rs6000_aix_find_module_id (objfile); + + cache.mod_id = mod_id; + cache.resolved = true; + + return (CORE_ADDR) mod_id; +} + +/* For a shared library loaded with module-id 0 (initial-exec model), the + AIX loader writes the actual runtime TP-relative offset into each R_TLS + slot. This runtime value can differ from the link-time XCOFF symbol + value because the initial TLS segment merges multiple modules and may + shift each module's variables relative to their per-module link-time + positions. + + Scan OBJFILE's .loader section for an R_TLS reloc whose loader-symbol + l_value matches STATIC_OFFSET (the XCOFF symbol value GDB has). Within + one module those link-time values are unique, so the match pinpoints the + right slot. If found, read the runtime value from the inferior's TOC + slot and store it in *RUNTIME_OFFSET. Return true on success. */ + +static bool +rs6000_aix_find_initial_exec_tls_offset (struct objfile *objfile, + CORE_ADDR static_offset, + int64_t *runtime_offset) +{ + bfd *abfd = objfile->obfd.get (); + if (abfd == nullptr) + return false; + + asection *loader_sec = bfd_get_section_by_name (abfd, ".loader"); + if (loader_sec == nullptr) + return false; + + 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)) + return false; + + struct internal_ldhdr ldhdr; + bfd_xcoff_swap_ldhdr_in (abfd, loader_buf.data (), &ldhdr); + + 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); + CORE_ADDR data_slide = objfile->data_section_offset (); + + const gdb_byte *rp = loader_buf.data () + reloc_start; + for (size_t i = 0; i < ldhdr.l_nreloc; i++, rp += relsz) + { + struct internal_ldrel ldrel; + bfd_xcoff_swap_ldrel_in (abfd, rp, &ldrel); + + /* R_TLSM and R_TLSML carry module-ids, not variable offsets. */ + if ((ldrel.l_rtype & 0xff) != XCOFF_R_TLS) + continue; + + /* Indices 0..2 are the special .text/.data/.bss section refs. */ + 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); + + /* l_value holds the link-time TP-relative offset. It is unique + within a module, so matching against static_offset identifies + exactly the right variable. */ + if ((CORE_ADDR)(int64_t) ldsym.l_value != static_offset) + continue; + + /* Read the runtime value the loader wrote into this TOC slot. */ + CORE_ADDR slot_addr = ldrel.l_vaddr + data_slide; + gdb_byte buf[8]; + if (target_read_memory (slot_addr, buf, sizeof buf) != 0) + return false; + + *runtime_offset + = (int64_t) extract_unsigned_integer (buf, sizeof buf, BFD_ENDIAN_BIG); + return true; + } + + return false; } /* Implement the get_thread_local_address gdbarch method for AIX. - 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 */ + On 64-bit AIX the thread pointer is in r13. + + lm_addr is what fetch_tls_load_module_address returned: + XCOFF_MODID_MAIN_EXE - main executable, local-exec. The XCOFF symbol + value is the signed TP-relative offset directly: address = tp + offset. + 0 - shared library with AIX module-id 0 (initial-exec). The XCOFF + static symbol value is the within-module link-time offset; the loader + may have shifted it in the merged initial TLS segment. Scan the R_TLS + slots to get the real runtime TP-relative offset. + n > 0 - shared library with global-dynamic model (module-id n). Walk + the thread vector: address = thread_vector[n] + offset. */ static CORE_ADDR rs6000_aix_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid, @@ -1401,12 +1794,40 @@ 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")); - /* local-exec: XCOFF symbol value is a signed TP-relative offset. */ - if (lm_addr == 0) + /* Local-exec (main executable): the XCOFF symbol value is the signed + TP-relative offset. */ + if (lm_addr == XCOFF_MODID_MAIN_EXE) return tp + (CORE_ADDR)(int64_t) offset; - throw_error (TLS_GENERIC_ERROR, - _("TLS in shared libraries not yet supported on AIX")); + /* Initial-exec shared library (module-id 0): the link-time symbol value + may not be the runtime TP-relative offset because the initial TLS + segment can shift a library's variables. Find the actual offset from + the R_TLS slot the loader filled in. */ + if (lm_addr == 0) + { + for (struct objfile &candidate : current_program_space->objfiles ()) + { + if (!(candidate.flags & OBJF_SHARED)) + continue; + if (candidate.obfd.get () == nullptr) + continue; + + int64_t runtime_tprel; + if (rs6000_aix_find_initial_exec_tls_offset (&candidate, + offset, + &runtime_tprel)) + return tp + (CORE_ADDR) runtime_tprel; + } + + /* No R_TLS slot found; fall back to the static offset. */ + return tp + (CORE_ADDR)(int64_t) offset; + } + + /* Global-dynamic (module-id > 0): walk the thread vector. */ + uint64_t mod_id = (uint64_t) lm_addr; + CORE_ADDR tls_base = rs6000_aix_thread_vec_lookup (tp, mod_id, + ""); + return tls_base + offset; } static void -- 2.51.2