Unix & Linux
dynamic-linking x86 elf dynamic-loading
Updated Sat, 21 May 2022 00:45:20 GMT

ld.so.preload doesn't differ x86_32 and x86_64


It's known you can run x86_32 programms with x86_64 kernel if it was compiled with support for that. But dynamic linker doesn't provide any way to define a separate set of preload libraries for 32-bit programs, so every time you run such a program, had you x86_64 preloads, you would face this error message:

ERROR: ld.so: object '  ' from /etc/ld.so.preload cannot be preloaded (wrong ELF class: ELFCLASS64): ignored.

In case you put there the same list of x86_32-libraries to pre-load, you would get it working, but all pure x86_64 runs would start complaining as well.

The best possible way is to modify the dynamic loader to support pre-loading from separate files, obviously, but it's at least lengthy process. Can you think of some clean workaround?

For now I'm thinking about some multi-class-pre-load.so, which could load needed files by itself, but, as I can see, there's no "multi-class" support in ELF.




Solution

In your ld.so.preload, you want to specify "$LIB" in your path rather than an explicit "lib" or "lib64". Thus, on a Redhat-style distro, "/usr/alternates/$LIB/libfoo.so" becomes "/usr/alternates/lib/libfoo.so" for a 32-bit process and "/usr/alternates/lib64/libfoo.so" for a 64-bit process. On an Debian-style distro, "/usr/alternates/$LIB/libfoo.so" becomes "/usr/alternates/lib/i386-linux-gnu/libfoo.so" and "/usr/alternates/x86_64-linux-gnu/libfoo.so" respectively. Your tree then needs to be populated with libraries for both architectures.

See "rpath token expansion" in the ld.so(8) man page for more on this.

Note however, that rather than preloading a library, if you're compiling the binaries whose loading you are attempting to modify, you may find it better to modify the paths by setting DT_RUNPATHon the link line (using the same "$LIB"-style paths, thus configuring the binary to prefer your library location over the system defaults.

Alternately, as others have noted, you may edit an ELF file to set DT_RUNPATH on binaries you're not compiling.

The following works for me on an x86_64 Centos 6.5 box:

cd /tmp
mkdir lib lib64
wget http://carrera.databits.net/~ksb/msrc/local/lib/snoopy/snoopy.h
wget http://carrera.databits.net/~ksb/msrc/local/lib/snoopy/snoopy.c
gcc -m64 -shared -fPIC -ldl snoopy.c -o /tmp/lib64/snoopy.so
gcc -m32 -shared -fPIC -ldl snoopy.c -o /tmp/lib/snoopy.so
cat > true.c <<EOF
int main(void)
{ return 0; }
EOF
gcc -m64 true.c -o true64
gcc -m32 true.c -o true32
sudo bash -c "echo '/tmp/\$LIB/snoopy.so' > /etc/ld.so.preload"
strace -fo /tmp/strace64.out /tmp/true64
strace -fo /tmp/strace32.out /tmp/true32
sudo rm /etc/ld.so.preload"

In the strace output, strace64.out has:

open("/tmp/lib64/snoopy.so", O_RDONLY) = 3

while strace32.out has:

open("/tmp/lib/snoopy.so", O_RDONLY) = 3

This is with an ld.so.preload contents of:

/tmp/$LIB/snoopy.so






External Links

External links referenced by this document: