Sweden-Number/dlls/ntdll/unix/loader.c

2540 lines
81 KiB
C

/*
* Unix interface for loader functions
*
* Copyright (C) 2020 Alexandre Julliard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#if 0
#pragma makedep unix
#endif
#include "config.h"
#include "wine/port.h"
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <signal.h>
#ifdef HAVE_LINK_H
# include <link.h>
#endif
#ifdef HAVE_PWD_H
# include <pwd.h>
#endif
#ifdef HAVE_ELF_H
# include <elf.h>
#endif
#ifdef HAVE_LINK_H
# include <link.h>
#endif
#ifdef HAVE_SYS_AUXV_H
# include <sys/auxv.h>
#endif
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef __APPLE__
# include <CoreFoundation/CoreFoundation.h>
# define LoadResource MacLoadResource
# define GetCurrentThread MacGetCurrentThread
# include <CoreServices/CoreServices.h>
# undef LoadResource
# undef GetCurrentThread
# include <pthread.h>
# include <mach/mach.h>
# include <mach/mach_error.h>
# include <mach-o/getsect.h>
# include <crt_externs.h>
# include <spawn.h>
# ifndef _POSIX_SPAWN_DISABLE_ASLR
# define _POSIX_SPAWN_DISABLE_ASLR 0x0100
# endif
#endif
#ifdef __ANDROID__
# include <jni.h>
#endif
#include "ntstatus.h"
#define WIN32_NO_STATUS
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winnt.h"
#include "winbase.h"
#include "winnls.h"
#include "winioctl.h"
#include "winternl.h"
#include "unix_private.h"
#include "wine/list.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(module);
#ifdef __i386__
static const char so_dir[] = "/i386-unix";
#elif defined(__x86_64__)
static const char so_dir[] = "/x86_64-unix";
#elif defined(__arm__)
static const char so_dir[] = "/arm-unix";
#elif defined(__aarch64__)
static const char so_dir[] = "/aarch64-unix";
#else
static const char so_dir[] = "";
#endif
void (WINAPI *pDbgUiRemoteBreakin)( void *arg ) = NULL;
NTSTATUS (WINAPI *pKiRaiseUserExceptionDispatcher)(void) = NULL;
NTSTATUS (WINAPI *pKiUserExceptionDispatcher)(EXCEPTION_RECORD*,CONTEXT*) = NULL;
void (WINAPI *pKiUserApcDispatcher)(CONTEXT*,ULONG_PTR,ULONG_PTR,ULONG_PTR,PNTAPCFUNC) = NULL;
void (WINAPI *pKiUserCallbackDispatcher)(ULONG,void*,ULONG) = NULL;
void (WINAPI *pLdrInitializeThunk)(CONTEXT*,void**,ULONG_PTR,ULONG_PTR) = NULL;
void (WINAPI *pRtlUserThreadStart)( PRTL_THREAD_START_ROUTINE entry, void *arg ) = NULL;
void (WINAPI *p__wine_ctrl_routine)(void*);
SYSTEM_DLL_INIT_BLOCK *pLdrSystemDllInitBlock = NULL;
static NTSTATUS (CDECL *p__wine_set_unix_funcs)( int version, const struct unix_funcs *funcs );
static void *p__wine_syscall_dispatcher;
static void * const syscalls[] =
{
NtAcceptConnectPort,
NtAccessCheck,
NtAccessCheckAndAuditAlarm,
NtAddAtom,
NtAdjustGroupsToken,
NtAdjustPrivilegesToken,
NtAlertResumeThread,
NtAlertThread,
NtAllocateLocallyUniqueId,
NtAllocateUuids,
NtAllocateVirtualMemory,
NtAllocateVirtualMemoryEx,
NtAreMappedFilesTheSame,
NtAssignProcessToJobObject,
NtCallbackReturn,
NtCancelIoFile,
NtCancelIoFileEx,
NtCancelTimer,
NtClearEvent,
NtClose,
NtCompleteConnectPort,
NtConnectPort,
NtContinue,
NtCreateDebugObject,
NtCreateDirectoryObject,
NtCreateEvent,
NtCreateFile,
NtCreateIoCompletion,
NtCreateJobObject,
NtCreateKey,
NtCreateKeyTransacted,
NtCreateKeyedEvent,
NtCreateLowBoxToken,
NtCreateMailslotFile,
NtCreateMutant,
NtCreateNamedPipeFile,
NtCreatePagingFile,
NtCreatePort,
NtCreateSection,
NtCreateSemaphore,
NtCreateSymbolicLinkObject,
NtCreateThread,
NtCreateThreadEx,
NtCreateTimer,
NtCreateUserProcess,
NtDebugActiveProcess,
NtDebugContinue,
NtDelayExecution,
NtDeleteAtom,
NtDeleteFile,
NtDeleteKey,
NtDeleteValueKey,
NtDeviceIoControlFile,
NtDisplayString,
NtDuplicateObject,
NtDuplicateToken,
NtEnumerateKey,
NtEnumerateValueKey,
NtFilterToken,
NtFindAtom,
NtFlushBuffersFile,
NtFlushInstructionCache,
NtFlushKey,
NtFlushProcessWriteBuffers,
NtFlushVirtualMemory,
NtFreeVirtualMemory,
NtFsControlFile,
NtGetContextThread,
NtGetCurrentProcessorNumber,
NtGetNextThread,
NtGetNlsSectionPtr,
NtGetWriteWatch,
NtImpersonateAnonymousToken,
NtInitiatePowerAction ,
NtIsProcessInJob,
NtListenPort,
NtLoadDriver,
NtLoadKey,
NtLoadKey2,
NtLockFile,
NtLockVirtualMemory,
NtMakeTemporaryObject,
NtMapViewOfSection,
NtNotifyChangeDirectoryFile,
NtNotifyChangeKey,
NtNotifyChangeMultipleKeys,
NtOpenDirectoryObject,
NtOpenEvent,
NtOpenFile,
NtOpenIoCompletion,
NtOpenJobObject,
NtOpenKey,
NtOpenKeyEx,
NtOpenKeyTransacted,
NtOpenKeyTransactedEx,
NtOpenKeyedEvent,
NtOpenMutant,
NtOpenProcess,
NtOpenProcessToken,
NtOpenProcessTokenEx,
NtOpenSection,
NtOpenSemaphore,
NtOpenSymbolicLinkObject ,
NtOpenThread,
NtOpenThreadToken,
NtOpenThreadTokenEx,
NtOpenTimer,
NtPowerInformation,
NtPrivilegeCheck,
NtProtectVirtualMemory,
NtPulseEvent,
NtQueryAttributesFile,
NtQueryDefaultLocale,
NtQueryDefaultUILanguage,
NtQueryDirectoryFile,
NtQueryDirectoryObject,
NtQueryEaFile,
NtQueryEvent,
NtQueryFullAttributesFile,
NtQueryInformationAtom,
NtQueryInformationFile,
NtQueryInformationJobObject,
NtQueryInformationProcess,
NtQueryInformationThread,
NtQueryInformationToken,
NtQueryInstallUILanguage,
NtQueryIoCompletion,
NtQueryKey,
NtQueryLicenseValue,
NtQueryMultipleValueKey,
NtQueryMutant,
NtQueryObject,
NtQueryPerformanceCounter,
NtQuerySection,
NtQuerySecurityObject,
NtQuerySemaphore ,
NtQuerySymbolicLinkObject,
NtQuerySystemEnvironmentValue,
NtQuerySystemEnvironmentValueEx,
NtQuerySystemInformation,
NtQuerySystemInformationEx,
NtQuerySystemTime,
NtQueryTimer,
NtQueryTimerResolution,
NtQueryValueKey,
NtQueryVirtualMemory,
NtQueryVolumeInformationFile,
NtQueueApcThread,
NtRaiseException,
NtRaiseHardError,
NtReadFile,
NtReadFileScatter,
NtReadVirtualMemory,
NtRegisterThreadTerminatePort,
NtReleaseKeyedEvent,
NtReleaseMutant,
NtReleaseSemaphore,
NtRemoveIoCompletion,
NtRemoveIoCompletionEx,
NtRemoveProcessDebug,
NtRenameKey,
NtReplaceKey,
NtReplyWaitReceivePort,
NtRequestWaitReplyPort,
NtResetEvent,
NtResetWriteWatch,
NtRestoreKey,
NtResumeProcess,
NtResumeThread,
NtSaveKey,
NtSecureConnectPort,
NtSetContextThread,
NtSetDefaultLocale,
NtSetDefaultUILanguage,
NtSetEaFile,
NtSetEvent,
NtSetInformationDebugObject,
NtSetInformationFile,
NtSetInformationJobObject,
NtSetInformationKey,
NtSetInformationObject,
NtSetInformationProcess,
NtSetInformationThread,
NtSetInformationToken,
NtSetIntervalProfile,
NtSetIoCompletion,
NtSetLdtEntries,
NtSetSecurityObject,
NtSetSystemInformation,
NtSetSystemTime,
NtSetThreadExecutionState,
NtSetTimer,
NtSetTimerResolution,
NtSetValueKey,
NtSetVolumeInformationFile,
NtShutdownSystem,
NtSignalAndWaitForSingleObject,
NtSuspendProcess,
NtSuspendThread,
NtSystemDebugControl,
NtTerminateJobObject,
NtTerminateProcess,
NtTerminateThread,
NtTestAlert,
NtTraceControl,
NtUnloadDriver,
NtUnloadKey,
NtUnlockFile,
NtUnlockVirtualMemory,
NtUnmapViewOfSection,
NtWaitForDebugEvent,
NtWaitForKeyedEvent,
NtWaitForMultipleObjects,
NtWaitForSingleObject,
#ifndef _WIN64
NtWow64AllocateVirtualMemory64,
NtWow64GetNativeSystemInformation,
NtWow64ReadVirtualMemory64,
NtWow64WriteVirtualMemory64,
#endif
NtWriteFile,
NtWriteFileGather,
NtWriteVirtualMemory,
NtYieldExecution,
__wine_dbg_write,
__wine_unix_call,
wine_nt_to_unix_file_name,
wine_server_call,
wine_server_fd_to_handle,
wine_server_handle_to_fd,
wine_unix_to_nt_file_name,
};
static BYTE syscall_args[ARRAY_SIZE(syscalls)];
SYSTEM_SERVICE_TABLE KeServiceDescriptorTable[4];
#ifdef __GNUC__
static void fatal_error( const char *err, ... ) __attribute__((noreturn, format(printf,1,2)));
#endif
#if defined(linux) || defined(__APPLE__)
static const BOOL use_preloader = TRUE;
#else
static const BOOL use_preloader = FALSE;
#endif
static char *argv0;
static const char *bin_dir;
static const char *dll_dir;
static const char *ntdll_dir;
static SIZE_T dll_path_maxlen;
static int *p___wine_main_argc;
static char ***p___wine_main_argv;
static WCHAR ***p___wine_main_wargv;
const char *home_dir = NULL;
const char *data_dir = NULL;
const char *build_dir = NULL;
const char *config_dir = NULL;
const char **dll_paths = NULL;
const char *user_name = NULL;
SECTION_IMAGE_INFORMATION main_image_info = { NULL };
static HMODULE ntdll_module;
static const IMAGE_EXPORT_DIRECTORY *ntdll_exports;
/* adjust an array of pointers to make them into RVAs */
static inline void fixup_rva_ptrs( void *array, BYTE *base, unsigned int count )
{
BYTE **src = array;
DWORD *dst = array;
for ( ; count; count--, src++, dst++) *dst = *src ? *src - base : 0;
}
/* fixup an array of RVAs by adding the specified delta */
static inline void fixup_rva_dwords( DWORD *ptr, int delta, unsigned int count )
{
for ( ; count; count--, ptr++) if (*ptr) *ptr += delta;
}
/* fixup an array of name/ordinal RVAs by adding the specified delta */
static inline void fixup_rva_names( UINT_PTR *ptr, int delta )
{
for ( ; *ptr; ptr++) if (!(*ptr & IMAGE_ORDINAL_FLAG)) *ptr += delta;
}
/* fixup RVAs in the resource directory */
static void fixup_so_resources( IMAGE_RESOURCE_DIRECTORY *dir, BYTE *root, int delta )
{
IMAGE_RESOURCE_DIRECTORY_ENTRY *entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
unsigned int i;
for (i = 0; i < dir->NumberOfNamedEntries + dir->NumberOfIdEntries; i++, entry++)
{
void *ptr = root + entry->u2.s2.OffsetToDirectory;
if (entry->u2.s2.DataIsDirectory) fixup_so_resources( ptr, root, delta );
else fixup_rva_dwords( &((IMAGE_RESOURCE_DATA_ENTRY *)ptr)->OffsetToData, delta, 1 );
}
}
/* die on a fatal error; use only during initialization */
static void fatal_error( const char *err, ... )
{
va_list args;
va_start( args, err );
fprintf( stderr, "wine: " );
vfprintf( stderr, err, args );
va_end( args );
exit(1);
}
static void set_max_limit( int limit )
{
struct rlimit rlimit;
if (!getrlimit( limit, &rlimit ))
{
rlimit.rlim_cur = rlimit.rlim_max;
if (setrlimit( limit, &rlimit ) != 0)
{
#if defined(__APPLE__) && defined(RLIMIT_NOFILE) && defined(OPEN_MAX)
/* On Leopard, setrlimit(RLIMIT_NOFILE, ...) fails on attempts to set
* rlim_cur above OPEN_MAX (even if rlim_max > OPEN_MAX). */
if (limit == RLIMIT_NOFILE && rlimit.rlim_cur > OPEN_MAX)
{
rlimit.rlim_cur = OPEN_MAX;
setrlimit( limit, &rlimit );
}
#endif
}
}
}
/* canonicalize path and return its directory name */
static char *realpath_dirname( const char *name )
{
char *p, *fullpath = realpath( name, NULL );
if (fullpath)
{
p = strrchr( fullpath, '/' );
if (p == fullpath) p++;
if (p) *p = 0;
}
return fullpath;
}
/* if string ends with tail, remove it */
static char *remove_tail( const char *str, const char *tail )
{
size_t len = strlen( str );
size_t tail_len = strlen( tail );
char *ret;
if (len < tail_len) return NULL;
if (strcmp( str + len - tail_len, tail )) return NULL;
ret = malloc( len - tail_len + 1 );
memcpy( ret, str, len - tail_len );
ret[len - tail_len] = 0;
return ret;
}
/* build a path from the specified dir and name */
static char *build_path( const char *dir, const char *name )
{
size_t len = strlen( dir );
char *ret = malloc( len + strlen( name ) + 2 );
memcpy( ret, dir, len );
if (len && ret[len - 1] != '/') ret[len++] = '/';
if (name[0] == '/') name++;
strcpy( ret + len, name );
return ret;
}
static const char *get_pe_dir( WORD machine )
{
if (!machine) machine = current_machine;
switch(machine)
{
case IMAGE_FILE_MACHINE_I386: return "/i386-windows";
case IMAGE_FILE_MACHINE_AMD64: return "/x86_64-windows";
case IMAGE_FILE_MACHINE_ARMNT: return "/arm-windows";
case IMAGE_FILE_MACHINE_ARM64: return "/aarch64-windows";
default: return "";
}
}
static void set_dll_path(void)
{
char *p, *path = getenv( "WINEDLLPATH" );
int i, count = 0;
if (path) for (p = path, count = 1; *p; p++) if (*p == ':') count++;
dll_paths = malloc( (count + 2) * sizeof(*dll_paths) );
count = 0;
if (!build_dir) dll_paths[count++] = dll_dir;
if (path)
{
path = strdup(path);
for (p = strtok( path, ":" ); p; p = strtok( NULL, ":" )) dll_paths[count++] = strdup( p );
free( path );
}
for (i = 0; i < count; i++) dll_path_maxlen = max( dll_path_maxlen, strlen(dll_paths[i]) );
dll_paths[count] = NULL;
}
static void set_home_dir(void)
{
const char *home = getenv( "HOME" );
const char *name = getenv( "USER" );
const char *p;
if (!home || !name)
{
struct passwd *pwd = getpwuid( getuid() );
if (pwd)
{
if (!home) home = pwd->pw_dir;
if (!name) name = pwd->pw_name;
}
if (!name) name = "wine";
}
if ((p = strrchr( name, '/' ))) name = p + 1;
if ((p = strrchr( name, '\\' ))) name = p + 1;
home_dir = strdup( home );
user_name = strdup( name );
}
static void set_config_dir(void)
{
char *p, *dir;
const char *prefix = getenv( "WINEPREFIX" );
if (prefix)
{
if (prefix[0] != '/')
fatal_error( "invalid directory %s in WINEPREFIX: not an absolute path\n", prefix );
config_dir = dir = strdup( prefix );
for (p = dir + strlen(dir) - 1; p > dir && *p == '/'; p--) *p = 0;
}
else
{
if (!home_dir) fatal_error( "could not determine your home directory\n" );
if (home_dir[0] != '/') fatal_error( "the home directory %s is not an absolute path\n", home_dir );
config_dir = build_path( home_dir, ".wine" );
}
}
static void init_paths( char *argv[] )
{
Dl_info info;
argv0 = strdup( argv[0] );
if (!dladdr( init_paths, &info ) || !(ntdll_dir = realpath_dirname( info.dli_fname )))
fatal_error( "cannot get path to ntdll.so\n" );
if (!(build_dir = remove_tail( ntdll_dir, "/dlls/ntdll" )))
{
if (!(dll_dir = remove_tail( ntdll_dir, so_dir ))) dll_dir = ntdll_dir;
#if (defined(__linux__) && !defined(__ANDROID__)) || 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( argv0 );
#endif
if (!bin_dir) bin_dir = build_path( dll_dir, DLL_TO_BINDIR );
data_dir = build_path( bin_dir, BIN_TO_DATADIR );
}
set_dll_path();
set_home_dir();
set_config_dir();
}
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 );
}
static NTSTATUS loader_exec( const char *loader, char **argv, WORD machine )
{
char *p, *path;
if (build_dir)
{
argv[1] = build_path( build_dir, (machine == IMAGE_FILE_MACHINE_AMD64) ? "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_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 )
{
WORD machine = pe_info->machine;
ULONGLONG res_start = pe_info->base;
ULONGLONG res_end = pe_info->base + pe_info->map_size;
const char *loader = argv0;
const char *loader_env = getenv( "WINELOADER" );
char preloader_reserve[64], socket_env[64];
BOOL is_child_64bit;
if (pe_info->image_flags & IMAGE_FLAGS_WineFakeDll) res_start = res_end = 0;
if (pe_info->image_flags & IMAGE_FLAGS_ComPlusNativeReady) machine = native_machine;
is_child_64bit = is_machine_64bit( machine );
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 );
return loader_exec( loader, argv, machine );
}
/***********************************************************************
* exec_wineserver
*
* Exec a new wine server.
*/
static void exec_wineserver( char **argv )
{
char *path;
if (build_dir)
{
if (!is_win64) /* look for 64-bit server */
{
char *loader = realpath_dirname( build_path( build_dir, "loader/wine64" ));
if (loader)
{
argv[0] = build_path( loader, "../server/wineserver" );
execv( argv[0], argv );
}
}
argv[0] = build_path( build_dir, "server/wineserver" );
execv( argv[0], argv );
return;
}
argv[0] = build_path( bin_dir, "wineserver" );
execv( argv[0], argv );
argv[0] = getenv( "WINESERVER" );
if (argv[0]) execv( argv[0], argv );
if ((path = getenv( "PATH" )))
{
for (path = strtok( strdup( path ), ":" ); path; path = strtok( NULL, ":" ))
{
argv[0] = build_path( path, "wineserver" );
execvp( argv[0], argv );
}
}
argv[0] = build_path( BINDIR, "wineserver" );
execv( argv[0], argv );
}
/***********************************************************************
* start_server
*
* Start a new wine server.
*/
void start_server( BOOL debug )
{
static BOOL started; /* we only try once */
char *argv[3];
static char debug_flag[] = "-d";
if (!started)
{
int status;
int pid = fork();
if (pid == -1) fatal_error( "fork: %s", strerror(errno) );
if (!pid)
{
argv[1] = debug ? debug_flag : NULL;
argv[2] = NULL;
exec_wineserver( argv );
fatal_error( "could not exec wineserver\n" );
}
waitpid( pid, &status, 0 );
status = WIFEXITED(status) ? WEXITSTATUS(status) : 1;
if (status == 2) return; /* server lock held by someone else, will retry later */
if (status) exit(status); /* server failed */
started = TRUE;
}
}
/*************************************************************************
* map_so_dll
*
* Map a builtin dll in memory and fixup RVAs.
*/
static NTSTATUS map_so_dll( const IMAGE_NT_HEADERS *nt_descr, HMODULE module )
{
static const char builtin_signature[32] = "Wine builtin DLL";
IMAGE_DATA_DIRECTORY *dir;
IMAGE_DOS_HEADER *dos;
IMAGE_NT_HEADERS *nt;
IMAGE_SECTION_HEADER *sec;
BYTE *addr = (BYTE *)module;
DWORD code_start, code_end, data_start, data_end, align_mask;
int delta, nb_sections = 2; /* code + data */
unsigned int i;
DWORD size = (sizeof(IMAGE_DOS_HEADER)
+ sizeof(builtin_signature)
+ sizeof(IMAGE_NT_HEADERS)
+ nb_sections * sizeof(IMAGE_SECTION_HEADER));
if (anon_mmap_fixed( addr, size, PROT_READ | PROT_WRITE, 0 ) != addr) return STATUS_NO_MEMORY;
dos = (IMAGE_DOS_HEADER *)addr;
nt = (IMAGE_NT_HEADERS *)((BYTE *)(dos + 1) + sizeof(builtin_signature));
sec = (IMAGE_SECTION_HEADER *)(nt + 1);
/* build the DOS and NT headers */
dos->e_magic = IMAGE_DOS_SIGNATURE;
dos->e_cblp = 0x90;
dos->e_cp = 3;
dos->e_cparhdr = (sizeof(*dos) + 0xf) / 0x10;
dos->e_minalloc = 0;
dos->e_maxalloc = 0xffff;
dos->e_ss = 0x0000;
dos->e_sp = 0x00b8;
dos->e_lfanew = sizeof(*dos) + sizeof(builtin_signature);
memcpy( dos + 1, builtin_signature, sizeof(builtin_signature) );
*nt = *nt_descr;
delta = (const BYTE *)nt_descr - addr;
align_mask = nt->OptionalHeader.SectionAlignment - 1;
code_start = (size + align_mask) & ~align_mask;
data_start = delta & ~align_mask;
#ifdef __APPLE__
{
Dl_info dli;
unsigned long data_size;
/* need the mach_header, not the PE header, to give to getsegmentdata(3) */
dladdr(addr, &dli);
code_end = getsegmentdata(dli.dli_fbase, "__DATA", &data_size) - addr;
data_end = (code_end + data_size + align_mask) & ~align_mask;
}
#else
code_end = data_start;
data_end = (nt->OptionalHeader.SizeOfImage + delta + align_mask) & ~align_mask;
#endif
fixup_rva_ptrs( &nt->OptionalHeader.AddressOfEntryPoint, addr, 1 );
nt->FileHeader.NumberOfSections = nb_sections;
nt->OptionalHeader.BaseOfCode = code_start;
#ifndef _WIN64
nt->OptionalHeader.BaseOfData = data_start;
#endif
nt->OptionalHeader.SizeOfCode = code_end - code_start;
nt->OptionalHeader.SizeOfInitializedData = data_end - data_start;
nt->OptionalHeader.SizeOfUninitializedData = 0;
nt->OptionalHeader.SizeOfImage = data_end;
nt->OptionalHeader.ImageBase = (ULONG_PTR)addr;
/* build the code section */
memcpy( sec->Name, ".text", sizeof(".text") );
sec->SizeOfRawData = code_end - code_start;
sec->Misc.VirtualSize = sec->SizeOfRawData;
sec->VirtualAddress = code_start;
sec->PointerToRawData = code_start;
sec->Characteristics = (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ);
sec++;
/* build the data section */
memcpy( sec->Name, ".data", sizeof(".data") );
sec->SizeOfRawData = data_end - data_start;
sec->Misc.VirtualSize = sec->SizeOfRawData;
sec->VirtualAddress = data_start;
sec->PointerToRawData = data_start;
sec->Characteristics = (IMAGE_SCN_CNT_INITIALIZED_DATA |
IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ);
sec++;
for (i = 0; i < nt->OptionalHeader.NumberOfRvaAndSizes; i++)
fixup_rva_dwords( &nt->OptionalHeader.DataDirectory[i].VirtualAddress, delta, 1 );
/* build the import directory */
dir = &nt->OptionalHeader.DataDirectory[IMAGE_FILE_IMPORT_DIRECTORY];
if (dir->Size)
{
IMAGE_IMPORT_DESCRIPTOR *imports = (IMAGE_IMPORT_DESCRIPTOR *)(addr + dir->VirtualAddress);
while (imports->Name)
{
fixup_rva_dwords( &imports->u.OriginalFirstThunk, delta, 1 );
fixup_rva_dwords( &imports->Name, delta, 1 );
fixup_rva_dwords( &imports->FirstThunk, delta, 1 );
if (imports->u.OriginalFirstThunk)
fixup_rva_names( (UINT_PTR *)(addr + imports->u.OriginalFirstThunk), delta );
if (imports->FirstThunk)
fixup_rva_names( (UINT_PTR *)(addr + imports->FirstThunk), delta );
imports++;
}
}
/* build the resource directory */
dir = &nt->OptionalHeader.DataDirectory[IMAGE_FILE_RESOURCE_DIRECTORY];
if (dir->Size)
{
void *ptr = addr + dir->VirtualAddress;
fixup_so_resources( ptr, ptr, delta );
}
/* build the export directory */
dir = &nt->OptionalHeader.DataDirectory[IMAGE_FILE_EXPORT_DIRECTORY];
if (dir->Size)
{
IMAGE_EXPORT_DIRECTORY *exports = (IMAGE_EXPORT_DIRECTORY *)(addr + dir->VirtualAddress);
fixup_rva_dwords( &exports->Name, delta, 1 );
fixup_rva_dwords( &exports->AddressOfFunctions, delta, 1 );
fixup_rva_dwords( &exports->AddressOfNames, delta, 1 );
fixup_rva_dwords( &exports->AddressOfNameOrdinals, delta, 1 );
fixup_rva_dwords( (DWORD *)(addr + exports->AddressOfNames), delta, exports->NumberOfNames );
fixup_rva_ptrs( addr + exports->AddressOfFunctions, addr, exports->NumberOfFunctions );
}
return STATUS_SUCCESS;
}
static ULONG_PTR find_ordinal_export( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports, DWORD ordinal )
{
const DWORD *functions = (const DWORD *)((BYTE *)module + exports->AddressOfFunctions);
if (ordinal >= exports->NumberOfFunctions) return 0;
if (!functions[ordinal]) return 0;
return (ULONG_PTR)module + functions[ordinal];
}
static ULONG_PTR find_named_export( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports,
const char *name )
{
const WORD *ordinals = (const WORD *)((BYTE *)module + exports->AddressOfNameOrdinals);
const DWORD *names = (const DWORD *)((BYTE *)module + exports->AddressOfNames);
int min = 0, max = exports->NumberOfNames - 1;
while (min <= max)
{
int res, pos = (min + max) / 2;
char *ename = (char *)module + names[pos];
if (!(res = strcmp( ename, name ))) return find_ordinal_export( module, exports, ordinals[pos] );
if (res > 0) max = pos - 1;
else min = pos + 1;
}
return 0;
}
static ULONG_PTR find_pe_export( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports,
const IMAGE_IMPORT_BY_NAME *name )
{
const WORD *ordinals = (const WORD *)((BYTE *)module + exports->AddressOfNameOrdinals);
const DWORD *names = (const DWORD *)((BYTE *)module + exports->AddressOfNames);
if (name->Hint < exports->NumberOfNames)
{
char *ename = (char *)module + names[name->Hint];
if (!strcmp( ename, (char *)name->Name ))
return find_ordinal_export( module, exports, ordinals[name->Hint] );
}
return find_named_export( module, exports, (char *)name->Name );
}
static inline void *get_rva( void *module, ULONG_PTR addr )
{
return (BYTE *)module + addr;
}
static const void *get_module_data_dir( HMODULE module, ULONG dir, ULONG *size )
{
const IMAGE_NT_HEADERS *nt = get_rva( module, ((IMAGE_DOS_HEADER *)module)->e_lfanew );
const IMAGE_DATA_DIRECTORY *data;
if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
data = &((const IMAGE_NT_HEADERS64 *)nt)->OptionalHeader.DataDirectory[dir];
else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
data = &((const IMAGE_NT_HEADERS32 *)nt)->OptionalHeader.DataDirectory[dir];
else
return NULL;
if (!data->VirtualAddress || !data->Size) return NULL;
if (size) *size = data->Size;
return get_rva( module, data->VirtualAddress );
}
static NTSTATUS fixup_ntdll_imports( const char *name, HMODULE module )
{
const IMAGE_IMPORT_DESCRIPTOR *descr;
const IMAGE_THUNK_DATA *import_list;
IMAGE_THUNK_DATA *thunk_list;
if (!(descr = get_module_data_dir( module, IMAGE_FILE_IMPORT_DIRECTORY, NULL ))) return STATUS_SUCCESS;
for (; descr->Name && descr->FirstThunk; descr++)
{
thunk_list = get_rva( module, descr->FirstThunk );
/* ntdll must be the only import */
if (strcmp( get_rva( module, descr->Name ), "ntdll.dll" ))
{
ERR( "module %s is importing %s\n", debugstr_a(name), (char *)get_rva( module, descr->Name ));
return STATUS_PROCEDURE_NOT_FOUND;
}
if (descr->u.OriginalFirstThunk)
import_list = get_rva( module, descr->u.OriginalFirstThunk );
else
import_list = thunk_list;
while (import_list->u1.Ordinal)
{
if (IMAGE_SNAP_BY_ORDINAL( import_list->u1.Ordinal ))
{
int ordinal = IMAGE_ORDINAL( import_list->u1.Ordinal ) - ntdll_exports->Base;
thunk_list->u1.Function = find_ordinal_export( ntdll_module, ntdll_exports, ordinal );
if (!thunk_list->u1.Function) ERR( "%s: ntdll.%u not found\n", debugstr_a(name), ordinal );
}
else /* import by name */
{
IMAGE_IMPORT_BY_NAME *pe_name = get_rva( module, import_list->u1.AddressOfData );
thunk_list->u1.Function = find_pe_export( ntdll_module, ntdll_exports, pe_name );
if (!thunk_list->u1.Function) ERR( "%s: ntdll.%s not found\n", debugstr_a(name), pe_name->Name );
}
import_list++;
thunk_list++;
}
}
return STATUS_SUCCESS;
}
static void load_ntdll_functions( HMODULE module )
{
ntdll_exports = get_module_data_dir( module, IMAGE_FILE_EXPORT_DIRECTORY, NULL );
assert( ntdll_exports );
#define GET_FUNC(name) \
if (!(p##name = (void *)find_named_export( module, ntdll_exports, #name ))) \
ERR( "%s not found\n", #name )
GET_FUNC( DbgUiRemoteBreakin );
GET_FUNC( KiRaiseUserExceptionDispatcher );
GET_FUNC( KiUserExceptionDispatcher );
GET_FUNC( KiUserApcDispatcher );
GET_FUNC( KiUserCallbackDispatcher );
GET_FUNC( LdrInitializeThunk );
GET_FUNC( LdrSystemDllInitBlock );
GET_FUNC( RtlUserThreadStart );
GET_FUNC( __wine_ctrl_routine );
GET_FUNC( __wine_set_unix_funcs );
GET_FUNC( __wine_syscall_dispatcher );
#ifdef __i386__
{
void **p__wine_ldt_copy;
GET_FUNC( __wine_ldt_copy );
*p__wine_ldt_copy = &__wine_ldt_copy;
}
#endif
#undef GET_FUNC
}
static void load_ntdll_wow64_functions( HMODULE module )
{
const IMAGE_EXPORT_DIRECTORY *exports;
exports = get_module_data_dir( module, IMAGE_FILE_EXPORT_DIRECTORY, NULL );
assert( exports );
pLdrSystemDllInitBlock->ntdll_handle = (ULONG_PTR)module;
#define GET_FUNC(name) pLdrSystemDllInitBlock->p##name = find_named_export( module, exports, #name )
GET_FUNC( KiUserApcDispatcher );
GET_FUNC( KiUserCallbackDispatcher );
GET_FUNC( KiUserExceptionDispatcher );
GET_FUNC( LdrInitializeThunk );
GET_FUNC( LdrSystemDllInitBlock );
GET_FUNC( RtlUserThreadStart );
GET_FUNC( RtlpFreezeTimeBias );
GET_FUNC( RtlpQueryProcessDebugInformationRemote );
#undef GET_FUNC
p__wine_ctrl_routine = (void *)find_named_export( module, exports, "__wine_ctrl_routine" );
/* also set the 32-bit LdrSystemDllInitBlock */
memcpy( (void *)(ULONG_PTR)pLdrSystemDllInitBlock->pLdrSystemDllInitBlock,
pLdrSystemDllInitBlock, sizeof(*pLdrSystemDllInitBlock) );
}
/* reimplementation of LdrProcessRelocationBlock */
static const IMAGE_BASE_RELOCATION *process_relocation_block( void *module, const IMAGE_BASE_RELOCATION *rel,
INT_PTR delta )
{
char *page = get_rva( module, rel->VirtualAddress );
UINT count = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT);
USHORT *relocs = (USHORT *)(rel + 1);
while (count--)
{
USHORT offset = *relocs & 0xfff;
switch (*relocs >> 12)
{
case IMAGE_REL_BASED_ABSOLUTE:
break;
case IMAGE_REL_BASED_HIGH:
*(short *)(page + offset) += HIWORD(delta);
break;
case IMAGE_REL_BASED_LOW:
*(short *)(page + offset) += LOWORD(delta);
break;
case IMAGE_REL_BASED_HIGHLOW:
*(int *)(page + offset) += delta;
break;
case IMAGE_REL_BASED_DIR64:
*(INT64 *)(page + offset) += delta;
break;
case IMAGE_REL_BASED_THUMB_MOV32:
{
DWORD *inst = (DWORD *)(page + offset);
WORD lo = ((inst[0] << 1) & 0x0800) + ((inst[0] << 12) & 0xf000) +
((inst[0] >> 20) & 0x0700) + ((inst[0] >> 16) & 0x00ff);
WORD hi = ((inst[1] << 1) & 0x0800) + ((inst[1] << 12) & 0xf000) +
((inst[1] >> 20) & 0x0700) + ((inst[1] >> 16) & 0x00ff);
DWORD imm = MAKELONG( lo, hi ) + delta;
lo = LOWORD( imm );
hi = HIWORD( imm );
inst[0] = (inst[0] & 0x8f00fbf0) + ((lo >> 1) & 0x0400) + ((lo >> 12) & 0x000f) +
((lo << 20) & 0x70000000) + ((lo << 16) & 0xff0000);
inst[1] = (inst[1] & 0x8f00fbf0) + ((hi >> 1) & 0x0400) + ((hi >> 12) & 0x000f) +
((hi << 20) & 0x70000000) + ((hi << 16) & 0xff0000);
break;
}
default:
FIXME("Unknown/unsupported relocation %x\n", *relocs);
return NULL;
}
relocs++;
}
return (IMAGE_BASE_RELOCATION *)relocs; /* return address of next block */
}
static void relocate_ntdll( void *module )
{
const IMAGE_NT_HEADERS *nt = get_rva( module, ((IMAGE_DOS_HEADER *)module)->e_lfanew );
const IMAGE_BASE_RELOCATION *rel, *end;
const IMAGE_SECTION_HEADER *sec;
ULONG protect_old[96], i, size;
INT_PTR delta;
ERR( "ntdll could not be mapped at preferred address (%p), expect trouble\n", module );
if (!(rel = get_module_data_dir( module, IMAGE_DIRECTORY_ENTRY_BASERELOC, &size ))) return;
sec = (IMAGE_SECTION_HEADER *)((char *)&nt->OptionalHeader + nt->FileHeader.SizeOfOptionalHeader);
for (i = 0; i < nt->FileHeader.NumberOfSections; i++)
{
void *addr = get_rva( module, sec[i].VirtualAddress );
SIZE_T size = sec[i].SizeOfRawData;
NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, PAGE_READWRITE, &protect_old[i] );
}
end = (IMAGE_BASE_RELOCATION *)((const char *)rel + size);
delta = (char *)module - (char *)nt->OptionalHeader.ImageBase;
while (rel && rel < end - 1 && rel->SizeOfBlock) rel = process_relocation_block( module, rel, delta );
for (i = 0; i < nt->FileHeader.NumberOfSections; i++)
{
void *addr = get_rva( module, sec[i].VirtualAddress );
SIZE_T size = sec[i].SizeOfRawData;
NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, protect_old[i], &protect_old[i] );
}
}
static void *callback_module;
/***********************************************************************
* load_builtin_callback
*
* Load a library in memory; callback function for wine_dll_register
*/
static void load_builtin_callback( void *module, const char *filename )
{
callback_module = module;
}
/***********************************************************************
* load_libwine
*/
static void load_libwine(void)
{
#ifdef __APPLE__
#define LIBWINE "libwine.1.dylib"
#else
#define LIBWINE "libwine.so.1"
#endif
typedef void (*load_dll_callback_t)( void *, const char * );
void (*p_wine_dll_set_callback)( load_dll_callback_t load );
char ***p___wine_main_environ;
char *path;
void *handle;
if (build_dir) path = build_path( build_dir, "libs/wine/" LIBWINE );
else path = build_path( ntdll_dir, LIBWINE );
handle = dlopen( path, RTLD_NOW );
free( path );
if (!handle && !(handle = dlopen( LIBWINE, RTLD_NOW ))) return;
p_wine_dll_set_callback = dlsym( handle, "wine_dll_set_callback" );
p___wine_main_argc = dlsym( handle, "__wine_main_argc" );
p___wine_main_argv = dlsym( handle, "__wine_main_argv" );
p___wine_main_wargv = dlsym( handle, "__wine_main_wargv" );
p___wine_main_environ = dlsym( handle, "__wine_main_environ" );
if (p_wine_dll_set_callback) p_wine_dll_set_callback( load_builtin_callback );
if (p___wine_main_environ) *p___wine_main_environ = main_envp;
}
/***********************************************************************
* fill_builtin_image_info
*/
static void fill_builtin_image_info( void *module, pe_image_info_t *info )
{
const IMAGE_DOS_HEADER *dos = (const IMAGE_DOS_HEADER *)module;
const IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)((const BYTE *)dos + dos->e_lfanew);
info->base = nt->OptionalHeader.ImageBase;
info->entry_point = nt->OptionalHeader.AddressOfEntryPoint;
info->map_size = nt->OptionalHeader.SizeOfImage;
info->stack_size = nt->OptionalHeader.SizeOfStackReserve;
info->stack_commit = nt->OptionalHeader.SizeOfStackCommit;
info->zerobits = 0;
info->subsystem = nt->OptionalHeader.Subsystem;
info->subsystem_minor = nt->OptionalHeader.MinorSubsystemVersion;
info->subsystem_major = nt->OptionalHeader.MajorSubsystemVersion;
info->osversion_major = nt->OptionalHeader.MajorOperatingSystemVersion;
info->osversion_minor = nt->OptionalHeader.MinorOperatingSystemVersion;
info->image_charact = nt->FileHeader.Characteristics;
info->dll_charact = nt->OptionalHeader.DllCharacteristics;
info->machine = nt->FileHeader.Machine;
info->contains_code = TRUE;
info->image_flags = IMAGE_FLAGS_WineBuiltin;
info->loader_flags = 0;
info->header_size = nt->OptionalHeader.SizeOfHeaders;
info->file_size = nt->OptionalHeader.SizeOfImage;
info->checksum = nt->OptionalHeader.CheckSum;
info->dbg_offset = 0;
info->dbg_size = 0;
}
/***********************************************************************
* dlopen_dll
*/
static NTSTATUS dlopen_dll( const char *so_name, UNICODE_STRING *nt_name, void **ret_module,
pe_image_info_t *image_info, BOOL prefer_native )
{
void *module, *handle;
const IMAGE_NT_HEADERS *nt;
callback_module = (void *)1;
handle = dlopen( so_name, RTLD_NOW );
if (!handle)
{
WARN( "failed to load .so lib %s: %s\n", debugstr_a(so_name), dlerror() );
return STATUS_INVALID_IMAGE_FORMAT;
}
if (callback_module != (void *)1) /* callback was called */
{
if (!callback_module) return STATUS_NO_MEMORY;
WARN( "got old-style builtin library %s, constructors won't work\n", debugstr_a(so_name) );
module = callback_module;
if (get_builtin_so_handle( module )) goto already_loaded;
}
else if ((nt = dlsym( handle, "__wine_spec_nt_header" )))
{
module = (HMODULE)((nt->OptionalHeader.ImageBase + 0xffff) & ~0xffff);
if (get_builtin_so_handle( module )) goto already_loaded;
if (map_so_dll( nt, module ))
{
dlclose( handle );
return STATUS_NO_MEMORY;
}
}
else /* already loaded .so */
{
WARN( "%s already loaded?\n", debugstr_a(so_name));
return STATUS_INVALID_IMAGE_FORMAT;
}
fill_builtin_image_info( module, image_info );
if (prefer_native && (image_info->dll_charact & IMAGE_DLLCHARACTERISTICS_PREFER_NATIVE))
{
TRACE( "%s has prefer-native flag, ignoring builtin\n", debugstr_a(so_name) );
dlclose( handle );
return STATUS_IMAGE_ALREADY_LOADED;
}
if (virtual_create_builtin_view( module, nt_name, image_info, handle ))
{
dlclose( handle );
return STATUS_NO_MEMORY;
}
*ret_module = module;
return STATUS_SUCCESS;
already_loaded:
fill_builtin_image_info( module, image_info );
*ret_module = module;
dlclose( handle );
return STATUS_SUCCESS;
}
/***********************************************************************
* init_unix_lib
*/
static NTSTATUS CDECL init_unix_lib( void *module, DWORD reason, const void *ptr_in, void *ptr_out )
{
NTSTATUS (CDECL *init_func)( HMODULE, DWORD, const void *, void * );
const IMAGE_NT_HEADERS *nt;
const char *name;
void *handle, *entry, *unix_module;
NTSTATUS status;
if ((status = get_builtin_unix_info( module, &name, &handle, &entry ))) return status;
if (!entry)
{
if (!name || !handle) return STATUS_DLL_NOT_FOUND;
if (!(nt = dlsym( handle, "__wine_spec_nt_header" )) ||
!(entry = dlsym( handle, "__wine_init_unix_lib" )))
return STATUS_INVALID_IMAGE_FORMAT;
TRACE( "loaded %s for %p\n", debugstr_a(name), module );
unix_module = (void *)((nt->OptionalHeader.ImageBase + 0xffff) & ~0xffff);
map_so_dll( nt, unix_module );
fixup_ntdll_imports( name, unix_module );
set_builtin_unix_entry( module, entry );
}
init_func = entry;
return init_func( module, reason, ptr_in, ptr_out );
}
/***********************************************************************
* ntdll_init_syscalls
*/
NTSTATUS ntdll_init_syscalls( ULONG id, SYSTEM_SERVICE_TABLE *table, void **dispatcher )
{
struct syscall_info
{
void *dispatcher;
USHORT limit;
BYTE args[1];
} *info = (struct syscall_info *)dispatcher;
if (id > 3) return STATUS_INVALID_PARAMETER;
if (info->limit != table->ServiceLimit)
{
ERR( "syscall count mismatch %u / %lu\n", info->limit, table->ServiceLimit );
NtTerminateProcess( GetCurrentProcess(), STATUS_INVALID_PARAMETER );
}
info->dispatcher = __wine_syscall_dispatcher;
memcpy( table->ArgumentTable, info->args, table->ServiceLimit );
KeServiceDescriptorTable[id] = *table;
return STATUS_SUCCESS;
}
/***********************************************************************
* __wine_unix_call
*/
NTSTATUS WINAPI __wine_unix_call( unixlib_handle_t handle, unsigned int code, void *args )
{
return ((unixlib_entry_t*)(UINT_PTR)handle)[code]( args );
}
/***********************************************************************
* load_so_dll
*/
static NTSTATUS CDECL load_so_dll( UNICODE_STRING *nt_name, void **module )
{
static const WCHAR soW[] = {'.','s','o',0};
OBJECT_ATTRIBUTES attr;
UNICODE_STRING redir;
pe_image_info_t info;
char *unix_name;
NTSTATUS status;
DWORD len;
if (get_load_order( nt_name ) == LO_DISABLED) return STATUS_DLL_NOT_FOUND;
InitializeObjectAttributes( &attr, nt_name, OBJ_CASE_INSENSITIVE, 0, 0 );
get_redirect( &attr, &redir );
if (nt_to_unix_file_name( &attr, &unix_name, FILE_OPEN ))
{
free( redir.Buffer );
return STATUS_DLL_NOT_FOUND;
}
/* remove .so extension from Windows name */
len = nt_name->Length / sizeof(WCHAR);
if (len > 3 && !wcsicmp( nt_name->Buffer + len - 3, soW )) nt_name->Length -= 3 * sizeof(WCHAR);
status = dlopen_dll( unix_name, nt_name, module, &info, FALSE );
free( unix_name );
free( redir.Buffer );
return status;
}
/* check if the library is the correct architecture */
/* only returns false for a valid library of the wrong arch */
static int check_library_arch( int fd )
{
#ifdef __APPLE__
struct /* Mach-O header */
{
unsigned int magic;
unsigned int cputype;
} header;
if (read( fd, &header, sizeof(header) ) != sizeof(header)) return 1;
if (header.magic != 0xfeedface) return 1;
if (sizeof(void *) == sizeof(int)) return !(header.cputype >> 24);
else return (header.cputype >> 24) == 1; /* CPU_ARCH_ABI64 */
#else
struct /* ELF header */
{
unsigned char magic[4];
unsigned char class;
unsigned char data;
unsigned char version;
} header;
if (read( fd, &header, sizeof(header) ) != sizeof(header)) return 1;
if (memcmp( header.magic, "\177ELF", 4 )) return 1;
if (header.version != 1 /* EV_CURRENT */) return 1;
#ifdef WORDS_BIGENDIAN
if (header.data != 2 /* ELFDATA2MSB */) return 1;
#else
if (header.data != 1 /* ELFDATA2LSB */) return 1;
#endif
if (sizeof(void *) == sizeof(int)) return header.class == 1; /* ELFCLASS32 */
else return header.class == 2; /* ELFCLASS64 */
#endif
}
static inline char *prepend( char *buffer, const char *str, size_t len )
{
return memcpy( buffer - len, str, len );
}
/***********************************************************************
* open_dll_file
*
* Open a file for a new dll. Helper for open_builtin_pe_file.
*/
static NTSTATUS open_dll_file( const char *name, OBJECT_ATTRIBUTES *attr, HANDLE *mapping )
{
LARGE_INTEGER size;
NTSTATUS status;
HANDLE handle;
if ((status = open_unix_file( &handle, name, GENERIC_READ | SYNCHRONIZE, attr, 0,
FILE_SHARE_READ | FILE_SHARE_DELETE, FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, NULL, 0 )))
{
if (status != STATUS_OBJECT_PATH_NOT_FOUND && status != STATUS_OBJECT_NAME_NOT_FOUND)
{
/* if the file exists but failed to open, report the error */
struct stat st;
if (!stat( name, &st )) return status;
}
/* otherwise continue searching */
return STATUS_DLL_NOT_FOUND;
}
size.QuadPart = 0;
status = NtCreateSection( mapping, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY |
SECTION_MAP_READ | SECTION_MAP_EXECUTE,
NULL, &size, PAGE_EXECUTE_READ, SEC_IMAGE, handle );
NtClose( handle );
return status;
}
/***********************************************************************
* open_builtin_pe_file
*/
static NTSTATUS open_builtin_pe_file( const char *name, OBJECT_ATTRIBUTES *attr, void **module,
SIZE_T *size, SECTION_IMAGE_INFORMATION *image_info,
WORD machine, BOOL prefer_native )
{
NTSTATUS status;
HANDLE mapping;
*module = NULL;
status = open_dll_file( name, attr, &mapping );
if (!status)
{
status = virtual_map_builtin_module( mapping, module, size, image_info, machine, prefer_native );
NtClose( mapping );
}
return status;
}
/***********************************************************************
* open_builtin_so_file
*/
static NTSTATUS open_builtin_so_file( const char *name, OBJECT_ATTRIBUTES *attr, void **module,
SECTION_IMAGE_INFORMATION *image_info,
WORD machine, BOOL prefer_native )
{
NTSTATUS status;
int fd;
*module = NULL;
if (machine != current_machine) return STATUS_DLL_NOT_FOUND;
if ((fd = open( name, O_RDONLY )) == -1) return STATUS_DLL_NOT_FOUND;
if (check_library_arch( fd ))
{
pe_image_info_t info;
status = dlopen_dll( name, attr->ObjectName, module, &info, prefer_native );
if (!status) virtual_fill_image_information( &info, image_info );
else if (status != STATUS_IMAGE_ALREADY_LOADED)
{
ERR( "failed to load .so lib %s\n", debugstr_a(name) );
status = STATUS_PROCEDURE_NOT_FOUND;
}
}
else status = STATUS_IMAGE_MACHINE_TYPE_MISMATCH;
close( fd );
return status;
}
/***********************************************************************
* find_builtin_dll
*/
static NTSTATUS find_builtin_dll( UNICODE_STRING *nt_name, void **module, SIZE_T *size_ptr,
SECTION_IMAGE_INFORMATION *image_info, WORD machine, BOOL prefer_native )
{
unsigned int i, pos, namepos, namelen, maxlen = 0;
unsigned int len = nt_name->Length / sizeof(WCHAR);
char *ptr = NULL, *file, *ext = NULL;
const char *pe_dir = get_pe_dir( machine );
OBJECT_ATTRIBUTES attr;
NTSTATUS status = STATUS_DLL_NOT_FOUND;
BOOL found_image = FALSE;
for (i = namepos = 0; i < len; i++)
if (nt_name->Buffer[i] == '/' || nt_name->Buffer[i] == '\\') namepos = i + 1;
len -= namepos;
if (!len) return STATUS_DLL_NOT_FOUND;
InitializeObjectAttributes( &attr, nt_name, 0, 0, NULL );
if (build_dir) maxlen = strlen(build_dir) + sizeof("/programs/") + len;
maxlen = max( maxlen, dll_path_maxlen + 1 ) + len + sizeof("/aarch64-windows") + sizeof(".so");
if (!(file = malloc( maxlen ))) return STATUS_NO_MEMORY;
pos = maxlen - len - sizeof(".so");
/* we don't want to depend on the current codepage here */
for (i = 0; i < len; i++)
{
if (nt_name->Buffer[namepos + i] > 127) goto done;
file[pos + i] = (char)nt_name->Buffer[namepos + i];
if (file[pos + i] >= 'A' && file[pos + i] <= 'Z') file[pos + i] += 'a' - 'A';
else if (file[pos + i] == '.') ext = file + pos + i;
}
file[--pos] = '/';
if (build_dir)
{
/* try as a dll */
ptr = file + pos;
namelen = len + 1;
file[pos + len + 1] = 0;
if (ext && !strcmp( ext, ".dll" )) namelen -= 4;
ptr = prepend( ptr, ptr, namelen );
ptr = prepend( ptr, "/dlls", sizeof("/dlls") - 1 );
ptr = prepend( ptr, build_dir, strlen(build_dir) );
status = open_builtin_pe_file( ptr, &attr, module, size_ptr, image_info, machine, prefer_native );
if (status != STATUS_DLL_NOT_FOUND) goto done;
strcpy( file + pos + len + 1, ".so" );
status = open_builtin_so_file( ptr, &attr, module, image_info, machine, prefer_native );
if (status != STATUS_DLL_NOT_FOUND) goto done;
/* now as a program */
ptr = file + pos;
namelen = len + 1;
file[pos + len + 1] = 0;
if (ext && !strcmp( ext, ".exe" )) namelen -= 4;
ptr = prepend( ptr, ptr, namelen );
ptr = prepend( ptr, "/programs", sizeof("/programs") - 1 );
ptr = prepend( ptr, build_dir, strlen(build_dir) );
status = open_builtin_pe_file( ptr, &attr, module, size_ptr, image_info, machine, prefer_native );
if (status != STATUS_DLL_NOT_FOUND) goto done;
strcpy( file + pos + len + 1, ".so" );
status = open_builtin_so_file( ptr, &attr, module, image_info, machine, prefer_native );
if (status != STATUS_DLL_NOT_FOUND) goto done;
}
for (i = 0; dll_paths[i]; i++)
{
ptr = file + pos;
file[pos + len + 1] = 0;
ptr = prepend( ptr, pe_dir, strlen(pe_dir) );
ptr = prepend( ptr, dll_paths[i], strlen(dll_paths[i]) );
status = open_builtin_pe_file( ptr, &attr, module, size_ptr, image_info, machine, prefer_native );
/* use so dir for unix lib */
ptr = file + pos;
ptr = prepend( ptr, so_dir, strlen(so_dir) );
ptr = prepend( ptr, dll_paths[i], strlen(dll_paths[i]) );
if (status != STATUS_DLL_NOT_FOUND) goto done;
strcpy( file + pos + len + 1, ".so" );
status = open_builtin_so_file( ptr, &attr, module, image_info, machine, prefer_native );
if (status != STATUS_DLL_NOT_FOUND) goto done;
file[pos + len + 1] = 0;
ptr = prepend( file + pos, dll_paths[i], strlen(dll_paths[i]) );
status = open_builtin_pe_file( ptr, &attr, module, size_ptr, image_info, machine, prefer_native );
if (status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
{
found_image = TRUE;
continue;
}
if (status != STATUS_DLL_NOT_FOUND) goto done;
strcpy( file + pos + len + 1, ".so" );
status = open_builtin_so_file( ptr, &attr, module, image_info, machine, prefer_native );
if (status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH) found_image = TRUE;
else if (status != STATUS_DLL_NOT_FOUND) goto done;
}
if (found_image) status = STATUS_IMAGE_MACHINE_TYPE_MISMATCH;
WARN( "cannot find builtin library for %s\n", debugstr_us(nt_name) );
done:
if (status >= 0 && ext)
{
void *handle;
strcpy( ext, ".so" );
if ((handle = dlopen( ptr, RTLD_NOW )))
{
if (set_builtin_unix_handle( *module, ptr, handle )) dlclose( handle );
}
}
free( file );
return status;
}
/***********************************************************************
* load_builtin
*
* Load the builtin dll if specified by load order configuration.
* Return STATUS_IMAGE_ALREADY_LOADED if we should keep the native one that we have found.
*/
NTSTATUS load_builtin( const pe_image_info_t *image_info, WCHAR *filename,
void **module, SIZE_T *size )
{
WORD machine = image_info->machine; /* request same machine as the native one */
NTSTATUS status;
UNICODE_STRING nt_name;
SECTION_IMAGE_INFORMATION info;
enum loadorder loadorder;
/* remove .fake extension if present */
if (image_info->image_flags & IMAGE_FLAGS_WineFakeDll)
{
static const WCHAR fakeW[] = {'.','f','a','k','e',0};
WCHAR *ext = wcsrchr( filename, '.' );
TRACE( "%s is a fake Wine dll\n", debugstr_w(filename) );
if (ext && !wcsicmp( ext, fakeW )) *ext = 0;
}
init_unicode_string( &nt_name, filename );
loadorder = get_load_order( &nt_name );
if (loadorder == LO_DISABLED) return STATUS_DLL_NOT_FOUND;
if (image_info->image_flags & IMAGE_FLAGS_WineBuiltin)
{
if (loadorder == LO_NATIVE) return STATUS_DLL_NOT_FOUND;
loadorder = LO_BUILTIN_NATIVE; /* load builtin, then fallback to the file we found */
}
else if (image_info->image_flags & IMAGE_FLAGS_WineFakeDll)
{
if (loadorder == LO_NATIVE) return STATUS_DLL_NOT_FOUND;
loadorder = LO_BUILTIN; /* builtin with no fallback since mapping a fake dll is not useful */
}
switch (loadorder)
{
case LO_NATIVE:
case LO_NATIVE_BUILTIN:
return STATUS_IMAGE_ALREADY_LOADED;
case LO_BUILTIN:
return find_builtin_dll( &nt_name, module, size, &info, machine, FALSE );
default:
status = find_builtin_dll( &nt_name, module, size, &info, machine, (loadorder == LO_DEFAULT) );
if (status == STATUS_DLL_NOT_FOUND || status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
return STATUS_IMAGE_ALREADY_LOADED;
return status;
}
}
/***************************************************************************
* get_machine_wow64_dir
*
* cf. GetSystemWow64Directory2.
*/
static const WCHAR *get_machine_wow64_dir( WORD machine )
{
static const WCHAR system32[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\','s','y','s','t','e','m','3','2','\\',0};
static const WCHAR syswow64[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\','s','y','s','w','o','w','6','4','\\',0};
static const WCHAR sysarm32[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\','s','y','s','a','r','m','3','2','\\',0};
static const WCHAR sysx8664[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\','s','y','s','x','8','6','6','4','\\',0};
static const WCHAR sysarm64[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\','s','y','s','a','r','m','6','4','\\',0};
if (machine == native_machine) machine = IMAGE_FILE_MACHINE_TARGET_HOST;
switch (machine)
{
case IMAGE_FILE_MACHINE_TARGET_HOST: return system32;
case IMAGE_FILE_MACHINE_I386: return syswow64;
case IMAGE_FILE_MACHINE_ARMNT: return sysarm32;
case IMAGE_FILE_MACHINE_AMD64: return sysx8664;
case IMAGE_FILE_MACHINE_ARM64: return sysarm64;
default: return NULL;
}
}
/***************************************************************************
* is_builtin_path
*
* Check if path is inside a system directory, to support loading builtins
* when the corresponding file doesn't exist yet.
*/
BOOL is_builtin_path( const UNICODE_STRING *path, WORD *machine )
{
unsigned int i, len = path->Length / sizeof(WCHAR), dirlen;
const WCHAR *sysdir, *p = path->Buffer;
/* only fake builtin existence during prefix bootstrap */
if (!is_prefix_bootstrap) return FALSE;
for (i = 0; i < supported_machines_count; i++)
{
sysdir = get_machine_wow64_dir( supported_machines[i] );
dirlen = wcslen( sysdir );
if (len <= dirlen) continue;
if (wcsnicmp( p, sysdir, dirlen )) continue;
/* check for remaining path components */
for (p += dirlen, len -= dirlen; len; p++, len--) if (*p == '\\') return FALSE;
*machine = supported_machines[i];
return TRUE;
}
return FALSE;
}
/***********************************************************************
* open_main_image
*/
static NTSTATUS open_main_image( WCHAR *image, void **module, SECTION_IMAGE_INFORMATION *info,
enum loadorder loadorder )
{
static const WCHAR soW[] = {'.','s','o',0};
UNICODE_STRING nt_name;
OBJECT_ATTRIBUTES attr;
pe_image_info_t pe_info;
SIZE_T size = 0;
char *unix_name;
NTSTATUS status;
HANDLE mapping;
WCHAR *p;
if (loadorder == LO_DISABLED) NtTerminateProcess( GetCurrentProcess(), STATUS_DLL_NOT_FOUND );
init_unicode_string( &nt_name, image );
InitializeObjectAttributes( &attr, &nt_name, OBJ_CASE_INSENSITIVE, 0, NULL );
if (nt_to_unix_file_name( &attr, &unix_name, FILE_OPEN )) return STATUS_DLL_NOT_FOUND;
status = open_dll_file( unix_name, &attr, &mapping );
if (!status)
{
*module = NULL;
status = NtMapViewOfSection( mapping, NtCurrentProcess(), module, 0, 0, NULL, &size,
ViewShare, 0, PAGE_EXECUTE_READ );
if (!status)
{
NtQuerySection( mapping, SectionImageInformation, info, sizeof(*info), NULL );
if (info->u.s.ComPlusNativeReady) info->Machine = native_machine;
}
NtClose( mapping );
}
else if (status == STATUS_INVALID_IMAGE_NOT_MZ && loadorder != LO_NATIVE)
{
/* remove .so extension from Windows name */
p = image + wcslen(image);
if (p - image > 3 && !wcsicmp( p - 3, soW ))
{
p[-3] = 0;
nt_name.Length -= 3 * sizeof(WCHAR);
}
status = dlopen_dll( unix_name, &nt_name, module, &pe_info, FALSE );
if (!status) virtual_fill_image_information( &pe_info, info );
}
free( unix_name );
return status;
}
/***********************************************************************
* load_main_exe
*/
NTSTATUS load_main_exe( const WCHAR *dos_name, const char *unix_name, const WCHAR *curdir,
WCHAR **image, void **module )
{
enum loadorder loadorder = LO_INVALID;
UNICODE_STRING nt_name;
WCHAR *tmp = NULL;
BOOL contains_path;
NTSTATUS status;
SIZE_T size;
struct stat st;
WORD machine;
/* special case for Unix file name */
if (unix_name && unix_name[0] == '/' && !stat( unix_name, &st ))
{
if ((status = unix_to_nt_file_name( unix_name, image ))) goto failed;
init_unicode_string( &nt_name, *image );
loadorder = get_load_order( &nt_name );
status = open_main_image( *image, module, &main_image_info, loadorder );
if (status != STATUS_DLL_NOT_FOUND) return status;
free( *image );
}
if (!dos_name)
{
dos_name = tmp = malloc( (strlen(unix_name) + 1) * sizeof(WCHAR) );
ntdll_umbstowcs( unix_name, strlen(unix_name) + 1, tmp, strlen(unix_name) + 1 );
}
contains_path = (wcschr( dos_name, '/' ) ||
wcschr( dos_name, '\\' ) ||
(dos_name[0] && dos_name[1] == ':'));
if ((status = get_full_path( dos_name, curdir, image ))) goto failed;
free( tmp );
init_unicode_string( &nt_name, *image );
if (loadorder == LO_INVALID) loadorder = get_load_order( &nt_name );
status = open_main_image( *image, module, &main_image_info, loadorder );
if (status != STATUS_DLL_NOT_FOUND) return status;
/* if path is in system dir, we can load the builtin even if the file itself doesn't exist */
if (loadorder != LO_NATIVE && is_builtin_path( &nt_name, &machine ))
{
status = find_builtin_dll( &nt_name, module, &size, &main_image_info, machine, FALSE );
if (status != STATUS_DLL_NOT_FOUND) return status;
}
if (!contains_path) return STATUS_DLL_NOT_FOUND;
failed:
MESSAGE( "wine: failed to open %s: %x\n",
unix_name ? debugstr_a(unix_name) : debugstr_w(dos_name), status );
NtTerminateProcess( GetCurrentProcess(), status );
return status; /* unreached */
}
/***********************************************************************
* load_start_exe
*
* Load start.exe as main image.
*/
NTSTATUS load_start_exe( WCHAR **image, void **module )
{
static const WCHAR startW[] = {'s','t','a','r','t','.','e','x','e',0};
UNICODE_STRING nt_name;
NTSTATUS status;
SIZE_T size;
*image = malloc( sizeof("\\??\\C:\\windows\\system32\\start.exe") * sizeof(WCHAR) );
wcscpy( *image, get_machine_wow64_dir( current_machine ));
wcscat( *image, startW );
init_unicode_string( &nt_name, *image );
status = find_builtin_dll( &nt_name, module, &size, &main_image_info, current_machine, FALSE );
if (status)
{
MESSAGE( "wine: failed to load start.exe: %x\n", status );
NtTerminateProcess( GetCurrentProcess(), status );
}
return status;
}
#ifdef __FreeBSD__
/* The PT_LOAD segments are sorted in increasing order, and the first
* starts at the beginning of the ELF file. By parsing the file, we can
* find that first PT_LOAD segment, from which we can find the base
* address it wanted, and knowing mapbase where the binary was actually
* loaded, use them to work out the relocbase offset. */
static BOOL get_relocbase(caddr_t mapbase, caddr_t *relocbase)
{
Elf_Half i;
#ifdef _WIN64
const Elf64_Ehdr *elf_header = (Elf64_Ehdr*) mapbase;
#else
const Elf32_Ehdr *elf_header = (Elf32_Ehdr*) mapbase;
#endif
const Elf_Phdr *prog_header = (const Elf_Phdr *)(mapbase + elf_header->e_phoff);
for (i = 0; i < elf_header->e_phnum; i++)
{
if (prog_header->p_type == PT_LOAD)
{
caddr_t desired_base = (caddr_t)((prog_header->p_vaddr / prog_header->p_align) * prog_header->p_align);
*relocbase = (caddr_t) (mapbase - desired_base);
return TRUE;
}
prog_header++;
}
return FALSE;
}
#endif
/*************************************************************************
* init_builtin_dll
*/
static void CDECL init_builtin_dll( void *module )
{
#ifdef HAVE_DLINFO
void *handle = NULL;
struct link_map *map;
void (*init_func)(int, char **, char **) = NULL;
void (**init_array)(int, char **, char **) = NULL;
ULONG_PTR i, init_arraysz = 0;
#ifdef _WIN64
const Elf64_Dyn *dyn;
#else
const Elf32_Dyn *dyn;
#endif
if (!(handle = get_builtin_so_handle( module ))) return;
if (dlinfo( handle, RTLD_DI_LINKMAP, &map )) map = NULL;
release_builtin_module( module );
if (!map) return;
for (dyn = map->l_ld; dyn->d_tag; dyn++)
{
caddr_t relocbase = (caddr_t)map->l_addr;
#ifdef __FreeBSD__
/* On older FreeBSD versions, l_addr was the absolute load address, now it's the relocation offset. */
if (offsetof(struct link_map, l_addr) == 0)
if (!get_relocbase(map->l_addr, &relocbase))
return;
#endif
switch (dyn->d_tag)
{
case 0x60009990: init_array = (void *)(relocbase + dyn->d_un.d_val); break;
case 0x60009991: init_arraysz = dyn->d_un.d_val; break;
case 0x60009992: init_func = (void *)(relocbase + dyn->d_un.d_val); break;
}
}
TRACE( "%p: got init_func %p init_array %p %lu\n", module, init_func, init_array, init_arraysz );
if (init_func) init_func( main_argc, main_argv, main_envp );
if (init_array)
for (i = 0; i < init_arraysz / sizeof(*init_array); i++)
init_array[i]( main_argc, main_argv, main_envp );
#endif
}
/***********************************************************************
* load_ntdll
*/
static void load_ntdll(void)
{
static WCHAR path[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s','\\',
's','y','s','t','e','m','3','2','\\','n','t','d','l','l','.','d','l','l',0};
const char *pe_dir = get_pe_dir( current_machine );
NTSTATUS status;
SECTION_IMAGE_INFORMATION info;
OBJECT_ATTRIBUTES attr;
UNICODE_STRING str;
void *module;
SIZE_T size = 0;
char *name;
init_unicode_string( &str, path );
InitializeObjectAttributes( &attr, &str, 0, 0, NULL );
name = malloc( strlen( ntdll_dir ) + strlen( pe_dir ) + sizeof("/ntdll.dll.so") );
if (build_dir) sprintf( name, "%s/ntdll.dll", ntdll_dir );
else sprintf( name, "%s%s/ntdll.dll", dll_dir, pe_dir );
status = open_builtin_pe_file( name, &attr, &module, &size, &info, current_machine, FALSE );
if (status == STATUS_DLL_NOT_FOUND)
{
sprintf( name, "%s/ntdll.dll.so", ntdll_dir );
status = open_builtin_so_file( name, &attr, &module, &info, current_machine, FALSE );
}
if (status == STATUS_IMAGE_NOT_AT_BASE) relocate_ntdll( module );
else if (status) fatal_error( "failed to load %s error %x\n", name, status );
free( name );
load_ntdll_functions( module );
ntdll_module = module;
}
/***********************************************************************
* load_wow64_ntdll
*/
static void load_wow64_ntdll( USHORT machine )
{
static const WCHAR ntdllW[] = {'n','t','d','l','l','.','d','l','l',0};
SECTION_IMAGE_INFORMATION info;
UNICODE_STRING nt_name;
void *module;
NTSTATUS status;
SIZE_T size;
WCHAR *path = malloc( sizeof("\\??\\C:\\windows\\system32\\ntdll.dll") * sizeof(WCHAR) );
wcscpy( path, get_machine_wow64_dir( machine ));
wcscat( path, ntdllW );
init_unicode_string( &nt_name, path );
status = find_builtin_dll( &nt_name, &module, &size, &info, machine, FALSE );
switch (status)
{
case STATUS_IMAGE_NOT_AT_BASE:
relocate_ntdll( module );
/* fall through */
case STATUS_SUCCESS:
load_ntdll_wow64_functions( module );
TRACE("loaded %s at %p\n", debugstr_w(path), module );
break;
default:
ERR( "failed to load %s error %x\n", debugstr_w(path), status );
break;
}
free( path );
}
/***********************************************************************
* get_image_address
*/
static ULONG_PTR get_image_address(void)
{
#ifdef HAVE_GETAUXVAL
ULONG_PTR size, num, phdr_addr = getauxval( AT_PHDR );
ElfW(Phdr) *phdr;
if (!phdr_addr) return 0;
phdr = (ElfW(Phdr) *)phdr_addr;
size = getauxval( AT_PHENT );
num = getauxval( AT_PHNUM );
while (num--)
{
if (phdr->p_type == PT_PHDR) return phdr_addr - phdr->p_offset;
phdr = (ElfW(Phdr) *)((char *)phdr + size);
}
#elif defined(__APPLE__) && defined(TASK_DYLD_INFO)
struct task_dyld_info dyld_info;
mach_msg_type_number_t size = TASK_DYLD_INFO_COUNT;
if (task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&dyld_info, &size) == KERN_SUCCESS)
return dyld_info.all_image_info_addr;
#endif
return 0;
}
/* math function wrappers */
static double CDECL ntdll_atan( double d ) { return atan( d ); }
static double CDECL ntdll_ceil( double d ) { return ceil( d ); }
static double CDECL ntdll_cos( double d ) { return cos( d ); }
static double CDECL ntdll_fabs( double d ) { return fabs( d ); }
static double CDECL ntdll_floor( double d ) { return floor( d ); }
static double CDECL ntdll_log( double d ) { return log( d ); }
static double CDECL ntdll_pow( double x, double y ) { return pow( x, y ); }
static double CDECL ntdll_sin( double d ) { return sin( d ); }
static double CDECL ntdll_sqrt( double d ) { return sqrt( d ); }
static double CDECL ntdll_tan( double d ) { return tan( d ); }
/***********************************************************************
* unix_funcs
*/
static struct unix_funcs unix_funcs =
{
#ifdef __aarch64__
NtCurrentTeb,
#endif
DbgUiIssueRemoteBreakin,
RtlGetSystemTimePrecise,
RtlWaitOnAddress,
RtlWakeAddressAll,
RtlWakeAddressSingle,
fast_RtlpWaitForCriticalSection,
fast_RtlpUnWaitCriticalSection,
fast_RtlDeleteCriticalSection,
fast_RtlTryAcquireSRWLockExclusive,
fast_RtlAcquireSRWLockExclusive,
fast_RtlTryAcquireSRWLockShared,
fast_RtlAcquireSRWLockShared,
fast_RtlReleaseSRWLockExclusive,
fast_RtlReleaseSRWLockShared,
fast_RtlWakeConditionVariable,
fast_wait_cv,
ntdll_atan,
ntdll_ceil,
ntdll_cos,
ntdll_fabs,
ntdll_floor,
ntdll_log,
ntdll_pow,
ntdll_sin,
ntdll_sqrt,
ntdll_tan,
load_so_dll,
init_builtin_dll,
init_unix_lib,
unwind_builtin_dll,
};
/***********************************************************************
* start_main_thread
*/
static void start_main_thread(void)
{
SYSTEM_SERVICE_TABLE syscall_table = { (ULONG_PTR *)syscalls, NULL, ARRAY_SIZE(syscalls), syscall_args };
NTSTATUS status;
TEB *teb = virtual_alloc_first_teb();
signal_init_threading();
signal_alloc_thread( teb );
signal_init_thread( teb );
dbg_init();
startup_info_size = server_init_process();
virtual_map_user_shared_data();
init_cpu_info();
init_files();
load_libwine();
init_startup_info();
if (p___wine_main_argc) *p___wine_main_argc = main_argc;
if (p___wine_main_argv) *p___wine_main_argv = main_argv;
if (p___wine_main_wargv) *p___wine_main_wargv = main_wargv;
*(ULONG_PTR *)&peb->CloudFileFlags = get_image_address();
set_load_order_app_name( main_wargv[0] );
init_thread_stack( teb, is_win64 ? 0x7fffffff : 0, 0, 0 );
NtCreateKeyedEvent( &keyed_event, GENERIC_READ | GENERIC_WRITE, NULL, 0 );
load_ntdll();
if (main_image_info.Machine != current_machine) load_wow64_ntdll( main_image_info.Machine );
ntdll_init_syscalls( 0, &syscall_table, p__wine_syscall_dispatcher );
status = p__wine_set_unix_funcs( NTDLL_UNIXLIB_VERSION, &unix_funcs );
if (status == STATUS_REVISION_MISMATCH)
{
ERR( "ntdll library version mismatch\n" );
NtTerminateProcess( GetCurrentProcess(), status );
}
server_init_process_done();
}
#ifdef __ANDROID__
#ifndef WINE_JAVA_CLASS
#define WINE_JAVA_CLASS "org/winehq/wine/WineActivity"
#endif
JavaVM *java_vm = NULL;
jobject java_object = 0;
unsigned short java_gdt_sel = 0;
/* main Wine initialisation */
static jstring wine_init_jni( JNIEnv *env, jobject obj, jobjectArray cmdline, jobjectArray environment )
{
char **argv;
char *str;
char error[1024];
int i, argc, length;
/* get the command line array */
argc = (*env)->GetArrayLength( env, cmdline );
for (i = length = 0; i < argc; i++)
{
jobject str_obj = (*env)->GetObjectArrayElement( env, cmdline, i );
length += (*env)->GetStringUTFLength( env, str_obj ) + 1;
}
argv = malloc( (argc + 1) * sizeof(*argv) + length );
str = (char *)(argv + argc + 1);
for (i = 0; i < argc; i++)
{
jobject str_obj = (*env)->GetObjectArrayElement( env, cmdline, i );
length = (*env)->GetStringUTFLength( env, str_obj );
(*env)->GetStringUTFRegion( env, str_obj, 0,
(*env)->GetStringLength( env, str_obj ), str );
argv[i] = str;
str[length] = 0;
str += length + 1;
}
argv[argc] = NULL;
/* set the environment variables */
if (environment)
{
int count = (*env)->GetArrayLength( env, environment );
for (i = 0; i < count - 1; i += 2)
{
jobject var_obj = (*env)->GetObjectArrayElement( env, environment, i );
jobject val_obj = (*env)->GetObjectArrayElement( env, environment, i + 1 );
const char *var = (*env)->GetStringUTFChars( env, var_obj, NULL );
if (val_obj)
{
const char *val = (*env)->GetStringUTFChars( env, val_obj, NULL );
setenv( var, val, 1 );
if (!strcmp( var, "LD_LIBRARY_PATH" ))
{
void (*update_func)( const char * ) = dlsym( RTLD_DEFAULT,
"android_update_LD_LIBRARY_PATH" );
if (update_func) update_func( val );
}
else if (!strcmp( var, "WINEDEBUGLOG" ))
{
int fd = open( val, O_WRONLY | O_CREAT | O_APPEND, 0666 );
if (fd != -1)
{
dup2( fd, 2 );
close( fd );
}
}
(*env)->ReleaseStringUTFChars( env, val_obj, val );
}
else unsetenv( var );
(*env)->ReleaseStringUTFChars( env, var_obj, var );
}
}
java_object = (*env)->NewGlobalRef( env, obj );
init_paths( argv );
virtual_init();
init_environment( argc, argv, environ );
#ifdef __i386__
{
unsigned short java_fs;
__asm__( "mov %%fs,%0" : "=r" (java_fs) );
if (!(java_fs & 4)) java_gdt_sel = java_fs;
__asm__( "mov %0,%%fs" :: "r" (0) );
start_main_thread();
__asm__( "mov %0,%%fs" :: "r" (java_fs) );
}
#else
start_main_thread();
#endif
return (*env)->NewStringUTF( env, error );
}
jint JNI_OnLoad( JavaVM *vm, void *reserved )
{
static const JNINativeMethod method =
{
"wine_init", "([Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/String;", wine_init_jni
};
JNIEnv *env;
jclass class;
java_vm = vm;
if ((*vm)->AttachCurrentThread( vm, &env, NULL ) != JNI_OK) return JNI_ERR;
if (!(class = (*env)->FindClass( env, WINE_JAVA_CLASS ))) return JNI_ERR;
(*env)->RegisterNatives( env, class, &method, 1 );
return JNI_VERSION_1_6;
}
#endif /* __ANDROID__ */
#ifdef __APPLE__
static void *apple_wine_thread( void *arg )
{
start_main_thread();
return NULL;
}
/***********************************************************************
* apple_create_wine_thread
*
* Spin off a secondary thread to complete Wine initialization, leaving
* the original thread for the Mac frameworks.
*
* Invoked as a CFRunLoopSource perform callback.
*/
static void apple_create_wine_thread( void *arg )
{
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init( &attr );
pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
if (pthread_create( &thread, &attr, apple_wine_thread, NULL )) exit(1);
pthread_attr_destroy( &attr );
}
/***********************************************************************
* apple_main_thread
*
* Park the process's original thread in a Core Foundation run loop for
* use by the Mac frameworks, especially receiving and handling
* distributed notifications. Spin off a new thread for the rest of the
* Wine initialization.
*/
static void apple_main_thread(void)
{
CFRunLoopSourceContext source_context = { 0 };
CFRunLoopSourceRef source;
if (!pthread_main_np()) return;
/* Multi-processing Services can get confused about the main thread if the
* first time it's used is on a secondary thread. Use it here to make sure
* that doesn't happen. */
MPTaskIsPreemptive(MPCurrentTaskID());
/* Give ourselves the best chance of having the distributed notification
* center scheduled on this thread's run loop. In theory, it's scheduled
* in the first thread to ask for it. */
CFNotificationCenterGetDistributedCenter();
/* We use this run loop source for two purposes. First, a run loop exits
* if it has no more sources scheduled. So, we need at least one source
* to keep the run loop running. Second, although it's not critical, it's
* preferable for the Wine initialization to not proceed until we know
* the run loop is running. So, we signal our source immediately after
* adding it and have its callback spin off the Wine thread. */
source_context.perform = apple_create_wine_thread;
source = CFRunLoopSourceCreate( NULL, 0, &source_context );
CFRunLoopAddSource( CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes );
CFRunLoopSourceSignal( source );
CFRelease( source );
CFRunLoopRun(); /* Should never return, except on error. */
}
#endif /* __APPLE__ */
#ifdef __ANDROID__
static int pre_exec(void)
{
#if defined(__i386__) || defined(__x86_64__)
return 1; /* we have a preloader */
#else
return 0; /* no exec needed */
#endif
}
#elif defined(__linux__) && (defined(__i386__) || defined(__arm__))
static void check_vmsplit( void *stack )
{
if (stack < (void *)0x80000000)
{
/* if the stack is below 0x80000000, assume we can safely try a munmap there */
if (munmap( (void *)0x80000000, 1 ) == -1 && errno == EINVAL)
ERR( "Warning: memory above 0x80000000 doesn't seem to be accessible.\n"
"Wine requires a 3G/1G user/kernel memory split to work properly.\n" );
}
}
static int pre_exec(void)
{
int temp;
check_vmsplit( &temp );
return 1; /* we have a preloader on x86/arm */
}
#elif defined(__linux__) && (defined(__x86_64__) || defined(__aarch64__))
static int pre_exec(void)
{
return 1; /* we have a preloader on x86-64/arm64 */
}
#elif defined(__APPLE__) && (defined(__i386__) || defined(__x86_64__))
static int pre_exec(void)
{
return 1; /* we have a preloader */
}
#elif (defined(__FreeBSD__) || defined (__FreeBSD_kernel__) || defined(__DragonFly__))
static int pre_exec(void)
{
struct rlimit rl;
rl.rlim_cur = 0x02000000;
rl.rlim_max = 0x02000000;
setrlimit( RLIMIT_DATA, &rl );
return 1;
}
#else
static int pre_exec(void)
{
return 0; /* no exec needed */
}
#endif
/***********************************************************************
* check_command_line
*
* Check if command line is one that needs to be handled specially.
*/
static void check_command_line( int argc, char *argv[] )
{
static const char usage[] =
"Usage: wine PROGRAM [ARGUMENTS...] Run the specified program\n"
" wine --help Display this help and exit\n"
" wine --version Output version information and exit";
if (argc <= 1)
{
fprintf( stderr, "%s\n", usage );
exit(1);
}
if (!strcmp( argv[1], "--help" ))
{
printf( "%s\n", usage );
exit(0);
}
if (!strcmp( argv[1], "--version" ))
{
printf( "%s\n", wine_build );
exit(0);
}
}
/***********************************************************************
* __wine_main
*
* Main entry point called by the wine loader.
*/
void __wine_main( int argc, char *argv[], char *envp[] )
{
init_paths( argv );
if (!getenv( "WINELOADERNOEXEC" )) /* first time around */
{
check_command_line( argc, argv );
if (pre_exec())
{
static char noexec[] = "WINELOADERNOEXEC=1";
char **new_argv = malloc( (argc + 2) * sizeof(*argv) );
memcpy( new_argv + 1, argv, (argc + 1) * sizeof(*argv) );
putenv( noexec );
loader_exec( argv0, new_argv, current_machine );
fatal_error( "could not exec the wine loader\n" );
}
}
#ifdef RLIMIT_NOFILE
set_max_limit( RLIMIT_NOFILE );
#endif
#ifdef RLIMIT_AS
set_max_limit( RLIMIT_AS );
#endif
virtual_init();
init_environment( argc, argv, envp );
#ifdef __APPLE__
apple_main_thread();
#endif
start_main_thread();
}