summaryrefslogtreecommitdiff
path: root/arm-riscos-gnueabi/recipes/patches/gccsdk/unixlib-unwind.p
diff options
context:
space:
mode:
Diffstat (limited to 'arm-riscos-gnueabi/recipes/patches/gccsdk/unixlib-unwind.p')
-rw-r--r--arm-riscos-gnueabi/recipes/patches/gccsdk/unixlib-unwind.p468
1 files changed, 468 insertions, 0 deletions
diff --git a/arm-riscos-gnueabi/recipes/patches/gccsdk/unixlib-unwind.p b/arm-riscos-gnueabi/recipes/patches/gccsdk/unixlib-unwind.p
new file mode 100644
index 0000000..9ff54c9
--- /dev/null
+++ b/arm-riscos-gnueabi/recipes/patches/gccsdk/unixlib-unwind.p
@@ -0,0 +1,468 @@
+Index: libunixlib/Makefile.am
+===================================================================
+--- libunixlib/Makefile.am (revision 7698)
++++ libunixlib/Makefile.am (working copy)
+@@ -33,7 +33,7 @@
+ # arguments can not be tested for NULL in UnixLib itself.
+ if ARM_EABI
+ AM_CFLAGS = -D__GNU_LIBRARY__ -DNO_LONG_DOUBLE -D_GNU_SOURCE=1 \
+- -D__UNIXLIB_NO_NONNULL -std=c99 $(LIBM_FLAGS)
++ -D__UNIXLIB_NO_NONNULL -std=c99 -mpoke-function-name -funwind-tables $(LIBM_FLAGS)
+ UNIXLIB_CHUNKED_STACK=0
+ else
+ AM_CFLAGS = -D__GNU_LIBRARY__ -DNO_LONG_DOUBLE -D_GNU_SOURCE=1 \
+Index: libunixlib/signal/_signal.s
+===================================================================
+--- libunixlib/signal/_signal.s (revision 7698)
++++ libunixlib/signal/_signal.s (working copy)
+@@ -352,8 +352,8 @@
+ CHGMODE a1, USR_Mode @ Back to USR mode now we have a stack
+
+ #ifdef __ARM_EABI__
+- STMFD sp!, {v1, v3}
+- ADD fp, sp, #4
++ ANDS v2, sp, #7 @ Align stack
++ SUBEQ sp, sp, #4
+ #else
+ ADR v4, __h_error + 4*3 @ Point at handler name for backtrace
+ STMFD sp!, {v1, v2, v3, v4} @ Setup an APCS-32 stack frame so we
+@@ -758,10 +758,9 @@
+ SWINE XOS_Byte @ This calls our escape handler
+
+ #ifdef __ARM_EABI__
+- LDR a3, [sp, #14*4 + 4] @ saved USR lr
+- LDR a1, [sp, #11*4 + 4] @ saved USR fp
+- STMFD sp!, {a1, a3} @ create signal frame
+- MOV fp, sp @ FIXME: check this with compiler output for similar function
++ MOV a1, sp @ a1 -> register save block
++ ANDS v2, sp, #7
++ SUBNE sp, sp, #4 @ align stack to 8 bytes
+ #else
+ @ Create an APCS-32 compilant signal stack frame
+ ADR a4, __h_cback + 4*3 @ point at handler name for backtrace
+@@ -796,11 +795,12 @@
+ STR a1, [a3, #GBL_EXECUTING_SIGNALHANDLER]
+
+ #ifdef __ARM_EABI__
+- ADD a1, sp, #8 @ Skip signal frame (fp, lr)
++ TEQ v2, #0
++ ADDNE a1, sp, #4 @ Undo stack alignment
+ #else
+ ADD a1, sp, #16 @ Skip signal frame (fp, sp, lr, name ptr)
+ #endif
+- ADD sp, sp, #16+17*4
++ ADD sp, sp, #17*4
+ SWI XOS_EnterOS @ We need to be in SVC mode so reenbling IRQs
+ @ is atomic with returning to USR mode,
+ @ otherwise USR sp could be overwitten by
+Index: libunixlib/signal/post.c
+===================================================================
+--- libunixlib/signal/post.c (revision 7698)
++++ libunixlib/signal/post.c (working copy)
+@@ -255,19 +255,230 @@
+ fprintf (stderr, "\nTermination signal received: %s\n", sys_siglist[signo]);
+ }
+
++static void
++__write_abort_block (const unsigned int *blk, int is32bit)
++{
++ const unsigned int pcmask = is32bit ? 0xfffffffcu : 0x03fffffcu;
++
++ fprintf (stderr, "\n Register dump at %08x:\n", (unsigned int) blk);
++
++ if (!__valid_address (blk, blk + 17))
++ fputs ("\n [bad register dump address]\n", stderr);
++ else
++ {
++ const char rnames[] = "a1a2a3a4v1v2v3v4v5v6slfpipsplrpc";
++ for (int reg = 0; reg < 16; reg++)
++ {
++ if ((reg & 0x3) == 0)
++ fputs ("\n ", stderr);
++
++ fprintf (stderr, " %c%c: %8x",
++ rnames[2*reg + 0], rnames[2*reg + 1], blk[reg + 1]);
++ }
++
++ if (is32bit)
++ fprintf (stderr, "\n cpsr: %8x\n", blk[0]);
++ else
++ {
++ const char * const pmode[4] = { "USR", "FIQ", "IRQ", "SVC" };
++ fprintf (stderr, "\n Mode %s, flags set: %c%c%c%c%c%c\n",
++ pmode[blk[15 + 1] & 3],
++ (blk[15 + 1] & (1<<31)) ? 'N' : 'n',
++ (blk[15 + 1] & (1<<30)) ? 'Z' : 'z',
++ (blk[15 + 1] & (1<<29)) ? 'C' : 'c',
++ (blk[15 + 1] & (1<<28)) ? 'V' : 'v',
++ (blk[15 + 1] & (1<<27)) ? 'I' : 'i',
++ (blk[15 + 1] & (1<<26)) ? 'F' : 'f');
++ }
++
++ unsigned int *pc = (unsigned int *) (blk[15 + 1] & pcmask);
++
++ /* Try LR if PC invalid (e.g. with a prefetch abort). */
++ if (pc < (unsigned int *)0x8000 || !__valid_address (pc - 5, pc + 4))
++ pc = (unsigned int *) (blk[14 + 1] & pcmask);
++
++ if (pc >= (unsigned int *)0x8000 && __valid_address (pc - 5, pc + 4))
++ {
++ for (unsigned int *diss = pc - 5; diss < pc + 4; diss++)
++ {
++ const char *ins;
++ int length;
++ _swix (Debugger_Disassemble, _INR(0,1) | _OUTR(1,2),
++ *diss, diss, &ins, &length);
++
++ const unsigned char c[4] =
++ {
++ (*diss >> 0) & 0xFF,
++ (*diss >> 8) & 0xFF,
++ (*diss >> 16) & 0xFF,
++ (*diss >> 24)
++ };
++ fprintf (stderr, "\n %08x : %c%c%c%c : %08x : ",
++ (unsigned int) diss,
++ (c[0] >= ' ' && c[0] != 127) ? c[0] : '.',
++ (c[1] >= ' ' && c[1] != 127) ? c[1] : '.',
++ (c[2] >= ' ' && c[2] != 127) ? c[2] : '.',
++ (c[3] >= ' ' && c[3] != 127) ? c[3] : '.',
++ *diss);
++ fwrite (ins, length, 1, stderr);
++ }
++ }
++ else
++ fputs ("\n [Disassembly not available]", stderr);
++ }
++
++ fputs ("\n\n", stderr);
++}
++
+ /* Clang and GCC do not have compatible frame pointers. */
+ #ifdef __clang__
+ #define FP_OFFSET (0)
+ #define LR_OFFSET (1)
+-#elif defined (__ARM_EABI__)
+-#define FP_OFFSET (-1)
+-#define LR_OFFSET (0)
+ #else
+ #define LR_OFFSET (-1)
+ #define FP_OFFSET (-3)
+ #endif
+
++#ifdef __ARM_EABI__
++/**
++ * AAPCS does not require the compiler to construct a backtrace structure
++ * in the stack (unlike APCS, which does). This results in FP rarely pointing
++ * at any form of valid stack frame (and, to complicate matters, at the time
++ * of writing, some frames end up with APCS-format frame records, anyway)
++ * which makes it nigh-on impossible to reliably unwind the stack without
++ * additional information). FP is thus often treated as an additional
++ * callee-saved register (i.e. v8) in AAPCS-conformant code.
++ *
++ * Additionally, where frame records are generated, AAPCS has them contain
++ * two entries: previous-FP and LR on entry. There is therefore (unlike APCS)
++ * no way of finding the function entry point from the frame record at all,
++ * even if it did exist.
++ *
++ * So, we cannot trust that FP ever points at a valid stack frame record and
++ * we cannot find function entry points to extract poked function names from.
++ * We can, however, make stack unwinding work if we have some means of
++ * identifying the function in which an arbitrary instruction lies.
++ *
++ * -funwind-tables will result in clang/GCC generating such a data structure,
++ * (an array between __exidx_start and __exidx_end) which will be consulted
++ * by _Unwind_Backtrace() when unwinding the stack.
++ */
++
++#include <unwind.h>
++
++typedef struct {
++ const unsigned int *regs;
++ const unsigned int *last_fn;
++} ul_unwind_ctx;
++
++static void __attribute__((naked))
++__do_unwind (_Unwind_Trace_Fn fn, const void *pw)
++{
++ __asm volatile(
++ "stmfd sp!, {fp, lr};"
++ "add fp, sp, #4;"
++ /* Registers at this point in time will be the initial state.
++ * The trace function must unwind the stack frame we just created
++ * because the personality function will be told there is nothing
++ * to do as we are declared naked.
++ */
++ "bl _Unwind_Backtrace;"
++ "ldmfd sp!, {fp, pc};"
++ );
++}
++
++static _Unwind_Reason_Code
++__write_backtrace_cb (_Unwind_Context *ctx, void *pw)
++{
++ ul_unwind_ctx *uctx = pw;
++ _Unwind_Control_Block *ucbp = NULL;
++ const unsigned int *fn;
++
++ ucbp = (_Unwind_Control_Block *) _Unwind_GetGR(ctx, UNWIND_POINTER_REG);
++ fn = (const unsigned int *) ucbp->pr_cache.fnstart;
++
++ uctx->last_fn = fn;
++
++ if (fn == (const unsigned int *) __do_unwind)
++ {
++ /* First call */
++ if (uctx->regs == NULL)
++ {
++ /* Running thread: unwind on behalf of __do_unwind */
++ _Unwind_VRS_Pop (ctx, _UVRSC_CORE, (1<<11)|(1<<14), _UVRSD_UINT32);
++ }
++ else
++ {
++ /* Thread backtrace: replace entire VRS */
++ int idx;
++ for (idx = 16; idx > 0; idx--)
++ _Unwind_SetGR (ctx, idx - 1, uctx->regs[idx - 1]);
++ }
++
++ return _URC_NO_REASON;
++ }
++
++ fprintf (stderr, " (%8x) fn: %8x pc: %8x sp: %8x ",
++ _Unwind_GetGR (ctx, 11), (unsigned int)fn, _Unwind_GetIP (ctx),
++ _Unwind_GetGR (ctx, 13));
++
++#if PIC
++ /* FIXME: extend this with source location when available. */
++ const char *lib = NULL;
++ unsigned offset;
++ _swix(SOM_Location,
++ _IN(0) | _OUTR(0,1), _Unwind_GetIP (ctx), &lib, &offset);
++ if (lib)
++ fprintf(stderr, " : %8X : %s\n", offset, lib);
++ else
++#endif
++ {
++ int cplusplus_name;
++ const char *name = extract_name (fn, &cplusplus_name);
++ fprintf (stderr, (cplusplus_name) ? " %s\n" : " %s()\n", name);
++ }
++
++ return _URC_NO_REASON;
++}
++
+ static void
++__write_backtrace_thread (const unsigned int *regs)
++{
++ ul_unwind_ctx ctx;
++
++ /* First pass: dump trace for stack as provided */
++ ctx.regs = regs;
++ ctx.last_fn = NULL;
++ __do_unwind (__write_backtrace_cb, &ctx);
++
++ /* If we got here via an environment handler, there may be a saved abort
++ * block to look at. We only want to look if the first pass terminated with
++ * __unixlib_raise_signal (being the entry point to all this unwind logic
++ * from the environment handlers) -- if the first pass terminated somewhere
++ * else, then it is likely that we have been invoked directly via raise(),
++ * and so the presence or otherwise of an abort block is irrelevant.
++ *
++ * If an abort block is available, it will be pointed at by the
++ * (misnamed for EABI) __ul_callbackfp; if not __ul_callbackfp will be NULL.
++ * Additionally, we only want to consider the abort block if we're dumping
++ * the running thread, so check for regs being NULL to identify that.
++ */
++ if (__ul_callbackfp != NULL && regs == NULL
++ && ctx.last_fn == (unsigned int *) __unixlib_raise_signal)
++ {
++ /* Abort block: cpsr, r0-r15. */
++ __write_abort_block (__ul_callbackfp, /* is32bit= */ 1);
++
++ /* Dump remaining trace from block (skipping over saved CPSR) */
++ ctx.regs = __ul_callbackfp + 1;
++ ctx.last_fn = NULL;
++ __do_unwind (__write_backtrace_cb, &ctx);
++ }
++
++ fputc ('\n', stderr);
++}
++#else
++static void
+ __write_backtrace_thread (const unsigned int *fp)
+ {
+ /* Running as USR26 or USR32 ? */
+@@ -306,22 +517,6 @@
+ break;
+ }
+
+-#ifdef __ARM_EABI__
+- const unsigned int * const lr = (unsigned int *)fp[LR_OFFSET];
+- fprintf (stderr, " (%8x) lr: %8x",
+- (unsigned int)fp, (unsigned int)lr);
+-#if PIC
+- /* FIXME: extend this with source location when available. */
+- const char *lib = NULL;
+- unsigned offset;
+- _swix(SOM_Location,
+- _IN(0) | _OUTR(0,1), lr, &lib, &offset);
+- if (lib)
+- fprintf(stderr, " : %8X : %s\n", offset, lib);
+- else
+-#endif
+- fputc('\n', stderr);
+-#else
+ /* Retrieve PC counter.
+ PC counter has been saved using STMxx ..., { ..., PC } so it can be
+ 8 or 12 bytes away from the STMxx instruction depending on the ARM
+@@ -347,96 +542,24 @@
+ int cplusplus_name;
+ const char *name = extract_name (pc, &cplusplus_name);
+ fprintf (stderr, (cplusplus_name) ? " %s\n" : " %s()\n", name);
+-#endif
++
+ oldfp = fp;
+ fp = (const unsigned int *)fp[FP_OFFSET];
+-#ifndef __ARM_EABI__
+ if (__ul_callbackfp != NULL && fp == __ul_callbackfp)
+ {
+ /* At &oldfp[1] = cpsr, a1-a4, v1-v6, sl, fp, ip, sp, lr, pc */
+- fprintf (stderr, "\n Register dump at %08x:\n",
+- (unsigned int) &oldfp[1]);
+-
+- if (!__valid_address (oldfp + 1, oldfp + 18))
+- fputs ("\n [bad register dump address]\n", stderr);
+- else
+- {
+- const char rnames[] = "a1a2a3a4v1v2v3v4v5v6slfpipsplrpc";
+- for (int reg = 0; reg < 16; reg++)
+- {
+- if ((reg & 0x3) == 0)
+- fputs ("\n ", stderr);
+-
+- fprintf (stderr, " %c%c: %8x",
+- rnames[2*reg + 0], rnames[2*reg + 1], oldfp[reg + 2]);
+- }
+-
+- if (is32bit)
+- fprintf (stderr, "\n cpsr: %8x\n", oldfp[1]);
+- else
+- {
+- const char * const pmode[4] = { "USR", "FIQ", "IRQ", "SVC" };
+- fprintf (stderr, "\n Mode %s, flags set: %c%c%c%c%c%c\n",
+- pmode[oldfp[15 + 2] & 3],
+- (oldfp[15 + 2] & (1<<31)) ? 'N' : 'n',
+- (oldfp[15 + 2] & (1<<30)) ? 'Z' : 'z',
+- (oldfp[15 + 2] & (1<<29)) ? 'C' : 'c',
+- (oldfp[15 + 2] & (1<<28)) ? 'V' : 'v',
+- (oldfp[15 + 2] & (1<<27)) ? 'I' : 'i',
+- (oldfp[15 + 2] & (1<<26)) ? 'F' : 'f');
+- }
+-
+- pc = (unsigned int *) (oldfp[17] & pcmask);
+-
+- /* Try LR if PC invalid (e.g. with a prefetch abort). */
+- if (pc < (unsigned int *)0x8000 || !__valid_address (pc - 5, pc + 4))
+- pc = (unsigned int *) (oldfp[16] & pcmask);
+-
+- if (pc >= (unsigned int *)0x8000 && __valid_address (pc - 5, pc + 4))
+- {
+- for (unsigned int *diss = pc - 5; diss < pc + 4; diss++)
+- {
+- const char *ins;
+- int length;
+- _swix (Debugger_Disassemble, _INR(0,1) | _OUTR(1,2),
+- *diss, diss, &ins, &length);
+-
+- const unsigned char c[4] =
+- {
+- (*diss >> 0) & 0xFF,
+- (*diss >> 8) & 0xFF,
+- (*diss >> 16) & 0xFF,
+- (*diss >> 24)
+- };
+- fprintf (stderr, "\n %08x : %c%c%c%c : %08x : ",
+- (unsigned int) diss,
+- (c[0] >= ' ' && c[0] != 127) ? c[0] : '.',
+- (c[1] >= ' ' && c[1] != 127) ? c[1] : '.',
+- (c[2] >= ' ' && c[2] != 127) ? c[2] : '.',
+- (c[3] >= ' ' && c[3] != 127) ? c[3] : '.',
+- *diss);
+- fwrite (ins, length, 1, stderr);
+- }
+- }
+- else
+- fputs ("\n [Disassembly not available]", stderr);
+- }
+-
+- fputs ("\n\n", stderr);
++ __write_abort_block (&oldfp[1], is32bit);
+ }
+-#endif
+ }
+
+ fputc ('\n', stderr);
+ }
++#endif
+
+-
+ void
+ __write_backtrace (int signo)
+ {
+-#ifdef __ARM_EABI__
+- register const unsigned int *fp = __builtin_frame_address(0);
+-#else
++#ifndef __ARM_EABI__
+ register const unsigned int *fp __asm ("fp");
+ #endif
+
+@@ -485,7 +608,11 @@
+ /* Dump first the details of the current thread. */
+ fprintf (stderr, "Stack backtrace:\n\nRunning thread %p (%s)\n",
+ __pthread_running_thread, __pthread_running_thread->name);
++#ifdef __ARM_EABI__
++ __write_backtrace_thread (NULL);
++#else
+ __write_backtrace_thread (fp);
++#endif
+
+ /* And then the other suspended threads if any. */
+ for (pthread_t th = __pthread_thread_list; th != NULL; th = th->next)
+@@ -494,7 +621,10 @@
+ continue;
+
+ fprintf (stderr, "\nThread %p (%s)\n", th, th->name);
+-#ifdef __clang__
++#ifdef __ARM_EABI__
++ __write_backtrace_thread (&th->saved_context->r[0]);
++#else
++# ifdef __clang__
+ const unsigned int fakestackframe[] =
+ {
+ (unsigned int)th->saved_context->r[11],
+@@ -501,22 +631,16 @@
+ (unsigned int)th->saved_context->r[14]
+ };
+ __write_backtrace_thread (&fakestackframe[0]);
+-#elif defined (__ARM_EABI__)
++# else
+ const unsigned int fakestackframe[] =
+ {
+ (unsigned int)th->saved_context->r[11],
+- (unsigned int)th->saved_context->r[14]
+- };
+- __write_backtrace_thread (&fakestackframe[1]);
+-#else
+- const unsigned int fakestackframe[] =
+- {
+- (unsigned int)th->saved_context->r[11],
+ (unsigned int)th->saved_context->r[13],
+ (unsigned int)th->saved_context->r[14],
+ (unsigned int)th->saved_context->r[15]
+ };
+ __write_backtrace_thread (&fakestackframe[3]);
++# endif
+ #endif
+ }
+ }