libgcc: Add a backchain fallback to _Unwind_Backtrace() on PowerPC
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, this is done twice: for __powerpc64__ and for !__powerpc64__. (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. gcc/testsuite/ChangeLog: * gcc.target/powerpc/unwind-backchain.c: New test.
This commit is contained in:
parent
b47490c572
commit
b7561b5d24
24
gcc/testsuite/gcc.target/powerpc/unwind-backchain.c
Normal file
24
gcc/testsuite/gcc.target/powerpc/unwind-backchain.c
Normal file
@ -0,0 +1,24 @@
|
||||
/* -linux* targets have a fallback for the absence of unwind tables, thus are
|
||||
the only ones we can guarantee backtrace returns all addresses. */
|
||||
/* { dg-do run { target { *-*-linux* } } } */
|
||||
/* { dg-options "-fno-asynchronous-unwind-tables" } */
|
||||
|
||||
#include <execinfo.h>
|
||||
|
||||
void
|
||||
test_backtrace()
|
||||
{
|
||||
int addresses;
|
||||
void *buffer[10];
|
||||
|
||||
addresses = backtrace(buffer, 10);
|
||||
if(addresses != 4)
|
||||
__builtin_abort();
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
test_backtrace();
|
||||
return 0;
|
||||
}
|
@ -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,73 @@ frob_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs ATT
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define MD_BACKCHAIN_FALLBACK ppc_backchain_fallback
|
||||
|
||||
struct trace_arg
|
||||
{
|
||||
/* Stores the list of addresses. */
|
||||
void **array;
|
||||
struct unwind_link *unwind_link;
|
||||
_Unwind_Word cfa;
|
||||
/* Number of addresses currently stored. */
|
||||
int count;
|
||||
/* Maximum number of addresses. */
|
||||
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 -> | previous frame--------> | previous frame--->... --> NULL
|
||||
| | | |
|
||||
| cr save | | cr save |
|
||||
| | | |
|
||||
| (unused) | | lr save |
|
||||
+----------------+ +-----------------+
|
||||
|
||||
The CR save is only present on 64-bit ABIs.
|
||||
*/
|
||||
struct frame_layout
|
||||
{
|
||||
struct frame_layout *backchain;
|
||||
#ifdef __powerpc64__
|
||||
long int cr_save;
|
||||
#endif
|
||||
void *lr_save;
|
||||
};
|
||||
|
||||
|
||||
void ppc_backchain_fallback (struct _Unwind_Context *context, void *a)
|
||||
{
|
||||
struct frame_layout *current;
|
||||
struct trace_arg *arg = a;
|
||||
int count;
|
||||
|
||||
/* Get the last address computed and start with the next. */
|
||||
current = context->cfa;
|
||||
current = current->backchain;
|
||||
|
||||
for (count = arg->count; current != NULL; current = current->backchain)
|
||||
{
|
||||
arg->array[count] = current->lr_save;
|
||||
|
||||
/* Check if the symbol is the signal trampoline and get the interrupted
|
||||
symbol address from the trampoline saved area. */
|
||||
context->ra = current->lr_save;
|
||||
if (current->lr_save && get_regs (context))
|
||||
{
|
||||
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];
|
||||
}
|
||||
if (count++ >= arg->size)
|
||||
break;
|
||||
}
|
||||
|
||||
arg->count = count-1;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user