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