CMake project fails to find shared library - c++

I am using CMake to build a cross platform project. For the moment I am trying to run it on Linux. I have recently added a project for running tests, but it will not run because it cannot find one of the shared libraries, specifically libtbbmalloc.so.2:
/tests: error while loading shared libraries: libtbbmalloc.so.2: cannot open shared object file: No such file or directory`
When I run ldd on the executable I get the following:
linux-vdso.so.1 (0x00007fffeb572000)
libtbbmalloc_proxy.so.2 => /home/username/dev/tbb/libtbbmalloc_proxy.so.2 (0x00007f50afe00000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f50afa70000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f50af6d0000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f50af4b0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f50af0a0000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f50aee90000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f50aec70000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f50aea60000)
/lib64/ld-linux-x86-64.so.2 (0x00007f50b0400000)
libtbbmalloc.so.2 => not found
The CMakeLists.txt for my test project looks like this:
set(test_sourcefiles main_tests.cpp)
add_executable(tests ${test_sourcefiles})
target_link_libraries(tests Catch2::Catch2 MyLib)
MyLib uses tbb, and I guess that is why my executable (tests) searches for it. When running ldd on MyLib it finds the library (libtbbmalloc.so.2):
(removed some output for readability)
libtbbmalloc_proxy.so.2 => /home/username/dev/tbb/libtbbmalloc_proxy.so.2 (0x00007f9af8110000)
libtbbmalloc.so.2 => /home/username/dev/tbb/libtbbmalloc.so.2 (0x00007f9ac4eb0000)
I have tried specifically adding libttbbmalloc.so.2 in my tests/CMakeLists.txt target_link_libraries(${project} /home/username/dev/tbb/libtbbmalloc.so.2), but it makes no difference.
If I add /home/username/dev/tbb/ to LD_LIBRARY_PATH, the program runs, and ldd reports that libtbbmalloc.so.2 is found.
Any ideas on what I might be doing wrong, and how can I get my program to run without setting LD_LIBRARY_PATH?
Update: I found out that it's possible to print runpath/rpath by using chrpath -l name-of-executable. When using this tool on my executable, it looks like the folder with libtbbmalloc.so.2 is added to runpath, but the program still won't run:
larjr#DESKTOP:~/dev/project/build/tests$ chrpath -l tests
tests: RUNPATH=/home/larsjr/dev/project/build/MyLib:/home/username/dev/tbb

The issue you are encountering does sound to be related to the search path for direct and indirect linkage of shared libraries. CMake may build with the new ld RUNPATH variable which will only work on direct dependents, where RPATH will work recursively on all indirect dependents. As shown in the snippet at bottom of this post, this may depend on your version of GCC. To check if your executable is using RPATH or RUNPATH, run:
$ readelf -d ./executable
and find the library rpath / runpath line after your dependencies. ie/eg: It may show either of the below:
a) 0x000000000000001d (RUNPATH) Library runpath: [/some/dir/lib]
b) 0x000000000000001d (RPATH) Library rpath: [/some/dir/lib]
You showed you used the command chrpath -l ./executable, which actually spat out:
larjr#DESKTOP:~/dev/project/build/tests$ chrpath -l tests
tests: RUNPATH=/home/larsjr/dev/project/build/MyLib:/home/username/dev/tbb
It says here "RUNPATH" which is non-recursive - that's your error.
Refer to this excellent github issue where members are discussing indirect linking issues with CMake, highly suggest reading it all. It has a a great example regarding dependencies and their search paths.
https://github.com/conan-io/conan/issues/2660
A snippet regarding new vs old linker behavior which can cause this 'issue':
The problem you are experiencing in conan-deptest (excellent isolated case, by the way!) is the behaviour that you are documenting, between DT_RPATH and DT_RUNPATH.
It is CMake (and not conan), that passes the -rpath flag with the correct paths to the dependent libraries, when building the executable.
The problem is that newer versions of the linker in some platforms, emit DT_RUNPATH where they used to emit DT_RPATH. Indeed this is the case as readelf -d is listing the RUNPATH tag.
There is also another stackoverflow post where a user asks about this change in behaviour in LD/GCC:
How to set RPATH and RUNPATH with GCC/LD?
A conan member further elaborates on how to change this behavior:
The way to replicate this behaviour on versions of the linker that emit RUNPATH instead, is to pass the --disable-new-dtags flag to the linker.
And the resulting cmake:
target_link_libraries(target "-Wl,--disable-new-dtags")
Now if you rebuild, and check readelf -d on your executable, you should see the RPATH tag. You can run:
env LD_DEBUG=files,libs ./executable
to see RPATH being passed into the search path for each dependency:
31658: file=libbar.so.1 [0]; needed by /home/user/projectA/lib/libfoo.so.2.5 [0]
31658: find library=libbar.so.1 [0]; searching
31658: search path=/home/user/projectA/lib (RPATH from file ./executable)
31658: trying file=/home/user/projectA/lib/libbar.so.1
Hopefully this fixes your issue!
Bonus:
Link to tutorial on shared libraries. At the bottom of the aritcle shows runpath vs rpath + origin!
https://amir.rachum.com/blog/2016/09/17/shared-libraries/

I believe you are looking for setting a runtime path on the executable:
set_target_properties(tests PROPERTIES
INSTALL_RPATH "<your path to libs>"
BUILD_WITH_INSTALL_RPATH 1
)
INSTALL_RPATH
Normally CMake uses the build tree for the RPATH when building executables etc on systems that use RPATH. When the software is installed the executables etc are relinked by CMake to have the install RPATH. If this variable is set to true then the software is always built with the install path for the RPATH and does not need to be relinked when installed.
BUILD_WITH_INSTALL_RPATH
Normally CMake uses the build tree for the RPATH when building executables etc on systems that use RPATH. When the software is installed the executables etc are relinked by CMake to have the install RPATH. If this variable is set to true then the software is always built with the install path for the RPATH and does not need to be relinked when installed.

add set_target_properties next to your executable install command
# install
INSTALL(TARGETS ${PROJECT_NAME} myLib
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
set_target_properties(${PROJECT_NAME} PROPERTIES
INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib
BUILD_WITH_INSTALL_RPATH ON
)

Related

Rpath/Runpath handle with dependent shared libraries in CMAKE

I will put you in context:
I have 2 third party shared libraries: libA.so and libB.so. My program contains only calls to libA.so symbols. The libA.so internaly needs to call to libB.so. If I readelf LibA.so it has a RunPath pointing to a path from the third party developer that doesn't exsist in my system.
My program builds with cmake. I use find_library(A) and find_libary(B) and then add them to my program executable target with target_link_libraries(my_executable PUBLIC A B)
With this setup the generated executable contains a correct RUNPATH for both libA.so and libB.so. The problem is that when running the executable the libB.so file is not found.
I need to keep the .so files in the place they are. Setting up LD_LIBRARY_PATH env variable or moving to a ldd valid folder is not an option. I have tried these solutions and they work btw.
How could I make the executable to find libB.so in this case.
Thanks in advance.
You can change RPATH with a tool named patchelf:
sudo apt install patchelf
patchelf --set-rpath \$ORIGIN libMyLibrary.so
Note that $ORIGIN here means "search in the folder where this library located". You can also combine values like this: \$ORIGIN:\$ORIGIN/../lib. This is convenient if you want to keep unix-like installation structure (executables in bin, libraries in lib).

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.

Shipping libstdc++.so.6 with application

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.

Compiling with -static-libgcc -static-libstdc++ still results in dynamic dependency on libc.so

I'm trying to make an executable that's as portable as possible. After removing a few dependencies, I came across the following when running the binary on another system:
/lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.15' not found (required by foob)
/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.15' not found (required by foob)
/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.14' not found (required by foob)
I'd prefer my binary not to require the user to upgrade their version of libc, so I'd like to remove this dependency as well.
The linker flags that produced the above binary already included -static-libgcc -static-libstdc++. How come the binary still requires on the shared libc.so.6?
I tried adding the -static flag as well, however when I try to run that binary the result is very strange:
$ ls -l foob
-rwxr-xr-x 1 claudiu claudiu 13278191 Oct 10 13:03 foob
$ ./foob
bash: ./foob: No such file or directory
What to do?
EDIT:
$ file foob
foob: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=5adee9a598b9261a29f1c7b0ffdadcfc72197cd7, not stripped
$ strace -f ./foob
execve("./foob", ["./foob"], [/* 64 vars */]) = -1 ENOENT (No such file or directory)
write(2, "strace: exec: No such file or di"..., 40strace: exec: No such file or directory
) = 40
exit_group(1) = ?
+++ exited with 1 +++
Interestingly, if I ldd the version without -static, it has two less entries than the version with -static, namely:
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f4f420c1000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f4f41636000)
GNU libc is not designed to be statically linked. Important functions, e.g. gethostbyname and iconv, will malfunction or not work at all in a static binary. Arguably even worse, under some conditions a static binary will attempt to dynamically open and use libc.so.6, even though the whole point of static linkage is to avoid such dependencies.
You should compile your program against uClibc or musl libc instead.
(This has been true for at least 15 years.)
First be aware that static linking of libc might not improve portability of your program, as libc might depend on other parts of your system e.g. kernel version.
If you want to try complete static linking just using -static should the trick. Provided that there are static versions of all used libraries installed.
You can check if your program has only linked static libraries by using:
ldd binary_name
EDIT:
Another way that provides useful information for debugging this problem would be to add --verbose to your linker flags.

Simple shared library

Is the STD library a shared library or what is it ? out of curiosity .
Are there any books describe in detail the shared , static libraries development ?
Are there any tutorial ?
p.s (i'm using netbeans , eclipse, anjuta) and the tutorials aren't useful as I'm trying to understand what's actually going on.
On my platform (Ubuntu Maverick) it is:
g++ test.cpp
ldd a.out
linux-vdso.so.1 => (0x00007fffee1ff000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f18755fd000)
libm.so.6 => /lib/libm.so.6 (0x00007f187537a000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00007f1875163000)
libc.so.6 => /lib/libc.so.6 (0x00007f1874de0000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1875920000)
Note libstdc++.so.6 above.
With cmake creating a shared library is very easy.
1.
Install cmake 2.6 or later.
2.
Create a file test.cpp with the code for your library.
3.
Create a file CMakeLists.txt:
cmake_minimum_required(VERSION 2.6)
project(TEST)
add_library(test SHARED test.cpp)
4.
Run cmake to create a makefile:
cmake -G "Unix Makefiles"
5.
Run make to build your shared library.
With CMake you can also generate an Eclipse CDT project using the following command
cmake -G "Eclipse CDT4 - Unix Makefiles"
You can also find an interesting article on the topic with further references here.
1.) Is the STD library a shared library or what is it?
I have no idea. Could be either. Probably both. Does it matter? Unless you are dealing with something really exotic like a stand-alone statically linked binary for system rebuilding, as long as the compiler/system knows how to link it in, you are unlikely to be concerned with it.
In a nutshell, code can be in static libraries, in which case it's linked into the final (compiled/generated) executable and those binaries can become quite large. Or it can be in a shared library, in which case the library is dynamically loaded and multiple applications can (theoretically) share one common memory image. Unless you are doing something that is quite large, and that will be shared across multiple applications, I'd question the wisdom of going with shared libraries. The additional headaches, especially debugging headaches, are rarely worth it. And without multiple concurrently running applications, there's no savings...
To make a static library, I'd compile a bunch of files into object files... Than use ar and randlib. E.g.:
g++ -c foo1.C -o foo1.o
g++ -c foo2.C -o foo2.o
ar -rv libfoo.a foo1.o foo2.o
ranlib libfoo.a
Subsequently, I'd just link that library in:
g++ testfoo.C -o testfoo -L. -lfoo
Note that if you are using multiple libraries, the ordering of -lbar1 -lbar2 on that (g++ testfoo.C) command line is important! It determines which libraries can call functions/methods in other libraries. Circular dependencies are BAD!
With respect to foo1.o foo2.o files to ar, the ordering makes no difference.
Dynamic libraries...
Some time ago, under an ancient fedora core 3 system, I was playing around with shared libraries under linux. Back then, I would compile my shared library, say fooLibrary.c, with:
g++ -shared -Wl,-soname,libfooLibrary.so.1 -o libfooLibrary.so.1.0 -fPIC fooLibrary.c -ldl
At that time I was playing with LD_PRELOAD, so I had a little script to run my program that did:
export LD_PRELOAD=libfooLibrary.so ; export LD_LIBRARY_PATH=. ; ./myTestProgram
(Note that I did NOT want LD_PRELOAD set when running commands like g++, ls, cd, etc as I was intercepting system calls.)
(FYI: strace is also fun to play with... You should also check out ldd and nm.)
You may want to look at things like dlopen() and dlsym() -- for manually accessing dynamic libraries...
Oh, and the environment variable LD_LIBRARY_PATH adds directories to the default searchpath for dynamic libraries...
(With respect to debugging, let me just mention that when I intercepted malloc(), I found that somewhere inside dlopen()/dlsym() were calls to malloc(). Meaning that I needed to use malloc() before I could manually load the library that provided the real malloc(). Fun times debugging that one...)
PS One more thought: You may want to review the command-line options to gcc/g++. There's a lot of useful info in there...
http://gcc.gnu.org/onlinedocs/gcc-4.5.1/gcc/index.html#toc_Invoking-GCC