Underlinking
Содержание
What is underlinking
Simple case
Underlinking states for the situation when a binary uses a symbol not provided by libraries it is directly linked to. For example, libeb uses "deflate" from zlib. But -lz is not passed during build. This is allowed for shared libraries.
objdump -p /usr/lib/libeb.so | grep NEEDED NEEDED libnsl.so.1 NEEDED libresolv.so.2 NEEDED libc.so.6
A program using libeb will have to link explicitly with libz, even if it doesn't use it directly:
% echo 'main() { eb_log("foo"); }' > t.c % gcc t.c -leb -lz % gcc t.c -leb /usr/lib/libeb.so: undefined reference to `inflateEnd' /usr/lib/libeb.so: undefined reference to `deflate' ... collect2: ld returned 1 exit status
That program can not be linked using -Wl,--as-needed:
% gcc t.c -Wl,--as-needed -leb -lz /usr/lib/libeb.so: undefined reference to `inflateEnd' /usr/lib/libeb.so: undefined reference to `deflate' ... collect2: ld returned 1 exit status
Indirect case
example: librsvg uses "tan" from libm. But -lm is not passed during build.
On contrary to the simple case above, this will not impact programs building with librsvg since librsvg is linked with many shared libraries, and some of them are linked with libm.
Why underlinking is bad
Bad for programs
If a new version of libfoo is released which now use zlib, but do not link with it, every programs using libfoo will have to adapt their build to use -lz.
Disallows --as-needed
As explained, --as-needed can not be used when compiling a program with an underlinked library.
example:
% gcc -Wl,--as-needed conftest.c -lgtk -lgdk -lgmodule -lgthread -lglib -lpthread -ldl -lXi -lXext -lX11 -lm /usr/lib/libgtk.so: undefined reference to `gdk_window_foreign_new' /usr/lib/libgtk.so: undefined reference to `gdk_drag_find_window' /usr/lib/libgtk.so: undefined reference to `gdk_colormap_get_visual' ...
-lgdk is mentioned, but since conftest.c doesn't use it, --as-needed simply discards it.
The solution is to ensure /usr/lib/libgtk.so is correctly linked with libgdk, so that it has no undefined reference.
Bad for distributions
In the context of a distribution, one need to know the dependencies between packages. With an underlinked library, one can't know that it needs to be rebuilt (except maybe through BuildRequires). It also forces to overlink.
example:
program foo needing libeb will be built with libeb and libz. If a new libz with a different ABI is released:
- foo will have to be uselessly rebuilt (overlinking)
- libeb must be rebuilt, but this information is hidden. Automatic handling of
libraries depencies can not help.
How to detect
When building a package
ld option --no-undefined
ROSA Linux sets -Wl,--no-undefined by default in LDFLAGS. This ensures every shared libraries (aka DSO) are not underlinked. The build will fail if some libraries are missing. Example:
% echo 'main() { deflate(); }' > libfoo.c % gcc -shared libfoo.c % gcc -shared libfoo.c -Wl,--no-undefined ccEqBIQo.o: In function `main': libfoo.c:(.text+0x12): undefined reference to `deflate' collect2: ld returned 1 exit status
The usage of --no-undefined by default can cause problems, especially for plugins/modules. See below.
spec-helper warning
You will get a warning as explained here (the check is done by strip_and_check_elf_files from spec-helper).
But this check uses ldd -r which doesn't detect all cases (see below)
Use "ldd -r", example:
% ldd -r /usr/lib/libeb.so.10.0.2 >/dev/null undefined symbol: inflateEnd (/usr/lib/libeb.so.10.0.2) undefined symbol: deflate (/usr/lib/libeb.so.10.0.2) undefined symbol: deflateInit_ (/usr/lib/libeb.so.10.0.2) undefined symbol: inflate (/usr/lib/libeb.so.10.0.2) undefined symbol: deflateEnd (/usr/lib/libeb.so.10.0.2) undefined symbol: inflateInit_ (/usr/lib/libeb.so.10.0.2)
But "ldd -r" is not enough. It will not detect the "indirect case". Using --no-unneeded during build handles all cases.
How to fix
programs using libtool
examples of fix:
distcache
--- distcache-1.5.1/ssl/libnalssl/Makefile.am 2004-04-30 13:09:14.000000000 -0400 +++ distcache-1.5.1.oden/ssl/libnalssl/Makefile.am 2008-05-21 12:11:36.000000000 -0400 @@ -3,3 +3,4 @@ lib_LTLIBRARIES = libnalssl.la libnalssl_la_SOURCES = bss_nal.c libnalssl_la_LDFLAGS = -version-info 1:1:0 +libnalssl_la_LIBADD = ../../libnal/libnal.la
gtk+1.2
--- gtk+-1.2.10/gtk/Makefile.am.gtkgdkdep 2003-10-15 15:20:27.000000000 -0400 +++ gtk+-1.2.10/gtk/Makefile.am 2003-10-15 15:22:50.000000000 -0400 @@ -23,6 +23,10 @@ # libtool stuff: set version and export symbols for resolving libgtkincludedir = $(includedir)/gtk-1.2/gtk + +libgtk_la_DEPENDENCIES = $(top_builddir)/gdk/libgdk.la +libgtk_la_LIBADD = $(top_builddir)/gdk/libgdk.la + libgtk_la_LDFLAGS = @STRIP_BEGIN@ \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -release $(LT_RELEASE) \
id3lib
make libid3_la_LIBADD="-lstdc++ -lz" fixes it.
note the two problems here:
- -lz was missing
- libtool --mode=link g++ is invalid because libtool will use gcc instead of g++ to link the shared library. The correct command is:
libtool --tag=CXX --mode=link g++. Alas --tag=CXX was introduced in libtool 1.4b (July 2001), and id3lib's libtool doesn't handle it.
Problems introduced by --no-undefined
Example, mpg123: the plugins use functions that are defined in the program:
% ldd -r /usr/lib/mpg123/output_sdl.so >/dev/null undefined symbol: sfifo_close (/usr/lib/mpg123/output_sdl.so) undefined symbol: sfifo_init (/usr/lib/mpg123/output_sdl.so) undefined symbol: sfifo_flush (/usr/lib/mpg123/output_sdl.so) undefined symbol: sfifo_read (/usr/lib/mpg123/output_sdl.so) undefined symbol: sfifo_write (/usr/lib/mpg123/output_sdl.so) % objdump -T /usr/bin/mpg123 | grep sfifo 0805ac30 g DF .text 00000013 Base sfifo_flush 0805ac80 g DF .text 0000007a Base sfifo_init 0805adc0 g DF .text 000000b8 Base sfifo_read 0805ad00 g DF .text 000000c0 Base sfifo_write 0805ac50 g DF .text 00000021 Base sfifo_close
here some symbols (unresolved in plugins) are part of the program opening those plugins. (eg: plugins in gnome-settings-daemon, compiz*, emerald, gnome-settings-daemon, libxslt, lxpanel)
For those specific cases where underlinking needs to be disabled, you may need this following in specfile :
%define _disable_ld_no_undefined 1
But this is often unneeded since drop-ld-no-undefined-for-shared-lib-modules-in-libtool (called in %configure) modifies ltmain.sh/libtool to discard --no-undefined when building shared library modules.
So a better fix is to ensure libtool is correctly called with option -module (sometimes only -export-dynamic is used whereas both should be)
Perl modules
ROSA Linux does not use --no-undefined for perl modules since perl modules do not use LDFLAGS during build.
Other it would have to be disabled since perl modules do not link to libperl.
Ruby modules
Ruby modules are linked against libruby so this should not be needed but --no-undefined is not used to build modules as it breaks ruby/GNOME2. This package builds several modules (glib, gtk, atk, poppler, ...) which are not linked together but get loaded in the right order by some ruby code. A better solution would be to fix this specific package but for now the workaround is in ruby package.