How to set `rpath` with gcc? - c++

I have an executable that uses some shared objects.
These shared objects have other shared objects as dependencies, and I want to set my main executable's rpath to include the directories for those dependencies, since runpath is not used for indirect dependencies.
I'm trying to bake rpath into my ELF, however when using this:
gcc -std=c++20 -o main main.cpp -lstdc++ -L./lib -Wl,-rpath,./lib
The result is that ./lib is set in the ELF as RUNPATH and not RPATH:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [./lib]
0x000000000000000c (INIT) 0x1000
...
Can someone explain why this happens? I was expecting ./lib to be defined in the RPATH section and not RUNPATH. Seems like RPATH section does not exist at all.
I am using gcc version 11.1.0, with ld version 2.34.
I know this might not be the best solution for managing indirect dependencies and I'd be happy to hear a better one, however I still wonder why -Wl,-rpath,./lib results in RUNPATH defined in the ELF, and not RPATH.

To get a DT_RUNPATH entry you need --enable-new-dtags.
To get a DT_RPATH entry (which is deprecated) you need --disable-new-dtags.
In your case, something like this:
gcc -std=c++20 -o main main.cpp -lstdc++ -L./lib -Wl,--disable-new-dtags,-rpath,./lib
I'll suggest to use an absolute path with rpath, I'm not sure from which directory relative paths are interpreted. There is also $ORIGIN if you want to use the executable as reference point.
See https://man7.org/linux/man-pages/man1/ld.1.html and https://man7.org/linux/man-pages/man8/ld.so.8.html for more informations.

Related

g++ compiling: can I link to a directory with symlinked binaries?

The below compiles:
g++ -L../../lib -o my_prog my_prog.cpp -ltest1 -ltest2
where ../../lib contains symlinks to libtest1.so and libtest2.so
But I am getting an error when I run the program: "error while loading shared libraries: libtest1.so: cannot open shared object file: No such file or directory" and am not sure if symlinking is the culprit.
Option -L is for linker ld to find .a and .so during linking.
Option -Wl,-rpath= is for the dynamic linker ld.so to find .so when the application is run. You need to use -Wl,-rpath= when a required shared library is not in (standard system) directories specified in /etc/ld.so.conf.
Use $ORIGIN dynamic linker variable to make rpath relative to the path of your executable:
g++ -L../../lib -Wl,-rpath='${ORIGIN}/../../lib' -o my_prog my_prog.cpp -ltest1 -ltest2
Be careful to make sure ${ORIGIN} is not expanded by the shell or your makefile (this is why it is in single quotes).
${ORIGIN}:
$ORIGIN and rpath
ld.so understands the string $ORIGIN (or equivalently ${ORIGIN}) in an rpath specification (DT_RPATH or DT_RUNPATH) to mean the directory containing the application executable. Thus, an application located in somedir/app could be compiled with gcc -Wl,-rpath,'$ORIGIN/../lib' so that it finds an associated shared library in somedir/lib no matter where somedir is located in the directory hierarchy. This facilitates the creation of "turn-key" applications that do not need to be installed into special directories, but can instead be unpacked into any directory and still find their own shared libraries.
What happens at runtime is related to the rpath.
You may want (not really recommended, see this) to set your LD_LIBRARY_PATH appropriately before running your executable, or better yet you want to set the executable's rpath when linking it (e.g. by passing -Wl,--rpath $(realpath ../../lib/) to the g++ command doing the link.
Read Drepper's How to write shared libraries paper and the Program Library HowTo

clang, linux, is there an option to change shared library name at linking?

I'm testing a somewhat non-conventional project layout and rake as make utility. There is a rule to compile binaries from source files in different directories and link them with a shared library. This rule is run from the root directory of the project. For instance the rule does this:
clang -I libs/ -o tests/sourcefile2 tests/sourcefile2.c shared_libs/libFoo.so
And as a result I get the full path shared_libs/libFoo.so in the binary:
readelf -d tests/sourcefile2
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [shared_libs/libFoo.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
...
I would like to change it to just 'libFoo.so' like this:
readelf -d tests/sourcefile2
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libFoo.so]
...
Then I could set RPATH for dynamic linker as I want and it would give some flexibility. But I cannot find the corresponding option or similar example. Could you suggest how to handle this? Should I just use a temporary directory for the build, copy everything and compile there?
Not sure if it will help you. But when I try compile some shit and I don't know what flags. I use pkg-config.
For example, to compile a program which uses Xlib
pkg-config -cflags -libs x11
and the output is the following
-I/usr/X11R7/include -D_REENTRANT -Wl,-rpath,/usr/X11R7/lib -L/usr/X11R7/lib -lX11
Note this vary on systems, for example NetBSD forces me to link it with rpath, and there are optional arguments in this output.
So I copy the output of pkg-config and it compiles.
if If you use 'ld' as your linker you should be able to use "-Wl,-soname ".

Undefined symbol when loading a shared library

In my program I need to load a shared library dynamically with dlopen(). Both the program and the shared library are successfully cross-compiled for an ARM architecture with the cross-compiler installed on my x86. However, whenever the program tries to load the library at run time on ARM, it fails giving this error:
undefined symbol: _dl_hwcap
I cannot find the culprit of this error.
Let me give details on how the shared library (libmyplugin.so) is built on x86 first. I use the g++ cross-compiler as below:
/home/me/arm/gcc-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ -march=armv7-a -mfloat-abi=hard -c -s -fPIC -o build/module1.o module1.cpp
/home/me/arm/gcc-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ -march=armv7-a -mfloat-abi=hard -c -s -fPIC -o build/module2.o module2.cpp
/home/me/arm/gcc-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ -o dist/libmyplugin.so build/module1.o build/module2.o --sysroot /home/me/arm/sysroot/ -Wl,--no-as-needed -ldl -lX11 -lXext /home/me/arm/libstatic.a -shared -s -fPIC
Please pay attention to the following notes:
module1.cpp and module2.cpp are my source code files.
libstatic.a is a big archive of object .o files implementing the stuff directly invoked/referenced by module1.cpp and module2.cpp. These object files have been compiled by others for the same ARM architecture as mine, with the same compiler flags, but using a slightly more updated g++ compiler (v4.9 instead of my v4.8.3). Unfortunately, I have no control on the building of these objects.
--sysroot /home/me/arm/sysroot/ represents the remote filesystem of my ARM OS from which the local g++ cross-compiler can take the native libraries while linking.
-Wl,--no-as-needed -ldl -lX11 -lXext: these flags are required to force the dynamic loader to load the X11 libraries present on the system when my shared library is loaded by the program. In particular, --no-as-needed is required because the X11 libraries are NOT directly referenced by module1.o and module2.o; on the contrary the X11 libraries are referenced by the static library only.
Note that all the above setup works on x86. It's just that I don't understand what is the reason of the _dl_hwcap symbol not resolved when the program tried to load the library on ARM.
Do you have any idea how to investigate this issue?
There are a myriad of things that could be problematic, but here are four avenues of exploration. I am concentrating on the -shared in your link line, but the last item addresses that as well.
(A nice HOWTO on shared libraries is here:
http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
a) Check your environment variable LD_LIBRARY_PATH. Since you aren't using RPATH to the linker (RPATH embeds a full path to the .so so you can find it at runtime), then the only way the linker can find your code is to search the LD_LIBRARY_PATH.
Make sure the .so or .0 you want is in the path.
b) Use the UNIX utility 'nm' to search .so (shared objects) and .a files for that symbol. For example, 'nm -D /usr/lib64/libpython2.6.so' will show all dynamic symbols
in the libpython.so, and you can look for symbols of interest:
For example, Is 'initgc' defined or used in libpython?
% nm -D /usr/lib64/libpython2.6.so | grep initgc
000003404300cf0 T initgc
The 'T' means TEXT or, yes, it is defined there. See if you can find the symbol in the module of interest using grep and nm. (A 'U' means undefined, which means it is defined in another module).
c) Another useful tool is 'ldd'. It shows all dynamic libraries that the library you are looking on depends on. For example:
% ldd /usr/lib64/libpython2.6.so
linux-vdso.so.1 => (0x00007fffa49ff000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00000033f0200000)
libdl.so.2 => /lib64/libdl.so.2 (0x00000033f0600000)
libutil.so.1 => /lib64/libutil.so.1 (0x00000033fea00000)
libm.so.6 => /lib64/libm.so.6 (0x00000033f0a00000)
libc.so.6 => /lib64/libc.so.6 (0x00000033efe00000)
/lib64/ld-linux-x86-64.so.2 (0x00000033efa00000)
If it can't find a library (because it's not on the LD_LIBRARY_PATH or wasn't specified in the RPATH), the library will turn up empty.
d) I am a little worried from your link line of seeing a '.a' file with a -shared option. Some compilers/linkers cannot use a '.a' (archive) file to create a '.so' file. '.so' files usually have to made from other '.so' files or '.o' files that have been compiled with -fPIC.
I would recommend (if you can), recompile /home/me/arm/libstatic.a so that it's a .so. If you can't do, you might have to make your final output a '.a' file as well. (In other words, get rid of the -shared command line option).
In summary: Check your LD_LIBRARY_PATH, use nm and ldd to look around at your .a and .so files, but I think the end result is that you may not be able to combine .so and .a files.
I hope this helps.
I think this symbol may be in the "ld-lsb" library needed by "Xext". On my system the library is a symlink "/lib64/ld-lsb-x86-64.so -> ld-linux-x86-64.so.2", but I am sure that is not the same on the arm. Maybe give it a whirl on your linker line?

How to compile cpp source to full static binary and except for: libc, libpthread, libdl, libstdc++, and libm

I want to compile a binary linux to full static, But I always fail with this configuration:
CFLAGS="--static" CPPFLAGS="-I/home/alan/arm/arm-none-linux-gnueabi/libc/usr/include" LDFLAGS="-L/home/alan/arm/arm-none-linux-gnueabi/libc//usr/lib" LIBS="-lcrypt -ldl -lpthread -lm -lc -lstdc++" CC=arm-none-linux-gnueabi-gcc AR=arm-none-linux-gnueabi-ar CXX=arm-none-linux-gnueabi-g++ ./configure --host=arm-none-linux-gnueabi target=arm-none-linux-gnueabi --prefix=/home/alan/armbin/test --without-pcre --without-zlib --without-bzip2 --without-openssl --disable-ipv6 --enable-static
But I always get warning dlopen, gethostbyname, etc. If I not except libc, libm, etc to shared. I get warning approximately as below:
warning: Using 'gethostbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
Thank you very much.
Use -pthread instead of -lpthread.
Source.
Your link command should look like this:
g++ objectFiles $(CFLAGS) -o executable -Wl,-Bstatic -L/path/to/static/lib1/ -ls1 -L/path/to/static/lib2 -ls2 -Wl,-Bdynamic
You only have to explicitly call the static libraries libs1.a, libs2.a . The shared system libraries you are referring to (libc.so, libpthreads.so, libm.so, libdl.so, libstdc++.so etc.) should be found implicitly by your linker and are affected by -Wl, -Bdynamic. You don't have to pass them explicitly. Try and do a
"ldd executable" to see the dynamic dependencies.

Is an executable built differently if linked against a library that's not used?

Apart from a longer compile time, is there any downside to linking against an unused library?
for example, is there any difference in the executable of a program that is compiled one of two ways:
g++ -o main main.cpp
g++ -o main main.cpp -llib1 -llib2 -llib3 -lmore
*no library files were actually needed to build main.
I believe it makes no difference because the file sizes are the same, but I'm asking for confirmation.
It depends.
If liblib1.a, liblib2.a, and liblib3.a are static libraries, and no symbols are used from them, then there will be no difference.
If liblib1.so, liblib2.so, or liblib3.so are shared libraries, then they will be loaded at runtime whether or not they are used. You can use the linker flag --as-needed to change this behavior, and this flag is recommended.
To check which shared libraries your binary directly loads at runtime, on an ELF system you can use readelf.
$ cat main.c
int main()
{
return 0;
}
$ gcc main.c
$ readelf -d a.out | grep NEEDED
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
$ gcc -lpng main.c
$ readelf -d a.out | grep NEEDED
0x0000000000000001 (NEEDED) Shared library: [libpng12.so.0]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
You can see that on my system, -lpng links against libpng12.so.0, whether or not symbols from it are actually used. The --as-needed linker flag fixes this:
$ gcc -Wl,--as-needed -lpng main.c
$ readelf -d a.out | grep NEEDED
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
Notes
The --as-needed flag must be specified before the libraries. It only affects libraries which appear after it. So gcc -lpng -Wl,--as-needed doesn't work.
The ldd command lists not only the libraries your binary directly links against, but also all the indirect dependencies. This can change depending on how those libraries were compiled. Only readelf will show you your direct dependencies, and only ldd will show you indirect dependencies.
It depends whether you are linking static libraries or shared libraries. If you are linking static libraries then the executable size would increase with each addition. Linking to shared libraries, doesn't not increases executable size greatly, only library symbols are added.
Absolutely yes. The downside is that others (or you in the future) will assume the libraries are needed for some reason. Most people will not take the time to pare down a program's dependencies, and so the list of them grows and grows.
The cost has nothing to do with the compiled code, but everything to do with maintaining and porting programs.
There are some really good answers above. A further note would be "what difference does it really make". Already mentioned is the cost of maintenance (e.g. problems when someone installs a fresh operating system, which doesn't have Lib3, so the user has to go find lib3 somewhere and install it, and because lib3 also needs lib17 which you also isn't installed, it adds more work for the user).
But also, when you load the binary, if you have linked against shared libraries that aren't actually used, the system will still go look for those libraries, and refuse to load if they are not present - this adds time, and install nightmare.
Once the code is loaded, it should have no additional runtime penalty.
Having said that, there are sometimes arguments for linking against unused libraries. Say your code has an option USE_FOO, where the FOO feature is only included based on some arbitrary choice when building (e.g. "is this on Linux kernel > 3.0" or "Does the system have a fancy graphics card"), and FOO uses Lib1 to do it's business, it can make the build system (makefile or similar) a little simpler to always link against lib1, even if you don't actually need it when USE_FOO is not set.
But in general, don't link against libraries not needed. It causes more dependencies, and that's never a good thing.