[PATCH v2 2/2] loader: Implement preloader for macOS.

Ken Thomases ken at codeweavers.com
Mon Dec 3 10:46:51 CST 2018


From: Sebastian Lackner <sebastian at fds-team.de>

Signed-off-by: Ken Thomases <ken at 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__ */
-- 
2.10.2




More information about the wine-devel mailing list