This matches what tests show is done on actual windows, in the current testcases; in all currently tested cases, handler_data is reset to NULL when no handler is returned.
Signed-off-by: Martin Storsjo martin@martin.st --- dlls/ntdll/signal_arm64.c | 2 ++ dlls/ntdll/tests/exception.c | 4 ++++ 2 files changed, 6 insertions(+)
diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index 134e40545f..5521e915a7 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -889,6 +889,8 @@ PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG_PTR base, ULONG_PTR pc,
TRACE( "type %x pc %lx sp %lx func %lx\n", type, pc, context->Sp, base + func->BeginAddress );
+ *handler_data = NULL; + if (func->u.s.Flag) handler = unwind_packed_data( base, pc, func, context, ctx_ptr ); else diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index dd9eb966fc..1e9047383b 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -4331,6 +4331,10 @@ static void call_virtual_unwind( int testnum, const struct unwind_test *test ) else { ok( handler == NULL, "%u/%u: handler %p instead of NULL\n", testnum, i, handler ); + ok( data == (test->results[i].handler < 0 ? + (void *)0xdeadbeef : NULL), + "%u/%u: handler data set to %p/%p\n", testnum, i, data, + (test->results[i].handler < 0 ? (void *)0xdeadbeef : NULL) ); }
ok( context.Pc == test->results[i].pc, "%u/%u: wrong pc %p/%p\n",
Also correct the previous definition of UWOP_NOP which was a typo (but it wasn't used in any testcase yet).
Signed-off-by: Martin Storsjo martin@martin.st --- dlls/ntdll/tests/exception.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 1e9047383b..528323c9bc 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -4231,10 +4231,31 @@ static void test_debug_service(DWORD numexc)
#define UWOP_TWOBYTES(x) (((x) >> 8) & 0xff), ((x) & 0xff)
-#define UWOP_ALLOC_SMALL(size) (0x00 | (size / 16)) -#define UWOP_SAVE_REGP(reg, offset) UWOP_TWOBYTES((0xC8 << 8) | ((reg - 19) << 6) | (offset/8)) -#define UWOP_NOP 0xE5 -#define UWOP_END 0xE4 +#define UWOP_ALLOC_SMALL(size) (0x00 | (size/16)) +#define UWOP_SAVE_R19R20_X(offset) (0x20 | (offset/8)) +#define UWOP_SAVE_FPLR(offset) (0x40 | (offset/8)) +#define UWOP_SAVE_FPLR_X(offset) (0x80 | (offset/8 - 1)) +#define UWOP_ALLOC_MEDIUM(size) UWOP_TWOBYTES((0xC0 << 8) | (size/16)) +#define UWOP_SAVE_REGP(reg, offset) UWOP_TWOBYTES((0xC8 << 8) | ((reg - 19) << 6) | (offset/8)) +#define UWOP_SAVE_REGP_X(reg, offset) UWOP_TWOBYTES((0xCC << 8) | ((reg - 19) << 6) | (offset/8 - 1)) +#define UWOP_SAVE_REG(reg, offset) UWOP_TWOBYTES((0xD0 << 8) | ((reg - 19) << 6) | (offset/8)) +#define UWOP_SAVE_REG_X(reg, offset) UWOP_TWOBYTES((0xD4 << 8) | ((reg - 19) << 5) | (offset/8 - 1)) +#define UWOP_SAVE_LRP(reg, offset) UWOP_TWOBYTES((0xD6 << 8) | ((reg - 19)/2 << 6) | (offset/8)) +#define UWOP_SAVE_FREGP(reg, offset) UWOP_TWOBYTES((0xD8 << 8) | ((reg - 8) << 6) | (offset/8)) +#define UWOP_SAVE_FREGP_X(reg, offset) UWOP_TWOBYTES((0xDA << 8) | ((reg - 8) << 6) | (offset/8 - 1)) +#define UWOP_SAVE_FREG(reg, offset) UWOP_TWOBYTES((0xDC << 8) | ((reg - 8) << 6) | (offset/8)) +#define UWOP_SAVE_FREG_X(reg, offset) UWOP_TWOBYTES((0xDE << 8) | ((reg - 8) << 5) | (offset/8 - 1)) +#define UWOP_ALLOC_LARGE(size) UWOP_TWOBYTES((0xE0 << 8) | ((size/16) >> 16)), UWOP_TWOBYTES(size/16) +#define UWOP_SET_FP 0xE1 +#define UWOP_ADD_FP(offset) UWOP_TWOBYTES((0xE2 << 8) | (offset/8)) +#define UWOP_NOP 0xE3 +#define UWOP_END 0xE4 +#define UWOP_END_C 0xE5 +#define UWOP_SAVE_NEXT 0xE6 +#define UWOP_TRAP_FRAME 0xE8 +#define UWOP_MACHINE_FRAME 0xE9 +#define UWOP_CONTEXT 0xEA +#define UWOP_CLEAR_UNWOUND_TO_CALL 0xEC
struct results {
Only testing d0-d15, not d16-d31, for keeping the test references a bit smaller.
Signed-off-by: Martin Storsjo martin@martin.st --- dlls/ntdll/tests/exception.c | 42 ++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 9 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 528323c9bc..59df6a71d3 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -4265,7 +4265,7 @@ struct results ULONG_PTR pc; /* expected final pc value */ int frame; /* expected frame return value */ int frame_offset; /* whether the frame return value is an offset or an absolute value */ - int regs[32][2]; /* expected values for registers */ + int regs[48][2]; /* expected values for registers */ };
struct unwind_test @@ -4283,15 +4283,19 @@ enum regs x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, - x24, x25, x26, x27, x28, x29, lr, sp + x24, x25, x26, x27, x28, x29, lr, sp, + d0, d1, d2, d3, d4, d5, d6, d7, + d8, d9, d10, d11, d12, d13, d14, d15 };
-static const char * const reg_names[32] = +static const char * const reg_names[48] = { "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", - "x24", "x25", "x26", "x27", "x28", "x29", "lr", "sp" + "x24", "x25", "x26", "x27", "x28", "x29", "lr", "sp", + "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", + "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", };
#define ORIG_LR 0xCCCCCCCC @@ -4378,8 +4382,10 @@ static void call_virtual_unwind( int testnum, const struct unwind_test *test ) ok( frame - sp_offset == context.Sp, "%u/%u: wrong sp %p/%p\n", testnum, i, (void *)frame - sp_offset, (void *)context.Sp);
- for (j = 0; j < 31; j++) /* Not including sp here */ + for (j = 0; j < 48; j++) { + if (j == sp) continue; /* Handling sp separately above */ + for (k = 0; k < nb_regs; k++) { if (test->results[i].regs[k][0] == -1) @@ -4399,11 +4405,25 @@ static void call_virtual_unwind( int testnum, const struct unwind_test *test ) "%u/%u: register %s wrong %p/%x\n", testnum, i, reg_names[j], (void *)context.X[j], test->results[i].regs[k][1] ); } + else if (j >= d8 && j <= d15 && (&ctx_ptr.D8)[j - d8]) + { + ok( k < nb_regs, "%u/%u: register %s should not be set to %llx\n", + testnum, i, reg_names[j], context.V[j - d0].Low ); + if (k < nb_regs) + ok( context.V[j - d0].Low == test->results[i].regs[k][1], + "%u/%u: register %s wrong %p/%x\n", + testnum, i, reg_names[j], (void *)context.V[j - d0].Low, test->results[i].regs[k][1] ); + } else if (k < nb_regs) { - ok( context.X[j] == test->results[i].regs[k][1], - "%u/%u: register %s wrong %p/%x\n", - testnum, i, reg_names[j], (void *)context.X[j], test->results[i].regs[k][1] ); + if (j < d0) + ok( context.X[j] == test->results[i].regs[k][1], + "%u/%u: register %s wrong %p/%x\n", + testnum, i, reg_names[j], (void *)context.X[j], test->results[i].regs[k][1] ); + else + ok( context.V[j - d0].Low == test->results[i].regs[k][1], + "%u/%u: register %s wrong %p/%x\n", + testnum, i, reg_names[j], (void *)context.V[j - d0].Low, test->results[i].regs[k][1] ); } else { @@ -4414,10 +4434,14 @@ static void call_virtual_unwind( int testnum, const struct unwind_test *test ) else if (j == x29) ok( context.Fp == orig_fp, "%u/%u: register fp wrong %p/unset\n", testnum, i, (void *)context.Fp ); - else + else if (j < d0) ok( context.X[j] == unset_reg, "%u/%u: register %s wrong %p/unset\n", testnum, i, reg_names[j], (void *)context.X[j]); + else + ok( context.V[j - d0].Low == unset_reg, + "%u/%u: register %s wrong %p/unset\n", + testnum, i, reg_names[j], (void *)context.V[j - d0].Low); } } }
Signed-off-by: Martin Storsjo martin@martin.st --- dlls/ntdll/signal_arm64.c | 13 +++++- dlls/ntdll/tests/exception.c | 82 ++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index 5521e915a7..719c71f952 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -635,6 +635,15 @@ static void process_unwind_codes( BYTE *ptr, BYTE *end, CONTEXT *context, ptr += len; continue; } + else if (*ptr == 0xe9) /* MSFT_OP_MACHINE_FRAME */ + { + context->Pc = ((DWORD64 *)context->Sp)[1]; + context->Sp = ((DWORD64 *)context->Sp)[0]; + } + else if (*ptr == 0xea) /* MSFT_OP_CONTEXT */ + { + memcpy( context, (DWORD64 *)context->Sp, sizeof(CONTEXT) ); + } else { WARN( "unsupported code %02x\n", *ptr ); @@ -891,13 +900,15 @@ PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG_PTR base, ULONG_PTR pc,
*handler_data = NULL;
+ context->Pc = 0; if (func->u.s.Flag) handler = unwind_packed_data( base, pc, func, context, ctx_ptr ); else handler = unwind_full_data( base, pc, func, context, handler_data, ctx_ptr );
TRACE( "ret: lr=%lx sp=%lx handler=%p\n", context->u.s.Lr, context->Sp, handler ); - context->Pc = context->u.s.Lr; + if (!context->Pc) + context->Pc = context->u.s.Lr; context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; *frame_ret = context->Sp; return handler; diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 59df6a71d3..736a294239 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -4539,12 +4539,94 @@ static void test_virtual_unwind(void) #endif };
+ static const BYTE function_2[] = + { + 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */ + 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */ + }; + + static const DWORD unwind_info_2_header = + (sizeof(function_2)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (1 << 27); /* codes */ + + static const BYTE unwind_info_2[] = + { + DW(unwind_info_2_header), + + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_MACHINE_FRAME, + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_END, + }; + + /* Partial prologues with the custom frame opcodes (machine frame, + * context) behave like there's an off-by-one bug; unwinding from + * offset 0, which normally does nothing, executes one opcode if + * there's a machine frame or context in the prologue, and for other + * offsets, it behaves like unwinding from one instruction further + * ahead. So only test the full prologue case. */ + static const struct results results_2[] = + { + /* offset fp handler pc frame offset registers */ +#if 0 + { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, 0x0008, 0x010, FALSE, { {-1,-1} }}, + { 0x08, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }}, +#endif + { 0x0c, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }}, + }; + + static const BYTE function_3[] = + { + 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */ + 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */ + }; + + static const DWORD unwind_info_3_header = + (sizeof(function_3)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (1 << 27); /* codes */ + + static const BYTE unwind_info_3[] = + { + DW(unwind_info_3_header), + + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_CONTEXT, + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_END, + }; + + static const struct results results_3[] = + { + /* offset fp handler pc frame offset registers */ +#if 0 + { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0 , 0x0108, 0x110, FALSE, { {x0, 0x08}, {x1, 0x10}, {x2, 0x18}, {x3, 0x20}, {x4, 0x28}, {x5, 0x30}, {x6, 0x38}, {x7, 0x40}, {x8, 0x48}, {x9, 0x50}, {x10, 0x58}, {x11, 0x60}, {x12, 0x68}, {x13, 0x70}, {x14, 0x78}, {x15, 0x80}, {x16, 0x88}, {x17, 0x90}, {x18, 0x98}, {x19, 0xA0}, {x20, 0xA8}, {x21, 0xB0}, {x22, 0xB8}, {x23, 0xC0}, {x24, 0xC8}, {x25, 0xD0}, {x26, 0xD8}, {x27, 0xE0}, {x28, 0xE8}, {x29, 0xF0}, {lr, 0xF8}, {d0, 0x110}, {d1, 0x120}, {d2, 0x130}, {d3, 0x140}, {d4, 0x150}, {d5, 0x160}, {d6, 0x170}, {d7, 0x180}, {d8, 0x190}, {d9, 0x1a0}, {d10, 0x1b0}, {d11, 0x1c0}, {d12, 0x1d0}, {d13, 0x1e0}, {d14, 0x1f0}, {d15, 0x200}, {-1,-1} }}, + { 0x08, 0x00, 0 , 0x0118, 0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }}, +#endif + { 0x0c, 0x00, 0 , 0x0118, 0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }}, + }; + static const struct unwind_test tests[] = { #define TEST(func, unwind, unwind_packed, results) \ { func, sizeof(func), unwind, unwind_packed ? 0 : sizeof(unwind), results, ARRAY_SIZE(results) } TEST(function_0, unwind_info_0, 0, results_0), TEST(function_1, unwind_info_1, 1, results_1), + TEST(function_2, unwind_info_2, 0, results_2), + TEST(function_3, unwind_info_3, 0, results_3), #undef TEST }; unsigned int i;
These all seem to be implemented correctly as no changes to the wine implementation is needed.
Signed-off-by: Martin Storsjo martin@martin.st --- dlls/ntdll/tests/exception.c | 139 +++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 736a294239..d2dbbe7b0d 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -4619,6 +4619,143 @@ static void test_virtual_unwind(void) { 0x0c, 0x00, 0 , 0x0118, 0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }}, };
+ static const BYTE function_4[] = + { + 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */ + 0xff, 0x03, 0x08, 0xd1, /* 04: sub sp, sp, #512 */ + 0xff, 0x43, 0x40, 0xd1, /* 08: sub sp, sp, #65536 */ + 0xfd, 0x03, 0x00, 0x91, /* 0c: mov x29, sp */ + 0xf3, 0x53, 0xbe, 0xa9, /* 10: stp x19, x20, [sp, #-32]! */ + 0xf5, 0x5b, 0x01, 0xa9, /* 14: stp x21, x22, [sp, #16] */ + 0xf7, 0x0f, 0x1e, 0xf8, /* 18: str x23, [sp, #-32]! */ + 0xf8, 0x07, 0x00, 0xf9, /* 1c: str x24, [sp, #8] */ + 0xf9, 0x7b, 0x01, 0xa9, /* 20: stp x25, x30, [sp, #16] */ + 0xfd, 0x7b, 0x03, 0xa9, /* 24: stp x29, x30, [sp, #48] */ + 0xfd, 0x7b, 0xbe, 0xa9, /* 28: stp x29, x30, [sp, #-32]! */ + 0xf3, 0x53, 0xbe, 0xa9, /* 2c: stp x19, x20, [sp, #-32]! */ + 0xe8, 0x27, 0xbe, 0x6d, /* 30: stp d8, d9, [sp, #-32]! */ + 0xea, 0x2f, 0x01, 0x6d, /* 34: stp d10, d11, [sp, #16] */ + 0xec, 0x0f, 0x1e, 0xfc, /* 38: str d12, [sp, #-32]! */ + 0xed, 0x07, 0x00, 0xfd, /* 3c: str d13, [sp, #8] */ + 0xfd, 0x43, 0x00, 0x91, /* 40: add x29, sp, #16 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 44: ret */ + }; + + static const DWORD unwind_info_4_header = + (sizeof(function_4)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (8 << 27); /* codes */ + + static const BYTE unwind_info_4[] = + { + DW(unwind_info_4_header), + + UWOP_ADD_FP(16), /* 40: add x29, sp, #16 */ + UWOP_SAVE_FREG(13, 8), /* 3c: str d13, [sp, #8] */ + UWOP_SAVE_FREG_X(12, 32), /* 38: str d12, [sp, #-32]! */ + UWOP_SAVE_FREGP(10, 16), /* 34: stp d10, d11, [sp, #16] */ + UWOP_SAVE_FREGP_X(8, 32), /* 30: stp d8, d9, [sp, #-32]! */ + UWOP_SAVE_R19R20_X(32), /* 2c: stp x19, x20, [sp, #-32]! */ + UWOP_SAVE_FPLR_X(32), /* 28: stp x29, x30, [sp, #-32]! */ + UWOP_SAVE_FPLR(16), /* 24: stp x29, x30, [sp, #16] */ + UWOP_SAVE_LRP(25, 16), /* 20: stp x25, x30, [sp, #16] */ + UWOP_SAVE_REG(24, 8), /* 1c: str x24, [sp, #8] */ + UWOP_SAVE_REG_X(23, 32), /* 18: str x23, [sp, #-32]! */ + UWOP_SAVE_REGP(21, 16), /* 14: stp x21, x22, [sp, #16] */ + UWOP_SAVE_REGP_X(19, 32), /* 10: stp x19, x20, [sp, #-32]! */ + UWOP_SET_FP, /* 0c: mov x29, sp */ + UWOP_ALLOC_LARGE(65536), /* 08: sub sp, sp, #65536 */ + UWOP_ALLOC_MEDIUM(512), /* 04: sub sp, sp, #512 */ + UWOP_ALLOC_SMALL(16), /* 00: sub sp, sp, #16 */ + UWOP_END, + }; + + static const struct results results_4[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x00010, TRUE, { {-1,-1} }}, + { 0x08, 0x10, 0, ORIG_LR, 0x00210, TRUE, { {-1,-1} }}, + { 0x0c, 0x10, 0, ORIG_LR, 0x10210, TRUE, { {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x18, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {x22, 0x18}, {-1,-1} }}, + { 0x1c, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {-1,-1} }}, + { 0x20, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {-1,-1} }}, + { 0x24, 0x00, 0, 0x0018, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {lr, 0x18}, {-1,-1} }}, + { 0x28, 0x00, 0, 0x0018, 0x10220, FALSE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {lr, 0x18}, {x29, 0x10}, {-1,-1} }}, + { 0x2c, 0x00, 0, 0x0038, 0x10240, FALSE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x20}, {x24, 0x28}, {x25, 0x30}, {lr, 0x38}, {x29, 0x30}, {-1,-1} }}, + { 0x30, 0x00, 0, 0x0058, 0x10260, FALSE, { {x19, 0x60}, {x20, 0x68}, {x21, 0x70}, {x22, 0x78}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {lr, 0x58}, {x29, 0x50}, {-1,-1} }}, + { 0x34, 0x00, 0, 0x0078, 0x10280, FALSE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x60}, {x24, 0x68}, {x25, 0x70}, {lr, 0x78}, {x29, 0x70}, {d8, 0x00}, {d9, 0x08}, {-1,-1} }}, + { 0x38, 0x00, 0, 0x0078, 0x10280, FALSE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x60}, {x24, 0x68}, {x25, 0x70}, {lr, 0x78}, {x29, 0x70}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {-1,-1} }}, + { 0x3c, 0x00, 0, 0x0098, 0x102a0, FALSE, { {x19, 0xa0}, {x20, 0xa8}, {x21, 0xb0}, {x22, 0xb8}, {x23, 0x80}, {x24, 0x88}, {x25, 0x90}, {lr, 0x98}, {x29, 0x90}, {d8, 0x20}, {d9, 0x28}, {d10, 0x30}, {d11, 0x38}, {d12, 0x00}, {-1,-1} }}, + { 0x40, 0x00, 0, 0x0098, 0x102a0, FALSE, { {x19, 0xa0}, {x20, 0xa8}, {x21, 0xb0}, {x22, 0xb8}, {x23, 0x80}, {x24, 0x88}, {x25, 0x90}, {lr, 0x98}, {x29, 0x90}, {d8, 0x20}, {d9, 0x28}, {d10, 0x30}, {d11, 0x38}, {d12, 0x00}, {d13, 0x08}, {-1,-1} }}, + { 0x44, 0x20, 0, 0x00a8, 0x102b0, FALSE, { {x19, 0xb0}, {x20, 0xb8}, {x21, 0xc0}, {x22, 0xc8}, {x23, 0x90}, {x24, 0x98}, {x25, 0xa0}, {lr, 0xa8}, {x29, 0xa0}, {d8, 0x30}, {d9, 0x38}, {d10, 0x40}, {d11, 0x48}, {d12, 0x10}, {d13, 0x18}, {-1,-1} }}, + }; + + static const BYTE function_5[] = + { + 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */ + 0xf5, 0x5b, 0x01, 0xa9, /* 04: stp x21, x22, [sp, #16] */ + 0xf7, 0x63, 0xbc, 0xa9, /* 08: stp x23, x24, [sp, #-64]! */ + 0xf9, 0x6b, 0x01, 0xa9, /* 0c: stp x25, x26, [sp, #16] */ + 0xfb, 0x73, 0x02, 0xa9, /* 10: stp x27, x28, [sp, #32] */ + 0xfd, 0x7b, 0x03, 0xa9, /* 14: stp x29, x30, [sp, #48] */ + 0xe8, 0x27, 0xbc, 0x6d, /* 18: stp d8, d9, [sp, #-64]! */ + 0xea, 0x2f, 0x01, 0x6d, /* 1c: stp d10, d11, [sp, #16] */ + 0xec, 0x37, 0x02, 0x6d, /* 20: stp d12, d13, [sp, #32] */ + 0xee, 0x3f, 0x03, 0x6d, /* 24: stp d14, d15, [sp, #48] */ + 0xc0, 0x03, 0x5f, 0xd6, /* 28: ret */ + }; + + static const DWORD unwind_info_5_header = + (sizeof(function_5)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (4 << 27); /* codes */ + + static const BYTE unwind_info_5[] = + { + DW(unwind_info_5_header), + + UWOP_SAVE_NEXT, /* 24: stp d14, d15, [sp, #48] */ + UWOP_SAVE_FREGP(12, 32), /* 20: stp d12, d13, [sp, #32] */ + UWOP_SAVE_NEXT, /* 1c: stp d10, d11, [sp, #16] */ + UWOP_SAVE_FREGP_X(8, 64), /* 18: stp d8, d9, [sp, #-64]! */ + UWOP_SAVE_NEXT, /* 14: stp x29, x30, [sp, #48] */ + UWOP_SAVE_REGP(27, 32), /* 10: stp x27, x28, [sp, #32] */ + UWOP_SAVE_NEXT, /* 0c: stp x25, x26, [sp, #16] */ + UWOP_SAVE_REGP_X(23, 64), /* 08: stp x23, x24, [sp, #-64]! */ + UWOP_SAVE_NEXT, /* 04: stp x21, x22, [sp, #16] */ + UWOP_SAVE_R19R20_X(32), /* 00: stp x19, x20, [sp, #-32]! */ + UWOP_END, + 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 results_5[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x00020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x00020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {x22, 0x18}, {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {x26, 0x18}, {-1,-1} }}, + { 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 struct unwind_test tests[] = { #define TEST(func, unwind, unwind_packed, results) \ @@ -4627,6 +4764,8 @@ static void test_virtual_unwind(void) TEST(function_1, unwind_info_1, 1, results_1), TEST(function_2, unwind_info_2, 0, results_2), TEST(function_3, unwind_info_3, 0, results_3), + TEST(function_4, unwind_info_4, 0, results_4), + TEST(function_5, unwind_info_5, 0, results_5), #undef TEST }; unsigned int i;