ntdll: Restore the original Lr value when unwinding through raise_func_trampoline on arm64.

When a signal is raised in a leaf function (that hasn't backed up
the Lr register itself), we need to restore both Pc and Lr separately.
This doesn't seem to be possible via libunwind, so add a special case
unwinding rule for this function.

As we have a special case for the function, we could also just as well
skip normal dwarf unwinding for this function altogether and just
restore the full context from the stack.

Signed-off-by: Martin Storsjo <martin@martin.st>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Martin Storsjo 2020-10-05 12:16:40 +03:00 committed by Alexandre Julliard
parent df794f3425
commit dcf3874e00
1 changed files with 51 additions and 11 deletions

View File

@ -157,6 +157,7 @@ static inline struct arm64_thread_data *arm64_thread_data(void)
return (struct arm64_thread_data *)ntdll_get_thread_data()->cpu_data; return (struct arm64_thread_data *)ntdll_get_thread_data()->cpu_data;
} }
extern void raise_func_trampoline( EXCEPTION_RECORD *rec, CONTEXT *context, void *dispatcher );
/*********************************************************************** /***********************************************************************
* unwind_builtin_dll * unwind_builtin_dll
@ -276,6 +277,17 @@ NTSTATUS CDECL unwind_builtin_dll( ULONG type, DISPATCHER_CONTEXT *dispatch, CON
unw_get_reg( &cursor, UNW_REG_IP, (unw_word_t *)&context->Pc ); unw_get_reg( &cursor, UNW_REG_IP, (unw_word_t *)&context->Pc );
context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
if (info.start_ip == (unw_word_t)raise_func_trampoline) {
/* raise_func_trampoline has a full CONTEXT stored on the stack;
* restore the original Lr value from there. The function we unwind
* to might be a leaf function that hasn't backed up its own original
* Lr value on the stack.
* We could also just restore the full context here without doing
* unw_step at all. */
const CONTEXT *next_ctx = (const CONTEXT *) dispatch->EstablisherFrame;
context->u.s.Lr = next_ctx->u.s.Lr;
}
TRACE( "next function pc=%016lx%s\n", context->Pc, rc ? "" : " (last frame)" ); TRACE( "next function pc=%016lx%s\n", context->Pc, rc ? "" : " (last frame)" );
TRACE(" x0=%016lx x1=%016lx x2=%016lx x3=%016lx\n", TRACE(" x0=%016lx x1=%016lx x2=%016lx x3=%016lx\n",
context->u.s.X0, context->u.s.X1, context->u.s.X2, context->u.s.X3 ); context->u.s.X0, context->u.s.X1, context->u.s.X2, context->u.s.X3 );
@ -573,20 +585,49 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
} }
extern void raise_func_trampoline( EXCEPTION_RECORD *rec, CONTEXT *context, void *dispatcher, void *sp ); /* Note, unwind_builtin_dll above has hardcoded assumptions on how this
* function stores things on the stack; if modified, modify that one in
* sync as well. */
__ASM_GLOBAL_FUNC( raise_func_trampoline, __ASM_GLOBAL_FUNC( raise_func_trampoline,
__ASM_CFI(".cfi_signal_frame\n\t") "stp x29, x30, [sp, #-0x30]!\n\t"
"stp x29, x30, [sp, #-0x20]!\n\t" __ASM_CFI(".cfi_def_cfa_offset 48\n\t")
__ASM_CFI(".cfi_def_cfa_offset 32\n\t") __ASM_CFI(".cfi_offset 29, -48\n\t")
__ASM_CFI(".cfi_offset 29, -32\n\t") __ASM_CFI(".cfi_offset 30, -40\n\t")
__ASM_CFI(".cfi_offset 30, -24\n\t") "stp x0, x1, [sp, #0x10]\n\t"
"str x2, [sp, #0x20]\n\t"
"mov x29, sp\n\t" "mov x29, sp\n\t"
__ASM_CFI(".cfi_def_cfa_register 29\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_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 */ /* Memcpy the context onto the stack */
__ASM_CFI(".cfi_escape 0x10,0x1e,0x02,0x8d,0x08\n\t") /* x30 */ "sub sp, sp, #0x390\n\t"
"mov x0, sp\n\t"
"mov x2, #0x390\n\t"
"bl " __ASM_NAME("memcpy") "\n\t"
__ASM_CFI(".cfi_def_cfa 31, 0\n\t")
__ASM_CFI(".cfi_escape 0x0f,0x04,0x8f,0x80,0x02,0x06\n\t") /* CFA, DW_OP_breg31 + 0x100, DW_OP_deref */
__ASM_CFI(".cfi_escape 0x10,0x13,0x03,0x8f,0xa0,0x01\n\t") /* x19, DW_OP_breg31 + 0xA0 */
__ASM_CFI(".cfi_escape 0x10,0x14,0x03,0x8f,0xa8,0x01\n\t") /* x20 */
__ASM_CFI(".cfi_escape 0x10,0x15,0x03,0x8f,0xb0,0x01\n\t") /* x21 */
__ASM_CFI(".cfi_escape 0x10,0x16,0x03,0x8f,0xb8,0x01\n\t") /* x22 */
__ASM_CFI(".cfi_escape 0x10,0x17,0x03,0x8f,0xc0,0x01\n\t") /* x23 */
__ASM_CFI(".cfi_escape 0x10,0x18,0x03,0x8f,0xc8,0x01\n\t") /* x24 */
__ASM_CFI(".cfi_escape 0x10,0x19,0x03,0x8f,0xd0,0x01\n\t") /* x25 */
__ASM_CFI(".cfi_escape 0x10,0x1a,0x03,0x8f,0xd8,0x01\n\t") /* x26 */
__ASM_CFI(".cfi_escape 0x10,0x1b,0x03,0x8f,0xe0,0x01\n\t") /* x27 */
__ASM_CFI(".cfi_escape 0x10,0x1c,0x03,0x8f,0xe8,0x01\n\t") /* x28 */
__ASM_CFI(".cfi_escape 0x10,0x1d,0x03,0x8f,0xf0,0x01\n\t") /* x29 */
__ASM_CFI(".cfi_escape 0x10,0x1e,0x03,0x8f,0x88,0x02\n\t") /* x30 = pc */
__ASM_CFI(".cfi_escape 0x10,0x48,0x03,0x8f,0x90,0x03\n\t") /* d8 */
__ASM_CFI(".cfi_escape 0x10,0x49,0x03,0x8f,0x98,0x03\n\t") /* d9 */
__ASM_CFI(".cfi_escape 0x10,0x4a,0x03,0x8f,0xa0,0x03\n\t") /* d10 */
__ASM_CFI(".cfi_escape 0x10,0x4b,0x03,0x8f,0xa8,0x03\n\t") /* d11 */
__ASM_CFI(".cfi_escape 0x10,0x4c,0x03,0x8f,0xb0,0x03\n\t") /* d12 */
__ASM_CFI(".cfi_escape 0x10,0x4d,0x03,0x8f,0xb8,0x03\n\t") /* d13 */
__ASM_CFI(".cfi_escape 0x10,0x4e,0x03,0x8f,0xc0,0x03\n\t") /* d14 */
__ASM_CFI(".cfi_escape 0x10,0x4f,0x03,0x8f,0xc8,0x03\n\t") /* d15 */
"ldp x0, x1, [x29, #0x10]\n\t"
"ldr x2, [x29, #0x20]\n\t"
"blr x2\n\t" "blr x2\n\t"
__ASM_CFI(".cfi_restore_state\n\t") __ASM_CFI(".cfi_restore_state\n\t")
"brk #1") "brk #1")
@ -625,7 +666,6 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
stack->rec = *rec; stack->rec = *rec;
stack->context = context; stack->context = context;
REGn_sig(3, sigcontext) = SP_sig(sigcontext); /* original stack pointer, fourth arg for raise_func_trampoline */
SP_sig(sigcontext) = (ULONG_PTR)stack; SP_sig(sigcontext) = (ULONG_PTR)stack;
LR_sig(sigcontext) = PC_sig(sigcontext); LR_sig(sigcontext) = PC_sig(sigcontext);
PC_sig(sigcontext) = (ULONG_PTR)raise_func_trampoline; PC_sig(sigcontext) = (ULONG_PTR)raise_func_trampoline;