deploy-stub.c 22 KB

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