Signed-off-by: Ken Thomases ken@codeweavers.com --- This is a no-op. It justs makes the next patch simpler.
loader/preloader.c | 288 +++++++++++++++++++++++++++-------------------------- 1 file changed, 148 insertions(+), 140 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c index 644244b..de15130 100644 --- a/loader/preloader.c +++ b/loader/preloader.c @@ -93,6 +93,14 @@
#include "main.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 ); + /* ELF definitions */ #define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) (mapstartpref) #define ELF_FIXED_ADDRESS(loader, mapstart) ((void) 0) @@ -559,85 +567,6 @@ static inline void *wld_memset( void *dest, int val, size_t len ) return dest; }
-/* - * wld_printf - just the basics - * - * %x prints a hex number - * %s prints a string - * %p prints a pointer - */ -static int wld_vsprintf(char *buffer, const char *fmt, va_list args ) -{ - static const char hex_chars[16] = "0123456789abcdef"; - const char *p = fmt; - char *str = buffer; - int i; - - while( *p ) - { - if( *p == '%' ) - { - p++; - if( *p == 'x' ) - { - unsigned int x = va_arg( args, unsigned int ); - for (i = 2*sizeof(x) - 1; i >= 0; i--) - *str++ = hex_chars[(x>>(i*4))&0xf]; - } - else if (p[0] == 'l' && p[1] == 'x') - { - unsigned long x = va_arg( args, unsigned long ); - for (i = 2*sizeof(x) - 1; i >= 0; i--) - *str++ = hex_chars[(x>>(i*4))&0xf]; - p++; - } - else if( *p == 'p' ) - { - unsigned long x = (unsigned long)va_arg( args, void * ); - for (i = 2*sizeof(x) - 1; i >= 0; i--) - *str++ = hex_chars[(x>>(i*4))&0xf]; - } - else if( *p == 's' ) - { - char *s = va_arg( args, char * ); - while(*s) - *str++ = *s++; - } - else if( *p == 0 ) - break; - p++; - } - *str++ = *p++; - } - *str = 0; - return str - buffer; -} - -static __attribute__((format(printf,1,2))) void wld_printf(const char *fmt, ... ) -{ - va_list args; - char buffer[256]; - int len; - - va_start( args, fmt ); - len = wld_vsprintf(buffer, fmt, args ); - va_end( args ); - wld_write(2, buffer, len); -} - -static __attribute__((noreturn,format(printf,1,2))) void fatal_error(const char *fmt, ... ) -{ - va_list args; - char buffer[256]; - int len; - - va_start( args, fmt ); - len = wld_vsprintf(buffer, fmt, args ); - va_end( args ); - wld_write(2, buffer, len); - wld_exit(1); -} - #ifdef DUMP_AUX_INFO /* * Dump interesting bits of the ELF auxv_t structure that is passed @@ -1120,67 +1049,6 @@ found: return (void *)(symtab[idx].st_value + map->l_addr); }
-/* - * preload_reserve - * - * Reserve a range specified in string format - */ -static void preload_reserve( const char *str ) -{ - const char *p; - unsigned long result = 0; - void *start = NULL, *end = NULL; - int i, first = 1; - - for (p = str; *p; p++) - { - if (*p >= '0' && *p <= '9') result = result * 16 + *p - '0'; - else if (*p >= 'a' && *p <= 'f') result = result * 16 + *p - 'a' + 10; - else if (*p >= 'A' && *p <= 'F') result = result * 16 + *p - 'A' + 10; - else if (*p == '-') - { - if (!first) goto error; - start = (void *)(result & ~page_mask); - result = 0; - first = 0; - } - else goto error; - } - if (!first) end = (void *)((result + page_mask) & ~page_mask); - else if (result) goto error; /* single value '0' is allowed */ - - /* sanity checks */ - if (end <= start) start = end = NULL; - else if ((char *)end > preloader_start && - (char *)start <= preloader_end) - { - wld_printf( "WINEPRELOADRESERVE range %p-%p overlaps preloader %p-%p\n", - start, end, preloader_start, preloader_end ); - start = end = NULL; - } - - /* check for overlap with low memory areas */ - for (i = 0; preload_info[i].size; i++) - { - if ((char *)preload_info[i].addr > (char *)0x00110000) break; - if ((char *)end <= (char *)preload_info[i].addr + preload_info[i].size) - { - start = end = NULL; - break; - } - if ((char *)start < (char *)preload_info[i].addr + preload_info[i].size) - start = (char *)preload_info[i].addr + preload_info[i].size; - } - - while (preload_info[i].size) i++; - preload_info[i].addr = start; - preload_info[i].size = (char *)end - (char *)start; - return; - -error: - fatal_error( "invalid WINEPRELOADRESERVE value '%s'\n", str ); -} - /* check if address is in one of the reserved ranges */ static int is_addr_reserved( const void *addr ) { @@ -1374,3 +1242,143 @@ void* wld_start( void **stack )
return (void *)ld_so_map.l_entry; } + +/* + * wld_printf - just the basics + * + * %x prints a hex number + * %s prints a string + * %p prints a pointer + */ +static int wld_vsprintf(char *buffer, const char *fmt, va_list args ) +{ + static const char hex_chars[16] = "0123456789abcdef"; + const char *p = fmt; + char *str = buffer; + int i; + + while( *p ) + { + if( *p == '%' ) + { + p++; + if( *p == 'x' ) + { + unsigned int x = va_arg( args, unsigned int ); + for (i = 2*sizeof(x) - 1; i >= 0; i--) + *str++ = hex_chars[(x>>(i*4))&0xf]; + } + else if (p[0] == 'l' && p[1] == 'x') + { + unsigned long x = va_arg( args, unsigned long ); + for (i = 2*sizeof(x) - 1; i >= 0; i--) + *str++ = hex_chars[(x>>(i*4))&0xf]; + p++; + } + else if( *p == 'p' ) + { + unsigned long x = (unsigned long)va_arg( args, void * ); + for (i = 2*sizeof(x) - 1; i >= 0; i--) + *str++ = hex_chars[(x>>(i*4))&0xf]; + } + else if( *p == 's' ) + { + char *s = va_arg( args, char * ); + while(*s) + *str++ = *s++; + } + else if( *p == 0 ) + break; + p++; + } + *str++ = *p++; + } + *str = 0; + return str - buffer; +} + +static __attribute__((format(printf,1,2))) void wld_printf(const char *fmt, ... ) +{ + va_list args; + char buffer[256]; + int len; + + va_start( args, fmt ); + len = wld_vsprintf(buffer, fmt, args ); + va_end( args ); + wld_write(2, buffer, len); +} + +static __attribute__((noreturn,format(printf,1,2))) void fatal_error(const char *fmt, ... ) +{ + va_list args; + char buffer[256]; + int len; + + va_start( args, fmt ); + len = wld_vsprintf(buffer, fmt, args ); + va_end( args ); + wld_write(2, buffer, len); + wld_exit(1); +} + +/* + * preload_reserve + * + * Reserve a range specified in string format + */ +static void preload_reserve( const char *str ) +{ + const char *p; + unsigned long result = 0; + void *start = NULL, *end = NULL; + int i, first = 1; + + for (p = str; *p; p++) + { + if (*p >= '0' && *p <= '9') result = result * 16 + *p - '0'; + else if (*p >= 'a' && *p <= 'f') result = result * 16 + *p - 'a' + 10; + else if (*p >= 'A' && *p <= 'F') result = result * 16 + *p - 'A' + 10; + else if (*p == '-') + { + if (!first) goto error; + start = (void *)(result & ~page_mask); + result = 0; + first = 0; + } + else goto error; + } + if (!first) end = (void *)((result + page_mask) & ~page_mask); + else if (result) goto error; /* single value '0' is allowed */ + + /* sanity checks */ + if (end <= start) start = end = NULL; + else if ((char *)end > preloader_start && + (char *)start <= preloader_end) + { + wld_printf( "WINEPRELOADRESERVE range %p-%p overlaps preloader %p-%p\n", + start, end, preloader_start, preloader_end ); + start = end = NULL; + } + + /* check for overlap with low memory areas */ + for (i = 0; preload_info[i].size; i++) + { + if ((char *)preload_info[i].addr > (char *)0x00110000) break; + if ((char *)end <= (char *)preload_info[i].addr + preload_info[i].size) + { + start = end = NULL; + break; + } + if ((char *)start < (char *)preload_info[i].addr + preload_info[i].size) + start = (char *)preload_info[i].addr + preload_info[i].size; + } + + while (preload_info[i].size) i++; + preload_info[i].addr = start; + preload_info[i].size = (char *)end - (char *)start; + return; + +error: + fatal_error( "invalid WINEPRELOADRESERVE value '%s'\n", str ); +}
From: Sebastian Lackner sebastian@fds-team.de
Signed-off-by: Ken Thomases ken@codeweavers.com --- Changes from v1: * Split Mac code out into separate file.
Differences from Staging version: * Don't attempt to use the --export-dynamic linker option on macOS. It's not supported. * On macOS, release part of the low memory reservation even for large-address-space aware apps. 3D games are already prone to exhausting the address space due to OpenGL resources. We don't want to exacerbate that. * Use dladdr() to find the Mach header of the loader. Eliminate some code for doing that a more roundabout way.
--- Makefile.in | 1 + configure.ac | 21 ++- dlls/ntdll/virtual.c | 12 +- libs/wine/config.c | 2 +- loader/Makefile.in | 9 +- loader/main.c | 46 +---- loader/preloader.c | 76 +++------ loader/preloader.h | 82 +++++++++ loader/preloader_mac.c | 445 +++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 589 insertions(+), 105 deletions(-) create mode 100644 loader/preloader.h create mode 100644 loader/preloader_mac.c
diff --git a/Makefile.in b/Makefile.in index 5e7ad68..249c9a2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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@ diff --git a/configure.ac b/configure.ac index 50cf9a0..2222c68 100644 --- a/configure.ac +++ b/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 **** diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c index 5dfbe74..030c658 100644 --- a/dlls/ntdll/virtual.c +++ b/dlls/ntdll/virtual.c @@ -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 ); diff --git a/libs/wine/config.c b/libs/wine/config.c index f638412..083e263 100644 --- a/libs/wine/config.c +++ b/libs/wine/config.c @@ -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; diff --git a/loader/Makefile.in b/loader/Makefile.in index bbb1b95..a5e04dd 100644 --- a/loader/Makefile.in +++ b/loader/Makefile.in @@ -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) diff --git a/loader/main.c b/loader/main.c index 0a25272..f197cf8 100644 --- a/loader/main.c +++ b/loader/main.c @@ -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) ); diff --git a/loader/preloader.c b/loader/preloader.c index de15130..575161f 100644 --- a/loader/preloader.c +++ b/loader/preloader.c @@ -4,6 +4,8 @@ * 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 @@ -63,7 +65,6 @@ #include "config.h" #include "wine/port.h"
-#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -91,15 +92,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 ); +#ifndef __APPLE__
/* ELF definitions */ #define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) (mapstartpref) @@ -250,7 +245,7 @@ __ASM_GLOBAL_FUNC(_start,
#define SYSCALL_RET(ret) (((ret) < 0 && (ret) > -4096) ? -1 : (ret))
-static inline __attribute__((noreturn)) void wld_exit( int code ) +__attribute__((noreturn)) void wld_exit( int code ) { for (;;) /* avoid warning */ __asm__ __volatile__( "pushl %%ebx; movl %1,%%ebx; int $0x80; popl %%ebx" @@ -283,7 +278,7 @@ static inline ssize_t wld_read( int fd, void *buffer, size_t len ) return SYSCALL_RET(ret); }
-static inline ssize_t wld_write( int fd, const void *buffer, size_t len ) +ssize_t wld_write( int fd, const void *buffer, size_t len ) { int ret; __asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx" @@ -399,13 +394,11 @@ __ASM_GLOBAL_FUNC(_start, "syscall\n\t" \ "ret" )
-void wld_exit( int code ) __attribute__((noreturn)); SYSCALL_NOERR( wld_exit, 60 /* SYS_exit */ );
ssize_t wld_read( int fd, void *buffer, size_t len ); SYSCALL_FUNC( wld_read, 0 /* SYS_read */ );
-ssize_t wld_write( int fd, const void *buffer, size_t len ); SYSCALL_FUNC( wld_write, 1 /* SYS_write */ );
int wld_open( const char *name, int flags ); @@ -414,7 +407,6 @@ SYSCALL_FUNC( wld_open, 2 /* SYS_open */ ); int wld_close( int fd ); SYSCALL_FUNC( wld_close, 3 /* SYS_close */ );
-void *wld_mmap( void *start, size_t len, int prot, int flags, int fd, off_t offset ); SYSCALL_FUNC( wld_mmap, 9 /* SYS_mmap */ );
int wld_mprotect( const void *addr, size_t len, int prot ); @@ -500,13 +492,11 @@ __ASM_GLOBAL_FUNC(_start, "ldp x8, x9, [SP], #16\n\t" \ "ret" )
-void wld_exit( int code ) __attribute__((noreturn)); SYSCALL_NOERR( wld_exit, 93 /* SYS_exit */ );
ssize_t wld_read( int fd, void *buffer, size_t len ); SYSCALL_FUNC( wld_read, 63 /* SYS_read */ );
-ssize_t wld_write( int fd, const void *buffer, size_t len ); SYSCALL_FUNC( wld_write, 64 /* SYS_write */ );
int wld_openat( int dirfd, const char *name, int flags ); @@ -520,7 +510,6 @@ int wld_open( const char *name, int flags ) int wld_close( int fd ); SYSCALL_FUNC( wld_close, 57 /* SYS_close */ );
-void *wld_mmap( void *start, size_t len, int prot, int flags, int fd, off_t offset ); SYSCALL_FUNC( wld_mmap, 222 /* SYS_mmap */ );
int wld_mprotect( const void *addr, size_t len, int prot ); @@ -545,28 +534,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 +1030,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 +1111,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 +1131,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 +1199,10 @@ void* wld_start( void **stack ) return (void *)ld_so_map.l_entry; }
+#endif /* __APPLE__ */ + +/* replacement for libc functions */ + /* * wld_printf - just the basics * @@ -1250,7 +1210,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 +1257,7 @@ static int wld_vsprintf(char *buffer, const char *fmt, va_list args ) return str - buffer; }
-static __attribute__((format(printf,1,2))) void wld_printf(const char *fmt, ... ) +__attribute__((format(printf,1,2))) void wld_printf(const char *fmt, ... ) { va_list args; char buffer[256]; @@ -1309,7 +1269,7 @@ static __attribute__((format(printf,1,2))) void wld_printf(const char *fmt, ... wld_write(2, buffer, len); }
-static __attribute__((noreturn,format(printf,1,2))) void fatal_error(const char *fmt, ... ) +__attribute__((noreturn,format(printf,1,2))) void fatal_error(const char *fmt, ... ) { va_list args; char buffer[256]; @@ -1327,7 +1287,7 @@ static __attribute__((noreturn,format(printf,1,2))) void fatal_error(const char * * 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; @@ -1353,6 +1313,7 @@ static void preload_reserve( const char *str )
/* sanity checks */ if (end <= start) start = end = NULL; +#ifndef __APPLE__ else if ((char *)end > preloader_start && (char *)start <= preloader_end) { @@ -1360,6 +1321,7 @@ static void preload_reserve( const char *str ) start, end, preloader_start, preloader_end ); start = end = NULL; } +#endif /* !__APPLE__ */
/* check for overlap with low memory areas */ for (i = 0; preload_info[i].size; i++) diff --git a/loader/preloader.h b/loader/preloader.h new file mode 100644 index 0000000..51c7d5d --- /dev/null +++ b/loader/preloader.h @@ -0,0 +1,82 @@ +/* + * Definitions for Wine preloader + * + * 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 + */ + +#ifndef __WINE_LOADER_PRELOADER_H +#define __WINE_LOADER_PRELOADER_H + +#include <stdarg.h> + +#include "main.h" + +void wld_exit( int code ) __attribute__((noreturn)); + +ssize_t wld_write( int fd, const void *buffer, size_t len ); + +void *wld_mmap( void *start, size_t len, int prot, int flags, int fd, +#if defined(__APPLE__) || !defined(__i386__) + off_t offset +#else + unsigned int offset +#endif + ); + + +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; +} + +int wld_vsprintf(char *buffer, const char *fmt, va_list args ); +__attribute__((format(printf,1,2))) void wld_printf(const char *fmt, ... ); +__attribute__((noreturn,format(printf,1,2))) void fatal_error(const char *fmt, ... ); + +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 */ diff --git a/loader/preloader_mac.c b/loader/preloader_mac.c new file mode 100644 index 0000000..36ed32c --- /dev/null +++ b/loader/preloader_mac.c @@ -0,0 +1,445 @@ +/* + * 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 + +SYSCALL_NOERR( wld_exit, 1 /* SYS_exit */ ); +SYSCALL_FUNC( wld_write, 4 /* SYS_write */ ); +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__ */