From: Martin Storsjö <martin(a)martin.st> Instead of trying to handcraft all the cases for dealing with partial prologues, just synthesize the full unwind info for the function and execute that instead. The packed unwind info format always maps to a combination of regular unwind opcodes. This is how the arm packed unwind info implementation works already; this makes it easier to reason about compared with the handwritten code for dealing with partial prologues. The previous implementation is certainly faster and more efficient, but much harder to reason about, and to modify. --- dlls/ntdll/unwind.c | 204 +++++++++++++++++++++----------------------- 1 file changed, 96 insertions(+), 108 deletions(-) diff --git a/dlls/ntdll/unwind.c b/dlls/ntdll/unwind.c index c897a9a97e1..0f8942c20b0 100644 --- a/dlls/ntdll/unwind.c +++ b/dlls/ntdll/unwind.c @@ -523,9 +523,12 @@ static void *unwind_packed_data( ULONG_PTR base, ULONG_PTR pc, ARM64_RUNTIME_FUN ARM64_NT_CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS_ARM64 *ptrs ) { int i; - unsigned int len, offset, skip = 0; - unsigned int int_size = func->RegI * 8, fp_size = func->RegF * 8, h_size = func->H * 4, regsave, local_size; - unsigned int int_regs, fp_regs, saved_regs, local_size_regs; + unsigned int len, offset; + unsigned int int_size = func->RegI * 8, fp_size = func->RegF * 8, regsave, local_size; + unsigned int int_regs, fp_regs, saved_regs; + BYTE prologue[40], *prologue_end, epilogue[40], *epilogue_end; + unsigned int ppos = 0, epos = 0; + BOOLEAN final_pc_from_lr; TRACE( "function %I64x-%I64x: len=%#x flag=%x regF=%u regI=%u H=%u CR=%u frame=%x\n", base + func->BeginAddress, base + func->BeginAddress + func->FunctionLength * 4, @@ -540,138 +543,123 @@ static void *unwind_packed_data( ULONG_PTR base, ULONG_PTR pc, ARM64_RUNTIME_FUN 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->Flag == 1) + offset = ((pc - base) - func->BeginAddress) / 4; + + /* Synthesize prologue opcodes */ +#define WRITE_ONE(x) do { prologue[ppos++] = epilogue[epos++] = (x); } while (0) +#define WRITE_TWO(x) do { WRITE_ONE((x) >> 8); WRITE_ONE((x) & 0xff); } while (0) + if (func->CR == 2 || func->CR == 3) { - offset = ((pc - base) - func->BeginAddress) / 4; - if (offset < 17 || offset >= func->FunctionLength - 15) + WRITE_ONE(0xe1); /* set_fp */ + if (local_size <= 512) { - len = (int_size + 8) / 16 + (fp_size + 8) / 16; - switch (func->CR) - { - case 2: - len++; /* pacibsp */ - /* fall through */ - case 3: - len++; /* mov x29,sp */ - len++; /* stp x29,lr,[sp,0] */ - if (local_size <= 512) break; - /* fall through */ - case 0: - case 1: - if (local_size) len++; /* sub sp,sp,#local_size */ - if (local_size > 4088) len++; /* sub sp,sp,#4088 */ - if (func->RegI == 1 && func->CR == 1) len++; /* sub sp,sp,#regsave */ - break; - } - if (offset < len + h_size) /* prolog */ - { - skip = len + h_size - offset; - } - else if (offset >= func->FunctionLength - (len + 1)) /* epilog */ - { - skip = offset - (func->FunctionLength - (len + 1)); - h_size = 0; - } + WRITE_ONE(0x80 | (local_size/8 - 1)); /* save_fplr_x */ + } + else + { + WRITE_ONE(0x40); /* save_fplr */ } } - - if (!skip) + if ((func->CR <= 1 && local_size > 0) || local_size > 512) { - if (func->CR == 3 || func->CR == 2) + if (local_size <= 512) { - /* mov x29,sp */ - context->Sp = context->Fp; - restore_regs( 29, 2, 0, context, ptrs ); + WRITE_ONE(local_size/16); /* alloc_s */ + } + else if (local_size <= 4080) + { + WRITE_TWO(0xc000 | (local_size/16)); /* alloc_m */ + } + else + { + WRITE_ONE((local_size - 4080)/16); /* alloc_s */ + WRITE_TWO(0xc000 | (4080/16)); /* alloc_m */ } - context->Sp += local_size; - if (fp_size) restore_fpregs( 8, fp_regs, int_regs, context, ptrs ); - if (func->CR == 1) restore_regs( 30, 1, int_regs - 1, context, ptrs ); - restore_regs( 19, func->RegI, -saved_regs, context, ptrs ); } - else + if (func->H) { - unsigned int pos = 0; - - switch (func->CR) + prologue[ppos++] = 0xe3; /* nop */ + prologue[ppos++] = 0xe3; /* nop */ + prologue[ppos++] = 0xe3; /* nop */ + prologue[ppos++] = 0xe3; /* nop */ + } + if (func->RegF > 0) + { + if (func->RegF % 2 == 0) { - case 3: - case 2: - /* mov x29,sp */ - if (pos++ >= skip) context->Sp = context->Fp; - if (local_size <= 512) - { - /* stp x29,lr,[sp,-#local_size]! */ - if (pos++ >= skip) restore_regs( 29, 2, -local_size_regs, context, ptrs ); - break; - } - /* stp x29,lr,[sp,0] */ - if (pos++ >= skip) restore_regs( 29, 2, 0, context, ptrs ); - /* fall through */ - case 0: - case 1: - if (!local_size) break; - /* sub sp,sp,#local_size */ - if (pos++ >= skip) context->Sp += (local_size - 1) % 4088 + 1; - if (local_size > 4088 && pos++ >= skip) context->Sp += 4088; - break; + WRITE_TWO(0xdc00 | ((func->RegF) << 6) | (int_regs + fp_regs - 1)); /* save_freg */ } - - pos += h_size; - - if (fp_size) + for (i = (func->RegF + 1) / 2 - 1; i >= 0; i--) { - if (func->RegF % 2 == 0 && pos++ >= skip) - /* str d%u,[sp,#fp_size] */ - restore_fpregs( 8 + func->RegF, 1, int_regs + fp_regs - 1, context, ptrs ); - for (i = (func->RegF + 1) / 2 - 1; i >= 0; i--) - { - if (pos++ < skip) continue; - if (!i && !int_size) - /* stp d8,d9,[sp,-#regsave]! */ - restore_fpregs( 8, 2, -saved_regs, context, ptrs ); - else - /* stp dn,dn+1,[sp,#offset] */ - restore_fpregs( 8 + 2 * i, 2, int_regs + 2 * i, context, ptrs ); - } + if (!i && !int_size) + WRITE_TWO(0xda00 | ((0) << 6) | (saved_regs - 1)); /* save_fregp_x */ + else + WRITE_TWO(0xd800 | ((2*i) << 6) | (int_regs + 2*i)); /* save_fregp d(8+2*i), int_regs + 2*i */ } - + } + if (func->CR == 1 && func->RegI % 2 == 0) + { + if (func->RegI == 0) + WRITE_TWO(0xd400 | ((30 - 19) << 5) | (saved_regs - 1)); /* save_reg_x x30 */ + else + WRITE_TWO(0xd000 | ((30 - 19) << 6) | (int_regs - 1)); /* save_reg x30 */ + } + if (func->RegI > 0) + { if (func->RegI % 2) { - if (pos++ >= skip) + if (func->CR == 1) { - /* stp xn,lr,[sp,#offset] */ - if (func->CR == 1) restore_regs( 30, 1, int_regs - 1, context, ptrs ); - /* str xn,[sp,#offset] */ - restore_regs( 18 + func->RegI, 1, - (func->RegI > 1 || func->CR == 1) ? func->RegI - 1 : -saved_regs, - context, ptrs ); + WRITE_TWO(0xd600 | ((func->RegI - 1)/2) << 6 | (int_regs - 2)); /* save_lrpair x(19+RegI-1), (int_regs-2) */ + if (func->RegI == 1) + WRITE_ONE(saved_regs/2); /* alloc_s */ } - if (func->CR == 1 && func->RegI == 1) + else { - if (pos++ >= skip) context->Sp += regsave; + if (func->RegI == 1) + WRITE_TWO(0xd400 | ((0) << 5) | (saved_regs - 1)); /* save_reg_x x19 */ + else + WRITE_TWO(0xd000 | ((int_regs - 1) << 6) | (int_regs - 1)); /* save_reg x(19+int_regs-1) */ } } - else if (func->CR == 1) - { - /* str lr,[sp,#offset] */ - if (pos++ >= skip) restore_regs( 30, 1, func->RegI ? int_regs - 1 : -saved_regs, context, ptrs ); - } - for (i = func->RegI / 2 - 1; i >= 0; i--) { - if (pos++ < skip) continue; if (i) - /* stp xn,xn+1,[sp,#offset] */ - restore_regs( 19 + 2 * i, 2, 2 * i, context, ptrs ); + WRITE_TWO(0xc800 | ((2*i) << 6) | (2*i)); /* save_regp x(19+2*i), 2*i */ else - /* stp x19,x20,[sp,-#regsave]! */ - restore_regs( 19, 2, -saved_regs, context, ptrs ); + WRITE_TWO(0xcc00 | ((0) << 6) | (saved_regs - 1)); /* save_regp_x x19, saved_regs */ + } + } + if (func->CR == 2) + WRITE_ONE(0xfc); /* pac_sign_lr */ + WRITE_ONE(0xe4); /* end */ + prologue_end = &prologue[ppos]; + epilogue_end = &epilogue[epos]; + + if (func->Flag == 1) + { + if (offset < (prologue_end - prologue) || offset >= func->FunctionLength - (epilogue_end - epilogue)) + { + /* Check prologue */ + len = get_sequence_len( prologue, prologue_end ); + if (offset < len) + { + process_unwind_codes( prologue, prologue_end, context, ptrs, len - offset, &final_pc_from_lr ); + return NULL; + } + /* Check epilogue */ + len = get_sequence_len( epilogue, epilogue_end ); + if (offset >= func->FunctionLength - (len + 1)) /* epilog */ + { + process_unwind_codes( epilogue, epilogue_end, context, ptrs, offset - (func->FunctionLength - (len + 1)), &final_pc_from_lr ); + return NULL; + } } } - if (func->CR == 2) do_pac_auth( context ); + + /* Execute full prologue */ + process_unwind_codes( prologue, prologue_end, context, ptrs, 0, &final_pc_from_lr ); return NULL; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9666