Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/loader/preloader_mac.c
4389 views
1
/*
2
* Preloader for macOS
3
*
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
9
*
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.
14
*
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.
19
*
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
23
*/
24
25
#ifdef __APPLE__
26
27
#include "config.h"
28
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
47
48
#include "wine/asm.h"
49
#include "main.h"
50
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)
55
*/
56
__asm__(".zerofill WINE_RESERVE,WINE_RESERVE,___wine_reserve,0x1fffff000");
57
58
static const struct wine_preload_info zerofill_sections[] =
59
{
60
{ (void *)0x000000001000, 0x1fffff000 }, /* WINE_RESERVE section */
61
{ 0, 0 } /* end of list */
62
};
63
#else
64
static const struct wine_preload_info zerofill_sections[] =
65
{
66
{ 0, 0 } /* end of list */
67
};
68
#endif
69
70
#ifndef LC_MAIN
71
#define LC_MAIN 0x80000028
72
struct entry_point_command
73
{
74
uint32_t cmd;
75
uint32_t cmdsize;
76
uint64_t entryoff;
77
uint64_t stacksize;
78
};
79
#endif
80
81
static struct wine_preload_info preload_info[] =
82
{
83
/* On macOS, we allocate the low 64k area in two steps because PAGEZERO
84
* might not always be available. */
85
#ifdef __i386__
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 */
91
#else /* __i386__ */
92
{ (void *)0x000000001000, 0x1fffff000 }, /* WINE_RESERVE section */
93
{ (void *)0x7ff000000000, 0x01ff0000 }, /* top-down allocations + virtual heap */
94
#endif /* __i386__ */
95
{ 0, 0 }, /* PE exe range set with WINEPRELOADRESERVE */
96
{ 0, 0 } /* end of list */
97
};
98
99
/*
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.
104
*/
105
void *__stack_chk_guard = 0;
106
void __stack_chk_fail_local(void) { return; }
107
void __stack_chk_fail(void) { return; }
108
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
114
* to crash on init).
115
* - starting with macOS Sonoma, Wine crashes on launch if libSystem is not initialized.
116
*
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.
119
*
120
* See also:
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>
123
*/
124
int NXArgc = 0;
125
const char** NXArgv = NULL;
126
const char** environ = NULL;
127
const char* __progname = NULL;
128
129
extern void* __dso_handle;
130
struct ProgramVars
131
{
132
void* mh;
133
int* NXArgcPtr;
134
const char*** NXArgvPtr;
135
const char*** environPtr;
136
const char** __prognamePtr;
137
};
138
__attribute__((used)) static struct ProgramVars pvars
139
__attribute__ ((section ("__DATA,__program_vars"))) = { &__dso_handle, &NXArgc, &NXArgv, &environ, &__progname };
140
141
142
/*
143
* When 'start' is called, stack frame looks like:
144
*
145
* :
146
* | STRING AREA |
147
* +-------------+
148
* | 0 |
149
* +-------------+
150
* | exec_path | extra "apple" parameters start after NULL terminating env array
151
* +-------------+
152
* | 0 |
153
* +-------------+
154
* | env[n] |
155
* +-------------+
156
* :
157
* :
158
* +-------------+
159
* | env[0] |
160
* +-------------+
161
* | 0 |
162
* +-------------+
163
* | arg[argc-1] |
164
* +-------------+
165
* :
166
* :
167
* +-------------+
168
* | arg[0] |
169
* +-------------+
170
* | argc | argc is always 4 bytes long, even in 64-bit architectures
171
* +-------------+ <- sp
172
*
173
* Where arg[i] and env[i] point into the STRING AREA
174
*
175
* See also:
176
* macOS C runtime 'start':
177
* <https://github.com/apple-oss-distributions/Csu/blob/Csu-88/start.s>
178
*
179
* macOS dyld '__dyld_start' (pre-dyld4):
180
* <https://github.com/apple-oss-distributions/dyld/blob/dyld-852.2/src/dyldStartup.s>
181
*/
182
183
#ifdef __i386__
184
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
192
#else
193
#define target_thread_ip(x) (x)->eip
194
#endif
195
196
#define SYSCALL_FUNC( name, nr ) \
197
__ASM_GLOBAL_FUNC( name, \
198
"\tmovl $" #nr ",%eax\n" \
199
"\tint $0x80\n" \
200
"\tjnb 1f\n" \
201
"\tmovl $-1,%eax\n" \
202
"1:\tret\n" )
203
204
#define SYSCALL_NOERR( name, nr ) \
205
__ASM_GLOBAL_FUNC( name, \
206
"\tmovl $" #nr ",%eax\n" \
207
"\tint $0x80\n" \
208
"\tret\n" )
209
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 */
219
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 */
225
"\tmovl $0,(%eax)\n"
226
"\tcall _wld_start\n"
227
228
/* jmp based on is_unix_thread */
229
"\tcmpl $0,8(%esp)\n"
230
"\tjne 2f\n"
231
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 */
235
"\tmovl %edx,%ecx\n"
236
"1:\tmovl (%ecx),%ebx\n"
237
"\tadd $4,%ecx\n"
238
"\torl %ebx,%ebx\n" /* look for the NULL ending the env[] array */
239
"\tjnz 1b\n" /* %ecx = apple data */
240
241
/* LC_MAIN */
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 */
249
"\thlt\n"
250
251
/* LC_UNIXTHREAD */
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 */
256
257
#elif defined(__x86_64__)
258
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
266
#else
267
#define target_thread_ip(x) (x)->rip
268
#endif
269
270
#define SYSCALL_FUNC( name, nr ) \
271
__ASM_GLOBAL_FUNC( name, \
272
"\tmovq %rcx, %r10\n" \
273
"\tmovq $(" #nr "|0x2000000),%rax\n" \
274
"\tsyscall\n" \
275
"\tjnb 1f\n" \
276
"\tmovq $-1,%rax\n" \
277
"1:\tret\n" )
278
279
#define SYSCALL_NOERR( name, nr ) \
280
__ASM_GLOBAL_FUNC( name, \
281
"\tmovq %rcx, %r10\n" \
282
"\tmovq $(" #nr "|0x2000000),%rax\n" \
283
"\tsyscall\n" \
284
"\tret\n" )
285
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 */
292
293
/* call wld_start(stack, &is_unix_thread) */
294
"\tleaq 8(%rbp),%rdi\n" /* stack */
295
"\tmovq %rsp,%rsi\n" /* &is_unix_thread */
296
"\tmovq $0,(%rsi)\n"
297
"\tcall _wld_start\n"
298
299
/* jmp based on is_unix_thread */
300
"\tcmpl $0,0(%rsp)\n"
301
"\tjne 2f\n"
302
303
/* LC_MAIN */
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 */
307
"\tmovq %rdx,%rcx\n"
308
"1:\tmovq (%rcx),%r8\n"
309
"\taddq $8,%rcx\n"
310
"\torq %r8,%r8\n" /* look for the NULL ending the env[] array */
311
"\tjnz 1b\n" /* %rcx = apple data */
312
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 */
317
"\thlt\n"
318
319
/* LC_UNIXTHREAD */
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 */
324
325
#else
326
#error preloader not implemented for this CPU
327
#endif
328
329
void wld_exit( int code ) __attribute__((noreturn));
330
SYSCALL_NOERR( wld_exit, 1 /* SYS_exit */ );
331
332
ssize_t wld_write( int fd, const void *buffer, size_t len );
333
SYSCALL_FUNC( wld_write, 4 /* SYS_write */ );
334
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 */ );
337
338
void *wld_munmap( void *start, size_t len );
339
SYSCALL_FUNC( wld_munmap, 73 /* SYS_munmap */ );
340
341
static intptr_t (*p_dyld_get_image_slide)( const struct target_mach_header* mh );
342
343
#define MAKE_FUNCPTR(f) static typeof(f) * p##f
344
MAKE_FUNCPTR(dlopen);
345
MAKE_FUNCPTR(dlsym);
346
MAKE_FUNCPTR(dladdr);
347
#undef MAKE_FUNCPTR
348
349
extern int _dyld_func_lookup( const char *dyld_func_name, void **address );
350
351
/* replacement for libc functions */
352
353
void * memmove( void *dst, const void *src, size_t len )
354
{
355
char *d = dst;
356
const char *s = src;
357
if (d < s)
358
while (len--)
359
*d++ = *s++;
360
else
361
{
362
const char *lasts = s + (len-1);
363
char *lastd = d + (len-1);
364
while (len--)
365
*lastd-- = *lasts--;
366
}
367
return dst;
368
}
369
370
static int wld_strncmp( const char *str1, const char *str2, size_t len )
371
{
372
if (len <= 0) return 0;
373
while ((--len > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; }
374
return *str1 - *str2;
375
}
376
377
/*
378
* wld_printf - just the basics
379
*
380
* %x prints a hex number
381
* %s prints a string
382
* %p prints a pointer
383
*/
384
static int wld_vsprintf(char *buffer, const char *fmt, va_list args )
385
{
386
static const char hex_chars[16] = "0123456789abcdef";
387
const char *p = fmt;
388
char *str = buffer;
389
int i;
390
391
while( *p )
392
{
393
if( *p == '%' )
394
{
395
p++;
396
if( *p == 'x' )
397
{
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];
401
}
402
else if (p[0] == 'l' && p[1] == 'x')
403
{
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];
407
p++;
408
}
409
else if( *p == 'p' )
410
{
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];
414
}
415
else if( *p == 's' )
416
{
417
char *s = va_arg( args, char * );
418
while(*s)
419
*str++ = *s++;
420
}
421
else if( *p == 0 )
422
break;
423
p++;
424
}
425
*str++ = *p++;
426
}
427
*str = 0;
428
return str - buffer;
429
}
430
431
static __attribute__((format(printf,1,2))) void wld_printf(const char *fmt, ... )
432
{
433
va_list args;
434
char buffer[256];
435
int len;
436
437
va_start( args, fmt );
438
len = wld_vsprintf(buffer, fmt, args );
439
va_end( args );
440
wld_write(2, buffer, len);
441
}
442
443
static __attribute__((noreturn,format(printf,1,2))) void fatal_error(const char *fmt, ... )
444
{
445
va_list args;
446
char buffer[256];
447
int len;
448
449
va_start( args, fmt );
450
len = wld_vsprintf(buffer, fmt, args );
451
va_end( args );
452
wld_write(2, buffer, len);
453
wld_exit(1);
454
}
455
456
static int preloader_overlaps_range( const void *start, const void *end )
457
{
458
intptr_t slide = p_dyld_get_image_slide(&_mh_execute_header);
459
struct load_command *cmd = (struct load_command*)(&_mh_execute_header + 1);
460
int i;
461
462
for (i = 0; i < _mh_execute_header.ncmds; ++i)
463
{
464
if (cmd->cmd == TARGET_LC_SEGMENT)
465
{
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";
470
471
if (!wld_strncmp( seg->segname, reserved_segname, sizeof(reserved_segname)-1 ))
472
continue;
473
474
if (end > seg_start && start <= seg_end)
475
{
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 );
481
return 1;
482
}
483
}
484
cmd = (struct load_command*)((char*)cmd + cmd->cmdsize);
485
}
486
487
return 0;
488
}
489
490
/*
491
* preload_reserve
492
*
493
* Reserve a range specified in string format
494
*/
495
static void preload_reserve( const char *str )
496
{
497
const char *p;
498
unsigned long result = 0;
499
void *start = NULL, *end = NULL;
500
int i, first = 1;
501
502
for (p = str; *p; p++)
503
{
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;
507
else if (*p == '-')
508
{
509
if (!first) goto error;
510
start = (void *)(result & ~page_mask);
511
result = 0;
512
first = 0;
513
}
514
else goto error;
515
}
516
if (!first) end = (void *)((result + page_mask) & ~page_mask);
517
else if (result) goto error; /* single value '0' is allowed */
518
519
/* sanity checks */
520
if (end <= start || preloader_overlaps_range(start, end))
521
start = end = NULL;
522
523
/* check for overlap with low memory areas */
524
for (i = 0; preload_info[i].size; i++)
525
{
526
if ((char *)preload_info[i].addr > (char *)0x00110000) break;
527
if ((char *)end <= (char *)preload_info[i].addr + preload_info[i].size)
528
{
529
start = end = NULL;
530
break;
531
}
532
if ((char *)start < (char *)preload_info[i].addr + preload_info[i].size)
533
start = (char *)preload_info[i].addr + preload_info[i].size;
534
}
535
536
while (preload_info[i].size) i++;
537
preload_info[i].addr = start;
538
preload_info[i].size = (char *)end - (char *)start;
539
return;
540
541
error:
542
fatal_error( "invalid WINEPRELOADRESERVE value '%s'\n", str );
543
}
544
545
/* remove a range from the preload list */
546
static void remove_preload_range( int i )
547
{
548
while (preload_info[i].size)
549
{
550
preload_info[i].addr = preload_info[i+1].addr;
551
preload_info[i].size = preload_info[i+1].size;
552
i++;
553
}
554
}
555
556
static void *get_entry_point( struct target_mach_header *mh, intptr_t slide, int *unix_thread )
557
{
558
struct entry_point_command *entry;
559
target_thread_state_t *state;
560
struct load_command *cmd;
561
int i;
562
563
/* try LC_MAIN first */
564
cmd = (struct load_command *)(mh + 1);
565
for (i = 0; i < mh->ncmds; i++)
566
{
567
if (cmd->cmd == LC_MAIN)
568
{
569
*unix_thread = FALSE;
570
entry = (struct entry_point_command *)cmd;
571
return (char *)mh + entry->entryoff;
572
}
573
cmd = (struct load_command *)((char *)cmd + cmd->cmdsize);
574
}
575
576
/* then try LC_UNIXTHREAD */
577
cmd = (struct load_command *)(mh + 1);
578
for (i = 0; i < mh->ncmds; i++)
579
{
580
if (cmd->cmd == LC_UNIXTHREAD)
581
{
582
*unix_thread = TRUE;
583
state = (target_thread_state_t *)((char *)cmd + 16);
584
return (void *)(target_thread_ip(state) + slide);
585
}
586
cmd = (struct load_command *)((char *)cmd + cmd->cmdsize);
587
}
588
589
return NULL;
590
};
591
592
static int is_zerofill( struct wine_preload_info *info )
593
{
594
int i;
595
596
for (i = 0; zerofill_sections[i].size; i++)
597
{
598
if ((zerofill_sections[i].addr == info->addr) &&
599
(zerofill_sections[i].size == info->size))
600
return 1;
601
}
602
return 0;
603
}
604
605
static int map_region( struct wine_preload_info *info )
606
{
607
int flags = MAP_PRIVATE | MAP_ANON;
608
void *ret;
609
610
if (!info->addr || is_zerofill( info )) flags |= MAP_FIXED;
611
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 );
615
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 );
620
return 0;
621
}
622
623
static inline void get_dyld_func( const char *name, void **func )
624
{
625
_dyld_func_lookup( name, func );
626
if (!*func) fatal_error( "Failed to get function pointer for %s\n", name );
627
}
628
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 )
631
632
static void fixup_stack( void *stack )
633
{
634
int *pargc;
635
char **argv, **env_new;
636
static char dummyvar[] = "WINEPRELOADERDUMMYVAR=1";
637
638
pargc = stack;
639
argv = (char **)pargc + 1;
640
641
/* decrement argc, and "remove" argv[0] */
642
*pargc = *pargc - 1;
643
memmove( &argv[0], &argv[1], (*pargc + 1) * sizeof(char *) );
644
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.
649
*/
650
env_new[0] = dummyvar;
651
}
652
653
static void set_program_vars( void *stack, void *mod )
654
{
655
int *pargc;
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" );
660
661
pargc = stack;
662
argv = (const char **)pargc + 1;
663
env = &argv[*pargc-1] + 2;
664
665
/* set vars in the loaded binary */
666
if (wine_NXArgc)
667
*wine_NXArgc = *pargc;
668
else
669
wld_printf( "preloader: Warning: failed to set NXArgc\n" );
670
671
if (wine_NXArgv)
672
*wine_NXArgv = argv;
673
else
674
wld_printf( "preloader: Warning: failed to set NXArgv\n" );
675
676
if (wine_environ)
677
*wine_environ = env;
678
else
679
wld_printf( "preloader: Warning: failed to set environ\n" );
680
681
/* set vars in the __program_vars section */
682
NXArgc = *pargc;
683
NXArgv = argv;
684
environ = env;
685
}
686
687
void *wld_start( void *stack, int *is_unix_thread )
688
{
689
#ifdef __i386__
690
struct wine_preload_info builtin_dlls = { (void *)0x7a000000, 0x02000000 };
691
#endif
692
struct wine_preload_info **wine_main_preload_info;
693
char **argv, **p, *reserve = NULL;
694
struct target_mach_header *mh;
695
void *mod, *entry;
696
int *pargc, i;
697
Dl_info info;
698
699
pargc = stack;
700
argv = (char **)pargc + 1;
701
if (*pargc < 2) fatal_error( "Usage: %s wine_binary [args]\n", argv[0] );
702
703
/* skip over the parameters */
704
p = argv + *pargc + 1;
705
706
/* skip over the environment */
707
while (*p)
708
{
709
static const char res[] = "WINEPRELOADRESERVE=";
710
if (!wld_strncmp( *p, res, sizeof(res)-1 )) reserve = *p + sizeof(res) - 1;
711
p++;
712
}
713
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 );
718
719
/* reserve memory that Wine needs */
720
if (reserve) preload_reserve( reserve );
721
for (i = 0; preload_info[i].size; i++)
722
{
723
if (!map_region( &preload_info[i] ))
724
{
725
remove_preload_range( i );
726
i--;
727
}
728
}
729
730
#ifdef __i386__
731
if (!map_region( &builtin_dlls ))
732
builtin_dlls.size = 0;
733
#endif
734
735
/* load the main binary */
736
if (!(mod = pdlopen( argv[1], RTLD_NOW )))
737
fatal_error( "%s: could not load binary\n", argv[1] );
738
739
#ifdef __i386__
740
if (builtin_dlls.size)
741
wld_munmap( builtin_dlls.addr, builtin_dlls.size );
742
#endif
743
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" );
748
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] );
753
754
/* decrement argc and "remove" argv[0] */
755
fixup_stack(stack);
756
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.
760
*
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.
763
*/
764
set_program_vars( stack, mod );
765
766
return entry;
767
}
768
769
#endif /* __APPLE__ */
770
771