2 * WoW64 syscall wrapping
4 * Copyright 2021 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #define WIN32_NO_STATUS
29 #include "wine/exception.h"
30 #include "wine/unixlib.h"
31 #include "wow64_private.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(wow
);
36 USHORT native_machine
= 0;
37 USHORT current_machine
= 0;
38 ULONG_PTR args_alignment
= 0;
40 typedef NTSTATUS (WINAPI
*syscall_thunk
)( UINT
*args
);
42 static const syscall_thunk syscall_thunks
[] =
44 #define SYSCALL_ENTRY(func) wow64_ ## func,
49 static const char *syscall_names
[] =
51 #define SYSCALL_ENTRY(func) #func,
56 static unsigned short syscall_map
[1024];
58 /* header for Wow64AllocTemp blocks; probably not the right layout */
61 struct mem_header
*next
;
66 static void **pWow64Transition
;
67 static void **p__wine_syscall_dispatcher
;
68 static SYSTEM_DLL_INIT_BLOCK
*pLdrSystemDllInitBlock
;
70 /* cpu backend dll functions */
71 static void * (WINAPI
*pBTCpuGetBopCode
)(void);
72 static void (WINAPI
*pBTCpuProcessInit
)(void);
73 static void (WINAPI
*pBTCpuSimulate
)(void);
76 void *dummy
= RtlUnwind
;
78 BOOL WINAPI
DllMain( HINSTANCE inst
, DWORD reason
, void *reserved
)
80 if (reason
== DLL_PROCESS_ATTACH
) LdrDisableThreadCalloutsForDll( inst
);
84 void __cdecl
__wine_spec_unimplemented_stub( const char *module
, const char *function
)
86 EXCEPTION_RECORD record
;
88 record
.ExceptionCode
= EXCEPTION_WINE_STUB
;
89 record
.ExceptionFlags
= EH_NONCONTINUABLE
;
90 record
.ExceptionRecord
= NULL
;
91 record
.ExceptionAddress
= __wine_spec_unimplemented_stub
;
92 record
.NumberParameters
= 2;
93 record
.ExceptionInformation
[0] = (ULONG_PTR
)module
;
94 record
.ExceptionInformation
[1] = (ULONG_PTR
)function
;
95 for (;;) RtlRaiseException( &record
);
99 /**********************************************************************
102 NTSTATUS WINAPI
wow64_NtAddAtom( UINT
*args
)
104 const WCHAR
*name
= get_ptr( &args
);
105 ULONG len
= get_ulong( &args
);
106 RTL_ATOM
*atom
= get_ptr( &args
);
108 return NtAddAtom( name
, len
, atom
);
112 /**********************************************************************
113 * wow64_NtAllocateLocallyUniqueId
115 NTSTATUS WINAPI
wow64_NtAllocateLocallyUniqueId( UINT
*args
)
117 LUID
*luid
= get_ptr( &args
);
119 return NtAllocateLocallyUniqueId( luid
);
123 /**********************************************************************
124 * wow64_NtAllocateUuids
126 NTSTATUS WINAPI
wow64_NtAllocateUuids( UINT
*args
)
128 ULARGE_INTEGER
*time
= get_ptr( &args
);
129 ULONG
*delta
= get_ptr( &args
);
130 ULONG
*sequence
= get_ptr( &args
);
131 UCHAR
*seed
= get_ptr( &args
);
133 return NtAllocateUuids( time
, delta
, sequence
, seed
);
137 /**********************************************************************
140 NTSTATUS WINAPI
wow64_NtClose( UINT
*args
)
142 HANDLE handle
= get_handle( &args
);
144 return NtClose( handle
);
148 /**********************************************************************
151 NTSTATUS WINAPI
wow64_NtDeleteAtom( UINT
*args
)
153 RTL_ATOM atom
= get_ulong( &args
);
155 return NtDeleteAtom( atom
);
159 /**********************************************************************
162 NTSTATUS WINAPI
wow64_NtFindAtom( UINT
*args
)
164 const WCHAR
*name
= get_ptr( &args
);
165 ULONG len
= get_ulong( &args
);
166 RTL_ATOM
*atom
= get_ptr( &args
);
168 return NtFindAtom( name
, len
, atom
);
172 /**********************************************************************
173 * wow64_NtGetCurrentProcessorNumber
175 NTSTATUS WINAPI
wow64_NtGetCurrentProcessorNumber( UINT
*args
)
177 return NtGetCurrentProcessorNumber();
181 /**********************************************************************
182 * wow64_NtQueryDefaultLocale
184 NTSTATUS WINAPI
wow64_NtQueryDefaultLocale( UINT
*args
)
186 BOOLEAN user
= get_ulong( &args
);
187 LCID
*lcid
= get_ptr( &args
);
189 return NtQueryDefaultLocale( user
, lcid
);
193 /**********************************************************************
194 * wow64_NtQueryDefaultUILanguage
196 NTSTATUS WINAPI
wow64_NtQueryDefaultUILanguage( UINT
*args
)
198 LANGID
*lang
= get_ptr( &args
);
200 return NtQueryDefaultUILanguage( lang
);
204 /**********************************************************************
205 * wow64_NtQueryInformationAtom
207 NTSTATUS WINAPI
wow64_NtQueryInformationAtom( UINT
*args
)
209 RTL_ATOM atom
= get_ulong( &args
);
210 ATOM_INFORMATION_CLASS
class = get_ulong( &args
);
211 void *info
= get_ptr( &args
);
212 ULONG len
= get_ulong( &args
);
213 ULONG
*retlen
= get_ptr( &args
);
215 if (class != AtomBasicInformation
) FIXME( "class %u not supported\n", class );
216 return NtQueryInformationAtom( atom
, class, info
, len
, retlen
);
220 /**********************************************************************
221 * wow64_NtQueryInstallUILanguage
223 NTSTATUS WINAPI
wow64_NtQueryInstallUILanguage( UINT
*args
)
225 LANGID
*lang
= get_ptr( &args
);
227 return NtQueryInstallUILanguage( lang
);
231 /**********************************************************************
232 * wow64_NtSetDefaultLocale
234 NTSTATUS WINAPI
wow64_NtSetDefaultLocale( UINT
*args
)
236 BOOLEAN user
= get_ulong( &args
);
237 LCID lcid
= get_ulong( &args
);
239 return NtSetDefaultLocale( user
, lcid
);
243 /**********************************************************************
244 * wow64_NtSetDefaultUILanguage
246 NTSTATUS WINAPI
wow64_NtSetDefaultUILanguage( UINT
*args
)
248 LANGID lang
= get_ulong( &args
);
250 return NtSetDefaultUILanguage( lang
);
254 /**********************************************************************
255 * wow64___wine_dbg_write
257 NTSTATUS WINAPI
wow64___wine_dbg_write( UINT
*args
)
259 const char *str
= get_ptr( &args
);
260 ULONG len
= get_ulong( &args
);
262 return __wine_dbg_write( str
, len
);
266 /**********************************************************************
267 * wow64___wine_unix_call
269 NTSTATUS WINAPI
wow64___wine_unix_call( UINT
*args
)
271 unixlib_handle_t handle
= get_ulong64( &args
);
272 unsigned int code
= get_ulong( &args
);
273 void *args_ptr
= get_ptr( &args
);
275 return __wine_unix_call( handle
, code
, args_ptr
);
279 /**********************************************************************
280 * wow64_wine_server_call
282 NTSTATUS WINAPI
wow64_wine_server_call( UINT
*args
)
284 struct __server_request_info32
*req32
= get_ptr( &args
);
288 struct __server_request_info req
;
290 req
.u
.req
= req32
->u
.req
;
291 req
.data_count
= req32
->data_count
;
292 for (i
= 0; i
< req
.data_count
; i
++)
294 req
.data
[i
].ptr
= ULongToPtr( req32
->data
[i
].ptr
);
295 req
.data
[i
].size
= req32
->data
[i
].size
;
297 req
.reply_data
= ULongToPtr( req32
->reply_data
);
298 status
= wine_server_call( &req
);
299 req32
->u
.reply
= req
.u
.reply
;
304 /**********************************************************************
307 static DWORD
get_syscall_num( const BYTE
*syscall
)
311 if (!syscall
) return id
;
312 switch (current_machine
)
314 case IMAGE_FILE_MACHINE_I386
:
315 if (syscall
[0] == 0xb8 && syscall
[5] == 0xba && syscall
[10] == 0xff && syscall
[11] == 0xd2)
316 id
= *(DWORD
*)(syscall
+ 1);
319 case IMAGE_FILE_MACHINE_ARM
:
320 if (*(WORD
*)syscall
== 0xb40f)
322 DWORD inst
= *(DWORD
*)((WORD
*)syscall
+ 1);
323 id
= ((inst
<< 1) & 0x0800) + ((inst
<< 12) & 0xf000) +
324 ((inst
>> 20) & 0x0700) + ((inst
>> 16) & 0x00ff);
332 /**********************************************************************
335 static void init_syscall_table( HMODULE ntdll
)
337 const IMAGE_EXPORT_DIRECTORY
*exports
;
338 const ULONG
*functions
, *names
;
339 const USHORT
*ordinals
;
340 ULONG id
, exp_size
, exp_pos
, wrap_pos
;
342 args_alignment
= (current_machine
== IMAGE_FILE_MACHINE_I386
) ? sizeof(ULONG
) : sizeof(ULONG64
);
344 exports
= RtlImageDirectoryEntryToData( ntdll
, TRUE
, IMAGE_DIRECTORY_ENTRY_EXPORT
, &exp_size
);
345 ordinals
= get_rva( ntdll
, exports
->AddressOfNameOrdinals
);
346 functions
= get_rva( ntdll
, exports
->AddressOfFunctions
);
347 names
= get_rva( ntdll
, exports
->AddressOfNames
);
349 for (exp_pos
= wrap_pos
= 0; exp_pos
< exports
->NumberOfNames
; exp_pos
++)
351 char *name
= get_rva( ntdll
, names
[exp_pos
] );
354 if (strncmp( name
, "Nt", 2 ) && strncmp( name
, "wine", 4 ) && strncmp( name
, "__wine", 6 ))
355 continue; /* not a syscall */
357 if ((id
= get_syscall_num( get_rva( ntdll
, functions
[ordinals
[exp_pos
]] ))) == ~0u)
358 continue; /* not a syscall */
360 if (wrap_pos
< ARRAY_SIZE(syscall_names
))
361 res
= strcmp( name
, syscall_names
[wrap_pos
] );
363 if (!res
) /* got a match */
365 if (id
< ARRAY_SIZE(syscall_map
)) syscall_map
[id
] = wrap_pos
++;
366 else ERR( "invalid syscall id %04x for %s\n", id
, name
);
370 FIXME( "no ntdll export for syscall %s\n", syscall_names
[wrap_pos
] );
372 exp_pos
--; /* try again */
374 else FIXME( "missing wrapper for syscall %04x %s\n", id
, name
);
377 for ( ; wrap_pos
< ARRAY_SIZE(syscall_thunks
); wrap_pos
++)
378 FIXME( "no ntdll export for syscall %s\n", syscall_names
[wrap_pos
] );
382 /**********************************************************************
385 static HMODULE
load_cpu_dll(void)
390 WCHAR path
[MAX_PATH
];
391 const WCHAR
*dir
, *name
;
393 switch (current_machine
)
395 case IMAGE_FILE_MACHINE_I386
:
396 name
= (native_machine
== IMAGE_FILE_MACHINE_ARM64
? L
"xtajit.dll" : L
"wow64cpu.dll");
398 case IMAGE_FILE_MACHINE_ARM
:
399 name
= L
"wowarmhw.dll";
402 ERR( "unsupported machine %04x\n", current_machine
);
403 RtlExitUserProcess( 1 );
406 dir
= get_machine_wow64_dir( IMAGE_FILE_MACHINE_TARGET_HOST
);
408 swprintf( path
, MAX_PATH
, L
"%s\\%s", dir
, name
);
409 RtlInitUnicodeString( &str
, path
);
410 if ((status
= LdrLoadDll( NULL
, 0, &str
, &module
)))
412 ERR( "failed to load CPU dll %x\n", status
);
413 NtTerminateProcess( GetCurrentProcess(), status
);
419 /**********************************************************************
422 static DWORD WINAPI
process_init( RTL_RUN_ONCE
*once
, void *param
, void **context
)
427 RtlWow64GetProcessMachines( GetCurrentProcess(), ¤t_machine
, &native_machine
);
428 if (!current_machine
) current_machine
= native_machine
;
430 #define GET_PTR(name) p ## name = RtlFindExportedRoutineByName( module, #name )
432 RtlInitUnicodeString( &str
, L
"ntdll.dll" );
433 LdrGetDllHandle( NULL
, 0, &str
, &module
);
434 GET_PTR( LdrSystemDllInitBlock
);
436 module
= (HMODULE
)(ULONG_PTR
)pLdrSystemDllInitBlock
->ntdll_handle
;
437 GET_PTR( Wow64Transition
);
438 GET_PTR( __wine_syscall_dispatcher
);
439 init_syscall_table( module
);
441 module
= load_cpu_dll();
442 GET_PTR( BTCpuGetBopCode
);
443 GET_PTR( BTCpuProcessInit
);
444 GET_PTR( BTCpuSimulate
);
447 *pWow64Transition
= *p__wine_syscall_dispatcher
= pBTCpuGetBopCode();
449 init_file_redirects();
456 /**********************************************************************
459 static void thread_init(void)
461 /* update initial context to jump to 32-bit LdrInitializeThunk (cf. 32-bit call_init_thunk) */
462 switch (current_machine
)
464 case IMAGE_FILE_MACHINE_I386
:
466 I386_CONTEXT
*ctx_ptr
, ctx
= { CONTEXT_I386_ALL
};
469 NtQueryInformationThread( GetCurrentThread(), ThreadWow64Context
, &ctx
, sizeof(ctx
), NULL
);
470 ctx_ptr
= (I386_CONTEXT
*)ULongToPtr( ctx
.Esp
) - 1;
473 stack
= (ULONG
*)ctx_ptr
;
477 *(--stack
) = PtrToUlong( ctx_ptr
);
478 *(--stack
) = 0xdeadbabe;
479 ctx
.Esp
= PtrToUlong( stack
);
480 ctx
.Eip
= pLdrSystemDllInitBlock
->pLdrInitializeThunk
;
481 NtSetInformationThread( GetCurrentThread(), ThreadWow64Context
, &ctx
, sizeof(ctx
) );
485 case IMAGE_FILE_MACHINE_ARMNT
:
487 ARM_CONTEXT
*ctx_ptr
, ctx
= { CONTEXT_ARM_ALL
};
489 NtQueryInformationThread( GetCurrentThread(), ThreadWow64Context
, &ctx
, sizeof(ctx
), NULL
);
490 ctx_ptr
= (ARM_CONTEXT
*)ULongToPtr( ctx
.Sp
& ~15 ) - 1;
493 ctx
.R0
= PtrToUlong( ctx_ptr
);
494 ctx
.Sp
= PtrToUlong( ctx_ptr
);
495 ctx
.Pc
= pLdrSystemDllInitBlock
->pLdrInitializeThunk
;
496 NtSetInformationThread( GetCurrentThread(), ThreadWow64Context
, &ctx
, sizeof(ctx
) );
501 ERR( "not supported machine %x\n", current_machine
);
502 NtTerminateProcess( GetCurrentProcess(), STATUS_INVALID_IMAGE_FORMAT
);
507 /**********************************************************************
510 static void free_temp_data(void)
512 struct mem_header
*next
, *mem
;
514 for (mem
= NtCurrentTeb()->TlsSlots
[WOW64_TLS_TEMPLIST
]; mem
; mem
= next
)
517 RtlFreeHeap( GetProcessHeap(), 0, mem
);
519 NtCurrentTeb()->TlsSlots
[WOW64_TLS_TEMPLIST
] = NULL
;
523 /**********************************************************************
524 * Wow64SystemServiceEx (NTDLL.@)
526 NTSTATUS WINAPI
Wow64SystemServiceEx( UINT num
, UINT
*args
)
530 if (num
>= ARRAY_SIZE( syscall_map
) || !syscall_map
[num
])
532 ERR( "unsupported syscall %04x\n", num
);
533 return STATUS_INVALID_SYSTEM_SERVICE
;
537 syscall_thunk thunk
= syscall_thunks
[syscall_map
[num
]];
538 status
= thunk( args
);
542 status
= GetExceptionCode();
550 /**********************************************************************
553 * FIXME: probably not 100% compatible.
555 void * WINAPI
Wow64AllocateTemp( SIZE_T size
)
557 struct mem_header
*mem
;
559 if (!(mem
= RtlAllocateHeap( GetProcessHeap(), 0, offsetof( struct mem_header
, data
[size
] ))))
561 mem
->next
= NtCurrentTeb()->TlsSlots
[WOW64_TLS_TEMPLIST
];
562 NtCurrentTeb()->TlsSlots
[WOW64_TLS_TEMPLIST
] = mem
;
567 /**********************************************************************
568 * Wow64ApcRoutine (NTDLL.@)
570 void WINAPI
Wow64ApcRoutine( ULONG_PTR arg1
, ULONG_PTR arg2
, ULONG_PTR arg3
, CONTEXT
*context
)
575 retval
= context
->Rax
;
576 #elif defined(__aarch64__)
577 retval
= context
->X0
;
580 /* cf. 32-bit call_user_apc_dispatcher */
581 switch (current_machine
)
583 case IMAGE_FILE_MACHINE_I386
:
585 struct apc_stack_layout
593 I386_CONTEXT context
;
595 I386_CONTEXT ctx
= { CONTEXT_I386_FULL
};
597 NtQueryInformationThread( GetCurrentThread(), ThreadWow64Context
, &ctx
, sizeof(ctx
), NULL
);
598 stack
= (struct apc_stack_layout
*)ULongToPtr( ctx
.Esp
& ~3 ) - 1;
599 stack
->context_ptr
= PtrToUlong( &stack
->context
);
600 stack
->func
= arg1
>> 32;
604 stack
->context
= ctx
;
605 stack
->context
.Eax
= retval
;
606 ctx
.Esp
= PtrToUlong( stack
);
607 ctx
.Eip
= pLdrSystemDllInitBlock
->pKiUserApcDispatcher
;
608 NtSetInformationThread( GetCurrentThread(), ThreadWow64Context
, &ctx
, sizeof(ctx
) );
612 case IMAGE_FILE_MACHINE_ARMNT
:
614 struct apc_stack_layout
620 ARM_CONTEXT ctx
= { CONTEXT_ARM_FULL
};
622 NtQueryInformationThread( GetCurrentThread(), ThreadWow64Context
, &ctx
, sizeof(ctx
), NULL
);
623 stack
= (struct apc_stack_layout
*)ULongToPtr( ctx
.Sp
& ~15 ) - 1;
624 stack
->func
= arg1
>> 32;
625 stack
->context
= ctx
;
626 stack
->context
.R0
= retval
;
627 ctx
.Sp
= PtrToUlong( stack
);
628 ctx
.Pc
= pLdrSystemDllInitBlock
->pKiUserApcDispatcher
;
629 ctx
.R0
= PtrToUlong( &stack
->context
);
633 NtSetInformationThread( GetCurrentThread(), ThreadWow64Context
, &ctx
, sizeof(ctx
) );
640 /**********************************************************************
641 * Wow64LdrpInitialize (NTDLL.@)
643 void WINAPI
Wow64LdrpInitialize( CONTEXT
*context
)
645 static RTL_RUN_ONCE init_done
;
647 RtlRunOnceExecuteOnce( &init_done
, process_init
, NULL
, NULL
);