diff options
Diffstat (limited to 'sysdeps')
-rw-r--r-- | sysdeps/powerpc/powerpc32/backtrace.c | 66 | ||||
-rw-r--r-- | sysdeps/powerpc/powerpc64/backtrace.c | 36 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/powerpc/bits/libc-vdso.h | 7 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/powerpc/init-first.c | 16 |
4 files changed, 123 insertions, 2 deletions
diff --git a/sysdeps/powerpc/powerpc32/backtrace.c b/sysdeps/powerpc/powerpc32/backtrace.c index b4b11dd..8d413e6 100644 --- a/sysdeps/powerpc/powerpc32/backtrace.c +++ b/sysdeps/powerpc/powerpc32/backtrace.c @@ -18,6 +18,9 @@ #include <execinfo.h> #include <stddef.h> +#include <string.h> +#include <signal.h> +#include <bits/libc-vdso.h> /* 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 @@ -35,6 +38,46 @@ struct layout void *return_address; }; +#define SIGNAL_FRAMESIZE 64 + +/* Since the signal handler is just like any other function it needs to + save/restore its LR and it will save it into callers stack frame. + Since a signal handler doesn't have a caller, the kernel creates a + dummy frame to make it look like it has a caller. */ +struct signal_frame_32 { + char dummy[SIGNAL_FRAMESIZE]; + struct sigcontext sctx; + mcontext_t mctx; + /* We don't care about the rest, since IP value is at 'mctx' field. */ +}; + +static inline int +is_sigtramp_address (unsigned int nip) +{ +#ifdef SHARED + if (nip == (unsigned int)__vdso_sigtramp32) + return 1; +#endif + return 0; +} + +struct rt_signal_frame_32 { + char dummy[SIGNAL_FRAMESIZE + 16]; + siginfo_t info; + struct ucontext uc; + /* We don't care about the rest, since IP value is at 'uc' field. */ +}; + +static inline int +is_sigtramp_address_rt (unsigned int nip) +{ +#ifdef SHARED + if (nip == (unsigned int)__vdso_sigtramp_rt32) + return 1; +#endif + return 0; +} + int __backtrace (void **array, int size) { @@ -50,7 +93,28 @@ __backtrace (void **array, int size) for ( count = 0; current != NULL && count < size; current = current->next, count++) - array[count] = current->return_address; + { + gregset_t *gregset = NULL; + + array[count] = current->return_address; + + /* Check if the symbol is the signal trampoline and get the interrupted + * symbol address from the trampoline saved area. */ + if (is_sigtramp_address ((unsigned int)current->return_address)) + { + struct signal_frame_32 *sigframe = + (struct signal_frame_32*) current; + gregset = &sigframe->mctx.gregs; + } + else if (is_sigtramp_address_rt ((unsigned int)current->return_address)) + { + struct rt_signal_frame_32 *sigframe = + (struct rt_signal_frame_32*) current; + gregset = &sigframe->uc.uc_mcontext.uc_regs->gregs; + } + if (gregset) + array[++count] = (void*)((*gregset)[PT_NIP]); + } /* It's possible the second-last stack frame can't return (that is, it's __libc_start_main), in which case diff --git a/sysdeps/powerpc/powerpc64/backtrace.c b/sysdeps/powerpc/powerpc64/backtrace.c index 2d3e051..9b9a9f1 100644 --- a/sysdeps/powerpc/powerpc64/backtrace.c +++ b/sysdeps/powerpc/powerpc64/backtrace.c @@ -18,6 +18,9 @@ #include <execinfo.h> #include <stddef.h> +#include <string.h> +#include <signal.h> +#include <bits/libc-vdso.h> /* 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 @@ -38,6 +41,27 @@ struct layout void *return_address; }; +/* Since the signal handler is just like any other function it needs to + save/restore its LR and it will save it into callers stack frame. + Since a signal handler doesn't have a caller, the kernel creates a + dummy frame to make it look like it has a caller. */ +struct signal_frame_64 { +#define SIGNAL_FRAMESIZE 128 + char dummy[SIGNAL_FRAMESIZE]; + struct ucontext uc; + /* We don't care about the rest, since the IP value is at 'uc' field. */ +}; + +static inline int +is_sigtramp_address (unsigned long nip) +{ +#ifdef SHARED + if (nip == (unsigned long)__vdso_sigtramp_rt64) + return 1; +#endif + return 0; +} + int __backtrace (void **array, int size) { @@ -53,7 +77,17 @@ __backtrace (void **array, int size) for ( count = 0; current != NULL && count < size; current = current->next, count++) - array[count] = current->return_address; + { + array[count] = current->return_address; + + /* Check if the symbol is the signal trampoline and get the interrupted + * symbol address from the trampoline saved area. */ + if (is_sigtramp_address ((unsigned long)current->return_address)) + { + struct signal_frame_64 *sigframe = (struct signal_frame_64*) current; + array[++count] = (void*)sigframe->uc.uc_mcontext.gp_regs[PT_NIP]; + } + } /* It's possible the second-last stack frame can't return (that is, it's __libc_start_main), in which case diff --git a/sysdeps/unix/sysv/linux/powerpc/bits/libc-vdso.h b/sysdeps/unix/sysv/linux/powerpc/bits/libc-vdso.h index 8b195db..ba54de4 100644 --- a/sysdeps/unix/sysv/linux/powerpc/bits/libc-vdso.h +++ b/sysdeps/unix/sysv/linux/powerpc/bits/libc-vdso.h @@ -34,6 +34,13 @@ extern void *__vdso_getcpu; extern void *__vdso_time; +#if defined(__PPC64__) || defined(__powerpc64__) +extern void *__vdso_sigtramp_rt64; +#else +extern void *__vdso_sigtramp32; +extern void *__vdso_sigtramp_rt32; +#endif + /* This macro is needed for PPC64 to return a skeleton OPD entry of a vDSO symbol. This works because _dl_vdso_vsym always return the function address, and no vDSO symbols use the TOC or chain pointers from the OPD diff --git a/sysdeps/unix/sysv/linux/powerpc/init-first.c b/sysdeps/unix/sysv/linux/powerpc/init-first.c index f6f05f0..061715f 100644 --- a/sysdeps/unix/sysv/linux/powerpc/init-first.c +++ b/sysdeps/unix/sysv/linux/powerpc/init-first.c @@ -29,6 +29,12 @@ void *__vdso_clock_getres; void *__vdso_get_tbfreq; void *__vdso_getcpu; void *__vdso_time; +#if defined(__PPC64__) || defined(__powerpc64__) +void *__vdso_sigtramp_rt64; +#else +void *__vdso_sigtramp32; +void *__vdso_sigtramp_rt32; +#endif static inline void _libc_vdso_platform_setup (void) @@ -46,6 +52,16 @@ _libc_vdso_platform_setup (void) __vdso_getcpu = _dl_vdso_vsym ("__kernel_getcpu", &linux2615); __vdso_time = _dl_vdso_vsym ("__kernel_time", &linux2615); + + /* PPC64 uses only one signal trampoline symbol, while PPC32 will use + two depending if SA_SIGINFO is used (__kernel_sigtramp_rt32) or not + (__kernel_sigtramp32). */ +#if defined(__PPC64__) || defined(__powerpc64__) + __vdso_sigtramp_rt64 = _dl_vdso_vsym ("__kernel_sigtramp_rt64", &linux2615); +#else + __vdso_sigtramp32 = _dl_vdso_vsym ("__kernel_sigtramp32", &linux2615); + __vdso_sigtramp_rt32 = _dl_vdso_vsym ("__kernel_sigtramp_rt32", &linux2615); +#endif } # define VDSO_SETUP _libc_vdso_platform_setup |