loader: Implement preloader for macOS.
Signed-off-by: Ken Thomases <ken@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
bd2d795240
commit
a0ab2a7b0c
@ -87,6 +87,7 @@ conf_manext = 5
|
||||
WINELOADER_PROGRAMS = @WINELOADER_PROGRAMS@
|
||||
WINELOADER_DEPENDS = @WINELOADER_DEPENDS@
|
||||
WINELOADER_LDFLAGS = @WINELOADER_LDFLAGS@
|
||||
WINEPRELOADER_LDFLAGS = @WINEPRELOADER_LDFLAGS@
|
||||
LIBWINE_SHAREDLIB = @LIBWINE_SHAREDLIB@
|
||||
LIBWINE_IMPORTLIB = @LIBWINE_IMPORTLIB@
|
||||
LIBWINE_LDFLAGS = @LIBWINE_LDFLAGS@
|
||||
|
46
configure
vendored
46
configure
vendored
@ -734,6 +734,7 @@ SUBDIRS
|
||||
READELF
|
||||
OTOOL
|
||||
LDD
|
||||
WINEPRELOADER_LDFLAGS
|
||||
WINELOADER_LDFLAGS
|
||||
TOP_INSTALL_DEV
|
||||
TOP_INSTALL_LIB
|
||||
@ -7756,6 +7757,8 @@ TOP_INSTALL_DEV=""
|
||||
|
||||
WINELOADER_LDFLAGS=""
|
||||
|
||||
WINEPRELOADER_LDFLAGS=""
|
||||
|
||||
LIBEXT="so"
|
||||
DLLEXT=".so"
|
||||
IMPLIBEXT="def"
|
||||
@ -8591,7 +8594,35 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
|
||||
|
||||
APPKIT_LIBS="-framework AppKit"
|
||||
|
||||
WINELOADER_LDFLAGS="-image_base 0x7bf00000 -Wl,-pagezero_size,0x1000,-segaddr,WINE_DOS,0x00001000,-segaddr,WINE_SHAREDHEAP,0x7f000000,-sectcreate,__TEXT,__info_plist,wine_info.plist"
|
||||
|
||||
WINELOADER_LDFLAGS="-Wl,-pie,-pagezero_size,0x1000,-sectcreate,__TEXT,__info_plist,wine_info.plist"
|
||||
|
||||
WINEPRELOADER_LDFLAGS="-nostartfiles -nodefaultlibs -e _start -ldylib1.o -Wl,-image_base,0x7c400000,-pagezero_size,0x1000,-sectcreate,__TEXT,__info_plist,wine_info.plist"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,-no_new_main -e _main" >&5
|
||||
$as_echo_n "checking whether the compiler supports -Wl,-no_new_main -e _main... " >&6; }
|
||||
if ${ac_cv_cflags__Wl__no_new_main__e__main+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_wine_try_cflags_saved=$CFLAGS
|
||||
CFLAGS="$CFLAGS -Wl,-no_new_main -e _main"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
int main(int argc, char **argv) { return 0; }
|
||||
_ACEOF
|
||||
if ac_fn_c_try_link "$LINENO"; then :
|
||||
ac_cv_cflags__Wl__no_new_main__e__main=yes
|
||||
else
|
||||
ac_cv_cflags__Wl__no_new_main__e__main=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
CFLAGS=$ac_wine_try_cflags_saved
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl__no_new_main__e__main" >&5
|
||||
$as_echo "$ac_cv_cflags__Wl__no_new_main__e__main" >&6; }
|
||||
if test "x$ac_cv_cflags__Wl__no_new_main__e__main" = xyes; then :
|
||||
WINEPRELOADER_LDFLAGS="-Wl,-no_new_main $WINEPRELOADER_LDFLAGS"
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -Wl,-no_pie" >&5
|
||||
$as_echo_n "checking whether the compiler supports -Wl,-no_pie... " >&6; }
|
||||
if ${ac_cv_cflags__Wl__no_pie+:} false; then :
|
||||
@ -8615,8 +8646,9 @@ fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags__Wl__no_pie" >&5
|
||||
$as_echo "$ac_cv_cflags__Wl__no_pie" >&6; }
|
||||
if test "x$ac_cv_cflags__Wl__no_pie" = xyes; then :
|
||||
WINELOADER_LDFLAGS="-Wl,-no_pie $WINELOADER_LDFLAGS"
|
||||
WINEPRELOADER_LDFLAGS="-Wl,-no_pie $WINEPRELOADER_LDFLAGS"
|
||||
fi
|
||||
|
||||
if test "$ac_cv_header_DiskArbitration_DiskArbitration_h" = "yes"
|
||||
then
|
||||
DISKARBITRATION_LIBS="-framework DiskArbitration -framework CoreFoundation"
|
||||
@ -8838,6 +8870,7 @@ $as_echo "$ac_cv_cflags__fPIC__Wl___export_dynamic" >&6; }
|
||||
if test "x$ac_cv_cflags__fPIC__Wl___export_dynamic" = xyes; then :
|
||||
WINELOADER_LDFLAGS="-Wl,--export-dynamic"
|
||||
fi
|
||||
WINEPRELOADER_LDFLAGS="-static -nostartfiles -nodefaultlibs -Wl,-Ttext=0x7c400000"
|
||||
as_ac_var=`$as_echo "ac_cv_cflags_-fPIC -Wl,--rpath,\\$ORIGIN/../lib" | $as_tr_sh`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fPIC -Wl,--rpath,\$ORIGIN/../lib" >&5
|
||||
$as_echo_n "checking whether the compiler supports -fPIC -Wl,--rpath,\$ORIGIN/../lib... " >&6; }
|
||||
@ -9177,6 +9210,7 @@ $as_echo "$ac_cv_cflags__fPIC__Wl___export_dynamic" >&6; }
|
||||
if test "x$ac_cv_cflags__fPIC__Wl___export_dynamic" = xyes; then :
|
||||
WINELOADER_LDFLAGS="-Wl,--export-dynamic"
|
||||
fi
|
||||
WINEPRELOADER_LDFLAGS="-static -nostartfiles -nodefaultlibs -Wl,-Ttext=0x7c400000"
|
||||
|
||||
as_ac_var=`$as_echo "ac_cv_cflags_-fPIC -Wl,--rpath,\\$ORIGIN/../lib" | $as_tr_sh`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports -fPIC -Wl,--rpath,\$ORIGIN/../lib" >&5
|
||||
@ -16478,6 +16512,14 @@ case $host_os in
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
darwin*|macosx*)
|
||||
case $host_cpu in
|
||||
*i[3456789]86*|x86_64*)
|
||||
test "$wine_binary" = wine || wine_fn_append_file CONFIGURE_TARGETS "loader/wine-preloader"
|
||||
WINELOADER_PROGRAMS="$WINELOADER_PROGRAMS $wine_binary-preloader"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
|
21
configure.ac
21
configure.ac
@ -707,6 +707,7 @@ AC_SUBST(LDEXECFLAGS,"")
|
||||
AC_SUBST(TOP_INSTALL_LIB,"")
|
||||
AC_SUBST(TOP_INSTALL_DEV,"")
|
||||
AC_SUBST(WINELOADER_LDFLAGS,"")
|
||||
AC_SUBST(WINEPRELOADER_LDFLAGS,"")
|
||||
LIBEXT="so"
|
||||
DLLEXT=".so"
|
||||
IMPLIBEXT="def"
|
||||
@ -767,9 +768,15 @@ case $host_os in
|
||||
AC_SUBST(APPLICATIONSERVICES_LIBS,"-framework ApplicationServices")
|
||||
AC_SUBST(CORESERVICES_LIBS,"-framework CoreServices")
|
||||
AC_SUBST(APPKIT_LIBS,"-framework AppKit")
|
||||
WINELOADER_LDFLAGS="-image_base 0x7bf00000 -Wl,-pagezero_size,0x1000,-segaddr,WINE_DOS,0x00001000,-segaddr,WINE_SHAREDHEAP,0x7f000000,-sectcreate,__TEXT,__info_plist,wine_info.plist"
|
||||
|
||||
WINELOADER_LDFLAGS="-Wl,-pie,-pagezero_size,0x1000,-sectcreate,__TEXT,__info_plist,wine_info.plist"
|
||||
|
||||
WINEPRELOADER_LDFLAGS="-nostartfiles -nodefaultlibs -e _start -ldylib1.o -Wl,-image_base,0x7c400000,-pagezero_size,0x1000,-sectcreate,__TEXT,__info_plist,wine_info.plist"
|
||||
WINE_TRY_CFLAGS([-Wl,-no_new_main -e _main],
|
||||
[WINEPRELOADER_LDFLAGS="-Wl,-no_new_main $WINEPRELOADER_LDFLAGS"])
|
||||
WINE_TRY_CFLAGS([-Wl,-no_pie],
|
||||
[WINELOADER_LDFLAGS="-Wl,-no_pie $WINELOADER_LDFLAGS"])
|
||||
[WINEPRELOADER_LDFLAGS="-Wl,-no_pie $WINEPRELOADER_LDFLAGS"])
|
||||
|
||||
if test "$ac_cv_header_DiskArbitration_DiskArbitration_h" = "yes"
|
||||
then
|
||||
dnl DiskArbitration API is not public on Darwin < 8.0, use it only if header found
|
||||
@ -885,6 +892,7 @@ case $host_os in
|
||||
enable_wineandroid_drv=${enable_wineandroid_drv:-yes}
|
||||
WINE_TRY_CFLAGS([-fPIC -Wl,--export-dynamic],
|
||||
[WINELOADER_LDFLAGS="-Wl,--export-dynamic"])
|
||||
WINEPRELOADER_LDFLAGS="-static -nostartfiles -nodefaultlibs -Wl,-Ttext=0x7c400000"
|
||||
WINE_TRY_CFLAGS([-fPIC -Wl,--rpath,\$ORIGIN/../lib],
|
||||
[LDRPATH_INSTALL="-Wl,--rpath,\\\$\$ORIGIN/\`\$(MAKEDEP) -R \${bindir} \${libdir}\`"
|
||||
LDRPATH_LOCAL="-Wl,--rpath,\\\$\$ORIGIN/\$(top_builddir)/libs/wine"],
|
||||
@ -932,6 +940,7 @@ case $host_os in
|
||||
|
||||
WINE_TRY_CFLAGS([-fPIC -Wl,--export-dynamic],
|
||||
[WINELOADER_LDFLAGS="-Wl,--export-dynamic"])
|
||||
WINEPRELOADER_LDFLAGS="-static -nostartfiles -nodefaultlibs -Wl,-Ttext=0x7c400000"
|
||||
|
||||
WINE_TRY_CFLAGS([-fPIC -Wl,--rpath,\$ORIGIN/../lib],
|
||||
[LDRPATH_INSTALL="-Wl,--rpath,\\\$\$ORIGIN/\`\$(MAKEDEP) -R \${bindir} \${libdir}\`"
|
||||
@ -2111,6 +2120,14 @@ case $host_os in
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
darwin*|macosx*)
|
||||
case $host_cpu in
|
||||
*i[[3456789]]86*|x86_64*)
|
||||
test "$wine_binary" = wine || WINE_IGNORE_FILE("loader/wine-preloader")
|
||||
WINELOADER_PROGRAMS="$WINELOADER_PROGRAMS $wine_binary-preloader"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
dnl **** Check for functions ****
|
||||
|
@ -2417,14 +2417,20 @@ void virtual_release_address_space(void)
|
||||
if (range.limit > range.base)
|
||||
{
|
||||
while (wine_mmap_enum_reserved_areas( free_reserved_memory, &range, 1 )) /* nothing */;
|
||||
#ifdef __APPLE__
|
||||
/* On macOS, we still want to free some of low memory, for OpenGL resources */
|
||||
range.base = (char *)0x40000000;
|
||||
#else
|
||||
range.base = NULL;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
range.base = (char *)0x20000000;
|
||||
|
||||
if (range.base)
|
||||
{
|
||||
#ifndef __APPLE__ /* dyld doesn't support parts of the WINE_DOS segment being unmapped */
|
||||
range.base = (char *)0x20000000;
|
||||
range.limit = (char *)0x7f000000;
|
||||
while (wine_mmap_enum_reserved_areas( free_reserved_memory, &range, 0 )) /* nothing */;
|
||||
#endif
|
||||
}
|
||||
|
||||
server_leave_uninterrupted_section( &csVirtual, &sigset );
|
||||
|
@ -573,7 +573,7 @@ void wine_exec_wine_binary( const char *name, char **argv, const char *env_var )
|
||||
|
||||
if (!name) name = argv0_name; /* no name means default loader */
|
||||
|
||||
#ifdef linux
|
||||
#if defined(linux) || defined(__APPLE__)
|
||||
use_preloader = !strendswith( name, "wineserver" );
|
||||
#else
|
||||
use_preloader = 0;
|
||||
|
@ -2,6 +2,7 @@ SOURCES = \
|
||||
l_intl.nls \
|
||||
main.c \
|
||||
preloader.c \
|
||||
preloader_mac.c \
|
||||
wine.de.UTF-8.man.in \
|
||||
wine.desktop \
|
||||
wine.fr.UTF-8.man.in \
|
||||
@ -23,8 +24,8 @@ wine64_OBJS = main.o
|
||||
wine64_DEPS = $(WINELOADER_DEPENDS)
|
||||
wine64_LDFLAGS = $(WINELOADER_LDFLAGS) $(LDEXECFLAGS) -lwine $(PTHREAD_LIBS)
|
||||
|
||||
wine_preloader_OBJS = preloader.o
|
||||
wine_preloader_LDFLAGS = -static -nostartfiles -nodefaultlibs -Wl,-Ttext=0x7c400000
|
||||
wine_preloader_OBJS = preloader.o preloader_mac.o
|
||||
wine_preloader_LDFLAGS = $(WINEPRELOADER_LDFLAGS)
|
||||
|
||||
wine64_preloader_OBJS = preloader.o
|
||||
wine64_preloader_LDFLAGS = -static -nostartfiles -nodefaultlibs -Wl,-Ttext=0x7c400000
|
||||
wine64_preloader_OBJS = preloader.o preloader_mac.o
|
||||
wine64_preloader_LDFLAGS = $(WINEPRELOADER_LDFLAGS)
|
||||
|
@ -41,44 +41,9 @@
|
||||
#include "wine/library.h"
|
||||
#include "main.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#ifndef __clang__
|
||||
__asm__(".zerofill WINE_DOS, WINE_DOS, ___wine_dos, 0x40000000");
|
||||
__asm__(".zerofill WINE_SHAREDHEAP, WINE_SHAREDHEAP, ___wine_shared_heap, 0x03000000");
|
||||
extern char __wine_dos[0x40000000], __wine_shared_heap[0x03000000];
|
||||
#else
|
||||
__asm__(".zerofill WINE_DOS, WINE_DOS");
|
||||
__asm__(".zerofill WINE_SHAREDHEAP, WINE_SHAREDHEAP");
|
||||
static char __wine_dos[0x40000000] __attribute__((section("WINE_DOS, WINE_DOS")));
|
||||
static char __wine_shared_heap[0x03000000] __attribute__((section("WINE_SHAREDHEAP, WINE_SHAREDHEAP")));
|
||||
#endif
|
||||
|
||||
static const struct wine_preload_info wine_main_preload_info[] =
|
||||
{
|
||||
{ __wine_dos, sizeof(__wine_dos) }, /* DOS area + PE exe */
|
||||
{ __wine_shared_heap, sizeof(__wine_shared_heap) }, /* shared user data + shared heap */
|
||||
{ 0, 0 } /* end of list */
|
||||
};
|
||||
|
||||
static inline void reserve_area( void *addr, size_t size )
|
||||
{
|
||||
wine_anon_mmap( addr, size, PROT_NONE, MAP_FIXED | MAP_NORESERVE );
|
||||
wine_mmap_add_reserved_area( addr, size );
|
||||
}
|
||||
|
||||
#else /* __APPLE__ */
|
||||
|
||||
/* the preloader will set this variable */
|
||||
const struct wine_preload_info *wine_main_preload_info = NULL;
|
||||
|
||||
static inline void reserve_area( void *addr, size_t size )
|
||||
{
|
||||
wine_mmap_add_reserved_area( addr, size );
|
||||
}
|
||||
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
/***********************************************************************
|
||||
* check_command_line
|
||||
*
|
||||
@ -202,6 +167,13 @@ 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)
|
||||
@ -247,12 +219,10 @@ int main( int argc, char *argv[] )
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
if (wine_main_preload_info)
|
||||
#endif
|
||||
{
|
||||
for (i = 0; wine_main_preload_info[i].size; i++)
|
||||
reserve_area( wine_main_preload_info[i].addr, wine_main_preload_info[i].size );
|
||||
wine_mmap_add_reserved_area( wine_main_preload_info[i].addr, wine_main_preload_info[i].size );
|
||||
}
|
||||
|
||||
wine_init( argc, argv, error, sizeof(error) );
|
||||
|
@ -63,7 +63,6 @@
|
||||
#include "config.h"
|
||||
#include "wine/port.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -91,15 +90,9 @@
|
||||
# include <sys/link.h>
|
||||
#endif
|
||||
|
||||
#include "main.h"
|
||||
#include "preloader.h"
|
||||
|
||||
static int wld_vsprintf(char *buffer, const char *fmt, va_list args );
|
||||
|
||||
static __attribute__((format(printf,1,2))) void wld_printf(const char *fmt, ... );
|
||||
|
||||
static __attribute__((noreturn,format(printf,1,2))) void fatal_error(const char *fmt, ... );
|
||||
|
||||
static void preload_reserve( const char *str );
|
||||
#ifdef __linux__
|
||||
|
||||
/* ELF definitions */
|
||||
#define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) (mapstartpref)
|
||||
@ -545,28 +538,6 @@ SYSCALL_NOERR( wld_getegid, 177 /* SYS_getegid */ );
|
||||
#error preloader not implemented for this CPU
|
||||
#endif
|
||||
|
||||
/* replacement for libc functions */
|
||||
|
||||
static int wld_strcmp( const char *str1, const char *str2 )
|
||||
{
|
||||
while (*str1 && (*str1 == *str2)) { str1++; str2++; }
|
||||
return *str1 - *str2;
|
||||
}
|
||||
|
||||
static int wld_strncmp( const char *str1, const char *str2, size_t len )
|
||||
{
|
||||
if (len <= 0) return 0;
|
||||
while ((--len > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; }
|
||||
return *str1 - *str2;
|
||||
}
|
||||
|
||||
static inline void *wld_memset( void *dest, int val, size_t len )
|
||||
{
|
||||
char *dst = dest;
|
||||
while (len--) *dst++ = val;
|
||||
return dest;
|
||||
}
|
||||
|
||||
#ifdef DUMP_AUX_INFO
|
||||
/*
|
||||
* Dump interesting bits of the ELF auxv_t structure that is passed
|
||||
@ -1063,17 +1034,6 @@ static int is_addr_reserved( const void *addr )
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* remove a range from the preload list */
|
||||
static void remove_preload_range( int i )
|
||||
{
|
||||
while (preload_info[i].size)
|
||||
{
|
||||
preload_info[i].addr = preload_info[i+1].addr;
|
||||
preload_info[i].size = preload_info[i+1].size;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* is_in_preload_range
|
||||
*
|
||||
@ -1155,13 +1115,13 @@ void* wld_start( void **stack )
|
||||
#endif
|
||||
|
||||
/* reserve memory that Wine needs */
|
||||
if (reserve) preload_reserve( reserve );
|
||||
if (reserve) preload_reserve( reserve, preload_info, page_mask );
|
||||
for (i = 0; preload_info[i].size; i++)
|
||||
{
|
||||
if ((char *)av >= (char *)preload_info[i].addr &&
|
||||
(char *)pargc <= (char *)preload_info[i].addr + preload_info[i].size)
|
||||
{
|
||||
remove_preload_range( i );
|
||||
remove_preload_range( i, preload_info );
|
||||
i--;
|
||||
}
|
||||
else if (wld_mmap( preload_info[i].addr, preload_info[i].size, PROT_NONE,
|
||||
@ -1175,7 +1135,7 @@ void* wld_start( void **stack )
|
||||
)
|
||||
wld_printf( "preloader: Warning: failed to reserve range %p-%p\n",
|
||||
preload_info[i].addr, (char *)preload_info[i].addr + preload_info[i].size );
|
||||
remove_preload_range( i );
|
||||
remove_preload_range( i, preload_info );
|
||||
i--;
|
||||
}
|
||||
}
|
||||
@ -1243,6 +1203,10 @@ void* wld_start( void **stack )
|
||||
return (void *)ld_so_map.l_entry;
|
||||
}
|
||||
|
||||
#endif /* __linux__ */
|
||||
|
||||
/* replacement for libc functions */
|
||||
|
||||
/*
|
||||
* wld_printf - just the basics
|
||||
*
|
||||
@ -1250,7 +1214,7 @@ void* wld_start( void **stack )
|
||||
* %s prints a string
|
||||
* %p prints a pointer
|
||||
*/
|
||||
static int wld_vsprintf(char *buffer, const char *fmt, va_list args )
|
||||
int wld_vsprintf(char *buffer, const char *fmt, va_list args )
|
||||
{
|
||||
static const char hex_chars[16] = "0123456789abcdef";
|
||||
const char *p = fmt;
|
||||
@ -1297,7 +1261,7 @@ static int wld_vsprintf(char *buffer, const char *fmt, va_list args )
|
||||
return str - buffer;
|
||||
}
|
||||
|
||||
static void wld_printf(const char *fmt, ... )
|
||||
void wld_printf(const char *fmt, ... )
|
||||
{
|
||||
va_list args;
|
||||
char buffer[256];
|
||||
@ -1309,7 +1273,7 @@ static void wld_printf(const char *fmt, ... )
|
||||
wld_write(2, buffer, len);
|
||||
}
|
||||
|
||||
static void fatal_error(const char *fmt, ... )
|
||||
void fatal_error(const char *fmt, ... )
|
||||
{
|
||||
va_list args;
|
||||
char buffer[256];
|
||||
@ -1327,7 +1291,7 @@ static void fatal_error(const char *fmt, ... )
|
||||
*
|
||||
* Reserve a range specified in string format
|
||||
*/
|
||||
static void preload_reserve( const char *str )
|
||||
void preload_reserve( const char *str, struct wine_preload_info *preload_info, size_t page_mask )
|
||||
{
|
||||
const char *p;
|
||||
unsigned long result = 0;
|
||||
|
66
loader/preloader.h
Normal file
66
loader/preloader.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Definitions for Wine preloader
|
||||
*
|
||||
* Copyright (C) 2004 Mike McCormack for CodeWeavers
|
||||
* Copyright (C) 2004 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
|
||||
*/
|
||||
|
||||
#ifndef __WINE_LOADER_PRELOADER_H
|
||||
#define __WINE_LOADER_PRELOADER_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
static inline int wld_strcmp( const char *str1, const char *str2 )
|
||||
{
|
||||
while (*str1 && (*str1 == *str2)) { str1++; str2++; }
|
||||
return *str1 - *str2;
|
||||
}
|
||||
|
||||
static inline int wld_strncmp( const char *str1, const char *str2, size_t len )
|
||||
{
|
||||
if (len <= 0) return 0;
|
||||
while ((--len > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; }
|
||||
return *str1 - *str2;
|
||||
}
|
||||
|
||||
static inline void *wld_memset( void *dest, int val, size_t len )
|
||||
{
|
||||
char *dst = dest;
|
||||
while (len--) *dst++ = val;
|
||||
return dest;
|
||||
}
|
||||
|
||||
extern int wld_vsprintf(char *buffer, const char *fmt, va_list args );
|
||||
extern __attribute__((format(printf,1,2))) void wld_printf(const char *fmt, ... );
|
||||
extern __attribute__((noreturn,format(printf,1,2))) void fatal_error(const char *fmt, ... );
|
||||
|
||||
extern void preload_reserve( const char *str, struct wine_preload_info *preload_info, size_t page_mask );
|
||||
|
||||
/* remove a range from the preload list */
|
||||
static inline void remove_preload_range( int i, struct wine_preload_info *preload_info )
|
||||
{
|
||||
while (preload_info[i].size)
|
||||
{
|
||||
preload_info[i].addr = preload_info[i+1].addr;
|
||||
preload_info[i].size = preload_info[i+1].size;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __WINE_LOADER_PRELOADER_H */
|
450
loader/preloader_mac.c
Normal file
450
loader/preloader_mac.c
Normal file
@ -0,0 +1,450 @@
|
||||
/*
|
||||
* Preloader for macOS
|
||||
*
|
||||
* Copyright (C) 1995,96,97,98,99,2000,2001,2002 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2004 Mike McCormack for CodeWeavers
|
||||
* Copyright (C) 2004 Alexandre Julliard
|
||||
* Copyright (C) 2017 Michael Müller
|
||||
* Copyright (C) 2017 Sebastian Lackner
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#include "config.h"
|
||||
#include "wine/port.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#ifdef HAVE_SYS_MMAN_H
|
||||
# include <sys/mman.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_SYSCALL_H
|
||||
# include <sys/syscall.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_MACH_O_LOADER_H
|
||||
#include <mach/thread_status.h>
|
||||
#include <mach-o/loader.h>
|
||||
#endif
|
||||
|
||||
#include "preloader.h"
|
||||
|
||||
#ifndef LC_MAIN
|
||||
#define LC_MAIN 0x80000028
|
||||
struct entry_point_command
|
||||
{
|
||||
uint32_t cmd;
|
||||
uint32_t cmdsize;
|
||||
uint64_t entryoff;
|
||||
uint64_t stacksize;
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct wine_preload_info preload_info[] =
|
||||
{
|
||||
/* On macOS, we allocate the low 64k area in two steps because PAGEZERO
|
||||
* might not always be available. */
|
||||
#ifdef __i386__
|
||||
{ (void *)0x00000000, 0x00001000 }, /* first page */
|
||||
{ (void *)0x00001000, 0x0000f000 }, /* low 64k */
|
||||
{ (void *)0x00010000, 0x00100000 }, /* DOS area */
|
||||
{ (void *)0x00110000, 0x67ef0000 }, /* low memory area */
|
||||
{ (void *)0x7f000000, 0x03000000 }, /* top-down allocations + shared heap + virtual heap */
|
||||
#else /* __i386__ */
|
||||
{ (void *)0x000000010000, 0x00100000 }, /* DOS area */
|
||||
{ (void *)0x000000110000, 0x67ef0000 }, /* low memory area */
|
||||
{ (void *)0x00007ff00000, 0x000f0000 }, /* shared user data */
|
||||
{ (void *)0x7fff40000000, 0x01ff0000 }, /* top-down allocations + virtual heap */
|
||||
#endif /* __i386__ */
|
||||
{ 0, 0 }, /* PE exe range set with WINEPRELOADRESERVE */
|
||||
{ 0, 0 } /* end of list */
|
||||
};
|
||||
|
||||
/*
|
||||
* These functions are only called when file is compiled with -fstack-protector.
|
||||
* They are normally provided by libc's startup files, but since we
|
||||
* build the preloader with "-nostartfiles -nodefaultlibs", we have to
|
||||
* provide our own versions, otherwise the linker fails.
|
||||
*/
|
||||
void *__stack_chk_guard = 0;
|
||||
void __stack_chk_fail_local(void) { return; }
|
||||
void __stack_chk_fail(void) { return; }
|
||||
|
||||
#ifdef __i386__
|
||||
|
||||
static const size_t page_size = 0x1000;
|
||||
static const size_t page_mask = 0xfff;
|
||||
#define target_mach_header mach_header
|
||||
#define target_thread_state_t i386_thread_state_t
|
||||
#ifdef __DARWIN_UNIX03
|
||||
#define target_thread_ip(x) (x)->__eip
|
||||
#else
|
||||
#define target_thread_ip(x) (x)->eip
|
||||
#endif
|
||||
|
||||
#define SYSCALL_FUNC( name, nr ) \
|
||||
__ASM_GLOBAL_FUNC( name, \
|
||||
"\tmovl $" #nr ",%eax\n" \
|
||||
"\tint $0x80\n" \
|
||||
"\tjnb 1f\n" \
|
||||
"\tmovl $-1,%eax\n" \
|
||||
"1:\tret\n" )
|
||||
|
||||
#define SYSCALL_NOERR( name, nr ) \
|
||||
__ASM_GLOBAL_FUNC( name, \
|
||||
"\tmovl $" #nr ",%eax\n" \
|
||||
"\tint $0x80\n" \
|
||||
"\tret\n" )
|
||||
|
||||
__ASM_GLOBAL_FUNC( start,
|
||||
__ASM_CFI("\t.cfi_undefined %eip\n")
|
||||
/* The first 16 bytes are used as a function signature on i386 */
|
||||
"\t.byte 0x6a,0x00\n" /* pushl $0 */
|
||||
"\t.byte 0x89,0xe5\n" /* movl %esp,%ebp */
|
||||
"\t.byte 0x83,0xe4,0xf0\n" /* andl $-16,%esp */
|
||||
"\t.byte 0x83,0xec,0x10\n" /* subl $16,%esp */
|
||||
"\t.byte 0x8b,0x5d,0x04\n" /* movl 4(%ebp),%ebx */
|
||||
"\t.byte 0x89,0x5c,0x24,0x00\n" /* movl %ebx,0(%esp) */
|
||||
|
||||
"\tleal 4(%ebp),%eax\n"
|
||||
"\tmovl %eax,0(%esp)\n" /* stack */
|
||||
"\tleal 8(%esp),%eax\n"
|
||||
"\tmovl %eax,4(%esp)\n" /* &is_unix_thread */
|
||||
"\tmovl $0,(%eax)\n"
|
||||
"\tcall _wld_start\n"
|
||||
|
||||
"\tmovl 4(%ebp),%edi\n"
|
||||
"\tdecl %edi\n" /* argc */
|
||||
"\tleal 12(%ebp),%esi\n" /* argv */
|
||||
"\tleal 4(%esi,%edi,4),%edx\n" /* env */
|
||||
"\tmovl %edx,%ecx\n" /* apple data */
|
||||
"1:\tmovl (%ecx),%ebx\n"
|
||||
"\tadd $4,%ecx\n"
|
||||
"\torl %ebx,%ebx\n"
|
||||
"\tjnz 1b\n"
|
||||
|
||||
"\tcmpl $0,8(%esp)\n"
|
||||
"\tjne 2f\n"
|
||||
|
||||
/* LC_MAIN */
|
||||
"\tmovl %edi,0(%esp)\n" /* argc */
|
||||
"\tmovl %esi,4(%esp)\n" /* argv */
|
||||
"\tmovl %edx,8(%esp)\n" /* env */
|
||||
"\tmovl %ecx,12(%esp)\n" /* apple data */
|
||||
"\tcall *%eax\n"
|
||||
"\tmovl %eax,(%esp)\n"
|
||||
"\tcall _wld_exit\n"
|
||||
"\thlt\n"
|
||||
|
||||
/* LC_UNIXTHREAD */
|
||||
"2:\tmovl (%ecx),%ebx\n"
|
||||
"\tadd $4,%ecx\n"
|
||||
"\torl %ebx,%ebx\n"
|
||||
"\tjnz 2b\n"
|
||||
|
||||
"\tsubl %ebp,%ecx\n"
|
||||
"\tsubl $8,%ecx\n"
|
||||
"\tleal 4(%ebp),%esp\n"
|
||||
"\tsubl %ecx,%esp\n"
|
||||
|
||||
"\tmovl %edi,(%esp)\n" /* argc */
|
||||
"\tleal 4(%esp),%edi\n"
|
||||
"\tshrl $2,%ecx\n"
|
||||
"\tcld\n"
|
||||
"\trep; movsd\n" /* argv, ... */
|
||||
|
||||
"\tmovl $0,%ebp\n"
|
||||
"\tjmpl *%eax\n" )
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
|
||||
static const size_t page_size = 0x1000;
|
||||
static const size_t page_mask = 0xfff;
|
||||
#define target_mach_header mach_header_64
|
||||
#define target_thread_state_t x86_thread_state64_t
|
||||
#ifdef __DARWIN_UNIX03
|
||||
#define target_thread_ip(x) (x)->__rip
|
||||
#else
|
||||
#define target_thread_ip(x) (x)->rip
|
||||
#endif
|
||||
|
||||
#define SYSCALL_FUNC( name, nr ) \
|
||||
__ASM_GLOBAL_FUNC( name, \
|
||||
"\tmovq %rcx, %r10\n" \
|
||||
"\tmovq $(" #nr "|0x2000000),%rax\n" \
|
||||
"\tsyscall\n" \
|
||||
"\tjnb 1f\n" \
|
||||
"\tmovq $-1,%rax\n" \
|
||||
"1:\tret\n" )
|
||||
|
||||
#define SYSCALL_NOERR( name, nr ) \
|
||||
__ASM_GLOBAL_FUNC( name, \
|
||||
"\tmovq %rcx, %r10\n" \
|
||||
"\tmovq $(" #nr "|0x2000000),%rax\n" \
|
||||
"\tsyscall\n" \
|
||||
"\tret\n" )
|
||||
|
||||
__ASM_GLOBAL_FUNC( start,
|
||||
__ASM_CFI("\t.cfi_undefined %rip\n")
|
||||
"\tpushq $0\n"
|
||||
"\tmovq %rsp,%rbp\n"
|
||||
"\tandq $-16,%rsp\n"
|
||||
"\tsubq $16,%rsp\n"
|
||||
|
||||
"\tleaq 8(%rbp),%rdi\n" /* stack */
|
||||
"\tmovq %rsp,%rsi\n" /* &is_unix_thread */
|
||||
"\tmovq $0,(%rsi)\n"
|
||||
"\tcall _wld_start\n"
|
||||
|
||||
"\tmovq 8(%rbp),%rdi\n"
|
||||
"\tdec %rdi\n" /* argc */
|
||||
"\tleaq 24(%rbp),%rsi\n" /* argv */
|
||||
"\tleaq 8(%rsi,%rdi,8),%rdx\n" /* env */
|
||||
"\tmovq %rdx,%rcx\n" /* apple data */
|
||||
"1:\tmovq (%rcx),%r8\n"
|
||||
"\taddq $8,%rcx\n"
|
||||
"\torq %r8,%r8\n"
|
||||
"\tjnz 1b\n"
|
||||
|
||||
"\tcmpl $0,0(%rsp)\n"
|
||||
"\tjne 2f\n"
|
||||
|
||||
/* LC_MAIN */
|
||||
"\taddq $16,%rsp\n"
|
||||
"\tcall *%rax\n"
|
||||
"\tmovq %rax,%rdi\n"
|
||||
"\tcall _wld_exit\n"
|
||||
"\thlt\n"
|
||||
|
||||
/* LC_UNIXTHREAD */
|
||||
"2:\tmovq (%rcx),%r8\n"
|
||||
"\taddq $8,%rcx\n"
|
||||
"\torq %r8,%r8\n"
|
||||
"\tjnz 2b\n"
|
||||
|
||||
"\tsubq %rbp,%rcx\n"
|
||||
"\tsubq $16,%rcx\n"
|
||||
"\tleaq 8(%rbp),%rsp\n"
|
||||
"\tsubq %rcx,%rsp\n"
|
||||
|
||||
"\tmovq %rdi,(%rsp)\n" /* argc */
|
||||
"\tleaq 8(%rsp),%rdi\n"
|
||||
"\tshrq $3,%rcx\n"
|
||||
"\tcld\n"
|
||||
"\trep; movsq\n" /* argv, ... */
|
||||
|
||||
"\tmovq $0,%rbp\n"
|
||||
"\tjmpq *%rax\n" )
|
||||
|
||||
#else
|
||||
#error preloader not implemented for this CPU
|
||||
#endif
|
||||
|
||||
void wld_exit( int code ) __attribute__((noreturn));
|
||||
SYSCALL_NOERR( wld_exit, 1 /* SYS_exit */ );
|
||||
|
||||
ssize_t wld_write( int fd, const void *buffer, size_t len );
|
||||
SYSCALL_FUNC( wld_write, 4 /* SYS_write */ );
|
||||
|
||||
void *wld_mmap( void *start, size_t len, int prot, int flags, int fd, off_t offset );
|
||||
SYSCALL_FUNC( wld_mmap, 197 /* SYS_mmap */ );
|
||||
|
||||
void *wld_munmap( void *start, size_t len );
|
||||
SYSCALL_FUNC( wld_munmap, 73 /* SYS_munmap */ );
|
||||
|
||||
int wld_mincore( void *addr, size_t length, unsigned char *vec );
|
||||
SYSCALL_FUNC( wld_mincore, 78 /* SYS_mincore */ );
|
||||
|
||||
static intptr_t (*p_dyld_get_image_slide)( const struct target_mach_header* mh );
|
||||
|
||||
#define MAKE_FUNCPTR(f) static typeof(f) * p##f
|
||||
MAKE_FUNCPTR(dlopen);
|
||||
MAKE_FUNCPTR(dlsym);
|
||||
MAKE_FUNCPTR(dladdr);
|
||||
#undef MAKE_FUNCPTR
|
||||
|
||||
extern int _dyld_func_lookup( const char *dyld_func_name, void **address );
|
||||
|
||||
static void *get_entry_point( struct target_mach_header *mh, intptr_t slide, int *unix_thread )
|
||||
{
|
||||
struct entry_point_command *entry;
|
||||
target_thread_state_t *state;
|
||||
struct load_command *cmd;
|
||||
int i;
|
||||
|
||||
/* try LC_MAIN first */
|
||||
cmd = (struct load_command *)(mh + 1);
|
||||
for (i = 0; i < mh->ncmds; i++)
|
||||
{
|
||||
if (cmd->cmd == LC_MAIN)
|
||||
{
|
||||
*unix_thread = FALSE;
|
||||
entry = (struct entry_point_command *)cmd;
|
||||
return (char *)mh + entry->entryoff;
|
||||
}
|
||||
cmd = (struct load_command *)((char *)cmd + cmd->cmdsize);
|
||||
}
|
||||
|
||||
/* then try LC_UNIXTHREAD */
|
||||
cmd = (struct load_command *)(mh + 1);
|
||||
for (i = 0; i < mh->ncmds; i++)
|
||||
{
|
||||
if (cmd->cmd == LC_UNIXTHREAD)
|
||||
{
|
||||
*unix_thread = TRUE;
|
||||
state = (target_thread_state_t *)((char *)cmd + 16);
|
||||
return (void *)(target_thread_ip(state) + slide);
|
||||
}
|
||||
cmd = (struct load_command *)((char *)cmd + cmd->cmdsize);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
};
|
||||
|
||||
static int is_region_empty( struct wine_preload_info *info )
|
||||
{
|
||||
unsigned char vec[1024];
|
||||
size_t pos, size, block = 1024 * page_size;
|
||||
int i;
|
||||
|
||||
for (pos = 0; pos < info->size; pos += size)
|
||||
{
|
||||
size = (pos + block <= info->size) ? block : (info->size - pos);
|
||||
if (wld_mincore( (char *)info->addr + pos, size, vec ) == -1)
|
||||
{
|
||||
if (size <= page_size) continue;
|
||||
block = page_size; size = 0; /* retry with smaller block size */
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < size / page_size; i++)
|
||||
if (vec[i] & 1) return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int map_region( struct wine_preload_info *info )
|
||||
{
|
||||
int flags = MAP_PRIVATE | MAP_ANON;
|
||||
void *ret;
|
||||
|
||||
if (!info->addr) flags |= MAP_FIXED;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
ret = wld_mmap( info->addr, info->size, PROT_NONE, flags, -1, 0 );
|
||||
if (ret == info->addr) return 1;
|
||||
if (ret != (void *)-1) wld_munmap( ret, info->size );
|
||||
if (flags & MAP_FIXED) break;
|
||||
|
||||
/* Some versions of macOS ignore the address hint passed to mmap -
|
||||
* use mincore() to check if its empty and then use MAP_FIXED */
|
||||
if (!is_region_empty( info )) break;
|
||||
flags |= MAP_FIXED;
|
||||
}
|
||||
|
||||
/* don't warn for zero page */
|
||||
if (info->addr >= (void *)0x1000)
|
||||
wld_printf( "preloader: Warning: failed to reserve range %p-%p\n",
|
||||
info->addr, (char *)info->addr + info->size );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void get_dyld_func( const char *name, void **func )
|
||||
{
|
||||
_dyld_func_lookup( name, func );
|
||||
if (!*func) fatal_error( "Failed to get function pointer for %s\n", name );
|
||||
}
|
||||
|
||||
#define LOAD_POSIX_DYLD_FUNC(f) get_dyld_func( "__dyld_" #f, (void **)&p##f )
|
||||
#define LOAD_MACHO_DYLD_FUNC(f) get_dyld_func( "_" #f, (void **)&p##f )
|
||||
|
||||
void *wld_start( void *stack, int *is_unix_thread )
|
||||
{
|
||||
struct wine_preload_info builtin_dlls = { (void *)0x7a000000, 0x02000000 };
|
||||
struct wine_preload_info **wine_main_preload_info;
|
||||
char **argv, **p, *reserve = NULL;
|
||||
struct target_mach_header *mh;
|
||||
void *mod, *entry;
|
||||
int *pargc, i;
|
||||
Dl_info info;
|
||||
|
||||
pargc = stack;
|
||||
argv = (char **)pargc + 1;
|
||||
if (*pargc < 2) fatal_error( "Usage: %s wine_binary [args]\n", argv[0] );
|
||||
|
||||
/* skip over the parameters */
|
||||
p = argv + *pargc + 1;
|
||||
|
||||
/* skip over the environment */
|
||||
while (*p)
|
||||
{
|
||||
static const char res[] = "WINEPRELOADRESERVE=";
|
||||
if (!wld_strncmp( *p, res, sizeof(res)-1 )) reserve = *p + sizeof(res) - 1;
|
||||
p++;
|
||||
}
|
||||
|
||||
/* reserve memory that Wine needs */
|
||||
if (reserve) preload_reserve( reserve, preload_info, page_mask );
|
||||
for (i = 0; preload_info[i].size; i++)
|
||||
{
|
||||
if (!map_region( &preload_info[i] ))
|
||||
{
|
||||
remove_preload_range( i, preload_info );
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (!map_region( &builtin_dlls ))
|
||||
builtin_dlls.size = 0;
|
||||
|
||||
LOAD_POSIX_DYLD_FUNC( dlopen );
|
||||
LOAD_POSIX_DYLD_FUNC( dlsym );
|
||||
LOAD_POSIX_DYLD_FUNC( dladdr );
|
||||
LOAD_MACHO_DYLD_FUNC( _dyld_get_image_slide );
|
||||
|
||||
/* load the main binary */
|
||||
if (!(mod = pdlopen( argv[1], RTLD_NOW )))
|
||||
fatal_error( "%s: could not load binary\n", argv[1] );
|
||||
|
||||
if (builtin_dlls.size)
|
||||
wld_munmap( builtin_dlls.addr, builtin_dlls.size );
|
||||
|
||||
/* store pointer to the preload info into the appropriate main binary variable */
|
||||
wine_main_preload_info = pdlsym( mod, "wine_main_preload_info" );
|
||||
if (wine_main_preload_info) *wine_main_preload_info = preload_info;
|
||||
else wld_printf( "wine_main_preload_info not found\n" );
|
||||
|
||||
if (!pdladdr( wine_main_preload_info, &info ) || !(mh = info.dli_fbase))
|
||||
fatal_error( "%s: could not find mach header\n", argv[1] );
|
||||
if (!(entry = get_entry_point( mh, p_dyld_get_image_slide(mh), is_unix_thread )))
|
||||
fatal_error( "%s: could not find entry point\n", argv[1] );
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
#endif /* __APPLE__ */
|
Loading…
x
Reference in New Issue
Block a user