Shipping libstdc++.so.6 with application - c++

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:
I'm not allowed to force them to upgrade to a new standard library.
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...

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.

Related

Why linux ld somtime use the symbolic link of libs

I use the jsoncpp lib in my linux cli tool.
The CMakeLists.txt contains
find_library(LIB_JSON jsoncpp)
target_link_libraries(${PROJECT_NAME} ${LIB_JSON})
The result is
/usr/bin/c++ -rdynamic CMakeFiles/cktwagent.dir/agent_main.cpp.o -o cktwagent -ljsoncpp
When i check the binary I found:
$> ldd cktwagent
linux-vdso.so.1 (0x00007ffe4cfd1000)
libjsoncpp.so.24 => /usr/lib/libjsoncpp.so.24 (0x00007f87505bd000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f87503e0000)
libm.so.6 => /usr/lib/libm.so.6 (0x00007f875029a000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007f8750280000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f87500b7000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f87506ce000)
Why ld use /usr/lib/libjsoncpp.so.24 and not the symbolic link /usr/lib/libjsoncpp.so?
Why the ld sometime resolv the linbrary link to the real library file?
$> ls -l /usr/lib/libjsoncpp.so
lrwxrwxrwx 1 root root 16 26. Sep 17:02 /usr/lib/libjsoncpp.so -> libjsoncpp.so.24
In case of /usr/lib/libstdc++.so.6 the ld use the symbolic link. When i check the path from ldd output, libstdc++.so.6 point to a symbolic link.
$> ls -l /usr/lib/libstdc++.so.6
lrwxrwxrwx 1 root root 19 9. Nov 12:43 /usr/lib/libstdc++.so.6 -> libstdc++.so.6.0.28
I like to understand this behavior. Because when i copy the binary to a different system, the link to libjsoncpp.so available. But it points to some different version.
Many thanks
Thomas
That's a version compatibility feature. By convention, API compatibility is determined by the major version number. So 6.0.28 would be is compatible with 6.1.1, but not with 5.0.1 or 7.0.1.
To allow for compatible upgrades, libstdc++.so.6.0.28 is symlinked as libstdc++.so.6. Then it can be binary-upgraded to e.g. libstdc++.so.6.0.29 without touching applications that rely on API version 6 of libstdc++.
In addition, it allows libraries with different major version numbers to coexist on the same system. Installing libstdc++.so.7.0.0 will not break apps that link against libstdc++.so.6.
Why ld use /usr/lib/libjsoncpp.so.24 and not the symbolic link /usr/lib/libjsoncpp.so?
That's because libjsoncpp has a soname of libjsoncpp.so.24. Apparently it is following the same convention of having the major version number determine compatibility.
You can achieve the same behavior with your shared lib libfoo if you link it like this:
gcc -shared -Wl,-soname,libfoo.so.<major> -o libfoo.so.<major>.<minor>
For more details refer to GCC Howto - Version numbering, sonames and symlinks.

Linker seems to ignore library and default to older version

I've built a debug version of a shared library (OpenSSL) so I can step through a certain function with a debugger to better understand what's going on.
However, I'm having a difficult time actually linking with the debug version I've built. For some reason, no matter what I do, the linker always ends up linking with the pre-installed system version, even though both versions are in the usr/lib directory, the soft-links are setup correctly (AFAIK), and I explicitly specify the debug lib on the command line when compiling.
So the original (system-installed) version of the shared library is:
>ls /usr/lib/x86_64-linux-gnu/ -lh | grep libssl
lrwxrwxrwx 1 root root 15 Sep 23 2016 libssl.so -> libssl.so.1.0.0
-rw-r--r-- 1 root root 386K Sep 23 2016 libssl.so.1.0.0
And the debug version, which I compiled from source and configured as a shared library (using the fPIC flag for all object files), is:
>ls /usr/lib/ -lh | grep libssl
lrwxrwxrwx 1 root root 29 Oct 19 11:31 libssldebug.so -> /usr/lib/libssldebug.so.1.0.2
-rwxr-xr-x 1 root root 2.3M Oct 19 00:53 libssldebug.so.1.0.2
And it's the same with the other OpenSSL shared library, libcrypto. I have a libcryptodebug.so.1.0.2 and a corresponding soft link in /usr/lib.
So, I try to build an executable and link against the debug shared lib like this:
>g++ test.cpp -o test -std=c++14 -lssldebug -lcryptodebug -I openssl-1.0.2p/include/
And it compiles and links with no errors.
And YET... when I examine the executable with ldd, I see:
>ldd test
linux-vdso.so.1 (0x00007ffcaa39b000)
libssl.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007ff717d37000)
libcrypto.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007ff71793b000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007ff717630000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ff71732f000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007ff717119000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff716d6e000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ff716b6a000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff717f98000)
So even though I explicitly linked with -lssldebug, and it compiled and linked with no errors, ldd still shows that the linker for some reason is linking with the non-debug version (/usr/lib/x86_64-linux-gnu/libssl.so.1.0.0). I also tried running ldconfig and then recompiling/linking, but it still links with the old (non-debug) version.
So what is going on here? What am I doing incorrectly that causes it to silently ignore the command line linker arguments and somehow just default to using the non-debug version (which has a completely different shared lib name!) somehow?
So even though I explicitly linked with -lssldebug, and it compiled and linked with no errors, ldd still shows that the linker for some reason is linking with the non-debug version (/usr/lib/x86_64-linux-gnu/libssl.so.1.0.0
You are mixing up static linking, and runtime loading (sometimes also called dynamic linking).
When you link with g++ test.cpp ... -lssldebug ..., you are linking with libssldebug.so, but (as ldd output tells you) that library is not used during runtime loading.
That is happening because libssldebug.so has a special dynamic tag DT_SONAME, which contains "libssl.so.1.0.0", and the static linker records that name as the library to be loaded at runtime.
You can confirm this with:
readelf -d libssldebug.so | grep SONAME
You can examine the libraries that runtime loader will try to locate for a given executable with:
readelf -d ./test | grep NEEDED
Now that you understand the problem, how do you fix it?
One of two ways:
You could change the SONAME encoded in libssldebug.so, by relinking it with -Wl,--soname=libssldebug.so.1.0.2, and then relinking your test program. Use above readelf commands to verify that SONAME and NEEDED now contain "libssldebug.so.1.0.2", and then ldd to verify that libssldebug.so.1.0.2 is what the runtime loader will use.
Alternatively, it may be simpler to install libssldebug.so into a different directory (say /tmp/libssldebug), create a symlink libssl.so.1.0.0 -> lissldebug.so in that directory, and then ask the runtime loader to search this directory first with g++ test.cpp ... -Wl,--rpath=/tmp/libssldebug. With this solution, the executable will still search for libssl.so.1.0.0, but it will search for it in the /tmp/libssldebug directory first, and will find your copy.

Static linking of an FORTRAN code with MPI

I want to build a standalone bainary (static linking) of my fortran code so that it does not use any shared libaries anymore.
The program uses mpi so I need all connected libaries.
I started with generating my own libary pack:
ar rc my_lib.a /opt/intel/impi/4.1.0.030/intel64/lib/* /opt/intel/composerxe/mpirt/lib/intel64/*
Followed by compiling my files:
ifort -c -I/home/.../Remote/mpif.h -L/home/../Remote/my_lib.a file1.f
ifort -c -I/home/.../Remote/mpif.h -L/home/.../Remote/my_lib.a file1.f
ifort -O3 -o dns zufall.o dnspipe.o my_lib.a
But this returned the following error message:
ld: dns: hidden symbol `stat' in /usr/lib/x86_64-linux-gnu/libc_nonshared.a(stat.oS) is referenced by DSO
ld: final link failed: Bad value
How could I fix that?
Remark 1: #Gilles
I tried that now and it works:
mpiifort -c zufall.f
mpiifort -c dnspipe.f
ifort -o dns zufall.o dnspipe.o -L/opt/intel/impi/4.1.0.030/intel64/lib -Wl,-non_shared,-lmpigf,-lmpi,-lmpigi,-call_shared
The ldd dns output than is:
linux-vdso.so.1 => (0x00007fff6ace6000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f557a6f9000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f557a4dd000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f557a151000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f5579f3b000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f5579d37000)
/lib64/ld-linux-x86-64.so.2 (0x00007f557a993000)
Is this now static?
I don't have the same version of Intel MPI so I can't test for sure my solution here. But still, here is how it goes after a few remarks:
Having a fully static binary might not be such a good idea, especially when it comes to the libc... But on a fully homogeneous cluster, why, not.
Normally, just using mpiifort -static should do the trick. However, experience shows that this is rarely the case because of some random functions missing here and there in the static libs, etc.
If only some of the libraries are the ones you want to have statically linked, then there is a solution. If for example this is the Intel libraries you want static, the -intel-static linking option is supposedly doing the tick. But again, unfortunately, most of the time it doesn't work as advertised, notably for the MPI libraries.
So let's assume that what you want is at least packing the MPI libraries into your binary. So this is how I do it:
> mpiifort -c hello_mpi.f90
> ifort -o hello_mpi hello_mpi.o -L$INTEL_MPI_PATH/intel64/lib -Wl,-non_shared,-lmpigf,-lmpi,-lmpigi,-call_shared
The list of libraries to include will depend on the version of the MPI library you use I guess, but you can easily get it with a mpiifort -show.
This works for me. Does it for you?

Linking 'libstdc++' library is broken in embedded linux

I have been working on a project which will be loaded on an embedded system, has not enough memory/disk space to install a C++ compiler and compile the code, natively.
So, I need to “cross-compile” the code on my development (Host) machine to be used on the target machine (Embedded Linux).
The happening problem related to using strings and iostreams which are a feature of the C++ standard template library (STL). However, because memory is so critical in an embedded system, the standard template library (libstdc++), can not be available on target side.
I need to statistically link the standard libraries on host machine, rather than dynamically link on target side. So, in my Makefile I need to have a slightly complex modification in both compiling and linking steps in order to build my project.
I have used some parameters such as -nodefaultlibs, -static-libstdc++, and -lstdc++ in the linking step and also added -nostdinc++ to the compiler flags. Still, there exist the problem on target side; " can not load library libstdc++.so.6 "
Most of these settings I have tried did not work. Is there any solution?
-lstdc++ overrides -static-libstdc++, try linking with just -static-libstdc++.
See this for example:
$ g++ -o foo foo.cpp -static-libstdc++ -lstdc++
$ ldd foo
linux-gate.so.1 => (0x0056b000)
libstdc++.so.6 => /usr/lib/i386-linux-gnu/libstdc++.so.6 (0x007ae000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x00110000)
libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0x005dd000)
/lib/ld-linux.so.2 (0x002bc000)
libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0x0095e000)
libstdc++ is linked dynamically!
$ g++ -o foo foo.cpp -static-libstdc++
$ ldd foo
linux-gate.so.1 => (0x0097b000)
libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0x001f9000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x0037f000)
/lib/ld-linux.so.2 (0x00199000)
now it is not.

Linking libpng with g++

I'm trying to get libpng working on linux. I couldn't get it to work through netbeans, so I ran g++ directly as g++ -lpng -lz main.cpp -o test and it compiles. When I try to run it it it outputs ./test: error while loading shared libraries: libpng14.so.14: cannot open shared object file: No such file or directory. I assume this means I am linking dynamically and it can't find the .so file
~/Programs/NetBeansProjects/DiamondSquare$ ldd test
linux-gate.so.1 => (0x008a5000)
libpng14.so.14 => not found
libz.so.1 => /usr/local/lib/libz.so.1 (0x00209000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x0094b000)
libm.so.6 => /lib/tls/i686/cmov/libm.so.6 (0x00e3a000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00927000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00220000)
/lib/ld-linux.so.2 (0x00b85000)
I didn't really want to link dynamically in the first place. How could I resolve this?
I know that libpng14.so.14 is in /usr/local/lib also, if that matters.
Extra points if you can tell me how to do this within netbeans.
It's odd that g++ is able to find the library but test can not (you can tell that g++ can find it because test specifically expect libpn14 even though you only tell g++ '-lpng'). Are you sure you aren't passing any -L or -R flags to g++? Are your LD_PRELOAD or LD_LIBRARY_PATH environment variables set in the shell you're running g++ in but not in the shell you're running test in? You can point LD_PRELOAD at a specific shared library to tell an app or g++ how to find it, and any folders in LD_LIBRARY_PATH are automatically searched.
Also to link libpng statically put "-Wl,-Bstatic" before "-lpng." Beware, any libraries after the -Bstatic will be linked statically. You can switch back to dynamic and list some more libraries by using "-Wl,-Bdynamic -lfoo".
Dynamic linking is the default and should be preferred in general. You say libpng is in /usr/local/lib, are you really positive about this? It finds /usr/local/lib/libz.so.1. If libpng14.so.14 was in /usr/local/lib, too it should find it.