winecoreaudio: Implement per-channel volume control.
[wine.git] / loader / preloader_mac.c
blob8e249a7dc4d54c8157dd6acede9b347496487c74
1 /*
2 * Preloader for macOS
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
25 #ifdef __APPLE__
27 #include "config.h"
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <sys/mman.h>
37 #ifdef HAVE_SYS_SYSCALL_H
38 # include <sys/syscall.h>
39 #endif
40 #include <unistd.h>
41 #include <dlfcn.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>
46 #endif
48 #include "wine/asm.h"
49 #include "main.h"
51 #if defined(__x86_64__)
52 /* Rosetta on Apple Silicon allocates memory starting at 0x100000000 (the 4GB line)
53 * before the preloader runs, which prevents any nonrelocatable EXEs with that
54 * base address from running.
56 * This empty linker section forces Rosetta's allocations (currently ~132 MB)
57 * to start at 0x114000000, and they should end below 0x120000000.
59 __asm__(".zerofill WINE_4GB_RESERVE,WINE_4GB_RESERVE,___wine_4gb_reserve,0x14000000");
61 static const struct wine_preload_info zerofill_sections[] =
63 { (void *)0x000100000000, 0x14000000 }, /* WINE_4GB_RESERVE section */
64 { 0, 0 } /* end of list */
66 #else
67 static const struct wine_preload_info zerofill_sections[] =
69 { 0, 0 } /* end of list */
71 #endif
73 #ifndef LC_MAIN
74 #define LC_MAIN 0x80000028
75 struct entry_point_command
77 uint32_t cmd;
78 uint32_t cmdsize;
79 uint64_t entryoff;
80 uint64_t stacksize;
82 #endif
84 static struct wine_preload_info preload_info[] =
86 /* On macOS, we allocate the low 64k area in two steps because PAGEZERO
87 * might not always be available. */
88 #ifdef __i386__
89 { (void *)0x00000000, 0x00001000 }, /* first page */
90 { (void *)0x00001000, 0x0000f000 }, /* low 64k */
91 { (void *)0x00010000, 0x00100000 }, /* DOS area */
92 { (void *)0x00110000, 0x67ef0000 }, /* low memory area */
93 { (void *)0x7f000000, 0x03000000 }, /* top-down allocations + shared heap + virtual heap */
94 #else /* __i386__ */
95 { (void *)0x000000010000, 0x00100000 }, /* DOS area */
96 { (void *)0x000000110000, 0x67ef0000 }, /* low memory area */
97 { (void *)0x00007ff00000, 0x000f0000 }, /* shared user data */
98 { (void *)0x000100000000, 0x14000000 }, /* WINE_4GB_RESERVE section */
99 { (void *)0x7ff000000000, 0x01ff0000 }, /* top-down allocations + virtual heap */
100 #endif /* __i386__ */
101 { 0, 0 }, /* PE exe range set with WINEPRELOADRESERVE */
102 { 0, 0 } /* end of list */
106 * These functions are only called when file is compiled with -fstack-protector.
107 * They are normally provided by libc's startup files, but since we
108 * build the preloader with "-nostartfiles -nodefaultlibs", we have to
109 * provide our own versions, otherwise the linker fails.
111 void *__stack_chk_guard = 0;
112 void __stack_chk_fail_local(void) { return; }
113 void __stack_chk_fail(void) { return; }
116 * When 'start' is called, stack frame looks like:
119 * | STRING AREA |
120 * +-------------+
121 * | 0 |
122 * +-------------+
123 * | exec_path | extra "apple" parameters start after NULL terminating env array
124 * +-------------+
125 * | 0 |
126 * +-------------+
127 * | env[n] |
128 * +-------------+
131 * +-------------+
132 * | env[0] |
133 * +-------------+
134 * | 0 |
135 * +-------------+
136 * | arg[argc-1] |
137 * +-------------+
140 * +-------------+
141 * | arg[0] |
142 * +-------------+
143 * | argc | argc is always 4 bytes long, even in 64-bit architectures
144 * +-------------+ <- sp
146 * Where arg[i] and env[i] point into the STRING AREA
148 * See also:
149 * macOS C runtime 'start':
150 * <https://github.com/apple-oss-distributions/Csu/blob/Csu-88/start.s>
152 * macOS dyld '__dyld_start' (pre-dyld4):
153 * <https://github.com/apple-oss-distributions/dyld/blob/dyld-852.2/src/dyldStartup.s>
156 #ifdef __i386__
158 static const size_t page_mask = 0xfff;
159 #define target_mach_header mach_header
160 #define target_segment_command segment_command
161 #define TARGET_LC_SEGMENT LC_SEGMENT
162 #define target_thread_state_t i386_thread_state_t
163 #ifdef __DARWIN_UNIX03
164 #define target_thread_ip(x) (x)->__eip
165 #else
166 #define target_thread_ip(x) (x)->eip
167 #endif
169 #define SYSCALL_FUNC( name, nr ) \
170 __ASM_GLOBAL_FUNC( name, \
171 "\tmovl $" #nr ",%eax\n" \
172 "\tint $0x80\n" \
173 "\tjnb 1f\n" \
174 "\tmovl $-1,%eax\n" \
175 "1:\tret\n" )
177 #define SYSCALL_NOERR( name, nr ) \
178 __ASM_GLOBAL_FUNC( name, \
179 "\tmovl $" #nr ",%eax\n" \
180 "\tint $0x80\n" \
181 "\tret\n" )
183 __ASM_GLOBAL_FUNC( start,
184 __ASM_CFI("\t.cfi_undefined %eip\n")
185 /* The first 16 bytes are used as a function signature on i386 */
186 "\t.byte 0x6a,0x00\n" /* pushl $0: push a zero for debugger end of frames marker */
187 "\t.byte 0x89,0xe5\n" /* movl %esp,%ebp: pointer to base of kernel frame */
188 "\t.byte 0x83,0xe4,0xf0\n" /* andl $-16,%esp: force SSE alignment */
189 "\t.byte 0x83,0xec,0x10\n" /* subl $16,%esp: room for new argc, argv, & envp, SSE aligned */
190 "\t.byte 0x8b,0x5d,0x04\n" /* movl 4(%ebp),%ebx: pickup argc in %ebx */
191 "\t.byte 0x89,0x5c,0x24,0x00\n" /* movl %ebx,0(%esp): argc to reserved stack word */
193 /* call wld_start(stack, &is_unix_thread) */
194 "\tleal 4(%ebp),%eax\n"
195 "\tmovl %eax,0(%esp)\n" /* stack */
196 "\tleal 8(%esp),%eax\n"
197 "\tmovl %eax,4(%esp)\n" /* &is_unix_thread */
198 "\tmovl $0,(%eax)\n"
199 "\tcall _wld_start\n"
201 /* jmp based on is_unix_thread */
202 "\tcmpl $0,8(%esp)\n"
203 "\tjne 2f\n"
205 "\tmovl 4(%ebp),%edi\n" /* %edi = argc */
206 "\tleal 8(%ebp),%esi\n" /* %esi = argv */
207 "\tleal 4(%esi,%edi,4),%edx\n" /* %edx = env */
208 "\tmovl %edx,%ecx\n"
209 "1:\tmovl (%ecx),%ebx\n"
210 "\tadd $4,%ecx\n"
211 "\torl %ebx,%ebx\n" /* look for the NULL ending the env[] array */
212 "\tjnz 1b\n" /* %ecx = apple data */
214 /* LC_MAIN */
215 "\tmovl %edi,0(%esp)\n" /* argc */
216 "\tmovl %esi,4(%esp)\n" /* argv */
217 "\tmovl %edx,8(%esp)\n" /* env */
218 "\tmovl %ecx,12(%esp)\n" /* apple data */
219 "\tcall *%eax\n" /* call main(argc,argv,env,apple) */
220 "\tmovl %eax,(%esp)\n" /* pass result from main() to exit() */
221 "\tcall _wld_exit\n" /* need to use call to keep stack aligned */
222 "\thlt\n"
224 /* LC_UNIXTHREAD */
225 "\t2:movl %ebp,%esp\n" /* restore the unaligned stack pointer */
226 "\taddl $4,%esp\n" /* remove the debugger end frame marker */
227 "\tmovl $0,%ebp\n" /* restore ebp back to zero */
228 "\tjmpl *%eax\n" ) /* jump to the entry point */
230 #elif defined(__x86_64__)
232 static const size_t page_mask = 0xfff;
233 #define target_mach_header mach_header_64
234 #define target_segment_command segment_command_64
235 #define TARGET_LC_SEGMENT LC_SEGMENT_64
236 #define target_thread_state_t x86_thread_state64_t
237 #ifdef __DARWIN_UNIX03
238 #define target_thread_ip(x) (x)->__rip
239 #else
240 #define target_thread_ip(x) (x)->rip
241 #endif
243 #define SYSCALL_FUNC( name, nr ) \
244 __ASM_GLOBAL_FUNC( name, \
245 "\tmovq %rcx, %r10\n" \
246 "\tmovq $(" #nr "|0x2000000),%rax\n" \
247 "\tsyscall\n" \
248 "\tjnb 1f\n" \
249 "\tmovq $-1,%rax\n" \
250 "1:\tret\n" )
252 #define SYSCALL_NOERR( name, nr ) \
253 __ASM_GLOBAL_FUNC( name, \
254 "\tmovq %rcx, %r10\n" \
255 "\tmovq $(" #nr "|0x2000000),%rax\n" \
256 "\tsyscall\n" \
257 "\tret\n" )
259 __ASM_GLOBAL_FUNC( start,
260 __ASM_CFI("\t.cfi_undefined %rip\n")
261 "\tpushq $0\n" /* push a zero for debugger end of frames marker */
262 "\tmovq %rsp,%rbp\n" /* pointer to base of kernel frame */
263 "\tandq $-16,%rsp\n" /* force SSE alignment */
264 "\tsubq $16,%rsp\n" /* room for local variables */
266 /* call wld_start(stack, &is_unix_thread) */
267 "\tleaq 8(%rbp),%rdi\n" /* stack */
268 "\tmovq %rsp,%rsi\n" /* &is_unix_thread */
269 "\tmovq $0,(%rsi)\n"
270 "\tcall _wld_start\n"
272 /* jmp based on is_unix_thread */
273 "\tcmpl $0,0(%rsp)\n"
274 "\tjne 2f\n"
276 /* LC_MAIN */
277 "\tmovq 8(%rbp),%rdi\n" /* %rdi = argc */
278 "\tleaq 16(%rbp),%rsi\n" /* %rsi = argv */
279 "\tleaq 8(%rsi,%rdi,8),%rdx\n" /* %rdx = env */
280 "\tmovq %rdx,%rcx\n"
281 "1:\tmovq (%rcx),%r8\n"
282 "\taddq $8,%rcx\n"
283 "\torq %r8,%r8\n" /* look for the NULL ending the env[] array */
284 "\tjnz 1b\n" /* %rcx = apple data */
286 "\taddq $16,%rsp\n" /* remove local variables */
287 "\tcall *%rax\n" /* call main(argc,argv,env,apple) */
288 "\tmovq %rax,%rdi\n" /* pass result from main() to exit() */
289 "\tcall _wld_exit\n" /* need to use call to keep stack aligned */
290 "\thlt\n"
292 /* LC_UNIXTHREAD */
293 "\t2:movq %rbp,%rsp\n" /* restore the unaligned stack pointer */
294 "\taddq $8,%rsp\n" /* remove the debugger end frame marker */
295 "\tmovq $0,%rbp\n" /* restore ebp back to zero */
296 "\tjmpq *%rax\n" ) /* jump to the entry point */
298 #else
299 #error preloader not implemented for this CPU
300 #endif
302 void wld_exit( int code ) __attribute__((noreturn));
303 SYSCALL_NOERR( wld_exit, 1 /* SYS_exit */ );
305 ssize_t wld_write( int fd, const void *buffer, size_t len );
306 SYSCALL_FUNC( wld_write, 4 /* SYS_write */ );
308 void *wld_mmap( void *start, size_t len, int prot, int flags, int fd, off_t offset );
309 SYSCALL_FUNC( wld_mmap, 197 /* SYS_mmap */ );
311 void *wld_munmap( void *start, size_t len );
312 SYSCALL_FUNC( wld_munmap, 73 /* SYS_munmap */ );
314 static intptr_t (*p_dyld_get_image_slide)( const struct target_mach_header* mh );
316 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
317 MAKE_FUNCPTR(dlopen);
318 MAKE_FUNCPTR(dlsym);
319 MAKE_FUNCPTR(dladdr);
320 #undef MAKE_FUNCPTR
322 extern int _dyld_func_lookup( const char *dyld_func_name, void **address );
324 /* replacement for libc functions */
326 void * memmove( void *dst, const void *src, size_t len )
328 char *d = dst;
329 const char *s = src;
330 if (d < s)
331 while (len--)
332 *d++ = *s++;
333 else
335 const char *lasts = s + (len-1);
336 char *lastd = d + (len-1);
337 while (len--)
338 *lastd-- = *lasts--;
340 return dst;
343 static int wld_strncmp( const char *str1, const char *str2, size_t len )
345 if (len <= 0) return 0;
346 while ((--len > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; }
347 return *str1 - *str2;
351 * wld_printf - just the basics
353 * %x prints a hex number
354 * %s prints a string
355 * %p prints a pointer
357 static int wld_vsprintf(char *buffer, const char *fmt, va_list args )
359 static const char hex_chars[16] = "0123456789abcdef";
360 const char *p = fmt;
361 char *str = buffer;
362 int i;
364 while( *p )
366 if( *p == '%' )
368 p++;
369 if( *p == 'x' )
371 unsigned int x = va_arg( args, unsigned int );
372 for (i = 2*sizeof(x) - 1; i >= 0; i--)
373 *str++ = hex_chars[(x>>(i*4))&0xf];
375 else if (p[0] == 'l' && p[1] == 'x')
377 unsigned long x = va_arg( args, unsigned long );
378 for (i = 2*sizeof(x) - 1; i >= 0; i--)
379 *str++ = hex_chars[(x>>(i*4))&0xf];
380 p++;
382 else if( *p == 'p' )
384 unsigned long x = (unsigned long)va_arg( args, void * );
385 for (i = 2*sizeof(x) - 1; i >= 0; i--)
386 *str++ = hex_chars[(x>>(i*4))&0xf];
388 else if( *p == 's' )
390 char *s = va_arg( args, char * );
391 while(*s)
392 *str++ = *s++;
394 else if( *p == 0 )
395 break;
396 p++;
398 *str++ = *p++;
400 *str = 0;
401 return str - buffer;
404 static __attribute__((format(printf,1,2))) void wld_printf(const char *fmt, ... )
406 va_list args;
407 char buffer[256];
408 int len;
410 va_start( args, fmt );
411 len = wld_vsprintf(buffer, fmt, args );
412 va_end( args );
413 wld_write(2, buffer, len);
416 static __attribute__((noreturn,format(printf,1,2))) void fatal_error(const char *fmt, ... )
418 va_list args;
419 char buffer[256];
420 int len;
422 va_start( args, fmt );
423 len = wld_vsprintf(buffer, fmt, args );
424 va_end( args );
425 wld_write(2, buffer, len);
426 wld_exit(1);
429 static int preloader_overlaps_range( const void *start, const void *end )
431 intptr_t slide = p_dyld_get_image_slide(&_mh_execute_header);
432 struct load_command *cmd = (struct load_command*)(&_mh_execute_header + 1);
433 int i;
435 for (i = 0; i < _mh_execute_header.ncmds; ++i)
437 if (cmd->cmd == TARGET_LC_SEGMENT)
439 struct target_segment_command *seg = (struct target_segment_command*)cmd;
440 const void *seg_start = (const void*)(seg->vmaddr + slide);
441 const void *seg_end = (const char*)seg_start + seg->vmsize;
442 static const char reserved_segname[] = "WINE_4GB_RESERVE";
444 if (!wld_strncmp( seg->segname, reserved_segname, sizeof(reserved_segname)-1 ))
445 continue;
447 if (end > seg_start && start <= seg_end)
449 char segname[sizeof(seg->segname) + 1];
450 memcpy(segname, seg->segname, sizeof(seg->segname));
451 segname[sizeof(segname) - 1] = 0;
452 wld_printf( "WINEPRELOADRESERVE range %p-%p overlaps preloader %s segment %p-%p\n",
453 start, end, segname, seg_start, seg_end );
454 return 1;
457 cmd = (struct load_command*)((char*)cmd + cmd->cmdsize);
460 return 0;
464 * preload_reserve
466 * Reserve a range specified in string format
468 static void preload_reserve( const char *str )
470 const char *p;
471 unsigned long result = 0;
472 void *start = NULL, *end = NULL;
473 int i, first = 1;
475 for (p = str; *p; p++)
477 if (*p >= '0' && *p <= '9') result = result * 16 + *p - '0';
478 else if (*p >= 'a' && *p <= 'f') result = result * 16 + *p - 'a' + 10;
479 else if (*p >= 'A' && *p <= 'F') result = result * 16 + *p - 'A' + 10;
480 else if (*p == '-')
482 if (!first) goto error;
483 start = (void *)(result & ~page_mask);
484 result = 0;
485 first = 0;
487 else goto error;
489 if (!first) end = (void *)((result + page_mask) & ~page_mask);
490 else if (result) goto error; /* single value '0' is allowed */
492 /* sanity checks */
493 if (end <= start || preloader_overlaps_range(start, end))
494 start = end = NULL;
496 /* check for overlap with low memory areas */
497 for (i = 0; preload_info[i].size; i++)
499 if ((char *)preload_info[i].addr > (char *)0x00110000) break;
500 if ((char *)end <= (char *)preload_info[i].addr + preload_info[i].size)
502 start = end = NULL;
503 break;
505 if ((char *)start < (char *)preload_info[i].addr + preload_info[i].size)
506 start = (char *)preload_info[i].addr + preload_info[i].size;
509 while (preload_info[i].size) i++;
510 preload_info[i].addr = start;
511 preload_info[i].size = (char *)end - (char *)start;
512 return;
514 error:
515 fatal_error( "invalid WINEPRELOADRESERVE value '%s'\n", str );
518 /* remove a range from the preload list */
519 static void remove_preload_range( int i )
521 while (preload_info[i].size)
523 preload_info[i].addr = preload_info[i+1].addr;
524 preload_info[i].size = preload_info[i+1].size;
525 i++;
529 static void *get_entry_point( struct target_mach_header *mh, intptr_t slide, int *unix_thread )
531 struct entry_point_command *entry;
532 target_thread_state_t *state;
533 struct load_command *cmd;
534 int i;
536 /* try LC_MAIN first */
537 cmd = (struct load_command *)(mh + 1);
538 for (i = 0; i < mh->ncmds; i++)
540 if (cmd->cmd == LC_MAIN)
542 *unix_thread = FALSE;
543 entry = (struct entry_point_command *)cmd;
544 return (char *)mh + entry->entryoff;
546 cmd = (struct load_command *)((char *)cmd + cmd->cmdsize);
549 /* then try LC_UNIXTHREAD */
550 cmd = (struct load_command *)(mh + 1);
551 for (i = 0; i < mh->ncmds; i++)
553 if (cmd->cmd == LC_UNIXTHREAD)
555 *unix_thread = TRUE;
556 state = (target_thread_state_t *)((char *)cmd + 16);
557 return (void *)(target_thread_ip(state) + slide);
559 cmd = (struct load_command *)((char *)cmd + cmd->cmdsize);
562 return NULL;
565 static int is_zerofill( struct wine_preload_info *info )
567 int i;
569 for (i = 0; zerofill_sections[i].size; i++)
571 if ((zerofill_sections[i].addr == info->addr) &&
572 (zerofill_sections[i].size == info->size))
573 return 1;
575 return 0;
578 static int map_region( struct wine_preload_info *info )
580 int flags = MAP_PRIVATE | MAP_ANON;
581 void *ret;
583 if (!info->addr || is_zerofill( info )) flags |= MAP_FIXED;
585 ret = wld_mmap( info->addr, info->size, PROT_NONE, flags, -1, 0 );
586 if (ret == info->addr) return 1;
587 if (ret != (void *)-1) wld_munmap( ret, info->size );
589 /* don't warn for zero page */
590 if (info->addr >= (void *)0x1000)
591 wld_printf( "preloader: Warning: failed to reserve range %p-%p\n",
592 info->addr, (char *)info->addr + info->size );
593 return 0;
596 static inline void get_dyld_func( const char *name, void **func )
598 _dyld_func_lookup( name, func );
599 if (!*func) fatal_error( "Failed to get function pointer for %s\n", name );
602 #define LOAD_POSIX_DYLD_FUNC(f) get_dyld_func( "__dyld_" #f, (void **)&p##f )
603 #define LOAD_MACHO_DYLD_FUNC(f) get_dyld_func( "_" #f, (void **)&p##f )
605 static void fixup_stack( void *stack )
607 int *pargc;
608 char **argv, **env_new;
609 static char dummyvar[] = "WINEPRELOADERDUMMYVAR=1";
611 pargc = stack;
612 argv = (char **)pargc + 1;
614 /* decrement argc, and "remove" argv[0] */
615 *pargc = *pargc - 1;
616 memmove( &argv[0], &argv[1], (*pargc + 1) * sizeof(char *) );
618 env_new = &argv[*pargc-1] + 2;
619 /* In the launched binary on some OSes, _NSGetEnviron() returns
620 * the original 'environ' pointer, so env_new[0] would be ignored.
621 * Put a dummy variable in env_new[0], so nothing is lost in this case.
623 env_new[0] = dummyvar;
626 static void set_program_vars( void *stack, void *mod )
628 int *pargc;
629 char **argv, **env;
630 int *wine_NXArgc = pdlsym( mod, "NXArgc" );
631 char ***wine_NXArgv = pdlsym( mod, "NXArgv" );
632 char ***wine_environ = pdlsym( mod, "environ" );
634 pargc = stack;
635 argv = (char **)pargc + 1;
636 env = &argv[*pargc-1] + 2;
638 if (wine_NXArgc)
639 *wine_NXArgc = *pargc;
640 else
641 wld_printf( "preloader: Warning: failed to set NXArgc\n" );
643 if (wine_NXArgv)
644 *wine_NXArgv = argv;
645 else
646 wld_printf( "preloader: Warning: failed to set NXArgv\n" );
648 if (wine_environ)
649 *wine_environ = env;
650 else
651 wld_printf( "preloader: Warning: failed to set environ\n" );
654 void *wld_start( void *stack, int *is_unix_thread )
656 struct wine_preload_info builtin_dlls = { (void *)0x7a000000, 0x02000000 };
657 struct wine_preload_info **wine_main_preload_info;
658 char **argv, **p, *reserve = NULL;
659 struct target_mach_header *mh;
660 void *mod, *entry;
661 int *pargc, i;
662 Dl_info info;
664 pargc = stack;
665 argv = (char **)pargc + 1;
666 if (*pargc < 2) fatal_error( "Usage: %s wine_binary [args]\n", argv[0] );
668 /* skip over the parameters */
669 p = argv + *pargc + 1;
671 /* skip over the environment */
672 while (*p)
674 static const char res[] = "WINEPRELOADRESERVE=";
675 if (!wld_strncmp( *p, res, sizeof(res)-1 )) reserve = *p + sizeof(res) - 1;
676 p++;
679 LOAD_POSIX_DYLD_FUNC( dlopen );
680 LOAD_POSIX_DYLD_FUNC( dlsym );
681 LOAD_POSIX_DYLD_FUNC( dladdr );
682 LOAD_MACHO_DYLD_FUNC( _dyld_get_image_slide );
684 /* reserve memory that Wine needs */
685 if (reserve) preload_reserve( reserve );
686 for (i = 0; preload_info[i].size; i++)
688 if (!map_region( &preload_info[i] ))
690 remove_preload_range( i );
691 i--;
695 if (!map_region( &builtin_dlls ))
696 builtin_dlls.size = 0;
698 /* load the main binary */
699 if (!(mod = pdlopen( argv[1], RTLD_NOW )))
700 fatal_error( "%s: could not load binary\n", argv[1] );
702 if (builtin_dlls.size)
703 wld_munmap( builtin_dlls.addr, builtin_dlls.size );
705 /* store pointer to the preload info into the appropriate main binary variable */
706 wine_main_preload_info = pdlsym( mod, "wine_main_preload_info" );
707 if (wine_main_preload_info) *wine_main_preload_info = preload_info;
708 else wld_printf( "wine_main_preload_info not found\n" );
710 if (!pdladdr( wine_main_preload_info, &info ) || !(mh = info.dli_fbase))
711 fatal_error( "%s: could not find mach header\n", argv[1] );
712 if (!(entry = get_entry_point( mh, p_dyld_get_image_slide(mh), is_unix_thread )))
713 fatal_error( "%s: could not find entry point\n", argv[1] );
715 /* decrement argc and "remove" argv[0] */
716 fixup_stack(stack);
718 /* Set NXArgc, NXArgv, and environ in the new binary.
719 * On different configurations these were either NULL/0 or still had their
720 * values from this preloader's launch.
722 * In particular, environ was not being updated, resulting in environ[0] being lost.
723 * And for LC_UNIXTHREAD binaries on Monterey and later, environ was just NULL.
725 set_program_vars( stack, mod );
727 return entry;
730 #endif /* __APPLE__ */