[RTL_APITEST] Add rtl_unittest
authorTimo Kreuzer <timo.kreuzer@reactos.org>
Sat, 20 May 2023 06:36:13 +0000 (09:36 +0300)
committerTimo Kreuzer <timo.kreuzer@reactos.org>
Fri, 10 Nov 2023 17:00:41 +0000 (19:00 +0200)
First test is RtlCaptureContext

14 files changed:
modules/rostests/apitests/CMakeLists.txt
modules/rostests/apitests/ntdll/CMakeLists.txt
modules/rostests/apitests/ntdll/testlist.c
modules/rostests/apitests/rtl/CMakeLists.txt [new file with mode: 0644]
modules/rostests/apitests/rtl/amd64/RtlCaptureContext-asm.s [new file with mode: 0644]
modules/rostests/apitests/rtl/amd64/RtlCaptureContext.c [new file with mode: 0644]
modules/rostests/apitests/rtl/i386/RtlCaptureContext.c [new file with mode: 0644]
modules/rostests/apitests/rtl/ldrstubs.c [new file with mode: 0644]
modules/rostests/apitests/rtl/rtltests.h [new file with mode: 0644]
modules/rostests/apitests/rtl/testlist.c [new file with mode: 0644]
modules/rostests/kmtests/CMakeLists.txt
modules/rostests/kmtests/include/rtltests.h [new file with mode: 0644]
modules/rostests/kmtests/kmtest/testlist.c
modules/rostests/kmtests/kmtest_drv/testlist.c

index ebc0bdf..4bb4e2e 100644 (file)
@@ -40,6 +40,7 @@ add_subdirectory(ole32)
 add_subdirectory(opengl32)
 add_subdirectory(pefile)
 add_subdirectory(powrprof)
+add_subdirectory(rtl)
 add_subdirectory(sdk)
 add_subdirectory(setupapi)
 add_subdirectory(sfc)
index b6ce3a1..acc24d4 100644 (file)
@@ -101,7 +101,8 @@ list(APPEND SOURCE
     StackOverflow.c
     SystemInfo.c
     UserModeException.c
-    Timer.c)
+    Timer.c
+    precomp.h)
 
 if(ARCH STREQUAL "i386")
     add_asm_files(ntdll_apitest_asm i386/NtContinue.S)
@@ -126,7 +127,7 @@ set_target_properties(ntdll_apitest
     ENABLE_EXPORTS TRUE
     DEFINE_SYMBOL "")
 
-target_link_libraries(ntdll_apitest wine uuid ${PSEH_LIB})
+target_link_libraries(ntdll_apitest rtl_test_lib wine uuid ${PSEH_LIB})
 set_module_type(ntdll_apitest win32cui)
 add_importlibs(ntdll_apitest msvcrt advapi32 kernel32 ntdll)
 add_pch(ntdll_apitest precomp.h "${PCH_SKIP_SOURCE}")
index 438cc56..cc19972 100644 (file)
@@ -54,6 +54,7 @@ extern void func_NtUnloadDriver(void);
 extern void func_NtWriteFile(void);
 extern void func_RtlAllocateHeap(void);
 extern void func_RtlBitmap(void);
+extern void func_RtlCaptureContext(void);
 extern void func_RtlComputePrivatizedDllName_U(void);
 extern void func_RtlCopyMappedMemory(void);
 extern void func_RtlCriticalSection(void);
@@ -194,6 +195,9 @@ const struct test winetest_testlist[] =
     { "StackOverflow",                  func_StackOverflow },
     { "TimerResolution",                func_TimerResolution },
     { "UserModeException",              func_UserModeException },
+#ifdef _M_AMD64
+    { "RtlCaptureContext",              func_RtlCaptureContext },
+#endif
 
     { 0, 0 }
 };
diff --git a/modules/rostests/apitests/rtl/CMakeLists.txt b/modules/rostests/apitests/rtl/CMakeLists.txt
new file mode 100644 (file)
index 0000000..7c74792
--- /dev/null
@@ -0,0 +1,56 @@
+
+
+list(APPEND SOURCE
+    # To be filled
+)
+
+if(ARCH STREQUAL "i386")
+    list(APPEND SOURCE
+        i386/RtlCaptureContext.c
+    )
+elseif(ARCH STREQUAL "amd64")
+    list(APPEND ASM_SOURCE
+        amd64/RtlCaptureContext-asm.s
+    )
+    list(APPEND SOURCE
+        amd64/RtlCaptureContext.c
+    )
+elseif(ARCH STREQUAL "arm")
+    list(APPEND SOURCE
+        # To be filled
+    )
+endif()
+
+add_asm_files(rtl_test_asm ${ASM_SOURCE})
+
+add_library(rtl_test_lib
+    ${SOURCE}
+    ${rtl_test_asm}
+)
+
+target_compile_definitions(rtl_test_lib PRIVATE _RTL_TEST _NTSYSTEM_)
+
+add_dependencies(rtl_test_lib asm)
+
+if(NOT MSVC)
+    set_source_files_properties(RtlGetFullPathName_UstrEx.c PROPERTIES COMPILE_FLAGS "-Wno-format")
+
+    # Avoid "universal character names are only valid in C++ and C99" error.
+    set_property(TARGET rtl_test_lib PROPERTY C_STANDARD 99)
+endif()
+
+# RTL tests with static linkage (called unittest, so it won't run in rosautotest)
+add_executable(rtl_unittest
+    testlist.c
+    ldrstubs.c)
+target_compile_definitions(rtl_unittest PRIVATE _RTL_TEST _NTSYSTEM_)
+target_link_libraries(rtl_unittest rtl_test_lib rtl rtl_um rtl_vista wine uuid ${PSEH_LIB})
+set_module_type(rtl_unittest win32cui)
+add_importlibs(rtl_unittest msvcrt advapi32 kernel32 ntdll)
+target_compile_definitions(rtl_unittest PRIVATE KMT_USER_MODE NTDDI_VERSION=NTDDI_WS03SP1)
+
+add_rostests_file(TARGET rtl_unittest)
+
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_compile_options(rtl_unittest PRIVATE -Wno-format)
+endif()
diff --git a/modules/rostests/apitests/rtl/amd64/RtlCaptureContext-asm.s b/modules/rostests/apitests/rtl/amd64/RtlCaptureContext-asm.s
new file mode 100644 (file)
index 0000000..35eb911
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * PROJECT:     ReactOS api tests
+ * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE:     Test helper for x64 RtlCaptureContext
+ * COPYRIGHT:   Copyright 2022 Timo Kreuzer <timo.kreuzer@reactos.org>
+ */
+
+#include <asm.inc>
+#include <ksamd64.inc>
+
+.code64
+
+EXTERN RtlCaptureContext:PROC
+
+FUNC ZeroContext
+
+    pushfq
+    .ALLOCSTACK 8
+    push rax
+    .PUSHREG rax
+    push rcx
+    .PUSHREG rcx
+    push rdi
+    .PUSHREG rdi
+    .ENDPROLOG
+
+    mov rdi, rcx
+    mov rcx, CONTEXT_FRAME_LENGTH
+    xor eax, eax
+    cld
+    rep stosb
+
+    pop rdi
+    pop rcx
+    pop rax
+    popfq
+    ret
+
+ENDFUNC
+
+//
+// VOID
+// RtlCaptureContextWrapper (
+//    _Inout_ PCONTEXT InOutContext,
+//    _Out_ PCONTEXT CapturedContext);
+//
+PUBLIC RtlCaptureContextWrapper
+FUNC RtlCaptureContextWrapper
+
+    // Generate a KEXCEPTION_FRAME on the stack
+    GENERATE_EXCEPTION_FRAME
+
+    // Save parameters
+    mov [rsp + ExP1Home], rcx
+    mov [rsp + ExP2Home], rdx
+
+    // Save current EFlags
+    pushfq
+    pop qword ptr [rsp + ExP3Home]
+
+    // Load integer registers from InOutContext
+    mov rax, [rcx + CxRax]
+    mov rdx, [rcx + CxRdx]
+    mov rbx, [rcx + CxRbx]
+    mov rbp, [rcx + CxRbp]
+    mov rsi, [rcx + CxRsi]
+    mov rdi, [rcx + CxRdi]
+    mov r8, [rcx + CxR8]
+    mov r9, [rcx + CxR9]
+    mov r10, [rcx + CxR10]
+    mov r11, [rcx + CxR11]
+    mov r12, [rcx + CxR12]
+    mov r13, [rcx + CxR13]
+    mov r14, [rcx + CxR14]
+    mov r15, [rcx + CxR15]
+
+    // Load floating point registers from InOutContext
+    fxrstor [rcx + CxFltSave]
+
+    // Load MxCsr (this overwrites the value from FltSave)
+    ldmxcsr [rcx + CxMxCsr]
+
+    // Load XMM registers
+    movaps xmm0, [rcx + CxXmm0]
+    movaps xmm1, [rcx + CxXmm1]
+    movaps xmm2, [rcx + CxXmm2]
+    movaps xmm3, [rcx + CxXmm3]
+    movaps xmm4, [rcx + CxXmm4]
+    movaps xmm5, [rcx + CxXmm5]
+    movaps xmm6, [rcx + CxXmm6]
+    movaps xmm7, [rcx + CxXmm7]
+    movaps xmm8, [rcx + CxXmm8]
+    movaps xmm9, [rcx + CxXmm9]
+    movaps xmm10, [rcx + CxXmm10]
+    movaps xmm11, [rcx + CxXmm11]
+    movaps xmm12, [rcx + CxXmm12]
+    movaps xmm13, [rcx + CxXmm13]
+    movaps xmm14, [rcx + CxXmm14]
+    movaps xmm15, [rcx + CxXmm15]
+
+    // Load EFlags
+    push qword ptr [rcx + CxEFlags]
+    popfq
+
+    // Capture the context
+    mov rcx, [rsp + ExP2Home]
+    call RtlCaptureContext
+ReturnAddress:
+
+    // Save the returned rcx
+    mov [rsp + ExP4Home], rcx
+
+    // Restore original rcx
+    mov rcx, [rsp + ExP1Home]
+
+    // Zero out the context (this does not clobber any registers/flags)
+    call ZeroContext
+
+    // Save returned Eflags
+    pushfq
+    pop qword ptr [rcx + CxEFlags]
+
+    // Restore original EFLags
+    push qword ptr [rsp + ExP3Home]
+    popfq
+
+    // Save returned integer registers to InOutContext
+    mov [rcx + CxRax], rax
+    mov [rcx + CxRdx], rdx
+    mov [rcx + CxRbx], rbx
+    mov [rcx + CxRbp], rbp
+    mov [rcx + CxRsi], rsi
+    mov [rcx + CxRdi], rdi
+    mov [rcx + CxR8], r8
+    mov [rcx + CxR9], r9
+    mov [rcx + CxR10], r10
+    mov [rcx + CxR11], r11
+    mov [rcx + CxR12], r12
+    mov [rcx + CxR13], r13
+    mov [rcx + CxR14], r14
+    mov [rcx + CxR15], r15
+
+    // Save the returned rcx in the context
+    mov rax, [rsp + ExP4Home]
+    mov [rcx + CxRcx], rax
+
+    // Save returned floating point registers to InOutContext
+    stmxcsr [rcx + CxMxCsr]
+    fxsave [rcx + CxFltSave]
+    movaps [rcx + CxXmm0], xmm0
+    movaps [rcx + CxXmm1], xmm1
+    movaps [rcx + CxXmm2], xmm2
+    movaps [rcx + CxXmm3], xmm3
+    movaps [rcx + CxXmm4], xmm4
+    movaps [rcx + CxXmm5], xmm5
+    movaps [rcx + CxXmm6], xmm6
+    movaps [rcx + CxXmm7], xmm7
+    movaps [rcx + CxXmm8], xmm8
+    movaps [rcx + CxXmm9], xmm9
+    movaps [rcx + CxXmm10], xmm10
+    movaps [rcx + CxXmm11], xmm11
+    movaps [rcx + CxXmm12], xmm12
+    movaps [rcx + CxXmm13], xmm13
+    movaps [rcx + CxXmm14], xmm14
+    movaps [rcx + CxXmm15], xmm15
+
+    // Save the expected return address
+    lea rax, ReturnAddress[rip]
+    mov [rcx + CxRip], rax
+
+    // Save the expected stored rsp
+    mov [rcx + CxRsp], rsp
+
+    // Restore the registers from the KEXCEPTION_FRAME
+    RESTORE_EXCEPTION_STATE
+
+    ret
+
+ENDFUNC
+
+
+END
diff --git a/modules/rostests/apitests/rtl/amd64/RtlCaptureContext.c b/modules/rostests/apitests/rtl/amd64/RtlCaptureContext.c
new file mode 100644 (file)
index 0000000..8091742
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * PROJECT:     ReactOS api tests
+ * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE:     Test for x64 RtlCaptureContext
+ * COPYRIGHT:   Copyright 2022 Timo Kreuzer <timo.kreuzer@reactos.org>
+ */
+
+#include <rtltests.h>
+
+VOID
+RtlCaptureContextWrapper(
+    _Inout_ PCONTEXT InOutContext,
+    _Out_ PCONTEXT CapturedContext);
+
+START_TEST(RtlCaptureContext)
+{
+    CONTEXT OriginalContext, InOutContext, CapturedContext;
+    SIZE_T Index, MaxIndex;
+    PUSHORT Buffer;
+
+    /* Initialize a pattern */
+    MaxIndex = sizeof(OriginalContext) / sizeof(USHORT);
+    Buffer = (PUSHORT)&OriginalContext;
+    for (Index = 0; Index < MaxIndex; Index++)
+    {
+        Buffer[Index] = Index;
+    }
+
+    /* Set all valid bits in EFlags */
+    OriginalContext.EFlags = 0xE7F;
+
+    /* Set up a valid floating point state */
+    OriginalContext.FltSave.ControlWord = 0x27f;
+    OriginalContext.FltSave.StatusWord = 0x1234;
+    OriginalContext.FltSave.TagWord = 0xab;
+    OriginalContext.FltSave.Reserved1 = 0x75;
+    OriginalContext.FltSave.MxCsr = 0x1f80; // Valid Mask is 2FFFF
+    OriginalContext.FltSave.MxCsr_Mask = 0xabcde;
+
+    /* Set up a unique MxCsr. This one will overwrite FltSave.MxCsr */
+    OriginalContext.MxCsr = 0xffff; // Valid Mask is 0xFFFF
+
+    /* Copy the original buffer */
+    InOutContext = OriginalContext;
+
+    /* Fill the output buffer with bogus */
+    RtlFillMemory(&CapturedContext, sizeof(CapturedContext), 0xCC);
+
+    /* Call the wrapper function */
+    RtlCaptureContextWrapper(&InOutContext, &CapturedContext);
+
+    /* These fields are not changed */
+    ok_eq_hex64(CapturedContext.P1Home, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.P2Home, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.P3Home, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.P4Home, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.P5Home, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.P6Home, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.Dr0, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.Dr1, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.Dr2, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.Dr3, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.Dr6, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.Dr7, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.VectorControl, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.DebugControl, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.LastBranchToRip, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.LastBranchFromRip, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.LastExceptionToRip, 0xccccccccccccccccULL);
+    ok_eq_hex64(CapturedContext.LastExceptionFromRip, 0xccccccccccccccccULL);
+    for (Index = 0; Index < ARRAYSIZE(CapturedContext.FltSave.Reserved4); Index++)
+    {
+        ok_eq_hex64(CapturedContext.FltSave.Reserved4[Index], 0xcc);
+    }
+    for (Index = 0; Index < ARRAYSIZE(CapturedContext.VectorRegister); Index++)
+    {
+        ok_eq_hex64(CapturedContext.VectorRegister[Index].Low, 0xccccccccccccccccULL);
+        ok_eq_hex64(CapturedContext.VectorRegister[Index].High, 0xccccccccccccccccULL);
+    }
+
+    /* ContextFlags is set */
+    ok_eq_hex(CapturedContext.ContextFlags, CONTEXT_FULL | CONTEXT_SEGMENTS);
+
+    /* These vallues are as passed in */
+    ok_eq_hex(CapturedContext.MxCsr, OriginalContext.MxCsr);
+    ok_eq_hex64(CapturedContext.Rax, OriginalContext.Rax);
+    ok_eq_hex64(CapturedContext.Rcx, (ULONG64)&CapturedContext);
+    ok_eq_hex64(CapturedContext.Rdx, OriginalContext.Rdx);
+    ok_eq_hex64(CapturedContext.Rbx, OriginalContext.Rbx);
+    ok_eq_hex64(CapturedContext.Rbp, OriginalContext.Rbp);
+    ok_eq_hex64(CapturedContext.Rsi, OriginalContext.Rsi);
+    ok_eq_hex64(CapturedContext.Rdi, OriginalContext.Rdi);
+    ok_eq_hex64(CapturedContext.R8, OriginalContext.R8);
+    ok_eq_hex64(CapturedContext.R9, OriginalContext.R9);
+    ok_eq_hex64(CapturedContext.R10, OriginalContext.R10);
+    ok_eq_hex64(CapturedContext.R11, OriginalContext.R11);
+    ok_eq_hex64(CapturedContext.R12, OriginalContext.R12);
+    ok_eq_hex64(CapturedContext.R13, OriginalContext.R13);
+    ok_eq_hex64(CapturedContext.R14, OriginalContext.R14);
+    ok_eq_hex64(CapturedContext.R15, OriginalContext.R15);
+
+    ok_eq_xmm(CapturedContext.Xmm0, OriginalContext.Xmm0);
+    ok_eq_xmm(CapturedContext.Xmm1, OriginalContext.Xmm1);
+    ok_eq_xmm(CapturedContext.Xmm2, OriginalContext.Xmm2);
+    ok_eq_xmm(CapturedContext.Xmm3, OriginalContext.Xmm3);
+    ok_eq_xmm(CapturedContext.Xmm4, OriginalContext.Xmm4);
+    ok_eq_xmm(CapturedContext.Xmm5, OriginalContext.Xmm5);
+    ok_eq_xmm(CapturedContext.Xmm6, OriginalContext.Xmm6);
+    ok_eq_xmm(CapturedContext.Xmm7, OriginalContext.Xmm7);
+    ok_eq_xmm(CapturedContext.Xmm8, OriginalContext.Xmm8);
+    ok_eq_xmm(CapturedContext.Xmm9, OriginalContext.Xmm9);
+    ok_eq_xmm(CapturedContext.Xmm10, OriginalContext.Xmm10);
+    ok_eq_xmm(CapturedContext.Xmm11, OriginalContext.Xmm11);
+    ok_eq_xmm(CapturedContext.Xmm12, OriginalContext.Xmm12);
+    ok_eq_xmm(CapturedContext.Xmm13, OriginalContext.Xmm13);
+    ok_eq_xmm(CapturedContext.Xmm14, OriginalContext.Xmm14);
+    ok_eq_xmm(CapturedContext.Xmm15, OriginalContext.Xmm15);
+
+    /* Some EFlags fields are cleared */
+    ok_eq_hex64(CapturedContext.EFlags, OriginalContext.EFlags & ~0x28);
+
+#ifndef KMT_KERNEL_MODE // User mode
+    ok_eq_hex(CapturedContext.FltSave.ControlWord, 0x27f);
+    ok_eq_hex(CapturedContext.FltSave.StatusWord, OriginalContext.FltSave.StatusWord);
+    ok_eq_hex(CapturedContext.FltSave.TagWord, OriginalContext.FltSave.TagWord);
+    ok_eq_hex(CapturedContext.FltSave.Reserved1, 0x00);
+    ok_eq_hex(CapturedContext.FltSave.MxCsr_Mask, 0xffff);
+    ok_eq_hex(CapturedContext.FltSave.ErrorOpcode, 0x00000083);
+    ok_eq_hex(CapturedContext.FltSave.ErrorOffset, 0x00850084);
+    ok_eq_hex(CapturedContext.FltSave.ErrorSelector, 0x0086);
+    ok_eq_hex(CapturedContext.FltSave.Reserved2, 0x0000);
+    ok_eq_hex(CapturedContext.FltSave.DataOffset, 0x00890088);
+    ok_eq_hex(CapturedContext.FltSave.DataSelector, 0x008a);
+    ok_eq_hex(CapturedContext.FltSave.Reserved3, 0x0000);
+#else
+    ok_eq_hex(CapturedContext.FltSave.ControlWord, 0xcccc);
+    ok_eq_hex(CapturedContext.FltSave.StatusWord, 0xcccc);
+    ok_eq_hex(CapturedContext.FltSave.TagWord, 0xcc);
+    ok_eq_hex(CapturedContext.FltSave.Reserved1, 0xcc);
+    ok_eq_hex(CapturedContext.FltSave.MxCsr_Mask, 0xcccccccc);
+    ok_eq_hex(CapturedContext.FltSave.ErrorOpcode, 0xcccc);
+    ok_eq_hex(CapturedContext.FltSave.ErrorOffset, 0xcccccccc);
+    ok_eq_hex(CapturedContext.FltSave.ErrorSelector, 0xcccc);
+    ok_eq_hex(CapturedContext.FltSave.Reserved2, 0xcccc);
+    ok_eq_hex(CapturedContext.FltSave.DataOffset, 0xcccccccc);
+    ok_eq_hex(CapturedContext.FltSave.DataSelector, 0xcccc);
+    ok_eq_hex(CapturedContext.FltSave.Reserved3, 0xcccc);
+#endif
+
+    /* We get the value from OriginalContext.MxCsr, since we set that later in the wrapper */
+    ok_eq_hex(CapturedContext.FltSave.MxCsr, OriginalContext.MxCsr);
+
+    /* Legacy floating point registers are truncated to 10 bytes */
+    ok_eq_hex64(CapturedContext.Legacy[0].Low, OriginalContext.Legacy[0].Low);
+    ok_eq_hex64(CapturedContext.Legacy[0].High, OriginalContext.Legacy[0].High & 0xFF);
+    ok_eq_hex64(CapturedContext.Legacy[1].Low, OriginalContext.Legacy[1].Low);
+    ok_eq_hex64(CapturedContext.Legacy[1].High, OriginalContext.Legacy[1].High & 0xFF);
+    ok_eq_hex64(CapturedContext.Legacy[2].Low, OriginalContext.Legacy[2].Low);
+    ok_eq_hex64(CapturedContext.Legacy[2].High, OriginalContext.Legacy[2].High & 0xFF);
+    ok_eq_hex64(CapturedContext.Legacy[3].Low, OriginalContext.Legacy[3].Low);
+    ok_eq_hex64(CapturedContext.Legacy[3].High, OriginalContext.Legacy[3].High & 0xFF);
+    ok_eq_hex64(CapturedContext.Legacy[4].Low, OriginalContext.Legacy[4].Low);
+    ok_eq_hex64(CapturedContext.Legacy[4].High, OriginalContext.Legacy[4].High & 0xFF);
+    ok_eq_hex64(CapturedContext.Legacy[5].Low, OriginalContext.Legacy[5].Low);
+    ok_eq_hex64(CapturedContext.Legacy[5].High, OriginalContext.Legacy[5].High & 0xFF);
+    ok_eq_hex64(CapturedContext.Legacy[6].Low, OriginalContext.Legacy[6].Low);
+    ok_eq_hex64(CapturedContext.Legacy[6].High, OriginalContext.Legacy[6].High & 0xFF);
+    ok_eq_hex64(CapturedContext.Legacy[7].Low, OriginalContext.Legacy[7].Low);
+    ok_eq_hex64(CapturedContext.Legacy[7].High, OriginalContext.Legacy[7].High & 0xFF);
+
+    /* We don't pass in segments, but expect the default values */
+    ok_eq_hex(CapturedContext.SegCs, 0x33);
+    ok_eq_hex(CapturedContext.SegDs, 0x2b);
+    ok_eq_hex(CapturedContext.SegEs, 0x2b);
+    ok_eq_hex(CapturedContext.SegFs, 0x53);
+    ok_eq_hex(CapturedContext.SegGs, 0x2b);
+    ok_eq_hex(CapturedContext.SegSs, 0x2b);
+
+    /* For Rsp and Rip we get the expected value back from the asm wrapper */
+    ok_eq_hex64(CapturedContext.Rsp, InOutContext.Rsp);
+    ok_eq_hex64(CapturedContext.Rip, InOutContext.Rip);
+
+    /* Check that these registers are not modified by RtlCaptureContext */
+    ok_eq_xmm(InOutContext.Xmm0, OriginalContext.Xmm0);
+    ok_eq_xmm(InOutContext.Xmm1, OriginalContext.Xmm1);
+    ok_eq_xmm(InOutContext.Xmm2, OriginalContext.Xmm2);
+    ok_eq_xmm(InOutContext.Xmm3, OriginalContext.Xmm3);
+    ok_eq_xmm(InOutContext.Xmm4, OriginalContext.Xmm4);
+    ok_eq_xmm(InOutContext.Xmm5, OriginalContext.Xmm5);
+    ok_eq_xmm(InOutContext.Xmm6, OriginalContext.Xmm6);
+    ok_eq_xmm(InOutContext.Xmm7, OriginalContext.Xmm7);
+    ok_eq_xmm(InOutContext.Xmm8, OriginalContext.Xmm8);
+    ok_eq_xmm(InOutContext.Xmm9, OriginalContext.Xmm9);
+    ok_eq_xmm(InOutContext.Xmm10, OriginalContext.Xmm10);
+    ok_eq_xmm(InOutContext.Xmm11, OriginalContext.Xmm11);
+    ok_eq_xmm(InOutContext.Xmm12, OriginalContext.Xmm12);
+    ok_eq_xmm(InOutContext.Xmm13, OriginalContext.Xmm13);
+    ok_eq_xmm(InOutContext.Xmm14, OriginalContext.Xmm14);
+    ok_eq_xmm(InOutContext.Xmm15, OriginalContext.Xmm15);
+    ok_eq_hex64(InOutContext.Rdx, OriginalContext.Rdx);
+    ok_eq_hex64(InOutContext.Rbx, OriginalContext.Rbx);
+    ok_eq_hex64(InOutContext.Rbp, OriginalContext.Rbp);
+    ok_eq_hex64(InOutContext.Rsi, OriginalContext.Rsi);
+    ok_eq_hex64(InOutContext.Rdi, OriginalContext.Rdi);
+    ok_eq_hex64(InOutContext.R8, OriginalContext.R8);
+    ok_eq_hex64(InOutContext.R9, OriginalContext.R9);
+    ok_eq_hex64(InOutContext.R10, OriginalContext.R10);
+    ok_eq_hex64(InOutContext.R11, OriginalContext.R11);
+    ok_eq_hex64(InOutContext.R12, OriginalContext.R12);
+    ok_eq_hex64(InOutContext.R13, OriginalContext.R13);
+    ok_eq_hex64(InOutContext.R14, OriginalContext.R14);
+    ok_eq_hex64(InOutContext.R15, OriginalContext.R15);
+
+    /* Eflags is changed (parity is flaky) */
+    ok_eq_hex64(InOutContext.EFlags & ~0x04, OriginalContext.EFlags & 0x782);
+
+    /* MxCsr is the one we passed in in OriginalContext.MxCsr */
+    ok_eq_hex(InOutContext.MxCsr, OriginalContext.MxCsr);
+    ok_eq_hex(InOutContext.FltSave.MxCsr, OriginalContext.MxCsr);
+
+    /* Rcx still points to the captured context */
+    ok_eq_hex64(InOutContext.Rcx, (ULONG64)&CapturedContext);
+
+    /* Rax contains eflags */
+    ok_eq_hex64(InOutContext.Rax, CapturedContext.EFlags);
+
+    /* Second run with minimal EFLags/MxCsr */
+    OriginalContext.EFlags = 0x200;
+    OriginalContext.MxCsr = 0x0000;
+    InOutContext = OriginalContext;
+    RtlFillMemory(&CapturedContext, sizeof(CapturedContext), 0xCC);
+    RtlCaptureContextWrapper(&InOutContext, &CapturedContext);
+
+    /* Captured Eflags has reserved flag set (which is always 1) */
+    ok_eq_hex64(CapturedContext.EFlags, OriginalContext.EFlags | 2);
+
+    /* Parity flag is flaky */
+    ok_eq_hex64(InOutContext.EFlags & ~4, CapturedContext.EFlags);
+
+    /* MxCsr is captured/returned as passed in */
+    ok_eq_hex64(InOutContext.MxCsr, CapturedContext.MxCsr);
+    ok_eq_hex64(CapturedContext.MxCsr, OriginalContext.MxCsr);
+}
diff --git a/modules/rostests/apitests/rtl/i386/RtlCaptureContext.c b/modules/rostests/apitests/rtl/i386/RtlCaptureContext.c
new file mode 100644 (file)
index 0000000..eccf129
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * PROJECT:     ReactOS api tests
+ * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE:     Test for x86 RtlCaptureContext
+ * COPYRIGHT:   Copyright 2022 Timo Kreuzer <timo.kreuzer@reactos.org>
+ */
+
+#include <rtltests.h>
+
+VOID
+RtlCaptureContextWrapper(
+    _Inout_ PCONTEXT InOutContext,
+    _Out_ PCONTEXT CapturedContext);
+
+START_TEST(RtlCaptureContext)
+{
+    // TODO
+}
diff --git a/modules/rostests/apitests/rtl/ldrstubs.c b/modules/rostests/apitests/rtl/ldrstubs.c
new file mode 100644 (file)
index 0000000..c04ac08
--- /dev/null
@@ -0,0 +1,17 @@
+#include <rtltests.h>
+#include <pseh/pseh2.h>
+#include <compat_undoc.h>
+#include <compatguid_undoc.h>
+
+#define NDEBUG
+#include <debug.h>
+
+BOOLEAN LdrpShutdownInProgress;
+HANDLE LdrpShutdownThreadId;
+
+VOID
+NTAPI
+LdrpInitializeProcessCompat(PVOID pProcessActctx, PVOID* pOldShimData)
+{
+}
+
diff --git a/modules/rostests/apitests/rtl/rtltests.h b/modules/rostests/apitests/rtl/rtltests.h
new file mode 100644 (file)
index 0000000..f6bf7c0
--- /dev/null
@@ -0,0 +1,12 @@
+
+#pragma once
+
+#include <stdio.h>
+
+#define WIN32_NO_STATUS
+#define _INC_WINDOWS
+#define COM_NO_WINDOWS_H
+
+#include <apitest.h>
+#include <ndk/ntndk.h>
+#include <strsafe.h>
diff --git a/modules/rostests/apitests/rtl/testlist.c b/modules/rostests/apitests/rtl/testlist.c
new file mode 100644 (file)
index 0000000..ca7ad4c
--- /dev/null
@@ -0,0 +1,15 @@
+#define __ROS_LONG64__
+
+#define STANDALONE
+#include <apitest.h>
+
+extern void func_RtlCaptureContext(void);
+
+const struct test winetest_testlist[] =
+{
+#ifdef _M_AMD64
+    { "RtlCaptureContext",        func_RtlCaptureContext },
+#endif
+
+    { 0, 0 }
+};
index da4d328..4dd5572 100644 (file)
@@ -26,6 +26,16 @@ list(APPEND COMMON_SOURCE
     rtl/RtlStrSafe.c
     rtl/RtlUnicodeString.c)
 
+if(ARCH STREQUAL "i386")
+
+elseif(ARCH STREQUAL "amd64")
+    add_asm_files(kmtest_common_asm ../apitests/rtl/amd64/RtlCaptureContext-asm.s)
+    list(APPEND COMMON_SOURCE
+        ../apitests/rtl/amd64/RtlCaptureContext.c
+        ${kmtest_common_asm}
+    )
+endif()
+
 #
 # kmtest_drv.sys driver
 #
diff --git a/modules/rostests/kmtests/include/rtltests.h b/modules/rostests/kmtests/include/rtltests.h
new file mode 100644 (file)
index 0000000..ec53f93
--- /dev/null
@@ -0,0 +1,5 @@
+
+#pragma once
+
+#define KMT_EMULATE_KERNEL
+#include <kmt_test.h>
index 5461e2f..9f9197e 100644 (file)
@@ -25,6 +25,7 @@ KMT_TESTFUNC Test_MmMapLockedPagesSpecifyCache;
 KMT_TESTFUNC Test_NtCreateSection;
 KMT_TESTFUNC Test_PoIrp;
 KMT_TESTFUNC Test_RtlAvlTree;
+KMT_TESTFUNC Test_RtlCaptureContext;
 KMT_TESTFUNC Test_RtlException;
 KMT_TESTFUNC Test_RtlIntSafe;
 KMT_TESTFUNC Test_RtlMemory;
@@ -69,5 +70,8 @@ const KMT_TEST TestList[] =
     { "RtlUnicodeString",             Test_RtlUnicodeString },
     { "TcpIpTdi",                     Test_TcpIpTdi },
     { "TcpIpConnect",                 Test_TcpIpConnect },
+#ifdef _M_AMD64
+    { "RtlCaptureContext",            Test_RtlCaptureContext },
+#endif
     { NULL,                           NULL },
 };
index 73daeba..1bc9fd0 100644 (file)
@@ -71,6 +71,7 @@ KMT_TESTFUNC Test_SeLogonSession;
 KMT_TESTFUNC Test_SeQueryInfoToken;
 KMT_TESTFUNC Test_SeTokenFiltering;
 KMT_TESTFUNC Test_RtlAvlTree;
+KMT_TESTFUNC Test_RtlCaptureContext;
 KMT_TESTFUNC Test_RtlException;
 KMT_TESTFUNC Test_RtlIntSafe;
 KMT_TESTFUNC Test_RtlIsValidOemCharacter;
@@ -167,5 +168,8 @@ const KMT_TEST TestList[] =
     { "ZwCreateSection",                    Test_ZwCreateSection },
     { "ZwMapViewOfSection",                 Test_ZwMapViewOfSection },
     { "ZwWaitForMultipleObjects",           Test_ZwWaitForMultipleObjects},
+#ifdef _M_AMD64
+    { "RtlCaptureContext",                  Test_RtlCaptureContext },
+#endif
     { NULL,                                 NULL }
 };