ntdll: Add a helper to exec wineloader.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2020-04-28 11:28:29 +02:00
parent 68a425b19c
commit 9fd2330a96
3 changed files with 151 additions and 118 deletions

View File

@ -113,6 +113,7 @@ extern size_t dll_path_maxlen DECLSPEC_HIDDEN;
extern timeout_t server_start_time DECLSPEC_HIDDEN;
extern unsigned int server_cpus DECLSPEC_HIDDEN;
extern BOOL is_wow64 DECLSPEC_HIDDEN;
extern NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_info ) DECLSPEC_HIDDEN;
extern void server_init_process(void) DECLSPEC_HIDDEN;
extern void server_init_process_done(void) DECLSPEC_HIDDEN;
extern size_t server_init_thread( void *entry_point, BOOL *suspend ) DECLSPEC_HIDDEN;

View File

@ -51,7 +51,6 @@
#include "winternl.h"
#include "ntdll_misc.h"
#include "wine/exception.h"
#include "wine/library.h"
#include "wine/server.h"
#ifdef HAVE_MACH_MACH_H
@ -68,10 +67,6 @@ static const BOOL is_win64 = (sizeof(void *) > sizeof(int));
static const char * const cpu_names[] = { "x86", "x86_64", "PowerPC", "ARM", "ARM64" };
static inline BOOL is_64bit_arch( client_cpu_t cpu )
{
return (cpu == CPU_x86_64 || cpu == CPU_ARM64);
}
/*
* Process object
@ -995,51 +990,6 @@ static startup_info_t *create_startup_info( const RTL_USER_PROCESS_PARAMETERS *p
}
/***********************************************************************
* get_alternate_loader
*
* Get the name of the alternate (32 or 64 bit) Wine loader.
*/
static const char *get_alternate_loader( char **ret_env )
{
char *env;
const char *loader = NULL;
const char *loader_env = getenv( "WINELOADER" );
*ret_env = NULL;
if (build_dir) loader = is_win64 ? "loader/wine" : "loader/wine64";
if (loader_env)
{
int len = strlen( loader_env );
if (!is_win64)
{
if (!(env = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("WINELOADER=") + len + 2 ))) return NULL;
strcpy( env, "WINELOADER=" );
strcat( env, loader_env );
strcat( env, "64" );
}
else
{
if (!(env = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("WINELOADER=") + len ))) return NULL;
strcpy( env, "WINELOADER=" );
strcat( env, loader_env );
len += sizeof("WINELOADER=") - 1;
if (!strcmp( env + len - 2, "64" )) env[len - 2] = 0;
}
if (!loader)
{
if ((loader = strrchr( env, '/' ))) loader++;
else loader = env;
}
*ret_env = env;
}
if (!loader) loader = is_win64 ? "wine" : "wine64";
return loader;
}
#ifdef __APPLE__
/***********************************************************************
* terminate_main_thread
@ -1117,15 +1067,10 @@ static NTSTATUS spawn_loader( const RTL_USER_PROCESS_PARAMETERS *params, int soc
{
pid_t pid;
int stdin_fd = -1, stdout_fd = -1;
char *wineloader = NULL;
const char *loader = NULL;
char **argv;
NTSTATUS status = STATUS_SUCCESS;
argv = build_argv( &params->CommandLine, 1 );
if (!is_win64 ^ !is_64bit_arch( pe_info->cpu ))
loader = get_alternate_loader( &wineloader );
argv = build_argv( &params->CommandLine, 2 );
wine_server_handle_to_fd( params->hStdInput, FILE_READ_DATA, &stdin_fd, NULL );
wine_server_handle_to_fd( params->hStdOutput, FILE_WRITE_DATA, &stdout_fd, NULL );
@ -1134,10 +1079,6 @@ static NTSTATUS spawn_loader( const RTL_USER_PROCESS_PARAMETERS *params, int soc
{
if (!(pid = fork())) /* grandchild */
{
char preloader_reserve[64], socket_env[64];
ULONGLONG res_start = pe_info->base;
ULONGLONG res_end = pe_info->base + pe_info->map_size;
if (params->ConsoleFlags ||
params->ConsoleHandle == (HANDLE)1 /* KERNEL32_CONSOLE_ALLOC */ ||
(params->hStdInput == INVALID_HANDLE_VALUE && params->hStdOutput == INVALID_HANDLE_VALUE))
@ -1150,20 +1091,10 @@ static NTSTATUS spawn_loader( const RTL_USER_PROCESS_PARAMETERS *params, int soc
if (stdin_fd != -1) close( stdin_fd );
if (stdout_fd != -1) close( stdout_fd );
/* 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 (winedebug) putenv( winedebug );
if (wineloader) putenv( wineloader );
if (unixdir) chdir( unixdir );
if (argv) wine_exec_wine_binary( loader, argv, getenv("WINELOADER") );
exec_wineloader( argv, socketfd, pe_info );
_exit(1);
}
@ -1182,56 +1113,11 @@ static NTSTATUS spawn_loader( const RTL_USER_PROCESS_PARAMETERS *params, int soc
if (stdin_fd != -1) close( stdin_fd );
if (stdout_fd != -1) close( stdout_fd );
RtlFreeHeap( GetProcessHeap(), 0, wineloader );
RtlFreeHeap( GetProcessHeap(), 0, argv );
return status;
}
/***********************************************************************
* 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
*/
@ -1636,7 +1522,24 @@ NTSTATUS restart_process( RTL_USER_PROCESS_PARAMETERS *params, NTSTATUS status )
}
SERVER_END_REQ;
if (!status) status = exec_loader( &strW, socketfd[0], &pe_info );
if (!status)
{
char **argv = build_argv( &strW, 2 );
if (argv)
{
do
{
status = exec_wineloader( argv, socketfd[0], &pe_info );
}
#ifdef __APPLE__
while (errno == ENOTSUP && terminate_main_thread());
#else
while (0);
#endif
RtlFreeHeap( GetProcessHeap(), 0, argv );
}
else status = STATUS_NO_MEMORY;
}
close( socketfd[0] );
return status;
}

View File

@ -75,6 +75,13 @@
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef __APPLE__
#include <crt_externs.h>
#include <spawn.h>
#ifndef _POSIX_SPAWN_DISABLE_ASLR
#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
#endif
#endif
#include "ntstatus.h"
#define WIN32_NO_STATUS
@ -113,6 +120,12 @@ static const enum cpu_type client_cpu = CPU_ARM64;
#error Unsupported CPU
#endif
#if defined(linux) || defined(__APPLE__)
static const BOOL use_preloader = TRUE;
#else
static const BOOL use_preloader = FALSE;
#endif
static const BOOL is_win64 = (sizeof(void *) > sizeof(int));
const char *build_dir = NULL;
@ -122,6 +135,7 @@ const char **dll_paths = NULL;
size_t dll_path_maxlen = 0;
static const char *server_dir;
static const char *bin_dir;
static const char *argv0;
unsigned int server_cpus = 0;
BOOL is_wow64 = FALSE;
@ -1218,6 +1232,119 @@ int server_pipe( int fd[2] )
}
/***********************************************************************
* preloader_exec
*/
static void preloader_exec( char **argv )
{
if (use_preloader)
{
static const char *preloader = "wine-preloader";
char *p;
if (!(p = strrchr( argv[1], '/' ))) p = argv[1];
else p++;
if (strlen(p) > 2 && !strcmp( p + strlen(p) - 2, "64" )) preloader = "wine64-preloader";
argv[0] = malloc( p - argv[1] + strlen(preloader) + 1 );
memcpy( argv[0], argv[1], p - argv[1] );
strcpy( argv[0] + (p - argv[1]), preloader );
#ifdef __APPLE__
{
posix_spawnattr_t attr;
posix_spawnattr_init( &attr );
posix_spawnattr_setflags( &attr, POSIX_SPAWN_SETEXEC | _POSIX_SPAWN_DISABLE_ASLR );
posix_spawn( NULL, argv[0], NULL, &attr, argv, *_NSGetEnviron() );
posix_spawnattr_destroy( &attr );
}
#endif
execv( argv[0], argv );
free( argv[0] );
}
execv( argv[1], argv + 1 );
}
/***********************************************************************
* exec_wineloader
*
* argv[0] and argv[1] must be reserved for the preloader and loader respectively.
*/
NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_info )
{
const int is_child_64bit = (pe_info->cpu == CPU_x86_64 || pe_info->cpu == CPU_ARM64);
const char *path, *loader = argv0;
const char *loader_env = getenv( "WINELOADER" );
char *p, preloader_reserve[64], socket_env[64];
ULONGLONG res_start = pe_info->base;
ULONGLONG res_end = pe_info->base + pe_info->map_size;
if (!is_win64 ^ !is_child_64bit)
{
/* remap WINELOADER to the alternate 32/64-bit version if necessary */
if (loader_env)
{
int len = strlen( loader_env );
char *env = malloc( sizeof("WINELOADER=") + len + 2 );
if (!env) return STATUS_NO_MEMORY;
strcpy( env, "WINELOADER=" );
strcat( env, loader_env );
if (is_child_64bit)
{
strcat( env, "64" );
}
else
{
len += sizeof("WINELOADER=") - 1;
if (!strcmp( env + len - 2, "64" )) env[len - 2] = 0;
}
loader = env;
putenv( env );
}
else loader = is_child_64bit ? "wine64" : "wine";
}
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 (build_dir)
{
argv[1] = build_path( build_dir, is_child_64bit ? "loader/wine64" : "loader/wine" );
preloader_exec( argv );
return STATUS_INVALID_IMAGE_FORMAT;
}
if ((p = strrchr( loader, '/' ))) loader = p + 1;
argv[1] = build_path( bin_dir, loader );
preloader_exec( argv );
argv[1] = getenv( "WINELOADER" );
if (argv[1]) preloader_exec( argv );
if ((path = getenv( "PATH" )))
{
for (p = strtok( strdup( path ), ":" ); p; p = strtok( NULL, ":" ))
{
argv[1] = build_path( p, loader );
preloader_exec( argv );
}
}
argv[1] = build_path( BINDIR, loader );
preloader_exec( argv );
return STATUS_INVALID_IMAGE_FORMAT;
}
/***********************************************************************
* exec_wineserver
*
@ -1402,12 +1529,14 @@ void init_paths(void)
dll_dir = realpath_dirname( info.dli_fname );
#endif
argv0 = strdup( __wine_main_argv[0] );
#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
bin_dir = realpath_dirname( "/proc/self/exe" );
#elif defined (__FreeBSD__) || defined(__DragonFly__)
bin_dir = realpath_dirname( "/proc/curproc/file" );
#else
bin_dir = realpath_dirname( __wine_main_argv[0] );
bin_dir = realpath_dirname( argv0 );
#endif
if (dll_dir) build_dir = remove_tail( dll_dir, "/dlls/ntdll" );