From: Brendan Shanks bshanks@codeweavers.com
--- loader/preloader_mac.c | 129 +++++++++++++++++++++++++++++------------ 1 file changed, 91 insertions(+), 38 deletions(-)
diff --git a/loader/preloader_mac.c b/loader/preloader_mac.c index 01a41f08d8c..7b7e589d684 100644 --- a/loader/preloader_mac.c +++ b/loader/preloader_mac.c @@ -101,6 +101,47 @@ void *__stack_chk_guard = 0; void __stack_chk_fail_local(void) { return; } void __stack_chk_fail(void) { return; }
+/* + * When 'start' is called, stack frame looks like: + * + * : + * | STRING AREA | + * +-------------+ + * | 0 | + * +-------------+ + * | exec_path | extra "apple" parameters start after NULL terminating env array + * +-------------+ + * | 0 | + * +-------------+ + * | env[n] | + * +-------------+ + * : + * : + * +-------------+ + * | env[0] | + * +-------------+ + * | 0 | + * +-------------+ + * | arg[argc-1] | + * +-------------+ + * : + * : + * +-------------+ + * | arg[0] | + * +-------------+ + * | argc | argc is always 4 bytes long, even in 64-bit architectures + * +-------------+ <- sp + * + * Where arg[i] and env[i] point into the STRING AREA + * + * See also: + * macOS C runtime 'start': + * https://github.com/apple-oss-distributions/Csu/blob/Csu-88/start.s + * + * macOS dyld '__dyld_start' (pre-dyld4): + * https://github.com/apple-oss-distributions/dyld/blob/dyld-852.2/src/dyldStartup.s + */ + #ifdef __i386__
static const size_t page_size = 0x1000; @@ -132,13 +173,14 @@ static const size_t page_mask = 0xfff; __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) */ - + "\t.byte 0x6a,0x00\n" /* pushl $0: push a zero for debugger end of frames marker */ + "\t.byte 0x89,0xe5\n" /* movl %esp,%ebp: pointer to base of kernel frame */ + "\t.byte 0x83,0xe4,0xf0\n" /* andl $-16,%esp: force SSE alignment */ + "\t.byte 0x83,0xec,0x10\n" /* subl $16,%esp: room for new argc, argv, & envp, SSE aligned */ + "\t.byte 0x8b,0x5d,0x04\n" /* movl 4(%ebp),%ebx: pickup argc in %ebx */ + "\t.byte 0x89,0x5c,0x24,0x00\n" /* movl %ebx,0(%esp): argc to reserved stack word */ + + /* call wld_start(stack, &is_unix_thread) */ "\tleal 4(%ebp),%eax\n" "\tmovl %eax,0(%esp)\n" /* stack */ "\tleal 8(%esp),%eax\n" @@ -146,16 +188,21 @@ __ASM_GLOBAL_FUNC( start, "\tmovl $0,(%eax)\n" "\tcall _wld_start\n"
+ /* argc/argv need to be fixed to remove argv[0]. + * With LC_MAIN, pointers to argv/env/apple are passed so this is easy. + * With LC_UNIXTHREAD, argv[1] to the end of apple[] need to be moved. + */ "\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 */ + "\tdecl %edi\n" /* %edi = argc (decremented) */ + "\tleal 12(%ebp),%esi\n" /* %esi = &argv[1] */ + "\tleal 4(%esi,%edi,4),%edx\n" /* %edx = env */ + "\tmovl %edx,%ecx\n" "1:\tmovl (%ecx),%ebx\n" "\tadd $4,%ecx\n" - "\torl %ebx,%ebx\n" - "\tjnz 1b\n" + "\torl %ebx,%ebx\n" /* look for the NULL ending the env[] array */ + "\tjnz 1b\n" /* %ecx = apple data */
+ /* jmp based on is_unix_thread */ "\tcmpl $0,8(%esp)\n" "\tjne 2f\n"
@@ -164,16 +211,16 @@ __ASM_GLOBAL_FUNC( start, "\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" + "\tcall *%eax\n" /* call main(argc,argv,env,apple) */ + "\tmovl %eax,(%esp)\n" /* pass result from main() to exit() */ + "\tcall _wld_exit\n" /* need to use call to keep stack aligned */ "\thlt\n"
/* LC_UNIXTHREAD */ "2:\tmovl (%ecx),%ebx\n" "\tadd $4,%ecx\n" - "\torl %ebx,%ebx\n" - "\tjnz 2b\n" + "\torl %ebx,%ebx\n" /* look for the NULL ending the apple[] array */ + "\tjnz 2b\n" /* %ecx = end of apple[] */
"\tsubl %ebp,%ecx\n" "\tsubl $8,%ecx\n" @@ -186,8 +233,8 @@ __ASM_GLOBAL_FUNC( start, "\tcld\n" "\trep; movsd\n" /* argv, ... */
- "\tmovl $0,%ebp\n" - "\tjmpl *%eax\n" ) + "\tmovl $0,%ebp\n" /* restore ebp back to zero */ + "\tjmpl *%eax\n" ) /* jump to the entry point */
#elif defined(__x86_64__)
@@ -221,41 +268,47 @@ static const size_t page_mask = 0xfff;
__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" + "\tpushq $0\n" /* push a zero for debugger end of frames marker */ + "\tmovq %rsp,%rbp\n" /* pointer to base of kernel frame */ + "\tandq $-16,%rsp\n" /* force SSE alignment */ + "\tsubq $16,%rsp\n" /* room for local variables */
+ /* call wld_start(stack, &is_unix_thread) */ "\tleaq 8(%rbp),%rdi\n" /* stack */ "\tmovq %rsp,%rsi\n" /* &is_unix_thread */ "\tmovq $0,(%rsi)\n" "\tcall _wld_start\n"
+ /* argc/argv need to be fixed to remove argv[0]. + * With LC_MAIN, pointers to argv/env/apple are passed so this is easy. + * With LC_UNIXTHREAD, argv[1] to the end of apple[] need to be moved. + */ "\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 */ + "\tdec %rdi\n" /* %rdi = argc (decremented) */ + "\tleaq 24(%rbp),%rsi\n" /* %rsi = &argv[1] */ + "\tleaq 8(%rsi,%rdi,8),%rdx\n" /* %rdx = env */ + "\tmovq %rdx,%rcx\n" "1:\tmovq (%rcx),%r8\n" "\taddq $8,%rcx\n" - "\torq %r8,%r8\n" - "\tjnz 1b\n" + "\torq %r8,%r8\n" /* look for the NULL ending the env[] array */ + "\tjnz 1b\n" /* %rcx = apple data */
+ /* jmp based on is_unix_thread */ "\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" + "\taddq $16,%rsp\n" /* remove local variables */ + "\tcall *%rax\n" /* call main(argc,argv,env,apple) */ + "\tmovq %rax,%rdi\n" /* pass result from main() to exit() */ + "\tcall _wld_exit\n" /* need to use call to keep stack aligned */ "\thlt\n"
/* LC_UNIXTHREAD */ "2:\tmovq (%rcx),%r8\n" "\taddq $8,%rcx\n" - "\torq %r8,%r8\n" - "\tjnz 2b\n" + "\torq %r8,%r8\n" /* look for the NULL ending the apple[] array */ + "\tjnz 2b\n" /* %rcx = end of apple[] */
"\tsubq %rbp,%rcx\n" "\tsubq $16,%rcx\n" @@ -268,8 +321,8 @@ __ASM_GLOBAL_FUNC( start, "\tcld\n" "\trep; movsq\n" /* argv, ... */
- "\tmovq $0,%rbp\n" - "\tjmpq *%rax\n" ) + "\tmovq $0,%rbp\n" /* restore ebp back to zero */ + "\tjmpq *%rax\n" ) /* jump to the entry point */
#else #error preloader not implemented for this CPU
From: Brendan Shanks bshanks@codeweavers.com
--- loader/preloader_mac.c | 116 ++++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 55 deletions(-)
diff --git a/loader/preloader_mac.c b/loader/preloader_mac.c index 7b7e589d684..eb0277469a9 100644 --- a/loader/preloader_mac.c +++ b/loader/preloader_mac.c @@ -188,13 +188,12 @@ __ASM_GLOBAL_FUNC( start, "\tmovl $0,(%eax)\n" "\tcall _wld_start\n"
- /* argc/argv need to be fixed to remove argv[0]. - * With LC_MAIN, pointers to argv/env/apple are passed so this is easy. - * With LC_UNIXTHREAD, argv[1] to the end of apple[] need to be moved. - */ - "\tmovl 4(%ebp),%edi\n" - "\tdecl %edi\n" /* %edi = argc (decremented) */ - "\tleal 12(%ebp),%esi\n" /* %esi = &argv[1] */ + /* jmp based on is_unix_thread */ + "\tcmpl $0,8(%esp)\n" + "\tjne 2f\n" + + "\tmovl 4(%ebp),%edi\n" /* %edi = argc */ + "\tleal 8(%ebp),%esi\n" /* %esi = argv */ "\tleal 4(%esi,%edi,4),%edx\n" /* %edx = env */ "\tmovl %edx,%ecx\n" "1:\tmovl (%ecx),%ebx\n" @@ -202,10 +201,6 @@ __ASM_GLOBAL_FUNC( start, "\torl %ebx,%ebx\n" /* look for the NULL ending the env[] array */ "\tjnz 1b\n" /* %ecx = apple data */
- /* jmp based on is_unix_thread */ - "\tcmpl $0,8(%esp)\n" - "\tjne 2f\n" - /* LC_MAIN */ "\tmovl %edi,0(%esp)\n" /* argc */ "\tmovl %esi,4(%esp)\n" /* argv */ @@ -217,22 +212,8 @@ __ASM_GLOBAL_FUNC( start, "\thlt\n"
/* LC_UNIXTHREAD */ - "2:\tmovl (%ecx),%ebx\n" - "\tadd $4,%ecx\n" - "\torl %ebx,%ebx\n" /* look for the NULL ending the apple[] array */ - "\tjnz 2b\n" /* %ecx = end of apple[] */ - - "\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, ... */ - + "\t2:movl %ebp,%esp\n" /* restore the unaligned stack pointer */ + "\taddl $4,%esp\n" /* remove the debugger end frame marker */ "\tmovl $0,%ebp\n" /* restore ebp back to zero */ "\tjmpl *%eax\n" ) /* jump to the entry point */
@@ -279,13 +260,13 @@ __ASM_GLOBAL_FUNC( start, "\tmovq $0,(%rsi)\n" "\tcall _wld_start\n"
- /* argc/argv need to be fixed to remove argv[0]. - * With LC_MAIN, pointers to argv/env/apple are passed so this is easy. - * With LC_UNIXTHREAD, argv[1] to the end of apple[] need to be moved. - */ - "\tmovq 8(%rbp),%rdi\n" - "\tdec %rdi\n" /* %rdi = argc (decremented) */ - "\tleaq 24(%rbp),%rsi\n" /* %rsi = &argv[1] */ + /* jmp based on is_unix_thread */ + "\tcmpl $0,0(%rsp)\n" + "\tjne 2f\n" + + /* LC_MAIN */ + "\tmovq 8(%rbp),%rdi\n" /* %rdi = argc */ + "\tleaq 16(%rbp),%rsi\n" /* %rsi = argv */ "\tleaq 8(%rsi,%rdi,8),%rdx\n" /* %rdx = env */ "\tmovq %rdx,%rcx\n" "1:\tmovq (%rcx),%r8\n" @@ -293,11 +274,6 @@ __ASM_GLOBAL_FUNC( start, "\torq %r8,%r8\n" /* look for the NULL ending the env[] array */ "\tjnz 1b\n" /* %rcx = apple data */
- /* jmp based on is_unix_thread */ - "\tcmpl $0,0(%rsp)\n" - "\tjne 2f\n" - - /* LC_MAIN */ "\taddq $16,%rsp\n" /* remove local variables */ "\tcall *%rax\n" /* call main(argc,argv,env,apple) */ "\tmovq %rax,%rdi\n" /* pass result from main() to exit() */ @@ -305,22 +281,8 @@ __ASM_GLOBAL_FUNC( start, "\thlt\n"
/* LC_UNIXTHREAD */ - "2:\tmovq (%rcx),%r8\n" - "\taddq $8,%rcx\n" - "\torq %r8,%r8\n" /* look for the NULL ending the apple[] array */ - "\tjnz 2b\n" /* %rcx = end of apple[] */ - - "\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, ... */ - + "\t2:movq %rbp,%rsp\n" /* restore the unaligned stack pointer */ + "\taddq $8,%rsp\n" /* remove the debugger end frame marker */ "\tmovq $0,%rbp\n" /* restore ebp back to zero */ "\tjmpq *%rax\n" ) /* jump to the entry point */
@@ -355,6 +317,23 @@ extern int _dyld_func_lookup( const char *dyld_func_name, void **address );
/* replacement for libc functions */
+void * memmove( void *dst, const void *src, size_t len ) +{ + char *d = dst; + const char *s = src; + if (d < s) + while (len--) + *d++ = *s++; + else + { + const char *lasts = s + (len-1); + char *lastd = d + (len-1); + while (len--) + *lastd-- = *lasts--; + } + return dst; +} + static int wld_strncmp( const char *str1, const char *str2, size_t len ) { if (len <= 0) return 0; @@ -637,6 +616,30 @@ static inline void get_dyld_func( const char *name, void **func ) #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 )
+static void fixup_stack( void *stack ) +{ + int *pargc; + char **argv, **env, **apple, **apple_end; + + pargc = stack; + argv = (char **)pargc + 1; + env = &argv[*pargc-1] + 2; + + apple = env; + while (*apple) + apple++; + apple++; + + apple_end = apple; + while (*apple_end) + apple_end++; + apple_end++; + + /* decrement argc, and move all the data between &argv[1] and apple_end down to start at &argv[0] */ + *pargc = *pargc - 1; + memmove(&argv[0], &argv[1], (char *)apple_end - (char *)&argv[1]); +} + void *wld_start( void *stack, int *is_unix_thread ) { struct wine_preload_info builtin_dlls = { (void *)0x7a000000, 0x02000000 }; @@ -698,6 +701,9 @@ void *wld_start( void *stack, int *is_unix_thread ) 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] );
+ /* decrement argc and "remove" argv[0] */ + fixup_stack(stack); + return entry; }
From: Brendan Shanks bshanks@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54009 --- loader/preloader_mac.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/loader/preloader_mac.c b/loader/preloader_mac.c index eb0277469a9..bb9336ead2c 100644 --- a/loader/preloader_mac.c +++ b/loader/preloader_mac.c @@ -704,6 +704,22 @@ void *wld_start( void *stack, int *is_unix_thread ) /* decrement argc and "remove" argv[0] */ fixup_stack(stack);
+#if defined(__x86_64__) + /* For LC_UNIXTHREAD binaries on Monterey and later, 'environ' is not set and is NULL. + * Set the correct value here. + */ + if (*is_unix_thread) + { + char **env, ***wine_environ = pdlsym( mod, "environ" ); + + pargc = stack; + argv = (char **)pargc + 1; + env = &argv[*pargc-1] + 2; + + *wine_environ = env; + } +#endif + return entry; }