[PATCH 2/3] kernel32: Implement InitializeContext[2]().

Paul Gofman pgofman at codeweavers.com
Mon Aug 24 06:26:45 CDT 2020


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 .../api-ms-win-core-xstate-l1-1-0.spec        |   2 +-
 .../api-ms-win-core-xstate-l2-1-0.spec        |   2 +-
 dlls/kernel32/kernel32.spec                   |   3 +-
 dlls/kernelbase/kernelbase.spec               |   3 +-
 dlls/kernelbase/thread.c                      |  47 +++
 dlls/ntdll/exception.c                        |  73 +++-
 dlls/ntdll/ntdll.spec                         |   2 +
 dlls/ntdll/tests/exception.c                  | 349 ++++++++++++++++--
 dlls/ntoskrnl.exe/ntoskrnl.exe.spec           |   2 +
 include/ddk/wdm.h                             |   2 +
 include/winbase.h                             |   2 +
 11 files changed, 445 insertions(+), 42 deletions(-)

diff --git a/dlls/api-ms-win-core-xstate-l1-1-0/api-ms-win-core-xstate-l1-1-0.spec b/dlls/api-ms-win-core-xstate-l1-1-0/api-ms-win-core-xstate-l1-1-0.spec
index 15126e5ddfe..2c72d52e452 100644
--- a/dlls/api-ms-win-core-xstate-l1-1-0/api-ms-win-core-xstate-l1-1-0.spec
+++ b/dlls/api-ms-win-core-xstate-l1-1-0/api-ms-win-core-xstate-l1-1-0.spec
@@ -2,7 +2,7 @@
 @ stdcall RtlGetEnabledExtendedFeatures(int64) ntdll.RtlGetEnabledExtendedFeatures
 @ stdcall RtlGetExtendedContextLength(long ptr) ntdll.RtlGetExtendedContextLength
 @ stub RtlGetExtendedFeaturesMask
-@ stub RtlInitializeExtendedContext
+@ stdcall RtlInitializeExtendedContext(ptr long ptr) ntdll.RtlInitializeExtendedContext
 @ stub RtlLocateExtendedFeature
 @ stub RtlLocateLegacyContext
 @ stub RtlSetExtendedFeaturesMask
diff --git a/dlls/api-ms-win-core-xstate-l2-1-0/api-ms-win-core-xstate-l2-1-0.spec b/dlls/api-ms-win-core-xstate-l2-1-0/api-ms-win-core-xstate-l2-1-0.spec
index f0510978198..2f79395bd28 100644
--- a/dlls/api-ms-win-core-xstate-l2-1-0/api-ms-win-core-xstate-l2-1-0.spec
+++ b/dlls/api-ms-win-core-xstate-l2-1-0/api-ms-win-core-xstate-l2-1-0.spec
@@ -1,6 +1,6 @@
 @ stub CopyContext
 @ stdcall -ret64 -arch=i386,x86_64 GetEnabledXStateFeatures() kernel32.GetEnabledXStateFeatures
 @ stub GetXStateFeaturesMask
-@ stub InitializeContext
+@ stdcall InitializeContext(ptr long ptr ptr) kernel32.InitializeContext
 @ stub LocateXStateFeature
 @ stub SetXStateFeaturesMask
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
index 5671ae7e93d..bf51bcc8491 100644
--- a/dlls/kernel32/kernel32.spec
+++ b/dlls/kernel32/kernel32.spec
@@ -950,7 +950,8 @@
 @ stdcall InitOnceExecuteOnce(ptr ptr ptr ptr) kernelbase.InitOnceExecuteOnce
 @ stdcall InitOnceInitialize(ptr) ntdll.RtlRunOnceInitialize
 @ stdcall InitializeConditionVariable(ptr) ntdll.RtlInitializeConditionVariable
-# @ stub InitializeContext
+@ stdcall -import InitializeContext(ptr long ptr ptr)
+@ stdcall -import InitializeContext2(ptr long ptr ptr int64)
 @ stdcall InitializeCriticalSection(ptr) ntdll.RtlInitializeCriticalSection
 @ stdcall -import InitializeCriticalSectionAndSpinCount(ptr long)
 @ stdcall -import InitializeCriticalSectionEx(ptr long long)
diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec
index e8989a0639d..3b4d20eea96 100644
--- a/dlls/kernelbase/kernelbase.spec
+++ b/dlls/kernelbase/kernelbase.spec
@@ -801,7 +801,8 @@
 @ stdcall InitOnceInitialize(ptr) ntdll.RtlRunOnceInitialize
 @ stdcall InitializeAcl(ptr long long)
 @ stdcall InitializeConditionVariable(ptr) ntdll.RtlInitializeConditionVariable
-# @ stub InitializeContext
+@ stdcall InitializeContext(ptr long ptr ptr)
+@ stdcall InitializeContext2(ptr long ptr ptr int64)
 @ stdcall InitializeCriticalSection(ptr) ntdll.RtlInitializeCriticalSection
 @ stdcall InitializeCriticalSectionAndSpinCount(ptr long)
 @ stdcall InitializeCriticalSectionEx(ptr long long)
diff --git a/dlls/kernelbase/thread.c b/dlls/kernelbase/thread.c
index c688b81e460..16954d4a385 100644
--- a/dlls/kernelbase/thread.c
+++ b/dlls/kernelbase/thread.c
@@ -29,6 +29,7 @@
 #include "winbase.h"
 #include "winnls.h"
 #include "winternl.h"
+#include "ddk/wdm.h"
 
 #include "kernelbase.h"
 #include "wine/exception.h"
@@ -1362,3 +1363,49 @@ BOOL WINAPI DECLSPEC_HOTPATCH QueryThreadpoolStackInformation( PTP_POOL pool, PT
 {
     return set_ntstatus( TpQueryPoolStackInformation( pool, stack_info ));
 }
+
+
+/***********************************************************************
+ * Context functions
+
+ ***********************************************************************/
+
+
+/***********************************************************************
+ *           InitializeContext2                (kernelbase.@)
+ */
+BOOL WINAPI InitializeContext2( void *buffer, DWORD context_flags, CONTEXT **context, DWORD *length,
+        ULONG64 compaction_mask )
+{
+    ULONG orig_length;
+    NTSTATUS status;
+
+    TRACE( "buffer %p, context_flags %#x, context %p, ret_length %p, compaction_mask %s.\n",
+            buffer, context_flags, context, length, wine_dbgstr_longlong(compaction_mask) );
+
+    orig_length = *length;
+
+    if ((status = RtlGetExtendedContextLength2( context_flags, length, compaction_mask )))
+        return set_ntstatus( status );
+
+    if (!buffer || orig_length < *length)
+    {
+        SetLastError( ERROR_INSUFFICIENT_BUFFER );
+        return FALSE;
+    }
+
+    if ((status = RtlInitializeExtendedContext2( buffer, context_flags, (CONTEXT_EX **)context, compaction_mask )))
+        return set_ntstatus( status );
+
+    *context = (CONTEXT *)((BYTE *)*context + (*(CONTEXT_EX **)context)->Legacy.Offset);
+
+    return TRUE;
+}
+
+/***********************************************************************
+ *           InitializeContext                 (kernelbase.@)
+ */
+BOOL WINAPI InitializeContext( void *buffer, DWORD context_flags, CONTEXT **context, DWORD *length )
+{
+    return InitializeContext2( buffer, context_flags, context, length, ~(ULONG64)0 );
+}
diff --git a/dlls/ntdll/exception.c b/dlls/ntdll/exception.c
index 1b212cdee7f..f8b1205471b 100644
--- a/dlls/ntdll/exception.c
+++ b/dlls/ntdll/exception.c
@@ -672,12 +672,14 @@ static const struct context_parameters
     ULONG supported_flags;
     ULONG context_size;    /* sizeof(CONTEXT) */
     ULONG context_ex_size; /* sizeof(CONTEXT_EX) */
-    ULONG alignment;
+    ULONG alignment;       /* Used when computing size of context. */
+    ULONG true_alignment;  /* Used for actual alignment. */
+    ULONG flags_offset;
 }
 arch_context_paramaters[] =
 {
-    {0x00100000, 0xd810005f, 0x4d0, 0x20, 7},
-    {0x00010000, 0xd801007f, 0x2cc, 0x18, 3},
+    {0x00100000, 0xd810005f, 0x4d0, 0x20, 7, 0xf, 0x30},
+    {0x00010000, 0xd801007f, 0x2cc, 0x18, 3, 0x3,    0},
 };
 
 static const struct context_parameters *context_get_parameters( ULONG context_flags )
@@ -736,3 +738,68 @@ ULONG64 WINAPI RtlGetExtendedContextLength( ULONG context_flags, ULONG *length )
 {
     return RtlGetExtendedContextLength2( context_flags, length, ~(ULONG64)0 );
 }
+
+
+/**********************************************************************
+ *              RtlInitializeExtendedContext2    (NTDLL.@)
+ */
+ULONG WINAPI RtlInitializeExtendedContext2( void *context, ULONG context_flags, CONTEXT_EX **context_ex,
+        ULONG64 compaction_mask )
+{
+    const struct context_parameters *p;
+    ULONG64 supported_mask = 0;
+    CONTEXT_EX *c_ex;
+
+    TRACE( "context %p, context_flags %#x, context_ex %p, compaction_mask %s.\n",
+            context, context_flags, context_ex, wine_dbgstr_longlong(compaction_mask));
+
+    if (!(p = context_get_parameters( context_flags )))
+        return STATUS_INVALID_PARAMETER;
+
+    if ((context_flags & 0x40) && !(supported_mask = RtlGetEnabledExtendedFeatures( ~(ULONG64)0 )))
+        return STATUS_NOT_SUPPORTED;
+
+    context = (void *)(((ULONG_PTR)context + p->true_alignment) & ~p->true_alignment);
+    *(ULONG *)((BYTE *)context + p->flags_offset) = context_flags;
+
+    *context_ex = c_ex = (CONTEXT_EX *)((BYTE *)context + p->context_size);
+    c_ex->Legacy.Offset = c_ex->All.Offset = -(LONG)p->context_size;
+    c_ex->Legacy.Length = p->context_size;
+
+    if (context_flags & 0x40)
+    {
+        XSTATE *xs;
+
+        compaction_mask &= supported_mask;
+
+        xs = (XSTATE *)(((ULONG_PTR)c_ex + p->context_ex_size + 63) & ~(ULONG_PTR)63);
+        c_ex->XState.Offset = (ULONG_PTR)xs - (ULONG_PTR)c_ex;
+        c_ex->XState.Length = offsetof(XSTATE, YmmContext);
+        memset( xs, 0, c_ex->XState.Length );
+        compaction_mask &= supported_mask;
+        if (user_shared_data->XState.CompactionEnabled)
+            xs->CompactionMask = ((ULONG64)1 << 63) | compaction_mask;
+
+        if (compaction_mask & (1 << XSTATE_AVX))
+            c_ex->XState.Length += sizeof(YMMCONTEXT);
+
+        c_ex->All.Length = p->context_size + c_ex->XState.Offset + c_ex->XState.Length;
+    }
+    else
+    {
+        c_ex->XState.Offset = 25; /* According to the tests, it is just 25 if CONTEXT_XSTATE is not specified. */
+        c_ex->XState.Length = 0;
+        c_ex->All.Length = p->context_size + 24; /* sizeof(CONTEXT_EX) minus 8 alignment bytes on x64. */
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+/**********************************************************************
+ *              RtlInitializeExtendedContext    (NTDLL.@)
+ */
+ULONG WINAPI RtlInitializeExtendedContext( void *context, ULONG context_flags, CONTEXT_EX **context_ex )
+{
+    return RtlInitializeExtendedContext2( context, context_flags, context_ex, ~(ULONG64)0 );
+}
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index 08c6e1849a2..2a33368d7f8 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -757,6 +757,8 @@
 @ stdcall RtlInitializeCriticalSection(ptr)
 @ stdcall RtlInitializeCriticalSectionAndSpinCount(ptr long)
 @ stdcall RtlInitializeCriticalSectionEx(ptr long long)
+@ stdcall RtlInitializeExtendedContext(ptr long ptr)
+@ stdcall RtlInitializeExtendedContext2(ptr long ptr int64)
 @ stdcall RtlInitializeGenericTable(ptr ptr ptr ptr ptr)
 @ stdcall RtlInitializeGenericTableAvl(ptr ptr ptr ptr ptr)
 @ stdcall RtlInitializeHandleTable(long long ptr)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index 0e00e889d41..8ee6561ab0b 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -47,6 +47,9 @@ static void      (WINAPI *pRtlSetUnhandledExceptionFilter)(PRTL_EXCEPTION_FILTER
 static ULONG64   (WINAPI *pRtlGetEnabledExtendedFeatures)(ULONG64);
 static ULONG64   (WINAPI *pRtlGetExtendedContextLength)(ULONG context_flags, ULONG *length);
 static ULONG64   (WINAPI *pRtlGetExtendedContextLength2)(ULONG context_flags, ULONG *length, ULONG64 compaction_mask);
+static ULONG     (WINAPI *pRtlInitializeExtendedContext)(void *context, ULONG context_flags, CONTEXT_EX **context_ex);
+static ULONG     (WINAPI *pRtlInitializeExtendedContext2)(void *context, ULONG context_flags, CONTEXT_EX **context_ex,
+        ULONG64 compaction_mask);
 static NTSTATUS  (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*);
 static NTSTATUS  (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code);
 static NTSTATUS  (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
@@ -56,6 +59,10 @@ static BOOL      (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
 static NTSTATUS  (WINAPI *pNtClose)(HANDLE);
 static NTSTATUS  (WINAPI *pNtSuspendProcess)(HANDLE process);
 static NTSTATUS  (WINAPI *pNtResumeProcess)(HANDLE process);
+static BOOL      (WINAPI *pInitializeContext)(void *buffer, DWORD context_flags, CONTEXT **context,
+        DWORD *length);
+static BOOL      (WINAPI *pInitializeContext2)(void *buffer, DWORD context_flags, CONTEXT **context,
+        DWORD *length, ULONG64 compaction_mask);
 
 #define RTL_UNLOAD_EVENT_TRACE_NUMBER 64
 
@@ -5469,6 +5476,7 @@ static const unsigned test_extended_context_spoil_data1[8] = {0x10, 0x20, 0x30,
 static const unsigned test_extended_context_spoil_data2[8] = {0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85};
 
 static BOOL test_extended_context_modified_state;
+static BOOL compaction_enabled;
 
 static DWORD test_extended_context_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
         CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
@@ -5477,14 +5485,8 @@ static DWORD test_extended_context_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGI
     CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1);
     unsigned int *context_ymm_data;
     DWORD expected_min_offset;
-    BOOL compaction;
-    int regs[4];
     XSTATE *xs;
 
-    /* Since we got xstates enabled by OS this cpuid level should be supported. */
-    __cpuidex(regs, 0xd, 1);
-    compaction = regs[0] & 2;
-
     ok((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) == (CONTEXT_FULL | CONTEXT_XSTATE),
             "Got unexpected ContextFlags %#x.\n", context->ContextFlags);
 
@@ -5515,9 +5517,9 @@ static DWORD test_extended_context_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGI
     context_ymm_data = (unsigned int *)&xs->YmmContext;
     ok(!((ULONG_PTR)xs % 64), "Got unexpected xs %p.\n", xs);
 
-    ok((compaction && (xs->CompactionMask & (expected_compaction_mask | 3)) == expected_compaction_mask)
-            || (!compaction && !xs->CompactionMask), "Got unexpected CompactionMask %s, compaction %#x.\n",
-            wine_dbgstr_longlong(xs->CompactionMask), compaction);
+    ok((compaction_enabled && (xs->CompactionMask & (expected_compaction_mask | 3)) == expected_compaction_mask)
+            || (!compaction_enabled && !xs->CompactionMask), "Got unexpected CompactionMask %s, compaction %#x.\n",
+            wine_dbgstr_longlong(xs->CompactionMask), compaction_enabled);
 
     if (test_extended_context_modified_state)
     {
@@ -5592,6 +5594,7 @@ static void test_extended_context(void)
         ULONG context_length;
         ULONG context_ex_length;
         ULONG align;
+        ULONG flags_offset;
     }
     context_arch[] =
     {
@@ -5602,6 +5605,7 @@ static void test_extended_context(void)
             0x4d0,       /* sizeof(CONTEXT) */
             0x20,        /* sizeof(CONTEXT_EX) */
             7,
+            0x30,
         },
         {
             0x00010000,  /* CONTEXT_X86  */
@@ -5610,14 +5614,21 @@ static void test_extended_context(void)
             0x2cc,       /* sizeof(CONTEXT) */
             0x18,        /* sizeof(CONTEXT_EX) */
             3,
+            0,
         },
     };
-    ULONG expected_length, expected_length_xstate;
-    unsigned int i, address_offset, test;
+    ULONG expected_length, expected_length_xstate, context_flags, expected_offset;
+    DECLSPEC_ALIGN(64) BYTE context_buffer2[2048];
+    DECLSPEC_ALIGN(64) BYTE context_buffer[2048];
+    unsigned int i, j, address_offset, test;
+    ULONG ret, ret2, length, length2, align;
     ULONG64 enabled_features;
-    ULONG ret, length;
+    CONTEXT_EX *context_ex;
+    CONTEXT *context;
     unsigned data[8];
     ULONG flags;
+    XSTATE *xs;
+    BOOL bret;
 
     address_offset = sizeof(void *) == 8 ? 2 : 1;
     *(void **)(except_code_set_ymm0 + address_offset) = data;
@@ -5631,6 +5642,14 @@ static void test_extended_context(void)
 
     enabled_features = pRtlGetEnabledExtendedFeatures(~(ULONG64)0);
 
+    if (enabled_features)
+    {
+        int regs[4];
+
+        __cpuidex(regs, 0xd, 1);
+        compaction_enabled = regs[0] & 2;
+    }
+
     /* Test context manipulation functions. */
     length = 0xdeadbeef;
     ret = pRtlGetExtendedContextLength(0, &length);
@@ -5656,7 +5675,7 @@ static void test_extended_context(void)
                 continue;
 
             flags = context_arch[test].flag | (1 << i);
-            length = 0xdeadbeef;
+            length = length2 = 0xdeadbeef;
             ret = pRtlGetExtendedContextLength(flags, &length);
 
             if ((context_arch[test].supported_flags & flags) || flags == context_arch[test].flag)
@@ -5672,6 +5691,137 @@ static void test_extended_context(void)
                 ok(ret == STATUS_INVALID_PARAMETER && length == 0xdeadbeef,
                         "Got unexpected result ret %#x, length %#x, flags 0x%08x.\n", ret, length, flags);
             }
+
+            SetLastError(0xdeadbeef);
+            bret = pInitializeContext(NULL, flags, NULL, &length2);
+            ok(!bret && length2 == length && GetLastError()
+                    == (!ret ? ERROR_INSUFFICIENT_BUFFER : ERROR_INVALID_PARAMETER),
+                    "Got unexpected bret %#x, length2 %#x, GetLastError() %u, flags %#x.\n",
+                    bret, length2, GetLastError(), flags);
+
+            if (GetLastError() == ERROR_INVALID_PARAMETER)
+                continue;
+
+            SetLastError(0xdeadbeef);
+            context = (void *)0xdeadbeef;
+            length2 = expected_length - 1;
+            bret = pInitializeContext(context_buffer, flags, &context, &length2);
+            ok(!bret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+                    "Got unexpected bret %#x, GetLastError() %u, flags %#x.\n", bret, GetLastError(), flags);
+            ok(context == (void *)0xdeadbeef, "Got unexpected context %p.\n", context);
+
+            SetLastError(0xdeadbeef);
+            memset(context_buffer, 0xcc, sizeof(context_buffer));
+            length2 = expected_length;
+            bret = pInitializeContext(context_buffer, flags, &context, &length2);
+            ok(bret && GetLastError() == 0xdeadbeef,
+                    "Got unexpected bret %#x, GetLastError() %u, flags %#x.\n", bret, GetLastError(), flags);
+            ok(length2 == expected_length, "Got unexpexted length %#x.\n", length);
+            ok((BYTE *)context == context_buffer, "Got unexpected context %p, flags %#x.\n", context, flags);
+
+            context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
+            ok(context_flags == flags, "Got unexpected ContextFlags %#x, flags %#x.\n", context_flags, flags);
+
+            context_ex = (CONTEXT_EX *)(context_buffer + context_arch[test].context_length);
+            ok(context_ex->Legacy.Offset == -(int)context_arch[test].context_length,
+                    "Got unexpected Offset %d, flags %#x.\n", context_ex->Legacy.Offset, flags);
+            if (!test)
+            {
+                /* Contains garbage on x86. */
+                ok(context_ex->Legacy.Length == context_arch[test].context_length,
+                        "Got unexpected Length %#x, flags %#x.\n", context_ex->Legacy.Length, flags);
+            }
+            ok(context_ex->All.Offset == -(int)context_arch[test].context_length,
+                    "Got unexpected Offset %d, flags %#x.\n", context_ex->All.Offset, flags);
+
+            /* No extra 8 bytes in x64 CONTEXT_EX here. */
+            ok(context_ex->All.Length == context_arch[test].context_length + context_arch[1].context_ex_length,
+                    "Got unexpected Length %#x, flags %#x.\n", context_ex->All.Length, flags);
+
+            /* No idea why is it so. */
+            ok(context_ex->XState.Offset == 25,
+                    "Got unexpected Offset %d, flags %#x.\n", context_ex->XState.Offset, flags);
+            ok(!context_ex->XState.Length,
+                    "Got unexpected Length %#x, flags %#x.\n", context_ex->XState.Length, flags);
+
+
+            for (j = 0; j < context_arch[test].flags_offset; ++j)
+            {
+                if (context_buffer[j] != 0xcc)
+                {
+                    ok(0, "Buffer data changed at offset %#x.\n", j);
+                    break;
+                }
+            }
+            for (j = context_arch[test].flags_offset + sizeof(context_flags);
+                    j < context_arch[test].context_length; ++j)
+            {
+                if (context_buffer[j] != 0xcc)
+                {
+                    ok(0, "Buffer data changed at offset %#x.\n", j);
+                    break;
+                }
+            }
+            for (j = context_arch[test].context_length + context_arch[test].context_ex_length;
+                    j < sizeof(context_buffer); ++j)
+            {
+                if (context_buffer[j] != 0xcc)
+                {
+                    ok(0, "Buffer data changed at offset %#x.\n", j);
+                    break;
+                }
+            }
+
+            memset(context_buffer2, 0xcc, sizeof(context_buffer2));
+            ret2 = pRtlInitializeExtendedContext(context_buffer2, flags, &context_ex);
+            ok(!ret2, "Got unexpected ret2 %#x, flags %#x.\n", ret2, flags);
+            ok(!memcmp(context_buffer2, context_buffer, sizeof(context_buffer2)),
+                    "Context data do not match, flags %#x.\n", flags);
+
+            memset(context_buffer2, 0xcc, sizeof(context_buffer2));
+            ret2 = pRtlInitializeExtendedContext(context_buffer2 + 2, flags, &context_ex);
+            ok(!ret2, "Got unexpected ret2 %#x, flags %#x.\n", ret2, flags);
+
+            /* Buffer gets aligned to 16 bytes on x64, while returned context length suggests it should be 8. */
+            align = test ? 4 : 16;
+            ok(!memcmp(context_buffer2 + align, context_buffer,
+                    sizeof(context_buffer2) - align),
+                    "Context data do not match, flags %#x.\n", flags);
+
+            SetLastError(0xdeadbeef);
+            memset(context_buffer2, 0xcc, sizeof(context_buffer2));
+            bret = pInitializeContext(context_buffer2 + 2, flags, &context, &length2);
+            ok(bret && GetLastError() == 0xdeadbeef,
+                    "Got unexpected bret %#x, GetLastError() %u, flags %#x.\n", bret, GetLastError(), flags);
+            ok(length2 == expected_length, "Got unexpexted length %#x.\n", length);
+            ok(!memcmp(context_buffer2 + align, context_buffer,
+                    sizeof(context_buffer2) - align),
+                    "Context data do not match, flags %#x.\n", flags);
+
+            if (!pRtlInitializeExtendedContext2 || !pInitializeContext2)
+            {
+                static int once;
+
+                if (!once++)
+                    win_skip("InitializeContext2 is not available.\n");
+                continue;
+            }
+
+            memset(context_buffer2, 0xcc, sizeof(context_buffer2));
+            ret2 = pRtlInitializeExtendedContext2(context_buffer2 + 2, flags, &context_ex, ~(ULONG64)0);
+            ok(!ret2, "Got unexpected ret2 %#x, flags %#x.\n", ret2, flags);
+            ok(!memcmp(context_buffer2 + align, context_buffer,
+                    sizeof(context_buffer2) - align),
+                    "Context data do not match, flags %#x.\n", flags);
+
+            memset(context_buffer2, 0xcc, sizeof(context_buffer2));
+            bret = pInitializeContext2(context_buffer2 + 2, flags, &context, &length2, ~(ULONG64)0);
+            ok(bret && GetLastError() == 0xdeadbeef,
+                    "Got unexpected bret %#x, GetLastError() %u, flags %#x.\n", bret, GetLastError(), flags);
+            ok(length2 == expected_length, "Got unexpexted length %#x.\n", length);
+            ok(!memcmp(context_buffer2 + align, context_buffer,
+                    sizeof(context_buffer2) - align),
+                    "Context data do not match, flags %#x.\n", flags);
         }
 
         flags = context_arch[test].flag | 0x40;
@@ -5683,6 +5833,10 @@ static void test_extended_context(void)
         {
             ok(ret == STATUS_NOT_SUPPORTED && length == 0xdeadbeef,
                     "Got unexpected result ret %#x, length %#x.\n", ret, length);
+
+            context_ex = (void *)0xdeadbeef;
+            ret2 = pRtlInitializeExtendedContext(context_buffer, flags, &context_ex);
+            ok(ret2 == STATUS_NOT_SUPPORTED, "Got unexpected result ret %#x, test %u.\n", ret2, test);
             continue;
         }
 
@@ -5692,39 +5846,156 @@ static void test_extended_context(void)
         if (!pRtlGetExtendedContextLength2)
         {
             win_skip("RtlGetExtendedContextLength2 is not available.\n");
-            continue;
         }
+        else
+        {
+            length = 0xdeadbeef;
+            ret = pRtlGetExtendedContextLength2(flags, &length, 7);
+            ok(!ret && length == expected_length_xstate,
+                    "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
 
-        length = 0xdeadbeef;
-        ret = pRtlGetExtendedContextLength2(flags, &length, 7);
-        ok(!ret && length == expected_length_xstate,
-                "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
+            length = 0xdeadbeef;
+            ret = pRtlGetExtendedContextLength2(flags, &length, ~0);
+            ok(!ret && length >= expected_length_xstate,
+                    "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
 
-        length = 0xdeadbeef;
-        ret = pRtlGetExtendedContextLength2(flags, &length, ~0);
-        ok(!ret && length >= expected_length_xstate,
-                "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
+            length = 0xdeadbeef;
+            ret = pRtlGetExtendedContextLength2(flags, &length, 0);
+            ok(!ret && length == expected_length_xstate - sizeof(YMMCONTEXT),
+                    "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
 
-        length = 0xdeadbeef;
-        ret = pRtlGetExtendedContextLength2(flags, &length, 0);
-        ok(!ret && length == expected_length_xstate - sizeof(YMMCONTEXT),
-                "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
+            length = 0xdeadbeef;
+            ret = pRtlGetExtendedContextLength2(flags, &length, 3);
+            ok(!ret && length == expected_length_xstate - sizeof(YMMCONTEXT),
+                    "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
 
-        length = 0xdeadbeef;
-        ret = pRtlGetExtendedContextLength2(flags, &length, 3);
-        ok(!ret && length == expected_length_xstate - sizeof(YMMCONTEXT),
-                "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
+            length = 0xdeadbeef;
+            ret = pRtlGetExtendedContextLength2(flags, &length, 4);
+            ok(!ret && length == expected_length_xstate,
+                    "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
+        }
 
-        length = 0xdeadbeef;
-        ret = pRtlGetExtendedContextLength2(flags, &length, 4);
-        ok(!ret && length == expected_length_xstate,
-                "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
+        pRtlGetExtendedContextLength(flags, &length);
+        SetLastError(0xdeadbeef);
+        bret = pInitializeContext(NULL, flags, NULL, &length2);
+        ok(!bret && length2 == length && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+                "Got unexpected bret %#x, length2 %#x, GetLastError() %u, flags %#x.\n",
+                bret, length2, GetLastError(), flags);
+
+        SetLastError(0xdeadbeef);
+        context = (void *)0xdeadbeef;
+        length2 = length - 1;
+        bret = pInitializeContext(context_buffer, flags, &context, &length2);
+        ok(!bret && GetLastError() == ERROR_INSUFFICIENT_BUFFER && length2 == length && context == (void *)0xdeadbeef,
+                "Got unexpected bret %#x, GetLastError() %u, length2 %#x, flags %#x.\n",
+                bret, GetLastError(), length2, flags);
+
+        SetLastError(0xdeadbeef);
+        memset(context_buffer, 0xcc, sizeof(context_buffer));
+        length2 = length + 1;
+        bret = pInitializeContext(context_buffer, flags, &context, &length2);
+        ok(bret && GetLastError() == 0xdeadbeef,
+                "Got unexpected bret %#x, GetLastError() %u, flags %#x.\n", bret, GetLastError(), flags);
+        ok(length2 == length, "Got unexpexted length %#x.\n", length);
+        ok((BYTE *)context == context_buffer, "Got unexpected context %p.\n", context);
+
+        context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
+        ok(context_flags == flags, "Got unexpected ContextFlags %#x, flags %#x.\n", context_flags, flags);
+
+        context_ex = (CONTEXT_EX *)(context_buffer + context_arch[test].context_length);
+        ok(context_ex->Legacy.Offset == -(int)context_arch[test].context_length,
+                "Got unexpected Offset %d, flags %#x.\n", context_ex->Legacy.Offset, flags);
+
+        if (!test)
+        {
+            /* Contains garbage on x86. */
+            ok(context_ex->Legacy.Length == context_arch[test].context_length,
+                    "Got unexpected Length %#x, flags %#x.\n", context_ex->Legacy.Length, flags);
+        }
+
+        expected_offset = (((ULONG_PTR)context + context_arch[test].context_length
+                + context_arch[test].context_ex_length + 63) & ~(ULONG64)63) - (ULONG_PTR)context
+                - context_arch[test].context_length;
+        ok(context_ex->XState.Offset == expected_offset,
+                "Got unexpected Offset %d, flags %#x.\n", context_ex->XState.Offset, flags);
+        ok(context_ex->XState.Length >= sizeof(XSTATE),
+                "Got unexpected Length %#x, flags %#x.\n", context_ex->XState.Length, flags);
+
+        ok(context_ex->All.Offset == -(int)context_arch[test].context_length,
+                "Got unexpected Offset %d, flags %#x.\n", context_ex->All.Offset, flags);
+        /* No extra 8 bytes in x64 CONTEXT_EX here. */
+        ok(context_ex->All.Length == context_arch[test].context_length
+                + context_ex->XState.Offset + context_ex->XState.Length,
+                "Got unexpected Length %#x, flags %#x.\n", context_ex->All.Length, flags);
+
+        xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset);
+        ok(!xs->Mask, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
+        ok(xs->CompactionMask == (compaction_enabled ? ((ULONG64)1 << 63) | enabled_features : 0),
+                "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask));
+        ok(!xs->Reserved[0], "Got unexpected Reserved[0]  %s.\n", wine_dbgstr_longlong(xs->Reserved[0]));
+
+        if (pRtlGetExtendedContextLength2)
+        {
+            memset(context_buffer, 0xcc, sizeof(context_buffer));
+            pRtlGetExtendedContextLength2(flags, &length, 0);
+            SetLastError(0xdeadbeef);
+            memset(context_buffer, 0xcc, sizeof(context_buffer));
+            length2 = length;
+            bret = pInitializeContext2(context_buffer, flags, &context, &length2, 0);
+            ok(bret && GetLastError() == 0xdeadbeef,
+                    "Got unexpected bret %#x, GetLastError() %u, flags %#x.\n", bret, GetLastError(), flags);
+            ok(length2 == length, "Got unexpexted length %#x.\n", length);
+            ok((BYTE *)context == context_buffer, "Got unexpected context %p.\n", context);
+
+            context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
+            ok(context_flags == flags, "Got unexpected ContextFlags %#x, flags %#x.\n", context_flags, flags);
+
+            context_ex = (CONTEXT_EX *)(context_buffer + context_arch[test].context_length);
+            ok(context_ex->Legacy.Offset == -(int)context_arch[test].context_length,
+                    "Got unexpected Offset %d, flags %#x.\n", context_ex->Legacy.Offset, flags);
+
+            if (!test)
+            {
+                /* Contains garbage on x86. */
+                ok(context_ex->Legacy.Length == context_arch[test].context_length,
+                        "Got unexpected Length %#x, flags %#x.\n", context_ex->Legacy.Length, flags);
+            }
+
+            expected_offset = (((ULONG_PTR)context + context_arch[test].context_length
+                    + context_arch[test].context_ex_length + 63) & ~(ULONG64)63) - (ULONG_PTR)context
+                    - context_arch[test].context_length;
+            ok(context_ex->XState.Offset == expected_offset,
+                    "Got unexpected Offset %d, flags %#x.\n", context_ex->XState.Offset, flags);
+            ok(context_ex->XState.Length == sizeof(XSTATE) - sizeof(YMMCONTEXT),
+                    "Got unexpected Length %#x, flags %#x.\n", context_ex->XState.Length, flags);
+
+            ok(context_ex->All.Offset == -(int)context_arch[test].context_length,
+                    "Got unexpected Offset %d, flags %#x.\n", context_ex->All.Offset, flags);
+            /* No extra 8 bytes in x64 CONTEXT_EX here. */
+            ok(context_ex->All.Length == context_arch[test].context_length
+                    + context_ex->XState.Offset + context_ex->XState.Length,
+                    "Got unexpected Length %#x, flags %#x.\n", context_ex->All.Length, flags);
+
+            xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset);
+            ok(!xs->Mask, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
+            ok(xs->CompactionMask == (compaction_enabled ? (ULONG64)1 << 63 : 0),
+                    "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask));
+            ok(!xs->Reserved[0], "Got unexpected Reserved[0]  %s.\n", wine_dbgstr_longlong(xs->Reserved[0]));
+        }
     }
 
+    length = 0xdeadbeef;
+    ret = pRtlGetExtendedContextLength(context_arch[0].flag | context_arch[1].flag, &length);
+    ok(ret == STATUS_INVALID_PARAMETER && length == 0xdeadbeef, "Got unexpected result ret %#x, length %#x.\n",
+            ret, length);
+
     if (0)
     {
         /* Crashes on Windows. */
         pRtlGetExtendedContextLength(CONTEXT_FULL, NULL);
+        length = sizeof(context_buffer);
+        pInitializeContext(context_buffer, CONTEXT_FULL, NULL, &length);
+        pInitializeContext(context_buffer, CONTEXT_FULL, &context, NULL);
     }
 
     if (!(enabled_features & (1 << XSTATE_AVX)))
@@ -5758,6 +6029,7 @@ static void test_extended_context(void)
 START_TEST(exception)
 {
     HMODULE hntdll = GetModuleHandleA("ntdll.dll");
+    HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
 #if defined(__x86_64__)
     HMODULE hmsvcrt = LoadLibraryA("msvcrt.dll");
 #endif
@@ -5800,11 +6072,18 @@ START_TEST(exception)
     X(RtlGetEnabledExtendedFeatures);
     X(RtlGetExtendedContextLength);
     X(RtlGetExtendedContextLength2);
+    X(RtlInitializeExtendedContext);
+    X(RtlInitializeExtendedContext2);
 #undef X
 
-    pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
+#define X(f) p##f = (void*)GetProcAddress(hkernel32, #f)
+    X(IsWow64Process);
     if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE;
 
+    X(InitializeContext);
+    X(InitializeContext2);
+#undef X
+
     if (pRtlAddVectoredExceptionHandler && pRtlRemoveVectoredExceptionHandler)
         have_vectored_api = TRUE;
     else
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
index 45024a3a8da..163e041065f 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
+++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
@@ -1122,6 +1122,8 @@
 @ stdcall RtlInitUnicodeString(ptr wstr)
 @ stdcall RtlInitUnicodeStringEx(ptr wstr)
 @ stdcall RtlInitializeBitMap(ptr ptr long)
+@ stdcall RtlInitializeExtendedContext(ptr long ptr)
+@ stdcall RtlInitializeExtendedContext2(ptr long ptr int64)
 @ stdcall RtlInitializeGenericTable(ptr ptr ptr ptr ptr)
 @ stdcall RtlInitializeGenericTableAvl(ptr ptr ptr ptr ptr)
 @ stub RtlInitializeRangeList
diff --git a/include/ddk/wdm.h b/include/ddk/wdm.h
index fa0f42aadef..b8a51fd7e61 100644
--- a/include/ddk/wdm.h
+++ b/include/ddk/wdm.h
@@ -1836,6 +1836,8 @@ HANDLE    WINAPI PsGetProcessInheritedFromUniqueProcessId(PEPROCESS);
 BOOLEAN   WINAPI PsGetVersion(ULONG*,ULONG*,ULONG*,UNICODE_STRING*);
 NTSTATUS  WINAPI PsTerminateSystemThread(NTSTATUS);
 
+ULONG     WINAPI RtlInitializeExtendedContext(void*,ULONG,CONTEXT_EX**);
+ULONG     WINAPI RtlInitializeExtendedContext2(void*,ULONG,CONTEXT_EX**,ULONG64);
 ULONG64   WINAPI RtlGetExtendedContextLength(ULONG,ULONG*);
 ULONG64   WINAPI RtlGetExtendedContextLength2(ULONG,ULONG*,ULONG64);
 
diff --git a/include/winbase.h b/include/winbase.h
index f2177ada1e4..9957cd43bb6 100644
--- a/include/winbase.h
+++ b/include/winbase.h
@@ -2379,6 +2379,8 @@ WINBASEAPI BOOL        WINAPI HeapWalk(HANDLE,LPPROCESS_HEAP_ENTRY);
 WINBASEAPI BOOL        WINAPI InitAtomTable(DWORD);
 WINADVAPI  BOOL        WINAPI InitializeAcl(PACL,DWORD,DWORD);
 WINBASEAPI VOID        WINAPI InitializeConditionVariable(PCONDITION_VARIABLE);
+WINBASEAPI BOOL        WINAPI InitializeContext(void *,DWORD,CONTEXT **,DWORD *);
+WINBASEAPI BOOL        WINAPI InitializeContext2(void *,DWORD,CONTEXT **,DWORD *,ULONG64);
 WINBASEAPI void        WINAPI InitializeCriticalSection(CRITICAL_SECTION *lpCrit);
 WINBASEAPI BOOL        WINAPI InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION *,DWORD);
 WINBASEAPI BOOL        WINAPI InitializeCriticalSectionEx(CRITICAL_SECTION *,DWORD,DWORD);
-- 
2.26.2




More information about the wine-devel mailing list