Fix exception stack unwinding for ARM Cortex-M

For Cortex-M targets using floating-point, eg the Cortex-M4F, its not possible
to get any call-stack backtrace if setting a breakpoint in ISR.

The exception stack unwinder for Cortex-M does not consider if floating-point
registers was stacked or not, further the Cortex-M has two stack pointers: MSP
(Main Stack Pointer) and PSP (Process Stack Pointer).
This is not handled when GDB tries to backtrace in the exception stack
unwinder.

This patch fixes this, and gives a correct call-stack backtrace from
breakpoints set in a handler or ISR.

gdb/ChangeLog:

        * arm-tdep.c (arm_m_exception_cache): Try use correct stack
        pointer and stack frame offset when unwinding.
This commit is contained in:
Fredrik Hederstierna
2020-09-14 14:56:34 +01:00
committed by Alan Hayward
parent 74fea55bb7
commit 55ea94da36
2 changed files with 123 additions and 8 deletions

View File

@ -1,3 +1,9 @@
2020-09-14 Fredrik Hederstierna <fredrik.hederstierna@verisure.com>
Adam Renquinha <arenquinha@cimeq.qc.ca>
* arm-tdep.c (arm_m_exception_cache): Try use correct stack
pointer and stack frame offset when unwinding.
2020-09-13 Pedro Alves <pedro@palves.net>
* NEWS: Document "-break-insert --qualified".

View File

@ -469,7 +469,7 @@ arm_pc_is_thumb (struct gdbarch *gdbarch, CORE_ADDR memaddr)
}
/* Determine if the address specified equals any of these magic return
values, called EXC_RETURN, defined by the ARM v6-M and v7-M
values, called EXC_RETURN, defined by the ARM v6-M, v7-M and v8-M
architectures.
From ARMv6-M Reference Manual B1.5.8
@ -500,13 +500,25 @@ arm_pc_is_thumb (struct gdbarch *gdbarch, CORE_ADDR memaddr)
0xFFFFFFFD Thread mode Process Basic
For more details see "B1.5.8 Exception return behavior"
in both ARMv6-M and ARMv7-M Architecture Reference Manuals. */
in both ARMv6-M and ARMv7-M Architecture Reference Manuals.
In the ARMv8-M Architecture Technical Reference also adds
for implementations without the Security Extension:
EXC_RETURN Condition
0xFFFFFFB0 Return to Handler mode.
0xFFFFFFB8 Return to Thread mode using the main stack.
0xFFFFFFBC Return to Thread mode using the process stack. */
static int
arm_m_addr_is_magic (CORE_ADDR addr)
{
switch (addr)
{
/* Values from ARMv8-M Architecture Technical Reference. */
case 0xffffffb0:
case 0xffffffb8:
case 0xffffffbc:
/* Values from Tables in B1.5.8 the EXC_RETURN definitions of
the exception return behavior. */
case 0xffffffe1:
@ -2923,14 +2935,64 @@ arm_m_exception_cache (struct frame_info *this_frame)
struct gdbarch *gdbarch = get_frame_arch (this_frame);
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
struct arm_prologue_cache *cache;
CORE_ADDR lr;
CORE_ADDR sp;
CORE_ADDR unwound_sp;
LONGEST xpsr;
uint32_t exc_return;
uint32_t process_stack_used;
uint32_t extended_frame_used;
uint32_t secure_stack_used;
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);
/* ARMv7-M Architecture Reference "B1.5.6 Exception entry behavior"
describes which bits in LR that define which stack was used prior
to the exception and if FPU is used (causing extended stack frame). */
lr = get_frame_register_unsigned (this_frame, ARM_LR_REGNUM);
sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM);
/* Check EXC_RETURN indicator bits. */
exc_return = (((lr >> 28) & 0xf) == 0xf);
/* Check EXC_RETURN bit SPSEL if Main or Thread (process) stack used. */
process_stack_used = ((lr & (1 << 2)) != 0);
if (exc_return && process_stack_used)
{
/* Thread (process) stack used.
Potentially this could be other register defined by target, but PSP
can be considered a standard name for the "Process Stack Pointer".
To be fully aware of system registers like MSP and PSP, these could
be added to a separate XML arm-m-system-profile that is valid for
ARMv6-M and ARMv7-M architectures. Also to be able to debug eg a
corefile off-line, then these registers must be defined by GDB,
and also be included in the corefile regsets. */
int psp_regnum = user_reg_map_name_to_regnum (gdbarch, "psp", -1);
if (psp_regnum == -1)
{
/* Thread (process) stack could not be fetched,
give warning and exit. */
warning (_("no PSP thread stack unwinding supported."));
/* Terminate any further stack unwinding by refer to self. */
cache->prev_sp = sp;
return cache;
}
else
{
/* Thread (process) stack used, use PSP as SP. */
unwound_sp = get_frame_register_unsigned (this_frame, psp_regnum);
}
}
else
{
/* Main stack used, use MSP as SP. */
unwound_sp = sp;
}
/* The hardware saves eight 32-bit words, comprising xPSR,
ReturnAddress, LR (R14), R12, R3, R2, R1, R0. See details in
@ -2940,15 +3002,62 @@ arm_m_exception_cache (struct frame_info *this_frame)
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_IP_REGNUM].addr = unwound_sp + 16;
cache->saved_regs[ARM_LR_REGNUM].addr = unwound_sp + 20;
cache->saved_regs[ARM_PC_REGNUM].addr = unwound_sp + 24;
cache->saved_regs[ARM_PS_REGNUM].addr = unwound_sp + 28;
/* Check EXC_RETURN bit FTYPE if extended stack frame (FPU regs stored)
type used. */
extended_frame_used = ((lr & (1 << 4)) == 0);
if (exc_return && extended_frame_used)
{
int i;
int fpu_regs_stack_offset;
/* This code does not take into account the lazy stacking, see "Lazy
context save of FP state", in B1.5.7, also ARM AN298, supported
by Cortex-M4F architecture.
To fully handle this the FPCCR register (Floating-point Context
Control Register) needs to be read out and the bits ASPEN and LSPEN
could be checked to setup correct lazy stacked FP registers.
This register is located at address 0xE000EF34. */
/* Extended stack frame type used. */
fpu_regs_stack_offset = unwound_sp + 0x20;
for (i = 0; i < 16; i++)
{
cache->saved_regs[ARM_D0_REGNUM + i].addr = fpu_regs_stack_offset;
fpu_regs_stack_offset += 4;
}
cache->saved_regs[ARM_FPSCR_REGNUM].addr = unwound_sp + 0x60;
/* Offset 0x64 is reserved. */
cache->prev_sp = unwound_sp + 0x68;
}
else
{
/* Standard stack frame type used. */
cache->prev_sp = unwound_sp + 0x20;
}
/* Check EXC_RETURN bit S if Secure or Non-secure stack used. */
secure_stack_used = ((lr & (1 << 6)) != 0);
if (exc_return && secure_stack_used)
{
/* ARMv8-M Exception and interrupt handling is not considered here.
In the ARMv8-M architecture also EXC_RETURN bit S is controlling if
the Secure or Non-secure stack was used. To separate Secure and
Non-secure stacks, processors that are based on the ARMv8-M
architecture support 4 stack pointers: MSP_S, PSP_S, MSP_NS, PSP_NS.
In addition, a stack limit feature is provided using stack limit
registers (accessible using MSR and MRS instructions) in Privileged
level. */
}
/* 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;