4 * Copyright (C) 1995,96,97,98,99,2000,2001,2002 Free Software Foundation, Inc.
5 * Copyright (C) 2004 Mike McCormack for CodeWeavers
6 * Copyright (C) 2004 Alexandre Julliard
7 * Copyright (C) 2017 Michael Müller
8 * Copyright (C) 2017 Sebastian Lackner
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
33 #include <sys/types.h>
37 #ifdef HAVE_SYS_SYSCALL_H
38 # include <sys/syscall.h>
42 #ifdef HAVE_MACH_O_LOADER_H
43 #include <mach/thread_status.h>
44 #include <mach-o/loader.h>
45 #include <mach-o/ldsyms.h>
51 #if defined(__x86_64__)
52 /* Reserve the low 8GB using a zero-fill section, this is the only way to
53 * prevent system frameworks from using any of it (including allocations
54 * before any preloader code runs)
56 __asm__(".zerofill WINE_RESERVE,WINE_RESERVE,___wine_reserve,0x1fffff000");
58 static const struct wine_preload_info zerofill_sections
[] =
60 { (void *)0x000000001000, 0x1fffff000 }, /* WINE_RESERVE section */
61 { 0, 0 } /* end of list */
64 static const struct wine_preload_info zerofill_sections
[] =
66 { 0, 0 } /* end of list */
71 #define LC_MAIN 0x80000028
72 struct entry_point_command
81 static struct wine_preload_info preload_info
[] =
83 /* On macOS, we allocate the low 64k area in two steps because PAGEZERO
84 * might not always be available. */
86 { (void *)0x00000000, 0x00001000 }, /* first page */
87 { (void *)0x00001000, 0x0000f000 }, /* low 64k */
88 { (void *)0x00010000, 0x00100000 }, /* DOS area */
89 { (void *)0x00110000, 0x67ef0000 }, /* low memory area */
90 { (void *)0x7f000000, 0x03000000 }, /* top-down allocations + shared user data + virtual heap */
92 { (void *)0x000000001000, 0x1fffff000 }, /* WINE_RESERVE section */
93 { (void *)0x7ff000000000, 0x01ff0000 }, /* top-down allocations + virtual heap */
95 { 0, 0 }, /* PE exe range set with WINEPRELOADRESERVE */
96 { 0, 0 } /* end of list */
100 * These functions are only called when file is compiled with -fstack-protector.
101 * They are normally provided by libc's startup files, but since we
102 * build the preloader with "-nostartfiles -nodefaultlibs", we have to
103 * provide our own versions, otherwise the linker fails.
105 void *__stack_chk_guard
= 0;
106 void __stack_chk_fail_local(void) { return; }
107 void __stack_chk_fail(void) { return; }
109 /* Binaries targeting 10.6 and 10.7 contain the __program_vars section, and
110 * dyld4 (starting in Monterey) does not like it to be missing:
111 * - running vmmap on a Wine process prints this warning:
112 * "Process exists but has not fully started -- dyld has initialized but libSystem has not"
113 * - because libSystem is not initialized, dlerror() always returns NULL (causing GStreamer
115 * - starting with macOS Sonoma, Wine crashes on launch if libSystem is not initialized.
117 * Adding __program_vars fixes those issues, and also allows more of the vars to
118 * be set correctly by the preloader for the loaded binary.
121 * <https://github.com/apple-oss-distributions/Csu/blob/Csu-88/crt.c#L42>
122 * <https://github.com/apple-oss-distributions/dyld/blob/dyld-1042.1/common/MachOAnalyzer.cpp#L2185>
125 const char** NXArgv
= NULL
;
126 const char** environ
= NULL
;
127 const char* __progname
= NULL
;
129 extern void* __dso_handle
;
134 const char*** NXArgvPtr
;
135 const char*** environPtr
;
136 const char** __prognamePtr
;
138 __attribute__((used
)) static struct ProgramVars pvars
139 __attribute__ ((section ("__DATA,__program_vars"))) = { &__dso_handle
, &NXArgc
, &NXArgv
, &environ
, &__progname
};
143 * When 'start' is called, stack frame looks like:
150 * | exec_path | extra "apple" parameters start after NULL terminating env array
170 * | argc | argc is always 4 bytes long, even in 64-bit architectures
171 * +-------------+ <- sp
173 * Where arg[i] and env[i] point into the STRING AREA
176 * macOS C runtime 'start':
177 * <https://github.com/apple-oss-distributions/Csu/blob/Csu-88/start.s>
179 * macOS dyld '__dyld_start' (pre-dyld4):
180 * <https://github.com/apple-oss-distributions/dyld/blob/dyld-852.2/src/dyldStartup.s>
185 static const size_t page_mask
= 0xfff;
186 #define target_mach_header mach_header
187 #define target_segment_command segment_command
188 #define TARGET_LC_SEGMENT LC_SEGMENT
189 #define target_thread_state_t i386_thread_state_t
190 #ifdef __DARWIN_UNIX03
191 #define target_thread_ip(x) (x)->__eip
193 #define target_thread_ip(x) (x)->eip
196 #define SYSCALL_FUNC( name, nr ) \
197 __ASM_GLOBAL_FUNC( name, \
198 "\tmovl $" #nr ",%eax\n" \
201 "\tmovl $-1,%eax\n" \
204 #define SYSCALL_NOERR( name, nr ) \
205 __ASM_GLOBAL_FUNC( name, \
206 "\tmovl $" #nr ",%eax\n" \
210 __ASM_GLOBAL_FUNC( start
,
211 __ASM_CFI("\t.cfi_undefined %eip\n")
212 /* The first 16 bytes are used as a function signature on i386 */
213 "\t.byte 0x6a,0x00\n" /* pushl $0: push a zero for debugger end of frames marker */
214 "\t.byte 0x89,0xe5\n" /* movl %esp,%ebp: pointer to base of kernel frame */
215 "\t.byte 0x83,0xe4,0xf0\n" /* andl $-16,%esp: force SSE alignment */
216 "\t.byte 0x83,0xec,0x10\n" /* subl $16,%esp: room for new argc, argv, & envp, SSE aligned */
217 "\t.byte 0x8b,0x5d,0x04\n" /* movl 4(%ebp),%ebx: pickup argc in %ebx */
218 "\t.byte 0x89,0x5c,0x24,0x00\n" /* movl %ebx,0(%esp): argc to reserved stack word */
220 /* call wld_start(stack, &is_unix_thread) */
221 "\tleal 4(%ebp),%eax\n"
222 "\tmovl %eax,0(%esp)\n" /* stack */
223 "\tleal 8(%esp),%eax\n"
224 "\tmovl %eax,4(%esp)\n" /* &is_unix_thread */
226 "\tcall _wld_start\n"
228 /* jmp based on is_unix_thread */
229 "\tcmpl $0,8(%esp)\n"
232 "\tmovl 4(%ebp),%edi\n" /* %edi = argc */
233 "\tleal 8(%ebp),%esi\n" /* %esi = argv */
234 "\tleal 4(%esi,%edi,4),%edx\n" /* %edx = env */
236 "1:\tmovl (%ecx),%ebx\n"
238 "\torl %ebx,%ebx\n" /* look for the NULL ending the env[] array */
239 "\tjnz 1b\n" /* %ecx = apple data */
242 "\tmovl %edi,0(%esp)\n" /* argc */
243 "\tmovl %esi,4(%esp)\n" /* argv */
244 "\tmovl %edx,8(%esp)\n" /* env */
245 "\tmovl %ecx,12(%esp)\n" /* apple data */
246 "\tcall *%eax\n" /* call main(argc,argv,env,apple) */
247 "\tmovl %eax,(%esp)\n" /* pass result from main() to exit() */
248 "\tcall _wld_exit\n" /* need to use call to keep stack aligned */
252 "\t2:movl %ebp,%esp\n" /* restore the unaligned stack pointer */
253 "\taddl $4,%esp\n" /* remove the debugger end frame marker */
254 "\tmovl $0,%ebp\n" /* restore ebp back to zero */
255 "\tjmpl *%eax\n" ) /* jump to the entry point */
257 #elif defined(__x86_64__)
259 static const size_t page_mask
= 0xfff;
260 #define target_mach_header mach_header_64
261 #define target_segment_command segment_command_64
262 #define TARGET_LC_SEGMENT LC_SEGMENT_64
263 #define target_thread_state_t x86_thread_state64_t
264 #ifdef __DARWIN_UNIX03
265 #define target_thread_ip(x) (x)->__rip
267 #define target_thread_ip(x) (x)->rip
270 #define SYSCALL_FUNC( name, nr ) \
271 __ASM_GLOBAL_FUNC( name, \
272 "\tmovq %rcx, %r10\n" \
273 "\tmovq $(" #nr "|0x2000000),%rax\n" \
276 "\tmovq $-1,%rax\n" \
279 #define SYSCALL_NOERR( name, nr ) \
280 __ASM_GLOBAL_FUNC( name, \
281 "\tmovq %rcx, %r10\n" \
282 "\tmovq $(" #nr "|0x2000000),%rax\n" \
286 __ASM_GLOBAL_FUNC( start
,
287 __ASM_CFI("\t.cfi_undefined %rip\n")
288 "\tpushq $0\n" /* push a zero for debugger end of frames marker */
289 "\tmovq %rsp,%rbp\n" /* pointer to base of kernel frame */
290 "\tandq $-16,%rsp\n" /* force SSE alignment */
291 "\tsubq $16,%rsp\n" /* room for local variables */
293 /* call wld_start(stack, &is_unix_thread) */
294 "\tleaq 8(%rbp),%rdi\n" /* stack */
295 "\tmovq %rsp,%rsi\n" /* &is_unix_thread */
297 "\tcall _wld_start\n"
299 /* jmp based on is_unix_thread */
300 "\tcmpl $0,0(%rsp)\n"
304 "\tmovq 8(%rbp),%rdi\n" /* %rdi = argc */
305 "\tleaq 16(%rbp),%rsi\n" /* %rsi = argv */
306 "\tleaq 8(%rsi,%rdi,8),%rdx\n" /* %rdx = env */
308 "1:\tmovq (%rcx),%r8\n"
310 "\torq %r8,%r8\n" /* look for the NULL ending the env[] array */
311 "\tjnz 1b\n" /* %rcx = apple data */
313 "\taddq $16,%rsp\n" /* remove local variables */
314 "\tcall *%rax\n" /* call main(argc,argv,env,apple) */
315 "\tmovq %rax,%rdi\n" /* pass result from main() to exit() */
316 "\tcall _wld_exit\n" /* need to use call to keep stack aligned */
320 "\t2:movq %rbp,%rsp\n" /* restore the unaligned stack pointer */
321 "\taddq $8,%rsp\n" /* remove the debugger end frame marker */
322 "\tmovq $0,%rbp\n" /* restore ebp back to zero */
323 "\tjmpq *%rax\n" ) /* jump to the entry point */
326 #error preloader not implemented for this CPU
329 void wld_exit( int code
) __attribute__((noreturn
));
330 SYSCALL_NOERR( wld_exit
, 1 /* SYS_exit */ );
332 ssize_t
wld_write( int fd
, const void *buffer
, size_t len
);
333 SYSCALL_FUNC( wld_write
, 4 /* SYS_write */ );
335 void *wld_mmap( void *start
, size_t len
, int prot
, int flags
, int fd
, off_t offset
);
336 SYSCALL_FUNC( wld_mmap
, 197 /* SYS_mmap */ );
338 void *wld_munmap( void *start
, size_t len
);
339 SYSCALL_FUNC( wld_munmap
, 73 /* SYS_munmap */ );
341 static intptr_t (*p_dyld_get_image_slide
)( const struct target_mach_header
* mh
);
343 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
344 MAKE_FUNCPTR(dlopen
);
346 MAKE_FUNCPTR(dladdr
);
349 extern int _dyld_func_lookup( const char *dyld_func_name
, void **address
);
351 /* replacement for libc functions */
353 void * memmove( void *dst
, const void *src
, size_t len
)
362 const char *lasts
= s
+ (len
-1);
363 char *lastd
= d
+ (len
-1);
370 static int wld_strncmp( const char *str1
, const char *str2
, size_t len
)
372 if (len
<= 0) return 0;
373 while ((--len
> 0) && *str1
&& (*str1
== *str2
)) { str1
++; str2
++; }
374 return *str1
- *str2
;
378 * wld_printf - just the basics
380 * %x prints a hex number
382 * %p prints a pointer
384 static int wld_vsprintf(char *buffer
, const char *fmt
, va_list args
)
386 static const char hex_chars
[16] = "0123456789abcdef";
398 unsigned int x
= va_arg( args
, unsigned int );
399 for (i
= 2*sizeof(x
) - 1; i
>= 0; i
--)
400 *str
++ = hex_chars
[(x
>>(i
*4))&0xf];
402 else if (p
[0] == 'l' && p
[1] == 'x')
404 unsigned long x
= va_arg( args
, unsigned long );
405 for (i
= 2*sizeof(x
) - 1; i
>= 0; i
--)
406 *str
++ = hex_chars
[(x
>>(i
*4))&0xf];
411 unsigned long x
= (unsigned long)va_arg( args
, void * );
412 for (i
= 2*sizeof(x
) - 1; i
>= 0; i
--)
413 *str
++ = hex_chars
[(x
>>(i
*4))&0xf];
417 char *s
= va_arg( args
, char * );
431 static __attribute__((format(printf
,1,2))) void wld_printf(const char *fmt
, ... )
437 va_start( args
, fmt
);
438 len
= wld_vsprintf(buffer
, fmt
, args
);
440 wld_write(2, buffer
, len
);
443 static __attribute__((noreturn
,format(printf
,1,2))) void fatal_error(const char *fmt
, ... )
449 va_start( args
, fmt
);
450 len
= wld_vsprintf(buffer
, fmt
, args
);
452 wld_write(2, buffer
, len
);
456 static int preloader_overlaps_range( const void *start
, const void *end
)
458 intptr_t slide
= p_dyld_get_image_slide(&_mh_execute_header
);
459 struct load_command
*cmd
= (struct load_command
*)(&_mh_execute_header
+ 1);
462 for (i
= 0; i
< _mh_execute_header
.ncmds
; ++i
)
464 if (cmd
->cmd
== TARGET_LC_SEGMENT
)
466 struct target_segment_command
*seg
= (struct target_segment_command
*)cmd
;
467 const void *seg_start
= (const void*)(seg
->vmaddr
+ slide
);
468 const void *seg_end
= (const char*)seg_start
+ seg
->vmsize
;
469 static const char reserved_segname
[] = "WINE_RESERVE";
471 if (!wld_strncmp( seg
->segname
, reserved_segname
, sizeof(reserved_segname
)-1 ))
474 if (end
> seg_start
&& start
<= seg_end
)
476 char segname
[sizeof(seg
->segname
) + 1];
477 memcpy(segname
, seg
->segname
, sizeof(seg
->segname
));
478 segname
[sizeof(segname
) - 1] = 0;
479 wld_printf( "WINEPRELOADRESERVE range %p-%p overlaps preloader %s segment %p-%p\n",
480 start
, end
, segname
, seg_start
, seg_end
);
484 cmd
= (struct load_command
*)((char*)cmd
+ cmd
->cmdsize
);
493 * Reserve a range specified in string format
495 static void preload_reserve( const char *str
)
498 unsigned long result
= 0;
499 void *start
= NULL
, *end
= NULL
;
502 for (p
= str
; *p
; p
++)
504 if (*p
>= '0' && *p
<= '9') result
= result
* 16 + *p
- '0';
505 else if (*p
>= 'a' && *p
<= 'f') result
= result
* 16 + *p
- 'a' + 10;
506 else if (*p
>= 'A' && *p
<= 'F') result
= result
* 16 + *p
- 'A' + 10;
509 if (!first
) goto error
;
510 start
= (void *)(result
& ~page_mask
);
516 if (!first
) end
= (void *)((result
+ page_mask
) & ~page_mask
);
517 else if (result
) goto error
; /* single value '0' is allowed */
520 if (end
<= start
|| preloader_overlaps_range(start
, end
))
523 /* check for overlap with low memory areas */
524 for (i
= 0; preload_info
[i
].size
; i
++)
526 if ((char *)preload_info
[i
].addr
> (char *)0x00110000) break;
527 if ((char *)end
<= (char *)preload_info
[i
].addr
+ preload_info
[i
].size
)
532 if ((char *)start
< (char *)preload_info
[i
].addr
+ preload_info
[i
].size
)
533 start
= (char *)preload_info
[i
].addr
+ preload_info
[i
].size
;
536 while (preload_info
[i
].size
) i
++;
537 preload_info
[i
].addr
= start
;
538 preload_info
[i
].size
= (char *)end
- (char *)start
;
542 fatal_error( "invalid WINEPRELOADRESERVE value '%s'\n", str
);
545 /* remove a range from the preload list */
546 static void remove_preload_range( int i
)
548 while (preload_info
[i
].size
)
550 preload_info
[i
].addr
= preload_info
[i
+1].addr
;
551 preload_info
[i
].size
= preload_info
[i
+1].size
;
556 static void *get_entry_point( struct target_mach_header
*mh
, intptr_t slide
, int *unix_thread
)
558 struct entry_point_command
*entry
;
559 target_thread_state_t
*state
;
560 struct load_command
*cmd
;
563 /* try LC_MAIN first */
564 cmd
= (struct load_command
*)(mh
+ 1);
565 for (i
= 0; i
< mh
->ncmds
; i
++)
567 if (cmd
->cmd
== LC_MAIN
)
569 *unix_thread
= FALSE
;
570 entry
= (struct entry_point_command
*)cmd
;
571 return (char *)mh
+ entry
->entryoff
;
573 cmd
= (struct load_command
*)((char *)cmd
+ cmd
->cmdsize
);
576 /* then try LC_UNIXTHREAD */
577 cmd
= (struct load_command
*)(mh
+ 1);
578 for (i
= 0; i
< mh
->ncmds
; i
++)
580 if (cmd
->cmd
== LC_UNIXTHREAD
)
583 state
= (target_thread_state_t
*)((char *)cmd
+ 16);
584 return (void *)(target_thread_ip(state
) + slide
);
586 cmd
= (struct load_command
*)((char *)cmd
+ cmd
->cmdsize
);
592 static int is_zerofill( struct wine_preload_info
*info
)
596 for (i
= 0; zerofill_sections
[i
].size
; i
++)
598 if ((zerofill_sections
[i
].addr
== info
->addr
) &&
599 (zerofill_sections
[i
].size
== info
->size
))
605 static int map_region( struct wine_preload_info
*info
)
607 int flags
= MAP_PRIVATE
| MAP_ANON
;
610 if (!info
->addr
|| is_zerofill( info
)) flags
|= MAP_FIXED
;
612 ret
= wld_mmap( info
->addr
, info
->size
, PROT_NONE
, flags
, -1, 0 );
613 if (ret
== info
->addr
) return 1;
614 if (ret
!= (void *)-1) wld_munmap( ret
, info
->size
);
616 /* don't warn for zero page */
617 if (info
->addr
>= (void *)0x1000)
618 wld_printf( "preloader: Warning: failed to reserve range %p-%p\n",
619 info
->addr
, (char *)info
->addr
+ info
->size
);
623 static inline void get_dyld_func( const char *name
, void **func
)
625 _dyld_func_lookup( name
, func
);
626 if (!*func
) fatal_error( "Failed to get function pointer for %s\n", name
);
629 #define LOAD_POSIX_DYLD_FUNC(f) get_dyld_func( "__dyld_" #f, (void **)&p##f )
630 #define LOAD_MACHO_DYLD_FUNC(f) get_dyld_func( "_" #f, (void **)&p##f )
632 static void fixup_stack( void *stack
)
635 char **argv
, **env_new
;
636 static char dummyvar
[] = "WINEPRELOADERDUMMYVAR=1";
639 argv
= (char **)pargc
+ 1;
641 /* decrement argc, and "remove" argv[0] */
643 memmove( &argv
[0], &argv
[1], (*pargc
+ 1) * sizeof(char *) );
645 env_new
= &argv
[*pargc
-1] + 2;
646 /* In the launched binary on some OSes, _NSGetEnviron() returns
647 * the original 'environ' pointer, so env_new[0] would be ignored.
648 * Put a dummy variable in env_new[0], so nothing is lost in this case.
650 env_new
[0] = dummyvar
;
653 static void set_program_vars( void *stack
, void *mod
)
656 const char **argv
, **env
;
657 int *wine_NXArgc
= pdlsym( mod
, "NXArgc" );
658 const char ***wine_NXArgv
= pdlsym( mod
, "NXArgv" );
659 const char ***wine_environ
= pdlsym( mod
, "environ" );
662 argv
= (const char **)pargc
+ 1;
663 env
= &argv
[*pargc
-1] + 2;
665 /* set vars in the loaded binary */
667 *wine_NXArgc
= *pargc
;
669 wld_printf( "preloader: Warning: failed to set NXArgc\n" );
674 wld_printf( "preloader: Warning: failed to set NXArgv\n" );
679 wld_printf( "preloader: Warning: failed to set environ\n" );
681 /* set vars in the __program_vars section */
687 void *wld_start( void *stack
, int *is_unix_thread
)
690 struct wine_preload_info builtin_dlls
= { (void *)0x7a000000, 0x02000000 };
692 struct wine_preload_info
**wine_main_preload_info
;
693 char **argv
, **p
, *reserve
= NULL
;
694 struct target_mach_header
*mh
;
700 argv
= (char **)pargc
+ 1;
701 if (*pargc
< 2) fatal_error( "Usage: %s wine_binary [args]\n", argv
[0] );
703 /* skip over the parameters */
704 p
= argv
+ *pargc
+ 1;
706 /* skip over the environment */
709 static const char res
[] = "WINEPRELOADRESERVE=";
710 if (!wld_strncmp( *p
, res
, sizeof(res
)-1 )) reserve
= *p
+ sizeof(res
) - 1;
714 LOAD_POSIX_DYLD_FUNC( dlopen
);
715 LOAD_POSIX_DYLD_FUNC( dlsym
);
716 LOAD_POSIX_DYLD_FUNC( dladdr
);
717 LOAD_MACHO_DYLD_FUNC( _dyld_get_image_slide
);
719 /* reserve memory that Wine needs */
720 if (reserve
) preload_reserve( reserve
);
721 for (i
= 0; preload_info
[i
].size
; i
++)
723 if (!map_region( &preload_info
[i
] ))
725 remove_preload_range( i
);
731 if (!map_region( &builtin_dlls
))
732 builtin_dlls
.size
= 0;
735 /* load the main binary */
736 if (!(mod
= pdlopen( argv
[1], RTLD_NOW
)))
737 fatal_error( "%s: could not load binary\n", argv
[1] );
740 if (builtin_dlls
.size
)
741 wld_munmap( builtin_dlls
.addr
, builtin_dlls
.size
);
744 /* store pointer to the preload info into the appropriate main binary variable */
745 wine_main_preload_info
= pdlsym( mod
, "wine_main_preload_info" );
746 if (wine_main_preload_info
) *wine_main_preload_info
= preload_info
;
747 else wld_printf( "wine_main_preload_info not found\n" );
749 if (!pdladdr( wine_main_preload_info
, &info
) || !(mh
= info
.dli_fbase
))
750 fatal_error( "%s: could not find mach header\n", argv
[1] );
751 if (!(entry
= get_entry_point( mh
, p_dyld_get_image_slide(mh
), is_unix_thread
)))
752 fatal_error( "%s: could not find entry point\n", argv
[1] );
754 /* decrement argc and "remove" argv[0] */
757 /* Set NXArgc, NXArgv, and environ in the new binary.
758 * On different configurations these were either NULL/0 or still had their
759 * values from this preloader's launch.
761 * In particular, environ was not being updated, resulting in environ[0] being lost.
762 * And for LC_UNIXTHREAD binaries on Monterey and later, environ was just NULL.
764 set_program_vars( stack
, mod
);
769 #endif /* __APPLE__ */