I'm working on a project that contains many libraries (built as shared objects)
that use functionality from each other. In normal usage (in this project) these
are loaded dynamically by python. I have no idea how the python loader resolves
dependencies between these libraries, but if one tries to build an application
that uses the plain dynamic linker to link the libraries, things become
difficult. The reason: these libraries have no DT_NEEDED
tags so the dynamic
linker has no dependency information.
Background
Suppose we have a library libA.so
that uses functionality from libB.so
to do
some of its work. Now suppose you have an application exe
that calls functions
in libA.so
, but not libB.so
. The person invoking the linker to link exe
knows to build with -lA
because they know that exe
uses libA.so
. However
this person may not know to link with -lB
: their program that they wrote
is exe
, and they have no idea how the internals of libA.so
work, nor should
they care. The solution is to put a DT_NEEDED
tag into libA.so
to indicate
that it needs libB.so
. With this tag, the linker command to build exe
only
needs to specify -lA
, and the linker will read the DT_NEEDED
tag, and link
in libB.so
automatically.
This tag is created when libA.so
is linked: its build command needs to
include -lB
. These tags can be queried with the objdump
command. For
instance:
dima@shorty:/tmp$ objdump -p /usr/lib/x86_64-linux-gnu/libopencv_highgui.so.2.4 | grep NEEDED NEEDED libopencv_imgproc.so.2.4 NEEDED libdl.so.2 NEEDED libpthread.so.0 NEEDED librt.so.1 NEEDED libtbb.so.2 NEEDED libatomic.so.1 NEEDED libz.so.1 NEEDED libjpeg.so.62 NEEDED libpng16.so.16 NEEDED libtiff.so.5 NEEDED libImath-2_2.so.12 NEEDED libIlmImf-2_2.so.22 NEEDED libIex-2_2.so.12 NEEDED libHalf.so.12 NEEDED libIlmThread-2_2.so.12 NEEDED libgtk-x11-2.0.so.0 NEEDED libgdk-x11-2.0.so.0 NEEDED libpangocairo-1.0.so.0 NEEDED libatk-1.0.so.0 NEEDED libcairo.so.2 NEEDED libgdk_pixbuf-2.0.so.0 NEEDED libgio-2.0.so.0 NEEDED libpangoft2-1.0.so.0 NEEDED libpango-1.0.so.0 NEEDED libgobject-2.0.so.0 NEEDED libglib-2.0.so.0 NEEDED libfontconfig.so.1 NEEDED libfreetype.so.6 NEEDED libgthread-2.0.so.0 NEEDED libdc1394.so.22 NEEDED libv4l1.so.0 NEEDED libavcodec.so.57 NEEDED libavformat.so.57 NEEDED libavutil.so.55 NEEDED libswscale.so.4 NEEDED libopencv_core.so.2.4 NEEDED libstdc++.so.6 NEEDED libm.so.6 NEEDED libgcc_s.so.1 NEEDED libc.so.6
So a user building an application that uses libopencv_highgui.so
doesn't need
to know to link in all this stuff when building their executable.
Solution
So I have a bunch of libraries that have no DT_NEEDED
tags, and I want to
change their build invocations to add those tags. But which tags do I need?
There are many libraries, and it would take a lot of work to look through each
one and make a list of libraries that it depends on. I wrote this script to do
that work for me:
infer_dt_needed.pl
#!/usr/bin/perl # Invoke this tool thusly: # # nm -o -D *.so | perl infer_dt_needed.pl use strict; use warnings; use feature ':5.10'; my %undef_syms_in_libs; my %defining_lib_for_sym; my %library_dep; my %libraries; while(<>) { my ($lib,$type,$sym) = /^(.*?): # symbol name: (?:[0-9a-fA-F]+)? # possibly an address \s+ ([a-zA-Z]) # symbol type \s+ (.*?) # symbol name $/x or next; $libraries{$lib} = 1; # read in each symbol, and make a record of each defined and undefined # function. I look for very specific symbol types here, and this set is # sufficient for my application. Look at the 'nm' manpage for the other # possible symbols that may be necessary for other applications if( length($type) ) { if($type eq 'T') { $defining_lib_for_sym{$sym} = $lib; } elsif($type eq 'U') { $undef_syms_in_libs{$lib} //= []; push @{$undef_syms_in_libs{$lib}}, $sym; } } } # I go through each undefined symbol, find the library that provides it, and if # found, record the dependency between those libraries for my $lib (keys %undef_syms_in_libs) { for my $sym ( @{$undef_syms_in_libs{$lib}} ) { if ($defining_lib_for_sym{$sym}) { $library_dep{$lib} //= {}; $library_dep{$lib}{$defining_lib_for_sym{$sym}} = 1; } } } # print out the dependencies say "######### All dependencies: #########"; for my $lib(keys %library_dep) { say "$lib:" . join('', map { ' -l' . s/^lib(.*)\.so.*/\1/r} keys %{$library_dep{$lib}} ); } # Find the existing DT_NEEDED flags my %libraries_have_needed; for my $lib (keys %libraries) { my %thislib_have_needed; my $fd; open $fd, '-|', "objdump -p $lib | awk '\$1 ~ /NEEDED/ { print \$2 }'"; while(<$fd>) { chomp; next unless length($_); $thislib_have_needed{$_} = 1; } $libraries_have_needed{$lib} = \%thislib_have_needed; } # Compare the existing DT_NEEDED flags with linkages we found by looking at the # symbols. We SHOULD have DT_NEEDED flags for everything. If we don't, report it say "######### All MISSING dependencies: #########"; for my $lib(keys %library_dep) { # Library $lib depends on the libraries in keys %{$library_dep{$lib}} for my $libdep (keys %{$library_dep{$lib}}) { if (not exists $libraries_have_needed{$lib}{$libdep}) { say "Missing DT_NEEDED: $lib depends on $libdep"; } } }
To use this tool, read the symbol table from all my shared libraries, and feed this list into the tool. The tool finds the connection between libraries that use a particular symbol and libraries that provide it. In the trivial example above, the invocation and output would look like this:
$ nm -o -D libA.so libB.so | perl infer_dt_needed.pl ######### All dependencies: ######### libA.so: -lB ######### All MISSING dependencies: ######### Missing DT_NEEDED: libA.so depends on libB.so
The nm
tool reads the symbol table, and the -o -D
options are more or less
required for this usage. The tool only looks at a very specific set of symbols
(nm types T
and U
), which is sufficient for my purposes. Read the nm
manpage and update the script if you need more.
With many libraries and many symbols this sort of automated bookeeping is invaluable. The resulting list can be used to update the Makefiles to put the appropriate tags in place.