How to link shared libraries in custom path directly without specifying RPATH? - c++

I am on Ubuntu 16.04, and I am required to use an external library (MCR). It puts all of it's shared libraries inside the MATLAB/bin/glnxa64/ folder. I only need the libmx.so in there but there are libraries in there that has the exact same name as the ones in /usr/lib but they are actually different (because the file size is different), for example libtiff.so.5.
This becomes a problem because when I use the find_library function in CMake to add MATLAB/bin/glnxa64/ into RPATH in order to link to libmx.so for my application, since my application also depends on another external library (OpenCV) that was linked to the libtiff.so.5 in /usr/lib when it was built, when I compile my application it shows an compilation error of
/usr/lib/libopencv_imgcodecs.so.3.3.0: undefined reference to
`TIFFReadDirectory#LIBTIFF_4.0'
It is because my application is trying to link to the libtiff.so.5 in MATLAB/bin/glnxa64/ instead of /usr/lib because RPATH has a higher priority than the default directories. What is the best way to solve this?
I tried renaming the libtiff.so.5 in MATLAB/bin/glnxa64/ to something like libtiff_old.so.5. This solves it but is very ugly.
Is there anyway I can alternate the search priority so RPATH goes after the default directories? Or is there something I can do in CMake so my application can directly link to libmx.so without having to add MATLAB/bin/glnxa64/ into RPATH to mess things up?

Well, first it would be good to see your cmake file, but alas..
You can try using this, the <...> is your base directory for the MATLAB install, which might be /usr/local/ or /opt.
find_library (MATLAB_RUNTIME libmx
PATHS <...>/MATLAB/bin/glnxa64/
NO_DEFAULT_PATH )

Related

Load shared libraries from lib directory in CMake?

I have downloaded a ROS2 demo from the examples repository.
Specifically, I have used minimal_subscriber and minimal_publisher.
I have ROS2 installed in /opt/ros2 and when I build these two examples with colcon build, it generates an install/ directory with lib/, shared/ and the other usual directory structure.
I can execute and run these demos perfectly fine with my current setup in my machine, but these executables link to libraries present in /opt/ros2, so when I want to execute them in another machine without ROS2 installed or I move my ROS2 installation in my machine, the executables cannot find the shared objects.
I've added a really simple script that adds all dependencies to install/lib when building but the executables don't seem to care, they aren't looking for shared libraries in the generated lib directory, they keep trying to search in /opt/ros2.
I believe this is something I should solve in CMake and it's not ROS2 specific, so, is there any way I can tell my generated executables to search in a diferent directory? (../lib or ./lib in my case)
If you are building them yourself (assumed since you mention CMake), you can set the RPATH in CMake (docs here). Specifically set the CMAKE_INSTALL_RPATH something like:
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
If you can't rebuild them, you can set LD_LIBRARY_PATH in your environment to where the libraries are located, or you can patch the executables themselves with an updated RPATH by using patchelf.
In order to get a relative RPATH rather than an absolute RPATH, use the $ORIGIN variable in your rpath spec. See "Recommendations" the the link above for more details.

How to let Cmake Install to link my executables to my target shared libraries [duplicate]

I have a question related to how to install a built executable program with cmake when it relies on some external libraries. Suppose my executable is abc, and it relies on two external libraries: lib1.so and lib2.so. The structure of the codes are as follows:
-.........
|----bin (lib1.so lib2.so)
|----include(lib1.h lib2.h)
|----src(main.cpp)
When the executable program is installed using the following cmake commands:
INSTALL(TARGETS ${Exe_Name}
RUNTIME DESTINATION Path to bin
LIBRARY DESTINATION Path to bin)
I expect that the executable program will be in the same directory with lib1.so and lib2.so. However, when I execute the built program in the installation folder, I met the following error:
error while loading shared libraries: lib1 can not open shared object file No such file or directory
If I use ldd to check the executable, I found lib1.so and lib2.so not found. After searching for possible solutions, I found if I call the executable in this way, then it worked:
LD_LIBRARY_PATH=./ ./my_program_run
Then my question is how I can let my executable program knows the locations of the shared libraries with cmake when it is installed? Thanks.
This is best solved this with the RPATH of the final executable. RPATH is a hardcoded search path for the executable itself, and allows the use of the string $ORIGIN, which expands to the location of the executable at runtime. See this reference: http://man7.org/linux/man-pages/man8/ld.so.8.html
CMake strips the rpath of a binary at installation time, to avoid the binary picking up libraries littered around your development tree. But it also provides a simple way to modify the installation rpath for exactly this reason. Here's the short answer:
IF(UNIX)
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH}:\$ORIGIN/../bin:\$ORIGIN")
ENDIF()
This particular example appends to the existing rpath, and adds . and ../bin to the search path, all relative to the location of the binary.
Some developers claim that adjusting the RPATH of the binary is not a good idea. In the ideal world, all the libraries would live in the system library directories. But if you take this to the extreme, you end up with Windows (at least the older ones), where c:\windows\system32 is full of junk that came from who knows where, and may or may not conflict with other software, etc. Using rpath and installing everything in one place seems like a great solution.
If the application is to be cleanly installed to a standard linux distribution, then you should either install the supporting shared libraries into a standard location (/usr/lib), or you should add the libraries location to the ld.so config, by create an /etc/ld.so.conf.d/myprogram.conf file containing the name of the directory the libraries are in.
If the installation is temporary or more ad-hoc, then a script to set the LD_LIBRARY_PATH is suitable.
The libraries are searched in the predefined locations which includes standard library paths configured with ld.so.conf and LD_LIBRARY_PATH. You can also try to compile your app with -rpath, but it is not recommended by some developers. I suggest you to create a wrapper script which will set LD_LIBRARY_PATH and run the real application like that:
"theapp" script:
#!/bin/sh
dir="`dirname \"$0\"`"
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}"$dir"
exec "$dir/theapp.real" # your real application
The application, script and libraries should be in the same location (under bin/).

CMake links against wrong version of library

I have some CMake packages that depend on Protobuf v2.5, and one that depend on Protobuf v3.4. I have v2.5 installed system-wide in /usr, whereas v3.4 is only used in a single package. Therefore, I put the headers for v3.4 in a 3rdparty subdirectory inside the package where it is being used, and then I call include_directories(3rdparty) in my CMakeLists.txt so it can be found.
As for the shared libraries, the .so files for v2.5 are present in /usr/lib/x86_64-linux-gnu, and I installed the .so files for v3.4 to /usr/lib. In short, this is what the directory structure looks like:
v2.5:
headers: /usr/include
libraries: /usr/lib/x86_64-linux-gnu
v3.4:
headers: <MY_PACKAGE_SRC_DIRECTORY>/3rdparty
libraries: /usr/lib
Now, the problem arises when I try to link against v3.4. To simplify things, I don't use any CMake module files to find protobuf v3.4, rather I just add a hard-coded path /usr/lib/libprotobuf.so to the list of libraries to link against when creating a target. But even so, when I run ldd my_target_executable, the result is:
libprotobuf.so.8 => /usr/lib/x86_64-linux-gnu/libprotobuf.so.8
meaning that it is linking against the libraries for v2.5 in /usr/lib/x86_64-linux-gnu, even though I added a hard-coded path to the correct .so file in /usr/lib in the call to target_link_libraries when building this executable.
What is worth noting is that if I remove the .so files in /usr/lib/x86_64-linux-gnu, then it does link against the correct .so file in /usr/lib, so it appears that for some reason, CMake searches in /usr/lib/x86_64-linux-gnu before using the library path that I provide it. How can I change this behavior, or fix this problem in any other way?
UPDATE
The library file for v3.4 /usr/lib/x86_64-linux-gnu/libprotobuf.so is a link to /usr/lib/x86_64-linux-gnu/libprotobuf.so.14 which in turn is a link to the actual file /usr/lib/x86_64-linux-gnu/libprotobuf.so.14.0.0.
Now, if I change the hard-coded path that I give in target_link_libraries from /usr/lib/x86_64-linux-gnu/libprotobuf.so to either the second symlink /usr/lib/x86_64-linux-gnu/libprotobuf.so.14, or to the actual file /usr/lib/x86_64-linux-gnu/libprotobuf.so.14.0.0, then my executable correctly links againt v3.4. It appears that the name of the provided symlink has some effect on CMake's behavior.
This isn't cmake specifically but also how things work on Linux with gcc and shared libraries.
http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html
When you specify target_link_libraries( target /usr/lib/x86_64-linux-gnu/libprotobuf.so) this sets up linking as -lprotobuf. In this case it should just use any version of the library it finds first.
target_link_libraries( target /usr/lib/x86_64-linux-gnu/libprotobuf.so.14) adjusts the cmake generated link line to use a specific library version. This seems to tell gcc to link against that version which will change what happens at run-time and library searches.
target_link_libraries
There are some cases where CMake may ask the linker to search for the library (e.g. /usr/lib/libfoo.so becomes -lfoo), such as when a shared library is detected to have no SONAME field. See policy CMP0060 for discussion of another case.

Preventing CMake from finding installed libraries instead of "local" libraries

I'm working on multiple projects at the same time (some libraries, and some games that depend on them). They're all on GitHub, in separate repos.
For convenience, I pull every repo in a "workspace folder", like this:
/home/myWorkspace/Library1/
/home/myWorkspace/Library2/
/home/myWorkspace/Library3/
/home/myWorkspace/App1/
/home/myWorkspace/App2/
I have FindLibrary1, FindLibrary2 and FindLibrary3 .cmake files, which, in order, look for the library in ../ (which corresponds to the "myWorkspace" folder), then /usr/lib/.
While on Windows CMake finds the libraries in myWorkspace/, on Linux, no matter what, installed libraries are always found first.
Since I'd like to work in myWorkspace/ folder, then installing the libraries after I'm done, I'd prefer CMake to find and link everything within the myWorkspace/ folder.
I'd also like CMake to search for the libraries in /usr/lib/ and /usr/local/lib/ if there is no myWorkspace/ folder, but if myWorkspace/ exists, it should have the priority.
Examples of CMake files I'm using:
SSVUtils: library with no dependancies
SSVUtils CMakeLists: https://github.com/SuperV1234/SSVUtils/blob/master/CMakeLists.txt
FindSSVUtils.cmake: https://github.com/SuperV1234/SSVUtils/blob/master/cmake/modules/FindSSVUtils.cmake
SSVUtilsJson: library that depends on SSVUtils and SSVJsonCpp
SSVUtilsJson CMakeLists: https://github.com/SuperV1234/SSVUtilsJson/blob/master/CMakeLists.txt
FindSSVUtilsJson.cmake: https://github.com/SuperV1234/SSVUtilsJson/blob/master/cmake/modules/FindSSVUtilsJson.cmake
Any ideas how I can prioritize the myWorkspace/ folder while still having the possibility to find libs in file system paths?
By default, find_path (or find_library, etc.) first checks for files in the system standard locations, before searching in the values provided in PATH. That's why "installed" libraries are always found first on Linux (but not on Windows, that doesn't have standard locations for installed libraries).
You can disable that behavior by using the NO_CMAKE_SYSTEM_PATH option: it will skip detection of files in standard locations.
Now... if you still want to use the installed libraries as a fallback when local versions are not found, you can do it in a two step process:
find_path(... NO_CMAKE_SYSTEM_PATH)
if (nothing_found)
find_path(...)
endif()

Error loading shared libraries of boost

I am working on centos. I installed boost version 1.45.0 on my system. The programs are compiled correctly but whenever I type command to see output it gives following error:
./a.out: error while loading shared libraries:
libboost_thread.so.1.45.0: cannot open shared object file: No such
file or directory
In addition to the other answers, you can also set the DT_RPATH elf tag when linking your executable
-Wl,-rpath,/path/to/boost/libraries -L /path/to/boost/libraries -lboost_whatever
This way you don't have to remember to set your LD_LIBRARY_PATH if the libraries are installed in a non-standard location.
How did you install the boost libraries?
The problem you're likely having is that the linker can not find the libraries, and when you built your program, you had to manually specify additional library paths to search for libraries.
A quick fix you can do is to set LD_LIBRARY_PATH to include the directory where the boost thread library is:
export LD_LIBRARY_PATH=/path/to/boost/libs:$LD_LIBRARY_PATH
./runExecutable
You need to set the LD_LIBRARY_PATH environment variable to include the path to the Boost libraries (they're possibly in /usr/local/lib, etc).
In bash, this is simply
export LD_LIBRARY_PATH=/path/to/boost:$LD_LIBRARY_PATH