Shipping libstdc++.so.6 with application
Asked Answered
T

1

10

I want to use gcc 4.8.1 for my application (requires libstdc++.so.6.0.18), however customers only have libstdc++.so.6.0.13. I have been using -static-libgcc -static-stdlibc++ for a while now, but my application consists of several dynamically linked libraries and one main application. This means that when compiling each dynamic library, they must statically compile the standard library, which is redundant and wasteful. I want to just ship the standard library of my choice with my product, however every time I run my application in an environment like theirs, it always loads the wrong standard library. It prefers the /usr/lib64/ version no matter what I seem to do (it seems to take precedence over LD_LIBRARY_PATH).

Constraints:

  1. I'm not allowed to force them to upgrade to a new standard library.

  2. I don't want to make the dynamic libraries static. (I'd be able to statically compile everything into the main app once, but there are some logistical barriers that prevent me from recompiling some libraries into static ones).

-Wl,-rpath=$(path_to_directory) is a bit dangerous, however it is legal because the customers do source some settings that allow me to set path variables. However, setting the rpath of my new stdlibc++ doesn't seem to be overriding the default /usr/lib64 version. I still get GLIBCXX errors because it won't use the right library.

Surely there is an elegant solution for this?

Perhaps there is just an error in my procedure. Here's an example (sorry about the censor, but it's just username stuff):

~/example$ pwd
/home/username/example
~/example$ echo $LD_LIBRARY_PATH

~/example$ ls
Makefile  libstdc++.so.6.0.18  test.cpp
~/example$ make
g++ -std=c++11 -Wall -Werror test.cpp -o test
~/example$ ldd test
./test: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.14' not found (required by ./test)
    linux-vdso.so.1 =>  (0x00007fffe5919000)
    libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x000000390b800000)
    libm.so.6 => /lib64/libm.so.6 (0x0000003904800000)
    libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x000000390b400000)
    libc.so.6 => /lib64/libc.so.6 (0x0000003904400000)
    /lib64/ld-linux-x86-64.so.2 (0x0000003904000000)
~/example$ setenv LD_LIBRARY_PATH /home/username/example
~/example$ echo $LD_LIBRARY_PATH
/home/username/example
~/example$ ldd test
./test: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.14' not found (required by ./test)
    linux-vdso.so.1 =>  (0x00007fff2d3ff000)
    libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x000000390b800000)
    libm.so.6 => /lib64/libm.so.6 (0x0000003904800000)
    libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x000000390b400000)
    libc.so.6 => /lib64/libc.so.6 (0x0000003904400000)
    /lib64/ld-linux-x86-64.so.2 (0x0000003904000000)

Sorry guys, I made a rather dumb mistake...

~/example$ file libstdc++.so.6.0.18 
libstdc++.so.6.0.18: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, not stripped

Some dweeb built the wrong version of the library, and another dweeb (namely myself) tried using it on a 64-bit machine. Using LD_LIBRARY_PATH was working all along...

Tit answered 9/12, 2014 at 18:18 Comment(7)
I would have expected LD_LIBRARY_PATH to work: Did you try ldding your binary both with and without LD_LIBRARY_PATH set? Also remember that LD_LIBRARY_PATH will never persist through elevations.Echinoid
Show how you are setting LD_LIBRARY_PATH, and also the output of ldd $x | fgrep ++ where $x ranges over all binaries you ship, with LD_LIBRARY_PATH set and/or -Wl,-rpath= in effect.Focalize
Okay so LD_LIBRARY_PATH is supposed to take precedence? My application runs in user mode, so there are no elevated privs. Here's what I basically did: Compile with gcc 4.8.1 (its configuration resolves links with the correct library), set setenv LD_LIBRARY_PATH /home/myusername/:${LD_LIBRARY_PATH}, and then try to run/ldd the program. The stdlib I want in this example is in my home directory. This is supposed to take precedence over system paths?Tit
Can you try export LD_LIBRARY_PATH=/home/username/example because I think setenv is just treating it as a shell variable.Echinoid
I'm in cshell, and so are the customers sadly.Tit
Does setting the LD_LIBRARY_PATH supersede the system library path for libstdc++.so in you shells?Tit
LD_LIBRARY_PATH should take precedence over directories cached by ldconfig. Be careful using /home/myusername/:${LD_LIBRARY_PATH}, if LD_LIBRARY_PATH is initially empty then that sets it to /home/myusername/: and the empty component means "look in the current working directory" which is a security risk.Chinfest
C
11

Your problem is that the executable is linked to the soname libstdc++.so.6 not to the full library filename libstdc++.so.6.0.16. The dynamic linker will look for libstdc++.so.6 in the usual places (i.e. LD_LIBRARY_PATH, DT_RPATH, ldconfig dirs etc.) so to ensure the 6.0.18 version is found you need a symlink called libstdc++.so.6 pointing to it.

Instead of using LD_LIBRARY_PATH (which is fragile on machines you don't control, because users might alter their environment) I prefer linking with '-Wl,-rpath,$ORIGIN' (N.B. the quotes are necessary to stop the shell expanding $ORIGIN)

An RPATH of $ORIGIN tells the dynamic linker to start looking for shared libraries in the same directory as the executable, so if you ship libstdc++.so alongside your executable it will be found. If you want to ship the executable in a bin directory and have the library in a lib directory you can use '-Wl,-rpath,$ORIGIN/../lib' or other paths relative to the location of the executable.

Chinfest answered 10/12, 2014 at 0:33 Comment(1)
Ahh I've seen that ORIGIN thing before, but I didn't realize it was a keyword for the linker; I thought it was just a commonly used shell variable (which would be entirely counterproductive).Tit

© 2022 - 2024 — McMap. All rights reserved.