deploy-stub.c 20 KB

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