Clang can generate SEH unwind info in PE mode on ARM now (since the very latest git versions of Clang 15). Add SEH assembly directives to let the unwinder step past handwritten assembly functions/trampolines.
Also fix a minor bug exposed by a testcase that I had missed to run before, and apply the same fix consistently to arm64 (where the same issue hadn't been exposed before).
From: Martin Storsjö martin@martin.st
Clang generates SEH unwind info on ARM in both MSVC and mingw mode since Clang 15. The unwind info gets which gets enabled automatically by building with a new enough compiler.
For functions/trampolines that are implemented in assembly, the unwind information needs to be provided by hand - in particular for cases where unwinding should be diverted.
Contrary to AArch64, Clang got SEH assembler directives directly from the start when SEH was implemented (when the __SEH__ compiler define gets set), so there's no need for compiler version checks (like for AArch64 in include/wine/asm.h), but the default check for __SEH__ works fine for enabling the __ARM_SEH() macro.
Use a custom unwind opcode in the private opcode space for unwinding to a specific CONTEXT instead of up to the caller. (Contrary to AArch64, there's no specific unwind opcode for restoring a full CONTEXT, but the unwind opcode space does have a couple unallocated values marked as "available", which can be used for vendor specific needs here.)
This fixes unwinding in ARM PE builds.
Signed-off-by: Martin Storsjö martin@martin.st --- dlls/ntdll/signal_arm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/dlls/ntdll/signal_arm.c b/dlls/ntdll/signal_arm.c index 7872962bbbe..01c911660de 100644 --- a/dlls/ntdll/signal_arm.c +++ b/dlls/ntdll/signal_arm.c @@ -731,6 +731,8 @@ static void process_unwind_codes( BYTE *ptr, BYTE *end, CONTEXT *context, WARN( "unsupported code %02x\n", *ptr ); else if (*ptr <= 0xef && ((val & 0xff) <= 0x0f)) /* ldr lr, [sp], #x */ pop_lr( 4 * (val & 0x0f), context, ptrs ); + else if (*ptr == 0xf4) /* Custom private (unallocated) opcode, saved a full CONTEXT on the stack */ + memcpy( context, (DWORD *)context->Sp, sizeof(CONTEXT) ); else if (*ptr <= 0xf4) /* Available */ WARN( "unsupported code %02x\n", *ptr ); else if (*ptr <= 0xf5) /* vpop {dS-dE} */ @@ -1075,11 +1077,18 @@ extern void * WINAPI call_consolidate_callback( CONTEXT *context, EXCEPTION_RECORD *rec ); __ASM_GLOBAL_FUNC( call_consolidate_callback, "push {r0-r2,lr}\n\t" + __ASM_SEH(".seh_nop\n\t") "sub sp, sp, #0x1a0\n\t" + __ASM_SEH(".seh_nop\n\t") "mov r1, r0\n\t" + __ASM_SEH(".seh_nop\n\t") "mov r0, sp\n\t" + __ASM_SEH(".seh_nop\n\t") "mov r2, #0x1a0\n\t" + __ASM_SEH(".seh_nop_w\n\t") "bl " __ASM_NAME("memcpy") "\n\t" + __ASM_SEH(".seh_custom 0xf4\n\t") /* A custom (unallocated) SEH opcode for CONTEXT on stack */ + __ASM_SEH(".seh_endprologue\n\t") __ASM_CFI(".cfi_def_cfa 13, 0\n\t") __ASM_CFI(".cfi_escape 0x0f,0x04,0x7d,0xb8,0x00,0x06\n\t") /* DW_CFA_def_cfa_expression: DW_OP_breg13 + 56, DW_OP_deref */ __ASM_CFI(".cfi_escape 0x10,0x04,0x02,0x7d,0x14\n\t") /* DW_CFA_expression: R4 DW_OP_breg13 + 20 */ @@ -1300,6 +1309,8 @@ extern LONG __C_ExecuteExceptionFilter(PEXCEPTION_POINTERS ptrs, PVOID frame, PUCHAR nonvolatile); __ASM_GLOBAL_FUNC( __C_ExecuteExceptionFilter, "push {r4-r11,lr}\n\t" + __ASM_SEH(".seh_save_regs_w {r4-r11,lr}\n\t") + __ASM_SEH(".seh_endprologue\n\t")
__ASM_CFI(".cfi_def_cfa 13, 36\n\t") __ASM_CFI(".cfi_offset r4, -36\n\t") @@ -1410,7 +1421,10 @@ EXCEPTION_DISPOSITION WINAPI __C_specific_handler( EXCEPTION_RECORD *rec, */ __ASM_STDCALL_FUNC( RtlRaiseException, 4, "push {r0, lr}\n\t" + __ASM_SEH(".seh_save_regs {r0, lr}\n\t") "sub sp, sp, #0x1a0\n\t" /* sizeof(CONTEXT) */ + __ASM_SEH(".seh_stackalloc 0x1a0\n\t") + __ASM_SEH(".seh_endprologue\n\t") __ASM_CFI(".cfi_adjust_cfa_offset 424\n\t") __ASM_CFI(".cfi_offset lr, -4\n\t") "mov r0, sp\n\t" /* context */
From: Martin Storsjö martin@martin.st
Don't just use ControlPcIsUnwound for picking which RUNTIME_FUNCTION to use, but also use it for adjusting the pc value that is passed to RtlVirtualUnwind.
This fixes one testcase (that I had missed to build and run before), where the pc points at the start of an epilogue (which would mean that no handler is returned from RtlVirtualUnwind). By taking ControlPcIsUnwound and adjusting the pc pointer, the unwind handler is returned and executed.
Signed-off-by: Martin Storsjö martin@martin.st --- dlls/ntdll/signal_arm.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/signal_arm.c b/dlls/ntdll/signal_arm.c index 01c911660de..c4cf557edaf 100644 --- a/dlls/ntdll/signal_arm.c +++ b/dlls/ntdll/signal_arm.c @@ -133,6 +133,7 @@ static NTSTATUS virtual_unwind( ULONG type, DISPATCHER_CONTEXT *dispatch, CONTEX { LDR_DATA_TABLE_ENTRY *module; NTSTATUS status; + DWORD pc;
dispatch->ImageBase = 0; dispatch->ScopeIndex = 0; @@ -143,14 +144,14 @@ static NTSTATUS virtual_unwind( ULONG type, DISPATCHER_CONTEXT *dispatch, CONTEX * signal frame. */ dispatch->ControlPcIsUnwound = (context->ContextFlags & CONTEXT_UNWOUND_TO_CALL) != 0; + pc = context->Pc - (dispatch->ControlPcIsUnwound ? 2 : 0);
/* first look for PE exception information */
- if ((dispatch->FunctionEntry = lookup_function_info( - context->Pc - (dispatch->ControlPcIsUnwound ? 2 : 0), + if ((dispatch->FunctionEntry = lookup_function_info(pc, (ULONG_PTR*)&dispatch->ImageBase, &module ))) { - dispatch->LanguageHandler = RtlVirtualUnwind( type, dispatch->ImageBase, context->Pc, + dispatch->LanguageHandler = RtlVirtualUnwind( type, dispatch->ImageBase, pc, dispatch->FunctionEntry, context, &dispatch->HandlerData, (ULONG_PTR *)&dispatch->EstablisherFrame, NULL );
From: Martin Storsjö martin@martin.st
This is the same modification as the previous patch, applied on arm64. It doesn't change the outcome for any of my tests though.
Signed-off-by: Martin Storsjö martin@martin.st --- dlls/ntdll/signal_arm64.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index 034c78f6b4d..fba0da48a16 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -165,6 +165,7 @@ static NTSTATUS virtual_unwind( ULONG type, DISPATCHER_CONTEXT *dispatch, CONTEX { LDR_DATA_TABLE_ENTRY *module; NTSTATUS status; + DWORD64 pc;
dispatch->ImageBase = 0; dispatch->ScopeIndex = 0; @@ -175,14 +176,14 @@ static NTSTATUS virtual_unwind( ULONG type, DISPATCHER_CONTEXT *dispatch, CONTEX * signal frame. */ dispatch->ControlPcIsUnwound = (context->ContextFlags & CONTEXT_UNWOUND_TO_CALL) != 0; + pc = context->Pc - (dispatch->ControlPcIsUnwound ? 4 : 0);
/* first look for PE exception information */
- if ((dispatch->FunctionEntry = lookup_function_info( - context->Pc - (dispatch->ControlPcIsUnwound ? 4 : 0), + if ((dispatch->FunctionEntry = lookup_function_info(pc, &dispatch->ImageBase, &module ))) { - dispatch->LanguageHandler = RtlVirtualUnwind( type, dispatch->ImageBase, context->Pc, + dispatch->LanguageHandler = RtlVirtualUnwind( type, dispatch->ImageBase, pc, dispatch->FunctionEntry, context, &dispatch->HandlerData, &dispatch->EstablisherFrame, NULL );