[PATCH 1/4] Teach arm unwinders to terminate gracefully

Antoine Tremblay
2016-01-07T17:45:11Z

When examining a trace buffer we have the following issue:

~~~
tfind start
Register 13 is not available
Found trace frame 0, tracepoint 2
#-1 0x40123556 in pendfunc2
^^^
~~~

The reason for this is that the target's stack pointer is unavailable
when examining the trace buffer. What we are seeing is due to the
'tfind' command creating a sentinel frame and unwinding it. If an
exception is thrown, we are left with the sentinel frame being displayed
at level #-1. The exception is thrown when the prologue unwinder tries
to read the stack pointer to construct an ID for the frame.

This patch fixes this and similar issues by making all the arm unwinders
catch NOT_AVAILABLE_ERROR exceptions when either register or memory is
unreadable and report back to the frame core code with UNWIND_UNAVAILABLE.

Note this commit log adapted from 7dfa3edc033c443036d9f2a3e01120f7fb54f498
which fixed a similar issue for aarch64.

No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }

gdb/ChangeLog:

* arm-tdep.c (struct arm_prologue_cache) <available_p>: New field.
(arm_make_prologue_cache): Swallow NOT_AVAIABLE_ERROR or set
available_p.
(arm_prologue_unwind_stop_reason): Return UNWIND_UNAVAILABLE if
available_p is not set.
(arm_prologue_this_id): Call frame_id_build_unavailable_stack if
available_p is not set.
(arm_make_stub_cache): Swallow NOT_AVAIABLE_ERROR or set
available_p.
(arm_stub_this_id): Call frame_id_build_unavailable_stack if
available_p is not set.
(arm_m_exception_cache): Swallow NOT_AVAIABLE_ERROR or set
available_p.
(arm_m_exception_this_id): Call frame_id_build_unavailable_stack if
available_p is not set.
---
gdb/arm-tdep.c | 142 ++++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 95 insertions(+), 47 deletions(-)

diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 05d60bb..5ee7fb0 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -252,6 +252,9 @@ struct arm_prologue_cache
to identify this frame. */
CORE_ADDR prev_sp;

+ /* Is the target available to read from ? */
+ int available_p;
+
/* The frame base for this frame is just prev_sp - frame size.
FRAMESIZE is the distance from the frame pointer to the
initial stack pointer. */
@@ -1793,19 +1796,29 @@ arm_make_prologue_cache (struct frame_info *this_frame)
cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);

- arm_scan_prologue (this_frame, cache);
+ TRY
+ {
+ arm_scan_prologue (this_frame, cache);
+ unwound_fp = get_frame_register_unsigned (this_frame, cache->framereg);
+ if (unwound_fp == 0)
+ return cache;

- unwound_fp = get_frame_register_unsigned (this_frame, cache->framereg);
- if (unwound_fp == 0)
- return cache;
+ cache->prev_sp = unwound_fp + cache->framesize;

- cache->prev_sp = unwound_fp + cache->framesize;
+ /* Calculate actual addresses of saved registers using offsets
+ determined by arm_scan_prologue. */
+ for (reg = 0; reg < gdbarch_num_regs (get_frame_arch (this_frame)); reg++)
+ if (trad_frame_addr_p (cache->saved_regs, reg))
+ cache->saved_regs[reg].addr += cache->prev_sp;

- /* Calculate actual addresses of saved registers using offsets
- determined by arm_scan_prologue. */
- for (reg = 0; reg < gdbarch_num_regs (get_frame_arch (this_frame)); reg++)
- if (trad_frame_addr_p (cache->saved_regs, reg))
- cache->saved_regs[reg].addr += cache->prev_sp;
+ cache->available_p = 1;
+ }
+ CATCH (ex, RETURN_MASK_ERROR)
+ {
+ if (ex.error != NOT_AVAILABLE_ERROR)
+ throw_exception (ex);
+ }
+ END_CATCH

return cache;
}
@@ -1823,6 +1836,9 @@ arm_prologue_unwind_stop_reason (struct frame_info *this_frame,
*this_cache = arm_make_prologue_cache (this_frame);
cache = (struct arm_prologue_cache *) *this_cache;

+ if (!cache->available_p)
+ return UNWIND_UNAVAILABLE;
+
/* This is meant to halt the backtrace at "_start". */
pc = get_frame_pc (this_frame);
if (pc <= gdbarch_tdep (get_frame_arch (this_frame))->lowest_pc)
@@ -1851,16 +1867,23 @@ arm_prologue_this_id (struct frame_info *this_frame,
*this_cache = arm_make_prologue_cache (this_frame);
cache = (struct arm_prologue_cache *) *this_cache;

- /* Use function start address as part of the frame ID. If we cannot
- identify the start address (due to missing symbol information),
- fall back to just using the current PC. */
- pc = get_frame_pc (this_frame);
- func = get_frame_func (this_frame);
- if (!func)
- func = pc;
+ if (!cache->available_p)
+ {
+ *this_id = frame_id_build_unavailable_stack (cache->prev_sp);
+ }
+ else
+ {
+ /* Use function start address as part of the frame ID. If we cannot
+ identify the start address (due to missing symbol information),
+ fall back to just using the current PC. */
+ pc = get_frame_pc (this_frame);
+ func = get_frame_func (this_frame);
+ if (!func)
+ func = pc;

- id = frame_id_build (cache->prev_sp, func);
- *this_id = id;
+ id = frame_id_build (cache->prev_sp, func);
+ *this_id = id;
+ }
}

static struct value *
@@ -2738,7 +2761,17 @@ arm_make_stub_cache (struct frame_info *this_frame)
cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);

- cache->prev_sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM);
+ TRY
+ {
+ cache->prev_sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM);
+ cache->available_p = 1;
+ }
+ CATCH (ex, RETURN_MASK_ERROR)
+ {
+ if (ex.error != NOT_AVAILABLE_ERROR)
+ throw_exception (ex);
+ }
+ END_CATCH

return cache;
}
@@ -2756,7 +2789,10 @@ arm_stub_this_id (struct frame_info *this_frame,
*this_cache = arm_make_stub_cache (this_frame);
cache = (struct arm_prologue_cache *) *this_cache;

- *this_id = frame_id_build (cache->prev_sp, get_frame_pc (this_frame));
+ if (!cache->available_p)
+ *this_id = frame_id_build_unavailable_stack (cache->prev_sp);
+ else
+ *this_id = frame_id_build (cache->prev_sp, get_frame_pc (this_frame));
}

static int
@@ -2809,29 +2845,38 @@ arm_m_exception_cache (struct frame_info *this_frame)
cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);

- unwound_sp = get_frame_register_unsigned (this_frame,
- ARM_SP_REGNUM);
-
- /* The hardware saves eight 32-bit words, comprising xPSR,
- ReturnAddress, LR (R14), R12, R3, R2, R1, R0. See details in
- "B1.5.6 Exception entry behavior" in
- "ARMv7-M Architecture Reference Manual". */
- cache->saved_regs[0].addr = unwound_sp;
- cache->saved_regs[1].addr = unwound_sp + 4;
- cache->saved_regs[2].addr = unwound_sp + 8;
- cache->saved_regs[3].addr = unwound_sp + 12;
- cache->saved_regs[12].addr = unwound_sp + 16;
- cache->saved_regs[14].addr = unwound_sp + 20;
- cache->saved_regs[15].addr = unwound_sp + 24;
- cache->saved_regs[ARM_PS_REGNUM].addr = unwound_sp + 28;
-
- /* If bit 9 of the saved xPSR is set, then there is a four-byte
- aligner between the top of the 32-byte stack frame and the
- previous context's stack pointer. */
- cache->prev_sp = unwound_sp + 32;
- if (safe_read_memory_integer (unwound_sp + 28, 4, byte_order, &xpsr)
- && (xpsr & (1 << 9)) != 0)
- cache->prev_sp += 4;
+ TRY
+ {
+ unwound_sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM);
+ /* The hardware saves eight 32-bit words, comprising xPSR,
+ ReturnAddress, LR (R14), R12, R3, R2, R1, R0. See details in
+ "B1.5.6 Exception entry behavior" in
+ "ARMv7-M Architecture Reference Manual". */
+ cache->saved_regs[0].addr = unwound_sp;
+ cache->saved_regs[1].addr = unwound_sp + 4;
+ cache->saved_regs[2].addr = unwound_sp + 8;
+ cache->saved_regs[3].addr = unwound_sp + 12;
+ cache->saved_regs[12].addr = unwound_sp + 16;
+ cache->saved_regs[14].addr = unwound_sp + 20;
+ cache->saved_regs[15].addr = unwound_sp + 24;
+ cache->saved_regs[ARM_PS_REGNUM].addr = unwound_sp + 28;
+
+ /* If bit 9 of the saved xPSR is set, then there is a four-byte
+ aligner between the top of the 32-byte stack frame and the
+ previous context's stack pointer. */
+ cache->prev_sp = unwound_sp + 32;
+ if (safe_read_memory_integer (unwound_sp + 28, 4, byte_order, &xpsr)
+ && (xpsr & (1 << 9)) != 0)
+ cache->prev_sp += 4;
+
+ cache->available_p = 1;
+ }
+ CATCH (ex, RETURN_MASK_ERROR)
+ {
+ if (ex.error != NOT_AVAILABLE_ERROR)
+ throw_exception (ex);
+ }
+ END_CATCH

return cache;
}
@@ -2850,9 +2895,12 @@ arm_m_exception_this_id (struct frame_info *this_frame,
*this_cache = arm_m_exception_cache (this_frame);
cache = (struct arm_prologue_cache *) *this_cache;

- /* Our frame ID for a stub frame is the current SP and LR. */
- *this_id = frame_id_build (cache->prev_sp,
- get_frame_pc (this_frame));
+ if (!cache->available_p)
+ *this_id = frame_id_build_unavailable_stack (cache->prev_sp);
+ else
+ /* Our frame ID for a stub frame is the current SP and LR. */
+ *this_id = frame_id_build (cache->prev_sp,
+ get_frame_pc (this_frame));
}

/* Implementation of function hook 'prev_register' in
--
2.6.4