This is followup to this RFC: 
https://gcc.gnu.org/pipermail/gcc-patches/2021-June/573954.html

---8<---

Without dwarf2 unwind tables available _Unwind_Backtrace() is not
able to return the full backtrace.
This patch adds a fallback function on powerpc to get the backtrace
by doing a backchain, this code was originally at glibc.

libgcc/ChangeLog:

        * config/rs6000/linux-unwind.h (struct rt_sigframe): Move it to
        outside of get_regs() in order to use it in another function.
        (struct trace_arg): New struct.
        (struct layout): New struct.
        (ppc_backchain_fallback): New function.
        * unwind.inc (_Unwind_Backtrace): Look for _URC_NORMAL_STOP
        code state and call MD_BACKCHAIN_FALLBACK.
---
 libgcc/config/rs6000/linux-unwind.h | 96 ++++++++++++++++++++++++-----
 libgcc/unwind.inc                   | 14 ++++-
 2 files changed, 94 insertions(+), 16 deletions(-)

diff --git a/libgcc/config/rs6000/linux-unwind.h 
b/libgcc/config/rs6000/linux-unwind.h
index acdc948f85d..af8fa01f450 100644
--- a/libgcc/config/rs6000/linux-unwind.h
+++ b/libgcc/config/rs6000/linux-unwind.h
@@ -94,6 +94,15 @@ struct gcc_ucontext
 
 enum { SIGNAL_FRAMESIZE = 128 };
 
+struct rt_sigframe {
+  char gap[SIGNAL_FRAMESIZE];
+  struct gcc_ucontext uc;
+  unsigned long pad[2];
+  int tramp[6];
+  void *pinfo;
+  struct gcc_ucontext *puc;
+};
+
 /* If PC is at a sigreturn trampoline, return a pointer to the
    regs.  Otherwise return NULL.  */
 
@@ -136,14 +145,7 @@ get_regs (struct _Unwind_Context *context)
 #endif
        {
          /* This works for 2.4.21 and later kernels.  */
-         struct rt_sigframe {
-           char gap[SIGNAL_FRAMESIZE];
-           struct gcc_ucontext uc;
-           unsigned long pad[2];
-           int tramp[6];
-           void *pinfo;
-           struct gcc_ucontext *puc;
-         } *frame = (struct rt_sigframe *) context->cfa;
+         struct rt_sigframe *frame = (struct rt_sigframe *) context->cfa;
          return frame->uc.regs;
        }
     }
@@ -154,6 +156,12 @@ get_regs (struct _Unwind_Context *context)
 
 enum { SIGNAL_FRAMESIZE = 64 };
 
+struct rt_sigframe {
+  char gap[SIGNAL_FRAMESIZE + 16];
+  char siginfo[128];
+  struct gcc_ucontext uc;
+};
+
 static struct gcc_regs *
 get_regs (struct _Unwind_Context *context)
 {
@@ -176,11 +184,7 @@ get_regs (struct _Unwind_Context *context)
     }
   else if (pc[0] == 0x38006666 || pc[0] == 0x380000AC)
     {
-      struct rt_sigframe {
-       char gap[SIGNAL_FRAMESIZE + 16];
-       char siginfo[128];
-       struct gcc_ucontext uc;
-      } *frame = (struct rt_sigframe *) context->cfa;
+      struct rt_sigframe *frame = (struct rt_sigframe *) context->cfa;
       return frame->uc.regs;
     }
   return NULL;
@@ -203,7 +207,7 @@ ppc_fallback_frame_state (struct _Unwind_Context *context,
   int i;
 
   if (regs == NULL)
-    return _URC_END_OF_STACK;
+    return _URC_NORMAL_STOP;
 
   new_cfa = regs->gpr[__LIBGCC_STACK_POINTER_REGNUM__];
   fs->regs.cfa_how = CFA_REG_OFFSET;
@@ -352,3 +356,67 @@ frob_update_context (struct _Unwind_Context *context, 
_Unwind_FrameState *fs ATT
     }
 #endif
 }
+
+#define MD_BACKCHAIN_FALLBACK ppc_backchain_fallback
+
+struct trace_arg
+{
+  void **array;
+  struct unwind_link *unwind_link;
+  _Unwind_Word cfa;
+  int cnt;
+  int size;
+};
+
+/* This is the stack layout we see with every stack frame.
+   Note that every routine is required by the ABI to lay out the stack
+   like this.
+
+           +----------------+        +-----------------+
+    %r1  -> | %r1 last frame--------> | %r1 last frame--->...  --> NULL
+           |                |        |                 |
+           | cr save        |        | cr save         |
+           |                |        |                 |
+           | (unused)       |        | return address  |
+           +----------------+        +-----------------+
+*/
+struct layout
+{
+  struct layout *next;
+#ifdef __powerpc64__
+  long int condition_register;
+#endif
+  void *return_address;
+};
+
+
+void ppc_backchain_fallback (struct _Unwind_Context *context, void *a)
+{
+  struct layout *current;
+  struct trace_arg *arg = a;
+  int count;
+
+  /* Get the last address computed and start with the next.  */
+  current = context->cfa;
+  current = current->next;
+
+  for (count = arg->cnt; current != NULL && count < arg->size;
+       current = current->next, count++)
+    {
+      arg->array[count] = current->return_address;
+
+      /* Check if the symbol is the signal trampoline and get the interrupted
+       * symbol address from the trampoline saved area.  */
+      context->ra = current->return_address;
+      if (current->return_address != NULL && get_regs(context) != NULL)
+       {
+         struct rt_sigframe *sigframe = (struct rt_sigframe*) current;
+         if (count + 1 == arg->size)
+           break;
+         arg->array[++count] = (void *) sigframe->uc.rsave.nip;
+         current =  (void *) sigframe->uc.rsave.gpr[1];
+       }
+    }
+
+  arg->cnt = count-1;
+}
diff --git a/libgcc/unwind.inc b/libgcc/unwind.inc
index aa48d104fd0..456a5ee682f 100644
--- a/libgcc/unwind.inc
+++ b/libgcc/unwind.inc
@@ -300,14 +300,24 @@ _Unwind_Backtrace(_Unwind_Trace_Fn trace, void * 
trace_argument)
 
       /* Set up fs to describe the FDE for the caller of context.  */
       code = uw_frame_state_for (&context, &fs);
-      if (code != _URC_NO_REASON && code != _URC_END_OF_STACK)
+      if (code != _URC_NO_REASON && code != _URC_END_OF_STACK
+          && code != _URC_NORMAL_STOP)
        return _URC_FATAL_PHASE1_ERROR;
 
       /* Call trace function.  */
       if ((*trace) (&context, trace_argument) != _URC_NO_REASON)
        return _URC_FATAL_PHASE1_ERROR;
 
-      /* We're done at end of stack.  */       
+#ifdef MD_BACKCHAIN_FALLBACK
+      /* Do a backchain if there is no DWARF data.  */
+      if (code == _URC_NORMAL_STOP)
+       {
+         MD_BACKCHAIN_FALLBACK(&context, trace_argument);
+         break;
+       }
+#endif
+
+      /* We're done at end of stack.  */
       if (code == _URC_END_OF_STACK)
        break;
 
-- 
2.31.1

Reply via email to