This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PowerPC] Fix eh unwind segfault
- From: Alan Modra <amodra at bigpond dot net dot au>
- To: gcc-patches at gcc dot gnu dot org
- Date: Tue, 31 Aug 2004 14:30:14 +0930
- Subject: [PowerPC] Fix eh unwind segfault
This fixes the problem of segfaults on powerpc*-linux during unwinding,
as described in http://gcc.gnu.org/ml/gcc/2004-08/msg01437.html.
Regression tested powerpc64-linux, with powerpc-linux regression test
still in progress.
Introducing a signal handler into the unwinder is somewhat tricky. I've
used direct syscalls to avoid the problem mentioned at the end of
libjava/include/i386-signal.h. We also need to handle the possibility
of a user signal handler existing before we enter the unwinder. So our
handler needs to call the user handler if we determine that a segv
wasn't caused by the unwinder. I think the code I have should work, but
would appreciate someone who knows Linux signal handlers well reviewing
my "unwind_segv" handler. As I state in a comment, a user segfault
handler that reinstalls itself will lose our handler. I suppose it
would be possible to reinstall our handler again after we call the user
one.. Another complication is that the unwind library functions need
to be thread safe, so we can't use static variables. That's why I use
a nested function for the signal handler.
The only other minor trickery in this patch is my use of subsections to
emit the table of addresses of instructions that might segfault. Using
subsections allows the label and terminating zero to be placed correctly
in the face of -funit-at-a-time reordering the way code is emitted.
* config/rs6000/linux64.h: Move unwind macros to unwind-support.h
(MD_UNWIND_SUPPORT): Define.
* config/rs6000/linux.h: Likewise.
* config/rs6000/unwind-support.h: New file.
(SYSCALL4): New define.
(struct kernel_sigaction): New.
(gcc_unwind_ex_table): New.
(unwind_segv): New function.
(MD_START_UNWINDING, MD_FINISH_UNWINDING, READ_WORD): New defines.
(MD_FROB_UPDATE_CONTEXT): Use READ_WORD.
(MD_FALLBACK_FRAME_STATE_FOR): Likewise. Fix a warning.
* unwind-dw2.c (MD_UNWIND_SUPPORT): Include.
* unwind.inc (MD_START_UNWINDING, MD_FINISH_UNWINDING): Default.
(_Unwind_RaiseException): Invoke MD_START_UNWINDING and
MD_FINISH_UNWINDING. Rearrange function exit so MD_START_UNWINDING
can introduce a lexical block.
(_Unwind_ForcedUnwind, _Unwind_Resume): Likewise.
(_Unwind_Resume_or_Rethrow, _Unwind_Backtrace): Likewise.
OK mainline and 3.4.3?
diff -urp -xCVS -x'*~' gcc-virgin/gcc/config/rs6000/linux.h gcc-current/gcc/config/rs6000/linux.h
--- gcc-virgin/gcc/config/rs6000/linux.h 2004-05-27 16:55:20.000000000 +0930
+++ gcc-current/gcc/config/rs6000/linux.h 2004-08-30 22:08:16.572721676 +0930
@@ -98,88 +98,6 @@
#define TARGET_HAS_F_SETLKW
-/* Do code reading to identify a signal frame, and set the frame
- state data appropriately. See unwind-dw2.c for the structs. */
-
-#ifdef IN_LIBGCC2
-#include <signal.h>
-
-/* During the 2.5 kernel series the kernel ucontext was changed, but
- the new layout is compatible with the old one, so we just define
- and use the old one here for simplicity and compatibility. */
-
-struct kernel_old_ucontext {
- unsigned long uc_flags;
- struct ucontext *uc_link;
- stack_t uc_stack;
- struct sigcontext_struct uc_mcontext;
- sigset_t uc_sigmask;
-};
-
-enum { SIGNAL_FRAMESIZE = 64 };
-#endif
-
-#define MD_FALLBACK_FRAME_STATE_FOR(CONTEXT, FS, SUCCESS) \
- do { \
- unsigned char *pc_ = (CONTEXT)->ra; \
- struct sigcontext *sc_; \
- long new_cfa_; \
- int i_; \
- \
- /* li r0, 0x7777; sc (sigreturn old) */ \
- /* li r0, 0x0077; sc (sigreturn new) */ \
- /* li r0, 0x6666; sc (rt_sigreturn old) */ \
- /* li r0, 0x00AC; sc (rt_sigreturn new) */ \
- if (*(unsigned int *) (pc_+4) != 0x44000002) \
- break; \
- if (*(unsigned int *) (pc_+0) == 0x38007777 \
- || *(unsigned int *) (pc_+0) == 0x38000077) \
- { \
- struct sigframe { \
- char gap[SIGNAL_FRAMESIZE]; \
- struct sigcontext sigctx; \
- } *rt_ = (CONTEXT)->cfa; \
- sc_ = &rt_->sigctx; \
- } \
- else if (*(unsigned int *) (pc_+0) == 0x38006666 \
- || *(unsigned int *) (pc_+0) == 0x380000AC) \
- { \
- struct rt_sigframe { \
- char gap[SIGNAL_FRAMESIZE]; \
- unsigned long _unused[2]; \
- struct siginfo *pinfo; \
- void *puc; \
- struct siginfo info; \
- struct kernel_old_ucontext uc; \
- } *rt_ = (CONTEXT)->cfa; \
- sc_ = &rt_->uc.uc_mcontext; \
- } \
- else \
- break; \
- \
- new_cfa_ = sc_->regs->gpr[STACK_POINTER_REGNUM]; \
- (FS)->cfa_how = CFA_REG_OFFSET; \
- (FS)->cfa_reg = STACK_POINTER_REGNUM; \
- (FS)->cfa_offset = new_cfa_ - (long) (CONTEXT)->cfa; \
- \
- for (i_ = 0; i_ < 32; i_++) \
- if (i_ != STACK_POINTER_REGNUM) \
- { \
- (FS)->regs.reg[i_].how = REG_SAVED_OFFSET; \
- (FS)->regs.reg[i_].loc.offset \
- = (long)&(sc_->regs->gpr[i_]) - new_cfa_; \
- } \
- \
- (FS)->regs.reg[LINK_REGISTER_REGNUM].how = REG_SAVED_OFFSET; \
- (FS)->regs.reg[LINK_REGISTER_REGNUM].loc.offset \
- = (long)&(sc_->regs->link) - new_cfa_; \
- \
- (FS)->regs.reg[CR0_REGNO].how = REG_SAVED_OFFSET; \
- (FS)->regs.reg[CR0_REGNO].loc.offset \
- = (long)&(sc_->regs->nip) - new_cfa_; \
- (FS)->retaddr_column = CR0_REGNO; \
- goto SUCCESS; \
- } while (0)
-
+#define MD_UNWIND_SUPPORT "config/rs6000/unwind-support.h"
#define OS_MISSING_POWERPC64 1
diff -urp -xCVS -x'*~' gcc-virgin/gcc/config/rs6000/linux64.h gcc-current/gcc/config/rs6000/linux64.h
--- gcc-virgin/gcc/config/rs6000/linux64.h 2004-08-29 18:51:39.452357085 +0930
+++ gcc-current/gcc/config/rs6000/linux64.h 2004-08-30 22:07:34.067498779 +0930
@@ -563,175 +563,6 @@ while (0)
#define USE_LD_AS_NEEDED 1
#endif
-/* Do code reading to identify a signal frame, and set the frame
- state data appropriately. See unwind-dw2.c for the structs. */
-
-#ifdef IN_LIBGCC2
-#include <signal.h>
-#ifdef __powerpc64__
-#include <sys/ucontext.h>
-
-enum { SIGNAL_FRAMESIZE = 128 };
-
-#else
-
-/* During the 2.5 kernel series the kernel ucontext was changed, but
- the new layout is compatible with the old one, so we just define
- and use the old one here for simplicity and compatibility. */
-
-struct kernel_old_ucontext {
- unsigned long uc_flags;
- struct ucontext *uc_link;
- stack_t uc_stack;
- struct sigcontext_struct uc_mcontext;
- sigset_t uc_sigmask;
-};
-enum { SIGNAL_FRAMESIZE = 64 };
-#endif
-
-#endif
-
-#ifdef __powerpc64__
-
-/* If the current unwind info (FS) does not contain explicit info
- saving R2, then we have to do a minor amount of code reading to
- figure out if it was saved. The big problem here is that the
- code that does the save/restore is generated by the linker, so
- we have no good way to determine at compile time what to do. */
-
-#define MD_FROB_UPDATE_CONTEXT(CTX, FS) \
- do { \
- if ((FS)->regs.reg[2].how == REG_UNSAVED) \
- { \
- unsigned int *insn \
- = (unsigned int *) \
- _Unwind_GetGR ((CTX), LINK_REGISTER_REGNUM); \
- if (*insn == 0xE8410028) \
- _Unwind_SetGRPtr ((CTX), 2, (CTX)->cfa + 40); \
- } \
- } while (0)
-
-#define MD_FALLBACK_FRAME_STATE_FOR(CONTEXT, FS, SUCCESS) \
- do { \
- unsigned char *pc_ = (CONTEXT)->ra; \
- struct sigcontext *sc_; \
- long new_cfa_; \
- int i_; \
- \
- /* addi r1, r1, 128; li r0, 0x0077; sc (sigreturn) */ \
- /* addi r1, r1, 128; li r0, 0x00AC; sc (rt_sigreturn) */ \
- if (*(unsigned int *) (pc_+0) != 0x38210000 + SIGNAL_FRAMESIZE \
- || *(unsigned int *) (pc_+8) != 0x44000002) \
- break; \
- if (*(unsigned int *) (pc_+4) == 0x38000077) \
- { \
- struct sigframe { \
- char gap[SIGNAL_FRAMESIZE]; \
- struct sigcontext sigctx; \
- } *rt_ = (CONTEXT)->cfa; \
- sc_ = &rt_->sigctx; \
- } \
- else if (*(unsigned int *) (pc_+4) == 0x380000AC) \
- { \
- struct rt_sigframe { \
- int tramp[6]; \
- struct siginfo *pinfo; \
- struct ucontext *puc; \
- } *rt_ = (struct rt_sigframe *) pc_; \
- sc_ = &rt_->puc->uc_mcontext; \
- } \
- else \
- break; \
- \
- new_cfa_ = sc_->regs->gpr[STACK_POINTER_REGNUM]; \
- (FS)->cfa_how = CFA_REG_OFFSET; \
- (FS)->cfa_reg = STACK_POINTER_REGNUM; \
- (FS)->cfa_offset = new_cfa_ - (long) (CONTEXT)->cfa; \
- \
- for (i_ = 0; i_ < 32; i_++) \
- if (i_ != STACK_POINTER_REGNUM) \
- { \
- (FS)->regs.reg[i_].how = REG_SAVED_OFFSET; \
- (FS)->regs.reg[i_].loc.offset \
- = (long)&(sc_->regs->gpr[i_]) - new_cfa_; \
- } \
- \
- (FS)->regs.reg[LINK_REGISTER_REGNUM].how = REG_SAVED_OFFSET; \
- (FS)->regs.reg[LINK_REGISTER_REGNUM].loc.offset \
- = (long)&(sc_->regs->link) - new_cfa_; \
- \
- (FS)->regs.reg[ARG_POINTER_REGNUM].how = REG_SAVED_OFFSET; \
- (FS)->regs.reg[ARG_POINTER_REGNUM].loc.offset \
- = (long)&(sc_->regs->nip) - new_cfa_; \
- (FS)->retaddr_column = ARG_POINTER_REGNUM; \
- goto SUCCESS; \
- } while (0)
-
-#else
-
-#define MD_FALLBACK_FRAME_STATE_FOR(CONTEXT, FS, SUCCESS) \
- do { \
- unsigned char *pc_ = (CONTEXT)->ra; \
- struct sigcontext *sc_; \
- long new_cfa_; \
- int i_; \
- \
- /* li r0, 0x7777; sc (sigreturn old) */ \
- /* li r0, 0x0077; sc (sigreturn new) */ \
- /* li r0, 0x6666; sc (rt_sigreturn old) */ \
- /* li r0, 0x00AC; sc (rt_sigreturn new) */ \
- if (*(unsigned int *) (pc_+4) != 0x44000002) \
- break; \
- if (*(unsigned int *) (pc_+0) == 0x38007777 \
- || *(unsigned int *) (pc_+0) == 0x38000077) \
- { \
- struct sigframe { \
- char gap[SIGNAL_FRAMESIZE]; \
- struct sigcontext sigctx; \
- } *rt_ = (CONTEXT)->cfa; \
- sc_ = &rt_->sigctx; \
- } \
- else if (*(unsigned int *) (pc_+0) == 0x38006666 \
- || *(unsigned int *) (pc_+0) == 0x380000AC) \
- { \
- struct rt_sigframe { \
- char gap[SIGNAL_FRAMESIZE]; \
- unsigned long _unused[2]; \
- struct siginfo *pinfo; \
- void *puc; \
- struct siginfo info; \
- struct kernel_old_ucontext uc; \
- } *rt_ = (CONTEXT)->cfa; \
- sc_ = &rt_->uc.uc_mcontext; \
- } \
- else \
- break; \
- \
- new_cfa_ = sc_->regs->gpr[STACK_POINTER_REGNUM]; \
- (FS)->cfa_how = CFA_REG_OFFSET; \
- (FS)->cfa_reg = STACK_POINTER_REGNUM; \
- (FS)->cfa_offset = new_cfa_ - (long) (CONTEXT)->cfa; \
- \
- for (i_ = 0; i_ < 32; i_++) \
- if (i_ != STACK_POINTER_REGNUM) \
- { \
- (FS)->regs.reg[i_].how = REG_SAVED_OFFSET; \
- (FS)->regs.reg[i_].loc.offset \
- = (long)&(sc_->regs->gpr[i_]) - new_cfa_; \
- } \
- \
- (FS)->regs.reg[LINK_REGISTER_REGNUM].how = REG_SAVED_OFFSET; \
- (FS)->regs.reg[LINK_REGISTER_REGNUM].loc.offset \
- = (long)&(sc_->regs->link) - new_cfa_; \
- \
- (FS)->regs.reg[CR0_REGNO].how = REG_SAVED_OFFSET; \
- (FS)->regs.reg[CR0_REGNO].loc.offset \
- = (long)&(sc_->regs->nip) - new_cfa_; \
- (FS)->retaddr_column = CR0_REGNO; \
- goto SUCCESS; \
- } while (0)
-
-#endif
-
+#define MD_UNWIND_SUPPORT "config/rs6000/unwind-support.h"
#define OS_MISSING_POWERPC64 !TARGET_64BIT
diff -urpN -xCVS -x'*~' gcc-virgin/gcc/config/rs6000/unwind-support.h gcc-current/gcc/config/rs6000/unwind-support.h
--- gcc-virgin/gcc/config/rs6000/unwind-support.h 1970-01-01 09:30:00.000000000 +0930
+++ gcc-current/gcc/config/rs6000/unwind-support.h 2004-08-31 09:15:28.932318238 +0930
@@ -0,0 +1,357 @@
+#include <signal.h>
+#include <sys/syscall.h>
+
+/* A syscall that aborts on error. */
+#define SYSCALL4(num, a1, a2, a3, a4) \
+ { \
+ register long int r0 __asm__ ("r0"); \
+ register long int r3 __asm__ ("r3"); \
+ register long int r4 __asm__ ("r4"); \
+ register long int r5 __asm__ ("r5"); \
+ register long int r6 __asm__ ("r6"); \
+ register long int r7 __asm__ ("r7"); \
+ register long int r8 __asm__ ("r8"); \
+ register long int r9 __asm__ ("r9"); \
+ register long int r10 __asm__ ("r10"); \
+ register long int r11 __asm__ ("r11"); \
+ register long int r12 __asm__ ("r12"); \
+ r0 = num; \
+ r3 = (long) a1; \
+ r4 = (long) a2; \
+ r5 = (long) a3; \
+ r6 = (long) a4; \
+ __asm__ __volatile__ \
+ ("sc\n\t" \
+ "bca 12,3,0" \
+ : "=&r" (r0), \
+ "=&r" (r3), "=&r" (r4), "=&r" (r5), "=&r" (r6), "=&r" (r7), \
+ "=&r" (r8), "=&r" (r9), "=&r" (r10), "=&r" (r11), "=&r" (r12) \
+ : "0" (r0), "1" (r3), "2" (r4), "3" (r5), "4" (r6)); \
+ }
+
+#ifdef __powerpc64__
+
+#include <sys/ucontext.h>
+
+enum { SIGNAL_FRAMESIZE = 128 };
+
+struct kernel_sigaction
+{
+ void (*handler) (int signum, struct siginfo *, struct ucontext *);
+ unsigned long flags;
+ void (*restorer) (void);
+ unsigned long mask;
+};
+
+/* Table of addresses that might segfault. Play tricks with assembler
+ subsections to ensure that the label comes first, and there is a
+ zero at the end. READ_WORD emits the addresses to subsection 1. */
+extern const unsigned long gcc_unwind_ex_table[];
+__asm__ (".pushsection .rodata.gcc_unwind_ex_table,\"a\",@progbits\n"
+ ".subsection 0\n\t"
+ "gcc_unwind_ex_table:\n\t"
+ ".subsection 2\n\t"
+ ".quad 0\n\t"
+ ".popsection");
+
+/* A SIGSEGV handler that skips over faulting instructions registered
+ in GCC_UNWIND_EX_TABLE. Passes the SIGSEGV on to a user handler
+ if one was installed prior to ours. Not all user SIGSEGV handlers
+ can be accommodated. For instance, a user SIGSEGV that reinstalls
+ itself will lose our handler. */
+
+static void unwind_segv (int signum,
+ struct siginfo *inf,
+ struct ucontext *uc,
+ struct kernel_sigaction *old_segv)
+{
+ const unsigned long *p;
+ for (p = gcc_unwind_ex_table; *p != 0; ++p)
+ {
+ if (*p + (unsigned long) p == uc->uc_mcontext.regs->nip)
+ {
+ uc->uc_mcontext.regs->nip += 4;
+ return;
+ }
+ }
+
+ if (old_segv->handler)
+ {
+ if (!(old_segv->flags & SA_SIGINFO))
+ /* Old style handlers have a "struct sigcontext *" second arg. */
+ inf = (struct siginfo *) &uc->uc_mcontext;
+ old_segv->handler (signum, inf, uc);
+ }
+ else
+ /* If the previous handler was SIG_DFL, set that up. The current
+ instruction will run once more on exit from this handler, at which
+ point the default handler will take over and coredump. */
+ SYSCALL4 (SYS_rt_sigaction, signum, old_segv, NULL, 8);
+}
+
+/* Set up target specific actions on entry to the unwinder.
+ On PowerPC, install a SIGSEGV handler to cope with the case where
+ unwinding up the stack hits a function in a shared lib that has
+ been dlclose'd. MD_START_UNWINDING has an opening brace, with
+ matching close brace in MD_FINISH_UNWINDING so that we can use
+ auto vars for thread safety. */
+
+#define MD_START_UNWINDING \
+ { \
+ struct kernel_sigaction segv, old_segv; \
+ void my_unwind_segv (int signum, \
+ struct siginfo *inf, struct ucontext *uc) \
+ { \
+ unwind_segv (signum, inf, uc, &old_segv); \
+ } \
+ memset (&segv, 0, sizeof (segv)); \
+ segv.handler = my_unwind_segv; \
+ segv.flags = SA_SIGINFO; \
+ SYSCALL4 (SYS_rt_sigaction, SIGSEGV, &segv, &old_segv, 8);
+
+#define MD_FINISH_UNWINDING \
+ SYSCALL4 (SYS_rt_sigaction, SIGSEGV, &old_segv, NULL, 8); \
+ }
+
+/* Read a 32-bit word, registering the address of the instruction
+ used in gcc_unwind_ex_table. */
+
+#define READ_WORD(dest, addr, off) \
+ __asm__ __volatile__ ("1: lwz %0,%3(%2)\n\t" \
+ ".pushsection .rodata.gcc_unwind_ex_table\n\t" \
+ ".subsection 1\n\t" \
+ ".quad 1b-.\n\t" \
+ ".popsection" \
+ : "=r" (dest) \
+ : "0" (-1), "b" (addr), "i" (off));
+
+/* If the current unwind info (FS) does not contain explicit info
+ saving R2, then we have to do a minor amount of code reading to
+ figure out if it was saved. The big problem here is that the
+ code that does the save/restore is generated by the linker, so
+ we have no good way to determine at compile time what to do. */
+
+#define MD_FROB_UPDATE_CONTEXT(CTX, FS) \
+ do { \
+ if ((FS)->regs.reg[2].how == REG_UNSAVED) \
+ { \
+ unsigned int *addr \
+ = (unsigned int *) \
+ _Unwind_GetGR ((CTX), LINK_REGISTER_REGNUM); \
+ unsigned int insn; \
+ READ_WORD (insn, addr, 0); \
+ if (insn == 0xE8410028) \
+ _Unwind_SetGRPtr ((CTX), 2, (CTX)->cfa + 40); \
+ } \
+ } while (0)
+
+/* Do code reading to identify a signal frame, and set the frame
+ state data appropriately. See unwind-dw2.c for the structs. */
+
+#define MD_FALLBACK_FRAME_STATE_FOR(CONTEXT, FS, SUCCESS) \
+ do { \
+ unsigned char *pc_ = (CONTEXT)->ra; \
+ struct sigcontext *sc_; \
+ long new_cfa_; \
+ int i_; \
+ unsigned int insn; \
+ \
+ /* addi r1, r1, 128; li r0, 0x0077; sc (sigreturn) */ \
+ /* addi r1, r1, 128; li r0, 0x00AC; sc (rt_sigreturn) */ \
+ READ_WORD (insn, pc_, 0); \
+ if (insn != 0x38210000 + SIGNAL_FRAMESIZE) \
+ break; \
+ READ_WORD (insn, pc_, 8); \
+ if (insn != 0x44000002) \
+ break; \
+ READ_WORD (insn, pc_, 4); \
+ if (insn == 0x38000077) \
+ { \
+ struct sigframe { \
+ char gap[SIGNAL_FRAMESIZE]; \
+ struct sigcontext sigctx; \
+ } *rt_ = (CONTEXT)->cfa; \
+ sc_ = &rt_->sigctx; \
+ } \
+ else if (insn == 0x380000AC) \
+ { \
+ struct rt_sigframe { \
+ int tramp[6]; \
+ struct siginfo *pinfo; \
+ struct ucontext *puc; \
+ } *rt_ = (struct rt_sigframe *) pc_; \
+ sc_ = (struct sigcontext *) &rt_->puc->uc_mcontext; \
+ } \
+ else \
+ break; \
+ \
+ new_cfa_ = sc_->regs->gpr[STACK_POINTER_REGNUM]; \
+ (FS)->cfa_how = CFA_REG_OFFSET; \
+ (FS)->cfa_reg = STACK_POINTER_REGNUM; \
+ (FS)->cfa_offset = new_cfa_ - (long) (CONTEXT)->cfa; \
+ \
+ for (i_ = 0; i_ < 32; i_++) \
+ if (i_ != STACK_POINTER_REGNUM) \
+ { \
+ (FS)->regs.reg[i_].how = REG_SAVED_OFFSET; \
+ (FS)->regs.reg[i_].loc.offset \
+ = (long)&(sc_->regs->gpr[i_]) - new_cfa_; \
+ } \
+ \
+ (FS)->regs.reg[LINK_REGISTER_REGNUM].how = REG_SAVED_OFFSET; \
+ (FS)->regs.reg[LINK_REGISTER_REGNUM].loc.offset \
+ = (long)&(sc_->regs->link) - new_cfa_; \
+ \
+ (FS)->regs.reg[ARG_POINTER_REGNUM].how = REG_SAVED_OFFSET; \
+ (FS)->regs.reg[ARG_POINTER_REGNUM].loc.offset \
+ = (long)&(sc_->regs->nip) - new_cfa_; \
+ (FS)->retaddr_column = ARG_POINTER_REGNUM; \
+ goto SUCCESS; \
+ } while (0)
+
+#else /* !__powerpc64__ */
+
+enum { SIGNAL_FRAMESIZE = 64 };
+
+/* During the 2.5 kernel series the kernel ucontext was changed, but
+ the new layout is compatible with the old one, so we just define
+ and use the old one here for simplicity and compatibility. */
+
+struct kernel_old_ucontext {
+ unsigned long uc_flags;
+ struct ucontext *uc_link;
+ stack_t uc_stack;
+ struct sigcontext_struct uc_mcontext;
+ sigset_t uc_sigmask;
+};
+
+struct kernel_sigaction
+{
+ void (*handler) (int signum, struct siginfo *, struct kernel_old_ucontext *);
+ unsigned long flags;
+ void (*restorer) (void);
+ unsigned long mask[2];
+};
+
+extern const unsigned long gcc_unwind_ex_table[];
+__asm__ (".pushsection .rodata.gcc_unwind_ex_table,\"a\",@progbits\n"
+ ".subsection 0\n\t"
+ "gcc_unwind_ex_table:\n\t"
+ ".subsection 2\n\t"
+ ".long 0\n\t"
+ ".popsection");
+
+static void unwind_segv (int signum,
+ struct siginfo *inf,
+ struct kernel_old_ucontext *uc,
+ struct kernel_sigaction *old_segv)
+{
+ const unsigned long *p;
+ for (p = gcc_unwind_ex_table; *p != 0; ++p)
+ {
+ if (*p + (unsigned long) p == uc->uc_mcontext.regs->nip)
+ {
+ uc->uc_mcontext.regs->nip += 4;
+ return;
+ }
+ }
+ if (old_segv->handler)
+ {
+ if (!(old_segv->flags & SA_SIGINFO))
+ inf = (struct siginfo *) &uc->uc_mcontext;
+ old_segv->handler (signum, inf, uc);
+ }
+ else
+ SYSCALL4 (SYS_rt_sigaction, signum, old_segv, NULL, 8);
+}
+
+#define MD_START_UNWINDING \
+ { \
+ struct kernel_sigaction segv, old_segv; \
+ void my_unwind_segv (int signum, struct siginfo *inf, \
+ struct kernel_old_ucontext *uc) \
+ { \
+ unwind_segv (signum, inf, uc, &old_segv); \
+ } \
+ memset (&segv, 0, sizeof (segv)); \
+ segv.handler = my_unwind_segv; \
+ segv.flags = SA_SIGINFO; \
+ SYSCALL4 (SYS_rt_sigaction, SIGSEGV, &segv, &old_segv, 8);
+
+#define MD_FINISH_UNWINDING \
+ SYSCALL4 (SYS_rt_sigaction, SIGSEGV, &old_segv, NULL, 8); \
+ }
+
+#define READ_WORD(dest, addr, off) \
+ __asm__ __volatile__ ("1: lwz %0,%3(%2)\n\t" \
+ ".pushsection .rodata.gcc_unwind_ex_table\n\t" \
+ ".subsection 1\n\t" \
+ ".long 1b-.\n\t" \
+ ".popsection" \
+ : "=r" (dest) \
+ : "0" (-1), "b" (addr), "i" (off));
+
+#define MD_FALLBACK_FRAME_STATE_FOR(CONTEXT, FS, SUCCESS) \
+ do { \
+ unsigned char *pc_ = (CONTEXT)->ra; \
+ struct sigcontext *sc_; \
+ long new_cfa_; \
+ int i_; \
+ unsigned int insn; \
+ \
+ /* li r0, 0x7777; sc (sigreturn old) */ \
+ /* li r0, 0x0077; sc (sigreturn new) */ \
+ /* li r0, 0x6666; sc (rt_sigreturn old) */ \
+ /* li r0, 0x00AC; sc (rt_sigreturn new) */ \
+ READ_WORD (insn, pc_, 4); \
+ if (insn != 0x44000002) \
+ break; \
+ READ_WORD (insn, pc_, 0); \
+ if (insn == 0x38007777 || insn == 0x38000077) \
+ { \
+ struct sigframe { \
+ char gap[SIGNAL_FRAMESIZE]; \
+ struct sigcontext sigctx; \
+ } *rt_ = (CONTEXT)->cfa; \
+ sc_ = &rt_->sigctx; \
+ } \
+ else if (insn == 0x38006666 || insn == 0x380000AC) \
+ { \
+ struct rt_sigframe { \
+ char gap[SIGNAL_FRAMESIZE]; \
+ unsigned long _unused[2]; \
+ struct siginfo *pinfo; \
+ void *puc; \
+ struct siginfo info; \
+ struct kernel_old_ucontext uc; \
+ } *rt_ = (CONTEXT)->cfa; \
+ sc_ = &rt_->uc.uc_mcontext; \
+ } \
+ else \
+ break; \
+ \
+ new_cfa_ = sc_->regs->gpr[STACK_POINTER_REGNUM]; \
+ (FS)->cfa_how = CFA_REG_OFFSET; \
+ (FS)->cfa_reg = STACK_POINTER_REGNUM; \
+ (FS)->cfa_offset = new_cfa_ - (long) (CONTEXT)->cfa; \
+ \
+ for (i_ = 0; i_ < 32; i_++) \
+ if (i_ != STACK_POINTER_REGNUM) \
+ { \
+ (FS)->regs.reg[i_].how = REG_SAVED_OFFSET; \
+ (FS)->regs.reg[i_].loc.offset \
+ = (long)&(sc_->regs->gpr[i_]) - new_cfa_; \
+ } \
+ \
+ (FS)->regs.reg[LINK_REGISTER_REGNUM].how = REG_SAVED_OFFSET; \
+ (FS)->regs.reg[LINK_REGISTER_REGNUM].loc.offset \
+ = (long)&(sc_->regs->link) - new_cfa_; \
+ \
+ (FS)->regs.reg[CR0_REGNO].how = REG_SAVED_OFFSET; \
+ (FS)->regs.reg[CR0_REGNO].loc.offset \
+ = (long)&(sc_->regs->nip) - new_cfa_; \
+ (FS)->retaddr_column = CR0_REGNO; \
+ goto SUCCESS; \
+ } while (0)
+
+#endif
--
Alan Modra
IBM OzLabs - Linux Technology Centre