How does a process find dynamic shared libraries in Linux? - linux

I was reading APUE and see following:
Several more segment types exist in an a.out, containing the symbol table, debugging information, linkage tables for dynamic shared libraries, and the like. These additional sections don’t get loaded as part of the program’s image executed by a process.
But, how does a process find necessary .so if linkage tables for dynamic shared libraries is not loaded to the program's image?

The dynamic linker is loaded as the program interpreter through the .interp (PT_INTERP) section added to the program headers during linking.
The dynamic linker reads the DT_NEEDED ELF tags (added during static linking) to figure out what shared libraries that it needs to resolve.
Finally, on resolving the library deps, from ld.so(8):
When resolving library dependencies, the dynamic linker first inspects
each dependency string to see if it contains a slash (this can occur
if a library pathname containing slashes was specified at link time).
If a slash is found, then the dependency string is interpreted as a
(relative or absolute) pathname, and the library is loaded using that
pathname.
If a library dependency does not contain a slash, then it is searched
for in the following order:
(ELF only) Using the directories specified in the DT_RPATH dynamic
section attribute of the binary if present and DT_RUNPATH attribute
does not exist. Use of DT_RPATH is deprecated.
Using the environment variable LD_LIBRARY_PATH. Except if the
executable is a set-user-ID/set-group-ID binary, in which case it is
ignored.
(ELF only) Using the directories specified in the DT_RUNPATH dynamic
section attribute of the binary if present.
From the cache file /etc/ld.so.cache, which contains a compiled list
of candidate libraries previously found in the augmented library path.
If, however, the binary was linked with the -z nodeflib linker option,
libraries in the default library paths are skipped. Libraries
installed in hardware capability directories (see below) are preferred
to other libraries.
In the default path /lib, and then /usr/lib. If the binary was linked
with the -z nodeflib linker option, this step is skipped.

Related

Managing secondary dependencies of shared libraries

I have a problem with the seconday dependences of the shared libraries.
Suppose we have the following dependency tree:
libA.so
├─libB.so
└─libC.so
└─libD.so
That is, a shared library A depends on shared libraries B and C, and shared library C depends itself on shared library D. A good example for libC is GSL shared library which depends on CBLAS.
To make a self-sufficient easy-to-use package and to avoid problems with different versions of installed shared libraries, I package the libraries libB, libC and libD with libA, and as explained, e.g., in Drepper’s article, “How to write shared libraries” (2011), I add the link flag -Wl,-rpath,\$ORIGIN --enable-new-dtags to set the RUN_PATH of libA.so, so that the dynamic loader can find the dependencies of libA in the corresponding directories, without needing to set LD_LIBRARY_PATH (which has its downsides).
The problem is that setting the RUN_PATH in libA does not help with the secondary dependencies, like libD.so.
Turning on the dynamic loader debug messages (via setting LD_DEBUG) reveals that the loader attempts to find libD.so only in the standard library locations, and not in the location of libA (contrary to what it does for finding libB or libC).
Is there a way to overcome this problem?
Indeed, I can link the libraries C and D statically, if I have the source code or properly compiled static libraries.
But is there a better way?
Solution:
As explained by Employed Russian the solution is to set RPATH instead of RUNPATH.
With recent versions of GCC, one should use the following link flag:
-Wl,--disable-new-dtags,-rpath,\$ORIGIN
RPATH is searched for transitive dependencies; that is, paths in RPATH will be considered for everything that is dynamically loaded, even dependencies of dependencies.
In contrast, the ld dynamic linker does not search RUNPATH locations for transitive dependencies (unlike RPATH).
See also:
Wikipedia: https://en.wikipedia.org/wiki/Rpath#The_role_of_GNU_ld
SO question: How to set RPATH and RUNPATH with GCC/LD?
By setting DT_RUNPATH you are telling the loader that each of your binaries is linked with all of its dependencies.
But that isn't true for libC.so -- it (apparently) doesn't have DT_RUNPATH of its own.
I can link the libraries C and D statically, ... But is there a better way?
Yes: link libC.so with correct DT_RUNPATH (if libC.so and libD.so are in the same directory, then -Wl,-rpath,\$ORIGIN will work for libC.so as well).
Update:
The problem is that I may not have the source or the properly compiled object files of libC
In that cause you should use RPATH instead of RUNPATH. Unlike the latter, the former applies to the object itself and to all dependencies of that object.
In other words, using --enable-new-dtags is wrong in this case -- you want the opposite.
In this case, there is no solution (besides static linking); correct?
The other solution (besides using RPATH) is to set LD_LIBRARY_PATH in the environment.
Update 2:
Difference between RPATH and RUNPATH
The difference is explained in the ld.so man page:
If a shared object dependency does not contain a slash, then it
is searched for in the following order:
o Using the directories specified in the DT_RPATH dynamic
section attribute of the binary if present and DT_RUNPATH
attribute does not exist. Use of DT_RPATH is deprecated.
...
o Using the directories specified in the DT_RUNPATH dynamic
section attribute of the binary if present. Such directories
are searched only to find those objects required by DT_NEEDED
(direct dependencies) entries and do not apply to those
objects' children, which must themselves have their own
DT_RUNPATH entries. This is unlike DT_RPATH, which is applied
to searches for all children in the dependency tree.

Are linux package manager installed libraries statically or dynamically linked?

If cryptopp as an example is installed using sudo apt install libcrypto++-dev and then included using #include <cryptopp/base64.h>, will this library be statically or dynamically linked?
CMakeLists.txt includes cryptopp in target_link_libraries.
will [a library installed via a package manager] be statically or dynamically linked?
It depends on several factors. First, both libraries must be available. In the case of Crypto++ on Unix and Linux, both static and dynamic libraries are available. On Windows, only a static library is provided.
Second, the linker's configuration matters assuming both libraries are available. On Linux with ld, the dynamic library is always used by default. On OS X, a dynamic library is also always used by default. On Windows the linker configuration does not factor into things because options control it.
Third, it depends on linker options. On Windows - and if a dynamic library was available - it would depend on the library you link to. It would be either the static on the import library for a dynamic link library. On Linux with ld you can use :filename to link with the static library:
-l namespec
--library=namespec
Add the archive or object file specified by namespec to the list of
files to link. This option may be used any number of times. If
namespec is of the form :filename, ld will search the library path for
a file called filename, otherwise it will search the library path for
a file called libnamespec.a.
On systems which support shared libraries, ld may also search for
files other than libnamespec.a. Specifically, on ELF and SunOS
systems, ld will search a directory for a library called
libnamespec.so before searching for one called libnamespec.a. (By
convention, a .so extension indicates a shared library.) Note that
this behavior does not apply to :filename, which always specifies a
file called filename.
The linker will search an archive only once, at the location where it
is specified on the command line. If the archive defines a symbol
which was undefined in some object which appeared before the archive
on the command line, the linker will include the appropriate file(s)
from the archive. However, an undefined symbol in an object appearing
later on the command line will not cause the linker to search the
archive again.
Finally, behavior is not a simple matter when using CMake. The default behavior will likely be to not add anything. Adding -lcryptopp or -l:cryptopp to your LDFLAGS or LDLIBS will have no effect because CMake does not honor customary flags. You will have to add the library to every target manually.

cmake compiling but not linking a new source file in a library (libonion)

I am a cmake newbie (on Debian/Sid/Linux/x86-64)
I forked libonion on https://github.com/bstarynk/onion to enable customization of malloc with Boehm's garbage collector; see this mail thread.
I added two files there onion/src/low_util.c and onion_src/low_util.h (which is #include-d successfully in several other patched files.
It is compiled but not linked.
set(SOURCES onion.c codecs.c dict.c low_util.c request.c response.c handler.c
log.c sessions.c sessions_mem.c shortcuts.c block.c mime.c url.c ${POLLER_C}
listen_point.c request_parser.c http.c ${HTTPS_C} websocket.c ${RANDOM_C} ${SQLITE3_C})
later:
SET(INCLUDES_ONION block.h codecs.h dict.h handler.h http.h https.h listen_point.h low_util.h log.h mime.h onion.h poller.h request.h response.h server.h sessions.h shortcuts.h types.h types_internal.h url.h websocket.h ${SQLITE3_H})
MESSAGE(STATUS "Found include files ${INCLUDES_ONION}")
but when I build, my file low_util.c got compiled but not linked.
Linking C executable otemplate
CMakeFiles/opack.dir/__/__/src/onion/dict.c.o: In function `onion_dict_new':
dict.c:(.text+0x1bc): undefined reference to `onionlow_calloc'
CMakeFiles/opack.dir/__/__/src/onion/dict.c.o: In function `onion_dict_node_data_free':
dict.c:(.text+0x2ec): undefined reference to `onionlow_free'
CMakeFiles/opack.dir/__/__/src/onion/dict.c.o: In function `onion_dict_node_add':
Notice that libonion is a library (in C, providing HTTP service) and that I just want to add a low_util.c file (wrapping malloc & pthread_create etc... to make Boehm's GC happy: it is calling GC_malloc and GC_pthread_create ....) with its low_util.h header. Surprisingly, they get compiled, but do not seems to be linked. And I am not familiar with cmake and I am not familiar with how D.Moreno (the main author of libonion) has organized his cmake files.
Any clues?
Apply the following patch to make it link. The two executables which are being linked with the symbols generated from the .c file you added are missing and are added in the patch.
http://pastebin.com/mDMRiUQu
Based on what you posted, its hard to tell what could be wrong. The cake source code above says that a variable ${SOURCES} is equivalent to onion.c codecs.c dict.c low_util.c ... ${SQLITE3_C}, and a variable ${INCLUDE_ONION} is equivalent to block.h codecs.h dict.h ... ${SQLITE3_H}. You did not provide any targets or the files included in those targets.
A brief list of things that may help:
where do you define the top level library or executable? If your making a library, you will need the command add_library(). If you are making an executable, you will need the add_executable() command.
Use the command target_link_libraries() to resolve dependencies. Rather than placing all of the source files in a single library, group similar together in a single target (a target is defined by the add_* commands), and use this command to link the targets after compilation.
Use the find_package() to get any libraries which are defined on your system but not in you project. Then, link to that library using the target_link_libraries() command.
In this case, if the onion_dict_* functions are defined within the same library, your not including those files in library. When you use add_library or add_executable, ensure you add those files to the list. If the functions are within your project but not in the same library, use the target_link_libraries() command to link to the library which contains the correct files. If those commands are defined in an external library, then first find the library using find_package(), and then link to the library using target_link_libraries().

When to use --dynamic option in nm

Sometimes when I do nm on a .so file (for example, libstdc++.so.6), it says no symbols, and I need to use nm --dynamic. But for some other .so files, I can see the symbols without --dynamic.
The doc says:
Display the dynamic symbols rather than the normal symbols. This is only meaningful for dynamic objects, such as certain types of shared libraries.
But it is confusing... what "types" of shared libraries need --dynamic? How is this determined? During the compilation of the library? I thought all shared libraries are dynamic (I mean, can be dynamically loaded during run time), but seems that my understanding is not quite right.
It could very well possible that if your symbol is not exported from your shared library, it would end up in the normal symbol table instead of the dynamic symbol table.
There are many types of symbols in ELF files.
symbols part of the Normal Symbol table. This is the output from mere nm libabc.so or objdump --syms libabc.so. These symbols are only used during static linking.
symbols part of the Dynamic Symbol table. This is the output from nm --dynamic libabc.so or objdump --dynamic-syms libabc.so. Dynamic symbol table is the one used by the run-time linker/loader which binds the symbols between the ELF file which refers them and the ELF file which defines them. It is also used by the static linker, while linking a shared library with an application which requires it. This is the component which helps in showing all the undefined symbol errors during linking.
Hidden symbols - these are the symbols which have been marked using _attribute_ ((visibility("hidden"))). These symbols are not exported outside and can only be used within the library. The visibility is checked during the linking step, and hence is enforced only for shared libraries. The default visibility is public, i.e. the symbols are exported unless otherwise specified. The behavior can be modified using the -fvisibility=default|internal|hidden|protected.
Set the default ELF image symbol visibility to the specified
option—all symbols will be marked with this unless overridden within
the code. Using this feature can very substantially improve linking
and load times of shared object libraries, produce more optimized
code, provide near-perfect API export and prevent symbol clashes. It
is strongly recommended that you use this in any shared objects you
distribute. Despite the nomenclature, default always means public ie;
available to be linked against from outside the shared object.
protected and internal are pretty useless in real-world usage so the
only other commonly used option will be hidden. The default if
-fvisibility isn't specified is default, i.e., make every symbol public—this causes the same behavior as previous versions of GCC.
An overview of these techniques, their benefits and how to use them is
at http://gcc.gnu.org/wiki/Visibility.
To answer your question when would you use the --dynamic option of nm, it would be when you want to list all the symbols exported by your shared library, and the only ones that are available to the ELF images which reference them.
You need to use --dynamic or -D option on a shared library if it is stripped and thus only contains a dynamic symbol table.
You may want to use this option for other shared libraries to explicitly display the dynamic symbol table as this is the table that is consulted by the dynamic linker.
The file utility indicates whether a shared library is stripped or not. Example:
$ file /usr/lib64/libcrypt-nss-2.26.so
[..] ELF 64-bit LSB shared object, x86-64 [..], not stripped
$ file /usr/lib64/libxml2.so.2.9.7
[..] ELF 64-bit LSB shared object, x86-64 [..], stripped
Example for how the different symbol tables may contain different symbols:
$ nm -D /usr/lib64/libcrypt-nss-2.26.so | wc -l
39
$ nm /usr/lib64/libcrypt-nss-2.26.so | wc -l
112

dlopen() .so fails to find symbols in a stripped executable

I have an executable in linux - exe
This executable has some functions in it, that are used throughout the code:
sendMsg
debugPrint
I then want to dynamically load a .so that provides extra functionality to my executable.
In this shared library I include the headers for sendMsg and debugPrint.
I load this shared library with dlopen() and create an API with dlsym().
However, at dlopen() I use RTLD_NOW to resolve all symbols at load time.
It fails stating that it cannot find sendMsg symbol.
This symbol must be in the executable as the sendMsg.c is compiled in there.
However, my executable is stripped by the make process. As such, it would make sense that dlopen cannot find the symbol.
How can i solve this situation?
I could build the shared functions into a static library and link that static library into both exe and the .so. This would increase code size :(
I could remove the stripping of the exe so the symbols can be found
Do some compile time linking magic that I don't know about so the .so knows where the symbols are in exe
man ld:
-E
--export-dynamic
--no-export-dynamic
When creating a dynamically linked executable, using the -E option or the --export-dynamic option causes the linker to add all symbols to the dynamic symbol table. The
dynamic symbol table is the set of symbols which are visible from dynamic objects at run time.
If you do not use either of these options (or use the --no-export-dynamic option to restore the default behavior), the dynamic symbol table will normally contain only those
symbols which are referenced by some dynamic object mentioned in the link.
If you use "dlopen" to load a dynamic object which needs to refer back to the symbols defined by the program, rather than some other dynamic object, then you will probably
need to use this option when linking the program itself.
You can also use the dynamic list to control what symbols should be added to the dynamic symbol table if the output format supports it. See the description of
--dynamic-list.
Note that this option is specific to ELF targeted ports. PE targets support a similar function to export all symbols from a DLL or EXE; see the description of
--export-all-symbols below.
You can also pass the -rdynamic option to gcc/g++ (as noted int the comment). Depending on how you setup your make script, this will be convenient

Resources