Underlinking

Материал из Rosalab Wiki
Перейти к: навигация, поиск

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)

Check a shared library

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.


Note:
This page is based on the Mandriva's underlinking description.