public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v4 0/5] arm: Add support for multiple stacks on Cortex-M
@ 2022-04-01  9:18 Christophe Lyon
  2022-04-01  9:18 ` [PATCH v4 1/5] gdb/arm: Fix prologue analysis to support vpush Christophe Lyon
                   ` (5 more replies)
  0 siblings, 6 replies; 8+ messages in thread
From: Christophe Lyon @ 2022-04-01  9:18 UTC (permalink / raw)
  To: gdb-patches; +Cc: torbjorn.svensson, yvan.roux, Christophe Lyon

This patch series introduces support for the multiple stack pointers
on v8-M architecture (MSP_S, MSP_NS, PSP_S, PSP_NS).

This enables to unwind through Secure vs Non-secure context switches.

Along the way, the first patch adds support to detect vpush
instructions in function prologues, which in particular is used in the
CMSE trampolines.

v4 addresses comments received about v2/v3 from Joel, as well as
others from Luis.

Christophe Lyon (5):
  gdb/arm: Fix prologue analysis to support vpush
  gdb/arm: Define MSP and PSP registers for M-Profile
  gdb/arm: Introduce arm_cache_init
  gdb/arm: Add support for multiple stack pointers on Cortex-M
  gdb/arm: Extend arm_m_addr_is_magic to support FNC_RETURN, add
    unwind-ns-to-s command

 gdb/NEWS                          |   5 +
 gdb/arch/arm.c                    |   6 +
 gdb/arch/arm.h                    |   1 +
 gdb/arm-tdep.c                    | 685 +++++++++++++++++++++++++-----
 gdb/arm-tdep.h                    |  10 +
 gdb/doc/gdb.texinfo               |  10 +
 gdb/features/Makefile             |   1 +
 gdb/features/arm/arm-m-system.c   |  15 +
 gdb/features/arm/arm-m-system.xml |  12 +
 gdb/features/arm/arm-secext.c     |  17 +
 gdb/features/arm/arm-secext.xml   |  15 +
 11 files changed, 678 insertions(+), 99 deletions(-)
 create mode 100644 gdb/features/arm/arm-m-system.c
 create mode 100644 gdb/features/arm/arm-m-system.xml
 create mode 100644 gdb/features/arm/arm-secext.c
 create mode 100644 gdb/features/arm/arm-secext.xml

-- 
2.17.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v4 1/5] gdb/arm: Fix prologue analysis to support vpush
  2022-04-01  9:18 [PATCH v4 0/5] arm: Add support for multiple stacks on Cortex-M Christophe Lyon
@ 2022-04-01  9:18 ` Christophe Lyon
  2022-04-01  9:18 ` [PATCH v4 2/5] gdb/arm: Define MSP and PSP registers for M-Profile Christophe Lyon
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Christophe Lyon @ 2022-04-01  9:18 UTC (permalink / raw)
  To: gdb-patches
  Cc: torbjorn.svensson, yvan.roux, Christophe Lyon, Christophe Lyon

From: Christophe Lyon <christophe.lyon@foss.st.com>

While working on adding support for Non-secure/Secure modes unwinding,
I noticed that the prologue analysis lacked support for vpush, which
is used for instance in the CMSE stub routine.

This patch updates thumb_analyze_prologue accordingly, adding support
for vpush of D-registers.

Signed-off-by: Christophe Lyon <christophe.lyon@foss.st.com>
Signed-off-by: Christophe Lyon <christophe.lyon@arm.com>
---
 gdb/arm-tdep.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index d36856e1f3b..9e623057e4a 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -896,6 +896,35 @@ thumb_analyze_prologue (struct gdbarch *gdbarch,
 		regs[bits (insn, 0, 3)] = addr;
 	    }
 
+	  /* vstmdb Rn{!}, { D-registers } (aka vpush).  */
+	  else if ((insn & 0xff20) == 0xed20
+		   && (inst2 & 0x0f00) == 0x0b00
+		   && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+	    {
+	      /* Address SP points to.  */
+	      pv_t addr = regs[bits (insn, 0, 3)];
+
+	      /* Number of registers saved.  */
+	      unsigned int number = bits (inst2, 0, 7) >> 1;
+
+	      /* First register to save.  */
+	      int vd = bits (inst2, 12, 15) | (bits (insn, 6, 6) << 4);
+
+	      if (stack.store_would_trash (addr))
+		break;
+
+	      /* Calculate offsets of saved registers.  */
+	      for (; number > 0; number--)
+		{
+		  addr = pv_add_constant (addr, -8);
+		  stack.store (addr, 8, pv_register (ARM_D0_REGNUM
+						     + vd + number, 0));
+		}
+
+	      /* Writeback SP to account for the saved registers.  */
+	      regs[bits (insn, 0, 3)] = addr;
+	    }
+
 	  else if ((insn & 0xff50) == 0xe940	/* strd Rt, Rt2,
 						   [Rn, #+/-imm]{!} */
 		   && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
-- 
2.17.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v4 2/5] gdb/arm: Define MSP and PSP registers for M-Profile
  2022-04-01  9:18 [PATCH v4 0/5] arm: Add support for multiple stacks on Cortex-M Christophe Lyon
  2022-04-01  9:18 ` [PATCH v4 1/5] gdb/arm: Fix prologue analysis to support vpush Christophe Lyon
@ 2022-04-01  9:18 ` Christophe Lyon
  2022-04-01  9:18 ` [PATCH v4 3/5] gdb/arm: Introduce arm_cache_init Christophe Lyon
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Christophe Lyon @ 2022-04-01  9:18 UTC (permalink / raw)
  To: gdb-patches
  Cc: torbjorn.svensson, yvan.roux, Christophe Lyon, Christophe Lyon,
	Christophe Lyon

From: Christophe Lyon <chrlyo01@e109359-lin.cambridge.arm.com>

This patch removes the hardcoded access to PSP in
arm_m_exception_cache() and relies on the definition with the XML
descriptions.

Signed-off-by: Christophe Lyon <christophe.lyon@foss.st.com>
Signed-off-by: Christophe Lyon <christophe.lyon@arm.com>
---
 gdb/arch/arm.c                    |  6 +++
 gdb/arch/arm.h                    |  1 +
 gdb/arm-tdep.c                    | 80 ++++++++++++++++++++-----------
 gdb/arm-tdep.h                    |  3 ++
 gdb/features/Makefile             |  1 +
 gdb/features/arm/arm-m-system.c   | 15 ++++++
 gdb/features/arm/arm-m-system.xml | 12 +++++
 7 files changed, 89 insertions(+), 29 deletions(-)
 create mode 100644 gdb/features/arm/arm-m-system.c
 create mode 100644 gdb/features/arm/arm-m-system.xml

diff --git a/gdb/arch/arm.c b/gdb/arch/arm.c
index 126e46a950a..bc6e5ce3f09 100644
--- a/gdb/arch/arm.c
+++ b/gdb/arch/arm.c
@@ -28,6 +28,7 @@
 #include "../features/arm/arm-m-profile.c"
 #include "../features/arm/arm-m-profile-with-fpa.c"
 #include "../features/arm/arm-m-profile-mve.c"
+#include "../features/arm/arm-m-system.c"
 
 /* See arm.h.  */
 
@@ -446,6 +447,11 @@ arm_create_mprofile_target_description (arm_m_profile_type m_type)
       regnum = create_feature_arm_arm_m_profile_mve (tdesc, regnum);
       break;
 
+    case ARM_M_TYPE_SYSTEM:
+      regnum = create_feature_arm_arm_m_profile (tdesc, regnum);
+      regnum = create_feature_arm_arm_m_system (tdesc, regnum);
+      break;
+
     default:
       error (_("Invalid Arm M type: %d"), m_type);
     }
diff --git a/gdb/arch/arm.h b/gdb/arch/arm.h
index f75470e7572..b8f38e61694 100644
--- a/gdb/arch/arm.h
+++ b/gdb/arch/arm.h
@@ -92,6 +92,7 @@ enum arm_m_profile_type {
    ARM_M_TYPE_VFP_D16,
    ARM_M_TYPE_WITH_FPA,
    ARM_M_TYPE_MVE,
+   ARM_M_TYPE_SYSTEM,
    ARM_M_TYPE_INVALID
 };
 
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 9e623057e4a..5faab6b6da6 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -3018,9 +3018,9 @@ 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);
+  arm_gdbarch_tdep *tdep = (arm_gdbarch_tdep *) gdbarch_tdep (gdbarch);
   struct arm_prologue_cache *cache;
   CORE_ADDR lr;
-  CORE_ADDR sp;
   CORE_ADDR unwound_sp;
   LONGEST xpsr;
   uint32_t exc_return;
@@ -3036,7 +3036,6 @@ arm_m_exception_cache (struct frame_info *this_frame)
      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);
@@ -3045,37 +3044,13 @@ arm_m_exception_cache (struct frame_info *this_frame)
   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);
-	}
+      /* Thread (process) stack used, use PSP as SP.  */
+      unwound_sp = get_frame_register_unsigned (this_frame, tdep->m_profile_psp_regnum);
     }
   else
     {
       /* Main stack used, use MSP as SP.  */
-      unwound_sp = sp;
+      unwound_sp = get_frame_register_unsigned (this_frame, tdep->m_profile_msp_regnum);
     }
 
   /* The hardware saves eight 32-bit words, comprising xPSR,
@@ -9043,6 +9018,10 @@ arm_register_g_packet_guesses (struct gdbarch *gdbarch)
       register_remote_g_packet_guess (gdbarch, ARM_CORE_REGS_SIZE
 				      + ARM_VFP2_REGS_SIZE
 				      + ARM_INT_REGISTER_SIZE, tdesc);
+
+      /* M-profile system (stack pointers).  */
+      tdesc = arm_read_mprofile_description (ARM_M_TYPE_SYSTEM);
+      register_remote_g_packet_guess (gdbarch, 2 * ARM_INT_REGISTER_SIZE, tdesc);
     }
 
   /* Otherwise we don't have a useful guess.  */
@@ -9103,6 +9082,9 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   bool have_mve = false;
   int mve_vpr_regnum = -1;
   int register_count = ARM_NUM_REGS;
+  bool have_m_profile_msp = false;
+  int m_profile_msp_regnum = -1;
+  int m_profile_psp_regnum = -1;
 
   /* If we have an object to base this architecture on, try to determine
      its ABI.  */
@@ -9305,6 +9287,35 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
       if (!valid_p)
 	return NULL;
 
+      if (is_m)
+	{
+	  feature = tdesc_find_feature (tdesc,
+					"org.gnu.gdb.arm.m-system");
+	  if (feature != nullptr)
+	    {
+	      /* MSP */
+	      valid_p &= tdesc_numbered_register (feature, tdesc_data.get (),
+						  register_count, "msp");
+	      if (!valid_p)
+		{
+		  warning (_("M-profile m-system feature is missing required register msp."));
+		  return nullptr;
+		}
+	      have_m_profile_msp = true;
+	      m_profile_msp_regnum = register_count++;
+
+	      /* PSP */
+	      valid_p &= tdesc_numbered_register (feature, tdesc_data.get (),
+						  register_count, "psp");
+	      if (!valid_p)
+		{
+		  warning (_("M-profile m-system feature is missing required register psp."));
+		  return nullptr;
+		}
+	      m_profile_psp_regnum = register_count++;
+	    }
+	}
+
       feature = tdesc_find_feature (tdesc,
 				    "org.gnu.gdb.arm.fpa");
       if (feature != NULL)
@@ -9504,6 +9515,13 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
       tdep->mve_vpr_regnum = mve_vpr_regnum;
     }
 
+  /* Adjust the M-profile stack pointers settings.  */
+  if (have_m_profile_msp)
+    {
+      tdep->m_profile_msp_regnum = m_profile_msp_regnum;
+      tdep->m_profile_psp_regnum = m_profile_psp_regnum;
+    }
+
   arm_register_g_packet_guesses (gdbarch);
 
   /* Breakpoints.  */
@@ -9776,6 +9794,10 @@ arm_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file)
 		    tdep->mve_pseudo_base);
   fprintf_filtered (file, _("arm_dump_tdep: mve_pseudo_count = %i\n"),
 		    tdep->mve_pseudo_count);
+  fprintf_filtered (file, _("arm_dump_tdep: m_profile_msp_regnum = %i\n"),
+		    tdep->m_profile_msp_regnum);
+  fprintf_filtered (file, _("arm_dump_tdep: m_profile_psp_regnum = %i\n"),
+		    tdep->m_profile_psp_regnum);
   fprintf_filtered (file, _("arm_dump_tdep: Lowest pc = 0x%lx\n"),
 		    (unsigned long) tdep->lowest_pc);
 }
diff --git a/gdb/arm-tdep.h b/gdb/arm-tdep.h
index 8a9f618539f..c6aaf929a8a 100644
--- a/gdb/arm-tdep.h
+++ b/gdb/arm-tdep.h
@@ -119,6 +119,9 @@ struct arm_gdbarch_tdep : gdbarch_tdep
   int mve_pseudo_base = 0;	/* Number of the first MVE pseudo register.  */
   int mve_pseudo_count = 0;	/* Total number of MVE pseudo registers.  */
 
+  int m_profile_msp_regnum = 0;	/* M-profile MSP register number.  */
+  int m_profile_psp_regnum = 0;	/* M-profile PSP register number.  */
+
   bool is_m = false;		/* Does the target follow the "M" profile.  */
   CORE_ADDR lowest_pc = 0;	/* Lowest address at which instructions
 				   will appear.  */
diff --git a/gdb/features/Makefile b/gdb/features/Makefile
index 68e17d0085d..9883c21860c 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -206,6 +206,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
 	arm/arm-fpa.xml \
 	arm/arm-m-profile.xml \
 	arm/arm-m-profile-mve.xml \
+	arm/arm-m-system.xml \
 	arm/arm-m-profile-with-fpa.xml \
 	arm/arm-vfpv2.xml \
 	arm/arm-vfpv3.xml \
diff --git a/gdb/features/arm/arm-m-system.c b/gdb/features/arm/arm-m-system.c
new file mode 100644
index 00000000000..3fb20a57198
--- /dev/null
+++ b/gdb/features/arm/arm-m-system.c
@@ -0,0 +1,15 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: arm-m-system.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_arm_arm_m_system (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.arm.m-system");
+  tdesc_create_reg (feature, "msp", regnum++, 1, NULL, 32, "data_ptr");
+  tdesc_create_reg (feature, "psp", regnum++, 1, NULL, 32, "data_ptr");
+  return regnum;
+}
diff --git a/gdb/features/arm/arm-m-system.xml b/gdb/features/arm/arm-m-system.xml
new file mode 100644
index 00000000000..eb167adb21a
--- /dev/null
+++ b/gdb/features/arm/arm-m-system.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2022 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.arm.m-system">
+  <reg name="msp" bitsize="32" type="data_ptr"/>
+  <reg name="psp" bitsize="32" type="data_ptr"/>
+</feature>
-- 
2.17.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v4 3/5] gdb/arm: Introduce arm_cache_init
  2022-04-01  9:18 [PATCH v4 0/5] arm: Add support for multiple stacks on Cortex-M Christophe Lyon
  2022-04-01  9:18 ` [PATCH v4 1/5] gdb/arm: Fix prologue analysis to support vpush Christophe Lyon
  2022-04-01  9:18 ` [PATCH v4 2/5] gdb/arm: Define MSP and PSP registers for M-Profile Christophe Lyon
@ 2022-04-01  9:18 ` Christophe Lyon
  2022-04-01  9:18 ` [PATCH v4 4/5] gdb/arm: Add support for multiple stack pointers on Cortex-M Christophe Lyon
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Christophe Lyon @ 2022-04-01  9:18 UTC (permalink / raw)
  To: gdb-patches
  Cc: torbjorn.svensson, yvan.roux, Christophe Lyon, Christophe Lyon

From: Christophe Lyon <christophe.lyon@foss.st.com>

This patch is a preparation for the rest of the series and adds two
arm_cache_init helper functions. It updates every place that updates
cache->saved_regs to call the helper instead.

Signed-off-by: Torbjörn Svensson <torbjorn.svensson@st.com>
Signed-off-by: Christophe Lyon <christophe.lyon@foss.st.com>
Signed-off-by: Christophe Lyon <christophe.lyon@arm.com>
---
 gdb/arm-tdep.c | 32 ++++++++++++++++++++++++++------
 1 file changed, 26 insertions(+), 6 deletions(-)

diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 5faab6b6da6..0e442e64b36 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -290,6 +290,25 @@ struct arm_prologue_cache
   trad_frame_saved_reg *saved_regs;
 };
 
+/* Initialize CACHE fields for which zero is not adequate (CACHE is
+   expected to have been ZALLOC'ed before calling this function).  */
+
+static void
+arm_cache_init (struct arm_prologue_cache *cache, struct gdbarch *gdbarch)
+{
+  cache->saved_regs = trad_frame_alloc_saved_regs (gdbarch);
+}
+
+/* Similar to the previous function, but extracts GDBARCH from FRAME.  */
+
+static void
+arm_cache_init (struct arm_prologue_cache *cache, struct frame_info *frame)
+{
+  struct gdbarch *gdbarch = get_frame_arch (frame);
+
+  arm_cache_init (cache, gdbarch);
+}
+
 namespace {
 
 /* Abstract class to read ARM instructions from memory.  */
@@ -1930,7 +1949,7 @@ arm_make_prologue_cache (struct frame_info *this_frame)
   CORE_ADDR unwound_fp;
 
   cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
-  cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
+  arm_cache_init (cache, this_frame);
 
   arm_scan_prologue (this_frame, cache);
 
@@ -2396,7 +2415,7 @@ arm_exidx_fill_cache (struct frame_info *this_frame, gdb_byte *entry)
 
   struct arm_prologue_cache *cache;
   cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
-  cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
+  arm_cache_init (cache, this_frame);
 
   for (;;)
     {
@@ -2790,7 +2809,7 @@ arm_make_epilogue_frame_cache (struct frame_info *this_frame)
   int reg;
 
   cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
-  cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
+  arm_cache_init (cache, this_frame);
 
   /* Still rely on the offset calculated from prologue.  */
   arm_scan_prologue (this_frame, cache);
@@ -2951,7 +2970,7 @@ arm_make_stub_cache (struct frame_info *this_frame)
   struct arm_prologue_cache *cache;
 
   cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
-  cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
+  arm_cache_init (cache, this_frame);
 
   cache->prev_sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM);
 
@@ -3029,7 +3048,7 @@ arm_m_exception_cache (struct frame_info *this_frame)
   uint32_t secure_stack_used;
 
   cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
-  cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
+  arm_cache_init (cache, this_frame);
 
   /* ARMv7-M Architecture Reference "B1.5.6 Exception entry behavior"
      describes which bits in LR that define which stack was used prior
@@ -13621,7 +13640,8 @@ arm_analyze_prologue_test ()
 
       test_arm_instruction_reader mem_reader (insns);
       arm_prologue_cache cache;
-      cache.saved_regs = trad_frame_alloc_saved_regs (gdbarch);
+      memset (&cache, 0, sizeof (arm_prologue_cache));
+      arm_cache_init (&cache, gdbarch);
 
       arm_analyze_prologue (gdbarch, 0, sizeof (insns) - 1, &cache, mem_reader);
     }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v4 4/5] gdb/arm: Add support for multiple stack pointers on Cortex-M
  2022-04-01  9:18 [PATCH v4 0/5] arm: Add support for multiple stacks on Cortex-M Christophe Lyon
                   ` (2 preceding siblings ...)
  2022-04-01  9:18 ` [PATCH v4 3/5] gdb/arm: Introduce arm_cache_init Christophe Lyon
@ 2022-04-01  9:18 ` Christophe Lyon
  2022-04-01  9:18 ` [PATCH v4 5/5] gdb/arm: Extend arm_m_addr_is_magic to support FNC_RETURN, add unwind-ns-to-s command Christophe Lyon
  2022-04-01  9:33 ` [PATCH v4 0/5] arm: Add support for multiple stacks on Cortex-M Christophe Lyon
  5 siblings, 0 replies; 8+ messages in thread
From: Christophe Lyon @ 2022-04-01  9:18 UTC (permalink / raw)
  To: gdb-patches
  Cc: torbjorn.svensson, yvan.roux, Christophe Lyon, Christophe Lyon,
	Christophe Lyon

From: Christophe Lyon <chrlyo01@e109359-lin.cambridge.arm.com>

Armv8-M architecture with Security extension features four stack pointers
to handle Secure and Non-secure modes.

This patch adds support to switch between them as needed during
unwinding, and replaces all updates of cache->prev_sp with calls to
arm_cache_set_prev_sp.

Signed-off-by: Torbjörn Svensson <torbjorn.svensson@st.com>
Signed-off-by: Christophe Lyon <christophe.lyon@foss.st.com>
Signed-off-by: Christophe Lyon <christophe.lyon@arm.com>
---
 gdb/arm-tdep.c                  | 266 +++++++++++++++++++++++++++++---
 gdb/arm-tdep.h                  |  11 +-
 gdb/features/arm/arm-secext.c   |  17 ++
 gdb/features/arm/arm-secext.xml |  15 ++
 4 files changed, 289 insertions(+), 20 deletions(-)
 create mode 100644 gdb/features/arm/arm-secext.c
 create mode 100644 gdb/features/arm/arm-secext.xml

diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 0e442e64b36..78a5d766b8c 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -275,7 +275,18 @@ struct arm_prologue_cache
   /* The stack pointer at the time this frame was created; i.e. the
      caller's stack pointer when this function was called.  It is used
      to identify this frame.  */
-  CORE_ADDR prev_sp;
+  CORE_ADDR sp;
+
+  /* Additional stack pointers used by M-profile with Security extension.  */
+  /* Use msp_s / psp_s to hold the values of msp / psp when there is
+     no Security extension.  */
+  CORE_ADDR msp_s;
+  CORE_ADDR msp_ns;
+  CORE_ADDR psp_s;
+  CORE_ADDR psp_ns;
+
+  /* Active stack pointer.  */
+  int active_sp_regnum;
 
   /* The frame base for this frame is just prev_sp - frame size.
      FRAMESIZE is the distance from the frame pointer to the
@@ -290,12 +301,28 @@ struct arm_prologue_cache
   trad_frame_saved_reg *saved_regs;
 };
 
+/* Initialize stack pointers, and flag the active one.  */
+
+static inline void arm_cache_init_sp (int regnum, CORE_ADDR* member,
+				      struct arm_prologue_cache *cache,
+				      struct frame_info *frame)
+{
+  CORE_ADDR val = get_frame_register_unsigned (frame, regnum);
+  if (val == cache->sp)
+    {
+      cache->active_sp_regnum = regnum;
+    }
+  *member = val;
+}
+
 /* Initialize CACHE fields for which zero is not adequate (CACHE is
    expected to have been ZALLOC'ed before calling this function).  */
 
 static void
 arm_cache_init (struct arm_prologue_cache *cache, struct gdbarch *gdbarch)
 {
+  cache->active_sp_regnum = ARM_SP_REGNUM;
+
   cache->saved_regs = trad_frame_alloc_saved_regs (gdbarch);
 }
 
@@ -305,8 +332,110 @@ static void
 arm_cache_init (struct arm_prologue_cache *cache, struct frame_info *frame)
 {
   struct gdbarch *gdbarch = get_frame_arch (frame);
+  arm_gdbarch_tdep *tdep = (arm_gdbarch_tdep *) gdbarch_tdep (gdbarch);
 
   arm_cache_init (cache, gdbarch);
+
+  if (tdep->have_sec_ext)
+    {
+      arm_cache_init_sp (tdep->m_profile_msp_s_regnum, &cache->msp_s, cache, frame);
+      arm_cache_init_sp (tdep->m_profile_psp_s_regnum, &cache->psp_s, cache, frame);
+      arm_cache_init_sp (tdep->m_profile_msp_ns_regnum, &cache->msp_ns, cache, frame);
+      arm_cache_init_sp (tdep->m_profile_psp_ns_regnum, &cache->psp_ns, cache, frame);
+      /* Use MSP_S as default stack pointer.  */
+      if (cache->active_sp_regnum == ARM_SP_REGNUM)
+	  cache->active_sp_regnum = tdep->m_profile_msp_s_regnum;
+    }
+  else if (tdep->is_m)
+    {
+      arm_cache_init_sp (tdep->m_profile_msp_regnum, &cache->msp_s, cache, frame);
+      arm_cache_init_sp (tdep->m_profile_psp_regnum, &cache->psp_s, cache, frame);
+    }
+  else
+    {
+      arm_cache_init_sp (ARM_SP_REGNUM, &cache->msp_s, cache, frame);
+    }
+}
+
+/* Return the requested stack pointer value (in REGNUM), taking into
+   account whether we have a Security extension or an M-profile
+   CPU.  */
+
+static CORE_ADDR
+arm_cache_get_sp_register (struct arm_prologue_cache *cache,
+			   arm_gdbarch_tdep *tdep, int regnum)
+{
+  if (regnum == ARM_SP_REGNUM)
+    return cache->sp;
+
+  if (tdep->have_sec_ext)
+    {
+      if (regnum == tdep->m_profile_msp_s_regnum)
+	return cache->msp_s;
+      if (regnum == tdep->m_profile_msp_ns_regnum)
+	return cache->msp_ns;
+      if (regnum == tdep->m_profile_psp_s_regnum)
+	return cache->psp_s;
+      if (regnum == tdep->m_profile_psp_ns_regnum)
+	return cache->psp_ns;
+    }
+  else if (tdep->is_m)
+    {
+      if (regnum == tdep->m_profile_msp_regnum)
+	return cache->msp_s;
+      if (regnum == tdep->m_profile_psp_regnum)
+	return cache->psp_s;
+    }
+
+  gdb_assert_not_reached ("Invalid SP selection");
+}
+
+/* Return the previous stack address, depending on which SP register
+   is active.  */
+
+static CORE_ADDR
+arm_cache_get_prev_sp_value (struct arm_prologue_cache *cache, arm_gdbarch_tdep *tdep)
+{
+  CORE_ADDR val = arm_cache_get_sp_register (cache, tdep, cache->active_sp_regnum);
+  return val;
+}
+
+/* Set the active stack pointer to VAL.  */
+
+static void
+arm_cache_set_active_sp_value (struct arm_prologue_cache *cache,
+			       arm_gdbarch_tdep *tdep, CORE_ADDR val)
+{
+  if (cache->active_sp_regnum == ARM_SP_REGNUM)
+    {
+      cache->sp = val;
+      return;
+    }
+
+  if (tdep->have_sec_ext)
+    {
+      if (cache->active_sp_regnum == tdep->m_profile_msp_s_regnum)
+	cache->msp_s = val;
+      else if (cache->active_sp_regnum == tdep->m_profile_msp_ns_regnum)
+	cache->msp_ns = val;
+      else if (cache->active_sp_regnum == tdep->m_profile_psp_s_regnum)
+	cache->psp_s = val;
+      else if (cache->active_sp_regnum == tdep->m_profile_psp_ns_regnum)
+	cache->psp_ns = val;
+
+      return;
+    }
+  else if (tdep->is_m)
+    {
+      if (cache->active_sp_regnum == tdep->m_profile_msp_regnum)
+	cache->msp_s = val;
+      else if (cache->active_sp_regnum == tdep->m_profile_psp_regnum)
+	cache->psp_s = val;
+
+      return;
+    }
+
+  gdb_assert_not_reached ("Invalid SP selection");
 }
 
 namespace {
@@ -1957,14 +2086,17 @@ arm_make_prologue_cache (struct frame_info *this_frame)
   if (unwound_fp == 0)
     return cache;
 
-  cache->prev_sp = unwound_fp + cache->framesize;
+  arm_gdbarch_tdep *tdep =
+    (arm_gdbarch_tdep *) gdbarch_tdep (get_frame_arch (this_frame));
+
+  arm_cache_set_active_sp_value (cache, tdep, 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 (cache->saved_regs[reg].is_addr ())
       cache->saved_regs[reg].set_addr (cache->saved_regs[reg].addr ()
-				       + cache->prev_sp);
+				       + arm_cache_get_prev_sp_value (cache, tdep));
 
   return cache;
 }
@@ -1990,7 +2122,7 @@ arm_prologue_unwind_stop_reason (struct frame_info *this_frame,
     return UNWIND_OUTERMOST;
 
   /* If we've hit a wall, stop.  */
-  if (cache->prev_sp == 0)
+  if (arm_cache_get_prev_sp_value (cache, tdep) == 0)
     return UNWIND_OUTERMOST;
 
   return UNWIND_NO_REASON;
@@ -2012,6 +2144,9 @@ arm_prologue_this_id (struct frame_info *this_frame,
     *this_cache = arm_make_prologue_cache (this_frame);
   cache = (struct arm_prologue_cache *) *this_cache;
 
+  arm_gdbarch_tdep *tdep
+    = (arm_gdbarch_tdep *) gdbarch_tdep (get_frame_arch (this_frame));
+
   /* 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.  */
@@ -2020,7 +2155,7 @@ arm_prologue_this_id (struct frame_info *this_frame,
   if (!func)
     func = pc;
 
-  id = frame_id_build (cache->prev_sp, func);
+  id = frame_id_build (arm_cache_get_prev_sp_value (cache, tdep), func);
   *this_id = id;
 }
 
@@ -2030,6 +2165,7 @@ arm_prologue_prev_register (struct frame_info *this_frame,
 			    int prev_regnum)
 {
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  arm_gdbarch_tdep *tdep = (arm_gdbarch_tdep *) gdbarch_tdep (gdbarch);
   struct arm_prologue_cache *cache;
 
   if (*this_cache == NULL)
@@ -2054,7 +2190,8 @@ arm_prologue_prev_register (struct frame_info *this_frame,
      identified by the next frame's stack pointer at the time of the call.
      The value was already reconstructed into PREV_SP.  */
   if (prev_regnum == ARM_SP_REGNUM)
-    return frame_unwind_got_constant (this_frame, prev_regnum, cache->prev_sp);
+    return frame_unwind_got_constant (this_frame, prev_regnum,
+				      arm_cache_get_prev_sp_value (cache, tdep));
 
   /* The CPSR may have been changed by the call instruction and by the
      called function.  The only bit we can reconstruct is the T bit,
@@ -2692,7 +2829,9 @@ arm_exidx_fill_cache (struct frame_info *this_frame, gdb_byte *entry)
     = vsp - get_frame_register_unsigned (this_frame, cache->framereg);
 
   /* We already got the previous SP.  */
-  cache->prev_sp = vsp;
+  arm_gdbarch_tdep *tdep
+    = (arm_gdbarch_tdep *) gdbarch_tdep (get_frame_arch (this_frame));
+  arm_cache_set_active_sp_value (cache, tdep, vsp);
 
   return cache;
 }
@@ -2815,14 +2954,18 @@ arm_make_epilogue_frame_cache (struct frame_info *this_frame)
   arm_scan_prologue (this_frame, cache);
 
   /* Since we are in epilogue, the SP has been restored.  */
-  cache->prev_sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM);
+  arm_gdbarch_tdep *tdep
+    = (arm_gdbarch_tdep *) gdbarch_tdep (get_frame_arch (this_frame));
+  arm_cache_set_active_sp_value (cache, tdep,
+				 get_frame_register_unsigned (this_frame,
+							      ARM_SP_REGNUM));
 
   /* 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 (cache->saved_regs[reg].is_addr ())
       cache->saved_regs[reg].set_addr (cache->saved_regs[reg].addr ()
-				       + cache->prev_sp);
+				       + arm_cache_get_prev_sp_value (cache, tdep));
 
   return cache;
 }
@@ -2850,7 +2993,9 @@ arm_epilogue_frame_this_id (struct frame_info *this_frame,
   if (func == 0)
     func = pc;
 
-  (*this_id) = frame_id_build (cache->prev_sp, pc);
+  arm_gdbarch_tdep *tdep
+    = (arm_gdbarch_tdep *) gdbarch_tdep (get_frame_arch (this_frame));
+  *this_id = frame_id_build (arm_cache_get_prev_sp_value (cache, tdep), pc);
 }
 
 /* Implementation of function hook 'prev_register' in
@@ -2972,7 +3117,11 @@ arm_make_stub_cache (struct frame_info *this_frame)
   cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
   arm_cache_init (cache, this_frame);
 
-  cache->prev_sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM);
+  arm_gdbarch_tdep *tdep
+    = (arm_gdbarch_tdep *) gdbarch_tdep (get_frame_arch (this_frame));
+  arm_cache_set_active_sp_value (cache, tdep,
+				 get_frame_register_unsigned (this_frame,
+							      ARM_SP_REGNUM));
 
   return cache;
 }
@@ -2990,7 +3139,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));
+  arm_gdbarch_tdep *tdep
+    = (arm_gdbarch_tdep *) gdbarch_tdep (get_frame_arch (this_frame));
+  *this_id = frame_id_build (arm_cache_get_prev_sp_value (cache, tdep),
+			     get_frame_pc (this_frame));
 }
 
 static int
@@ -3111,12 +3263,12 @@ arm_m_exception_cache (struct frame_info *this_frame)
       cache->saved_regs[ARM_FPSCR_REGNUM].set_addr (unwound_sp + 0x60);
 
       /* Offset 0x64 is reserved.  */
-      cache->prev_sp = unwound_sp + 0x68;
+      arm_cache_set_active_sp_value (cache, tdep, unwound_sp + 0x68);
     }
   else
     {
       /* Standard stack frame type used.  */
-      cache->prev_sp = unwound_sp + 0x20;
+      arm_cache_set_active_sp_value (cache, tdep, unwound_sp + 0x20);
     }
 
   /* Check EXC_RETURN bit S if Secure or Non-secure stack used.  */
@@ -3138,7 +3290,8 @@ arm_m_exception_cache (struct frame_info *this_frame)
      previous context's stack pointer.  */
   if (safe_read_memory_integer (unwound_sp + 28, 4, byte_order, &xpsr)
       && (xpsr & (1 << 9)) != 0)
-    cache->prev_sp += 4;
+    arm_cache_set_active_sp_value (cache, tdep,
+				   arm_cache_get_prev_sp_value (cache, tdep) + 4);
 
   return cache;
 }
@@ -3158,7 +3311,9 @@ arm_m_exception_this_id (struct frame_info *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,
+  arm_gdbarch_tdep *tdep
+    = (arm_gdbarch_tdep *) gdbarch_tdep (get_frame_arch (this_frame));
+  *this_id = frame_id_build (arm_cache_get_prev_sp_value (cache, tdep),
 			     get_frame_pc (this_frame));
 }
 
@@ -3177,9 +3332,11 @@ arm_m_exception_prev_register (struct frame_info *this_frame,
   cache = (struct arm_prologue_cache *) *this_cache;
 
   /* The value was already reconstructed into PREV_SP.  */
+  arm_gdbarch_tdep *tdep
+    = (arm_gdbarch_tdep *) gdbarch_tdep (get_frame_arch (this_frame));
   if (prev_regnum == ARM_SP_REGNUM)
     return frame_unwind_got_constant (this_frame, prev_regnum,
-				      cache->prev_sp);
+				      arm_cache_get_prev_sp_value (cache, tdep));
 
   return trad_frame_get_prev_register (this_frame, cache->saved_regs,
 				       prev_regnum);
@@ -3224,7 +3381,9 @@ arm_normal_frame_base (struct frame_info *this_frame, void **this_cache)
     *this_cache = arm_make_prologue_cache (this_frame);
   cache = (struct arm_prologue_cache *) *this_cache;
 
-  return cache->prev_sp - cache->framesize;
+  arm_gdbarch_tdep *tdep
+    = (arm_gdbarch_tdep *) gdbarch_tdep (get_frame_arch (this_frame));
+  return arm_cache_get_prev_sp_value (cache, tdep) - cache->framesize;
 }
 
 struct frame_base arm_normal_base = {
@@ -9091,6 +9250,7 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   tdesc_arch_data_up tdesc_data;
   int i;
   bool is_m = false;
+  bool have_sec_ext = false;
   int vfp_register_count = 0;
   bool have_s_pseudos = false, have_q_pseudos = false;
   bool have_wmmx_registers = false;
@@ -9104,6 +9264,10 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   bool have_m_profile_msp = false;
   int m_profile_msp_regnum = -1;
   int m_profile_psp_regnum = -1;
+  int m_profile_msp_ns_regnum = -1;
+  int m_profile_psp_ns_regnum = -1;
+  int m_profile_msp_s_regnum = -1;
+  int m_profile_psp_s_regnum = -1;
 
   /* If we have an object to base this architecture on, try to determine
      its ABI.  */
@@ -9476,6 +9640,59 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 	      if (have_vfp)
 		have_q_pseudos = true;
 	    }
+
+	  /* Do we have the Security extension?  */
+	  feature = tdesc_find_feature (tdesc,
+					"org.gnu.gdb.arm.secext");
+	  if (feature != nullptr)
+	    {
+	      /* Secure/Non-secure stack pointers.  */
+	      /* MSP_NS */
+	      valid_p &= tdesc_numbered_register (feature, tdesc_data.get (),
+						 register_count, "msp_ns");
+	      if (!valid_p)
+		{
+		  warning (_("M-profile secext feature is missing required register msp_ns."));
+		  return nullptr;
+		}
+	      m_profile_msp_ns_regnum = register_count++;
+
+	      /* PSP_NS */
+	      valid_p &= tdesc_numbered_register (feature, tdesc_data.get (),
+						 register_count, "psp_ns");
+	      if (!valid_p)
+		{
+		  warning (_("M-profile secext feature is missing required register psp_ns."));
+		  return nullptr;
+		}
+	      m_profile_psp_ns_regnum = register_count++;
+
+	      /* MSP_S */
+	      valid_p &= tdesc_numbered_register (feature, tdesc_data.get (),
+						 register_count, "msp_s");
+	      if (!valid_p)
+		{
+		  warning (_("M-profile secext feature is missing required register msp_s."));
+		  return nullptr;
+		}
+	      m_profile_msp_s_regnum = register_count++;
+
+	      /* PSP_S */
+	      valid_p &= tdesc_numbered_register (feature, tdesc_data.get (),
+						 register_count, "psp_s");
+	      if (!valid_p)
+		{
+		  warning (_("M-profile secext feature is missing required register psp_s."));
+		  return nullptr;
+		}
+	      m_profile_psp_s_regnum = register_count++;
+
+	      if (!valid_p)
+		return nullptr;
+
+	      have_sec_ext = true;
+	    }
+
 	}
     }
 
@@ -9517,6 +9734,7 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   tdep->arm_abi = arm_abi;
   tdep->fp_model = fp_model;
   tdep->is_m = is_m;
+  tdep->have_sec_ext = have_sec_ext;
   tdep->have_fpa_registers = have_fpa_registers;
   tdep->have_wmmx_registers = have_wmmx_registers;
   gdb_assert (vfp_register_count == 0
@@ -9539,6 +9757,10 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
     {
       tdep->m_profile_msp_regnum = m_profile_msp_regnum;
       tdep->m_profile_psp_regnum = m_profile_psp_regnum;
+      tdep->m_profile_msp_ns_regnum = m_profile_msp_ns_regnum;
+      tdep->m_profile_psp_ns_regnum = m_profile_psp_ns_regnum;
+      tdep->m_profile_msp_s_regnum = m_profile_msp_s_regnum;
+      tdep->m_profile_psp_s_regnum = m_profile_psp_s_regnum;
     }
 
   arm_register_g_packet_guesses (gdbarch);
@@ -9817,6 +10039,14 @@ arm_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file)
 		    tdep->m_profile_msp_regnum);
   fprintf_filtered (file, _("arm_dump_tdep: m_profile_psp_regnum = %i\n"),
 		    tdep->m_profile_psp_regnum);
+  fprintf_filtered (file, _("arm_dump_tdep: m_profile_msp_ns_regnum = %i\n"),
+		    tdep->m_profile_msp_ns_regnum);
+  fprintf_filtered (file, _("arm_dump_tdep: m_profile_psp_ns_regnum = %i\n"),
+		    tdep->m_profile_psp_ns_regnum);
+  fprintf_filtered (file, _("arm_dump_tdep: m_profile_msp_s_regnum = %i\n"),
+		    tdep->m_profile_msp_s_regnum);
+  fprintf_filtered (file, _("arm_dump_tdep: m_profile_psp_s_regnum = %i\n"),
+		    tdep->m_profile_psp_s_regnum);
   fprintf_filtered (file, _("arm_dump_tdep: Lowest pc = 0x%lx\n"),
 		    (unsigned long) tdep->lowest_pc);
 }
diff --git a/gdb/arm-tdep.h b/gdb/arm-tdep.h
index c6aaf929a8a..b77d272f128 100644
--- a/gdb/arm-tdep.h
+++ b/gdb/arm-tdep.h
@@ -119,10 +119,17 @@ struct arm_gdbarch_tdep : gdbarch_tdep
   int mve_pseudo_base = 0;	/* Number of the first MVE pseudo register.  */
   int mve_pseudo_count = 0;	/* Total number of MVE pseudo registers.  */
 
-  int m_profile_msp_regnum = 0;	/* M-profile MSP register number.  */
-  int m_profile_psp_regnum = 0;	/* M-profile PSP register number.  */
+  int m_profile_msp_regnum = ARM_SP_REGNUM;	/* M-profile MSP register number.  */
+  int m_profile_psp_regnum = ARM_SP_REGNUM;	/* M-profile PSP register number.  */
+
+  /* Secure and Non-secure stack pointers with security extension.  */
+  int m_profile_msp_ns_regnum = ARM_SP_REGNUM;	/* M-profile MSP_NS register number.  */
+  int m_profile_psp_ns_regnum = ARM_SP_REGNUM;	/* M-profile PSP_NS register number.  */
+  int m_profile_msp_s_regnum = ARM_SP_REGNUM;	/* M-profile MSP_S register number.  */
+  int m_profile_psp_s_regnum = ARM_SP_REGNUM;	/* M-profile PSP_S register number.  */
 
   bool is_m = false;		/* Does the target follow the "M" profile.  */
+  bool have_sec_ext = false;	/* Do we have security extensions?  */
   CORE_ADDR lowest_pc = 0;	/* Lowest address at which instructions
 				   will appear.  */
 
diff --git a/gdb/features/arm/arm-secext.c b/gdb/features/arm/arm-secext.c
new file mode 100644
index 00000000000..39ef4afb05f
--- /dev/null
+++ b/gdb/features/arm/arm-secext.c
@@ -0,0 +1,17 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: arm-secext.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_arm_arm_m_system (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.arm.secext");
+  tdesc_create_reg (feature, "msp_ns", regnum++, 1, NULL, 32, "data_ptr");
+  tdesc_create_reg (feature, "psp_ns", regnum++, 1, NULL, 32, "data_ptr");
+  tdesc_create_reg (feature, "msp_s", regnum++, 1, NULL, 32, "data_ptr");
+  tdesc_create_reg (feature, "psp_s", regnum++, 1, NULL, 32, "data_ptr");
+  return regnum;
+}
diff --git a/gdb/features/arm/arm-secext.xml b/gdb/features/arm/arm-secext.xml
new file mode 100644
index 00000000000..52b2c20a982
--- /dev/null
+++ b/gdb/features/arm/arm-secext.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2022 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.arm.secext">
+  <!-- We have 4 stack pointers with the security extension.  -->
+  <reg name="msp_ns" bitsize="32" type="data_ptr"/>
+  <reg name="psp_ns" bitsize="32" type="data_ptr"/>
+  <reg name="msp_s" bitsize="32" type="data_ptr"/>
+  <reg name="psp_s" bitsize="32" type="data_ptr"/>
+</feature>
-- 
2.17.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v4 5/5] gdb/arm: Extend arm_m_addr_is_magic to support FNC_RETURN, add unwind-ns-to-s command
  2022-04-01  9:18 [PATCH v4 0/5] arm: Add support for multiple stacks on Cortex-M Christophe Lyon
                   ` (3 preceding siblings ...)
  2022-04-01  9:18 ` [PATCH v4 4/5] gdb/arm: Add support for multiple stack pointers on Cortex-M Christophe Lyon
@ 2022-04-01  9:18 ` Christophe Lyon
  2022-04-01  9:33 ` [PATCH v4 0/5] arm: Add support for multiple stacks on Cortex-M Christophe Lyon
  5 siblings, 0 replies; 8+ messages in thread
From: Christophe Lyon @ 2022-04-01  9:18 UTC (permalink / raw)
  To: gdb-patches
  Cc: torbjorn.svensson, yvan.roux, Christophe Lyon, Christophe Lyon

From: Christophe Lyon <christophe.lyon@foss.st.com>

This patch makes use of the support for several stack pointers
introduced by the previous patch to switch between them as needed
during unwinding.

It introduces a new 'unwind-ns-to-s' arm command to enable/disable
mode switching during unwinding. It is enabled by default.

It has been tested using an STM32L5 board (with cortex-m33) and the
sample applications shipped with the STM32Cube development
environment: GTZC_TZSC_MPCBB_TrustZone in
STM32CubeL5/Projects/NUCLEO-L552ZE-Q/Examples/GTZC.

The test consisted in setting breakpoints in various places and check
that the backtrace is correct: SecureFault_Callback (Non-secure mode),
__gnu_cmse_nonsecure_call (before and after the vpush instruction),
SecureFault_Handler (Secure mode).

This implies that we tested only some parts of this patch (only MSP*
were used), but remaining parts seem reasonable.

Signed-off-by: Torbjörn Svensson <torbjorn.svensson@st.com>
Signed-off-by: Christophe Lyon <christophe.lyon@foss.st.com>
Signed-off-by: Christophe Lyon <christophe.lyon@arm.com>
---
 gdb/NEWS            |   5 +
 gdb/arm-tdep.c      | 310 +++++++++++++++++++++++++++++++++++---------
 gdb/doc/gdb.texinfo |  10 ++
 3 files changed, 263 insertions(+), 62 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index dc2cac1871b..74920653f35 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -5102,6 +5102,11 @@ show arm force-mode
   the current CPSR value for instructions without symbols; previous
   versions of GDB behaved as if "set arm fallback-mode arm".
 
+set arm unwind-ns-to-s
+  Enable unwinding from Non-secure to Secure mode on Cortex-M with
+  Security extension.
+  This can trigger security exceptions when unwinding exception stacks.
+
 set disable-randomization
 show disable-randomization
   Standalone programs run with the virtual address space randomization enabled
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 78a5d766b8c..8edc9bb0c3d 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -438,6 +438,40 @@ arm_cache_set_active_sp_value (struct arm_prologue_cache *cache,
   gdb_assert_not_reached ("Invalid SP selection");
 }
 
+/* Return true if REGNUM is one of the stack pointers.  */
+
+static bool
+arm_cache_is_sp_register (struct arm_prologue_cache *cache,
+			  arm_gdbarch_tdep *tdep, int regnum)
+{
+  if ((regnum == ARM_SP_REGNUM)
+      || (regnum == tdep->m_profile_msp_regnum)
+      || (regnum == tdep->m_profile_msp_s_regnum)
+      || (regnum == tdep->m_profile_msp_ns_regnum)
+      || (regnum == tdep->m_profile_psp_regnum)
+      || (regnum == tdep->m_profile_psp_s_regnum)
+      || (regnum == tdep->m_profile_psp_ns_regnum))
+    return true;
+  else
+    return false;
+}
+
+/* Set the active stack pointer to SP_REGNUM.  */
+
+static void
+arm_cache_switch_prev_sp (struct arm_prologue_cache *cache,
+			  arm_gdbarch_tdep *tdep, int sp_regnum)
+{
+  gdb_assert (sp_regnum != ARM_SP_REGNUM);
+  gdb_assert (arm_cache_is_sp_register (cache, tdep, sp_regnum));
+
+  if (tdep->have_sec_ext)
+    gdb_assert (sp_regnum != tdep->m_profile_msp_regnum
+		&& sp_regnum != tdep->m_profile_psp_regnum);
+
+  cache->active_sp_regnum = sp_regnum;
+}
+
 namespace {
 
 /* Abstract class to read ARM instructions from memory.  */
@@ -474,6 +508,7 @@ static CORE_ADDR arm_analyze_prologue
 /* See arm-tdep.h.  */
 
 bool arm_apcs_32 = true;
+bool arm_unwind_ns_to_s = true;
 
 /* Return the bit mask in ARM_PS_REGNUM that indicates Thumb mode.  */
 
@@ -690,28 +725,43 @@ arm_pc_is_thumb (struct gdbarch *gdbarch, CORE_ADDR memaddr)
    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:
-      case 0xffffffe9:
-      case 0xffffffed:
-      case 0xfffffff1:
-      case 0xfffffff9:
-      case 0xfffffffd:
-	/* Address is magic.  */
-	return 1;
+arm_m_addr_is_magic (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  arm_gdbarch_tdep *tdep = (arm_gdbarch_tdep *) gdbarch_tdep (gdbarch);
+  if (tdep->have_sec_ext)
+    {
+      switch ((addr & 0xff000000))
+	{
+	case 0xff000000: /* EXC_RETURN pattern.  */
+	case 0xfe000000: /* FNC_RETURN pattern.  */
+	  return 1;
+	default:
+	  return 0;
+	}
+    }
+  else
+    {
+      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:
+	case 0xffffffe9:
+	case 0xffffffed:
+	case 0xfffffff1:
+	case 0xfffffff9:
+	case 0xfffffffd:
+	  /* Address is magic.  */
+	  return 1;
 
-      default:
-	/* Address is not magic.  */
-	return 0;
+	default:
+	  /* Address is not magic.  */
+	  return 0;
+	}
     }
 }
 
@@ -723,7 +773,7 @@ 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).  */
-  if (tdep->is_m && arm_m_addr_is_magic (val))
+  if (tdep->is_m && arm_m_addr_is_magic (gdbarch, val))
     return val;
 
   if (arm_apcs_32)
@@ -2167,6 +2217,7 @@ arm_prologue_prev_register (struct frame_info *this_frame,
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
   arm_gdbarch_tdep *tdep = (arm_gdbarch_tdep *) gdbarch_tdep (gdbarch);
   struct arm_prologue_cache *cache;
+  gdb::optional<CORE_ADDR> sp_value;
 
   if (*this_cache == NULL)
     *this_cache = arm_make_prologue_cache (this_frame);
@@ -2193,6 +2244,12 @@ arm_prologue_prev_register (struct frame_info *this_frame,
     return frame_unwind_got_constant (this_frame, prev_regnum,
 				      arm_cache_get_prev_sp_value (cache, tdep));
 
+  /* The value might be one of the alternative SP, if so, use the
+     value already constructed.  */
+  sp_value = arm_cache_get_sp_register (cache, tdep, prev_regnum);
+  if (sp_value.has_value ())
+    return frame_unwind_got_constant (this_frame, prev_regnum, *sp_value);
+
   /* The CPSR may have been changed by the call instruction and by the
      called function.  The only bit we can reconstruct is the T bit,
      by checking the low bit of LR as of the call.  This is a reliable
@@ -3188,16 +3245,20 @@ static struct arm_prologue_cache *
 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);
   arm_gdbarch_tdep *tdep = (arm_gdbarch_tdep *) gdbarch_tdep (gdbarch);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
   struct arm_prologue_cache *cache;
   CORE_ADDR lr;
+  CORE_ADDR sp;
   CORE_ADDR unwound_sp;
+  uint32_t sp_r0_offset = 0;
   LONGEST xpsr;
   uint32_t exc_return;
-  uint32_t process_stack_used;
+  bool fnc_return;
   uint32_t extended_frame_used;
-  uint32_t secure_stack_used;
+  bool secure_stack_used = false;
+  bool default_callee_register_stacking = false;
+  bool exception_domain_is_secure = false;
 
   cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
   arm_cache_init (cache, this_frame);
@@ -3207,35 +3268,123 @@ arm_m_exception_cache (struct frame_info *this_frame)
      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);
+
+  fnc_return = ((lr & 0xfffffffe) == 0xfefffffe);
+  if (tdep->have_sec_ext && fnc_return)
+    {
+      int actual_sp;
+
+      arm_cache_switch_prev_sp (cache, tdep, tdep->m_profile_msp_ns_regnum);
+      arm_cache_set_active_sp_value (cache, tdep, sp);
+      if (lr & 1)
+	actual_sp = tdep->m_profile_msp_s_regnum;
+      else
+	actual_sp = tdep->m_profile_msp_ns_regnum;
+
+      arm_cache_switch_prev_sp (cache, tdep, actual_sp);
+      sp = get_frame_register_unsigned (this_frame, actual_sp);
 
-  /* Check EXC_RETURN indicator bits.  */
-  exc_return = (((lr >> 28) & 0xf) == 0xf);
+      cache->saved_regs[ARM_LR_REGNUM].set_addr (sp);
 
-  /* 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)
+      arm_cache_set_active_sp_value (cache, tdep, sp + 8);
+
+      return cache;
+    }
+
+  /* Check EXC_RETURN indicator bits (24-31).  */
+  exc_return = (((lr >> 24) & 0xff) == 0xff);
+  if (exc_return)
     {
-      /* Thread (process) stack used, use PSP as SP.  */
-      unwound_sp = get_frame_register_unsigned (this_frame, tdep->m_profile_psp_regnum);
+      /* Check EXC_RETURN bit SPSEL if Main or Thread (process) stack used.  */
+      bool process_stack_used = ((lr & (1 << 2)) != 0);
+
+      if (tdep->have_sec_ext)
+	{
+	  secure_stack_used = ((lr & (1 << 6)) != 0);
+	  default_callee_register_stacking = ((lr & (1 << 5)) != 0);
+	  exception_domain_is_secure = ((lr & (1 << 0)) == 0);
+
+	  /* Unwinding from non-secure to secure can trip security
+	     measures.  In order to avoid the debugger to be
+	     intrusive, rely on the user to configure the requested
+	     mode.  */
+	  if (secure_stack_used && !exception_domain_is_secure
+	      && !arm_unwind_ns_to_s)
+	    {
+	      warning (_("Non-secure to secure stack unwinding disabled."));
+
+	      /* Terminate any further stack unwinding by referring to self.  */
+	      arm_cache_set_active_sp_value (cache, tdep, sp);
+	      return cache;
+	    }
+
+	  if (process_stack_used)
+	    {
+	      if (secure_stack_used)
+		/* Secure thread (process) stack used, use PSP_S as SP.  */
+		arm_cache_switch_prev_sp (cache, tdep, tdep->m_profile_psp_s_regnum);
+	      else
+		/* Non-secure thread (process) stack used, use PSP_NS as SP.  */
+		arm_cache_switch_prev_sp (cache, tdep, tdep->m_profile_psp_ns_regnum);
+	    }
+	  else
+	    {
+	      if (secure_stack_used)
+		/* Secure main stack used, use MSP_S as SP.  */
+		arm_cache_switch_prev_sp (cache, tdep, tdep->m_profile_msp_s_regnum);
+	      else
+		/* Non-secure main stack used, use MSP_NS as SP.  */
+		arm_cache_switch_prev_sp (cache, tdep, tdep->m_profile_msp_ns_regnum);
+	    }
+	}
+      else
+	{
+	  if (process_stack_used)
+	    /* Thread (process) stack used, use PSP as SP.  */
+	    arm_cache_switch_prev_sp (cache, tdep, tdep->m_profile_psp_regnum);
+	  else
+	    /* Main stack used, use MSP as SP.  */
+	    arm_cache_switch_prev_sp (cache, tdep, tdep->m_profile_msp_regnum);
+	}
     }
   else
     {
       /* Main stack used, use MSP as SP.  */
-      unwound_sp = get_frame_register_unsigned (this_frame, tdep->m_profile_msp_regnum);
+      arm_cache_switch_prev_sp (cache, tdep, tdep->m_profile_msp_regnum);
+    }
+
+  /* Fetch the SP to use for this frame.  */
+  unwound_sp = arm_cache_get_prev_sp_value (cache, tdep);
+
+  /* With the Security extension, the hardware saves R4..R11 too.  */
+  if (exc_return && tdep->have_sec_ext && secure_stack_used
+      && (!default_callee_register_stacking || exception_domain_is_secure))
+    {
+      /* Read R4..R11 from the integer callee registers.  */
+      cache->saved_regs[4].set_addr (unwound_sp + 0x08);
+      cache->saved_regs[5].set_addr (unwound_sp + 0x0C);
+      cache->saved_regs[6].set_addr (unwound_sp + 0x10);
+      cache->saved_regs[7].set_addr (unwound_sp + 0x14);
+      cache->saved_regs[8].set_addr (unwound_sp + 0x18);
+      cache->saved_regs[9].set_addr (unwound_sp + 0x1C);
+      cache->saved_regs[10].set_addr (unwound_sp + 0x20);
+      cache->saved_regs[11].set_addr (unwound_sp + 0x24);
+      sp_r0_offset = 0x28;
     }
 
   /* 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].set_addr (unwound_sp);
-  cache->saved_regs[1].set_addr (unwound_sp + 4);
-  cache->saved_regs[2].set_addr (unwound_sp + 8);
-  cache->saved_regs[3].set_addr (unwound_sp + 12);
-  cache->saved_regs[ARM_IP_REGNUM].set_addr (unwound_sp + 16);
-  cache->saved_regs[ARM_LR_REGNUM].set_addr (unwound_sp + 20);
-  cache->saved_regs[ARM_PC_REGNUM].set_addr (unwound_sp + 24);
-  cache->saved_regs[ARM_PS_REGNUM].set_addr (unwound_sp + 28);
+  cache->saved_regs[0].set_addr (unwound_sp + sp_r0_offset);
+  cache->saved_regs[1].set_addr (unwound_sp + sp_r0_offset + 4);
+  cache->saved_regs[2].set_addr (unwound_sp + sp_r0_offset + 8);
+  cache->saved_regs[3].set_addr (unwound_sp + sp_r0_offset + 12);
+  cache->saved_regs[ARM_IP_REGNUM].set_addr (unwound_sp + sp_r0_offset + 16);
+  cache->saved_regs[ARM_LR_REGNUM].set_addr (unwound_sp + sp_r0_offset + 20);
+  cache->saved_regs[ARM_PC_REGNUM].set_addr (unwound_sp + sp_r0_offset + 24);
+  cache->saved_regs[ARM_PS_REGNUM].set_addr (unwound_sp + sp_r0_offset + 28);
 
   /* Check EXC_RETURN bit FTYPE if extended stack frame (FPU regs stored)
      type used.  */
@@ -3254,41 +3403,43 @@ arm_m_exception_cache (struct frame_info *this_frame)
 	 This register is located at address 0xE000EF34.  */
 
       /* Extended stack frame type used.  */
-      fpu_regs_stack_offset = unwound_sp + 0x20;
+      fpu_regs_stack_offset = unwound_sp + sp_r0_offset + 0x20;
       for (i = 0; i < 16; i++)
 	{
 	  cache->saved_regs[ARM_D0_REGNUM + i].set_addr (fpu_regs_stack_offset);
 	  fpu_regs_stack_offset += 4;
 	}
-      cache->saved_regs[ARM_FPSCR_REGNUM].set_addr (unwound_sp + 0x60);
+      cache->saved_regs[ARM_FPSCR_REGNUM].set_addr (unwound_sp + sp_r0_offset + 0x60);
+      fpu_regs_stack_offset += 4;
+
+      if (tdep->have_sec_ext && !default_callee_register_stacking)
+	{
+	  /* Handle floating-point callee saved registers.  */
+	  fpu_regs_stack_offset = 0x90;
+	  for (i = 16; i < 32; i++)
+	    {
+	      cache->saved_regs[ARM_D0_REGNUM + i].set_addr (fpu_regs_stack_offset);
+	      fpu_regs_stack_offset += 4;
+	    }
 
-      /* Offset 0x64 is reserved.  */
-      arm_cache_set_active_sp_value (cache, tdep, unwound_sp + 0x68);
+	  arm_cache_set_active_sp_value (cache, tdep, unwound_sp + sp_r0_offset + 0xD0);
+	}
+      else
+	{
+	  /* Offset 0x64 is reserved.  */
+	  arm_cache_set_active_sp_value (cache, tdep, unwound_sp + sp_r0_offset + 0x68);
+	}
     }
   else
     {
       /* Standard stack frame type used.  */
-      arm_cache_set_active_sp_value (cache, tdep, 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.  */
+      arm_cache_set_active_sp_value (cache, tdep, unwound_sp + sp_r0_offset + 0x20);
     }
 
   /* 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.  */
-  if (safe_read_memory_integer (unwound_sp + 28, 4, byte_order, &xpsr)
+  if (safe_read_memory_integer (unwound_sp + sp_r0_offset + 28, 4, byte_order, &xpsr)
       && (xpsr & (1 << 9)) != 0)
     arm_cache_set_active_sp_value (cache, tdep,
 				   arm_cache_get_prev_sp_value (cache, tdep) + 4);
@@ -3326,6 +3477,7 @@ arm_m_exception_prev_register (struct frame_info *this_frame,
 			       int prev_regnum)
 {
   struct arm_prologue_cache *cache;
+  gdb::optional<CORE_ADDR> sp_value;
 
   if (*this_cache == NULL)
     *this_cache = arm_m_exception_cache (this_frame);
@@ -3338,6 +3490,21 @@ arm_m_exception_prev_register (struct frame_info *this_frame,
     return frame_unwind_got_constant (this_frame, prev_regnum,
 				      arm_cache_get_prev_sp_value (cache, tdep));
 
+  /* The value might be one of the alternative SP, if so, use the
+     value already constructed.  */
+  sp_value = arm_cache_get_sp_register (cache, tdep, prev_regnum);
+  if (sp_value.has_value ())
+    return frame_unwind_got_constant (this_frame, prev_regnum, *sp_value);
+
+  if (prev_regnum == ARM_PC_REGNUM)
+    {
+      CORE_ADDR lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM);
+      struct gdbarch *gdbarch = get_frame_arch (this_frame);
+
+      return frame_unwind_got_constant (this_frame, prev_regnum,
+					arm_addr_bits_remove (gdbarch, lr));
+    }
+
   return trad_frame_get_prev_register (this_frame, cache->saved_regs,
 				       prev_regnum);
 }
@@ -3350,13 +3517,14 @@ arm_m_exception_unwind_sniffer (const struct frame_unwind *self,
 				struct frame_info *this_frame,
 				void **this_prologue_cache)
 {
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
   CORE_ADDR this_pc = get_frame_pc (this_frame);
 
   /* No need to check is_m; this sniffer is only registered for
      M-profile architectures.  */
 
   /* Check if exception frame returns to a magic PC value.  */
-  return arm_m_addr_is_magic (this_pc);
+  return arm_m_addr_is_magic (gdbarch, this_pc);
 }
 
 /* Frame unwinder for M-profile exceptions.  */
@@ -8773,6 +8941,15 @@ arm_show_force_mode (struct ui_file *file, int from_tty,
 		    arm_force_mode_string);
 }
 
+static void
+arm_show_unwind_ns_to_s (struct ui_file *file, int from_tty,
+			 struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file,
+		    _("Usage of non-secure to secure exception stack unwinding is %s.\n"),
+		    arm_unwind_ns_to_s ? "on" : "off");
+}
+
 /* If the user changes the register disassembly style used for info
    register and other commands, we have to also switch the style used
    in opcodes for disassembly output.  This function is run in the "set
@@ -10170,6 +10347,15 @@ vfp - VFP co-processor."),
 			NULL, NULL, arm_show_force_mode,
 			&setarmcmdlist, &showarmcmdlist);
 
+  /* Add a command to stop triggering security exceptions when
+     unwinding exception stacks.  */
+  add_setshow_boolean_cmd ("unwind-ns-to-s", no_class, &arm_unwind_ns_to_s,
+			   _("Set usage of non-secure to secure exception stack unwinding."),
+			   _("Show usage of non-secure to secure exception stack unwinding."),
+			   _("When on, the debugger can trigger memory access traps."),
+			   NULL, arm_show_unwind_ns_to_s,
+			   &setarmcmdlist, &showarmcmdlist);
+
   /* Debugging flag.  */
   add_setshow_boolean_cmd ("arm", class_maintenance, &arm_debug,
 			   _("Set ARM debugging."),
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index f7f5f7a6158..e9c5799886d 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24963,6 +24963,16 @@ of @samp{set arm fallback-mode}.
 @item show arm force-mode
 Show the current forced instruction mode.
 
+@item set arm unwind-ns-to-s
+This command enables unwinding from Non-secure to Secure mode on
+Cortex-M with Security extension.
+This can trigger security exceptions when unwinding the exception
+stack.
+It is enabled by default.
+
+@item show arm unwind-ns-to-s
+Show whether unwind from Non-secure to Secure mode is enabled.
+
 @item set debug arm
 Toggle whether to display ARM-specific debugging messages from the ARM
 target support subsystem.
-- 
2.17.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v4 0/5] arm: Add support for multiple stacks on Cortex-M
  2022-04-01  9:18 [PATCH v4 0/5] arm: Add support for multiple stacks on Cortex-M Christophe Lyon
                   ` (4 preceding siblings ...)
  2022-04-01  9:18 ` [PATCH v4 5/5] gdb/arm: Extend arm_m_addr_is_magic to support FNC_RETURN, add unwind-ns-to-s command Christophe Lyon
@ 2022-04-01  9:33 ` Christophe Lyon
  5 siblings, 0 replies; 8+ messages in thread
From: Christophe Lyon @ 2022-04-01  9:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: torbjorn.svensson, yvan.roux

[sorry for double-posting the first patches, I had Yvan's address wrong. 
But I still missed that my @foss.st.com address is no longer valid, so 
you may still get bounces on replies]

On 4/1/22 11:18, Christophe Lyon wrote:
> This patch series introduces support for the multiple stack pointers
> on v8-M architecture (MSP_S, MSP_NS, PSP_S, PSP_NS).
> 
> This enables to unwind through Secure vs Non-secure context switches.
> 
> Along the way, the first patch adds support to detect vpush
> instructions in function prologues, which in particular is used in the
> CMSE trampolines.
> 
> v4 addresses comments received about v2/v3 from Joel, as well as
> others from Luis.
> 
> Christophe Lyon (5):
>    gdb/arm: Fix prologue analysis to support vpush
>    gdb/arm: Define MSP and PSP registers for M-Profile
>    gdb/arm: Introduce arm_cache_init
>    gdb/arm: Add support for multiple stack pointers on Cortex-M
>    gdb/arm: Extend arm_m_addr_is_magic to support FNC_RETURN, add
>      unwind-ns-to-s command
> 
>   gdb/NEWS                          |   5 +
>   gdb/arch/arm.c                    |   6 +
>   gdb/arch/arm.h                    |   1 +
>   gdb/arm-tdep.c                    | 685 +++++++++++++++++++++++++-----
>   gdb/arm-tdep.h                    |  10 +
>   gdb/doc/gdb.texinfo               |  10 +
>   gdb/features/Makefile             |   1 +
>   gdb/features/arm/arm-m-system.c   |  15 +
>   gdb/features/arm/arm-m-system.xml |  12 +
>   gdb/features/arm/arm-secext.c     |  17 +
>   gdb/features/arm/arm-secext.xml   |  15 +
>   11 files changed, 678 insertions(+), 99 deletions(-)
>   create mode 100644 gdb/features/arm/arm-m-system.c
>   create mode 100644 gdb/features/arm/arm-m-system.xml
>   create mode 100644 gdb/features/arm/arm-secext.c
>   create mode 100644 gdb/features/arm/arm-secext.xml
> 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v4 1/5] gdb/arm: Fix prologue analysis to support vpush
  2022-04-01  9:16 Christophe Lyon
@ 2022-04-01  9:16 ` Christophe Lyon
  0 siblings, 0 replies; 8+ messages in thread
From: Christophe Lyon @ 2022-04-01  9:16 UTC (permalink / raw)
  To: gdb-patches
  Cc: torbjorn.svensson, yvan.roux, Christophe Lyon, Christophe Lyon

From: Christophe Lyon <christophe.lyon@foss.st.com>

While working on adding support for Non-secure/Secure modes unwinding,
I noticed that the prologue analysis lacked support for vpush, which
is used for instance in the CMSE stub routine.

This patch updates thumb_analyze_prologue accordingly, adding support
for vpush of D-registers.

Signed-off-by: Christophe Lyon <christophe.lyon@foss.st.com>
Signed-off-by: Christophe Lyon <christophe.lyon@arm.com>
---
 gdb/arm-tdep.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index d36856e1f3b..9e623057e4a 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -896,6 +896,35 @@ thumb_analyze_prologue (struct gdbarch *gdbarch,
 		regs[bits (insn, 0, 3)] = addr;
 	    }
 
+	  /* vstmdb Rn{!}, { D-registers } (aka vpush).  */
+	  else if ((insn & 0xff20) == 0xed20
+		   && (inst2 & 0x0f00) == 0x0b00
+		   && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+	    {
+	      /* Address SP points to.  */
+	      pv_t addr = regs[bits (insn, 0, 3)];
+
+	      /* Number of registers saved.  */
+	      unsigned int number = bits (inst2, 0, 7) >> 1;
+
+	      /* First register to save.  */
+	      int vd = bits (inst2, 12, 15) | (bits (insn, 6, 6) << 4);
+
+	      if (stack.store_would_trash (addr))
+		break;
+
+	      /* Calculate offsets of saved registers.  */
+	      for (; number > 0; number--)
+		{
+		  addr = pv_add_constant (addr, -8);
+		  stack.store (addr, 8, pv_register (ARM_D0_REGNUM
+						     + vd + number, 0));
+		}
+
+	      /* Writeback SP to account for the saved registers.  */
+	      regs[bits (insn, 0, 3)] = addr;
+	    }
+
 	  else if ((insn & 0xff50) == 0xe940	/* strd Rt, Rt2,
 						   [Rn, #+/-imm]{!} */
 		   && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
-- 
2.17.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2022-04-01  9:33 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-01  9:18 [PATCH v4 0/5] arm: Add support for multiple stacks on Cortex-M Christophe Lyon
2022-04-01  9:18 ` [PATCH v4 1/5] gdb/arm: Fix prologue analysis to support vpush Christophe Lyon
2022-04-01  9:18 ` [PATCH v4 2/5] gdb/arm: Define MSP and PSP registers for M-Profile Christophe Lyon
2022-04-01  9:18 ` [PATCH v4 3/5] gdb/arm: Introduce arm_cache_init Christophe Lyon
2022-04-01  9:18 ` [PATCH v4 4/5] gdb/arm: Add support for multiple stack pointers on Cortex-M Christophe Lyon
2022-04-01  9:18 ` [PATCH v4 5/5] gdb/arm: Extend arm_m_addr_is_magic to support FNC_RETURN, add unwind-ns-to-s command Christophe Lyon
2022-04-01  9:33 ` [PATCH v4 0/5] arm: Add support for multiple stacks on Cortex-M Christophe Lyon
  -- strict thread matches above, loose matches on Subject: below --
2022-04-01  9:16 Christophe Lyon
2022-04-01  9:16 ` [PATCH v4 1/5] gdb/arm: Fix prologue analysis to support vpush Christophe Lyon

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).