[PATCH 0/4] MR9638: ntdll: Fix handling of an arm64 unwind info corner case
Also update existing tests for consistency, after previous changes, and take more tests into use. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9638
From: Martin Storsjö <martin(a)martin.st> This test case was updated in 41d7baa0139252f66042313f366b199e710f7b7b. The behaviour of this testcase has changed; the older behaviour could still be reproduced on Windows 11 version 10.0.22000.2176, but on 10.0.26100.6899 we see the newer behaviour. (As the test was updated in Feb 2024, it probably changed in some earlier build than that.) Update the function definition to match the actual observed behaviour. --- dlls/ntdll/tests/unwind.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/tests/unwind.c b/dlls/ntdll/tests/unwind.c index 558ae07ba20..1ce98bc0864 100644 --- a/dlls/ntdll/tests/unwind.c +++ b/dlls/ntdll/tests/unwind.c @@ -2237,11 +2237,11 @@ static void test_virtual_unwind_arm64(void) 0xe6, 0x9f, 0x03, 0xa9, /* 10: stp x6, x7, [sp, #56] */ 0xff, 0x83, 0x00, 0xd1, /* 14: sub sp, sp, #32 */ 0x1f, 0x20, 0x03, 0xd5, /* 18: nop */ - 0xff, 0x83, 0x00, 0x91, /* 1c: add sp, sp, #32 */ + 0x1f, 0x20, 0x03, 0xd5, /* 1c: nop */ 0x1f, 0x20, 0x03, 0xd5, /* 20: nop */ 0x1f, 0x20, 0x03, 0xd5, /* 24: nop */ 0x1f, 0x20, 0x03, 0xd5, /* 28: nop */ - 0x1f, 0x20, 0x03, 0xd5, /* 2c: nop */ + 0xff, 0x83, 0x00, 0x91, /* 2c: add sp, sp, #32 */ 0xf3, 0x0f, 0x1b, 0xf8, /* 30: ldr x19, [sp], #80 */ 0xc0, 0x03, 0x5f, 0xd6, /* 34: ret */ }; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9638
From: Martin Storsjö <martin(a)martin.st> This testcase was broken at current versions of Windows at the end of 2020, but was fixed sometime after that; on 10.0.22000.2176 it is working correctly. --- dlls/ntdll/tests/unwind.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/dlls/ntdll/tests/unwind.c b/dlls/ntdll/tests/unwind.c index 1ce98bc0864..bdb51a16e09 100644 --- a/dlls/ntdll/tests/unwind.c +++ b/dlls/ntdll/tests/unwind.c @@ -2090,10 +2090,6 @@ static void test_virtual_unwind_arm64(void) UWOP_NOP /* padding */ }; - /* Windows seems to only save one register for UWOP_SAVE_NEXT for - * float registers, contrary to what the documentation says. The tests - * for those cases are commented out; they succeed in wine but fail - * on native windows. */ static const struct results_arm64 results_5[] = { /* offset fp handler pc frame offset registers */ @@ -2105,11 +2101,9 @@ static void test_virtual_unwind_arm64(void) { 0x14, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {x26, 0x18}, {x27, 0x20}, {x28, 0x28}, {-1,-1} }}, { 0x18, 0x00, 0, 0x38, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {x26, 0x18}, {x27, 0x20}, {x28, 0x28}, {x29, 0x30}, {lr, 0x38}, {-1,-1} }}, { 0x1c, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {-1,-1} }}, -#if 0 { 0x20, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {-1,-1} }}, { 0x24, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {d12, 0x20}, {d13, 0x28}, {-1,-1} }}, { 0x28, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {d12, 0x20}, {d13, 0x28}, {d14, 0x30}, {d15, 0x38}, {-1,-1} }}, -#endif }; static const BYTE function_6[] = -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9638
From: Martin Storsjö <martin(a)martin.st> See https://github.com/llvm/llvm-project/issues/169588 for discussion around this case. As there is no unwind opcode that can describe "stp x19,lr,[sp,-#N]!", this case is handled with two separate instructions, "sub sp, sp, #N" and "stp x19,lr,[sp]". --- tools/winedump/pe.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/winedump/pe.c b/tools/winedump/pe.c index 745982390fd..2b738a2f7c3 100644 --- a/tools/winedump/pe.c +++ b/tools/winedump/pe.c @@ -2176,9 +2176,10 @@ static void dump_arm64_packed_info( const struct runtime_function_arm64 *func ) printf( " %04x: str lr,[sp,-#%#x]!\n", pos++, savesz ); break; case 1: - if (func->CR == 1) - printf( " %04x: stp x19,lr,[sp,-#%#x]!\n", pos++, savesz ); - else + if (func->CR == 1) { + printf( " %04x: sub sp, sp, %#x\n", pos++, savesz ); + printf( " %04x: stp x19,lr,[sp]\n", pos++ ); + } else printf( " %04x: str x19,[sp,-#%#x]!\n", pos++, savesz ); break; default: -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9638
From: Martin Storsjö <martin(a)martin.st> See https://github.com/llvm/llvm-project/issues/169588 for explanation of how this case is supposed to work. The former testcase works on older versions of Windows as well; the latter gives the wrong frame adjustment on 10.0.22000.2176 but works correctly on 10.0.26100.6899. --- dlls/ntdll/tests/unwind.c | 82 +++++++++++++++++++++++++++++++++++++++ dlls/ntdll/unwind.c | 7 +++- 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/tests/unwind.c b/dlls/ntdll/tests/unwind.c index bdb51a16e09..727cbb08b12 100644 --- a/dlls/ntdll/tests/unwind.c +++ b/dlls/ntdll/tests/unwind.c @@ -2593,6 +2593,86 @@ static void test_virtual_unwind_arm64(void) { 0x0c, 0x00, -2, 0, 0xdeadbeef, FALSE, { {-1,-1} }}, }; + static const BYTE function_19[] = + { + 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */ + 0xf3, 0x7b, 0x00, 0xa9, /* 04: stp x19, x30, [sp] */ + 0xff, 0x03, 0x01, 0xd1, /* 08: sub sp, sp, #64 */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0xff, 0x03, 0x01, 0x91, /* 10: add sp, sp, #64 */ + 0xf3, 0x7b, 0x40, 0xa9, /* 14: ldp x19, x30, [sp] */ + 0xff, 0x43, 0x00, 0x91, /* 18: add sp, sp, #16 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */ + }; + + static const DWORD unwind_info_19_packed = + (1 << 0) | /* Flag */ + (sizeof(function_19)/4 << 2) | /* FunctionLength */ + (0 << 13) | /* RegF */ + (1 << 16) | /* RegI */ + (0 << 20) | /* H */ + (1 << 21) | /* CR */ + (5 << 23); /* FrameSize */ + + static const BYTE unwind_info_19[] = { DW(unwind_info_19_packed) }; + + static const struct results_arm64 results_19[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x08, 0x00, 0, 0x08, 0x010, TRUE, { {x19,0x00}, {lr,0x08}, {-1,-1} }}, + { 0x0c, 0x00, 0, 0x48, 0x050, TRUE, { {x19,0x40}, {lr,0x48}, {-1,-1} }}, + { 0x10, 0x00, 0, 0x48, 0x050, TRUE, { {x19,0x40}, {lr,0x48}, {-1,-1} }}, + { 0x14, 0x00, 0, 0x08, 0x010, TRUE, { {x19,0x00}, {lr,0x08}, {-1,-1} }}, + { 0x18, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x1c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_20[] = + { + 0xff, 0xc3, 0x00, 0xd1, /* 00: sub sp, sp, #48 */ + 0xf3, 0x7b, 0x00, 0xa9, /* 04: stp x19, x30, [sp] */ + 0xe8, 0x27, 0x01, 0x6d, /* 08: stp d8, d9, [sp, #16] */ + 0xea, 0x13, 0x00, 0xfd, /* 0c: str d10, [sp, #32] */ + 0xff, 0x03, 0x01, 0xd1, /* 10: sub sp, sp, #64 */ + 0x1f, 0x20, 0x03, 0xd5, /* 14: nop */ + 0xff, 0x03, 0x01, 0x91, /* 18: add sp, sp, #64 */ + 0xea, 0x13, 0x40, 0xfd, /* 1c: ldr d10, [sp, #32] */ + 0xe8, 0x27, 0x41, 0x6d, /* 20: ldp d8, d9, [sp, #16] */ + 0xf3, 0x7b, 0x40, 0xa9, /* 24: ldp x19, x30, [sp] */ + 0xff, 0xc3, 0x00, 0x91, /* 28: add sp, sp, #48 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 2c: ret */ + }; + + static const DWORD unwind_info_20_packed = + (1 << 0) | /* Flag */ + (sizeof(function_20)/4 << 2) | /* FunctionLength */ + (2 << 13) | /* RegF */ + (1 << 16) | /* RegI */ + (0 << 20) | /* H */ + (1 << 21) | /* CR */ + (7 << 23); /* FrameSize */ + + static const BYTE unwind_info_20[] = { DW(unwind_info_20_packed) }; + + static const struct results_arm64 results_20[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x030, TRUE, { {-1,-1} }}, + { 0x08, 0x00, 0, 0x08, 0x030, TRUE, { {x19,0x00}, {lr,0x08}, {-1,-1} }}, + { 0x0c, 0x00, 0, 0x08, 0x030, TRUE, { {x19,0x00}, {lr,0x08}, {d8,0x10}, {d9,0x18}, {-1,-1} }}, + { 0x10, 0x00, 0, 0x08, 0x030, TRUE, { {x19,0x00}, {lr,0x08}, {d8,0x10}, {d9,0x18}, {d10,0x20}, {-1,-1} }}, + { 0x14, 0x00, 0, 0x48, 0x070, TRUE, { {x19,0x40}, {lr,0x48}, {d8,0x50}, {d9,0x58}, {d10,0x60}, {-1,-1} }}, + { 0x18, 0x00, 0, 0x48, 0x070, TRUE, { {x19,0x40}, {lr,0x48}, {d8,0x50}, {d9,0x58}, {d10,0x60}, {-1,-1} }}, + { 0x1c, 0x00, 0, 0x08, 0x030, TRUE, { {x19,0x00}, {lr,0x08}, {d8,0x10}, {d9,0x18}, {d10,0x20}, {-1,-1} }}, + { 0x20, 0x00, 0, 0x08, 0x030, TRUE, { {x19,0x00}, {lr,0x08}, {d8,0x10}, {d9,0x18}, {-1,-1} }}, + { 0x24, 0x00, 0, 0x08, 0x030, TRUE, { {x19,0x00}, {lr,0x08}, {-1,-1} }}, + { 0x28, 0x00, 0, ORIG_LR, 0x030, TRUE, { {-1,-1} }}, + { 0x2c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + static const struct unwind_test_arm64 tests[] = { #define TEST(func, unwind, size, results, unwound_clear, last_ptr, stack_value_index, stack_value) \ @@ -2616,6 +2696,8 @@ static void test_virtual_unwind_arm64(void) TEST(function_16, unwind_info_16, sizeof(unwind_info_16), results_16, 2, x18, 6, CONTEXT_ARM64_UNWOUND_TO_CALL), TEST(function_17, unwind_info_17, sizeof(unwind_info_17), results_17, 2, 0, -1, 0), TEST(function_18, NULL, 0, results_18, 0, 0, -1, 0), + TEST(function_19, unwind_info_19, 0, results_19, 0, 0, -1, 0), + TEST(function_20, unwind_info_20, 0, results_20, 0, 0, -1, 0), #undef TEST }; unsigned int i; diff --git a/dlls/ntdll/unwind.c b/dlls/ntdll/unwind.c index 6c18b58b7a8..c897a9a97e1 100644 --- a/dlls/ntdll/unwind.c +++ b/dlls/ntdll/unwind.c @@ -563,6 +563,7 @@ static void *unwind_packed_data( ULONG_PTR base, ULONG_PTR pc, ARM64_RUNTIME_FUN 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 */ @@ -645,9 +646,13 @@ static void *unwind_packed_data( ULONG_PTR base, ULONG_PTR pc, ARM64_RUNTIME_FUN 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->RegI - 1 : -saved_regs, + (func->RegI > 1 || func->CR == 1) ? func->RegI - 1 : -saved_regs, context, ptrs ); } + if (func->CR == 1 && func->RegI == 1) + { + if (pos++ >= skip) context->Sp += regsave; + } } else if (func->CR == 1) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9638
participants (1)
-
Martin Storsjö