Search code examples
pythondependenciespyinstallerchroot

PyInstaller OS Library Dependencies and Chroot


The PyInstaller documentation states

PyInstaller does not include libraries that should exist in any installation of this OS. For example in GNU/Linux, it does not bundle any file from /lib or /usr/lib, assuming these will be found in every system.

However the target environment may have installed a different version of the same set of libraries. Is there a way to find out all the OS libraries a pyisntaller generated binary depends on?

I created a binary my_app/myapp using pyisntaller. Then used ldd to list all the .so files it depends on, and copied all of them into the distribution directory. In my assumption, the resulting dist directory can be used as a root for a chroot environment. However it does not work.

From the ldd output:

# ldd my_app/my_app 
    linux-vdso.so.1 (0x00007ffdddc67000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f6d3342f000)
    libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f6d33212000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6d32e21000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f6d3384e000)

It looks only these libraries are required dependents:

# ls -l lib
-rwxr-xr-x 1 root root  170960 Apr 16  2018 ld-2.27.so
-rwxr-xr-x 1 root root 2030544 Apr 16  2018 libc-2.27.so
lrwxrwxrwx 1 root root      12 Apr 16  2018 libc.so.6 -> libc-2.27.so
-rw-r--r-- 1 root root   14560 Apr 16  2018 libdl-2.27.so
lrwxrwxrwx 1 root root      13 Apr 16  2018 libdl.so.2 -> libdl-2.27.so
lrwxrwxrwx 1 root root      14 May 23  2017 libz.so.1 -> libz.so.1.2.11
-rw-r--r-- 1 root root  116960 May 23  2017 libz.so.1.2.11

When I run chroot ./ /lib/ld-2.27.so /my_app/my_app, the screen shows

[26499] PyInstaller Bootloader 3.x
[26499] LOADER: Cannot get fullpath for /my_app/my_app
[26499] LOADER: homepath is /my_app
[26499] LOADER: _MEIPASS2 is NULL
[26499] LOADER: archivename is /my_app/my_app
[26499] LOADER: Extracting binaries
[26499] LOADER: Executing self as child
[26499] LOADER: set _MEIPASS2 to /my_app
[26499] LOADER: LD_LIBRARY_PATH=/my_app
[26499] LOADER: Registering signal handlers
[26500] Failed to exec: No such file or directory

Where the last line should be [99] PyInstaller Bootloader 3.x and from there the program should continue.

I know chroot would work because it does when the whole / is copied.


Solution

  • The error message Failed to exec: No such file or directory was due to the ld has to be at the path /lib64/ld-linux-x86-64.so.2. Despite the actual file it points to is really ld-2.27.so:

    ls -l /lib64/
    /lib64/ld-linux-x86-64.so.2 -> /lib/x86_64-linux-gnu/ld-2.27.so
    

    Once that is fixed, a new error message makes more sense, pointing to a missing libpthread....

    Looked deeper into the PyInstaller internal, it packaged at least three other binary executables inside: A python interpreter, and two binaries for a bootloader. Thus run ldd on the python which the PyInstaller uses, and on those two bootloaders found under the site packages directory, it will be all the dependencies required. The above libpthread... is required by the python interpreter.

    And the ldd output shows it wants the library files to be under /lib/x86_64-linux-gnu/ instead of /lib/.

    The final content of lib/ and lib64/ are:

    # ls -l lib
    -rwxr-xr-x    170960 Apr 16  2018 ld-2.27.so
    -rwxr-xr-x   2030544 Apr 16  2018 libc-2.27.so
    lrwxrwxrwx        12 Apr 16  2018 libc.so.6 -> libc-2.27.so
    -rw-r--r--     14560 Apr 16  2018 libdl-2.27.so
    lrwxrwxrwx        13 Apr 16  2018 libdl.so.2 -> libdl-2.27.so
    lrwxrwxrwx        17 Sep 10 11:05 libexpat.so.1 -> libexpat.so.1.6.7
    -rw-r--r--    202880 Sep 10 11:05 libexpat.so.1.6.7
    -rw-r--r--   1700792 Apr 16  2018 libm-2.27.so
    lrwxrwxrwx        12 Apr 16  2018 libm.so.6 -> libm-2.27.so
    -rwxr-xr-x    144976 Apr 16  2018 libpthread-2.27.so
    lrwxrwxrwx        18 Apr 16  2018 libpthread.so.0 -> libpthread-2.27.so
    -rw-r--r--     10592 Apr 16  2018 libutil-2.27.so
    lrwxrwxrwx        15 Apr 16  2018 libutil.so.1 -> libutil-2.27.so
    lrwxrwxrwx        14 May 23  2017 libz.so.1 -> libz.so.1.2.11
    -rw-r--r--    116960 May 23  2017 libz.so.1.2.11
    lrwxrwxrwx         1 Feb 27 17:14 x86_64-linux-gnu -> .
    
    # ls -l lib64
    lrwxrwxrwx   32 Apr 16  2018 ld-linux-x86-64.so.2 -> /lib/x86_64-linux-gnu/ld-2.27.so
    

    This seems to have been working well.

    This directory potentially can run on top of the bare metal of a kernel, without including other user space applications in the rootfs. It's becoming very interesting.