On aarch64-linux-gnu, unwind tables aren't emitted by default (contrary to x86_64-linux-gnu), so we must pass the flag (or -funwind-tables) to the compiler to make it generate them.
Previously wine just added the flag to the linker options.
Signed-off-by: Martin Storsjo martin@martin.st --- With this whole series in place, wine successfully manages to handle all C++ unwinding tests from https://github.com/microsoft/compiler-tests (both built with clang in mingw mode, with itanium style unwinding, and with MSVC), and almost all of the plain C SEH tests from the same site.
I can share my collection of unwinding test binaries if there's interest in that. --- configure.ac | 1 + 1 file changed, 1 insertion(+)
diff --git a/configure.ac b/configure.ac index c0f60fe694a..816e3684510 100644 --- a/configure.ac +++ b/configure.ac @@ -2141,6 +2141,7 @@ AC_CACHE_CHECK([whether CFI directives are supported in assembly code], ac_cv_c_ ac_cv_c_cfi_support="yes",ac_cv_c_cfi_support="no")) if test "$ac_cv_c_cfi_support" = "yes" then + DLLFLAGS="$DLLFLAGS -fasynchronous-unwind-tables" LDDLLFLAGS="$LDDLLFLAGS -fasynchronous-unwind-tables" else if test "x$enable_win64" = "xyes"
The register offset should be multiplied by 2, and the second register is always Lr.
Signed-off-by: Martin Storsjo martin@martin.st --- dlls/ntdll/signal_arm64.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index 4c5944759fd..7a3625b706f 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -1467,7 +1467,10 @@ static void process_unwind_codes( BYTE *ptr, BYTE *end, CONTEXT *context, else if (*ptr < 0xd6) /* save_reg_x */ restore_regs( 19 + ((val >> 5) & 0xf), 1, -(val & 0x1f) - 1, context, ptrs ); else if (*ptr < 0xd8) /* save_lrpair */ - restore_regs( 19 + ((val >> 6) & 0x7), 2, val & 0x3f, context, ptrs ); + { + restore_regs( 19 + 2 * ((val >> 6) & 0x7), 1, val & 0x3f, context, ptrs ); + restore_regs( 30, 1, (val & 0x3f) + 1, context, ptrs ); + } else if (*ptr < 0xda) /* save_fregp */ restore_fpregs( 8 + ((val >> 6) & 0x7), save_next, val & 0x3f, context, ptrs ); else if (*ptr < 0xdc) /* save_fregp_x */
restore_regs and restore_fpregs take offsets in units of registers, not bytes.
Signed-off-by: Martin Storsjo martin@martin.st --- It's just a question of code style how to go about fixing it - changing the existing variables to register unit (making a number of stack offset calculations a bit less obvious), adding a new variable like this one, adding /8 in each function call, or adding function wrappers that takes the offsets in byte units, calling the actual restore_regs and restore_fpregs. --- dlls/ntdll/signal_arm64.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-)
diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index 7a3625b706f..8ccf3da57e6 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -1517,6 +1517,7 @@ static void *unwind_packed_data( ULONG_PTR base, ULONG_PTR pc, RUNTIME_FUNCTION int i; unsigned int len, offset, skip = 0; unsigned int int_size = func->u.s.RegI * 8, fp_size = func->u.s.RegF * 8, regsave, local_size; + unsigned int int_regs, fp_regs, saved_regs, local_size_regs;
TRACE( "function %lx-%lx: len=%#x flag=%x regF=%u regI=%u H=%u CR=%u frame=%x\n", base + func->BeginAddress, base + func->BeginAddress + func->u.s.FunctionLength * 4, @@ -1529,6 +1530,11 @@ static void *unwind_packed_data( ULONG_PTR base, ULONG_PTR pc, RUNTIME_FUNCTION regsave = ((int_size + fp_size + 8 * 8 * func->u.s.H) + 0xf) & ~0xf; local_size = func->u.s.FrameSize * 16 - regsave;
+ int_regs = int_size / 8; + fp_regs = fp_size / 8; + saved_regs = regsave / 8; + local_size_regs = local_size / 8; + /* check for prolog/epilog */ if (func->u.s.Flag == 1) { @@ -1568,9 +1574,9 @@ static void *unwind_packed_data( ULONG_PTR base, ULONG_PTR pc, RUNTIME_FUNCTION context->u.X[30] = fp[1]; } context->Sp += local_size; - if (fp_size) restore_fpregs( 8, fp_size / 8, int_size, context, ptrs ); - if (func->u.s.CR == 1) restore_regs( 30, 1, int_size - 8, context, ptrs ); - restore_regs( 19, func->u.s.RegI, -regsave, context, ptrs ); + if (fp_size) restore_fpregs( 8, fp_regs, int_regs, context, ptrs ); + if (func->u.s.CR == 1) restore_regs( 30, 1, int_regs - 1, context, ptrs ); + restore_regs( 19, func->u.s.RegI, -saved_regs, context, ptrs ); } else { @@ -1582,7 +1588,7 @@ static void *unwind_packed_data( ULONG_PTR base, ULONG_PTR pc, RUNTIME_FUNCTION if (local_size <= 512) { /* stp x29,lr,[sp,-#local_size]! */ - if (pos++ > skip) restore_regs( 29, 2, -local_size, context, ptrs ); + if (pos++ > skip) restore_regs( 29, 2, -local_size_regs, context, ptrs ); break; } /* stp x29,lr,[sp,0] */ @@ -1603,16 +1609,16 @@ static void *unwind_packed_data( ULONG_PTR base, ULONG_PTR pc, RUNTIME_FUNCTION { if (func->u.s.RegF % 2 == 0 && pos++ > skip) /* str d%u,[sp,#fp_size] */ - restore_fpregs( 8 + func->u.s.RegF, 1, int_size + fp_size - 8, context, ptrs ); + restore_fpregs( 8 + func->u.s.RegF, 1, int_regs + fp_regs - 1, context, ptrs ); for (i = func->u.s.RegF / 2 - 1; i >= 0; i--) { if (pos++ <= skip) continue; if (!i && !int_size) /* stp d8,d9,[sp,-#regsave]! */ - restore_fpregs( 8, 2, -regsave, context, ptrs ); + restore_fpregs( 8, 2, -saved_regs, context, ptrs ); else /* stp dn,dn+1,[sp,#offset] */ - restore_fpregs( 8 + 2 * i, 2, int_size + 16 * i, context, ptrs ); + restore_fpregs( 8 + 2 * i, 2, int_regs + 2 * i, context, ptrs ); } }
@@ -1621,15 +1627,15 @@ static void *unwind_packed_data( ULONG_PTR base, ULONG_PTR pc, RUNTIME_FUNCTION if (func->u.s.RegI % 2) { /* stp xn,lr,[sp,#offset] */ - if (func->u.s.CR == 1) restore_regs( 30, 1, int_size - 8, context, ptrs ); + if (func->u.s.CR == 1) restore_regs( 30, 1, int_regs - 1, context, ptrs ); /* str xn,[sp,#offset] */ restore_regs( 18 + func->u.s.RegI, 1, - (func->u.s.RegI > 1) ? 8 * func->u.s.RegI - 8 : -regsave, + (func->u.s.RegI > 1) ? func->u.s.RegI - 1 : -saved_regs, context, ptrs ); } else if (func->u.s.CR == 1) /* str lr,[sp,#offset] */ - restore_regs( 30, 1, func->u.s.RegI ? int_size - 8 : -regsave, context, ptrs ); + restore_regs( 30, 1, func->u.s.RegI ? int_regs - 1 : -saved_regs, context, ptrs ); }
for (i = func->u.s.RegI / 2 - 1; i >= 0; i--) @@ -1637,10 +1643,10 @@ static void *unwind_packed_data( ULONG_PTR base, ULONG_PTR pc, RUNTIME_FUNCTION if (pos++ <= skip) continue; if (i) /* stp xn,xn+1,[sp,#offset] */ - restore_regs( 19 + 2 * i, 2, 16 * i, context, ptrs ); + restore_regs( 19 + 2 * i, 2, 2 * i, context, ptrs ); else /* stp x19,x20,[sp,-#regsave]! */ - restore_regs( 19, 2, -regsave, context, ptrs ); + restore_regs( 19, 2, -saved_regs, context, ptrs ); } } return NULL;
This matches what was done for RtlUnwindEx in 93ecc54ae560a3677dfa78ca0f5448d7aef0cbd9, applying the same change in call_function_handlers (when called from raise_exception).
Signed-off-by: Martin Storsjo martin@martin.st --- dlls/ntdll/signal_arm64.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index 8ccf3da57e6..b364f08e9aa 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -859,7 +859,7 @@ static NTSTATUS call_function_handlers( EXCEPTION_RECORD *rec, CONTEXT *orig_con dispatch.ContextRecord = &context; RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase, dispatch.ControlPc, dispatch.FunctionEntry, - &context, NULL, &frame, NULL ); + &context, &dispatch.HandlerData, &frame, NULL ); goto unwind_done; } default: @@ -889,7 +889,7 @@ static NTSTATUS call_function_handlers( EXCEPTION_RECORD *rec, CONTEXT *orig_con dispatch.ContextRecord = &context; RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase, dispatch.ControlPc, dispatch.FunctionEntry, - &context, NULL, &frame, NULL ); + &context, &dispatch.HandlerData, &frame, NULL ); teb_frame = teb_frame->Prev; goto unwind_done; }
In most cases, unwinding will use the frame pointer anyway, so it doesn't make much of a difference, but for cases where it won't, capture the actual stack pointer.
(In most cases on arm64, calling the RtlCaptureContext won't cause anything extra to be pushed on the stack at that point anyway).
Signed-off-by: Martin Storsjo martin@martin.st --- Also fix the vertical alignment for the row above. --- dlls/ntdll/signal_arm64.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index b364f08e9aa..4c9017b9e21 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -265,8 +265,8 @@ __ASM_STDCALL_FUNC( RtlCaptureContext, 8, "stp x23, x24, [x0, #0xc0]\n\t" /* context->X23,X24 */ "stp x25, x26, [x0, #0xd0]\n\t" /* context->X25,X26 */ "stp x27, x28, [x0, #0xe0]\n\t" /* context->X27,X28 */ - "stp x29, x30, [x0, #0xf0]\n\t" /* context->Fp,Lr */ - "add x1, x29, #0x10\n\t" + "stp x29, x30, [x0, #0xf0]\n\t" /* context->Fp,Lr */ + "mov x1, sp\n\t" "stp x1, x30, [x0, #0x100]\n\t" /* context->Sp,Pc */ "mov w1, #0x400000\n\t" /* CONTEXT_ARM64 */ "add w1, w1, #0x3\n\t" /* CONTEXT_FULL */
This happens with functions that aren't intended to return e.g. like _Unwind_Resume. In these cases, the return address is outside of the function (the first instruction in the next function).
Set the flag CONTEXT_UNWOUND_TO_CALL after unwinding to a callsite, and if this flag is set, look up a RUNTIME_FUNCTION based on Control.Pc - 4.
This isn't a complete (nor probably entirely correct) implementation of the flag CONTEXT_UNWOUND_TO_CALL, but it practically seems to work fine and fixes a large number of unwinding cases.
Signed-off-by: Martin Storsjo martin@martin.st --- dlls/ntdll/signal_arm64.c | 11 ++++++++++- include/winnt.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index 4c9017b9e21..1a97864786d 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -527,6 +527,7 @@ static NTSTATUS libunwind_virtual_unwind( ULONG_PTR ip, ULONG_PTR *frame, CONTEX *frame = context->Sp; context->Pc = context->u.s.Lr; context->Sp = context->Sp + sizeof(ULONG64); + context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; return STATUS_SUCCESS; }
@@ -577,6 +578,7 @@ static NTSTATUS libunwind_virtual_unwind( ULONG_PTR ip, ULONG_PTR *frame, CONTEX unw_get_reg( &cursor, UNW_AARCH64_X30, (unw_word_t *)&context->u.s.Lr ); unw_get_reg( &cursor, UNW_AARCH64_SP, (unw_word_t *)&context->Sp ); context->Pc = context->u.s.Lr; + context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
TRACE( "next function pc=%016lx%s\n", context->Pc, rc ? "" : " (last frame)" ); TRACE(" x0=%016lx x1=%016lx x2=%016lx x3=%016lx\n", @@ -614,10 +616,15 @@ static NTSTATUS virtual_unwind( ULONG type, DISPATCHER_CONTEXT *dispatch, CONTEX dispatch->ScopeIndex = 0; dispatch->EstablisherFrame = 0; dispatch->ControlPc = context->Pc; + // TODO: CONTEXT_UNWOUND_TO_CALL should be cleared if unwound past a + // signal frame. + dispatch->ControlPcIsUnwound = (context->ContextFlags & CONTEXT_UNWOUND_TO_CALL) != 0;
/* first look for PE exception information */
- if ((dispatch->FunctionEntry = lookup_function_info( context->Pc, &dispatch->ImageBase, &module ))) + if ((dispatch->FunctionEntry = lookup_function_info( + context->Pc - (dispatch->ControlPcIsUnwound ? 4 : 0), + &dispatch->ImageBase, &module ))) { dispatch->LanguageHandler = RtlVirtualUnwind( type, dispatch->ImageBase, context->Pc, dispatch->FunctionEntry, context, @@ -654,6 +661,7 @@ static NTSTATUS virtual_unwind( ULONG type, DISPATCHER_CONTEXT *dispatch, CONTEX dispatch->EstablisherFrame = context->u.s.Fp; dispatch->LanguageHandler = NULL; context->Pc = context->u.s.Lr; + context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; return STATUS_SUCCESS; }
@@ -1758,6 +1766,7 @@ PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG_PTR base, ULONG_PTR pc,
TRACE( "ret: lr=%lx sp=%lx handler=%p\n", context->u.s.Lr, context->Sp, handler ); context->Pc = context->u.s.Lr; + context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; *frame_ret = context->Sp; return handler; } diff --git a/include/winnt.h b/include/winnt.h index 34951766dd1..aa4daaa55f4 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -1863,6 +1863,8 @@ NTSYSAPI PVOID WINAPI RtlVirtualUnwind(DWORD,DWORD,DWORD,RUNTIME_FUNCTION*,CONTE #define CONTEXT_FLOATING_POINT (CONTEXT_ARM64 | 0x00000004) #define CONTEXT_DEBUG_REGISTERS (CONTEXT_ARM64 | 0x00000008)
+#define CONTEXT_UNWOUND_TO_CALL 0x20000000 + #define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER) #define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=72387
Your paranoid android.
=== debiant (64 bit WoW report) ===
ntdll: om.c:2133: Test failed: got 88
It has already been unconditionally enabled for macOS (which uses clang).
Signed-off-by: Martin Storsjo martin@martin.st --- The sanitizer runtimes seem to make the same assumption: https://github.com/llvm/llvm-project/blob/master/compiler-rt/lib/sanitizer_c... --- include/wine/asm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/wine/asm.h b/include/wine/asm.h index 7165f47e96d..7650d84a797 100644 --- a/include/wine/asm.h +++ b/include/wine/asm.h @@ -33,7 +33,7 @@ # define __ASM_STDCALL(name,args) __ASM_NAME(name) #endif
-#if defined(__GCC_HAVE_DWARF2_CFI_ASM) || defined(__APPLE__) +#if defined(__GCC_HAVE_DWARF2_CFI_ASM) || defined(__APPLE__) || defined(__clang__) # define __ASM_CFI(str) str #else # define __ASM_CFI(str)
Hi Martin,
On 28.05.2020 10:14, Martin Storsjo wrote:
It has already been unconditionally enabled for macOS (which uses clang).
Should we use it when building with llvm-mingw and SEH is enabled?
Thanks,
Jacek
On Thu, 28 May 2020, Jacek Caban wrote:
Hi Martin,
On 28.05.2020 10:14, Martin Storsjo wrote:
It has already been unconditionally enabled for macOS (which uses clang).
Should we use it when building with llvm-mingw and SEH is enabled?
Right - if targeting a PE, we should definitely be emitting SEH instead. (I have no idea how to express the fake frames used in the following patch in SEH form though - but I guess there's still some time before the core of ntdll can be built as a PE...)
Maybe (defined(__clang__) && defined(__ELF__)) for this then?
// Martin
On 28.05.2020 23:40, Martin Storsjö wrote:
On Thu, 28 May 2020, Jacek Caban wrote:
Hi Martin,
On 28.05.2020 10:14, Martin Storsjo wrote:
It has already been unconditionally enabled for macOS (which uses clang).
Should we use it when building with llvm-mingw and SEH is enabled?
Right - if targeting a PE, we should definitely be emitting SEH instead. (I have no idea how to express the fake frames used in the following patch in SEH form though - but I guess there's still some time before the core of ntdll can be built as a PE...)
Maybe (defined(__clang__) && defined(__ELF__)) for this then?
Maybe (defined(__clang__) && defined(__GNUC__) && !defined(__SEH__))? It would take care of macOS as well.
Jacek
On Fri, 29 May 2020, Jacek Caban wrote:
On 28.05.2020 23:40, Martin Storsjö wrote:
On Thu, 28 May 2020, Jacek Caban wrote:
Hi Martin,
On 28.05.2020 10:14, Martin Storsjo wrote:
It has already been unconditionally enabled for macOS (which uses clang).
Should we use it when building with llvm-mingw and SEH is enabled?
Right - if targeting a PE, we should definitely be emitting SEH instead. (I have no idea how to express the fake frames used in the following patch in SEH form though - but I guess there's still some time before the core of ntdll can be built as a PE...)
Maybe (defined(__clang__) && defined(__ELF__)) for this then?
Maybe (defined(__clang__) && defined(__GNUC__) && !defined(__SEH__))? It would take care of macOS as well.
Thanks, that sounds sensible - will send a patch with that soon.
// Martin
Call the consolidate frame callback before resuming. Before calling the callback, fill in ExceptionInformation[10] with the equivalent of dispatch.NonVolatileRegisters.
This fixes unwinding of MSVC C++ exceptions in a lot of cases.
Signed-off-by: Martin Storsjo martin@martin.st --- dlls/ntdll/ntdll.spec | 2 +- dlls/ntdll/signal_arm64.c | 134 +++++++++++++++++++++++++++++++++++++- 2 files changed, 134 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 6bd0a599a90..42532bd9f1c 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -913,7 +913,7 @@ @ stdcall RtlRemoveVectoredContinueHandler(ptr) @ stdcall RtlRemoveVectoredExceptionHandler(ptr) @ stdcall RtlResetRtlTranslations(ptr) -@ cdecl -arch=x86_64 RtlRestoreContext(ptr ptr) +@ cdecl -arch=arm64,x86_64 RtlRestoreContext(ptr ptr) @ stdcall RtlRestoreLastWin32Error(long) RtlSetLastWin32Error @ stub RtlRevertMemoryStream @ stub RtlRunDecodeUnicodeString diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index 1a97864786d..d7d963ced2d 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -69,6 +69,29 @@ WINE_DECLARE_DEBUG_CHANNEL(relay);
static pthread_key_t teb_key;
+/* layering violation: the setjmp buffer is defined in msvcrt, but used by RtlUnwindEx */ +struct MSVCRT_JUMP_BUFFER +{ + unsigned __int64 Frame; + unsigned __int64 Reserved; + unsigned __int64 X19; + unsigned __int64 X20; + unsigned __int64 X21; + unsigned __int64 X22; + unsigned __int64 X23; + unsigned __int64 X24; + unsigned __int64 X25; + unsigned __int64 X26; + unsigned __int64 X27; + unsigned __int64 X28; + unsigned __int64 Fp; + unsigned __int64 Lr; + unsigned __int64 Sp; + unsigned long Fpcr; + unsigned long Fpsr; + double D[8]; +}; + /*********************************************************************** * signal context platform-specific definitions */ @@ -1771,6 +1794,115 @@ PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG_PTR base, ULONG_PTR pc, return handler; }
+/********************************************************************** + * call_consolidate_callback + * + * Wrapper function to call a consolidate callback from a fake frame. + * If the callback executes RtlUnwindEx (like for example done in C++ handlers), + * we have to skip all frames which were already processed. To do that we + * trick the unwinding functions into thinking the call came from somewhere + * else. All CFI instructions are either DW_CFA_def_cfa_expression or + * DW_CFA_expression, and the expressions have the following format: + * + * DW_OP_breg29; sleb128 0x10 | Load x29 + 0x10 + * DW_OP_deref | Get *(x29 + 0x10) == context + * DW_OP_plus_uconst; uleb128 <OFFSET> | Add offset to get struct member + * [DW_OP_deref] | Dereference, only for CFA + */ +extern void * WINAPI call_consolidate_callback( CONTEXT *context, + void *(CALLBACK *callback)(EXCEPTION_RECORD *), + EXCEPTION_RECORD *rec, + TEB *teb ); +__ASM_GLOBAL_FUNC( call_consolidate_callback, + "stp x29, x30, [sp, #-0x20]!\n\t" + __ASM_CFI(".cfi_def_cfa_offset 32\n\t") + __ASM_CFI(".cfi_offset 29, -32\n\t") + __ASM_CFI(".cfi_offset 30, -24\n\t") + "mov x29, sp\n\t" + __ASM_CFI(".cfi_def_cfa_register 29\n\t") + "str x0, [sp, 0x10]\n\t" + __ASM_CFI(".cfi_remember_state\n\t") + __ASM_CFI(".cfi_escape 0x0f,0x07,0x8d,0x10,0x06,0x23,0x80,0x02,0x06\n\t") /* CFA */ + __ASM_CFI(".cfi_escape 0x10,0x13,0x06,0x8d,0x10,0x06,0x23,0xa0,0x01\n\t") /* x19 */ + __ASM_CFI(".cfi_escape 0x10,0x14,0x06,0x8d,0x10,0x06,0x23,0xa8,0x01\n\t") /* x20 */ + __ASM_CFI(".cfi_escape 0x10,0x15,0x06,0x8d,0x10,0x06,0x23,0xb0,0x01\n\t") /* x21 */ + __ASM_CFI(".cfi_escape 0x10,0x16,0x06,0x8d,0x10,0x06,0x23,0xb8,0x01\n\t") /* x22 */ + __ASM_CFI(".cfi_escape 0x10,0x17,0x06,0x8d,0x10,0x06,0x23,0xc0,0x01\n\t") /* x23 */ + __ASM_CFI(".cfi_escape 0x10,0x18,0x06,0x8d,0x10,0x06,0x23,0xc8,0x01\n\t") /* x24 */ + __ASM_CFI(".cfi_escape 0x10,0x19,0x06,0x8d,0x10,0x06,0x23,0xd0,0x01\n\t") /* x25 */ + __ASM_CFI(".cfi_escape 0x10,0x1a,0x06,0x8d,0x10,0x06,0x23,0xd8,0x01\n\t") /* x26 */ + __ASM_CFI(".cfi_escape 0x10,0x1b,0x06,0x8d,0x10,0x06,0x23,0xe0,0x01\n\t") /* x27 */ + __ASM_CFI(".cfi_escape 0x10,0x1c,0x06,0x8d,0x10,0x06,0x23,0xe8,0x01\n\t") /* x28 */ + __ASM_CFI(".cfi_escape 0x10,0x1d,0x06,0x8d,0x10,0x06,0x23,0xf0,0x01\n\t") /* x29 */ + __ASM_CFI(".cfi_escape 0x10,0x1e,0x06,0x8d,0x10,0x06,0x23,0xf8,0x01\n\t") /* x30 */ + __ASM_CFI(".cfi_escape 0x10,0x48,0x06,0x8d,0x10,0x06,0x23,0x90,0x03\n\t") /* d8 */ + __ASM_CFI(".cfi_escape 0x10,0x49,0x06,0x8d,0x10,0x06,0x23,0xa0,0x03\n\t") /* d9 */ + __ASM_CFI(".cfi_escape 0x10,0x4a,0x06,0x8d,0x10,0x06,0x23,0xb0,0x03\n\t") /* d10 */ + __ASM_CFI(".cfi_escape 0x10,0x4b,0x06,0x8d,0x10,0x06,0x23,0xc0,0x03\n\t") /* d11 */ + __ASM_CFI(".cfi_escape 0x10,0x4c,0x06,0x8d,0x10,0x06,0x23,0xd0,0x03\n\t") /* d12 */ + __ASM_CFI(".cfi_escape 0x10,0x4d,0x06,0x8d,0x10,0x06,0x23,0xe0,0x03\n\t") /* d13 */ + __ASM_CFI(".cfi_escape 0x10,0x4e,0x06,0x8d,0x10,0x06,0x23,0xf0,0x03\n\t") /* d14 */ + __ASM_CFI(".cfi_escape 0x10,0x4f,0x06,0x8d,0x10,0x06,0x23,0x80,0x04\n\t") /* d15 */ + "mov x0, x2\n\t" + "mov x18, x3\n\t" + "blr x1\n\t" + __ASM_CFI(".cfi_restore_state\n\t") + "ldp x29, x30, [sp], #32\n\t" + __ASM_CFI(".cfi_restore 30\n\t") + __ASM_CFI(".cfi_restore 29\n\t") + __ASM_CFI(".cfi_def_cfa 31, 0\n\t") + "ret") + +/******************************************************************* + * RtlRestoreContext (NTDLL.@) + */ +void CDECL RtlRestoreContext( CONTEXT *context, EXCEPTION_RECORD *rec ) +{ + EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList; + + if (rec && rec->ExceptionCode == STATUS_LONGJUMP && rec->NumberParameters >= 1) + { + struct MSVCRT_JUMP_BUFFER *jmp = (struct MSVCRT_JUMP_BUFFER *)rec->ExceptionInformation[0]; + int i; + + context->u.s.X19 = jmp->X19; + context->u.s.X20 = jmp->X20; + context->u.s.X21 = jmp->X21; + context->u.s.X22 = jmp->X22; + context->u.s.X23 = jmp->X23; + context->u.s.X24 = jmp->X24; + context->u.s.X25 = jmp->X25; + context->u.s.X26 = jmp->X26; + context->u.s.X27 = jmp->X27; + context->u.s.X28 = jmp->X28; + context->u.s.Fp = jmp->Fp; + context->u.s.Lr = jmp->Lr; + context->Sp = jmp->Sp; + context->Fpcr = jmp->Fpcr; + context->Fpsr = jmp->Fpsr; + + for (i = 0; i < 8; i++) + context->V[8+i].D[0] = jmp->D[0]; + } + else if (rec && rec->ExceptionCode == STATUS_UNWIND_CONSOLIDATE && rec->NumberParameters >= 1) + { + PVOID (CALLBACK *consolidate)(EXCEPTION_RECORD *) = (void *)rec->ExceptionInformation[0]; + TRACE( "calling consolidate callback %p (rec=%p)\n", consolidate, rec ); + rec->ExceptionInformation[10] = (ULONG_PTR)&context->u.s.X19; + + context->Pc = (ULONG64)call_consolidate_callback( context, consolidate, rec, NtCurrentTeb() ); + } + + /* hack: remove no longer accessible TEB frames */ + while ((ULONG64)teb_frame < context->Sp) + { + TRACE( "removing TEB frame: %p\n", teb_frame ); + teb_frame = __wine_pop_frame( teb_frame ); + } + + TRACE( "returning to %lx stack %lx\n", context->Pc, context->Sp ); + set_cpu_context( context ); +}
/******************************************************************* * RtlUnwindEx (NTDLL.@) @@ -1907,7 +2039,7 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec
context->u.s.X0 = (ULONG64)retval; context->Pc = (ULONG64)target_ip; - set_cpu_context( context ); + RtlRestoreContext(context, rec); }
Some language specific handlers, called by call_handler, can use the NonVolatileRegisters to restore the context before running code, and that assumes that NonVolatileRegisters contains the frame pointer as it was within the function (before unwinding).
Signed-off-by: Martin Storsjo martin@martin.st --- dlls/ntdll/signal_arm64.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index d7d963ced2d..88492755bec 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -847,14 +847,16 @@ static NTSTATUS call_function_handlers( EXCEPTION_RECORD *rec, CONTEXT *orig_con EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList; UNWIND_HISTORY_TABLE table; DISPATCHER_CONTEXT dispatch; - CONTEXT context; + CONTEXT context, prev_context; NTSTATUS status;
context = *orig_context; dispatch.TargetPc = 0; dispatch.ContextRecord = &context; dispatch.HistoryTable = &table; - dispatch.NonVolatileRegisters = (BYTE *)&context.u.s.X19; + prev_context = context; + dispatch.NonVolatileRegisters = (BYTE *)&prev_context.u.s.X19; + for (;;) { status = virtual_unwind( UNW_FLAG_EHANDLER, &dispatch, &context ); @@ -931,6 +933,7 @@ static NTSTATUS call_function_handlers( EXCEPTION_RECORD *rec, CONTEXT *orig_con }
if (context.Sp == (ULONG64)NtCurrentTeb()->Tib.StackBase) break; + prev_context = context; } return STATUS_UNHANDLED_EXCEPTION; }
Signed-off-by: Martin Storsjo martin@martin.st --- dlls/ntdll/signal_arm64.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index 88492755bec..cc6c1574ecb 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -135,6 +135,8 @@ static DWORD64 get_fault_esr( ucontext_t *sigcontext )
static const size_t teb_size = 0x2000; /* we reserve two pages for the TEB */
+typedef void (*raise_func)( EXCEPTION_RECORD *rec, CONTEXT *context ); + /* stack layout when calling an exception raise function */ struct stack_layout { @@ -1044,6 +1046,24 @@ static void WINAPI raise_generic_exception( EXCEPTION_RECORD *rec, CONTEXT *cont raise_status( status, rec ); }
+extern void raise_func_trampoline( EXCEPTION_RECORD *rec, CONTEXT *context, raise_func func, void *sp ); +__ASM_GLOBAL_FUNC( raise_func_trampoline, + __ASM_CFI(".cfi_signal_frame\n\t") + "stp x29, x30, [sp, #-0x20]!\n\t" + __ASM_CFI(".cfi_def_cfa_offset 32\n\t") + __ASM_CFI(".cfi_offset 29, -32\n\t") + __ASM_CFI(".cfi_offset 30, -24\n\t") + "mov x29, sp\n\t" + __ASM_CFI(".cfi_def_cfa_register 29\n\t") + "str x3, [sp, 0x10]\n\t" + __ASM_CFI(".cfi_remember_state\n\t") + __ASM_CFI(".cfi_escape 0x0f,0x03,0x8d,0x10,0x06\n\t") /* CFA */ + __ASM_CFI(".cfi_escape 0x10,0x1d,0x02,0x8d,0x00\n\t") /* x29 */ + __ASM_CFI(".cfi_escape 0x10,0x1e,0x02,0x8d,0x08\n\t") /* x30 */ + "blr x2\n\t" + __ASM_CFI(".cfi_restore_state\n\t") + "brk #1") + /*********************************************************************** * setup_raise_exception * @@ -1058,10 +1078,13 @@ static void setup_raise_exception( ucontext_t *sigcontext, struct stack_layout * restore_context( &stack->context, sigcontext ); return; } + REGn_sig(3, sigcontext) = SP_sig(sigcontext); /* original stack pointer, fourth arg for raise_func_trampoline */ SP_sig(sigcontext) = (ULONG_PTR)stack; - PC_sig(sigcontext) = (ULONG_PTR)raise_generic_exception; + LR_sig(sigcontext) = PC_sig(sigcontext); + PC_sig(sigcontext) = (ULONG_PTR)raise_func_trampoline; // raise_generic_exception; REGn_sig(0, sigcontext) = (ULONG_PTR)&stack->rec; /* first arg for raise_generic_exception */ REGn_sig(1, sigcontext) = (ULONG_PTR)&stack->context; /* second arg for raise_generic_exception */ + REGn_sig(2, sigcontext) = (ULONG_PTR)raise_generic_exception; /* third arg for raise_func_trampoline */ REGn_sig(18, sigcontext) = (ULONG_PTR)NtCurrentTeb(); }
If the handlers returned ExceptionContinueExecution and we restore the stored context, make sure it's a context that ends up returning from the RtlRaiseException function.
This matches how it's done on x86_64.
Signed-off-by: Martin Storsjo martin@martin.st --- dlls/ntdll/signal_arm64.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-)
diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index cc6c1574ecb..c522308cd9a 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -2095,14 +2095,29 @@ NTSTATUS WINAPI NtRaiseException( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL /*********************************************************************** * RtlRaiseException (NTDLL.@) */ -void WINAPI RtlRaiseException( EXCEPTION_RECORD *rec ) -{ - CONTEXT context; - - RtlCaptureContext( &context ); - rec->ExceptionAddress = (LPVOID)context.Pc; - RtlRaiseStatus( NtRaiseException( rec, &context, TRUE )); -} +__ASM_STDCALL_FUNC( RtlRaiseException, 4, + "sub sp, sp, #0x3b0\n\t" /* 0x390 (context) + 0x20 */ + "stp x29, x30, [sp]\n\t" + __ASM_CFI(".cfi_def_cfa x29, 944\n\t") + __ASM_CFI(".cfi_offset x30, -936\n\t") + __ASM_CFI(".cfi_offset x29, -944\n\t") + "mov x29, sp\n\t" + "str x0, [sp, #0x10]\n\t" + "add x0, sp, #0x20\n\t" + "bl " __ASM_NAME("RtlCaptureContext") "\n\t" + "add x1, sp, #0x20\n\t" /* context pointer */ + "add x2, sp, #0x3b0\n\t" /* orig stack pointer */ + "str x2, [x1, #0x100]\n\t" /* context->Sp */ + "ldr x0, [sp, #0x10]\n\t" /* original first parameter */ + "str x0, [x1, #0x08]\n\t" /* context->X0 */ + "ldp x4, x5, [sp]\n\t" /* frame pointer, return address */ + "stp x4, x5, [x1, #0xf0]\n\t" /* context->Fp, Lr */ + "str x5, [x1, #0x108]\n\t" /* context->Pc */ + "str x5, [x1, #0x108]\n\t" /* context->Pc */ + "str x5, [x0, #0x10]\n\t" /* rec->ExceptionAddress */ + "mov x2, #1\n\t" + "bl " __ASM_NAME("NtRaiseException") "\n\t" + "bl " __ASM_NAME("RtlRaiseStatus") /* does not return */ );
/************************************************************************* * RtlCaptureStackBackTrace (NTDLL.@)