I've built a simple program like this:
g++ application.cpp -o application.exe
and then executed the command;
ldd application.exe
...
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6
...
I want to list all the symbols of the libc library:
nm /lib/x86_64-linux-gnu/libc.so.6
nm: /lib/x86_64-linux-gnu/libc.so.6: no symbols
nm --defined-only /lib/x86_64-linux-gnu/libc.so.6
nm: /lib/x86_64-linux-gnu/libc.so.6: no symbols
Why nm reports no symbols? If libc.so.6 is not a library, but a some kind of a link to the actual library, then how can I find the actual library?
By default, nm reads the .symtab section in ELF objects, which is optional in non-relocatable objects. With the -D/--dynamic option, you can instruct nm to read the dynamic symbol table (which are the symbols actually used at run time). You may also want to use --with-symbol-versions because glibc uses symbol versioning extensively.
Alternatively, you can use eu-readelf --symbols=.dynsym or objdump -Tw. (readelf -sDW does not include symbol versioning information.)
Related
I am linking GLIBC statically into my .so:
g++ -shared -o morpher.so -static-libstdc++ -static-libgcc -Wl,-L. -Wl,--whole-archive -l:hello.o -l:libmorpher.a -Wl,--no-whole-archive
I do this to make my binary (.so) portable across different Linux distributions and versions.
However, this has an undesirable side effect: all GLIBC symbols, including e.g. malloc() and free(), are exported from my shared library:
$ nm morpher.so | grep free
00000000000ef520 t _ZN12_GLOBAL__N_14pool4freeEPv.constprop.0
000000000008b08c t _ZN12_GLOBAL__N_14pool4freeEPv.constprop.0.cold
00000000000ef710 T _ZN9__gnu_cxx9__freeresEv
00000000000ef860 T __cxa_free_dependent_exception
00000000000ef7b0 T __cxa_free_exception
U __freelocale##GLIBC_2.2.5
U free##GLIBC_2.2.5
Adding -fvisibility=hidden has no effect: it only works on source files at compile time.
Is there a way to prevent GLIBC and libstdc++ symbols from being exported?
nm (without the -D option) shows the static symbol table (.symtab), not the dynamic symbol table (.dynsym). Only the dynamic symbol table is relevant to dynamic linking.
In your case, it appears that the symbols are not actually defined in the shared object. Rather, __freelocale and free are undefined (U) or imported. If you build your shared object on the oldest distribution you want to support, such imports do not impact cross-distribution compatibility because glibc has a compatible ABI across distributions, and later versions support symbols from older versions.
-static-libgcc is about libgcc (not glibc). A lot of libgcc is always statically linked. The shared-by-default part is mostly about the unwinder, and linking that dynamically is recommended, so that the system unwinder is used.
I am trying to compile a program to have all libraries statically inside the final binary, but I still want glibc to be dynamically linked. If I use "-static" it compiles ALL libraries statically, including glibc. Basically I need a -static parameter together with something like -exclude=glibc
Would be awesome with both an example when using "make" as well as an example with pure "gcc". Running "ldd" on the final binary should show only glibc dynamically linked.
You can link a subset of libraries statically using -Bstatic and -Bdynamic. On the GCC command line, this looks like this (for linking statically against PCRE, just as an example):
-Wl,-Bstatic -lpcre -Wl,-Bdynamic
Note that -lanl, -ldl, -lm, -lmvec, -lnsl, -lpthread, -lresolv, -lrt, -lutil are all part of glibc and must therefore come after the -Wl,-Bdynamic (so that they are linked dynamically). For -lcrypt, this depends on the distribution.
What you ask can be done on some systems, approximately, but not with GCC's -static option. That option has global effect on linking:
On systems that support dynamic linking, this overrides -pie and prevents linking with the shared libraries. On other systems, this option has no effect.
(GCC 9.2 manual)
To have the wanted level of control over linking, you need to pass flags through to the linker. You can do that with GCC's -Wl option. If you are using GCC then you are presumably also using the GNU linker, and on build targets that support both static and dynamic linking, it has a variety of mechanisms for mixing them. In particular, the GNU linker's -Bstatic flag and its counterpart -Bdynamic flag each take effect only for libraries named after them on the command line, up to the next such flag. That is, they allow you to switch back and forth between designating libraries for static linking and for dynamic linking.
Example:
This C program requires the math library to be linked, which is not automatic with GCC:
link_test.c:
#include <stdio.h>
#include <math.h>
int main(void) {
printf("The square root of 2 is approximately %f\n", sqrt(2.0));
}
This gcc command will cause the-lm to be linked statically, but libc to be linked dynamically:
gcc -o link_test link_test.c -Wl,-Bstatic -lm -Wl,-Bdynamic
Any number of addional -l options, library names, and object file names could be put between the -Wl,-Bstatic and -Wl,-Bdynamic options along with -lm; all such objects will be linked statically. Although libc is not explicitly linked (GCC does not require that), leaving the link type toggled to "dynamic" at the end of the explicit argument list does, for me, cause libc to be linked dynamically:
$ ldd link_test
linux-vdso.so.1 => (0x00007ffe185af000)
libc.so.6 => /lib64/libc.so.6 (0x00002b775f059000)
/lib64/ld-linux-x86-64.so.2 (0x00002b775ee35000)
(Observe that libm does not appear in the dynamic library listing, unlike when -Wl,-Bstatic is not used, but libc does.)
Note that your objective that "Running 'ldd' on the final binary should show only glibc dynamically linked" is not necessarily viable, as the above ldd output demonstrates. If your executable is dynamically linked at all, then in addition to any dynamic libraries it will have the dynamic loader linked in, and possibly also platform-specific pseudo-libraries such as linux-vdso.so.1.
You ask for a makefile example, but that's like asking just "write me a program". Nothing about this is make-specific, and there are innumerable ways to incorporate the above approach into a makefile. But since you asked, this is one of the simplest possible variations:
Makefile
link_test: link_test.c
gcc -o $# $< -Wl,-Bstatic -lm -Wl,-Bdynamic
I am working on a CentOS machine. I have a file (test.c) that I'm compiling into a shared library.
test.c:
#include <stdio.h>
#include <stdlib.h>
#include <quadmath.h>
int my_func(void){
__float128 r;
r = strtoflt128 ("1.2345678", NULL);
int * b = malloc(sizeof(int) * 5);
double a = 10*M_PI_2q;
printf("Hello world %f\n",a);
return 0;
}
To compile it :
$ gcc -c -fPIC test.c -o test.o
$ gcc -shared -Wl,--verbose -Wl,-soname,poo.so -Wl,-rpath,/opt/gcc/5.5.0/lib/ -o poo.so test.o -lquadmath
$ echo $LD_LIBRARY_PATH ### This shouldn't matter?
/opt/gcc/5.5.0/lib
From the verbose output from ld, it looks like it /opt/gcc/5.5.0/lib64/libquadmath.so.0 is found (which is what I want). I.e.
.
.
.
attempt to open /opt/gcc/5.5.0/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.5.0/libquadmath.so failed
attempt to open /opt/gcc/5.5.0/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.5.0/libquadmath.a failed
attempt to open /opt/gcc/5.5.0/bin/../lib/gcc/libquadmath.so failed
attempt to open /opt/gcc/5.5.0/bin/../lib/gcc/libquadmath.a failed
attempt to open /opt/gcc/5.5.0/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.5.0/../../../../lib64/libquadmath.so succeeded
-lquadmath (/opt/gcc/5.5.0/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.5.0/../../../../lib64/libquadmath.so)
.
.
.
However, when I look with ldd poo.so, it does not have the correct library. I.e.
$ ldd poo.so
linux-vdso.so.1 => (0x00007fffc0e8e000)
libquadmath.so.0 => /act/gcc-4.7.2/lib64/libquadmath.so.0 (0x00002aea8fd39000)
libc.so.6 => /lib64/libc.so.6 (0x00002aea8ff6f000)
libm.so.6 => /lib64/libm.so.6 (0x00002aea90303000)
/lib64/ld-linux-x86-64.so.2 (0x00002aea8f8f2000)
I am using gcc version 5.5.0 and would like poo.so to link to /opt/gcc/5.5.0/lib/libquadmath.so.0 instead of the 4.7.2 version.
I think this is related to the existence of files in /etc/ld.so.conf specifying the location of the older libraries. I.e.
$ cat /etc/ld.so.conf.d/gcc-4.7.2.conf
/act/gcc-4.7.2/lib64
/act/gcc-4.7.2/lib
The man page of ld (as I inquired here) is less than useful.
QUESTION : I specify -rpath when linking and building the shared library, yet I don't understand why it is finding the older gcc-4.7.2 version of the quadmath library. Why does the linker seem to prioritize the paths in /etc/ld.so.conf and ignore the -rpath option? How can I specify the library location at compile time and how do I fix this?
To understand the solution, first we must understand the incorrect thinking.
I did not realize that there two utilities doing linking. There is ld (which is used at compile time) and ld.so (which is used at run time). The man page of ld.so describes how libraries are found at run time. To summarize it searches :
a) Directories specified in DT_RPATH
b) Directories specified in LD_LIBRARY_PATH
c) Directories in DT_RUNPATH dynamic section of binary (if present)
d) Cache file /etc/ld.so.cache
e) The default path /lib and then /usr/lib
The man page of ld primarily confused me. It says under the -rpath option:
Add a directory to the runtime library search path. This is used when linking an ELF executable with shared objects
I thought that passing -rpath when building a shared library would
guarantee that it would find the library without needing to set
LD_LIBRARY_PATH. Wrong. This feature only seems to be useful
when building an executable, not a shared library. There is an -rpath-link option, but I couldn't figure out how to make it work.
You can see this by doing the following:
$ cat test2.c
#include <stdio.h>
#include <stdlib.h>
#include <quadmath.h>
int main(void){
__float128 r;
r = strtoflt128 ("1.2345678", NULL);
int * b = malloc(sizeof(int) * 5);
double a = 10*M_PI_2q;
printf("Hello world %f\n",a);
return 0;
}
$ export LD_LIBRARY_PATH=
$ which gcc
/opt/gcc/5.5.0/bin/gcc
$ gcc -Wl,-rpath,/opt/gcc/5.5.0/lib64 test2.c -lquadmath
$ ldd a.out
linux-vdso.so.1 => (0x00007fffff34f000)
libquadmath.so.0 => /opt/gcc/5.5.0/lib64/libquadmath.so.0 (0x00002b9c35e07000)
libc.so.6 => /lib64/libc.so.6 (0x00002b9c36069000)
libm.so.6 => /lib64/libm.so.6 (0x00002b9c363fd000)
/lib64/ld-linux-x86-64.so.2 (0x00002b9c35be5000)
$ readelf -a a.out | grep -i rpath
0x000000000000000f (RPATH) Library rpath: [/opt/gcc/5.5.0/lib64]
The way that the linker found the correct library was by utilizing the path to /opt/gcc/5.5.0/bin/gcc and looking at relative paths with respect to it. I checked this by setting export LD_LIBRARY_PATH= and excluding the rpath argument, i.e. gcc -shared -Wl,--verbose -Wl,-soname,poo.so -o poo.so test.o -lquadmath. Even then, it was able to link to the correct libquadmath.so.0 library.
The reason that ldd poo.so could not find the correct library was because it wanted the 64-bit version of the libquadmath.so.0 library IS NOT /opt/gcc/5.5.0/lib/libquadmath.so.0. That is the 32-bit version of the library. This is obvious once you inspect both versions of the library and see that the they are ELF64 and ELF32 respectively (e.g. readelf -a /opt/gcc/5.5.0/lib64/libquadmath.so.0 | grep Class).
Since I never specify the path to the correct library that it is looking for, ld.so by default looked at /etc/ld.conf.cache (which builds on `/etc/ld.conf.d/') to decide which directories to look in. This is why it found the 4.7.2 version of quadmath.
To get the correct library at runtime, I need to set export
LD_LIBRARY_PATH=/opt/gcc/5.5.0/lib64.
Summary, it was finding the correct library at compile time using ld (b/c it looked at the relative paths of gcc) but could not find the correct library at run time (b/c LD_LIBRARY_PATH was incorrectly set). The solution is :
export LD_LIBRARY_PATH=/opt/gcc/5.5.0/lib64. -rpath is not a valid option when building a shared library.
The path specified by --rpath option will be hard coded into binary you created. At running time, ld will search dependent libraries from those paths preferentially. This is determined by ld.so.
But when you compile program, The linker will search dependent libraries from LD_LIBRARY_PATH and those folder you specified by -L option. At compiling time, the path specified by --rpath do not take effect. This is determined by ld, the GNU linker.
I'm reading Intel VTune documentation about OpenMP and I found this:
To analyze an OpenMP application compiled with GCC*, make sure the GCC
OpenMP library (libgomp.so) contains symbol information. To verify,
search for libgomp.so and use the nm command to check symbols, for
example: $ nm libgomp.so.1.0.0 If the library does not contain any
symbols, either install/compile a new library with symbols or Debug
Information for Application Binaries for the library.
nm /usr/lib/x86_64-linux-gnu/libgomp.so.1.0.0
nm: /usr/lib/x86_64-linux-gnu/libgomp.so.1.0.0: no symbols
But now I don't know install/compile a new library with symbols or Debug Information for Application Binaries for the library.
I'm trying to build a project using a static library, so that the binary can be used even if the library isn't installed. However, I get lots of errors about undefined symbols when I try to do so.
Looking at the library, I see it has tons of undefined symbols, even though it's a .a static lib:
nm - u /usr/local/lib/libthis.a
....
U EVP_DigestFinal_ex
U EVP_DigestInit_ex
U EVP_DigestUpdate
U EVP_MD_CTX_cleanup
U EVP_MD_CTX_init
Those seem to be from openssl; others seem to be from libbzip2; etc.
Questions:
1. Why does the static (.a) lib have dependencies on shared objects (e.g. libopenssl) that aren't statically compiled?
2. How do I solve this? Trying to manually add -lssl doesn't seem to work. How do I get the binary to compile and not have external dependcies?
Why does the static (.a) lib have dependencies on shared objects (e.g. libopenssl) that aren't statically compiled?
Just about every static library that you can build will have unresolved symbols, e.g.
int my_open_for_read(const char *filename)
{
return open(filename, O_RDONLY); // unresolved reference to open
}
As Marc Glisse pointed out, this a plain unresolved symbol, not a dependency on libc.so.
How do I solve this?
There is no problem to solve here. When you link your binary, you get to decide which libraries to link statically, and which to link dynamically.
Trying to manually add -lssl doesn't seem to work.
This should work:
gcc main.o -lthis -lssl
Possibly you did something like
gcc main.o -lssl -lthis
which is wrong: the order of libraries on the link line matters.
How do I get the binary to compile and not have external dependcies?
Most OSes support using fully-static binaries. Generally this should not be your goal: it makes for less portable binaries, and their use is strongly discouraged.
If you really do want to produce a fully-static binary, link it with -static flag.
Why do you say full static is less portable?
Because they are.
if the user doesn't have the exact same build of the lib, the binary won't be portable with shared libs, but will be portable with static.
This is incorrect: most shared libraries support backward compatibility, e.g. libc.so.6 version 2.22 will happily run executables linked against version 2.3.6 from 10 years ago.
If you do ldd firefox
You need to pay attention to what you are doing:
file -L `which /usr/bin/firefox`
/usr/bin/firefox: POSIX shell script, ASCII text executable
If you look inside the shell script, you'll discover that it invokes /usr/lib/firefox/firefox, and that binary is dynamically linked:
ldd /usr/lib/firefox/firefox
linux-vdso.so.1 => (0x00007ffca278d000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f511731b000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f5117117000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f5116e13000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f5116b0d000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f51168f7000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5116532000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5117757000)