From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2049) id 4330438576B2; Wed, 2 Nov 2022 14:53:05 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 4330438576B2 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1667400785; bh=1EIE1f3Rp4Bl+1OInwA/ZU2L8xKrJ/JDUQcm1ofVwH4=; h=From:To:Subject:Date:From; b=lSddNTzi+Q3XfwDfrT2hYZ7ZFE5ipiElzh2MtVDj7j/ZXUbBPY4F0/eH7BvKJX8lk i+WEcl0zJzGOEcDT2eFMl/SToVTGHmpX4Z+Vqyn8QjOxVgB4sxcWnrkrZk0HFZpqcB ee2XeM9FkgFn5rTkzGLnC+UkyytrAEra7SUEi3RI= Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Matthew Malcomson To: gcc-cvs@gcc.gnu.org Subject: [gcc(refs/vendors/ARM/heads/morello)] Morello unwinder including register merging. X-Act-Checkin: gcc X-Git-Author: Matthew Malcomson X-Git-Refname: refs/vendors/ARM/heads/morello X-Git-Oldrev: b8ad03811a0703efe77111e11ffd20f28b6b12fa X-Git-Newrev: 5ddc12ffe4f6bc3953635fe8e29d11ec1a7d7494 Message-Id: <20221102145305.4330438576B2@sourceware.org> Date: Wed, 2 Nov 2022 14:53:05 +0000 (GMT) List-Id: https://gcc.gnu.org/g:5ddc12ffe4f6bc3953635fe8e29d11ec1a7d7494 commit 5ddc12ffe4f6bc3953635fe8e29d11ec1a7d7494 Author: Matthew Malcomson 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];