[PATCH 2/2] loader: Implement preloader for Mac OS.
Ken Thomases
ken at codeweavers.com
Mon Nov 26 17:27:06 CST 2018
From: Sebastian Lackner <sebastian at fds-team.de>
Signed-off-by: Ken Thomases <ken at codeweavers.com>
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=33159
---
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 | 4 +-
loader/main.c | 46 +-----
loader/preloader.c | 436 +++++++++++++++++++++++++++++++++++++++++++++++++--
7 files changed, 465 insertions(+), 57 deletions(-)
diff --git a/Makefile.in b/Makefile.in
index 6b65fdf..40ae5ce 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -89,6 +89,7 @@ WINELOADER_PROGRAMS = @WINELOADER_PROGRAMS@
WINELOADER_DEPENDS = @WINELOADER_DEPENDS@
WINELOADER_INSTALL = @WINELOADER_INSTALL@
WINELOADER_LDFLAGS = @WINELOADER_LDFLAGS@
+WINEPRELOADER_LDFLAGS = @WINEPRELOADER_LDFLAGS@
LIBWINE_SHAREDLIB = @LIBWINE_SHAREDLIB@
LIBWINE_IMPORTLIB = @LIBWINE_IMPORTLIB@
LIBWINE_INSTALL_LIB = @LIBWINE_INSTALL_LIB@
diff --git a/configure.ac b/configure.ac
index 294fe4b..140a783 100644
--- a/configure.ac
+++ b/configure.ac
@@ -715,6 +715,7 @@ AC_SUBST(LDRPATH_INSTALL,"")
AC_SUBST(LDRPATH_LOCAL,"")
AC_SUBST(LDEXECFLAGS,"")
AC_SUBST(WINELOADER_LDFLAGS,"")
+AC_SUBST(WINEPRELOADER_LDFLAGS,"")
LIBEXT="so"
DLLEXT=".so"
IMPLIBEXT="def"
@@ -775,9 +776,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
@@ -893,6 +900,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"],
@@ -940,6 +948,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}\`"
@@ -2119,6 +2128,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 13f016a..b76519b 100644
--- a/libs/wine/config.c
+++ b/libs/wine/config.c
@@ -548,7 +548,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 b1eafd1..05a7503 100644
--- a/loader/Makefile.in
+++ b/loader/Makefile.in
@@ -24,7 +24,7 @@ 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_LDFLAGS = $(WINEPRELOADER_LDFLAGS)
wine64_preloader_OBJS = preloader.o
-wine64_preloader_LDFLAGS = -static -nostartfiles -nodefaultlibs -Wl,-Ttext=0x7c400000
+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 644244b..91cac51 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
@@ -90,15 +92,13 @@
#ifdef HAVE_SYS_LINK_H
# include <sys/link.h>
#endif
+#ifdef HAVE_MACH_O_LOADER_H
+#include <mach/thread_status.h>
+#include <mach-o/loader.h>
+#endif
#include "main.h"
-/* ELF definitions */
-#define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) (mapstartpref)
-#define ELF_FIXED_ADDRESS(loader, mapstart) ((void) 0)
-
-#define MAP_BASE_ADDR(l) 0
-
#ifndef MAP_COPY
#define MAP_COPY MAP_PRIVATE
#endif
@@ -108,21 +108,63 @@
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. Also, the top-down allocations area
+ * on x86_64 is moved because the address on Linux exceeds the maximum
+ * allowed user space limit. Please note that on Linux, it is better to
+ * allocate the low 64k as a single chunk to avoid SELinux warnings on
+ * systems with CONFIG_DEFAULT_MMAP_MIN_ADDR < CONFIG_LSM_MMAP_MIN_ADDR. */
+#ifdef __APPLE__
+#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__ */
+#else /* __APPLE__ */
#ifdef __i386__
{ (void *)0x00000000, 0x00010000 }, /* low 64k */
{ (void *)0x00010000, 0x00100000 }, /* DOS area */
{ (void *)0x00110000, 0x67ef0000 }, /* low memory area */
{ (void *)0x7f000000, 0x03000000 }, /* top-down allocations + shared heap + virtual heap */
-#else
+#else /* __i386__ */
{ (void *)0x000000010000, 0x00100000 }, /* DOS area */
{ (void *)0x000000110000, 0x67ef0000 }, /* low memory area */
{ (void *)0x00007ff00000, 0x000f0000 }, /* shared user data */
{ (void *)0x7ffffe000000, 0x01ff0000 }, /* top-down allocations + virtual heap */
-#endif
+#endif /* __i386__ */
+#endif /* __APPLE__ */
{ 0, 0 }, /* PE exe range set with WINEPRELOADRESERVE */
{ 0, 0 } /* end of list */
};
+#ifdef __APPLE__
+
+#ifndef LC_MAIN
+#define LC_MAIN 0x80000028
+struct entry_point_command
+{
+ uint32_t cmd;
+ uint32_t cmdsize;
+ uint64_t entryoff;
+ uint64_t stacksize;
+};
+#endif
+
+#else /* __APPLE__ */
+
+/* ELF definitions */
+#define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) (mapstartpref)
+#define ELF_FIXED_ADDRESS(loader, mapstart) ((void) 0)
+
+#define MAP_BASE_ADDR(l) 0
+
/* debugging */
#undef DUMP_SEGMENTS
#undef DUMP_AUX_INFO
@@ -168,6 +210,8 @@ struct wld_auxv
} a_un;
};
+#endif /* __APPLE__ */
+
/*
* The __bb_init_func is an empty function only called when file is
* compiled with gcc flags "-fprofile-arcs -ftest-coverage". This
@@ -182,6 +226,193 @@ void *__stack_chk_guard = 0;
void __stack_chk_fail_local(void) { return; }
void __stack_chk_fail(void) { return; }
+#ifdef __APPLE__
+#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 */ );
+
+#else /* __APPLE__ */
#ifdef __i386__
/* data for setting up the glibc-style thread-local storage in %gs */
@@ -536,16 +767,17 @@ SYSCALL_NOERR( wld_getegid, 177 /* SYS_getegid */ );
#else
#error preloader not implemented for this CPU
#endif
+#endif /* __APPLE__ */
/* replacement for libc functions */
-static int wld_strcmp( const char *str1, const char *str2 )
+static inline 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 )
+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++; }
@@ -638,6 +870,8 @@ static __attribute__((noreturn,format(printf,1,2))) void fatal_error(const char
wld_exit(1);
}
+#ifndef __APPLE__
+
#ifdef DUMP_AUX_INFO
/*
* Dump interesting bits of the ELF auxv_t structure that is passed
@@ -1120,6 +1354,8 @@ found:
return (void *)(symtab[idx].st_value + map->l_addr);
}
+#endif /* !__APPLE__ */
+
/*
* preload_reserve
*
@@ -1151,6 +1387,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)
{
@@ -1158,6 +1395,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++)
@@ -1182,7 +1420,7 @@ error:
}
/* check if address is in one of the reserved ranges */
-static int is_addr_reserved( const void *addr )
+static inline int is_addr_reserved( const void *addr )
{
int i;
@@ -1206,6 +1444,180 @@ static void remove_preload_range( int i )
}
}
+#ifdef __APPLE__
+
+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 );
+ for (i = 0; preload_info[i].size; i++)
+ {
+ if (!map_region( &preload_info[i] ))
+ {
+ remove_preload_range( i );
+ 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;
+}
+
+#else /* __APPLE__ */
+
/*
* is_in_preload_range
*
@@ -1374,3 +1786,5 @@ void* wld_start( void **stack )
return (void *)ld_so_map.l_entry;
}
+
+#endif /* __APPLE__ */
--
2.10.2
More information about the wine-devel
mailing list