kernel32: Move exec process functionality to ntdll.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2019-11-11 21:04:27 +01:00
parent 8c4a1cc792
commit 83e520f556
4 changed files with 210 additions and 257 deletions

View File

@ -104,8 +104,6 @@ static WCHAR winevdm[] = {'C',':','\\','w','i','n','d','o','w','s',
static const char * const cpu_names[] = { "x86", "x86_64", "PowerPC", "ARM", "ARM64" };
static void exec_process( LPCWSTR name );
/* return values for get_binary_info */
enum binary_type
{
@ -580,9 +578,7 @@ void WINAPI start_process( LPTHREAD_START_ROUTINE entry, PEB *peb )
void * CDECL __wine_kernel_init(void)
{
static const WCHAR kernel32W[] = {'k','e','r','n','e','l','3','2',0};
static const WCHAR dotW[] = {'.',0};
WCHAR *p, main_exe_name[MAX_PATH+1];
PEB *peb = NtCurrentTeb()->Peb;
RTL_USER_PROCESS_PARAMETERS *params = peb->ProcessParameters;
@ -597,53 +593,8 @@ void * CDECL __wine_kernel_init(void)
LOCALE_Init();
init_windows_dirs();
convert_old_config();
/* if there's no extension, append a dot to prevent LoadLibrary from appending .dll */
strcpyW( main_exe_name, peb->ProcessParameters->ImagePathName.Buffer );
p = strrchrW( main_exe_name, '.' );
if (!p || strchrW( p, '/' ) || strchrW( p, '\\' )) strcatW( main_exe_name, dotW );
TRACE( "starting process name=%s argv[0]=%s\n",
debugstr_w(main_exe_name), debugstr_w(__wine_main_wargv[0]) );
set_library_argv( __wine_main_wargv );
if (!peb->ImageBaseAddress &&
!(peb->ImageBaseAddress = LoadLibraryExW( main_exe_name, 0, DONT_RESOLVE_DLL_REFERENCES )))
{
DWORD_PTR args[1];
WCHAR msgW[1024];
char msg[1024];
DWORD error = GetLastError();
/* if Win16/DOS format, or unavailable address, exec a new process with the proper setup */
if (error == ERROR_BAD_EXE_FORMAT ||
error == ERROR_INVALID_ADDRESS ||
error == ERROR_NOT_ENOUGH_MEMORY)
{
if (!getenv("WINEPRELOADRESERVE")) exec_process( main_exe_name );
/* if we get back here, it failed */
}
else if (error == ERROR_MOD_NOT_FOUND)
{
if (!strcmpiW( main_exe_name, winevdm ) && __wine_main_argc > 3)
{
/* args 1 and 2 are --app-name full_path */
MESSAGE( "wine: could not run %s: 16-bit/DOS support missing\n",
debugstr_w(__wine_main_wargv[3]) );
ExitProcess( ERROR_BAD_EXE_FORMAT );
}
MESSAGE( "wine: cannot find %s\n", debugstr_w(main_exe_name) );
ExitProcess( ERROR_FILE_NOT_FOUND );
}
args[0] = (DWORD_PTR)main_exe_name;
FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
NULL, error, 0, msgW, ARRAY_SIZE( msgW ), (__ms_va_list *)args );
WideCharToMultiByte( CP_UNIXCP, 0, msgW, -1, msg, sizeof(msg), NULL, NULL );
MESSAGE( "wine: %s", msg );
ExitProcess( error );
}
if (!params->CurrentDirectory.Handle) chdir("/"); /* avoid locking removable devices */
return start_process_wrapper;
@ -1175,54 +1126,6 @@ static const char *get_alternate_loader( char **ret_env )
return loader;
}
#ifdef __APPLE__
/***********************************************************************
* terminate_main_thread
*
* On some versions of Mac OS X, the execve system call fails with
* ENOTSUP if the process has multiple threads. Wine is always multi-
* threaded on Mac OS X because it specifically reserves the main thread
* for use by the system frameworks (see apple_main_thread() in
* libs/wine/loader.c). So, when we need to exec without first forking,
* we need to terminate the main thread first. We do this by installing
* a custom run loop source onto the main run loop and signaling it.
* The source's "perform" callback is pthread_exit and it will be
* executed on the main thread, terminating it.
*
* Returns TRUE if there's still hope the main thread has terminated or
* will soon. Return FALSE if we've given up.
*/
static BOOL terminate_main_thread(void)
{
static int delayms;
if (!delayms)
{
CFRunLoopSourceContext source_context = { 0 };
CFRunLoopSourceRef source;
source_context.perform = pthread_exit;
if (!(source = CFRunLoopSourceCreate( NULL, 0, &source_context )))
return FALSE;
CFRunLoopAddSource( CFRunLoopGetMain(), source, kCFRunLoopCommonModes );
CFRunLoopSourceSignal( source );
CFRunLoopWakeUp( CFRunLoopGetMain() );
CFRelease( source );
delayms = 20;
}
if (delayms > 1000)
return FALSE;
usleep(delayms * 1000);
delayms *= 2;
return TRUE;
}
#endif
/***********************************************************************
* set_stdio_fd
@ -1319,49 +1222,6 @@ static pid_t spawn_loader( const RTL_USER_PROCESS_PARAMETERS *params, int socket
return pid;
}
/***********************************************************************
* exec_loader
*/
static NTSTATUS exec_loader( const RTL_USER_PROCESS_PARAMETERS *params, int socketfd,
const pe_image_info_t *pe_info )
{
char *wineloader = NULL;
const char *loader = NULL;
char **argv;
char preloader_reserve[64], socket_env[64];
ULONGLONG res_start = pe_info->base;
ULONGLONG res_end = pe_info->base + pe_info->map_size;
if (!(argv = build_argv( &params->CommandLine, 1 ))) return STATUS_NO_MEMORY;
if (!is_win64 ^ !is_64bit_arch( pe_info->cpu ))
loader = get_alternate_loader( &wineloader );
/* Reset signals that we previously set to SIG_IGN */
signal( SIGPIPE, SIG_DFL );
sprintf( socket_env, "WINESERVERSOCKET=%u", socketfd );
sprintf( preloader_reserve, "WINEPRELOADRESERVE=%x%08x-%x%08x",
(ULONG)(res_start >> 32), (ULONG)res_start, (ULONG)(res_end >> 32), (ULONG)res_end );
putenv( preloader_reserve );
putenv( socket_env );
if (wineloader) putenv( wineloader );
do
{
wine_exec_wine_binary( loader, argv, getenv("WINELOADER") );
}
#ifdef __APPLE__
while (errno == ENOTSUP && terminate_main_thread());
#else
while (0);
#endif
HeapFree( GetProcessHeap(), 0, wineloader );
HeapFree( GetProcessHeap(), 0, argv );
return STATUS_INVALID_IMAGE_FORMAT;
}
/* creates a struct security_descriptor and contained information in one contiguous piece of memory */
static NTSTATUS alloc_object_attributes( const SECURITY_ATTRIBUTES *attr, struct object_attributes **ret,
@ -1425,59 +1285,6 @@ static NTSTATUS alloc_object_attributes( const SECURITY_ATTRIBUTES *attr, struct
return STATUS_SUCCESS;
}
/***********************************************************************
* replace_process
*
* Replace the existing process by exec'ing a new one.
*/
static BOOL replace_process( const RTL_USER_PROCESS_PARAMETERS *params, const pe_image_info_t *pe_info )
{
NTSTATUS status;
int socketfd[2];
if (socketpair( PF_UNIX, SOCK_STREAM, 0, socketfd ) == -1)
{
SetLastError( ERROR_TOO_MANY_OPEN_FILES );
return FALSE;
}
#ifdef SO_PASSCRED
else
{
int enable = 1;
setsockopt( socketfd[0], SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable) );
}
#endif
wine_server_send_fd( socketfd[1] );
close( socketfd[1] );
SERVER_START_REQ( exec_process )
{
req->socket_fd = socketfd[1];
req->cpu = pe_info->cpu;
status = wine_server_call( req );
}
SERVER_END_REQ;
switch (status)
{
case STATUS_INVALID_IMAGE_WIN_64:
ERR( "64-bit application %s not supported in 32-bit prefix\n",
debugstr_w( params->ImagePathName.Buffer ));
break;
case STATUS_INVALID_IMAGE_FORMAT:
ERR( "%s not supported on this installation (%s binary)\n",
debugstr_w( params->ImagePathName.Buffer ), cpu_names[pe_info->cpu] );
break;
case STATUS_SUCCESS:
status = exec_loader( params, socketfd[0], pe_info );
break;
}
close( socketfd[0] );
SetLastError( RtlNtStatusToDosError( status ));
return FALSE;
}
/***********************************************************************
* create_process
*
@ -1870,7 +1677,7 @@ BOOL WINAPI CreateProcessInternalW( HANDLE token, LPCWSTR app_name, LPWSTR cmd_l
RTL_USER_PROCESS_PARAMETERS *params = NULL;
pe_image_info_t pe_info;
enum binary_type type;
BOOL is_64bit;
BOOL is_64bit = FALSE;
/* Process the AppName and/or CmdLine to get module name and path */
@ -2094,59 +1901,6 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessAsUserW( HANDLE token, LPCWSTR app_na
}
/**********************************************************************
* exec_process
*/
static void exec_process( LPCWSTR name )
{
HANDLE hFile;
WCHAR *p;
STARTUPINFOW startup_info = { sizeof(startup_info) };
RTL_USER_PROCESS_PARAMETERS *params, *new_params;
pe_image_info_t pe_info;
BOOL is_64bit;
hFile = open_exe_file( name, &is_64bit );
if (!hFile || hFile == INVALID_HANDLE_VALUE) return;
if (!(params = create_process_params( name, GetCommandLineW(), NULL, NULL, 0, &startup_info )))
return;
/* Determine executable type */
switch (get_binary_info( hFile, &pe_info ))
{
case BINARY_PE:
if (pe_info.image_charact & IMAGE_FILE_DLL) break;
TRACE( "starting %s as Win%d binary (%s-%s, %s)\n",
debugstr_w(name), is_64bit_arch(pe_info.cpu) ? 64 : 32,
wine_dbgstr_longlong(pe_info.base), wine_dbgstr_longlong(pe_info.base + pe_info.map_size),
cpu_names[pe_info.cpu] );
replace_process( params, &pe_info );
break;
case BINARY_UNIX_LIB:
TRACE( "%s is a Unix library, starting as Winelib app\n", debugstr_w(name) );
replace_process( params, &pe_info );
break;
case BINARY_UNKNOWN:
/* check for .com or .pif extension */
if (!(p = strrchrW( name, '.' ))) break;
if (strcmpiW( p, comW ) && strcmpiW( p, pifW )) break;
/* fall through */
case BINARY_WIN16:
TRACE( "starting %s as Win16/DOS binary\n", debugstr_w(name) );
if (!(new_params = get_vdm_params( params, &pe_info ))) break;
replace_process( new_params, &pe_info );
RtlDestroyProcessParameters( new_params );
break;
default:
break;
}
CloseHandle( hFile );
RtlDestroyProcessParameters( params );
}
/***********************************************************************
* wait_input_idle
*

View File

@ -4286,23 +4286,42 @@ void __wine_process_init(void)
{
peb->ImageBaseAddress = wm->ldr.BaseAddress;
TRACE( "main exe loaded %s at %p\n", debugstr_us(&params->ImagePathName), peb->ImageBaseAddress );
if (wm->ldr.Flags & LDR_IMAGE_IS_DLL)
{
MESSAGE( "wine: %s is a dll, not an executable\n", debugstr_w(wm->ldr.FullDllName.Buffer) );
NtTerminateProcess( GetCurrentProcess(), STATUS_INVALID_IMAGE_FORMAT );
}
}
else if (info_size)
else
{
WARN( "failed to load %s status %x\n", debugstr_us(&params->ImagePathName), status );
if (!info_size) status = restart_process( params, status );
switch (status)
{
case STATUS_INVALID_IMAGE_WIN_64:
ERR( "%s 64-bit application not supported in 32-bit prefix\n",
debugstr_us(&params->ImagePathName) );
break;
case STATUS_INVALID_IMAGE_WIN_16:
case STATUS_INVALID_IMAGE_NE_FORMAT:
case STATUS_INVALID_IMAGE_PROTECT:
ERR( "%s 16-bit application not supported on this system\n",
debugstr_us(&params->ImagePathName) );
break;
case STATUS_INVALID_IMAGE_FORMAT:
ERR( "%s not supported on this system\n", debugstr_us(&params->ImagePathName) );
break;
case STATUS_DLL_NOT_FOUND:
ERR( "%s not found\n", debugstr_us(&params->ImagePathName) );
break;
default:
ERR( "failed to load %s, error %x\n", debugstr_us(&params->ImagePathName), status );
break;
}
NtTerminateProcess( GetCurrentProcess(), status );
}
kernel32_start_process = init_func();
wm = get_modref( peb->ImageBaseAddress );
assert( wm );
if (wm->ldr.Flags & LDR_IMAGE_IS_DLL)
{
MESSAGE( "wine: %s is a dll, not an executable\n", debugstr_w(wm->ldr.FullDllName.Buffer) );
NtTerminateProcess( GetCurrentProcess(), STATUS_INVALID_IMAGE_FORMAT );
}
virtual_set_large_address_space();
/* the main exe needs to be the first in the load order list */

View File

@ -86,6 +86,7 @@ extern void virtual_init_threading(void) DECLSPEC_HIDDEN;
extern void fill_cpu_info(void) DECLSPEC_HIDDEN;
extern void heap_set_debug_flags( HANDLE handle ) DECLSPEC_HIDDEN;
extern void init_user_process_params( SIZE_T data_size ) DECLSPEC_HIDDEN;
extern NTSTATUS restart_process( RTL_USER_PROCESS_PARAMETERS *params, NTSTATUS status ) DECLSPEC_HIDDEN;
/* server support */
extern timeout_t server_start_time DECLSPEC_HIDDEN;

View File

@ -39,6 +39,10 @@
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <pthread.h>
#endif
#include "ntstatus.h"
#define WIN32_NO_STATUS
@ -1036,6 +1040,55 @@ static const char *get_alternate_loader( char **ret_env )
}
#ifdef __APPLE__
/***********************************************************************
* terminate_main_thread
*
* On some versions of Mac OS X, the execve system call fails with
* ENOTSUP if the process has multiple threads. Wine is always multi-
* threaded on Mac OS X because it specifically reserves the main thread
* for use by the system frameworks (see apple_main_thread() in
* libs/wine/loader.c). So, when we need to exec without first forking,
* we need to terminate the main thread first. We do this by installing
* a custom run loop source onto the main run loop and signaling it.
* The source's "perform" callback is pthread_exit and it will be
* executed on the main thread, terminating it.
*
* Returns TRUE if there's still hope the main thread has terminated or
* will soon. Return FALSE if we've given up.
*/
static BOOL terminate_main_thread(void)
{
static int delayms;
if (!delayms)
{
CFRunLoopSourceContext source_context = { 0 };
CFRunLoopSourceRef source;
source_context.perform = pthread_exit;
if (!(source = CFRunLoopSourceCreate( NULL, 0, &source_context )))
return FALSE;
CFRunLoopAddSource( CFRunLoopGetMain(), source, kCFRunLoopCommonModes );
CFRunLoopSourceSignal( source );
CFRunLoopWakeUp( CFRunLoopGetMain() );
CFRelease( source );
delayms = 20;
}
if (delayms > 1000)
return FALSE;
usleep(delayms * 1000);
delayms *= 2;
return TRUE;
}
#endif
/***********************************************************************
* set_stdio_fd
*/
@ -1135,6 +1188,50 @@ static NTSTATUS spawn_loader( const RTL_USER_PROCESS_PARAMETERS *params, int soc
}
/***********************************************************************
* exec_loader
*/
static NTSTATUS exec_loader( const UNICODE_STRING *cmdline, int socketfd, const pe_image_info_t *pe_info )
{
char *wineloader = NULL;
const char *loader = NULL;
char **argv;
char preloader_reserve[64], socket_env[64];
ULONGLONG res_start = pe_info->base;
ULONGLONG res_end = pe_info->base + pe_info->map_size;
if (!(argv = build_argv( cmdline, 1 ))) return STATUS_NO_MEMORY;
if (!is_win64 ^ !is_64bit_arch( pe_info->cpu ))
loader = get_alternate_loader( &wineloader );
/* Reset signals that we previously set to SIG_IGN */
signal( SIGPIPE, SIG_DFL );
sprintf( socket_env, "WINESERVERSOCKET=%u", socketfd );
sprintf( preloader_reserve, "WINEPRELOADRESERVE=%x%08x-%x%08x",
(ULONG)(res_start >> 32), (ULONG)res_start, (ULONG)(res_end >> 32), (ULONG)res_end );
putenv( preloader_reserve );
putenv( socket_env );
if (wineloader) putenv( wineloader );
do
{
wine_exec_wine_binary( loader, argv, getenv("WINELOADER") );
}
#ifdef __APPLE__
while (errno == ENOTSUP && terminate_main_thread());
#else
while (0);
#endif
RtlFreeHeap( GetProcessHeap(), 0, wineloader );
RtlFreeHeap( GetProcessHeap(), 0, argv );
return STATUS_INVALID_IMAGE_FORMAT;
}
/***************************************************************************
* is_builtin_path
*/
@ -1367,6 +1464,88 @@ static char *get_unix_curdir( const RTL_USER_PROCESS_PARAMETERS *params )
}
/***********************************************************************
* restart_process
*/
NTSTATUS restart_process( RTL_USER_PROCESS_PARAMETERS *params, NTSTATUS status )
{
static const WCHAR argsW[] = {'%','s','%','s',' ','-','-','a','p','p','-','n','a','m','e',' ','"','%','s','"',' ','%','s',0};
static const WCHAR winevdm[] = {'w','i','n','e','v','d','m','.','e','x','e',0};
static const WCHAR comW[] = {'.','c','o','m',0};
static const WCHAR pifW[] = {'.','p','i','f',0};
int socketfd[2];
WCHAR *p, *cmdline;
UNICODE_STRING strW;
pe_image_info_t pe_info;
HANDLE handle;
/* check for .com or .pif extension */
if (status == STATUS_INVALID_IMAGE_NOT_MZ &&
(p = strrchrW( params->ImagePathName.Buffer, '.' )) &&
(!strcmpiW( p, comW ) || !strcmpiW( p, pifW )))
status = STATUS_INVALID_IMAGE_WIN_16;
switch (status)
{
case STATUS_CONFLICTING_ADDRESSES:
case STATUS_NO_MEMORY:
case STATUS_INVALID_IMAGE_FORMAT:
case STATUS_INVALID_IMAGE_NOT_MZ:
if (getenv( "WINEPRELOADRESERVE" ))
return status;
if ((status = RtlDosPathNameToNtPathName_U_WithStatus( params->ImagePathName.Buffer, &strW,
NULL, NULL )))
return status;
if ((status = get_pe_file_info( &strW, OBJ_CASE_INSENSITIVE, &handle, &pe_info )))
return status;
strW = params->CommandLine;
break;
case STATUS_INVALID_IMAGE_WIN_16:
case STATUS_INVALID_IMAGE_NE_FORMAT:
case STATUS_INVALID_IMAGE_PROTECT:
cmdline = RtlAllocateHeap( GetProcessHeap(), 0,
(strlenW(system_dir) + strlenW(winevdm) + 16 +
strlenW(params->ImagePathName.Buffer) +
strlenW(params->CommandLine.Buffer)) * sizeof(WCHAR));
if (!cmdline) return STATUS_NO_MEMORY;
sprintfW( cmdline, argsW, (is_win64 || is_wow64) ? syswow64_dir : system_dir,
winevdm, params->ImagePathName.Buffer, params->CommandLine.Buffer );
RtlInitUnicodeString( &strW, cmdline );
memset( &pe_info, 0, sizeof(pe_info) );
pe_info.cpu = CPU_x86;
break;
default:
return status;
}
/* exec the new process */
if (socketpair( PF_UNIX, SOCK_STREAM, 0, socketfd ) == -1) return STATUS_TOO_MANY_OPENED_FILES;
#ifdef SO_PASSCRED
else
{
int enable = 1;
setsockopt( socketfd[0], SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable) );
}
#endif
wine_server_send_fd( socketfd[1] );
close( socketfd[1] );
SERVER_START_REQ( exec_process )
{
req->socket_fd = socketfd[1];
req->cpu = pe_info.cpu;
status = wine_server_call( req );
}
SERVER_END_REQ;
if (!status) status = exec_loader( &strW, socketfd[0], &pe_info );
close( socketfd[0] );
return status;
}
/**********************************************************************
* RtlCreateUserProcess (NTDLL.@)
*/