How can I programatically determine where my C++ runtime libraries are? - c++

I'm building C++11 with GCC 4.8 on CentOS 6 using "hax", then deploying on arbitrary CentOS 6 targets (on which anything C++-related out of the box will have been built as C++03 with GCC 4.3) (ref).
To make this work, I'm going to ship all of my third-party libs as well as the g++ runtimes, and rpath my executables so the newer libs are assuredly found in the proper place. For the runtimes, by my count I need to ship libstdc++ and libgcc_s. But I need to know where they are on my build system so that I can package them up.
Is there some neat way I can query for their location from within my packaging script?
(If the best approach is too awkward I'll just link them statically, but I'd like to avoid that if I can as my project includes several executables. Also if I were to statically link everything I believe I'd run the risk of GPL-ing my whole project, e.g. by statically linking the MySQL C API via my C++ MySQL wrapper lib. Could do a mixture of both, I suppose, though some sources warn against this…)
For bonus points, do I need to add anything to this list, out of libssl, libcrypto, libm, libpthread, libc, librt, libz and ld-linux-x86-64?

If I understand correctly, you have already built your binaries and just want to get a list of runtime libraries to package them along with binaries? You can try using ldd for this, like:
> ldd /usr/bin/ls
linux-vdso.so.1 (0x00007ffe76dd2000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fc97131f000)
libcap.so.2 => /lib64/libcap.so.2 (0x00007fc97111a000)
libacl.so.1 => /lib64/libacl.so.1 (0x00007fc970f10000)
libc.so.6 => /lib64/libc.so.6 (0x00007fc970b68000)
libpcre.so.1 => /usr/lib64/libpcre.so.1 (0x00007fc970902000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fc9706fd000)
/lib64/ld-linux-x86-64.so.2 (0x000055c4ba4ed000)
libattr.so.1 => /lib64/libattr.so.1 (0x00007fc9704f8000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fc9702db000)
This way you will see all libraries needed except, of course, the ones that are used via dlopen().

In my Makefile:
GCC_INSTALL_DIR := $(shell $(CXX) -print-search-dirs | grep install | cut -d' ' -f2)
…then my main build target will perform:
ln -sf $(GCC_INSTALL_DIR)/libstdc++.so $(BIN_DIR)/deps/
…and I can dump everything in $(BIN_DIR)/deps into the right place on install.
I think.

Related

Link libm statically to get equal results on multiple computers

I have a numerical C++ model of thousands of LOC that outputs different results on different machines and I want to fully understand why that is. Those differences start out in like the 14th significant digit but then propagate to produce quite different results.
I've spent many days on this now and I finally came to this point: When I build it on my machine and copy it to machine B, I can get the exact same results on both machines if I compiled it via
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXE_LINKER_FLAGS="-static" -DCMAKE_FIND_LIBRARY_SUFFIXES=".a" ..
So that is already great. But I want to know more specifically what causes the differences. So I checked which libraries are possible culprits:
$ ldd my_software
linux-vdso.so.1 (0x00007ffdbd99c000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f08720b4000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f0871d16000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f0871afe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f087170d000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0872724000)
So my idea was that I would ideally find one library that changes it and then think about what part of this library that is and if I can do something about this.
Only linking a single library statically worked for, e.g., libgcc:
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXE_LINKER_FLAGS="-static-libgcc" ..
But the results were still different. So that was not it. The problem now is, that I seem to not be able to link libc or libm statically:
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXE_LINKER_FLAGS="-static-libm" .. # works
cd ..
cmake --build statically_linked_m/ --target my_software
...
[100%] Linking CXX executable my_software
g++: error: unrecognized command line option ‘-static-libm’; did you mean ‘-static-libgo’?
So I am wondering, why can't I do that? I mean, it apparently got linked statically when I compiled it with the -DCMAKE_EXE_LINKER_FLAGS="-static" -DCMAKE_FIND_LIBRARY_SUFFIXES=".a" flags. So why is this not working?
I hope I made my issue clear. Any pointers are highly appreciated!

Library not linked even though present in link targets

I'm trying to build an application using cmake but the executable is unable to link to the libraries, the ldd of my executable looks like this
linux-vdso.so.1 (0x00007ffd447a0000)
libavcodec.so.58 => /usr/local/ffmpeg/libavcodec.so.58 (0x00007f5673121000)
libavformat.so.58 => /usr/local/ffmpeg/libavformat.so.58 (0x00007f5672cdb000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f5671ffb000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f5671de3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f56719f2000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f5671654000)
/lib64/ld-linux-x86-64.so.2 (0x00007f567629b000)
libswresample.so.3 => not found
libavutil.so.56 => not found
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f5671435000)
libavutil.so.56 => not found
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f5671231000)
while in my make --trace the library is present
/usr/bin/c++ -I/home/arunabh-compute/sensor-drivers -isystem
/usr/local/include -isystem /usr/local/GenICam/include -Wall -O3 -
std=gnu++14 -o CMakeFiles/save_cpp.dir/stdafx.cpp.o -c /home/arunabh-
compute/sensor-drivers/stdafx.cpp
CMakeFiles/save_cpp.dir/build.make:178: update target 'save_cpp' due
to: CMakeFiles/save_cpp.dir/link.txt
CMakeFiles/save_cpp.dir/Cpp_Save.cpp.o
CMakeFiles/save_cpp.dir/stdafx.cpp.o
CMakeFiles/save_cpp.dir/build.make /usr/local/ffmpeg/libavcodec.so
/usr/local/ffmpeg/libavcodec.so.58
/usr/local/ffmpeg/libavcodec.so.58.18.100
/usr/local/ffmpeg/libavdevice.so /usr/local/ffmpeg/libavdevice.so.58
/usr/local/ffmpeg/libavdevice.so.58.3.100
/usr/local/ffmpeg/libavfilter.so /usr/local/ffmpeg/libavfilter.so.7
/usr/local/ffmpeg/libavfilter.so.7.16.100
/usr/local/ffmpeg/libavformat.so /usr/local/ffmpeg/libavformat.so.58
/usr/local/ffmpeg/libavformat.so.58.12.100
/usr/local/ffmpeg/libavutil.so /usr/local/ffmpeg/libavutil.so.56
/usr/local/ffmpeg/libavutil.so.56.14.100
/usr/local/ffmpeg/libswresample.so
/usr/local/ffmpeg/libswresample.so.3
/usr/local/ffmpeg/libswresample.so.3.1.100
/usr/local/ffmpeg/libswscale.so /usr/local/ffmpeg/libswscale.so.5
/usr/local/ffmpeg/libswscale.so.5.1.100
even though libswresample.so.3, libavutil.so.56 is clearly present in the make --trace the application doesn't link to it for some reason, additionally if i put the /usr/local/ffmpeg folder in my LD_LIBRARY_PATH everything starts working just fine. Additionally libavcodec.so.58 is found. Trying to understand what's happening and how to fix it. Thanks.
By linking with these libraries, you marked them as needed in your binary, without recording any paths.
By default, your system only looks for dynamic libraries in a handful of locations.
You have a few options:
Add /usr/local/ffmpeg to a file in /etc/ld.so.conf.d/ (don't forget to call ldconfig). This will tell your dynamic linker (ld.so) to look there.
Add /usr/local/ffmpeg to the LD_LIBRARY_PATH environment variable as you did. You can make this permanent by editing /etc/environment on some machines. This has the same effect, but can be overridden by users. Most games on Linux rely on this to find the bundled libraries, for example, but this does necessitate a launcher script.
Link your binary with -rpath=/usr/local/ffmpeg. This informs the dynamic linker that you would like it to look there too. You can do this in CMake by setting the INSTALL_RPATH target property. You probably want to set BUILD_WITH_INSTALL_RPATH as well, so you do not have to set LD_LIBRARY_PATH during development or set a separate BUILD_RPATH.
Finally, you can just move the ffmpeg libraries to a directory in the default search path. However, this may clash with system libraries and requires root access. This is more a last-resort or convenience-over-everything approach.

CLion can't find shared library when running an executable

I am working on a project. I've been using a simple editor so far and my own Makefile to build it. I would like to switch to CLion, though.
According to this question you can tell CMake to run your Makefile. So my CMake.txt looks like this:
cmake_minimum_required(VERSION 3.6)
project(rekotrans_testbed_simulator)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
add_custom_target(rekotrans_testbed_simulator COMMAND make -C ${rekotrans_testbed_simulator_SOURCE_DIR} CLION_EXE_DIR=${PROJECT_BINARY_DIR})
It builds fine. I also set the working directory and pointed at the correct executable.
In my project I test using cppunit 1.13. However it can't find the shared library:
/home/kunterbunt/dev/comnets/git-repository/rekotrans-testbed-simulator/rekotrans-testbed-simulator-tests: error while loading shared libraries: libcppunit-1.13.so.0: cannot open shared object file: No such file or directory
LD_LIBRARY_PATH points to
echo $LD_LIBRARY_PATH
/usr/local/lib
and /usr/local/lib contains the library:
ls /usr/local/lib/
libcppunit-1.13.so.0# libcppunit-1.13.so.0.0.2* libcppunit.a libcppunit.la* libcppunit.so# pkgconfig/
ldd shows this:
ldd /home/kunterbunt/dev/comnets/git-repository/rekotrans-testbed-simulator/rekotrans-testbed-simulator-tests
linux-vdso.so.1 (0x00007ffc257e8000)
libboost_thread.so.1.63.0 => /usr/lib/libboost_thread.so.1.63.0 (0x00007f1c73254000)
libboost_system.so.1.63.0 => /usr/lib/libboost_system.so.1.63.0 (0x00007f1c73050000)
libboost_date_time.so.1.63.0 => /usr/lib/libboost_date_time.so.1.63.0 (0x00007f1c72e3f000)
libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007f1c72c22000)
libboost_program_options.so.1.63.0 => /usr/lib/libboost_program_options.so.1.63.0 (0x00007f1c729a4000)
libdl.so.2 => /usr/lib/libdl.so.2 (0x00007f1c727a0000)
libcppunit-1.13.so.0 => /usr/local/lib/libcppunit-1.13.so.0 (0x00007f1c72563000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f1c721db000)
libm.so.6 => /usr/lib/libm.so.6 (0x00007f1c71ed7000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007f1c71cc0000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f1c71922000)
librt.so.1 => /usr/lib/librt.so.1 (0x00007f1c7171a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1c7347c000)
So why can't CLion find it? Everything works if I run the binary from console.
Have a look at How to set the environmental variable LD_LIBRARY_PATH in linux:
If you add your custom library path to the LD configuration, then CLion will find your libraries automatically and you don't have to add them to the run configurations.
On Ubuntu/Debian, you can configure LD by creating a new .conf file
sudo nano /etc/ld.so.conf.d/myLocalLibs.conf
that simply contains the path to your libraries: /usr/local/lib. Finally, call
sudo ldconfig
to update the LD configuration.
Note that on some systems (Ubuntu/Debian) you cannot set the LD_LIBRARY_PATH in /etc/profile or /etc/environment:
Since Ubuntu 9.04 Jaunty Jackalope, LD_LIBRARY_PATH cannot be set in
$HOME/.profile, /etc/profile, nor /etc/environment files. You must use
/etc/ld.so.conf.d/*.conf configuration files. See Launchpad bug #366728
for more information.
(help.ubuntu.com)
As oLen correctly pointed out, CLion doesn't seem to start as my user when launched via the GUI (Gnome in my case). I don't know as what it starts, but setting LD_LIBRARY_PATH=/usr/local/lib in /etc/profile and rebooting (or resourcing) it makes it work - in a nutshell the variable wasn't set for whatever user is running CLion.
Another way is Run -> Edit Configurations -> (select your application) -> Environment variables. Here you can manually set LD_LIBRARY_PATH to whatever you need, in my case to /usr/local/lib.
As an alternative to the accepted answer you can go to Run -> Edit Configurations -> Templates, choose CMake Application (and/or Google Test) and set Environment variables: to:
LD_LIBRARY_PATH=/usr/local/gcc-latest/lib64
Any new application created from now on will inherit these settings.

Conflicting boost versions

I have compiled boost::system and boost::serialization from with the older GCC ABI (https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html) due to some older third-party code I'm using.
I had them built to /usr/local/lib which is already set as a valid path to the linker (I use other libraries there as well), and renamed them to:
$ ls /usr/local/lib/libboost_*
/usr/local/lib/libboost_serialization_old_abi.so
/usr/local/lib/libboost_serialization_old_abi.so.1.60.0
/usr/local/lib/libboost_system_old_abi.so
/usr/local/lib/libboost_system_old_abi.so.1.60.0
/usr/local/lib/libboost_wserialization_old_abi.so
/usr/local/lib/libboost_wserialization_old_abi.so.1.60.0
Default, mainstream boost libraries are under /usr/lib as usual. What happens is that when I link any piece of code to these specific libraries with the custom flags -lboost_system_old_abi and -lboost_serialization_old_abi, the resulting binary will be linked to the default boost libraries:
$ ldd darwin_socket
linux-vdso.so.1 (0x00007ffd137ea000)
/usr/local/webots/resources/projects/robots/darwin-op/libraries/darwin/libdarwin.so (0x00007fcb9edaa000)
libipsocket.so.1 => /usr/local/lib/libipsocket.so.1 (0x00007fcb9eb7b000)
libboost_system.so.1.60.0 => /usr/lib/libboost_system.so.1.60.0 (0x00007fcb9e977000)
libboost_serialization.so.1.60.0 => /usr/lib/libboost_serialization.so.1.60.0 (0x00007fcb9e739000)
libController.so => not found
libCppController.so => not found
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007fcb9e3b7000)
libm.so.6 => /usr/lib/libm.so.6 (0x00007fcb9e0b2000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007fcb9de9c000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007fcb9dafb000)
/lib64/ld-linux-x86-64.so.2 (0x00007fcb9efc1000)
librt.so.1 => /usr/lib/librt.so.1 (0x00007fcb9d8f3000)
libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007fcb9d6d6000)
which is quite strange, because, if use original -lboost_system and -lboost_serialization flags, gcc can't even link to the default boost due to new/old ABI incompatibilities.
So what exactly is happening here?
The problem is that just renaming your custom built libraries is not enough. The library name is embedded into the library as soname (you can see it with the readelf -d command) and is used when your application is liked with the library. Basically, the sonames from the custom libraries are put as the dependencies into your application binary, and since they are the same as the official Boost library names, the wrong binaries are loaded in run time.
You have to make sure the custom built Boost libraries are properly named in the build process. You can try doing this by adding --buildid=old_abi option to your b2 command line.

Version numbers in shared object files

I'm building a shared object file from a group of C++ source files using GCC. All the example tutorials on building .so files show the file created with a version number after the .so suffix. For example:
gcc -shared -Wl,-soname,libmean.so.1 -o libmean.so.1.0.1 calc_mean.o
This would produce the .so file libmean.so.1.0.1
Additionally, if I browse the /usr/lib directory on my local machine, I see that many of the .so files have version numbers at the end.
However, when I compile a shared object file and place it in /usr/lib, the linker is unable to find it if I put a version number at the end. If I remove the version number, it works fine. I really don't care about putting a version number or not, I just don't understand why this seems to be a common convention, and yet this causes the shared library to not work with the linker. So, what's going on here? Why is there a convention to place the version number at the end of an .so file name?
The version number is appended so you can have multiple incompatible library versions coexisting in the system. You should increment the major version number (the number in soname) every time you change the API in an incompatible way (assuming the previous version is installed and used in the system, of course).
The 2nd and 3rd numbers in the file name allows for multiple minor revisions of the library in the system, switchable system-wide with a simple symlink update.
At link time you can give the .so file name as a linker argument, instead of -l option. ldd is smart enough to extract the soname from it, the binary linked this way uses it to find the library.
For example, let's compile the library and test binary using it:
czajnik#czajnik:~/z$ gcc -shared -Wl,-soname,libtest.so.2 -o libtest.so.2.3.4 a.c
czajnik#czajnik:~/z$ gcc -o test b.c -L. ./libtest.so.2.3.4
You can use ldd to verify, that the binary now looks for libtest.so.2:
czajnik#czajnik:~/z$ LD_LIBRARY_PATH=. ldd ./test
linux-gate.so.1 => (0x002c1000)
libtest.so.2 => not found
libc.so.6 => /lib/libc.so.6 (0x00446000)
/lib/ld-linux.so.2 (0x00a28000)
It obviously can't find it, but that's what the symlink is for:
czajnik#czajnik:~/z$ ln -s libtest.so.2.3.4 libtest.so.2
czajnik#czajnik:~/z$ LD_LIBRARY_PATH=. ldd ./test
linux-gate.so.1 => (0x00d75000)
libtest.so.2 => ./libtest.so.2 (0x00e31000)
libc.so.6 => /lib/libc.so.6 (0x00a5e000)
/lib/ld-linux.so.2 (0x00378000)
Update: All of the above is true, yet I wasn't myself aware of the meaning of the 3rd component of the version number. Until recently, I believed it is simply a patch number (or similar thing). Wrong! For libtool it has a special meaning.
The 3rd component turned out to be age field, which says how many major versions are backward compatible with the current one.
Recommended reading:
Idiot's Guide to ABI Versioning
Libtool's versioning system