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