-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Binary tools spawned from exe made with PyInstaller and StaticX pick up wrong libraries #239
Comments
Hi, sorry I'm just getting to this. TL;DRRead BackgroundSo, this is complicated. First, you should understand how PyInstaller and Staticx work under the covers. PyInstallerAfter the PyInstaller bootloader extracts the embedded archive, it launches the Python runtime and tweaks a few path-related things to make sure that the files from the unpacked archive are used. This involves Python This works great for basic scripts which may include extension modules (.so). However, the See also:
StaticxStaticx works differently. (And I need to document this #241). It does not use At build-time, when given an app to staticx-ify:
At runtime, the bootloader:
ProblemNow, back to your question.
Correction: I think you said Invoking system appsIf you're trying to invoke an app installed on the target system, the problem is exactly as as described in the PyInstaller docs, and includes a solution: I think this explains your specific error:
So:
Try their advice. Invoking bundled appsThis is where it gets tricky. But it basically devolves into the common staticx use case! If you do nothing special, PyIntaller's
The only real solution here today is nested-staticx. Hope this helps! Please let me know if I can clarify anything. |
Jonathon,
Thanks a lot for your detailed reply. We have found that fixing up the LD_LIBRARY_PATH, as detailed in the PyInstaller documentation, allows all our external tools to run.
Many thanks,
Kevin
From: Jonathon Reinhart ***@***.***>
Sent: 22 June 2023 04:22
To: JonathonReinhart/staticx ***@***.***>
Cc: Kevin White ***@***.***>; Author ***@***.***>
Subject: Re: [JonathonReinhart/staticx] Binary tools spawned from exe made with PyInstaller and StaticX pick up wrong libraries (Issue #239)
Hi, sorry I'm just getting to this.
Background
So, this is complicated. First, you should understand how PyInstaller and Staticx work under the covers.
PyInstaller
After the PyInstaller bootloader extracts the embedded archive, it launches the Python runtime and tweaks a few path-related things to make sure that the files from the unpacked archive are used. This involves Python sys.path for loading modules as well as LD_LIBRARY_PATH<https://pyinstaller.org/en/stable/runtime-information.html?highlight=sys%20meipass#ld-library-path-libpath-considerations> environment variable for telling ld.so where to find shared libraries.
This works great for basic scripts which may include extension modules (.so). However, the LD_LIBRARY_PATH environment variable persists across fork+exec, which means it applies to all child processes too. This includes when you're trying to run external programs -- both those from the host system, as well as ones you bring along in the PyInstaller archive! This is not what you want for system executables, but may be what you want for bundled applications..... however it is probably not good enough for those.... enter staticx.
See also:
* https://pyinstaller.org/en/stable/operating-mode.html#how-the-one-file-program-works
* https://pyinstaller.org/en/stable/runtime-information.html
Staticx
Staticx works differently. (And I need to document this #241<https://protect2.fireeye.com/v1/url?k=dac8fcf1-ba2a61ac-dac977be-000babd9f1ba-73c64b11944d4fab&q=1&e=bca617bf-3502-4a68-9dd8-42c386f8d167&u=https%3A%2F%2Fgithub.com%2FJonathonReinhart%2Fstaticx%2Fissues%2F241>). It does not use LD_LIBRARY_PATH.
At build-time, when given an app to staticx-ify:
* Makes a copy of the app, and modifies it<https://protect2.fireeye.com/v1/url?k=13ed5cf1-730fc1ac-13ecd7be-000babd9f1ba-7745a8e40ed2b6db&q=1&e=bca617bf-3502-4a68-9dd8-42c386f8d167&u=https%3A%2F%2Fgithub.com%2FJonathonReinhart%2Fstaticx%2Fblob%2F390cd624003cdaaba899c415e648478971fd79e7%2Fstaticx%2Fapi.py%23L280> slightly, before adding it to a new staticx archive
* Modifies the INTERP and RPATH fields to long dummy values which will be fixed up by the bootloader at runtime
* Examines the app to determine all of its dynamic library dependencies (via ldd), and adds them to the staticx archive
* This includes things that PyInstaller explicitly excludes, like ld.so and libc.so
* If the app is a PyInstaller onefile app (see hooks/pyinstaller.py<https://protect2.fireeye.com/v1/url?k=a89f3720-c87daa7d-a89ebc6f-000babd9f1ba-8ebaa256ae93ca22&q=1&e=bca617bf-3502-4a68-9dd8-42c386f8d167&u=https%3A%2F%2Fgithub.com%2FJonathonReinhart%2Fstaticx%2Fblob%2Fmaster%2Fstaticx%2Fhooks%2Fpyinstaller.py>):
* Iterates the contents of the embedded archive, and extracts all binary files for analysis
* Examines all extracted shared objects, and adds all of their dependencies to the staticx archive
* (As I write this, I'm actually second-guessing the correctness of this approach, but I digress)
* Creates a copy of the staticx bootloader, and attaches the generated archive to it (via new ELF section)
At runtime, the bootloader:
* Creates a temporary dir (the "bundle dir") and extracts the embedded archive (just like PyInstaller)
* But now we need to run your embedded app, making sure to only reference files from the bundle dir!
* Patch<https://protect2.fireeye.com/v1/url?k=d865ebfd-b88776a0-d86460b2-000babd9f1ba-d5100f433cbd2959&q=1&e=bca617bf-3502-4a68-9dd8-42c386f8d167&u=https%3A%2F%2Fgithub.com%2FJonathonReinhart%2Fstaticx%2Fblob%2F390cd624003cdaaba899c415e648478971fd79e7%2Fbootloader%2Fmain.c%23L198> your embedded app, replacing:
* RPATH: The absolute path of the bundle dir
* INTERP: The absolute path of ld.so in the bundle dir
* The bootloader does set a couple environment variables<https://protect2.fireeye.com/v1/url?k=77af5020-174dcd7d-77aedb6f-000babd9f1ba-b4816630d157c582&q=1&e=bca617bf-3502-4a68-9dd8-42c386f8d167&u=https%3A%2F%2Fgithub.com%2FJonathonReinhart%2Fstaticx%2Fblob%2F390cd624003cdaaba899c415e648478971fd79e7%2Fbootloader%2Fmain.c%23L285>, but these are purely informative and do not affect the loading of shared libraries, etc.
* STATICX_BUNDLE_DIR
* STATICX_PROG_PATH
* Forks + execs the modified app
* ld.so (interp) gets control. It sees RPATH (and NODEFLIB) and restricts its search for shared objects to that directory
* (I need to double-check how this interacts with LD_LIBRARY_PATH which will be subsequently set by the PyInstaller bootloader, which is 'the app')
* NOTE: The RPATH trick does not apply to child processes!
Problem
Now, back to your question.
It spawns some binary tools such as adb and our own tools that are bundled in by PyInstaller. These do not pick up libraries correctly.
However when run from the exe created by StaticX it seems to pick up a libgcc_s.so from the tmp directory created by StaticX.
Correction: /tmp/_MEIxxxxxx is a temporary directory created by PyInstaller. The staticx tempdir is named<https://protect2.fireeye.com/v1/url?k=b03de3fe-d0df7ea3-b03c68b1-000babd9f1ba-cc415df7445978b7&q=1&e=bca617bf-3502-4a68-9dd8-42c386f8d167&u=https%3A%2F%2Fgithub.com%2FJonathonReinhart%2Fstaticx%2Fblob%2F390cd624003cdaaba899c415e648478971fd79e7%2Fbootloader%2Fmain.c%23LL212C42-L212C56> /tmp/staticx-XXXXXX.
I think you said adb is expected to be installed on the target system. But let's discuss. both cases:
Invoking system apps
If you're trying to invoke an app installed on the target system, the problem is exactly as as described in the PyInstaller docs, and includes a solution: LD_LIBRARY_PATH / LIBPATH considerations<https://pyinstaller.org/en/stable/runtime-information.html#ld-library-path-libpath-considerations>.
I think this explains your specific error:
Shell failed. output: adb: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.35' not found (required by /tmp/_MEIs3cXJk/libgcc_s.so.1)
adb: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by /tmp/_MEIs3cXJk/libgcc_s.so.1)
So:
* Staticx did its job and launched your PyInstalled app
* PyInstaller did its job and loaded all of the required libraries
* Your python code ran adb (from the system), but left LD_LIBRARY_PATH set
* system ld.so interpreter loaded it
* system libc.so (old) was loaded
* I'm not 100% sure why your attempt to bring libc.so didn't work; it's possible that PyInstaller ignored it, or that the target system ld.so config ignored LD_LIBRARY_PATH for libc. That code is complex.
* PyInstaller-bundled libgcc_s.so (new) was loaded
* BUT it depends on newer libc
Try their advice.
Invoking bundled apps
This is where it gets tricky. But it basically devolves into the common staticx use case!
If you do nothing special, PyIntaller's LD_LIBRARY_PATH will apply; if you take the PyInstaller advice from the previous section, it will not.
* Either way, the system dynamic linker (e.g. /lib64/ld-linux-x86-64.so.2) will load the application. There's nothing you can do about this. (Other than also staticx-ify those apps before bundling them into your pyinstaller app, and then staticx again. Russian dolls!)
* Either way, libc.so will come from the system, because PyInstaller does not bundle it
* Other libraries will either come from the PyInstaller bundle (if LD_LIBRARY_PATH is still set and they were included), or the target system if not.
The only real solution here today is nested-staticx.
Hope this helps! Please let me know if I can clarify anything.
—
Reply to this email directly, view it on GitHub<https://protect2.fireeye.com/v1/url?k=c38c22ec-a36ebfb1-c38da9a3-000babd9f1ba-7da5fe2ecc13363d&q=1&e=bca617bf-3502-4a68-9dd8-42c386f8d167&u=https%3A%2F%2Fgithub.com%2FJonathonReinhart%2Fstaticx%2Fissues%2F239%23issuecomment-1601960532>, or unsubscribe<https://protect2.fireeye.com/v1/url?k=1e1f55cf-7efdc892-1e1ede80-000babd9f1ba-6ee91214ff185bef&q=1&e=bca617bf-3502-4a68-9dd8-42c386f8d167&u=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAHHIF5VCDGHPXPZ23FGXNM3XMO25ZANCNFSM6AAAAAAY2RBPFI>.
You are receiving this because you authored the thread.Message ID: ***@***.******@***.***>>
|
We have a standalone exe we've created with PyInstaller and StaticX. It spawns some binary tools such as adb and our own tools that are bundled in by PyInstaller. These do not pick up libraries correctly.
E.g. running an exe we created on Ubuntu 22.04 on Ubuntu 16.04 gives the following:
This is running a pre-installed adb which runs fine when run from the command line. However when run from the exe created by StaticX it seems to pick up a libgcc_s.so from the tmp directory created by StaticX. This then needs GLIBC_2.34 which isn't available. (Ubuntu 16.04 only has 2.23).
I've tried asking StaticX to include a libc which
-l /usr/lib/x86_64-linux-gnu/libc.so.6
but this doesn't seem to help.The text was updated successfully, but these errors were encountered: