mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-01 11:59:27 +08:00
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:

committed by
Alan Hayward

parent
74fea55bb7
commit
55ea94da36
@ -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".
|
||||
|
125
gdb/arm-tdep.c
125
gdb/arm-tdep.c
@ -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;
|
||||
|
Reference in New Issue
Block a user