summaryrefslogtreecommitdiff
path: root/arm-riscos-gnueabi
diff options
context:
space:
mode:
authorJohn-Mark Bell <jmb@netsurf-browser.org>2023-03-08 01:47:21 +0000
committerJohn-Mark Bell <jmb@netsurf-browser.org>2023-03-08 01:49:17 +0000
commit8dcff366a1c8379aa327f20304ebc0e6fe987f2f (patch)
tree2e728590a07ae1594bff90469a442d06543617a2 /arm-riscos-gnueabi
parentf07f57628981294b64c6b72bf25f5f588b58444e (diff)
downloadtoolchains-8dcff366a1c8379aa327f20304ebc0e6fe987f2f.tar.gz
toolchains-8dcff366a1c8379aa327f20304ebc0e6fe987f2f.tar.bz2
arm-riscos-gnueabi: dump abort block and disassembly
If we're generating a traceback as the result of an environment handler being invoked, there may well be a stored abort block to inspect. Dump the registers it contains and disassemble the code at the point of explosion, as for non-EABI builds. Note, however, that at present, ARMEABISupport gobbles data aborts so they end up appearing via the Error handler and thus no abort block is available. This does mean that invalid data accesses no longer produce useful stack traces.
Diffstat (limited to 'arm-riscos-gnueabi')
-rw-r--r--arm-riscos-gnueabi/recipes/patches/gccsdk/unixlib-unwind.p254
1 files changed, 240 insertions, 14 deletions
diff --git a/arm-riscos-gnueabi/recipes/patches/gccsdk/unixlib-unwind.p b/arm-riscos-gnueabi/recipes/patches/gccsdk/unixlib-unwind.p
index a917d7c..9ff54c9 100644
--- a/arm-riscos-gnueabi/recipes/patches/gccsdk/unixlib-unwind.p
+++ b/arm-riscos-gnueabi/recipes/patches/gccsdk/unixlib-unwind.p
@@ -11,11 +11,134 @@ Index: libunixlib/Makefile.am
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)
-@@ -259,15 +259,115 @@
+@@ -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)
@@ -54,6 +177,11 @@ Index: libunixlib/signal/post.c
+
+#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)
+{
@@ -73,16 +201,19 @@ Index: libunixlib/signal/post.c
+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 (pw == NULL)
++ if (uctx->regs == NULL)
+ {
+ /* Running thread: unwind on behalf of __do_unwind */
+ _Unwind_VRS_Pop (ctx, _UVRSC_CORE, (1<<11)|(1<<14), _UVRSD_UINT32);
@@ -92,7 +223,7 @@ Index: libunixlib/signal/post.c
+ /* Thread backtrace: replace entire VRS */
+ int idx;
+ for (idx = 16; idx > 0; idx--)
-+ _Unwind_SetGR (ctx, idx - 1, ((unsigned int *) pw)[idx - 1]);
++ _Unwind_SetGR (ctx, idx - 1, uctx->regs[idx - 1]);
+ }
+
+ return _URC_NO_REASON;
@@ -118,15 +249,43 @@ Index: libunixlib/signal/post.c
+ fprintf (stderr, (cplusplus_name) ? " %s\n" : " %s()\n", name);
+ }
+
-+ /* TODO: __ul_callback_fp stuff (might need to save/match lr, instead) */
-+
+ return _URC_NO_REASON;
+}
+
static void
+__write_backtrace_thread (const unsigned int *regs)
+{
-+ __do_unwind (__write_backtrace_cb, 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
@@ -134,7 +293,7 @@ Index: libunixlib/signal/post.c
__write_backtrace_thread (const unsigned int *fp)
{
/* Running as USR26 or USR32 ? */
-@@ -306,22 +406,6 @@
+@@ -306,22 +517,6 @@
break;
}
@@ -157,7 +316,7 @@ Index: libunixlib/signal/post.c
/* 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,10 +431,9 @@
+@@ -347,96 +542,24 @@
int cplusplus_name;
const char *name = extract_name (pc, &cplusplus_name);
fprintf (stderr, (cplusplus_name) ? " %s\n" : " %s()\n", name);
@@ -169,9 +328,76 @@ Index: libunixlib/signal/post.c
if (__ul_callbackfp != NULL && fp == __ul_callbackfp)
{
/* At &oldfp[1] = cpsr, a1-a4, v1-v6, sl, fp, ip, sp, lr, pc */
-@@ -424,19 +507,16 @@
-
- fputs ("\n\n", stderr);
+- 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
}
@@ -191,7 +417,7 @@ Index: libunixlib/signal/post.c
register const unsigned int *fp __asm ("fp");
#endif
-@@ -485,7 +565,11 @@
+@@ -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);
@@ -203,7 +429,7 @@ Index: libunixlib/signal/post.c
/* And then the other suspended threads if any. */
for (pthread_t th = __pthread_thread_list; th != NULL; th = th->next)
-@@ -494,7 +578,10 @@
+@@ -494,7 +621,10 @@
continue;
fprintf (stderr, "\nThread %p (%s)\n", th, th->name);
@@ -215,7 +441,7 @@ Index: libunixlib/signal/post.c
const unsigned int fakestackframe[] =
{
(unsigned int)th->saved_context->r[11],
-@@ -501,22 +588,16 @@
+@@ -501,22 +631,16 @@
(unsigned int)th->saved_context->r[14]
};
__write_backtrace_thread (&fakestackframe[0]);