Allows for native threads to be created under wow64 processes.
From: Billy Laws blaws05@gmail.com
--- dlls/ntdll/tests/thread.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/dlls/ntdll/tests/thread.c b/dlls/ntdll/tests/thread.c index 9fd23809488..74753b2f6cb 100644 --- a/dlls/ntdll/tests/thread.c +++ b/dlls/ntdll/tests/thread.c @@ -30,6 +30,7 @@
static BOOL is_wow64; static BOOL old_wow64; +static BOOL is_arm64_native_machine;
static NTSTATUS (WINAPI *pNtAllocateReserveObject)( HANDLE *, const OBJECT_ATTRIBUTES *, MEMORY_RESERVE_OBJECT_TYPE ); static NTSTATUS (WINAPI *pNtCreateThreadEx)( HANDLE *, ACCESS_MASK, OBJECT_ATTRIBUTES *, @@ -276,6 +277,12 @@ static void test_thread_bypass_process_freeze(void) HANDLE thread; NTSTATUS status;
+ if (is_wow64 && is_arm64_native_machine) + { + win_skip( "Skipping process suspend test broken under ARM64 WOW64.\n" ); + return; + } + if (!pNtCreateThreadEx || !pNtSuspendProcess || !pNtResumeProcess) { win_skip( "NtCreateThreadEx/NtSuspendProcess/NtResumeProcess are not available.\n" ); @@ -457,6 +464,13 @@ START_TEST(thread) } }
+ if (pRtlWow64GetProcessMachines) + { + USHORT current, native; + is_arm64_native_machine = !pRtlWow64GetProcessMachines( GetCurrentProcess(), ¤t, &native ) && + native == IMAGE_FILE_MACHINE_ARM64; + } + test_dbg_hidden_thread_creation(); test_unique_teb(); test_errno();
From: Billy Laws blaws05@gmail.com
--- dlls/ntdll/tests/thread.c | 67 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)
diff --git a/dlls/ntdll/tests/thread.c b/dlls/ntdll/tests/thread.c index 74753b2f6cb..6192104b9dc 100644 --- a/dlls/ntdll/tests/thread.c +++ b/dlls/ntdll/tests/thread.c @@ -36,12 +36,15 @@ static NTSTATUS (WINAPI *pNtAllocateReserveObject)( HANDLE *, const OBJECT_ATTRI static NTSTATUS (WINAPI *pNtCreateThreadEx)( HANDLE *, ACCESS_MASK, OBJECT_ATTRIBUTES *, HANDLE, PRTL_THREAD_START_ROUTINE, void *, ULONG, ULONG_PTR, SIZE_T, SIZE_T, PS_ATTRIBUTE_LIST * ); +static NTSTATUS (WINAPI *pNtAllocateVirtualMemoryEx)(HANDLE, PVOID *, SIZE_T *, ULONG, ULONG, + MEM_EXTENDED_PARAMETER *, ULONG); static NTSTATUS (WINAPI *pNtSuspendProcess)(HANDLE process); static NTSTATUS (WINAPI *pNtResumeProcess)(HANDLE process); static NTSTATUS (WINAPI *pNtQueueApcThreadEx)(HANDLE handle, HANDLE reserve_handle, PNTAPCFUNC func, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3); static NTSTATUS (WINAPI *pNtQueueApcThreadEx2)(HANDLE handle, HANDLE reserve_handle, ULONG flags, PNTAPCFUNC func, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3); +static NTSTATUS (WINAPI *pRtlWow64GetProcessMachines)(HANDLE,WORD*,WORD*);
static int * (CDECL *p_errno)(void);
@@ -54,10 +57,12 @@ static void init_function_pointers(void) hdll = GetModuleHandleA( "ntdll.dll" ); GET_FUNC( NtAllocateReserveObject ); GET_FUNC( NtCreateThreadEx ); + GET_FUNC( NtAllocateVirtualMemoryEx ); GET_FUNC( NtSuspendProcess ); GET_FUNC( NtQueueApcThreadEx ); GET_FUNC( NtQueueApcThreadEx2 ); GET_FUNC( NtResumeProcess ); + GET_FUNC( RtlWow64GetProcessMachines ); GET_FUNC( _errno );
hdll = GetModuleHandleA( "kernel32.dll" ); @@ -448,6 +453,67 @@ delete: DeleteFileA(path_dll_local); }
+struct test_arm64_skip_load_init_args +{ + USHORT teb_same_teb_flags; +}; + +static void test_arm64_skip_loader_init(void) +{ + static ULONG native_code[] = + { + 0xd282fdc2, /* mov x2, #0x17ee */ + 0x78626a41, /* ldrh w1, [x18, x2] (NtCurrentTeb()->SameTebFlags) */ + 0x79000001, /* strh w1, [x0] (args->teb_same_teb_flags) */ + 0xd65f03c0, /* ret */ + }; + + struct test_arm64_skip_load_init_args args; + HANDLE thread; + NTSTATUS status; + void *code_mem = NULL; +#ifdef __x86_64__ + MEM_EXTENDED_PARAMETER param = { 0 }; + SIZE_T code_size = 0x10000; + + param.Type = MemExtendedParameterAttributeFlags; + param.ULong64 = MEM_EXTENDED_PARAMETER_EC_CODE; + if (!pNtAllocateVirtualMemoryEx || + pNtAllocateVirtualMemoryEx( GetCurrentProcess(), &code_mem, &code_size, MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ¶m, 1 )) + { + trace("NtAllocateVirtualMemoryEx failed\n"); + return; + + } +#else + code_mem = VirtualAlloc( NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + if (!code_mem) + { + trace("VirtualAlloc failed\n"); + return; + } +#endif + if (!pNtCreateThreadEx) + { + win_skip( "NtCreateThreadEx is not available.\n" ); + return; + } + + memcpy( code_mem, native_code, sizeof(native_code) ); + + status = pNtCreateThreadEx( &thread, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), (PRTL_THREAD_START_ROUTINE)code_mem, + &args, THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH | THREAD_CREATE_FLAGS_SKIP_LOADER_INIT, 0, 0, 0, NULL ); + + todo_wine ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + + WaitForSingleObject( thread, INFINITE ); + + todo_wine ok( (args.teb_same_teb_flags & 0x4008) == 0x4008, "wrong value %x\n", args.teb_same_teb_flags ); + + CloseHandle( thread ); +} + START_TEST(thread) { init_function_pointers(); @@ -469,6 +535,7 @@ START_TEST(thread) USHORT current, native; is_arm64_native_machine = !pRtlWow64GetProcessMachines( GetCurrentProcess(), ¤t, &native ) && native == IMAGE_FILE_MACHINE_ARM64; + if (is_arm64_native_machine) test_arm64_skip_loader_init(); }
test_dbg_hidden_thread_creation();
From: Billy Laws blaws05@gmail.com
The native arch kernel32.dll isn't loaded in WOW64 mode, but threads can still be created if the SKIP_LOADER_INIT flag is set, provide a default implementation to support this. --- dlls/ntdll/loader.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index c20d6a5d87b..5859d1e5a74 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -69,7 +69,8 @@ static const WCHAR pe_dir[] = L""; typedef DWORD (CALLBACK *DLLENTRYPROC)(HMODULE,DWORD,LPVOID); typedef void (CALLBACK *LDRENUMPROC)(LDR_DATA_TABLE_ENTRY *, void *, BOOLEAN *);
-void (FASTCALL *pBaseThreadInitThunk)(DWORD,LPTHREAD_START_ROUTINE,void *) = NULL; +static void __fastcall default_thread_init_func( DWORD unknown, LPTHREAD_START_ROUTINE entry, void *arg ); +void (FASTCALL *pBaseThreadInitThunk)(DWORD,LPTHREAD_START_ROUTINE,void *) = default_thread_init_func; NTSTATUS (WINAPI *__wine_unix_call_dispatcher)( unixlib_handle_t, unsigned int, void * ) = NULL;
static DWORD (WINAPI *pCtrlRoutine)(void *); @@ -220,6 +221,11 @@ static RTL_UNLOAD_EVENT_TRACE unload_traces[RTL_UNLOAD_EVENT_TRACE_NUMBER]; static RTL_UNLOAD_EVENT_TRACE *unload_trace_ptr; static unsigned int unload_trace_seq;
+static void __fastcall default_thread_init_func( DWORD unknown, LPTHREAD_START_ROUTINE entry, void *arg ) +{ + RtlExitUserThread( entry( arg ) ); +} + static void module_push_unload_trace( const WINE_MODREF *wm ) { RTL_UNLOAD_EVENT_TRACE *ptr = &unload_traces[unload_trace_seq];
From: Billy Laws blaws05@gmail.com
I confirmed that ARM64EC BT thread creation callbacks aren't called through some logging in FEX. --- dlls/ntdll/loader.c | 2 ++ dlls/ntdll/tests/thread.c | 4 ++-- dlls/ntdll/unix/thread.c | 10 ++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 5859d1e5a74..8df76c6cc26 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -4401,6 +4401,8 @@ void loader_init( CONTEXT *context, void **entry )
if (process_detaching) NtTerminateThread( GetCurrentThread(), 0 );
+ if (NtCurrentTeb()->SkipLoaderInit) return; + RtlEnterCriticalSection( &loader_section );
if (!imports_fixup_done) diff --git a/dlls/ntdll/tests/thread.c b/dlls/ntdll/tests/thread.c index 6192104b9dc..b892ca04f6c 100644 --- a/dlls/ntdll/tests/thread.c +++ b/dlls/ntdll/tests/thread.c @@ -505,11 +505,11 @@ static void test_arm64_skip_loader_init(void) status = pNtCreateThreadEx( &thread, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), (PRTL_THREAD_START_ROUTINE)code_mem, &args, THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH | THREAD_CREATE_FLAGS_SKIP_LOADER_INIT, 0, 0, 0, NULL );
- todo_wine ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status );
WaitForSingleObject( thread, INFINITE );
- todo_wine ok( (args.teb_same_teb_flags & 0x4008) == 0x4008, "wrong value %x\n", args.teb_same_teb_flags ); + ok( (args.teb_same_teb_flags & 0x4008) == 0x4008, "wrong value %x\n", args.teb_same_teb_flags );
CloseHandle( thread ); } diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 65e7fbcb6bf..8f3fb701a38 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -1316,7 +1316,8 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT SIZE_T stack_reserve, PS_ATTRIBUTE_LIST *attr_list ) { static const ULONG supported_flags = THREAD_CREATE_FLAGS_CREATE_SUSPENDED | THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH | - THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER | THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE; + THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER | THREAD_CREATE_FLAGS_SKIP_LOADER_INIT | + THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE; sigset_t sigset; pthread_t pthread_id; pthread_attr_t pthread_attr; @@ -1413,8 +1414,13 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT set_thread_id( teb, GetCurrentProcessId(), tid );
teb->SkipThreadAttach = !!(flags & THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH); + teb->SkipLoaderInit = !!(flags & THREAD_CREATE_FLAGS_SKIP_LOADER_INIT); wow_teb = get_wow_teb( teb ); - if (wow_teb) wow_teb->SkipThreadAttach = teb->SkipThreadAttach; + if (wow_teb) + { + wow_teb->SkipThreadAttach = teb->SkipThreadAttach; + wow_teb->SkipLoaderInit = teb->SkipLoaderInit; + }
thread_data = (struct ntdll_thread_data *)&teb->GdiTebBatch; thread_data->request_fd = request_pipe[1];