public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] Fix exception unwinding for ARM Cortex-M
@ 2016-07-28  8:13 Fredrik Hederstierna
  2016-07-29 11:47 ` Yao Qi
  2016-08-02  9:43 ` Fredrik Hederstierna
  0 siblings, 2 replies; 16+ messages in thread
From: Fredrik Hederstierna @ 2016-07-28  8:13 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 2268 bytes --]

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.
Meaning for eg. Cortex-M4F its not possible to get any call-stack backtrace if setting a breakpoint in ISR, and floating-point variable was used.

This patch was inspired by the great work done by James-Adam Renquinha Henri, submitted April this year.

See
https://sourceware.org/ml/gdb-patches/2016-04/msg00170.html
https://bugs.launchpad.net/gcc-arm-embedded/+bug/1566054

Though it seems like the patch got some comments that were never finally addressed,
so I now tried to rewrite the patch from scratch, and try to address some of the issues raised by the maintainers.

But
* Still "psp" could (in theory) be mixed with a target that could pass 'another' PSP reg definition that is something else
(but seems very unlikely, and not really appropriate to use "psp" for something else).

* Still FPU regs could be lazy stacked and not correct, though a warning is now given to user in either case.
At least it makes the situation better than today, when no call-stack can be retrieved at all, for any registers.

The best solution would be as mailing threads proposed to have PSP and MSP defined in GDB.
Now eg. OpenOCD defines them which makes access to PSP possible.

I'm working in parallel with another patch that adds XML feature descriptions for 'arm-m-system-v6.xml' and 'arm-m-system-v7.xml'
that will define PSP and MSP in GDB which can then be used by exception unwinder code. Its not ready yet though.

The next thing would then be to also add FPU context control reg FPCCR, which is needed for retrieving info on the FPU lazy stacking.
Though its complicated I think and I will try to investigate an 'arm-m-fpu.xml' profile further, if this is solution perhaps.

Its needed by GDB to be aware of both MSP and PSP, and also FPCCR to be fully able to unwind exception stack properly.
Even if eg. OpenOCD could define these in run-time, these are still needed when eg. debugging a corefile off-line in GDB.

Please review patch and comment.

Best Regards,
Fredrik Hederstierna

Senior Software Developer
Verisure Innovation Centre
Malmoe Sweden

[-- Attachment #2: gdb-cortex-m-exception-unwind.patch --]
[-- Type: application/octet-stream, Size: 7258 bytes --]

diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index d2661cb..6854ec0 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -469,9 +469,11 @@ static CORE_ADDR
 arm_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR val)
 {
   /* On M-profile devices, do not strip the low bit from EXC_RETURN
-     (the magic exception return address).  */
+     (the magic exception return address).
+     According to B1.5.8 of the ARMv7-M Reference Manual the EXC_RETURN
+     value is 0xF in Bits[31:28].  */
   if (gdbarch_tdep (gdbarch)->is_m
-      && (val & 0xfffffff0) == 0xfffffff0)
+      && (val & 0xf0000000) == 0xf0000000)
     return val;
 
   if (arm_apcs_32)
@@ -2902,14 +2904,59 @@ 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 main_stack_used;
+  uint32_t extended_frame_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 if main stack was used.  */
+  main_stack_used = ((lr & 0xf) != 0xd);
+  if (main_stack_used)
+    {
+      /* Main stack used, use MSP as SP.  */
+      unwound_sp = sp;
+    }
+  else
+    {
+      /* 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, exiting."));
+
+		  /* 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);
+	    }
+    }
 
   /* The hardware saves eight 32-bit words, comprising xPSR,
      ReturnAddress, LR (R14), R12, R3, R2, R1, R0.  See details in
@@ -2919,15 +2966,47 @@ 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 if extended stack frame (FPU regs stored) was used.  */
+  extended_frame_used = ((lr & (1 << 4)) == 0);
+  if (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. Give a warning and try do best effort.
+	     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.  */
+
+	  warning (_("no FPU lazy stack unwinding supported, check FPCCR."));
+
+	  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
+    {
+      /* Basic frame type used.  */
+      cache->prev_sp = unwound_sp + 32;
+    }
+
   /* 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;
@@ -2977,6 +3056,41 @@ arm_m_exception_prev_register (struct frame_info *this_frame,
 				       prev_regnum);
 }
 
+/* Determine if the program counter specified equals any of
+  these magic return values defined by v7-M architecture. */
+
+static int
+arm_m_pc_is_magic (CORE_ADDR pc)
+{
+  /* Exception frames return to one of these magic PCs defined in v7-M.
+     For more details see "B1.5.8 Exception return behavior"
+     in "ARMv7-M Architecture Reference Manual".  */
+  switch (pc)
+    {
+      /* From Table B1-8 and B1-9 the EXC_RETURN definition of
+         the exception return behavior.  */
+
+      /* Return to Handler mode. Return stack Main. Frame type Extended.  */
+      case 0xffffffe1:
+      /* Return to Thread mode. Return stack Main. Frame type Extended.  */
+      case 0xffffffe9:
+      /* Return to Thread mode. Return stack Process. Frame type Extended.  */
+      case 0xffffffed:
+      /* Return to Handler mode. Return stack Main. Frame type Basic.  */
+      case 0xfffffff1:
+      /* Return to Thread mode. Return stack Main. Frame type Basic.  */
+      case 0xfffffff9:
+      /* Return to Thread mode. Return stack Process. Frame type Basic.  */
+      case 0xfffffffd:
+        /* PC is magic.  */
+        return 1;
+
+      default:
+        /* PC is not magic.  */
+        return 0;
+    }
+}
+
 /* Implementation of function hook 'sniffer' in
    'struct frame_uwnind'.  */
 
@@ -2990,14 +3104,8 @@ arm_m_exception_unwind_sniffer (const struct frame_unwind *self,
   /* No need to check is_m; this sniffer is only registered for
      M-profile architectures.  */
 
-  /* Exception frames return to one of these magic PCs.  Other values
-     are not defined as of v7-M.  See details in "B1.5.8 Exception
-     return behavior" in "ARMv7-M Architecture Reference Manual".  */
-  if (this_pc == 0xfffffff1 || this_pc == 0xfffffff9
-      || this_pc == 0xfffffffd)
-    return 1;
-
-  return 0;
+  /* Check if exception frame returns to a magic PC value.  */
+  return arm_m_pc_is_magic (this_pc);
 }
 
 /* Frame unwinder for M-profile exceptions.  */

^ permalink raw reply	[flat|nested] 16+ messages in thread
[parent not found: <AM4PR1001MB0948AC4D9CB635F5A9A2FC82EFDC0@AM4PR1001MB0948.EURPRD10.PROD.OUTLOOK.COM>]

end of thread, other threads:[~2019-06-12  9:01 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-28  8:13 [PATCH] Fix exception unwinding for ARM Cortex-M Fredrik Hederstierna
2016-07-29 11:47 ` Yao Qi
2016-08-02  9:43 ` Fredrik Hederstierna
2016-08-02 16:01   ` Yao Qi
2016-08-03 10:56   ` Fredrik Hederstierna
2016-08-03 11:19     ` Yao Qi
2016-08-03 17:33     ` James-Adam Renquinha Henri
2016-08-04  7:04     ` Fredrik Hederstierna
2016-08-05 19:02       ` Adam Renquinha
2016-08-09  9:17         ` Yao Qi
2016-09-23 17:32           ` Adam Renquinha
2016-09-26  3:03             ` Yao Qi
2016-09-27  1:38               ` Adam Renquinha
2016-09-27  4:40                 ` Yao Qi
     [not found] <AM4PR1001MB0948AC4D9CB635F5A9A2FC82EFDC0@AM4PR1001MB0948.EURPRD10.PROD.OUTLOOK.COM>
     [not found] ` <HE1PR1001MB130613C0995C4C21A630373BEF1B0@HE1PR1001MB1306.EURPRD10.PROD.OUTLOOK.COM>
2019-06-10 21:25   ` James-Adam Renquinha Henri
2019-06-12  9:01     ` Alan Hayward

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).