From f760976803346fb2308d4c3c9e6f8ad360e62dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Thu, 20 Oct 2022 13:44:24 +0300 Subject: [PATCH] ntdll: Add ARM EHABI unwind instructions in assembly functions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On most ELF platforms on ARM, ARM EHABI is the unwind info format normally used, instead of DWARF like on most other platforms. Currently, when unwinding through ELF objects with libunwind, the libraries don't have any .eh_frame section mapped at runtime (since DWARF isn't used for unwinding). Instead, what happens is that libunwind ends up loading .debug_frame from the libraries on disk instead. Therefore, currently, ELF unwinding relies on the .so files not being stripped. This patch adds the necessary EHABI unwinding instructions in the assembly functions that currently have DWARF CFI instructions. EHABI isn't signaled via any specific preprocessor macro, but is signaled by the absence of other unwind mechanisms (such as __ARM_DWARF_EH__ and __SEH__, or maybe SjLj). Mark the asm functions in the preloaders as .cantunwind, to avoid undefined references to __aeabi_unwind_cpp_pr* functions. Also mark other assembly functions as .cantunwind; for signal_exit_thread this is essential if the function is marked with .fnstart/.fnend - otherwise exiting threads does hang. (pthread_exit internally calls _Unwind_ForcedUnwind, which would hang if signal_exit_thread had .fnstart without any matching unwind info). This would, in principle, allow unwinding through these functions with libunwind, for versions of libunwind that can parse the EHABI unwind info - see e.g. https://github.com/libunwind/libunwind/commit/4d779f55c0d0a3814453517e54cd0f7bed69ca74. (This commit isn't yet in any current release AFAIK). Unwinding with EHABI via libunwind would require a few tweaks to the libunwind interface usage in unix/signal_arm.c though, since e.g. the unw_get_proc_info call fails if there's no .eh_frame or .debug_frame available. Signed-off-by: Martin Storsjö --- dlls/ntdll/signal_arm.c | 15 +++++++++++++++ dlls/ntdll/unix/signal_arm.c | 10 ++++++++++ include/wine/asm.h | 8 +++++++- loader/preloader.c | 3 +++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/signal_arm.c b/dlls/ntdll/signal_arm.c index c3674487277..0952be6ba33 100644 --- a/dlls/ntdll/signal_arm.c +++ b/dlls/ntdll/signal_arm.c @@ -1129,6 +1129,18 @@ __ASM_GLOBAL_FUNC( call_consolidate_callback, __ASM_CFI(".cfi_escape 0x10,0x8e,0x02,0x03,0x7d,0xc0,0x01\n\t") /* DW_CFA_expression: D14 DW_OP_breg13 + 192 */ __ASM_CFI(".cfi_escape 0x10,0x8f,0x02,0x03,0x7d,0xc8,0x01\n\t") /* DW_CFA_expression: D15 DW_OP_breg13 + 200 */ #endif + /* These EHABI opcodes are to be read bottom up - they + * restore relevant registers from the CONTEXT. */ + __ASM_EHABI(".save {sp}\n\t") /* Restore Sp last */ + __ASM_EHABI(".pad #-(0x80 + 0x0c + 0x0c)\n\t") /* Move back across D0-D15, Cpsr, Fpscr, Padding, Pc, Lr and Sp */ + __ASM_EHABI(".vsave {d8-d15}\n\t") + __ASM_EHABI(".pad #0x40\n\t") /* Skip past D0-D7 */ + __ASM_EHABI(".pad #0x0c\n\t") /* Skip past Cpsr, Fpscr and Padding */ + __ASM_EHABI(".save {lr, pc}\n\t") + __ASM_EHABI(".pad #0x08\n\t") /* Skip past R12 and Sp - Sp is restored last */ + __ASM_EHABI(".save {r4-r11}\n\t") + __ASM_EHABI(".pad #0x14\n\t") /* Skip past ContextFlags and R0-R3 */ + "ldrd r1, r2, [sp, #0x1a4]\n\t" "mov r0, r2\n\t" "blx r1\n\t" @@ -1327,6 +1339,7 @@ extern LONG __C_ExecuteExceptionFilter(PEXCEPTION_POINTERS ptrs, PVOID frame, PUCHAR nonvolatile); __ASM_GLOBAL_FUNC( __C_ExecuteExceptionFilter, "push {r4-r11,lr}\n\t" + __ASM_EHABI(".save {r4-r11,lr}\n\t") __ASM_SEH(".seh_save_regs_w {r4-r11,lr}\n\t") __ASM_SEH(".seh_endprologue\n\t") @@ -1439,8 +1452,10 @@ EXCEPTION_DISPOSITION WINAPI __C_specific_handler( EXCEPTION_RECORD *rec, */ __ASM_STDCALL_FUNC( RtlRaiseException, 4, "push {r0, lr}\n\t" + __ASM_EHABI(".save {r0, lr}\n\t") __ASM_SEH(".seh_save_regs {r0, lr}\n\t") "sub sp, sp, #0x1a0\n\t" /* sizeof(CONTEXT) */ + __ASM_EHABI(".pad #0x1a0\n\t") __ASM_SEH(".seh_stackalloc 0x1a0\n\t") __ASM_SEH(".seh_endprologue\n\t") __ASM_CFI(".cfi_adjust_cfa_offset 424\n\t") diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index 5d1478a1ff4..add01687c4b 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -593,6 +593,11 @@ __ASM_GLOBAL_FUNC( raise_func_trampoline, "push {r3}\n\t" /* Original Sp */ __ASM_CFI(".cfi_escape 0x0f,0x03,0x7D,0x04,0x06\n\t") /* CFA, DW_OP_breg13 + 0x04, DW_OP_deref */ __ASM_CFI(".cfi_escape 0x10,0x0e,0x02,0x7D,0x0c\n\t") /* LR, DW_OP_breg13 + 0x0c */ + __ASM_EHABI(".save {sp}\n\t") + __ASM_EHABI(".pad #-12\n\t") + __ASM_EHABI(".save {pc}\n\t") + __ASM_EHABI(".pad #8\n\t") + __ASM_EHABI(".save {lr}\n\t") /* We can't express restoring both Pc and Lr with CFI * directives, but we manually load Lr from the stack * in unwind_builtin_dll above. */ @@ -1171,6 +1176,7 @@ void DECLSPEC_HIDDEN call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, B * signal_start_thread */ __ASM_GLOBAL_FUNC( signal_start_thread, + __ASM_EHABI(".cantunwind\n\t") "push {r4-r12,lr}\n\t" /* store exit frame */ "str sp, [r3, #0x1d4]\n\t" /* arm_thread_data()->exit_frame */ @@ -1187,6 +1193,7 @@ __ASM_GLOBAL_FUNC( signal_start_thread, * signal_exit_thread */ __ASM_GLOBAL_FUNC( signal_exit_thread, + __ASM_EHABI(".cantunwind\n\t") "ldr r3, [r2, #0x1d4]\n\t" /* arm_thread_data()->exit_frame */ "mov ip, #0\n\t" "str ip, [r2, #0x1d4]\n\t" @@ -1200,6 +1207,7 @@ __ASM_GLOBAL_FUNC( signal_exit_thread, * __wine_syscall_dispatcher */ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, + __ASM_EHABI(".cantunwind\n\t") "mrc p15, 0, r1, c13, c0, 2\n\t" /* NtCurrentTeb() */ "ldr r1, [r1, #0x1d8]\n\t" /* arm_thread_data()->syscall_frame */ "add r0, r1, #0x10\n\t" @@ -1276,6 +1284,7 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, * __wine_setjmpex */ __ASM_GLOBAL_FUNC( __wine_setjmpex, + __ASM_EHABI(".cantunwind\n\t") "stm r0, {r1,r4-r11}\n" /* jmp_buf->Frame,R4..R11 */ "str sp, [r0, #0x24]\n\t" /* jmp_buf->Sp */ "str lr, [r0, #0x28]\n\t" /* jmp_buf->Pc */ @@ -1293,6 +1302,7 @@ __ASM_GLOBAL_FUNC( __wine_setjmpex, * __wine_longjmp */ __ASM_GLOBAL_FUNC( __wine_longjmp, + __ASM_EHABI(".cantunwind\n\t") "ldm r0, {r3-r11}\n\t" /* jmp_buf->Frame,R4..R11 */ "ldr sp, [r0, #0x24]\n\t" /* jmp_buf->Sp */ "ldr r2, [r0, #0x28]\n\t" /* jmp_buf->Pc */ diff --git a/include/wine/asm.h b/include/wine/asm.h index 2c996ccd86b..2448f591189 100644 --- a/include/wine/asm.h +++ b/include/wine/asm.h @@ -41,6 +41,12 @@ # define __ASM_CFI(str) #endif +#if defined(__arm__) && defined(__ELF__) && defined(__GNUC__) && !defined(__SEH__) && !defined(__ARM_DWARF_EH__) +# define __ASM_EHABI(str) str +#else +# define __ASM_EHABI(str) +#endif + #if defined(__SEH__) || (defined(_MSC_VER) && defined(__clang__) && (defined(__x86_64__) || defined(__aarch64__))) # if defined(__aarch64__) && defined(__clang_major__) && (__clang_major__ < 12 || defined(__apple_build_version__)) /* Clang got support for aarch64 SEH assembly directives in Clang 12, @@ -86,7 +92,7 @@ #define __ASM_DEFINE_FUNC(name,code) \ __ASM_BLOCK_BEGIN(__LINE__) \ asm(".text\n\t.align 4\n\t.globl " name "\n\t" __ASM_FUNC_TYPE(name) __ASM_SEH("\n\t.seh_proc " name) "\n" name ":\n\t" \ - __ASM_CFI(".cfi_startproc\n\t") code __ASM_CFI("\n\t.cfi_endproc") __ASM_SEH("\n\t.seh_endproc") __ASM_FUNC_SIZE(name)); \ + __ASM_CFI(".cfi_startproc\n\t") __ASM_EHABI(".fnstart\n\t") code __ASM_CFI("\n\t.cfi_endproc") __ASM_EHABI("\n\t.fnend") __ASM_SEH("\n\t.seh_endproc") __ASM_FUNC_SIZE(name)); \ __ASM_BLOCK_END #define __ASM_GLOBAL_FUNC(name,code) __ASM_DEFINE_FUNC(__ASM_NAME(#name),code) diff --git a/loader/preloader.c b/loader/preloader.c index d88964e9c4b..72556f09720 100644 --- a/loader/preloader.c +++ b/loader/preloader.c @@ -542,6 +542,7 @@ void *thread_data[256]; void _start(void); extern char _end[]; __ASM_GLOBAL_FUNC(_start, + __ASM_EHABI(".cantunwind\n\t") "mov r0, sp\n\t" "sub sp, sp, #144\n\t" /* allocate some space for extra aux values */ "str r0, [sp]\n\t" /* orig stack pointer */ @@ -564,6 +565,7 @@ __ASM_GLOBAL_FUNC(_start, #define SYSCALL_FUNC( name, nr ) \ __ASM_GLOBAL_FUNC( name, \ + __ASM_EHABI(".cantunwind\n\t") \ "push {r4-r5,r7,lr}\n\t" \ "ldr r4, [sp, #16]\n\t" \ "ldr r5, [sp, #20]\n\t" \ @@ -576,6 +578,7 @@ __ASM_GLOBAL_FUNC(_start, #define SYSCALL_NOERR( name, nr ) \ __ASM_GLOBAL_FUNC( name, \ + __ASM_EHABI(".cantunwind\n\t") \ "push {r7,lr}\n\t" \ "mov r7, #" #nr "\n\t" \ "svc #0\n\t" \