Currently there is `NtContinue()` test only for `aarch64` so implement it for amd64 aswell.
This implementation is very similar to `aarch64` and it's very basic test.
It doesn't change/test `ContextFlags` so it won't catch https://bugs.winehq.org/show_bug.cgi?id=56050 but that can be implemented later on top of this.
From: Dāvis Mosāns davispuh@gmail.com
--- dlls/ntdll/tests/exception.c | 262 +++++++++++++++++++---------------- 1 file changed, 140 insertions(+), 122 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index bac571b2733..06d700a5873 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -8870,128 +8870,6 @@ static void test_debug_service(DWORD numexc) /* not supported */ }
-static void test_continue(void) -{ - struct context_pair { - CONTEXT before; - CONTEXT after; - } contexts; - unsigned int i; - NTSTATUS (*func_ptr)( struct context_pair *, BOOL alertable, void *continue_func, void *capture_func ) = code_mem; - - static const DWORD call_func[] = - { - 0xa9bd7bfd, /* stp x29, x30, [sp, #-0x30]! */ - /* stash volatile registers before calling capture */ - 0xa90107e0, /* stp x0, x1, [sp, #0x10] */ - 0xa9020fe2, /* stp x2, x3, [sp, #0x20] */ - 0xd63f0060, /* blr x3 * capture context from before NtContinue to contexts->before */ - 0xa9420fe2, /* ldp x2, x3, [sp, #0x20] */ - 0xa94107e0, /* ldp x0, x1, [sp, #0x10] */ - /* overwrite the contents of x4...k28 with a dummy value */ - 0xd297dde4, /* mov x4, #0xbeef */ - 0xf2bbd5a4, /* movk x4, #0xdead, lsl #16 */ - 0xaa048084, /* orr x4, x4, x4, lsl #32 */ - 0xaa0403e5, /* mov x5, x4 */ - 0xaa0403e6, /* mov x6, x4 */ - 0xaa0403e7, /* mov x7, x4 */ - 0xaa0403e8, /* mov x8, x4 */ - 0xaa0403e9, /* mov x9, x4 */ - 0xaa0403ea, /* mov x10, x4 */ - 0xaa0403eb, /* mov x11, x4 */ - 0xaa0403ec, /* mov x12, x4 */ - 0xaa0403ed, /* mov x13, x4 */ - 0xaa0403ee, /* mov x14, x4 */ - 0xaa0403ef, /* mov x15, x4 */ - 0xaa0403f0, /* mov x16, x4 */ - 0xaa0403f1, /* mov x17, x4 */ - /* avoid overwriting the TEB in x18 */ - 0xaa0403f3, /* mov x19, x4 */ - 0xaa0403f4, /* mov x20, x4 */ - 0xaa0403f5, /* mov x21, x4 */ - 0xaa0403f6, /* mov x22, x4 */ - 0xaa0403f7, /* mov x23, x4 */ - 0xaa0403f8, /* mov x24, x4 */ - 0xaa0403f9, /* mov x25, x4 */ - 0xaa0403fa, /* mov x26, x4 */ - 0xaa0403fb, /* mov x27, x4 */ - 0xaa0403fc, /* mov x28, x4 */ - /* overwrite the contents all vector registers a dummy value */ - 0x4e080c80, /* dup v0.2d, x4 */ - 0x4ea01c01, /* mov v1.2d, v0.2d */ - 0x4ea01c02, /* mov v2.2d, v0.2d */ - 0x4ea01c03, /* mov v3.2d, v0.2d */ - 0x4ea01c04, /* mov v4.2d, v0.2d */ - 0x4ea01c05, /* mov v5.2d, v0.2d */ - 0x4ea01c06, /* mov v6.2d, v0.2d */ - 0x4ea01c07, /* mov v7.2d, v0.2d */ - 0x4ea01c08, /* mov v8.2d, v0.2d */ - 0x4ea01c09, /* mov v9.2d, v0.2d */ - 0x4ea01c0a, /* mov v10.2d, v0.2d */ - 0x4ea01c0b, /* mov v11.2d, v0.2d */ - 0x4ea01c0c, /* mov v12.2d, v0.2d */ - 0x4ea01c0d, /* mov v13.2d, v0.2d */ - 0x4ea01c0e, /* mov v14.2d, v0.2d */ - 0x4ea01c0f, /* mov v15.2d, v0.2d */ - 0x4ea01c10, /* mov v16.2d, v0.2d */ - 0x4ea01c11, /* mov v17.2d, v0.2d */ - 0x4ea01c12, /* mov v18.2d, v0.2d */ - 0x4ea01c13, /* mov v19.2d, v0.2d */ - 0x4ea01c14, /* mov v20.2d, v0.2d */ - 0x4ea01c15, /* mov v21.2d, v0.2d */ - 0x4ea01c16, /* mov v22.2d, v0.2d */ - 0x4ea01c17, /* mov v23.2d, v0.2d */ - 0x4ea01c18, /* mov v24.2d, v0.2d */ - 0x4ea01c19, /* mov v25.2d, v0.2d */ - 0x4ea01c1a, /* mov v26.2d, v0.2d */ - 0x4ea01c1b, /* mov v27.2d, v0.2d */ - 0x4ea01c1c, /* mov v28.2d, v0.2d */ - 0x4ea01c1d, /* mov v29.2d, v0.2d */ - 0x4ea01c1e, /* mov v30.2d, v0.2d */ - 0x4ea01c1f, /* mov v31.2d, v0.2d */ - 0xd51b441f, /* msr fpcr, xzr */ - 0xd51b443f, /* msr fpsr, xzr */ - /* setup the control context so execution continues from label 1 */ - 0x10000064, /* adr x4, #0xc */ - 0xf9008404, /* str x4, [x0, #0x108] */ - 0xd63f0040, /* blr x2 * restore the captured integer and floating point context */ - 0xf94017e3, /* 1: ldr x3, [sp, #0x28] */ - 0xf9400be0, /* ldr x0, [sp, #0x10] */ - 0x910e4000, /* add x0, x0, #0x390 * adjust contexts to point to contexts->after */ - 0xd63f0060, /* blr x3 * capture context from after NtContinue to contexts->after */ - 0xa8c37bfd, /* ldp x29, x30, [sp], #0x30 */ - 0xd65f03c0, /* ret */ - }; - - if (!pRtlCaptureContext) - { - win_skip("RtlCaptureContext is not available.\n"); - return; - } - - memcpy( func_ptr, call_func, sizeof(call_func) ); - FlushInstructionCache( GetCurrentProcess(), func_ptr, sizeof(call_func) ); - -#define COMPARE(reg) \ - ok( contexts.before.reg == contexts.after.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)contexts.before.reg, (void *)(ULONG64)contexts.after.reg ) -#define COMPARE_INDEXED(reg) \ - ok( contexts.before.reg == contexts.after.reg, "wrong " #reg " i: %u, %p/%p\n", i, (void *)(ULONG64)contexts.before.reg, (void *)(ULONG64)contexts.after.reg ) - - func_ptr( &contexts, FALSE, NtContinue, pRtlCaptureContext ); - - for (i = 1; i < 29; i++) COMPARE_INDEXED( X[i] ); - - COMPARE( Fpcr ); - COMPARE( Fpsr ); - - for (i = 0; i < 32; i++) - { - COMPARE_INDEXED( V[i].Low ); - COMPARE_INDEXED( V[i].High ); - } -#undef COMPARE -} - static BOOL hook_called; static BOOL got_exception;
@@ -9230,6 +9108,144 @@ static void WINAPI hook_KiUserCallbackDispatcher(void *sp)
#endif /* __aarch64__ */
+static void test_continue(void) +{ + struct context_pair { + CONTEXT before; + CONTEXT after; + } contexts; + unsigned int i; + NTSTATUS (*func_ptr)( struct context_pair *, void *arg2, void *continue_func, void *capture_func ) = code_mem; + +#ifdef __x86_64__ + /* FIXME */ + static const BYTE call_func[] = + { + }; + skip("test for NtContinue() not implemented\n"); + return; +#elif defined(__aarch64__) + static const DWORD call_func[] = + { + 0xa9bd7bfd, /* stp x29, x30, [sp, #-0x30]! */ + /* stash volatile registers before calling capture */ + 0xa90107e0, /* stp x0, x1, [sp, #0x10] */ + 0xa9020fe2, /* stp x2, x3, [sp, #0x20] */ + 0xd63f0060, /* blr x3 * capture context from before NtContinue to contexts->before */ + 0xa9420fe2, /* ldp x2, x3, [sp, #0x20] */ + 0xa94107e0, /* ldp x0, x1, [sp, #0x10] */ + /* overwrite the contents of x4...k28 with a dummy value */ + 0xd297dde4, /* mov x4, #0xbeef */ + 0xf2bbd5a4, /* movk x4, #0xdead, lsl #16 */ + 0xaa048084, /* orr x4, x4, x4, lsl #32 */ + 0xaa0403e5, /* mov x5, x4 */ + 0xaa0403e6, /* mov x6, x4 */ + 0xaa0403e7, /* mov x7, x4 */ + 0xaa0403e8, /* mov x8, x4 */ + 0xaa0403e9, /* mov x9, x4 */ + 0xaa0403ea, /* mov x10, x4 */ + 0xaa0403eb, /* mov x11, x4 */ + 0xaa0403ec, /* mov x12, x4 */ + 0xaa0403ed, /* mov x13, x4 */ + 0xaa0403ee, /* mov x14, x4 */ + 0xaa0403ef, /* mov x15, x4 */ + 0xaa0403f0, /* mov x16, x4 */ + 0xaa0403f1, /* mov x17, x4 */ + /* avoid overwriting the TEB in x18 */ + 0xaa0403f3, /* mov x19, x4 */ + 0xaa0403f4, /* mov x20, x4 */ + 0xaa0403f5, /* mov x21, x4 */ + 0xaa0403f6, /* mov x22, x4 */ + 0xaa0403f7, /* mov x23, x4 */ + 0xaa0403f8, /* mov x24, x4 */ + 0xaa0403f9, /* mov x25, x4 */ + 0xaa0403fa, /* mov x26, x4 */ + 0xaa0403fb, /* mov x27, x4 */ + 0xaa0403fc, /* mov x28, x4 */ + /* overwrite the contents all vector registers a dummy value */ + 0x4e080c80, /* dup v0.2d, x4 */ + 0x4ea01c01, /* mov v1.2d, v0.2d */ + 0x4ea01c02, /* mov v2.2d, v0.2d */ + 0x4ea01c03, /* mov v3.2d, v0.2d */ + 0x4ea01c04, /* mov v4.2d, v0.2d */ + 0x4ea01c05, /* mov v5.2d, v0.2d */ + 0x4ea01c06, /* mov v6.2d, v0.2d */ + 0x4ea01c07, /* mov v7.2d, v0.2d */ + 0x4ea01c08, /* mov v8.2d, v0.2d */ + 0x4ea01c09, /* mov v9.2d, v0.2d */ + 0x4ea01c0a, /* mov v10.2d, v0.2d */ + 0x4ea01c0b, /* mov v11.2d, v0.2d */ + 0x4ea01c0c, /* mov v12.2d, v0.2d */ + 0x4ea01c0d, /* mov v13.2d, v0.2d */ + 0x4ea01c0e, /* mov v14.2d, v0.2d */ + 0x4ea01c0f, /* mov v15.2d, v0.2d */ + 0x4ea01c10, /* mov v16.2d, v0.2d */ + 0x4ea01c11, /* mov v17.2d, v0.2d */ + 0x4ea01c12, /* mov v18.2d, v0.2d */ + 0x4ea01c13, /* mov v19.2d, v0.2d */ + 0x4ea01c14, /* mov v20.2d, v0.2d */ + 0x4ea01c15, /* mov v21.2d, v0.2d */ + 0x4ea01c16, /* mov v22.2d, v0.2d */ + 0x4ea01c17, /* mov v23.2d, v0.2d */ + 0x4ea01c18, /* mov v24.2d, v0.2d */ + 0x4ea01c19, /* mov v25.2d, v0.2d */ + 0x4ea01c1a, /* mov v26.2d, v0.2d */ + 0x4ea01c1b, /* mov v27.2d, v0.2d */ + 0x4ea01c1c, /* mov v28.2d, v0.2d */ + 0x4ea01c1d, /* mov v29.2d, v0.2d */ + 0x4ea01c1e, /* mov v30.2d, v0.2d */ + 0x4ea01c1f, /* mov v31.2d, v0.2d */ + 0xd51b441f, /* msr fpcr, xzr */ + 0xd51b443f, /* msr fpsr, xzr */ + /* setup the control context so execution continues from label 1 */ + 0x10000064, /* adr x4, #0xc */ + 0xf9008404, /* str x4, [x0, #0x108] */ + 0xd63f0040, /* blr x2 * restore the captured integer and floating point context */ + 0xf94017e3, /* 1: ldr x3, [sp, #0x28] */ + 0xf9400be0, /* ldr x0, [sp, #0x10] */ + 0x910e4000, /* add x0, x0, #0x390 * adjust contexts to point to contexts->after */ + 0xd63f0060, /* blr x3 * capture context from after NtContinue to contexts->after */ + 0xa8c37bfd, /* ldp x29, x30, [sp], #0x30 */ + 0xd65f03c0, /* ret */ + }; +#else + static const BYTE call_func[] = { }; + skip("test for NtContinue() not implemented\n"); + return; +#endif + + if (!pRtlCaptureContext) + { + win_skip("RtlCaptureContext is not available.\n"); + return; + } + + memcpy( func_ptr, call_func, sizeof(call_func) ); + FlushInstructionCache( GetCurrentProcess(), func_ptr, sizeof(call_func) ); + + func_ptr( &contexts, FALSE, NtContinue, pRtlCaptureContext ); + +#ifdef __x86_64__ +#elif defined(__aarch64__) +#define COMPARE(reg) \ + ok( contexts.before.reg == contexts.after.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)contexts.before.reg, (void *)(ULONG64)contexts.after.reg ) +#define COMPARE_INDEXED(reg) \ + ok( contexts.before.reg == contexts.after.reg, "wrong " #reg " i: %u, %p/%p\n", i, (void *)(ULONG64)contexts.before.reg, (void *)(ULONG64)contexts.after.reg ) + + for (i = 1; i < 29; i++) COMPARE_INDEXED( X[i] ); + + COMPARE( Fpcr ); + COMPARE( Fpsr ); + + for (i = 0; i < 32; i++) + { + COMPARE_INDEXED( V[i].Low ); + COMPARE_INDEXED( V[i].High ); + } +#endif +#undef COMPARE +} + #if defined(__i386__) || defined(__x86_64__)
static DWORD WINAPI register_check_thread(void *arg) @@ -12429,6 +12445,7 @@ START_TEST(exception) test_extended_context(); test_copy_context(); test_set_live_context(); + test_continue();
#elif defined(__x86_64__)
@@ -12475,6 +12492,7 @@ START_TEST(exception) test_unwind_from_apc(); test_syscall_clobbered_regs(); test_raiseexception_regs(); + test_continue();
#elif defined(__aarch64__)
From: Dāvis Mosāns davispuh@gmail.com
--- dlls/ntdll/tests/exception.c | 131 ++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 06d700a5873..5e4bdc1dac4 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -9118,12 +9118,115 @@ static void test_continue(void) NTSTATUS (*func_ptr)( struct context_pair *, void *arg2, void *continue_func, void *capture_func ) = code_mem;
#ifdef __x86_64__ - /* FIXME */ static const BYTE call_func[] = { + /* need to preserve these */ + 0x53, /* push rbx */ + 0x55, /* push rbp */ + 0x56, /* push rsi */ + 0x57, /* push rdi */ + 0x41, 0x54, /* push r12 */ + 0x41, 0x55, /* push r13 */ + 0x41, 0x56, /* push r14 */ + 0x41, 0x57, /* push r15 */ + + 0x48, 0x83, 0xec, 0x28, /* sub rsp, 8*5; stack space */ + 0x48, 0x89, 0x24, 0x24, /* mov [rsp+8*0], rsp; for validation */ + + /* save args */ + 0x48, 0x89, 0x4c, 0x24, 0x08, /* mov [rsp+8*1], rcx */ + 0x48, 0x89, 0x54, 0x24, 0x10, /* mov [rsp+8*2], rdx */ + 0x4c, 0x89, 0x44, 0x24, 0x18, /* mov [rsp+8*3], r8 */ + 0x4c, 0x89, 0x4c, 0x24, 0x20, /* mov [rsp+8*4], r9 */ + + /* invoke capture context */ + 0x41, 0xff, 0xd1, /* call r9 */ + + /* overwrite general registers */ + 0x48, 0xb8, 0xef, 0xbe, 0xad, 0xde, 0xef, 0xbe, 0xad, 0xde, /* mov rax, 0xdeadbeefdeadbeef */ + 0x48, 0x89, 0xc1, /* mov rcx, rax */ + 0x48, 0x89, 0xc2, /* mov rdx, rax */ + 0x48, 0x89, 0xc3, /* mov rbx, rax */ + 0x48, 0x89, 0xc5, /* mov rbp, rax */ + 0x48, 0x89, 0xc6, /* mov rsi, rax */ + 0x48, 0x89, 0xc7, /* mov rdi, rax */ + 0x49, 0x89, 0xc0, /* mov r8, rax */ + 0x49, 0x89, 0xc1, /* mov r9, rax */ + 0x49, 0x89, 0xc2, /* mov r10, rax */ + 0x49, 0x89, 0xc3, /* mov r11, rax */ + 0x49, 0x89, 0xc4, /* mov r12, rax */ + 0x49, 0x89, 0xc5, /* mov r13, rax */ + 0x49, 0x89, 0xc6, /* mov r14, rax */ + 0x49, 0x89, 0xc7, /* mov r15, rax */ + + /* overwrite SSE registers */ + 0x66, 0x48, 0x0f, 0x6e, 0xc0, /* movq xmm0, rax */ + 0x66, 0x0f, 0x6c, 0xc0, /* punpcklqdq xmm0,xmm0; extend to high quadword */ + 0x0f, 0x28, 0xc8, /* movaps xmm1, xmm0 */ + 0x0f, 0x28, 0xd0, /* movaps xmm2, xmm0 */ + 0x0f, 0x28, 0xd8, /* movaps xmm3, xmm0 */ + 0x0f, 0x28, 0xe0, /* movaps xmm4, xmm0 */ + 0x0f, 0x28, 0xe8, /* movaps xmm5, xmm0 */ + 0x0f, 0x28, 0xf0, /* movaps xmm6, xmm0 */ + 0x0f, 0x28, 0xf8, /* movaps xmm7, xmm0 */ + 0x44, 0x0f, 0x28, 0xc0, /* movaps xmm8, xmm0 */ + 0x44, 0x0f, 0x28, 0xc8, /* movaps xmm9, xmm0 */ + 0x44, 0x0f, 0x28, 0xd0, /* movaps xmm10, xmm0 */ + 0x44, 0x0f, 0x28, 0xd8, /* movaps xmm11, xmm0 */ + 0x44, 0x0f, 0x28, 0xe0, /* movaps xmm12, xmm0 */ + 0x44, 0x0f, 0x28, 0xe8, /* movaps xmm13, xmm0 */ + 0x44, 0x0f, 0x28, 0xf0, /* movaps xmm14, xmm0 */ + 0x44, 0x0f, 0x28, 0xf8, /* movaps xmm15, xmm0 */ + + /* FIXME: overwrite debug, x87 FPU and AVX registers to test those */ + + /* load args */ + 0x48, 0x8b, 0x4c, 0x24, 0x08, /* mov rcx, [rsp+8*1]; context */ + 0x48, 0x8b, 0x54, 0x24, 0x10, /* mov rdx, [rsp+8*2]; arg2 */ + 0x48, 0x83, 0xec, 0x70, /* sub rsp, 0x70; change stack */ + + /* setup context to return to label 1 */ + 0x48, 0x8d, 0x05, 0x1b, 0x00, 0x00, 0x00, /* lea rax, [rel 1] (0x1b) */ + 0x48, 0x89, 0x81, 0xf8, 0x00, 0x00, 0x00, /* mov [rcx + 0xf8 (context.Rip)], rax */ + + /* trash EFLAGS */ + 0x9c, /* pushfq */ + 0x58, /* pop rax */ + 0x48, 0x0d, 0x00, 0x45, 0x00, 0x00, /* or rax, 0x0100 + 0x4400; set trap flag so it's cleared. + FIXME: also clearing Direction Flag and Nested Task as those break Wine */ + + 0x48, 0xf7, 0xd0, /* not rax */ + 0x50, /* push rax */ + 0x9d, /* popfq */ + + /* invoke NtContinue... */ + 0xff, 0x94, 0x24, 0x88, 0x00, 0x00, 0x00, /* call [rsp+8*3+0x70] */ + + /* validate stack pointer */ + 0x48, 0x8b, 0x0c, 0x24, /* 1: mov rcx, [rsp] */ + 0x48, 0x39, 0xe1, /* cmp rcx, rsp */ + 0x74, 0x02, /* je 2; jump over ud2 */ + 0x0f, 0x0b, /* ud2; stack pointer invalid, let's crash */ + + /* invoke capture context */ + 0x48, 0x8b, 0x4c, 0x24, 0x08, /* mov rcx, [rsp+8*1]; context */ + 0x48, 0x81, 0xc1, 0xd0, 0x04, 0x00, 0x00, /* add rcx, 0x4d0; +sizeof(CONTEXT) to get context->after */ + 0xff, 0x54, 0x24, 0x20, /* call [rsp+8*4] */ + + /* free stack */ + 0x48, 0x83, 0xc4, 0x28, /* 2: add rsp, 8*5 */ + + /* restore back */ + 0x41, 0x5f, /* pop r15 */ + 0x41, 0x5e, /* pop r14 */ + 0x41, 0x5d, /* pop r13 */ + 0x41, 0x5c, /* pop r12 */ + 0x5f, /* pop rdi */ + 0x5e, /* pop rsi */ + 0x5d, /* pop rbp */ + 0x5b, /* pop rbx */ + 0xc3 /* ret */ }; - skip("test for NtContinue() not implemented\n"); - return; #elif defined(__aarch64__) static const DWORD call_func[] = { @@ -9226,6 +9329,28 @@ static void test_continue(void) func_ptr( &contexts, FALSE, NtContinue, pRtlCaptureContext );
#ifdef __x86_64__ +#define COMPARE(reg) \ + ok( contexts.before.reg == contexts.after.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)contexts.before.reg, (void *)(ULONG64)contexts.after.reg ) + + COMPARE( Rax ); + COMPARE( Rdx ); + COMPARE( Rbx ); + COMPARE( Rbp ); + COMPARE( Rsi ); + COMPARE( Rdi ); + COMPARE( R8 ); + COMPARE( R9 ); + COMPARE( R10 ); + COMPARE( R11 ); + COMPARE( R12 ); + COMPARE( R13 ); + COMPARE( R14 ); + COMPARE( R15 ); + + ok(memcmp(&contexts.before.Xmm0, &contexts.after.Xmm0, &contexts.before.Xmm15 - &contexts.before.Xmm0) == 0, + "wrong some SSE register, Xmm0 %lld/%lld Xmm15 %lld/%lld", contexts.before.Xmm0.Low, contexts.after.Xmm0.Low, + contexts.before.Xmm15.High, contexts.after.Xmm15.High); + #elif defined(__aarch64__) #define COMPARE(reg) \ ok( contexts.before.reg == contexts.after.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)contexts.before.reg, (void *)(ULONG64)contexts.after.reg )
Hello there! Thanks for your contribution.
Apropos commit "ntdll/tests: Move test_continue() to be used for all platforms":
I think there is a better way to test `NtContinue()` across platforms. You're approach of sharing the `test_continue()` function header/footer works, but ends up branching on the architecture *twice*.
```c static void test_continue(void) { ...
#ifdef __x86_64__ static const BYTE call_func[] = { ... }; #elif defined(__aarch64__) static const DWORD call_func[] = { ... }; #else ... #endif
... memcpy( func_ptr, call_func, sizeof(call_func) ); FlushInstructionCache( GetCurrentProcess(), func_ptr, sizeof(call_func) );
func_ptr( &contexts, FALSE, NtContinue, pRtlCaptureContext );
#ifdef __x86_64__ ... #elif defined(__aarch64__) ... #endif ... } ```
Notice that there is little code shared between architectures, other than `NtContinue` and `RtlCaptureContext`.
Instead, I suggest that you define the entire `test_continue()` per each architecture:
```c #ifdef __x86_64__ ... static void test_thread_context(void) { ... #undef COMPARE }
static void test_continue(void) { ...
static const BYTE call_func[] = { ... };
... memcpy( func_ptr, call_func, sizeof(call_func) ); FlushInstructionCache( GetCurrentProcess(), func_ptr, sizeof(call_func) );
func_ptr( &contexts, FALSE, NtContinue, pRtlCaptureContext );
... }
static void test_wow64_context(void) { ... } ... #elif defined(__arch64__) ... static void test_continue(void) { ... } ```
Even if this leads to code duplication, this allows us flexibility to extend `test_continue` with arch-specific logic in the future, and avoids interleaving of different test code for each architecture.