From b5d451ae12ab2abbbc43e8d0ff7b86adf85af8b5 Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Mon, 12 Jun 2017 00:50:05 +0200 Subject: [PATCH] Added patch to implement preloader for Mac OS. --- ...-restrict-base-address-of-main-threa.patch | 49 ++ ...oader-Implement-preloader-for-Mac-OS.patch | 723 ++++++++++++++++++ patches/loader-OSX_Preloader/definition | 3 + patches/patchinstall.sh | 36 + 4 files changed, 811 insertions(+) create mode 100644 patches/loader-OSX_Preloader/0001-libs-wine-Do-not-restrict-base-address-of-main-threa.patch create mode 100644 patches/loader-OSX_Preloader/0002-loader-Implement-preloader-for-Mac-OS.patch create mode 100644 patches/loader-OSX_Preloader/definition diff --git a/patches/loader-OSX_Preloader/0001-libs-wine-Do-not-restrict-base-address-of-main-threa.patch b/patches/loader-OSX_Preloader/0001-libs-wine-Do-not-restrict-base-address-of-main-threa.patch new file mode 100644 index 00000000..1084c910 --- /dev/null +++ b/patches/loader-OSX_Preloader/0001-libs-wine-Do-not-restrict-base-address-of-main-threa.patch @@ -0,0 +1,49 @@ +From 3174d46a02a1112d2f31a16ad895731fbbaddf21 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20M=C3=BCller?= +Date: Mon, 12 Jun 2017 00:18:37 +0200 +Subject: libs/wine: Do not restrict base address of main thread on 64 bit mac + os. + +--- + libs/wine/loader.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/libs/wine/loader.c b/libs/wine/loader.c +index e362a1d1939..5f37dc978ba 100644 +--- a/libs/wine/loader.c ++++ b/libs/wine/loader.c +@@ -700,6 +700,7 @@ struct apple_stack_info + * Callback for wine_mmap_enum_reserved_areas to allocate space for + * the secondary thread's stack. + */ ++#ifndef _WIN64 + static int apple_alloc_thread_stack( void *base, size_t size, void *arg ) + { + struct apple_stack_info *info = arg; +@@ -716,6 +717,7 @@ static int apple_alloc_thread_stack( void *base, size_t size, void *arg ) + info->desired_size, PROT_READ|PROT_WRITE, MAP_FIXED ); + return (info->stack != (void *)-1); + } ++#endif + + /*********************************************************************** + * apple_create_wine_thread +@@ -733,6 +735,7 @@ static void apple_create_wine_thread( void *init_func ) + + if (!pthread_attr_init( &attr )) + { ++#ifndef _WIN64 + struct apple_stack_info info; + + /* Try to put the new thread's stack in the reserved area. If this +@@ -744,6 +747,7 @@ static void apple_create_wine_thread( void *init_func ) + wine_mmap_remove_reserved_area( info.stack, info.desired_size, 0 ); + pthread_attr_setstackaddr( &attr, (char*)info.stack + info.desired_size ); + } ++#endif + + if (!pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ) && + !pthread_create( &thread, &attr, init_func, NULL )) +-- +2.13.1 + diff --git a/patches/loader-OSX_Preloader/0002-loader-Implement-preloader-for-Mac-OS.patch b/patches/loader-OSX_Preloader/0002-loader-Implement-preloader-for-Mac-OS.patch new file mode 100644 index 00000000..2cdc9353 --- /dev/null +++ b/patches/loader-OSX_Preloader/0002-loader-Implement-preloader-for-Mac-OS.patch @@ -0,0 +1,723 @@ +From e3a9a59d25e11351bf8ff6ed0578c5defbad84ae Mon Sep 17 00:00:00 2001 +From: Sebastian Lackner +Date: Mon, 12 Jun 2017 00:16:08 +0200 +Subject: loader: Implement preloader for Mac OS. + +--- + Makefile.in | 1 + + configure.ac | 32 ++++- + dlls/ntdll/virtual.c | 2 - + libs/wine/config.c | 2 +- + loader/Makefile.in | 4 +- + loader/main.c | 44 ++---- + loader/preloader.c | 386 +++++++++++++++++++++++++++++++++++++++++++++++++-- + 7 files changed, 420 insertions(+), 51 deletions(-) + +diff --git a/Makefile.in b/Makefile.in +index 22b2ae72305..29b6cbdd535 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 5aaa20c22b6..433e7fa959c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -709,6 +709,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" +@@ -767,9 +768,26 @@ 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,-image_base,0x7bf00000,-pie,-sectcreate,__TEXT,__info_plist,wine_info.plist" ++ WINE_TRY_CFLAGS([-fPIC -Wl,--export-dynamic], ++ [WINELOADER_LDFLAGS="-Wl,--export-dynamic $WINELOADER_LDFLAGS"]) ++ ++ WINEPRELOADER_LDFLAGS="-nostartfiles -nodefaultlibs -e _start -ldylib1.o -Wl,-image_base,0x7c400000,-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"]) ++ ++ case $host_cpu in ++ *i[[3456789]]86*) ++ WINELOADER_LDFLAGS="-Wl,-pagezero_size,0x0 $WINELOADER_LDFLAGS" ++ WINEPRELOADER_LDFLAGS="-Wl,-pagezero_size,0x0 $WINEPRELOADER_LDFLAGS" ;; ++ *x86_64*) ++ WINELOADER_LDFLAGS="-Wl,-pagezero_size,0x1000 $WINELOADER_LDFLAGS" ++ WINEPRELOADER_LDFLAGS="-Wl,-pagezero_size,0x1000 $WINEPRELOADER_LDFLAGS" ;; ++ esac ++ + 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 +@@ -882,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"], +@@ -926,6 +945,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}\`:\$(DESTDIR)\${libdir}" +@@ -2085,6 +2105,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 0d3fa1e5715..3a280a4b0d8 100644 +--- a/dlls/ntdll/virtual.c ++++ b/dlls/ntdll/virtual.c +@@ -1941,11 +1941,9 @@ void virtual_release_address_space(void) + } + else + { +-#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 7daf38a07b5..7223ecd89cf 100644 +--- a/libs/wine/config.c ++++ b/libs/wine/config.c +@@ -1709,7 +1709,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 4fb91ff5417..b715f022578 100644 +--- a/loader/Makefile.in ++++ b/loader/Makefile.in +@@ -26,7 +26,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 13740c7b3c5..5212e3f0798 100644 +--- a/loader/main.c ++++ b/loader/main.c +@@ -50,30 +50,6 @@ + #ifdef __APPLE__ + #include + +-#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 ); +-} +- + static const char *get_macho_library_path( const char *libname ) + { + unsigned int path_len, libname_len = strlen( libname ); +@@ -94,18 +70,11 @@ static const char *get_macho_library_path( const char *libname ) + return NULL; + } + +-#else /* __APPLE__ */ ++#endif /* __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 + * +@@ -315,6 +284,13 @@ static int pre_exec(void) + return 1; /* we have a preloader on x86-64 */ + } + ++#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) +@@ -360,12 +336,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 5e6add7830f..b3e80527309 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,14 @@ + #ifdef HAVE_SYS_LINK_H + # include + #endif ++#ifdef HAVE_MACH_O_LOADER_H ++#include ++#include ++#include ++#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 +@@ -117,12 +118,25 @@ static struct wine_preload_info preload_info[] = + { (void *)0x000000010000, 0x00100000 }, /* DOS area */ + { (void *)0x000000110000, 0x67ef0000 }, /* low memory area */ + { (void *)0x00007ff00000, 0x000f0000 }, /* shared user data */ ++#ifdef __APPLE__ ++ /* address below exceeds maximum allowed user space address in mac os */ ++ { (void *)0x7fff40000000, 0x01ff0000 }, /* top-down allocations + virtual heap */ ++#else + { (void *)0x7ffffe000000, 0x01ff0000 }, /* top-down allocations + virtual heap */ + #endif ++#endif + { 0, 0 }, /* PE exe range set with WINEPRELOADRESERVE */ + { 0, 0 } /* end of list */ + }; + ++#ifndef __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 +182,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 +198,196 @@ 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_mask = 0xfff; ++#define TARGET_CPU_TYPE CPU_TYPE_X86 ++#define TARGET_MH_MAGIC MH_MAGIC ++#define TARGET_SEGMENT_COMMAND LC_SEGMENT ++#define target_mach_header mach_header ++#define target_segment_command segment_command ++#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_mask = 0xfff; ++#define TARGET_CPU_TYPE CPU_TYPE_X86_64 ++#define TARGET_MH_MAGIC MH_MAGIC_64 ++#define TARGET_SEGMENT_COMMAND LC_SEGMENT_64 ++#define target_mach_header mach_header_64 ++#define target_segment_command segment_command_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 */ ); ++ ++#else /* __APPLE__ */ + #ifdef __i386__ + + /* data for setting up the glibc-style thread-local storage in %gs */ +@@ -458,16 +664,17 @@ SYSCALL_NOERR( wld_getegid, 108 /* 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++; } +@@ -560,6 +767,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 +@@ -1039,6 +1248,8 @@ found: + return (void *)(symtab[idx].st_value + map->l_addr); + } + ++#endif /* !__APPLE__ */ ++ + /* + * preload_reserve + * +@@ -1070,6 +1281,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) + { +@@ -1077,6 +1289,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++) +@@ -1101,7 +1314,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; + +@@ -1125,6 +1338,159 @@ static void remove_preload_range( int i ) + } + } + ++#ifdef __APPLE__ ++ ++#define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL ++MAKE_FUNCPTR(dlopen); ++MAKE_FUNCPTR(dlsym); ++MAKE_FUNCPTR(_dyld_image_count); ++MAKE_FUNCPTR(_dyld_get_image_header); ++MAKE_FUNCPTR(_dyld_get_image_vmaddr_slide); ++#undef MAKE_FUNCPTR ++ ++extern int _dyld_func_lookup( const char *dyld_func_name, void **address ); ++ ++static struct target_mach_header *find_executable( intptr_t *slide ) ++{ ++ struct target_mach_header *mh; ++ int i; ++ ++ /* skip our own executable */ ++ for (i = 1; i < p_dyld_image_count(); i++) ++ { ++ mh = (struct target_mach_header *)p_dyld_get_image_header( i ); ++ if (!mh) continue; ++ if (mh->magic != TARGET_MH_MAGIC) continue; ++ if (mh->cputype != TARGET_CPU_TYPE) continue; ++ if (mh->filetype != MH_EXECUTE) continue; ++ ++ *slide = p_dyld_get_image_vmaddr_slide( i ); ++ return mh; ++ } ++ ++ return NULL; ++} ++ ++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 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 **wine_main_preload_info; ++ char **argv, **p, *reserve = NULL; ++ struct target_mach_header *mh; ++ void *mod, *entry; ++ intptr_t slide; ++ int *pargc, i; ++ ++ 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++) ++ { ++ int flags = MAP_PRIVATE | MAP_ANON; ++ void *ret; ++ ++ if (!preload_info[i].addr) ++ flags |= MAP_FIXED; ++ ++ ret = wld_mmap( preload_info[i].addr, preload_info[i].size, PROT_NONE, flags, -1, 0 ); ++ if (ret != (void *)-1 && ret != preload_info[i].addr) ++ { ++ wld_munmap(ret, preload_info[i].size); ++ ret = (void *)-1; ++ } ++ if (ret == (void *)-1) ++ { ++ 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 ); ++ i--; ++ } ++ } ++ ++ LOAD_POSIX_DYLD_FUNC( dlopen ); ++ LOAD_POSIX_DYLD_FUNC( dlsym ); ++ LOAD_MACHO_DYLD_FUNC( _dyld_image_count ); ++ LOAD_MACHO_DYLD_FUNC( _dyld_get_image_header ); ++ LOAD_MACHO_DYLD_FUNC( _dyld_get_image_vmaddr_slide ); ++ ++ /* load the main binary */ ++ if (!(mod = pdlopen( argv[1], RTLD_NOW ))) ++ fatal_error( "%s: could not load binary\n", argv[1] ); ++ ++ /* 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" ); ++ ++ /* there is no way to translate the dlopen handle to the mach header :-( */ ++ if (!(mh = find_executable( &slide ))) ++ fatal_error( "%s: could not find mach header\n", argv[1] ); ++ if (!(entry = get_entry_point( mh, slide, is_unix_thread ))) ++ fatal_error( "%s: could not find entry point\n", argv[1] ); ++ ++ return entry; ++} ++ ++#else /* __APPLE__ */ ++ + /* + * is_in_preload_range + * +@@ -1293,3 +1659,5 @@ void* wld_start( void **stack ) + + return (void *)ld_so_map.l_entry; + } ++ ++#endif /* __APPLE__ */ +-- +2.13.1 + diff --git a/patches/loader-OSX_Preloader/definition b/patches/loader-OSX_Preloader/definition new file mode 100644 index 00000000..660e0db3 --- /dev/null +++ b/patches/loader-OSX_Preloader/definition @@ -0,0 +1,3 @@ +Fixes: [33159] Implement preloader for Mac OS +Depends: Staging +Depends: configure-Absolute_RPATH diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index c771d8d2..22dd25fe 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -203,6 +203,7 @@ patch_enable_all () enable_krnl386_exe16__lclose16="$1" enable_libs_Debug_Channel="$1" enable_libs_Unicode_Collation="$1" + enable_loader_OSX_Preloader="$1" enable_makedep_PARENTSPEC="$1" enable_mmsystem_dll16_MIDIHDR_Refcount="$1" enable_mountmgr_DosDevices="$1" @@ -846,6 +847,9 @@ patch_enable () libs-Unicode_Collation) enable_libs_Unicode_Collation="$2" ;; + loader-OSX_Preloader) + enable_loader_OSX_Preloader="$2" + ;; makedep-PARENTSPEC) enable_makedep_PARENTSPEC="$2" ;; @@ -2477,6 +2481,17 @@ if test "$enable_ntdll_ApiSetMap" -eq 1; then enable_ntdll_ThreadTime=1 fi +if test "$enable_loader_OSX_Preloader" -eq 1; then + if test "$enable_Staging" -gt 1; then + abort "Patchset Staging disabled, but loader-OSX_Preloader depends on that." + fi + if test "$enable_configure_Absolute_RPATH" -gt 1; then + abort "Patchset configure-Absolute_RPATH disabled, but loader-OSX_Preloader depends on that." + fi + enable_Staging=1 + enable_configure_Absolute_RPATH=1 +fi + if test "$enable_kernel32_SetFileCompletionNotificationModes" -eq 1; then if test "$enable_ntdll_FileNameInformation" -gt 1; then abort "Patchset ntdll-FileNameInformation disabled, but kernel32-SetFileCompletionNotificationModes depends on that." @@ -5115,6 +5130,27 @@ if test "$enable_libs_Unicode_Collation" -eq 1; then ) >> "$patchlist" fi +# Patchset loader-OSX_Preloader +# | +# | This patchset has the following (direct or indirect) dependencies: +# | * Staging, configure-Absolute_RPATH +# | +# | This patchset fixes the following Wine bugs: +# | * [#33159] Implement preloader for Mac OS +# | +# | Modified files: +# | * Makefile.in, configure.ac, dlls/ntdll/virtual.c, libs/wine/config.c, libs/wine/loader.c, loader/Makefile.in, +# | loader/main.c, loader/preloader.c +# | +if test "$enable_loader_OSX_Preloader" -eq 1; then + patch_apply loader-OSX_Preloader/0001-libs-wine-Do-not-restrict-base-address-of-main-threa.patch + patch_apply loader-OSX_Preloader/0002-loader-Implement-preloader-for-Mac-OS.patch + ( + printf '%s\n' '+ { "Michael Müller", "libs/wine: Do not restrict base address of main thread on 64 bit mac os.", 1 },'; + printf '%s\n' '+ { "Sebastian Lackner", "loader: Implement preloader for Mac OS.", 1 },'; + ) >> "$patchlist" +fi + # Patchset makedep-PARENTSPEC # | # | Modified files: