On 2/8/22 04:05, RĂ©mi Bernon wrote:
Making sure stack pointer points to previous syscall / exit frame before entering a syscall, and restoring the PE frame information on return.
Would this unwind into the PE side? I don't thunk libunwind would be able to handle SEH unwind infos; perhaps what we want to do instead is to unwind into exit_frame?
If what we indeed want is to unwind to PE, I have a much accurate (albeit ugly) version for the CFI expressions.
It supports: - dual fxsave / xsave handling (the bulk of the complexity) - can unwind from every point inside the syscall dispatcher - works with GDB (attach to wine via normal GDB, and it will trace through the PE up to the initial frame)
--- dlls/ntdll/unix/inline_dwarf.h | 59 +++++++++ dlls/ntdll/unix/signal_i386.c | 213 +++++++++++++++++++++++++++++++-- 2 files changed, 260 insertions(+), 12 deletions(-) create mode 100644 dlls/ntdll/unix/inline_dwarf.h
diff --git a/dlls/ntdll/unix/inline_dwarf.h b/dlls/ntdll/unix/inline_dwarf.h new file mode 100644 index 00000000000..0027164a9f3 --- /dev/null +++ b/dlls/ntdll/unix/inline_dwarf.h @@ -0,0 +1,59 @@ +#ifndef __WINE_INLINE_DWARF_H +#define __WINE_INLINE_DWARF_H + +#define PP_ARG_64TH( \ + _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \ + _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ + _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \ + _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \ + _61, _62, _63, N, ...) N + +#define PP_NARG(...) PP_ARG_64TH(__VA_ARGS__, \ + 63, 62, 61, 60, \ + 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \ + 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \ + 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \ + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \ + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \ + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) + +#define IDW_STRINGIFY_(...) #__VA_ARGS__ +#define IDW_STRINGIFY(...) IDW_STRINGIFY_(__VA_ARGS__) +#define IDW_EXPAND(...) __VA_ARGS__ + +#define I_DW_CFA_expression 0x10 +#define I_DW_CFA_val_expression 0x16 + +#define I_DW_OP_deref 0x06 +#define I_DW_OP_const1u 0x08 +#define I_DW_OP_dup 0x12 +#define I_DW_OP_swap 0x16 +#define I_DW_OP_and 0x1a +#define I_DW_OP_neg 0x1f +#define I_DW_OP_not 0x20 +#define I_DW_OP_or 0x21 +#define I_DW_OP_plus 0x22 +#define I_DW_OP_plus_uconst 0x23 +#define I_DW_OP_bra 0x28 +#define I_DW_OP_eq 0x29 +#define I_DW_OP_lit0 0x30 +#define I_DW_OP_reg0 0x50 +#define I_DW_OP_deref_size 0x94 +#define I_DW_OP_call_frame_cfa 0x9c + +#define I_MAX_DW_OP_LITERAL 0x1f + +#define IDW_const1u(x) I_DW_OP_const1u, x + +#define I_DW_BLOCK(...) \ + IDW_STRINGIFY(PP_NARG(__VA_ARGS__)) ", " IDW_STRINGIFY(__VA_ARGS__) + +#define I_DW_then_else(t, f) \ + I_DW_OP_lit0, I_DW_OP_eq, I_DW_OP_neg, \ + I_DW_OP_dup, IDW_EXPAND f, \ + I_DW_OP_and, I_DW_OP_swap, I_DW_OP_not, IDW_EXPAND t, \ + I_DW_OP_and, I_DW_OP_or + +#endif diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 6bb5649e2b5..3bda41de0c6 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -2466,6 +2466,133 @@ __ASM_GLOBAL_FUNC( signal_exit_thread, "pushl %eax\n\t" "call *%ecx" )
+#define SYSCALL_FRAME_SYSCALL_FLAGS_OFFSET 0 +#define SYSCALL_FRAME_RESTORE_FLAGS_OFFSET 2 +#define I_SYSCALL_HAVE_FXSAVE_OR_LATER 7 +#define I_CONTEXT_INTEGER 2 + +typedef char syscall_frame_syscall_flags_offset[( + offsetof(struct syscall_frame, syscall_flags) == SYSCALL_FRAME_SYSCALL_FLAGS_OFFSET ? 1 : -1 +)]; +typedef char syscall_frame_restore_flags_offset[( + offsetof(struct syscall_frame, restore_flags) == SYSCALL_FRAME_RESTORE_FLAGS_OFFSET +)]; +typedef char syscall_have_fxsave_or_later[( + I_SYSCALL_HAVE_FXSAVE_OR_LATER == (SYSCALL_HAVE_XSAVE | SYSCALL_HAVE_XSAVEC | + SYSCALL_HAVE_FXSAVE) ? 1 : -1 +)]; +typedef char i_context_integer[(CONTEXT_i386 | I_CONTEXT_INTEGER) == CONTEXT_INTEGER ? 1 : -1]; + +#include "inline_dwarf.h" + +#if SYSCALL_FRAME_SYSCALL_FLAGS_OFFSET != 0 +#error please modify ASM_CFI_XSAVE_OR_FSAVE to add offset before dup +#endif + +#if SYSCALL_FRAME_RESTORE_FLAGS_OFFSET <= 0x7f +#define I_DW_OP_plus_off_restore_flags \ + I_DW_OP_plus_uconst, SYSCALL_FRAME_RESTORE_FLAGS_OFFSET +#else +#error please define I_DW_OP_plus_off_restore_flags to hold syscall_frame::restore_flags offset +#endif + +#if I_SYSCALL_HAVE_FXSAVE_OR_LATER <= I_MAX_DW_OP_LITERAL +#define I_DW_OP_lit_SYSCALL_HAVE_FXSAVE_OR_LATER I_DW_OP_lit0 + I_SYSCALL_HAVE_FXSAVE_OR_LATER +#else +#error please define I_DW_OP_lit_SYSCALL_HAVE_FXSAVE_OR_LATER to hold I_SYSCALL_HAVE_FXSAVE_OR_LATER value +#endif + +#if I_CONTEXT_INTEGER <= I_MAX_DW_OP_LITERAL +#define I_DW_OP_lit_CONTEXT_INTEGER I_DW_OP_lit0 + I_CONTEXT_INTEGER +#else +#error please define I_DW_OP_lit_CONTEXT_INTEGER to hold I_CONTEXT_INTEGER value +#endif + +#define ASM_CFI_XSAVE_OR_FSAVE(reg_seq, off_xsave, off_fsave) \ + ".cfi_escape " IDW_STRINGIFY(I_DW_CFA_expression) ", " reg_seq ", " \ + I_DW_BLOCK( \ + I_DW_OP_dup, I_DW_OP_deref_size, 0x02, \ + I_DW_OP_lit_SYSCALL_HAVE_FXSAVE_OR_LATER, I_DW_OP_and, \ + I_DW_then_else((off_xsave), (off_fsave)), \ + I_DW_OP_plus \ + ) + +#define ASM_CFI_SYSCALL_FRAME_FLTSAVE() \ + ASM_CFI_XSAVE_OR_FSAVE("11" /* ST0 */, IDW_const1u(0x60), IDW_const1u(0x5c)) "\n\t" \ + ASM_CFI_XSAVE_OR_FSAVE("12" /* ST1 */, IDW_const1u(0x70), IDW_const1u(0x66)) "\n\t" \ + ASM_CFI_XSAVE_OR_FSAVE("13" /* ST2 */, IDW_const1u(0x80), IDW_const1u(0x70)) "\n\t" \ + ASM_CFI_XSAVE_OR_FSAVE("14" /* ST3 */, IDW_const1u(0x90), IDW_const1u(0x7a)) "\n\t" \ + ASM_CFI_XSAVE_OR_FSAVE("15" /* ST4 */, IDW_const1u(0xa0), IDW_const1u(0x84)) "\n\t" \ + ASM_CFI_XSAVE_OR_FSAVE("16" /* ST5 */, IDW_const1u(0xb0), IDW_const1u(0x8e)) "\n\t" \ + ASM_CFI_XSAVE_OR_FSAVE("17" /* ST6 */, IDW_const1u(0xc0), IDW_const1u(0x98)) "\n\t" \ + ASM_CFI_XSAVE_OR_FSAVE("18" /* ST7 */, IDW_const1u(0xd0), IDW_const1u(0xa2)) "\n\t" \ + ".cfi_offset %xmm0,0xe0\n\t" \ + ".cfi_offset %xmm1,0xf0\n\t" \ + ".cfi_offset %xmm2,0x100\n\t" \ + ".cfi_offset %xmm3,0x110\n\t" \ + ".cfi_offset %xmm4,0x120\n\t" \ + ".cfi_offset %xmm5,0x130\n\t" \ + ".cfi_offset %xmm6,0x140\n\t" \ + ".cfi_offset %xmm7,0x150\n\t" \ + ".cfi_offset 39,0x18" /* MXCSR */ + +#define ASM_CFI_FLT_SAME_VALUE() \ + ".cfi_same_value 11\n\t" /* ST0 */ \ + ".cfi_same_value 12\n\t" /* ST1 */ \ + ".cfi_same_value 13\n\t" /* ST2 */ \ + ".cfi_same_value 14\n\t" /* ST3 */ \ + ".cfi_same_value 15\n\t" /* ST4 */ \ + ".cfi_same_value 16\n\t" /* ST5 */ \ + ".cfi_same_value 17\n\t" /* ST6 */ \ + ".cfi_same_value 18\n\t" /* ST7 */ \ + ".cfi_same_value %xmm0\n\t" \ + ".cfi_same_value %xmm1\n\t" \ + ".cfi_same_value %xmm2\n\t" \ + ".cfi_same_value %xmm3\n\t" \ + ".cfi_same_value %xmm4\n\t" \ + ".cfi_same_value %xmm5\n\t" \ + ".cfi_same_value %xmm6\n\t" \ + ".cfi_same_value %xmm7\n\t" \ + ".cfi_same_value 39" /* MXCSR */ + +#define SYSCALL_DISPATCHER_POSTPROLOG_CFI(basereg) \ + ".cfi_signal_frame\n\t" \ + ".cfi_def_cfa " basereg ",0\n\t" \ + ".cfi_offset %eip,0x08\n\t" \ + ".cfi_offset %eflags,0x04\n\t" + +#define SYSCALL_DISPATCHER_FOREACH_SAVE_REG(func, basereg) \ + func(l, "%esp", 4, 0x0c, basereg) /* frame->esp */ \ + func(w, "%cs" , 41, 0x10, basereg) \ + func(w, "%ss" , 42, 0x12, basereg) \ + func(w, "%ds" , 43, 0x14, basereg) \ + func(w, "%es" , 40, 0x16, basereg) \ + func(w, "%fs" , 44, 0x18, basereg) \ + func(w, "%gs" , 45, 0x1a, basereg) \ + func(l, "%eax", 0, 0x1c, basereg) \ + func(l, "%ebx", 3, 0x20, basereg) \ + func(l, "%edi", 7, 0x2c, basereg) \ + func(l, "%esi", 6, 0x30, basereg) \ + func(l, "%ebp", 5, 0x34, basereg) \ + +#define SAVE_REG_CFI_l(reg, regno, off, basereg) \ + __ASM_CFI(".cfi_offset " #regno "," #off "\n\t") + +#define SAVE_REG_CFI_w(reg, regno, off, basereg) \ + __ASM_CFI(".cfi_escape " IDW_STRINGIFY(I_DW_CFA_val_expression) ", " #regno ", " \ + I_DW_BLOCK(I_DW_OP_plus_uconst, off, I_DW_OP_deref_size, 0x02) "\n\t") \ + +#define SAVE_REG_AND_CFI(prefix, reg, regno, off, basereg) \ + "mov" #prefix " " reg "," #off "(" basereg ")\n\t" \ + SAVE_REG_CFI_##prefix(reg, regno, off, basereg) + +#define SAVE_REG_CFI_ONLY(prefix, reg, regno, off, basereg) \ + SAVE_REG_CFI_##prefix(reg, regno, off, basereg) + +#define SYSCALL_DISPATCHER_OTHER_START(basereg) \ + __ASM_CFI(SYSCALL_DISPATCHER_POSTPROLOG_CFI(basereg)) \ + SYSCALL_DISPATCHER_FOREACH_SAVE_REG(SAVE_REG_CFI_ONLY, basereg) \ + __ASM_CFI(ASM_CFI_SYSCALL_FRAME_FLTSAVE())
/*********************************************************************** * __wine_syscall_dispatcher @@ -2474,22 +2601,20 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "movl %fs:0x1f8,%ecx\n\t" /* x86_thread_data()->syscall_frame */ "movw $0,0x02(%ecx)\n\t" /* frame->restore_flags */ "popl 0x08(%ecx)\n\t" /* frame->eip */ + __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t") + __ASM_CFI(".cfi_escape 0x10,0x08,0x02,0x71,0x08\n\t") /* eip, DW_op_breg1 + 0x08 */ "pushfl\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + __ASM_CFI(".cfi_rel_offset %eflags,0\n\t") "popl 0x04(%ecx)\n" /* frame->eflags */ + __ASM_CFI("\t.cfi_endproc\n") __ASM_NAME("__wine_syscall_dispatcher_prolog_end") ":\n\t" - "movl %esp,0x0c(%ecx)\n\t" /* frame->esp */ - "movw %cs,0x10(%ecx)\n\t" - "movw %ss,0x12(%ecx)\n\t" - "movw %ds,0x14(%ecx)\n\t" - "movw %es,0x16(%ecx)\n\t" - "movw %fs,0x18(%ecx)\n\t" - "movw %gs,0x1a(%ecx)\n\t" - "movl %eax,0x1c(%ecx)\n\t" - "movl %ebx,0x20(%ecx)\n\t" - "movl %edi,0x2c(%ecx)\n\t" - "movl %esi,0x30(%ecx)\n\t" - "movl %ebp,0x34(%ecx)\n\t" + __ASM_CFI(".cfi_startproc simple\n\t") /* don't emit default frame instructions */ + __ASM_CFI(SYSCALL_DISPATCHER_POSTPROLOG_CFI("%ecx")) + __ASM_CFI(".cfi_same_value %esp\n\t") + SYSCALL_DISPATCHER_FOREACH_SAVE_REG(SAVE_REG_AND_CFI, "%ecx") "leal 0x34(%ecx),%ebp\n\t" + __ASM_CFI(".cfi_def_cfa %ebp, -0x34\n\t") "leal 4(%esp),%esi\n\t" /* first argument */ "movl %eax,%ebx\n\t" "shrl $8,%ebx\n\t" @@ -2518,14 +2643,24 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "movl %edx,0x278(%ecx)\n\t" "movl %edx,0x27c(%ecx)\n\t" "xsavec 0x40(%ecx)\n\t" + __ASM_CFI(ASM_CFI_SYSCALL_FRAME_FLTSAVE() "\n\t") "jmp 4f\n" + __ASM_CFI("\t.cfi_remember_state\n") + __ASM_CFI("\t" ASM_CFI_FLT_SAME_VALUE() "\n") "1:\txsave 0x40(%ecx)\n\t" + __ASM_CFI(".cfi_restore_state\n\t") "jmp 4f\n" + __ASM_CFI("\t.cfi_remember_state\n") + __ASM_CFI("\t" ASM_CFI_FLT_SAME_VALUE() "\n") "2:\ttestl $4,(%ecx)\n\t" /* frame->syscall_flags & SYSCALL_HAVE_FXSAVE */ "jz 3f\n\t" "fxsave 0x40(%ecx)\n\t" + __ASM_CFI(".cfi_restore_state\n\t") "jmp 4f\n" + __ASM_CFI("\t.cfi_remember_state\n") + __ASM_CFI("\t" ASM_CFI_FLT_SAME_VALUE() "\n") "3:\tfnsave 0x40(%ecx)\n\t" + __ASM_CFI(".cfi_restore_state\n\t") "fwait\n" "4:\tmovl %ecx,%esp\n\t" "movl 0x1c(%esp),%edx\n\t" /* frame->eax */ @@ -2543,6 +2678,7 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "rep; movsl\n\t" "call *(%eax,%edx,4)\n\t" "leal -0x34(%ebp),%esp\n" + __ASM_CFI("\t.cfi_def_cfa %esp, 0\n") "5:\tmovl 0(%esp),%ecx\n\t" /* frame->syscall_flags + (frame->restore_flags << 16) */ "testl $0x68 << 16,%ecx\n\t" /* CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS | CONTEXT_XSAVE */ "jz 3f\n\t" @@ -2552,46 +2688,99 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "movl $7,%eax\n\t" "xorl %edx,%edx\n\t" "xrstor 0x40(%esp)\n\t" + __ASM_CFI(".cfi_remember_state\n\t") + __ASM_CFI(ASM_CFI_FLT_SAME_VALUE() "\n\t") "movl %esi,%eax\n\t" "jmp 3f\n" + __ASM_CFI("\t.cfi_restore_state\n") "1:\ttestl $4,%ecx\n\t" /* SYSCALL_HAVE_FXSAVE */ "jz 2f\n\t" "fxrstor 0x40(%esp)\n\t" + __ASM_CFI(".cfi_remember_state\n\t") + __ASM_CFI(ASM_CFI_FLT_SAME_VALUE() "\n\t") "jmp 3f\n" + __ASM_CFI("\t.cfi_restore_state\n") "2:\tfrstor 0x40(%esp)\n\t" + __ASM_CFI(ASM_CFI_FLT_SAME_VALUE() "\n\t") "fwait\n" "3:\tmovl 0x2c(%esp),%edi\n\t" + __ASM_CFI(".cfi_same_value %edi\n\t") "movl 0x30(%esp),%esi\n\t" + __ASM_CFI(".cfi_same_value %esi\n\t") "movl 0x34(%esp),%ebp\n\t" + __ASM_CFI(".cfi_same_value %ebp\n\t") "testl $0x7 << 16,%ecx\n\t" /* CONTEXT_CONTROL | CONTEXT_SEGMENTS | CONTEXT_INTEGER */ "jnz 1f\n\t" "movl 0x20(%esp),%ebx\n\t" + __ASM_CFI(".cfi_remember_state\n\t") + __ASM_CFI(".cfi_same_value %ebx\n\t") "movl 0x08(%esp),%ecx\n\t" /* frame->eip */ + __ASM_CFI(".cfi_register %eip, %ecx\n\t") "movl 0x0c(%esp),%esp\n\t" /* frame->esp */ + __ASM_CFI(".cfi_same_value %esp\n\t") "jmpl *%ecx\n" + __ASM_CFI("\t.cfi_restore_state\n") "1:\ttestl $0x2 << 16,%ecx\n\t" /* CONTEXT_INTEGER */ "jz 1f\n\t" "movl 0x1c(%esp),%eax\n\t" + __ASM_CFI(".cfi_same_value %eax\n\t") "movl 0x24(%esp),%ecx\n\t" + __ASM_CFI(".cfi_same_value %ecx\n\t") "movl 0x28(%esp),%edx\n" + __ASM_CFI("\t.cfi_same_value %edx\n") "1:\tmovl 0x0c(%esp),%ebx\n\t" /* frame->esp */ + __ASM_CFI(".cfi_register %esp, %ebx\n\t") "movw 0x12(%esp),%ss\n\t" + __ASM_CFI(".cfi_same_value %ss\n\t") "xchgl %ebx,%esp\n\t" + __ASM_CFI(".cfi_endproc\n\t") + __ASM_CFI(".cfi_startproc simple\n\t") + __ASM_CFI(".cfi_def_cfa %esp,0\n\t") + __ASM_CFI(".cfi_escape 0x10,0x09,0x02,0x71,0x04\n\t") /* eflags, DW_op_breg1 + 0x04 */ + __ASM_CFI(".cfi_escape 0x10,0x29,0x02,0x71,0x10\n\t") /* cs, DW_op_breg1 + 0x10 */ + __ASM_CFI(".cfi_escape 0x10,0x08,0x02,0x71,0x08\n\t") /* eip, DW_op_breg1 + 0x08 */ + __ASM_CFI(".cfi_escape 0x10,0x2b,0x02,0x71,0x14\n\t") /* ds, DW_op_breg1 + 0x14 */ + __ASM_CFI(".cfi_escape 0x10,0x28,0x02,0x71,0x16\n\t") /* es, DW_op_breg1 + 0x16 */ + __ASM_CFI(".cfi_escape 0x10,0x2c,0x02,0x71,0x18\n\t") /* fs, DW_op_breg1 + 0x18 */ + __ASM_CFI(".cfi_escape 0x10,0x2d,0x02,0x71,0x1a\n\t") /* gs, DW_op_breg1 + 0x1a */ + __ASM_CFI(".cfi_escape 0x10,0x03,0x02,0x71,0x20\n\t") /* ebx, DW_op_breg1 + 0x20 */ "pushl 0x04(%ebx)\n\t" /* frame->eflags */ + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + __ASM_CFI(".cfi_rel_offset %eflags, 0\n\t") "pushl 0x10(%ebx)\n\t" /* frame->cs */ + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + __ASM_CFI(".cfi_rel_offset %cs, 0\n\t") "pushl 0x08(%ebx)\n\t" /* frame->eip */ + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + __ASM_CFI(".cfi_rel_offset %eip, 0\n\t") "pushl 0x14(%ebx)\n\t" /* frame->ds */ + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + __ASM_CFI(".cfi_rel_offset %ds, 0\n\t") "movw 0x16(%ebx),%es\n\t" + __ASM_CFI(".cfi_same_value %es\n\t") "movw 0x18(%ebx),%fs\n\t" + __ASM_CFI(".cfi_same_value %fs\n\t") "movw 0x1a(%ebx),%gs\n\t" + __ASM_CFI(".cfi_same_value %gs\n\t") "movl 0x20(%ebx),%ebx\n\t" + __ASM_CFI(".cfi_same_value %ebx\n\t") "popl %ds\n\t" + __ASM_CFI(".cfi_same_value %ds\n\t") + __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t") "iret\n" + __ASM_CFI("\t.cfi_endproc\n") + __ASM_CFI("\t.cfi_startproc simple\n") + "\t" SYSCALL_DISPATCHER_OTHER_START("%esp") "\n" "6:\tmovl $0xc000000d,%eax\n\t" /* STATUS_INVALID_PARAMETER */ "jmp 5b\n" + __ASM_CFI("\t.cfi_endproc\n") __ASM_NAME("__wine_syscall_dispatcher_return") ":\n\t" + __ASM_CFI(".cfi_startproc\n\t") "movl 8(%esp),%eax\n\t" "movl 4(%esp),%esp\n\t" + __ASM_CFI(".cfi_endproc\n\t") + __ASM_CFI(".cfi_startproc simple\n\t") + SYSCALL_DISPATCHER_OTHER_START("%esp") "\n\t" "jmp 5b" )