Minimizing dependencies for Linux shared library - c++

I am writing a shared library that I am deploying under Windows, Linux and Mac. On the Linux side, I am attempting to make sure that my library has as few dependencies as possible. I don't want the end developer to have to worry at all about what my library uses internally, and in particular I don't want to force them to install anything.
When I run ldd on my library at the moment, I see:
linux-gate.so.1 => (0xf57fe000)
libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xb773d000)
libstdc++.so.6 => /usr/lib/i386-linux-gnu/libstdc++.so.6 (0xb7654000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb74a1000)
/lib/ld-linux.so.2 (0xb7782000)
libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xb745d000)
libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0xb7440000)
This looks fairly reasonable to me, but some of these libraries I am not really sure what they are. Can anyone tell me whether this list of dependencies is reasonable, or whether I can get rid of some of these? With this list of dependencies, will my library run on a wide array of Linux configurations and distros? That is what I am aiming for, maximum portability.
When compiling, I am specifying the flag -static-libgcc. Are there any more flags I can specify to link in the C++ standard library as well, for example? Internally my library uses std::thread in C++11, but I don't want to force the application writer to necessarily have that available (if they are using an older version of GCC for instance).
Update:
I am now specifying -static-libstdc++, in addition to -static-libgcc. My dependency list now looks as follows:
linux-gate.so.1 => (0xf57fe000)
libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xb7737000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7584000)
/lib/ld-linux.so.2 (0xb77a2000)
The only ones that cause me concern are libc.so.6, and linux-gate.so.1. I don't know what these are. Are they old, and if so have they remained backwards compatible for a long time? If so I will just keep them linking dynamically, but otherwise I have to continue investigating. Any tips would be appreciated.

linux-gate.so.1 is a virtual DSO, meaning it doesn't really exist. The best way to explain it is just to read this link Here.
To answer your question, I think your best choice is to continue linking dynamically to both of them and do a few different builds to target different distros. I have found that typically when you build for Ubuntu, it will work on several of the Debian linux systems.

Related

version-independent dynamic linking with boost libraries

question
How can I compile a shared library linking with version-independent boost shared library?
My cmakelists.txt is like as follows
find_package(Boost REQUIRED COMPONENTS serialization)
...
target_link_libraries(_omplpy PRIVATE ${Boost_LIBRARIES} ${otherdeps})
And, after compiling this, I checked dependency by ldd command and it shows that only the dependency of boost libraries are too specific (seems that version 1.71.0 is specified, though other libraries does not care about the minor version)
h-ishida#stone-jsk:~/python/ompl-python-thin$ ldd build/_omplpy.cpython-38-x86_64-linux-gnu.so
linux-vdso.so.1 (0x00007ffd34ca9000)
libboost_serialization.so.1.71.0 => /lib/x86_64-linux-gnu/libboost_serialization.so.1.71.0 (0x00007f208012f000)
libboost_filesystem.so.1.71.0 => /lib/x86_64-linux-gnu/libboost_filesystem.so.1.71.0 (0x00007f2080111000)
distir libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f20800ee000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f207ff0c000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f207fdbd000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f207fda0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f207fbae000)
/lib64/ld-linux-x86-64.so.2 (0x00007f20812a6000)
The problem is, boost libraries version are different for different ubuntu distributions, thus my compiled shard library _omplpy cannot be used in different distribution.
context (maybe unrelated)
I am trying to distribute a python package where a shared library linked with boost stuff is inside. Because python wheel (binary version of package) is only python-version (like 2.7, 3.8), os (mac, windows, ldistirinux), and archtecture dependent (like x86_64, aarch64), it seems impossible to distribute packages dependent on specific ubuntu distribution. For your information, the package mentioned is https://github.com/HiroIshida/ompl-thin-python and corresponding CMakeLists.txt is here https://github.com/HiroIshida/ompl-thin-python/blob/master/CMakeLists.txt
How can I compile a shared library linking with version-independent boost shared library?
You can't, because the different boost libraries are not ABI-compatible. If you somehow succeeded in linking your library with "version-independent boost", the result would be a runtime crash.
Read about the reasons for external library versioning here.
P.S.
I decided to build static boost library with -fPIC option and compile the target library with linking that.
That is much easier, but unless you are very careful with symbol hiding, this approach will cause runtime crashes (if you are lucky) as soon as someone tries to use your python package in program which uses a different version of boost libraries (due to symbol collision and ABI incompatibility). And if you are unlucky, symbol collision may cause other very hard to find bugs.

ldd shows that ELF interpreter is present but I still get a "No such file or directory"

Recently I struggled porting an existing C++ application onto a new production environment (updated kernel, updated glibc, etc). Even though the output of ldd showed that all my .so were found, including the ELF interpreter, the execution always resulted on a "No such file or directory".
$ ldd my_app
libpthread.so.0 => /lib/libpthread.so.0 (0x00007fd41afd6000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007fd41ae52000)
libm.so.6 => /lib/libm.so.6 (0x00007fd41ad11000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00007fd41acf7000)
libc.so.6 => /lib/libc.so.6 (0x00007fd41ab3a000)
/lib64/ld-linux-x86-64.so.2 => /lib/ld-linux-x86-64.so.2 (0x00007fd4258b5000) <= ELF Interpreter
libconfig.so.9 => /usr/lib/libconfig.so.9 (0x00007fd41ab2c000)
libsensors.so.4 => /usr/lib/libsensors.so.4 (0x00007fd41ab1b000)
$ file my_app
ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=c8c7eeb2f6bdb96dab7b0cc9ad41aa6e3d610ec7, stripped
$ readelf -a my_app | grep "interpreter"
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
I didn't think my problem had anything to do with the interpreter because of this output of ldd. So I tried all other possible workarounds with no success. After much digging online I encountered a related problem that the solution suggested the creation of a symlink from the application's hard-coded path for the interpreter to the real path one. In my case:
ln -s /lib/ld-2.29.so /lib64/ld-linux-x86-64.so.2
This finally resolved the problem. At the end I was frustrated since I wasn't looking for the real problem and it took me too long to realize it because of the misleading output of ldd.
Why does ldd say that the interpreter is there even though it will not be loaded when you run the binary? Is my interpretation of the use/output of ldd mistaken?
onto a new production environment
Your new production environment installed ld-linux-x86-64.so.2 into /lib instead of /lib64. This is highly unusual and non-standard. Many binaries will fail to run on such a system. Whoever "made" this system, probably made a grave mistake.
Why does ldd say that the interpreter is there
The way ldd works is: it sets an environment variable LD_TRACE_OBJECTS=1 and invokes the real interpreter it was compiled with.
Since your system (erroneously) installed ld-linux into /lib, ldd dutifully invoked /lib/ld-linux-x86-64.so.2, which does exist, and actually told you so.
In general, ldd is not trustworthy, especially when several versions of GLIBC are installed on a single system.
In glibc, the dynamic loader ld.so. is both used as the program interpreter and as a loaded shared object. ldd only displays the latter. It can find ld.so because it is either on the search path or in the ld.so cache. In contrast, when the kernel tries to load the program interpreter, it only uses the exact path specified in the program, so search paths and the like do not apply.
The correct path according to the ABI manual is /lib64/ld-linux-x86-64.so.2. If your system does not have that, it does not follow the GNU/Linux x86-64 ABI, which is rather odd.

How to configure libstdc++ with GCC 4.8?

A while back, I decided to upgrade to GCC 4.8 in order to get an early start on some c++11 features. I got a bit sidetracked, though, and didn't really put any of the new features to use until a project a few days ago (the new compiler seemed to have been working fine, but it may just be because I wasn't utilizing any new functionality.)
In this new project, when I compiled with the =std=c++11 flag, I had no problems. However, at runtime, I get the error:
./main: /usr/lib/i386-linux-gnu/libstdc++.so.6: versionGLIBCXX_3.4.18' not found (required by ./main)`
I assume that there is a problem linking to a more modern libstdc++ library associated with GCC 4.8, but I can't for the life of me figure out how to fix this or where the appropriate library should be. I remember symbolically linking the g++ and gcc binaries to gcc-4.8, which appears to be working, since g++ -v returns:
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/app/gcc/4.8.0/libexec/gcc/i686-pc-linux-gnu/4.8.0/lto-wrapper
Target: i686-pc-linux-gnu
Configured with: ./gcc-4.8.0/configure --prefix=/app/gcc/4.8.0
Thread model: posix
gcc version 4.8.0 (GCC)
Another thread online led me to look at the ldd output for the program, which did show me that the directory structure for the libstdc++ libraries being linked to was different than the directory structure for the binaries. I couldn't, however, find the appropriate libstdc++ libraries in the latter, so I'm not sure where to look. The output for ldd main is:
./main: /usr/lib/i386-linux-gnu/libstdc++.so.6: versionGLIBCXX_3.4.18' not found (required by ./main)
linux-gate.so.1 => (0xb7791000)
libstdc++.so.6 => /usr/lib/i386-linux-gnu/libstdc++.so.6 (0xb768e000)
libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xb7662000)
libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0xb7644000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb749b000)
/lib/ld-linux.so.2 (0xb7792000)`
I'm not sure exactly where this is going wrong, and I'll continue Googling and looking around for answers, but any help you guys could offer would be greatly appreciated. If anything is unclear about the issue or I forgot some information, just let me know and I'll try to add that in. Thanks so much!
You need to tell your dynamic linker (it's executed when you run your program) where to find the library. Set LD_LIBRARY_PATH to the path of the library (probably somewhere under /app/gcc/4.8.0/lib or something).
Use find /app/gcc/4.8.0 -name "libstdc++.so.6". Add the directory to your LD_LIBRARY_PATH. e.g with the path I mentioned:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/app/gcc/4.8.0/lib (if you're using a bourne-like shell which the default on Linux).
Then try to run your program.
If it works, you'll probably want to configure your dynamic linker to look in the directory without using LD_LIBRARY_PATH. See man ld.so for details about how to configure the path.

My friend can't run my openGL program on his linux distro

I'm programming an opengl application on ubuntu and it's compiling and running fine. But when I send my program (yes the one compiled in release mode) to my friend who I think is running plain debian the program wont work for him. He told me he was missing some libraries. So I'm wondering: If I'm sending him the libraries I'm using (the .so file) to him with the program, will he be able to run it?
I ran lddtree and here is the output:
game => ./game (interpreter => /lib/ld-linux.so.2)
libGL.so.1 => /usr/lib/libGL.so.1
libnvidia-tls.so.280.13 => /usr/lib/libnvidia-tls.so.280.13
libnvidia-glcore.so.280.13 => /usr/lib/libnvidia-glcore.so.280.13
libXext.so.6 => not found
librt.so.1 => not found
libdl.so.2 => not found
libX11.so.6 => not found
libXxf86vm.so.1 => not found
libstdc++.so.6 => not found
libm.so.6 => not found
libgcc_s.so.1 => not found
libc.so.6 => not found
libpthread.so.0 => not found
(This is the output from my computer, not his)
Thanks.
It is a little more complicated than sending him a binary. You may need to build a Debian package.
Most likely you're compiling against different versions (like libc for example -- I guarantee he has it, but it probably doesn't match your version).
The best solution is to just send him the source code and have him compile it (assuming all he has to do is type make).
Beyond that, you could cross-compile it for his version of Debian, or statically link everything. I'm not sure how to do either of those though.
Building redistributeable binaries for linux is perfectly feasible. It just requires some bit of extra work. Take Blender for an example.
The guys of Listaller created some tools and libraries to aid in the process. I suggest you read their documentation.
http://listaller.tenstral.net/
Unfortunately most of the interesting docs vanished from the internet (why?), but you can still find them in the Wayback Machine. Look for "binreloc" and "Autopackage"
Update
Now in your case the canonical solution is to build all required libraries anew (independently from the distribution binaries) and link them with relative paths into. Sorry I can't tell you out of my head the details, because frankly, I don't remember them. I'd first have to go through my notes again, and probably tinker.
If you're looking for inspiration, just look at how Blender does it.

How to statically link all libraries except a few using g++?

I have a requirement that I link all my libraries statically including libstdc++, libc, pthread etc. There is one omniorb library which I want to link dynamically.
Currently I have dynamically linked all the libraries.
ldd shows the following
linux-vdso.so.1 => (0x00007fff251ff000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f291cc47000)
libomniDynamic4.so.1 (0x00007f291c842000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007f291c536000)
libm.so.6 => /lib64/libm.so.6 (0x00007f291c2e0000)
libgomp.so.1 => /usr/lib64/libgomp.so.1 (0x00007f291c0d7000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f291bebf000)
libc.so.6 => /lib64/libc.so.6 (0x00007f291bb66000)
/lib64/ld-linux-x86-64.so.2 (0x00007f291ce63000)
librt.so.1 => /lib64/librt.so.1 (0x00007f291b95d000)
libomniORB4.so.1 (0x00007f291b6aa000)
libomnithread.so.3 (0x00007f291cf35000
I need ldd to show libomniDynamic4.so.1 as the only dynamically linked library.
How do I achieve this?
Trying to make a linux executable that runs on all distros eh? Good luck...But I digress...
You want to look at the -v flag output for g++. It shows the internal link commands executed by g++/ld. Specifically, you'll want to inspect the the final link command collect2 and all of its arguments. You can then specify the exact paths to the .a libs you want to link against. You'll also have to track down static libs of everything. My libstdc++.a is in /usr/lib/gcc/x86_64-linux-gnu/4.4/libstdc++.a
rant on: My biggest complaint about linux is the fractured state of executables. Why cant I compile a binary on one machine and copy it to another and run it!? Even the Ubuntu distros one release apart will produce binary files that cannot be run on the other due to libc/libstdc++ ABI incompatibilites
edit #1 I just wanted to add that The script on this page produces a .png of an executables .so dependencies. This is very useful when attempting to do what you describe.
Be aware ldd <exename> will list all dependencies down the chain, not just immediate dependencies of the executable. So even if your executable only depended upon omniorb.so, but omniorb.so depended upon, libphread.so, ldd's output would list that. Look up the manpage of readelf to find only the immediate dependencies of a binary.
One other item that to be aware of. if omniorb.so depends upon libstdc++.so, you'll have no choice but to be dependant on that same lib. Otherwise ABI incompatibilities will break RTTI between your code and omniorb's code.
I need ldd to show libomniDynamic4.so.1 as the only dynamically linked library.
That is impossible.
First, ldd will always show ld-linux-x86-64.so.2 for any (x86_64) binary that requires dynamic linking. If you use dynamic linking (which you would with libomniDynamic4.so.1), then you will get ld-linux-x86-64.so.2.
Second, linux-vdso.so.1 is "injected" into your process by the kernel. You can't get rid of that either.
Next, the question is why you want to minimize use of dynamic libraries. The most common reason is usually mistaken belief that "mostly static" binaries are more portable, and will run on more systems. On Linux this is the opposite of true.
If in fact you are trying to achieve a portable binary, several methods exist. The best one so far (in my experience) has been to use apgcc.
It is very difficult to build a single binary that runs on a lot of Linux distros and linking statically is not the key point.
Please note that a binary built with an older glibc version--i.e., an old Linux distro--may run on newer Linux distros as well. This works because glibc is back-compatible.
A possible way to attain the desired result is:
compile the binary on an old Linux OS
find out all the required libraries for your compiled binary using the command ldd or lsof
(when running) on the binary, details here
copy the required libraries of the old Linux OS in a 'custom-lib' folder
always bundle/release this custom-lib folder with your binary
create a bash script that puts the custom-lib folder on top of the folders list in LD_LIBRARY_PATH environment variable, and then invokes your binary.
In this way, by executing the binary with the bash script, I was able to execute binaries on a wide range of embedded devices with very different Linux versions.
But there are always problematic cases where this fails.
Please note, I always tested this with cli applications/binaries.
Other possible ways..
There also seems to be elegant ways to compile glibc-back-compatible binaries, for example this that seems to compile binaries compatible with older ABI. But I have not checked this route.
when linking, use -static before specifying the libraries you want to link statically to, and use -dynamic before the libraries you want to link dynamically to. You should end up with a command line looking like this:
g++ <other options here> -dynamic -lomniDynamic4 -static -lpthread -lm -lgomp <etc>
Of course, you'll need .a versions of the libraries you want to link statically (duh).