From e18a8d33bd2cebce20e1609a2a69b6b22ef38b5a Mon Sep 17 00:00:00 2001
From: Aditya Kamath <Aditya.Kamath1@ibm.com>
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;
