Panda3D
deploy-stub.c
1/* Python interpreter main program for frozen scripts */
2
3#include "Python.h"
4#ifdef _WIN32
5# include "malloc.h"
6# include <Shlobj.h>
7#else
8# include <sys/mman.h>
9# include <pwd.h>
10#endif
11
12#ifdef __FreeBSD__
13# include <sys/sysctl.h>
14#endif
15
16#ifdef __APPLE__
17# include <mach-o/dyld.h>
18# include <libgen.h>
19#endif
20
21#include <stdio.h>
22#include <stdint.h>
23#include <fcntl.h>
24
25#if PY_MAJOR_VERSION >= 3
26# include <locale.h>
27
28# if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 5
29# define Py_DecodeLocale _Py_char2wchar
30# endif
31
32# include "structmember.h"
33#endif
34
35/* Leave room for future expansion. We only read pointer 0, but there are
36 other pointers that are being read by configPageManager.cxx. */
37#define MAX_NUM_POINTERS 24
38
39/* Stored in the flags field of the blobinfo structure below. */
40enum Flags {
41 F_log_append = 1,
42 F_log_filename_strftime = 2,
43};
44
45/* Define an exposed symbol where we store the offset to the module data. */
46#ifdef _MSC_VER
47__declspec(dllexport)
48#else
49__attribute__((__visibility__("default"), used))
50#endif
51volatile struct {
52 uint64_t blob_offset;
53 uint64_t blob_size;
54 uint16_t version;
55 uint16_t num_pointers;
56 uint16_t codepage;
57 uint16_t flags;
58 uint64_t reserved;
59 void *pointers[MAX_NUM_POINTERS];
60
61 // The reason we initialize it to -1 is because otherwise, smart linkers may
62 // end up putting it in the .bss section for zero-initialized data.
63} blobinfo = {(uint64_t)-1};
64
65#ifdef MS_WINDOWS
66# define WIN32_LEAN_AND_MEAN
67# include <windows.h>
68
69extern void PyWinFreeze_ExeInit(void);
70extern void PyWinFreeze_ExeTerm(void);
71
72static struct _inittab extensions[] = {
73 {0, 0},
74};
75
76#if PY_MAJOR_VERSION >= 3
77# define WIN_UNICODE
78#endif
79#endif
80
81#ifdef _WIN32
82static wchar_t *log_pathw = NULL;
83#endif
84
85#if defined(_WIN32) && PY_VERSION_HEX < 0x03060000
86static int supports_code_page(UINT cp) {
87 if (cp == 0) {
88 cp = GetACP();
89 }
90
91 /* Shortcut, because we know that these encodings are bundled by default--
92 * see FreezeTool.py and Python's encodings/aliases.py */
93 if (cp != 0 && cp != 1252 && cp != 367 && cp != 437 && cp != 850 && cp != 819) {
94 const struct _frozen *moddef;
95 char codec[100];
96
97 /* Check if the codec was frozen into the program. We can't check this
98 * using _PyCodec_Lookup, since Python hasn't been initialized yet. */
99 PyOS_snprintf(codec, sizeof(codec), "encodings.cp%u", (unsigned int)cp);
100
101 moddef = PyImport_FrozenModules;
102 while (moddef->name) {
103 if (strcmp(moddef->name, codec) == 0) {
104 return 1;
105 }
106 ++moddef;
107 }
108 return 0;
109 }
110
111 return 1;
112}
113#endif
114
115/**
116 * Sets the main_dir field of the blobinfo structure, but only if it wasn't
117 * already set.
118 */
119static void set_main_dir(char *main_dir) {
120 if (blobinfo.num_pointers >= 10) {
121 if (blobinfo.num_pointers == 10) {
122 ++blobinfo.num_pointers;
123 blobinfo.pointers[10] = NULL;
124 }
125 if (blobinfo.pointers[10] == NULL) {
126 blobinfo.pointers[10] = main_dir;
127 }
128 }
129}
130
131/**
132 * Creates the parent directories of the given path. Returns 1 on success.
133 */
134#ifdef _WIN32
135static int mkdir_parent(const wchar_t *path) {
136 // Copy the path to a temporary buffer.
137 wchar_t buffer[4096];
138 size_t buflen = wcslen(path);
139 if (buflen + 1 >= _countof(buffer)) {
140 return 0;
141 }
142 wcscpy_s(buffer, _countof(buffer), path);
143
144 // Seek back to find the last path separator.
145 while (buflen-- > 0) {
146 if (buffer[buflen] == '/' || buffer[buflen] == '\\') {
147 buffer[buflen] = 0;
148 break;
149 }
150 }
151 if (buflen == (size_t)-1 || buflen == 0) {
152 // There was no path separator, or this was the root directory.
153 return 0;
154 }
155
156 if (CreateDirectoryW(buffer, NULL) != 0) {
157 // Success!
158 return 1;
159 }
160
161 // Failed.
162 DWORD last_error = GetLastError();
163 if (last_error == ERROR_ALREADY_EXISTS) {
164 // Not really an error: the directory is already there.
165 return 1;
166 }
167
168 if (last_error == ERROR_PATH_NOT_FOUND) {
169 // We need to make the parent directory first.
170 if (mkdir_parent(buffer)) {
171 // Parent successfully created. Try again to make the child.
172 if (CreateDirectoryW(buffer, NULL) != 0) {
173 // Got it!
174 return 1;
175 }
176 }
177 }
178 return 0;
179}
180#else
181static int mkdir_parent(const char *path) {
182 // Copy the path to a temporary buffer.
183 char buffer[4096];
184 size_t buflen = strlen(path);
185 if (buflen + 1 >= sizeof(buffer)) {
186 return 0;
187 }
188 strcpy(buffer, path);
189
190 // Seek back to find the last path separator.
191 while (buflen-- > 0) {
192 if (buffer[buflen] == '/') {
193 buffer[buflen] = 0;
194 break;
195 }
196 }
197 if (buflen == (size_t)-1 || buflen == 0) {
198 // There was no path separator, or this was the root directory.
199 return 0;
200 }
201 if (mkdir(buffer, 0755) == 0) {
202 // Success!
203 return 1;
204 }
205
206 // Failed.
207 if (errno == EEXIST) {
208 // Not really an error: the directory is already there.
209 return 1;
210 }
211
212 if (errno == ENOENT || errno == EACCES) {
213 // We need to make the parent directory first.
214 if (mkdir_parent(buffer)) {
215 // Parent successfully created. Try again to make the child.
216 if (mkdir(buffer, 0755) == 0) {
217 // Got it!
218 return 1;
219 }
220 }
221 }
222 return 0;
223}
224#endif
225
226/**
227 * Redirects the output streams to point to the log file with the given path.
228 *
229 * @param path specifies the location of log file, may start with ~
230 * @param append should be nonzero if it should not truncate the log file.
231 */
232static int setup_logging(const char *path, int append) {
233#ifdef _WIN32
234 // Does it start with a tilde? Perform tilde expansion if so.
235 wchar_t *pathw = (wchar_t *)malloc(sizeof(wchar_t) * MAX_PATH);
236 pathw[0] = 0;
237 size_t offset = 0;
238 if (path[0] == '~' && (path[1] == 0 || path[1] == '/' || path[1] == '\\')) {
239 // Strip off the tilde.
240 ++path;
241
242 // Get the home directory path for the current user.
243 if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, pathw))) {
244 free(pathw);
245 return 0;
246 }
247 offset = wcslen(pathw);
248 }
249
250 // We need to convert the rest of the path from UTF-8 to UTF-16.
251 if (MultiByteToWideChar(CP_UTF8, 0, path, -1, pathw + offset,
252 (int)(MAX_PATH - offset)) == 0) {
253 free(pathw);
254 return 0;
255 }
256
257 DWORD access = append ? FILE_APPEND_DATA : (GENERIC_READ | GENERIC_WRITE);
258 int creation = append ? OPEN_ALWAYS : CREATE_ALWAYS;
259 HANDLE handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
260 NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
261
262 if (handle == INVALID_HANDLE_VALUE) {
263 // Make the parent directories first.
264 mkdir_parent(pathw);
265 handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
266 NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
267 }
268
269 if (handle == INVALID_HANDLE_VALUE) {
270 free(pathw);
271 return 0;
272 }
273
274 log_pathw = pathw;
275
276 if (append) {
277 SetFilePointer(handle, 0, NULL, FILE_END);
278 }
279
280 SetStdHandle(STD_OUTPUT_HANDLE, handle);
281 SetStdHandle(STD_ERROR_HANDLE, handle);
282
283 // If we are running under the UCRT in a GUI application, we can't be sure
284 // that we have valid fds for stdout and stderr, so we have to set them up.
285 // One way to do this is to reopen them to something silly (like NUL).
286 if (_fileno(stdout) < 0) {
287 _close(1);
288 _wfreopen(L"\\\\.\\NUL", L"w", stdout);
289 }
290
291 if (_fileno(stderr) < 0) {
292 _close(2);
293 _wfreopen(L"\\\\.\\NUL", L"w", stderr);
294 }
295
296 // Now replace the stdout and stderr file descriptors with one pointing to
297 // our desired handle.
298 int fd = _open_osfhandle((intptr_t)handle, _O_WRONLY | _O_TEXT | _O_APPEND);
299 _dup2(fd, _fileno(stdout));
300 _dup2(fd, _fileno(stderr));
301 _close(fd);
302
303 return 1;
304#else
305 // Does it start with a tilde? Perform tilde expansion if so.
306 char buffer[PATH_MAX * 2];
307 size_t offset = 0;
308 if (path[0] == '~' && (path[1] == 0 || path[1] == '/')) {
309 // Strip off the tilde.
310 ++path;
311
312 // Get the home directory path for the current user.
313 const char *home_dir = getenv("HOME");
314 if (home_dir == NULL) {
315 home_dir = getpwuid(getuid())->pw_dir;
316 }
317 offset = strlen(home_dir);
318 assert(offset < sizeof(buffer));
319 strncpy(buffer, home_dir, sizeof(buffer));
320 }
321
322 // Copy over the rest of the path.
323 strcpy(buffer + offset, path);
324
325 mode_t mode = O_CREAT | O_WRONLY | (append ? O_APPEND : O_TRUNC);
326 int fd = open(buffer, mode, 0644);
327 if (fd == -1) {
328 // Make the parent directories first.
329 mkdir_parent(buffer);
330 fd = open(buffer, mode, 0644);
331 }
332
333 if (fd == -1) {
334 perror(buffer);
335 return 0;
336 }
337
338 fflush(stdout);
339 fflush(stderr);
340
341 dup2(fd, 1);
342 dup2(fd, 2);
343
344 close(fd);
345 return 1;
346#endif
347}
348
349/**
350 * Sets the line_buffering property on a TextIOWrapper object.
351 */
352#if PY_MAJOR_VERSION >= 3
353static int enable_line_buffering(PyObject *file) {
354#if PY_VERSION_HEX >= 0x03070000
355 /* Python 3.7 has a useful reconfigure() method. */
356 PyObject *kwargs = _PyDict_NewPresized(1);
357 PyDict_SetItemString(kwargs, "line_buffering", Py_True);
358 PyObject *args = PyTuple_New(0);
359
360 PyObject *method = PyObject_GetAttrString(file, "reconfigure");
361 if (method != NULL) {
362 PyObject *result = PyObject_Call(method, args, kwargs);
363 Py_DECREF(method);
364 Py_DECREF(kwargs);
365 Py_DECREF(args);
366 if (result != NULL) {
367 Py_DECREF(result);
368 } else {
369 PyErr_Clear();
370 return 0;
371 }
372 } else {
373 Py_DECREF(kwargs);
374 Py_DECREF(args);
375 PyErr_Clear();
376 return 0;
377 }
378#else
379 /* Older versions just don't expose a way to reconfigure(), but it's still
380 safe to override the property; we just have to use a hack to do it,
381 because it's officially marked "readonly". */
382
383 PyTypeObject *type = Py_TYPE(file);
384 PyMemberDef *member = type->tp_members;
385
386 while (member != NULL && member->name != NULL) {
387 if (strcmp(member->name, "line_buffering") == 0) {
388 *((char *)file + member->offset) = 1;
389 return 1;
390 }
391 ++member;
392 }
393 fflush(stdout);
394#endif
395 return 1;
396}
397#endif
398
399/* Main program */
400
401#ifdef WIN_UNICODE
402int Py_FrozenMain(int argc, wchar_t **argv)
403#else
404int Py_FrozenMain(int argc, char **argv)
405#endif
406{
407 char *p;
408 int n, sts = 1;
409 int unbuffered = 0;
410#ifndef NDEBUG
411 int inspect = 0;
412#endif
413
414#if PY_MAJOR_VERSION >= 3 && !defined(WIN_UNICODE)
415 int i;
416 char *oldloc;
417 wchar_t **argv_copy = NULL;
418 /* We need a second copies, as Python might modify the first one. */
419 wchar_t **argv_copy2 = NULL;
420
421 if (argc > 0) {
422 argv_copy = (wchar_t **)alloca(sizeof(wchar_t *) * argc);
423 argv_copy2 = (wchar_t **)alloca(sizeof(wchar_t *) * argc);
424 }
425#endif
426
427#if defined(MS_WINDOWS) && PY_VERSION_HEX >= 0x03040000 && PY_VERSION_HEX < 0x03060000
428 if (!supports_code_page(GetConsoleOutputCP()) ||
429 !supports_code_page(GetConsoleCP())) {
430 /* Revert to the active codepage, and tell Python to use the 'mbcs'
431 * encoding (which always uses the active codepage). In 99% of cases,
432 * this will be the same thing anyway. */
433 UINT acp = GetACP();
434 SetConsoleCP(acp);
435 SetConsoleOutputCP(acp);
436 Py_SetStandardStreamEncoding("mbcs", NULL);
437 }
438#endif
439
440 Py_FrozenFlag = 1; /* Suppress errors from getpath.c */
441 Py_NoSiteFlag = 0;
442 Py_NoUserSiteDirectory = 1;
443
444#if PY_VERSION_HEX >= 0x03020000
445 Py_OptimizeFlag = 2;
446#endif
447
448#ifndef NDEBUG
449 if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')
450 inspect = 1;
451#endif
452 if ((p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0')
453 unbuffered = 1;
454
455 if (unbuffered) {
456 setbuf(stdin, (char *)NULL);
457 setbuf(stdout, (char *)NULL);
458 setbuf(stderr, (char *)NULL);
459 }
460
461#if PY_MAJOR_VERSION >= 3 && !defined(WIN_UNICODE)
462 oldloc = setlocale(LC_ALL, NULL);
463 setlocale(LC_ALL, "");
464 for (i = 0; i < argc; i++) {
465 argv_copy[i] = Py_DecodeLocale(argv[i], NULL);
466 argv_copy2[i] = argv_copy[i];
467 if (!argv_copy[i]) {
468 fprintf(stderr, "Unable to decode the command line argument #%i\n",
469 i + 1);
470 argc = i;
471 goto error;
472 }
473 }
474 setlocale(LC_ALL, oldloc);
475#endif
476
477#ifdef MS_WINDOWS
478 PyImport_ExtendInittab(extensions);
479#endif /* MS_WINDOWS */
480
481 if (argc >= 1) {
482#if PY_MAJOR_VERSION >= 3 && !defined(WIN_UNICODE)
483 Py_SetProgramName(argv_copy[0]);
484#else
485 Py_SetProgramName(argv[0]);
486#endif
487 }
488
489 Py_Initialize();
490#ifdef MS_WINDOWS
491 PyWinFreeze_ExeInit();
492#endif
493
494#if defined(MS_WINDOWS) && PY_VERSION_HEX < 0x03040000
495 /* We can't rely on our overriding of the standard I/O to work on older
496 * versions of Python, since they are compiled with an incompatible CRT.
497 * The best solution I've found was to just replace sys.stdout/stderr with
498 * the log file reopened in append mode (which requires not locking it for
499 * write, and also passing in _O_APPEND above, and disabling buffering).
500 * It's not the most elegant solution, but it's better than crashing. */
501#if PY_MAJOR_VERSION < 3
502 if (log_pathw != NULL) {
503 PyObject *uniobj = PyUnicode_FromWideChar(log_pathw, (Py_ssize_t)wcslen(log_pathw));
504 PyObject *file = PyObject_CallFunction((PyObject*)&PyFile_Type, "Nsi", uniobj, "a", 0);
505
506 if (file != NULL) {
507 PyFile_SetEncodingAndErrors(file, "utf-8", NULL);
508
509 PySys_SetObject("stdout", file);
510 PySys_SetObject("stderr", file);
511 PySys_SetObject("__stdout__", file);
512 PySys_SetObject("__stderr__", file);
513
514 /* Be sure to disable buffering, otherwise we'll get overlap */
515 setbuf(stdout, (char *)NULL);
516 setbuf(stderr, (char *)NULL);
517 }
518 }
519 else
520#endif
521 if (!supports_code_page(GetConsoleOutputCP()) ||
522 !supports_code_page(GetConsoleCP())) {
523 /* Same hack as before except for Python 2.7, which doesn't seem to have
524 * a way to set the encoding ahead of time, and setting PYTHONIOENCODING
525 * doesn't seem to work. Fortunately, Python 2.7 doesn't usually start
526 * causing codec errors until the first print statement. */
527 PyObject *sys_stream;
528 UINT acp = GetACP();
529 SetConsoleCP(acp);
530 SetConsoleOutputCP(acp);
531
532 sys_stream = PySys_GetObject("stdin");
533 if (sys_stream && PyFile_Check(sys_stream)) {
534 PyFile_SetEncodingAndErrors(sys_stream, "mbcs", NULL);
535 }
536 sys_stream = PySys_GetObject("stdout");
537 if (sys_stream && PyFile_Check(sys_stream)) {
538 PyFile_SetEncodingAndErrors(sys_stream, "mbcs", NULL);
539 }
540 sys_stream = PySys_GetObject("stderr");
541 if (sys_stream && PyFile_Check(sys_stream)) {
542 PyFile_SetEncodingAndErrors(sys_stream, "mbcs", NULL);
543 }
544 }
545#endif
546
547#if defined(MS_WINDOWS) && PY_VERSION_HEX >= 0x03040000
548 /* Ensure that line buffering is enabled on the output streams. */
549 if (!unbuffered) {
550 /* Python 3.7 has a useful reconfigure() method. */
551 PyObject *sys_stream;
552 sys_stream = PySys_GetObject("__stdout__");
553 if (sys_stream && !enable_line_buffering(sys_stream)) {
554 fprintf(stderr, "Failed to enable line buffering on sys.stdout\n");
555 fflush(stderr);
556 }
557 sys_stream = PySys_GetObject("__stderr__");
558 if (sys_stream && !enable_line_buffering(sys_stream)) {
559 fprintf(stderr, "Failed to enable line buffering on sys.stderr\n");
560 fflush(stderr);
561 }
562 }
563#endif
564
565 if (Py_VerboseFlag)
566 fprintf(stderr, "Python %s\n%s\n",
567 Py_GetVersion(), Py_GetCopyright());
568
569#if PY_MAJOR_VERSION >= 3 && !defined(WIN_UNICODE)
570 PySys_SetArgv(argc, argv_copy);
571#else
572 PySys_SetArgv(argc, argv);
573#endif
574
575#ifdef MACOS_APP_BUNDLE
576 // Add the Frameworks directory to sys.path.
577 char buffer[PATH_MAX];
578 uint32_t bufsize = sizeof(buffer);
579 if (_NSGetExecutablePath(buffer, &bufsize) != 0) {
580 assert(false);
581 return 1;
582 }
583 char resolved[PATH_MAX];
584 if (!realpath(buffer, resolved)) {
585 perror("realpath");
586 return 1;
587 }
588 const char *dir = dirname(resolved);
589 sprintf(buffer, "%s/../Frameworks", dir);
590
591 PyObject *sys_path = PyList_New(1);
592 #if PY_MAJOR_VERSION >= 3
593 PyList_SET_ITEM(sys_path, 0, PyUnicode_FromString(buffer));
594 #else
595 PyList_SET_ITEM(sys_path, 0, PyString_FromString(buffer));
596 #endif
597 PySys_SetObject("path", sys_path);
598 Py_DECREF(sys_path);
599
600 // Now, store a path to the Resources directory into the main_dir pointer,
601 // for ConfigPageManager to read out and assign to MAIN_DIR.
602 sprintf(buffer, "%s/../Resources", dir);
603 set_main_dir(buffer);
604
605 // Finally, chdir to it, so that regular Python files are read from the
606 // right location.
607 chdir(buffer);
608#endif
609
610 n = PyImport_ImportFrozenModule("__main__");
611 if (n == 0)
612 Py_FatalError("__main__ not frozen");
613 if (n < 0) {
614 PyErr_Print();
615 sts = 1;
616 }
617 else
618 sts = 0;
619
620#ifndef NDEBUG
621 if (inspect && isatty((int)fileno(stdin)))
622 sts = PyRun_AnyFile(stdin, "<stdin>") != 0;
623#endif
624
625#ifdef MS_WINDOWS
626 PyWinFreeze_ExeTerm();
627#endif
628 Py_Finalize();
629
630#if PY_MAJOR_VERSION >= 3 && !defined(WIN_UNICODE)
631error:
632 if (argv_copy2) {
633 for (i = 0; i < argc; i++) {
634#if PY_MAJOR_VERSION > 3 || PY_MINOR_VERSION >= 4
635 PyMem_RawFree(argv_copy2[i]);
636#else
637 PyMem_Free(argv_copy2[i]);
638#endif
639 }
640 }
641#endif
642 return sts;
643}
644
645/**
646 * Maps the binary blob at the given memory address to memory, and returns the
647 * pointer to the beginning of it.
648 */
649static void *map_blob(off_t offset, size_t size) {
650 void *blob;
651 FILE *runtime;
652
653#ifdef _WIN32
654 wchar_t buffer[2048];
655 GetModuleFileNameW(NULL, buffer, 2048);
656 runtime = _wfopen(buffer, L"rb");
657#elif defined(__FreeBSD__)
658 size_t bufsize = 4096;
659 char buffer[4096];
660 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
661 mib[3] = getpid();
662 if (sysctl(mib, 4, (void *)buffer, &bufsize, NULL, 0) == -1) {
663 perror("sysctl");
664 return NULL;
665 }
666 runtime = fopen(buffer, "rb");
667#elif defined(__APPLE__)
668 char buffer[4096];
669 uint32_t bufsize = sizeof(buffer);
670 if (_NSGetExecutablePath(buffer, &bufsize) != 0) {
671 return NULL;
672 }
673 runtime = fopen(buffer, "rb");
674#else
675 char buffer[4096];
676 ssize_t pathlen = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1);
677 if (pathlen <= 0) {
678 perror("readlink(/proc/self/exe)");
679 return NULL;
680 }
681 buffer[pathlen] = '\0';
682 runtime = fopen(buffer, "rb");
683#endif
684
685 // Get offsets. In version 0, we read it from the end of the file.
686 if (blobinfo.version == 0) {
687 uint64_t end, begin;
688 fseek(runtime, -8, SEEK_END);
689 end = ftell(runtime);
690 fread(&begin, 8, 1, runtime);
691
692 offset = (off_t)begin;
693 size = (size_t)(end - begin);
694 }
695
696 // mmap the section indicated by the offset (or malloc/fread on windows)
697#ifdef _WIN32
698 blob = (void *)malloc(size);
699 assert(blob != NULL);
700 fseek(runtime, (long)offset, SEEK_SET);
701 fread(blob, size, 1, runtime);
702#else
703 blob = (void *)mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(runtime), offset);
704 assert(blob != MAP_FAILED);
705#endif
706
707 fclose(runtime);
708 return blob;
709}
710
711/**
712 * The inverse of map_blob.
713 */
714static void unmap_blob(void *blob) {
715 if (blob) {
716#ifdef _WIN32
717 free(blob);
718#else
719 munmap(blob, blobinfo.blob_size);
720#endif
721 }
722}
723
724/**
725 * Main entry point to deploy-stub.
726 */
727#if defined(_WIN32) && PY_MAJOR_VERSION >= 3
728int wmain(int argc, wchar_t *argv[]) {
729#else
730int main(int argc, char *argv[]) {
731#endif
732 int retval;
733 struct _frozen *moddef;
734 const char *log_filename;
735 void *blob = NULL;
736 log_filename = NULL;
737
738#ifdef __APPLE__
739 // Strip a -psn_xxx argument passed in by macOS when run from an .app bundle.
740 if (argc > 1 && strncmp(argv[1], "-psn_", 5) == 0) {
741 argv[1] = argv[0];
742 ++argv;
743 --argc;
744 }
745#endif
746
747 /*
748 printf("blob_offset: %d\n", (int)blobinfo.blob_offset);
749 printf("blob_size: %d\n", (int)blobinfo.blob_size);
750 printf("version: %d\n", (int)blobinfo.version);
751 printf("num_pointers: %d\n", (int)blobinfo.num_pointers);
752 printf("codepage: %d\n", (int)blobinfo.codepage);
753 printf("flags: %d\n", (int)blobinfo.flags);
754 printf("reserved: %d\n", (int)blobinfo.reserved);
755 */
756
757 // If we have a blob offset, we have to map the blob to memory.
758 if (blobinfo.version == 0 || blobinfo.blob_offset != 0) {
759 void *blob = map_blob((off_t)blobinfo.blob_offset, (size_t)blobinfo.blob_size);
760 assert(blob != NULL);
761
762 // Offset the pointers in the header using the base mmap address.
763 if (blobinfo.version > 0 && blobinfo.num_pointers > 0) {
764 uint32_t i;
765 assert(blobinfo.num_pointers <= MAX_NUM_POINTERS);
766 for (i = 0; i < blobinfo.num_pointers; ++i) {
767 // Only offset if the pointer is non-NULL. Except for the first
768 // pointer, which may never be NULL and usually (but not always)
769 // points to the beginning of the blob.
770 if (i == 0 || blobinfo.pointers[i] != 0) {
771 blobinfo.pointers[i] = (void *)((uintptr_t)blobinfo.pointers[i] + (uintptr_t)blob);
772 }
773 }
774 if (blobinfo.num_pointers >= 12) {
775 log_filename = blobinfo.pointers[11];
776 }
777 } else {
778 blobinfo.pointers[0] = blob;
779 }
780
781 // Offset the pointers in the module table using the base mmap address.
782 moddef = blobinfo.pointers[0];
783 while (moddef->name) {
784 moddef->name = (char *)((uintptr_t)moddef->name + (uintptr_t)blob);
785 if (moddef->code != 0) {
786 moddef->code = (unsigned char *)((uintptr_t)moddef->code + (uintptr_t)blob);
787 }
788 //printf("MOD: %s %p %d\n", moddef->name, (void*)moddef->code, moddef->size);
789 moddef++;
790 }
791 }
792
793 if (log_filename != NULL) {
794 char log_filename_buf[4096];
795 if (blobinfo.flags & F_log_filename_strftime) {
796 log_filename_buf[0] = 0;
797 time_t now = time(NULL);
798 if (strftime(log_filename_buf, sizeof(log_filename_buf), log_filename, localtime(&now)) > 0) {
799 log_filename = log_filename_buf;
800 }
801 }
802 setup_logging(log_filename, (blobinfo.flags & F_log_append) != 0);
803 }
804
805#ifdef _WIN32
806 if (blobinfo.codepage != 0) {
807 SetConsoleCP(blobinfo.codepage);
808 SetConsoleOutputCP(blobinfo.codepage);
809 }
810#endif
811
812 // Run frozen application
813 PyImport_FrozenModules = blobinfo.pointers[0];
814 retval = Py_FrozenMain(argc, argv);
815
816 fflush(stdout);
817 fflush(stderr);
818
819 unmap_blob(blob);
820 return retval;
821}
822
823#ifdef WIN_UNICODE
824int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, wchar_t *lpCmdLine, int nCmdShow) {
825 return wmain(__argc, __wargv);
826}
827#elif defined(_WIN32)
828int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char *lpCmdLine, int nCmdShow) {
829 return main(__argc, __argv);
830}
831#endif