From e18a8d33bd2cebce20e1609a2a69b6b22ef38b5a Mon Sep 17 00:00:00 2001 From: Aditya Kamath Date: Thu, 18 Jun 2026 09:59:29 -0500 Subject: [PATCH 3/3] Add 32 bit support for multi-thread backtrace --- gdb/aix-thread.c | 59 +++++++++++++++++++++ gdb/ppc-tdep.h | 4 ++ gdb/rs6000-aix-tdep.c | 56 ++++++++++++++++---- gdb/rs6000-aix-vapi.h | 7 ++- gdb/rs6000-tdep.c | 116 ++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 225 insertions(+), 17 deletions(-) diff --git a/gdb/aix-thread.c b/gdb/aix-thread.c index 2716fb138be..fb31945286d 100644 --- a/gdb/aix-thread.c +++ b/gdb/aix-thread.c @@ -270,6 +270,65 @@ pd_status2str (int status) default: return "UNKNOWN"; } } +/* Get the pthread address for the current thread. + This is used for VAPI frame unwinding to calculate the TLS base address. + Returns 0 if not in a pthread context or if the call fails. */ + +/* Check if the current inferior is pthread-enabled (has a pthdb session). */ +bool +aix_thread_is_pthread_enabled (void) +{ + struct aix_thread_variables *data; + data = get_thread_data_helper_for_ptid (inferior_ptid); + return (data != nullptr && data->pd_session != 0); +} + +CORE_ADDR +aix_thread_get_pthread_addr (void) +{ + thread_info *thread = inferior_thread (); + if (!thread) + { + //gdb_printf (gdb_stdlog, "aix_thread_get_pthread_addr: no thread\n"); + return 0; + } + + aix_thread_info *priv = get_aix_thread_info (thread); + if (!priv) + { + //gdb_printf (gdb_stdlog, "aix_thread_get_pthread_addr: no priv (thread not synced yet)\n"); + return 0; + } + + pthdb_pthread_t pdtid = priv->pdtid; + if (pdtid == 0) + { + //gdb_printf (gdb_stdlog, "aix_thread_get_pthread_addr: pdtid=0 (not a pthread)\n"); + return 0; /* Not a pthread */ + } + + struct aix_thread_variables *data; + data = get_thread_data_helper_for_ptid (inferior_ptid); + if (!data || !data->pd_session) + { + //gdb_printf (gdb_stdlog, "aix_thread_get_pthread_addr: no data or session\n"); + return 0; + } + + pthdb_addr_t pthread_addr; + int status = pthdb_pthread_addr (data->pd_session, pdtid, &pthread_addr); + + if (status != PTHDB_SUCCESS) + { + //gdb_printf (gdb_stdlog, "aix_thread_get_pthread_addr: pthdb_pthread_addr failed, status=%d\n", status); + return 0; + } + + //gdb_printf (gdb_stdlog, "aix_thread_get_pthread_addr: SUCCESS, pthread_addr=0x%lx\n", + // (unsigned long)pthread_addr); + return (CORE_ADDR) pthread_addr; +} + /* A call to ptrace(REQ, ID, ...) just returned RET. Check for exceptional conditions and either return nonlocally or else return diff --git a/gdb/ppc-tdep.h b/gdb/ppc-tdep.h index aca8914f055..3b6ec6b6469 100644 --- a/gdb/ppc-tdep.h +++ b/gdb/ppc-tdep.h @@ -446,6 +446,10 @@ extern CORE_ADDR ppc_insn_prefix_dform (unsigned int insn1, extern int ppc_process_record (struct gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr); +/* AIX thread support - check if pthread-enabled and get pthread address for VAPI frame unwinding. */ +extern bool aix_thread_is_pthread_enabled (void); +extern CORE_ADDR aix_thread_get_pthread_addr (void); + /* Instruction size. */ #define PPC_INSN_SIZE 4 diff --git a/gdb/rs6000-aix-tdep.c b/gdb/rs6000-aix-tdep.c index d6614077b1d..ad8c896e349 100644 --- a/gdb/rs6000-aix-tdep.c +++ b/gdb/rs6000-aix-tdep.c @@ -491,20 +491,54 @@ rs6000_aix_adjust_vapi_lr (const frame_info_ptr &frame, CORE_ADDR pc, if (tls_base != 0) vapi_lr_addr = tls_base - VAPI_CB_OFFSET_64 + VAPI_SAVED_LR_OFFSET; } - /* For 32-bit, SPRG3 (not r13) holds the thread pointer and is at a fixed - address. r13 returns 0xdeadbeef because ptrace cannot read SPRG3. */ + /* For 32-bit, SPRG3 (not r13) holds the thread pointer. + r13 returns 0xdeadbeef because ptrace cannot read SPRG3. + For multi-threaded programs, we need to get pthread address and + calculate TLS base as: pthread_addr + pthread_size. */ else { - /* In 32-bit AIX, SPRG3 always points to a fixed address. - This is the thread pointer for 32-bit programs. */ - tls_base = VAPI_CB_ADDR_32_NOTHREADS; - vapi_lr_addr = tls_base + VAPI_SAVED_LR_OFFSET; + bool is_pthread = aix_thread_is_pthread_enabled (); - gdb_printf (gdb_stdlog, - "rs6000_aix_adjust_vapi_lr: Using fixed SPRG3=0x%s, " - "v_save_lr at 0x%s for 32-bit\n", - phex_nz (tls_base, wordsize), - phex_nz (vapi_lr_addr, wordsize)); + if (is_pthread) + { + /* Multi-threaded program - try to get pthread address */ + CORE_ADDR pthread_addr = aix_thread_get_pthread_addr (); + + if (pthread_addr != 0) + { + /* Multi-threaded case: TLS base = pthread_addr + pthread_size */ + tls_base = pthread_addr + PTHREAD_SIZE_32; + vapi_lr_addr = tls_base + VAPI_SAVED_LR_OFFSET; + + //gdb_printf (gdb_stdlog, + // "rs6000_aix_adjust_vapi_lr: Multi-threaded 32-bit: " + // "pthread_addr=0x%s, TLS_base=0x%s, v_save_lr at 0x%s\n", + // phex_nz (pthread_addr, wordsize), + // phex_nz (tls_base, wordsize), + // phex_nz (vapi_lr_addr, wordsize)); + } + else + { + /* Pthread-enabled but can't get pthread_addr (thread not synced). + Don't adjust LR - return false to skip VAPI adjustment. */ + //gdb_printf (gdb_stdlog, + // "rs6000_aix_adjust_vapi_lr: Multi-threaded but pthread_addr=0 " + // "(thread not synced), skipping VAPI LR adjustment\n"); + return false; + } + } + else + { + /* Non-threaded case: use fixed SPRG3 address */ + tls_base = VAPI_CB_ADDR_32_NOTHREADS; + vapi_lr_addr = tls_base + VAPI_SAVED_LR_OFFSET; + + //gdb_printf (gdb_stdlog, + //"rs6000_aix_adjust_vapi_lr: Non-threaded 32-bit: " + //"Using fixed SPRG3=0x%s, v_save_lr at 0x%s\n", + //phex_nz (tls_base, wordsize), + //phex_nz (vapi_lr_addr, wordsize)); + } } /* Read the saved LR from VAPI control block */ diff --git a/gdb/rs6000-aix-vapi.h b/gdb/rs6000-aix-vapi.h index 761ecda9312..10754edc4c8 100644 --- a/gdb/rs6000-aix-vapi.h +++ b/gdb/rs6000-aix-vapi.h @@ -75,9 +75,14 @@ ======================================================================== */ /* For 32-bit programs, SPRG3 (not r13) holds the thread pointer and always - points to this fixed address. */ + points to this fixed address for non-threaded programs. */ #define VAPI_CB_ADDR_32_NOTHREADS 0x2ff22ef0 +/* Pthread structure size for 32-bit AIX (used to calculate TLS base). + For multi-threaded programs: TLS_base = pthread_addr + pthread_size. + This is the size of the pthread structure on 32-bit AIX. */ +#define PTHREAD_SIZE_32 0x290 + /* The saved LR (v_save_lr) is at offset +8 from VAPI CB base for both 32-bit and 64-bit. The VAPI control block structure: +0: version/magic (0x00110000) diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c index 6a3c05e2cd8..b1a234f3cde 100644 --- a/gdb/rs6000-tdep.c +++ b/gdb/rs6000-tdep.c @@ -3595,16 +3601,116 @@ rs6000_frame_cache (const frame_info_ptr &this_frame, void **this_cache) because VAPI glue is frameless and doesn't save LR normally. */ CORE_ADDR pc_low = pc & 0xFFFF; bool pc_in_vapi = ((wordsize == 8 && pc_low >= vapi_addr_64 && pc_low < vapi_glue_addr_high) || - (wordsize == 4 && pc_low >= vapi_glue_addr_low)); + (wordsize == 4 && pc_low >= vapi_glue_addr_low && pc_low < vapi_glue_addr_high)); + + //gdb_printf (gdb_stdlog, + // "rs6000_frame_cache: PC=0x%s, pc_low=0x%x, wordsize=%d, pc_in_vapi=%d, adjust_fn=%p\n", + // phex_nz (pc, wordsize), (unsigned int)pc_low, wordsize, pc_in_vapi, + // (void*)tdep->ppc_adjust_trampoline_lr); if (pc_in_vapi && tdep->ppc_adjust_trampoline_lr != nullptr) { - /* PC is in VAPI glue - mark as frameless and set LR to read from VAPI CB */ + /* PC is in VAPI glue - this is a frameless function. + The LR register contains garbage; we need to read the real LR + from the VAPI control block in TLS. */ fdata.frameless = 1; - fdata.lr_offset = 0; + /* Set lr_offset to non-zero to prevent the frameless check at line 3727 + from trying to read LR from the frame (which would fail). We've already + stored the correct LR in the cache. */ + fdata.lr_offset = -1; //gdb_printf (gdb_stdlog, - // "rs6000_frame_cache: PC in VAPI glue, using VAPI control block for LR\n"); + // "rs6000_frame_cache: PC in VAPI glue, reading LR from VAPI CB\n"); + + /* Read LR from VAPI control block using current thread's regcache */ + try + { + regcache *regcache = get_thread_regcache (inferior_thread ()); + CORE_ADDR tls_base = 0; + CORE_ADDR vapi_lr_addr = 0; + + /* For 64-bit, use r13 register */ + if (wordsize == 8) + { + int r13_regnum = tdep->ppc_gp0_regnum + 13; + ULONGEST r13_val; + if (regcache_cooked_read_unsigned (regcache, r13_regnum, &r13_val) + == REG_VALID) + { + tls_base = r13_val; + if (tls_base != 0) + vapi_lr_addr = tls_base - VAPI_CB_OFFSET_64 + VAPI_SAVED_LR_OFFSET; + } + } + /* For 32-bit, check if pthread-enabled first */ + else + { + bool is_pthread = aix_thread_is_pthread_enabled (); + + if (is_pthread) + { + /* Multi-threaded program - try to get pthread address */ + CORE_ADDR pthread_addr = aix_thread_get_pthread_addr (); + if (pthread_addr != 0) + { + tls_base = pthread_addr + PTHREAD_SIZE_32; + vapi_lr_addr = tls_base + VAPI_SAVED_LR_OFFSET; + + //gdb_printf (gdb_stdlog, + // "rs6000_frame_cache: Multi-threaded 32-bit: " + // "pthread_addr=0x%s, TLS_base=0x%s\n", + // phex_nz (pthread_addr, wordsize), + // phex_nz (tls_base, wordsize)); + } + else + { + /* Pthread-enabled but can't get pthread_addr yet (thread not synced). + Skip VAPI unwinding for now - let normal unwinding handle it. */ + //gdb_printf (gdb_stdlog, + // "rs6000_frame_cache: Multi-threaded but pthread_addr=0 " + // "(thread not synced), skipping VAPI unwinding\n"); + vapi_lr_addr = 0; + } + } + else + { + /* Non-threaded program - use fixed SPRG3 address */ + tls_base = VAPI_CB_ADDR_32_NOTHREADS; + vapi_lr_addr = tls_base + VAPI_SAVED_LR_OFFSET; + + //gdb_printf (gdb_stdlog, + // "rs6000_frame_cache: Non-threaded 32-bit: " + // "TLS_base=0x%s\n", + // phex_nz (tls_base, wordsize)); + } + } + + /* Read the LR value from VAPI control block */ + if (vapi_lr_addr != 0) + { + gdb_byte vapi_buf[8]; + if (target_read_memory (vapi_lr_addr, vapi_buf, wordsize) == 0) + { + CORE_ADDR vapi_lr = extract_unsigned_integer (vapi_buf, + wordsize, + byte_order); + + //gdb_printf (gdb_stdlog, + // "rs6000_frame_cache: Read LR=0x%s from VAPI CB at 0x%s\n", + // phex_nz (vapi_lr, wordsize), + // phex_nz (vapi_lr_addr, wordsize)); + + /* Store the LR value in the cache */ + cache->saved_regs[tdep->ppc_lr_regnum].set_value (vapi_lr); + } + } + } + catch (const gdb_exception &ex) + { + //gdb_printf (gdb_stdlog, + // "rs6000_frame_cache: Exception reading VAPI LR: %s\n", + // ex.what ()); + } } else { -- 2.51.2 --- ./gdb/rs6000-tdep.c_orig 2026-06-18 12:52:54.414453381 -0500 +++ ./gdb/rs6000-tdep.c 2026-06-18 12:54:23.532370560 -0500 @@ -3627,8 +3627,15 @@ int wordsize = tdep->wordsize; CORE_ADDR func = 0, pc = 0; + //gdb_printf (gdb_stdlog, "rs6000_frame_cache: ENTRY, this_cache=%p, *this_cache=%p\n", + // (void*)this_cache, (void*)(*this_cache)); + + if ((*this_cache) != NULL) - return (struct rs6000_frame_cache *) (*this_cache); + { + //gdb_printf (gdb_stdlog, "rs6000_frame_cache: Returning cached frame\n"); + return (struct rs6000_frame_cache *) (*this_cache); + } cache = FRAME_OBSTACK_ZALLOC (struct rs6000_frame_cache); (*this_cache) = cache; cache->pc = 0;