public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/vendors/ARM/heads/morello)] Morello unwinder including register merging.
@ 2022-11-02 14:53 Matthew Malcomson
  0 siblings, 0 replies; only message in thread
From: Matthew Malcomson @ 2022-11-02 14:53 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:5ddc12ffe4f6bc3953635fe8e29d11ec1a7d7494

commit 5ddc12ffe4f6bc3953635fe8e29d11ec1a7d7494
Author: Matthew Malcomson <matthew.malcomson@arm.com>
Date:   Wed Nov 2 14:51:15 2022 +0000

    Morello unwinder including register merging.
    
    This is the first step towards getting unwinding past signal frames
    working correctly.
    
    When unwinding past signal frames we have to look in different places
    to restore registers.  Usually there is a DWARF description telling the
    unwinder where to find register information, but there is not in signal
    frames.
    
    A signal frame is identified by looking at the instructions which are
    pointed to by the LR.  For a signal frame these are known to be a
    special kernel trampoline that can be identified by its code.
    
    Usually on a Morello-Linux system the CLR is sealed, which means that
    the instructions pointed to be the CLR could not be read.  However this
    is specifically not the case for the CLR in a signal frame.  Hence the
    same mechanism by which we can inspect the trampoline for non-Morello
    systems can be used again.
    The kernel provides an extension context containing the capability
    metadata for each register.  We could directly take all registers from
    there but this gives a slightly different behaviour to what happens in a
    non-Morello system.  For Morello the kernel "merges" registers on
    returning from a signal frame.  This only matters if a signal handler
    has modified the saved registers on the stack, and we do not believe
    this case is ever really used.  However it's still nice to match
    logical behaviour.
    
    The "merging" happens by taking the u64 values in the standard signal
    context and combining them with the capability metadata for each
    corresponding register in the morello_context extension.  This means
    that a signal frame can change the values in its callers by adjusting
    the values in this non-capability structure.  Hence in order to unwind
    in such a way that the unwinder would see the same register values as
    the user-frame above the signal handler would see we need to express
    this register "merging" for uw_update_context to handle.
    
    The unwinder first makes a description of how to calculate registers in
    the frame above, and later uses that description to update the relevant
    register.
    
    In order to describe the "register merging" that the kernel does on
    returning from a signal frame we need some new description mechanism.
    
    Here we introduce a new enum value REG_SAVED_SET_ADDRESS to describe
    this register merging behaviour.  The enum makes use of a new entry in
    the _Unwind_FrameState `loc` union which records two offsets.  It
    happens that for Pure Capability targets the offset is half the size of
    a pointer, which means that we can record these two offsets in the `loc`
    union without increasing the size of an _Unwind_FrameState.

Diff:
---
 libgcc/config/aarch64/linux-unwind.h | 67 +++++++++++++++++++++++++-----------
 libgcc/unwind-dw2.c                  | 15 ++++++++
 libgcc/unwind-dw2.h                  | 13 +++++++
 3 files changed, 75 insertions(+), 20 deletions(-)

diff --git a/libgcc/config/aarch64/linux-unwind.h b/libgcc/config/aarch64/linux-unwind.h
index c23995a93c6..e8626075f24 100644
--- a/libgcc/config/aarch64/linux-unwind.h
+++ b/libgcc/config/aarch64/linux-unwind.h
@@ -41,13 +41,7 @@
 #define SVC_0		0xd4000001
 #endif
 
-/* The mechanism of reading the instructions pointed to by our LR can not work
-   for Morello since the CLR provided by the kernel will be sealed.
-   That means we can't read instructions from it.  Which means our
-   determination mechanism can no longer work.  */
-#ifndef __CHERI_PURE_CAPABILITY__
 #define MD_FALLBACK_FRAME_STATE_FOR aarch64_fallback_frame_state
-#endif
 
 static _Unwind_Reason_Code
 aarch64_fallback_frame_state (struct _Unwind_Context *context,
@@ -76,8 +70,16 @@ aarch64_fallback_frame_state (struct _Unwind_Context *context,
 
      0xd2801168         movz x8, #0x8b
      0xd4000001         svc  0x0
-   */
-  if (pc[0] != MOVZ_X8_8B || pc[1] != SVC_0)
+
+     In pure capability most context->ra entries are sealed, it is only in the
+     special signal frame that we expect to see an unsealed return address.
+     Hence we need to check that before attempting to read from the return
+     address to avoid a segmentation fault in the unwinder.  */
+  if (
+#ifdef __CHERI_PURE_CAPABILITY__
+      __builtin_cheri_sealed_get (pc) ||
+#endif
+      pc[0] != MOVZ_X8_8B || pc[1] != SVC_0)
     {
       return _URC_END_OF_STACK;
     }
@@ -91,13 +93,20 @@ aarch64_fallback_frame_state (struct _Unwind_Context *context,
   new_cfa = (_Unwind_Ptr) sc;
   fs->regs.cfa_how = CFA_REG_OFFSET;
   fs->regs.cfa_reg = __LIBGCC_STACK_POINTER_REGNUM__;
-  fs->regs.cfa_offset = new_cfa - (_Unwind_Ptr) context->cfa;
+  fs->regs.cfa_offset = new_cfa - (_Unwind_Address) context->cfa;
+
+  fs->regs.reg[31].how = REG_SAVED_OFFSET;
+  fs->regs.reg[31].loc.offset = (_Unwind_Ptr)&sc->sp - new_cfa;
+  fs->regs.reg[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__].how =
+    REG_SAVED_OFFSET;
+  fs->regs.reg[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__].loc.offset =
+    (_Unwind_Ptr)&sc->pc - new_cfa;
 
   for (i = 0; i < AARCH64_DWARF_NUMBER_R; i++)
     {
       fs->regs.reg[AARCH64_DWARF_R0 + i].how = REG_SAVED_OFFSET;
       fs->regs.reg[AARCH64_DWARF_R0 + i].loc.offset =
-	(_Unwind_Ptr) & (sc->regs[i]) - new_cfa;
+	(_Unwind_Address)(sc->regs + i) - (_Unwind_Address) new_cfa;
     }
 
   /* The core context may be extended with an arbitrary set of
@@ -131,7 +140,8 @@ aarch64_fallback_frame_state (struct _Unwind_Context *context,
 		 need to offset into the saved V register dependent on
 		 our endianness to find the saved D register.  */
 
-	      offset = (_Unwind_Ptr) & (ctx->vregs[i]) - new_cfa;
+	      offset = (_Unwind_Address)(ctx->vregs + i)
+			      - (_Unwind_Address) new_cfa;
 
 	      /* The endianness adjustment code below expects that a
 		 saved V register is 16 bytes.  */
@@ -142,22 +152,39 @@ aarch64_fallback_frame_state (struct _Unwind_Context *context,
 	      fs->regs.reg[AARCH64_DWARF_V0 + i].loc.offset = offset;
 	    }
 	}
+      /* N.b. we put this under an ifdef since it uses REG_SAVED_SET_ADDRESS
+	 which is not available for non-CHERI targets.  */
+#ifdef __CHERI_PURE_CAPABILITY__
+      else if (extension_marker->magic == MORELLO_MAGIC)
+	{
+#define ADD_OFFSET2(reg, newoffset) \
+	  do { \
+	      (reg).how = REG_SAVED_SET_ADDRESS;  \
+	      (reg).loc.offs.offset1 = (reg).loc.offset; \
+	      (reg).loc.offs.offset2 = (newoffset);  \
+	  } while (0)
+	  struct morello_context *ctx =
+	    (struct morello_context *) extension_marker;
+	  int i;
+	  for (i = 0; i < AARCH64_DWARF_NUMBER_R; i++)
+	    ADD_OFFSET2 (fs->regs.reg[AARCH64_DWARF_R0 + i],
+			 (_Unwind_Address)(ctx->cregs + i) - new_cfa);
+	  /* Override the existing SP and PC expressions with some
+	     describing the capability versions.  */
+	  ADD_OFFSET2 (fs->regs.reg[31],
+		       (_Unwind_Address)&ctx->csp - new_cfa);
+	  ADD_OFFSET2 (fs->regs.reg[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__],
+		       (_Unwind_Address)&ctx->pcc - new_cfa);
+#undef ADD_OFFSET2
+	}
+#endif
       else
 	{
 	  /* There is context provided that we do not recognize!  */
 	}
     }
 
-  fs->regs.reg[31].how = REG_SAVED_OFFSET;
-  fs->regs.reg[31].loc.offset = (_Unwind_Ptr) & (sc->sp) - new_cfa;
-
   fs->signal_frame = 1;
-
-  fs->regs.reg[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__].how =
-    REG_SAVED_VAL_OFFSET;
-  fs->regs.reg[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__].loc.offset =
-    (_Unwind_Ptr) (sc->pc) - new_cfa;
-
   fs->retaddr_column = __LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__;
 
   return _URC_NO_REASON;
diff --git a/libgcc/unwind-dw2.c b/libgcc/unwind-dw2.c
index c3dfe85275c..15eb625c201 100644
--- a/libgcc/unwind-dw2.c
+++ b/libgcc/unwind-dw2.c
@@ -1554,6 +1554,21 @@ uw_update_context_1 (struct _Unwind_Context *context, _Unwind_FrameState *fs)
 	  _Unwind_SetGRValue (context, i, val);
 	}
 	break;
+
+#ifdef __CHERI_PURE_CAPABILITY__
+      /* Special description which contains two offsets where one points us to
+	 the address value of a capability and the other points us to a
+	 capability with the metadata we want for the result.  */
+      case REG_SAVED_SET_ADDRESS:
+	{
+	  _Unwind_SwordAddr cap_offset = fs->regs.reg[i].loc.offs.offset2;
+	  _Unwind_SwordAddr addr_offset = fs->regs.reg[i].loc.offs.offset1;
+	  _Unwind_Ptr creg = *(void **)(cfa + cap_offset);
+	  _Unwind_WordAddr xreg = read_8u (cfa + addr_offset);
+	  creg = __builtin_cheri_address_set (creg, xreg);
+	  _Unwind_SetGRValue (context, i, creg);
+	}
+#endif
       }
 
   _Unwind_SetSignalFrame (context, fs->signal_frame);
diff --git a/libgcc/unwind-dw2.h b/libgcc/unwind-dw2.h
index 88b2f2bd6c9..966d6b61096 100644
--- a/libgcc/unwind-dw2.h
+++ b/libgcc/unwind-dw2.h
@@ -37,6 +37,16 @@ typedef struct
 	_Unwind_Reg reg;
 	_Unwind_SwordAddr offset;
 	const unsigned char *exp;
+#ifdef __CHERI_PURE_CAPABILITY__
+	struct {
+	    /* Only used for REG_SAVED_SETADDRESS.
+	       N.b. two SwordAddr's are the same size as a pointer in pure
+	       capability, hence this is not increasing the size of the frame
+	       state structure.  */
+	    _Unwind_SwordAddr offset1;
+	    _Unwind_SwordAddr offset2;
+	} offs;
+#endif
       } loc;
       enum {
 	REG_UNSAVED,
@@ -45,6 +55,9 @@ typedef struct
 	REG_SAVED_EXP,
 	REG_SAVED_VAL_OFFSET,
 	REG_SAVED_VAL_EXP,
+#ifdef __CHERI_PURE_CAPABILITY__
+	REG_SAVED_SET_ADDRESS,
+#endif
 	REG_UNDEFINED
       } how;
     } reg[__LIBGCC_DWARF_FRAME_REGISTERS__+1];

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-11-02 14:53 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-02 14:53 [gcc(refs/vendors/ARM/heads/morello)] Morello unwinder including register merging Matthew Malcomson

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