patchelf
patchelf copied to clipboard
Accessing a corrupted shared library after patching
Describe the bug First of all, I suspect that this is not a bug, and it's just some problem with what I'm trying to do. But this is the most relevant place where I can ask the question, so, here goes. My goal is to move a python3 build from a Fedora Core 8 server to a RHEL4 server. I know, both servers are past their due date, but python3 is necessary to facilitate their upgrades. Now, python3 has been compiled and runs fine on FC8, but the precompiled version fails to start on RHEL4 because RHEL4 uses an older glibc.
Steps To Reproduce I copied over /lib from the FC8 server to /glibc on the RHEL4 server. I copied over the precompiled python3 and when trying to start it normally I get this (which is expected):
# LD_LIBRARY_PATH=/glibc:/opt/python3/lib:/lib:/usr/lib:/usr/local/lib /opt/python3/bin/python3
/opt/python3/bin/python3: /lib64/tls/libc.so.6: version `GLIBC_2.7' not found (required by /opt/python3/lib/libpython3.9.so.1.0)
/opt/python3/bin/python3: /lib64/tls/libc.so.6: version `GLIBC_2.6' not found (required by /opt/python3/lib/libpython3.9.so.1.0)
/opt/python3/bin/python3: /lib64/tls/libc.so.6: version `GLIBC_2.4' not found (required by /opt/python3/lib/libpython3.9.so.1.0)
The python3 binary looks like this before:
# find python3 -type f -exec file '{}' \; 2>&1 | grep "LSB executable"
python3/bin/python3.9: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.9, BuildID[sha1]=d544c6c7ec7a57aecd5a184ad5cfeb20d3706fae, with debug_info, not stripped
I used patchelf to rewrite the loader from /lib64/ld-linux-x86-64.so.2 to /glibc/ld-linux.so.2:
# patchelf --set-interpreter /glibc/ld-linux.so.2 python3/bin/python3.9
Afterwards, it looks like this:
# file python3/bin/python3.9
python3/bin/python3.9: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /glibc/ld-linux.so.2, for GNU/Linux 2.6.9, BuildID[sha1]=d544c6c7ec7a57aecd5a184ad5cfeb20d3706fae, with debug_info, not stripped
I've transferred the patched code on the RHEL4 server and when trying to run it I get:
# LD_LIBRARY_PATH=/glibc:/opt/python3/lib:/lib:/usr/lib:/usr/local/lib /opt/python3/bin/python3
-bash: /opt/python3/bin/python3: Accessing a corrupted shared library
# ldd /opt/python3/bin/python3
/usr/bin/ldd: line 116: /opt/python3/bin/python3: Accessing a corrupted shared library
I did a diff between the patched and original file and I noticed other things got changed apart from the path in the ELF header:

So, I thought - I'll try to manually patch the file and change /lib64/ld-linux-x86-64.so.2 to /glibc/ld-linux-x86-64.so.2 (I copied /glibc/ld-linux.so.2 to /glibc/ld-linux-x86-64.so.2). I did this and I get the same error, so I expect there's something wrong with my environment. I'd like to learn how to troubleshoot further, please!
Expected behavior
I expected the patched binary to load properly.
patchelf --version output
patchelf 0.12 (cloned from git)
A small update - ldd can read the libraries just fine when run on a different system (Ubuntu 20.04):
ldd python3/bin/python3.9
linux-vdso.so.1 (0x00007fff0cdce000)
libpython3.9.so.1.0 => not found
libcrypt.so.1 => /lib/x86_64-linux-gnu/libcrypt.so.1 (0x00007fcd088fc000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fcd088d9000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fcd088d3000)
libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007fcd088ce000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fcd088c3000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fcd08772000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcd08580000)
/glibc/ld-linux.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fcd0897b000)
though I'm not sure why it links /glibc/ld-linux.so.2 to the old library name...
Hey. I would suggest you to compile both glibc and python from source into a different prefix.
If you compile python, you can use -Wl,--rpath=/path/to/newglibc -Wl,--dynamic-linker=/path/to/newglibc/ld-linux.so.2 to specify in CFLAGS the newer glibc version. ldd will preload its own glibc, hence you should not trust it outputs for libc libraries.
A better way to debug this is, is using LD_DEBUG=all in environment flags. This will print linker output and show what libraries it tries to load. One issue I see with using patchelf here is that python will not just depend on libc but also a number of other libraries such as zlib, openssl etc. If you have libraries compiled against different libc's this will ask for troubles.