This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
developingpld:advanceddeveloping:fixingasneeded [2006-08-07 16:27] SamChi - some fixes, gcc <library> trick |
developingpld:advanceddeveloping:fixingasneeded [2007-05-27 20:51] arekm |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | |||
- | |||
- | ====== Fixing --as-needed problems ====== | ||
- | |||
- | |||
- | ==== Quick Workaround ==== | ||
- | Put in spec file this line: | ||
- | |||
- | <file>%devine filterout_ld -Wl,--as-needed | ||
- | </file> | ||
- | But **don't use it** unless you really need. | ||
- | |||
- | Why ? Because it's very stupid. If something fails to build normally it's a sign ''--as-needed'' did well it's job and disabled unneeded libraries. | ||
- | |||
- | |||
- | |||
- | ===== How --as-needed works ===== | ||
- | With ''--as-needed'' enabled, only libraries containing symbols required by __object files__ are linked. | ||
- | |||
- | |||
- | |||
- | == About positions == | ||
- | Linker options are positional, and position is very important. It looks for missing symbols only in libraries coming after object file. | ||
- | |||
- | Correct positions are: | ||
- | |||
- | <file>$ gcc $(LDFLAGS) -o $@ $(OBJECTS) $(LIBS) | ||
- | </file> | ||
- | and this generally works well. | ||
- | |||
- | |||
- | If there is something like that: | ||
- | |||
- | <file>$ gcc -Wl,--as-needed -l<library> <objects.o> | ||
- | </file> | ||
- | the library will __never__ be linked, because it's not required at the time it's checked. | ||
- | |||
- | |||
- | And with something like that: | ||
- | |||
- | <file>$ gcc -l<library> -Wl,--as-needed <objects.o> | ||
- | </file> | ||
- | library will allways be linked, even if it's not required. | ||
- | |||
- | |||
- | |||
- | ==== Short examples ==== | ||
- | Most of problems are with readline/ncurses and tinfo, and it's nice example: tinfo is required by both readline and ncurses, and both are linked with this library (now, when I've [[http://cvs.pld-linux.org/cgi-bin/cvsweb/SOURCES/readline-shared.patch?r1=1.6&r2=1.7|fixed readline]];). | ||
- | |||
- | But some packages link with readline or ncurses while they use __only__ symbols from tinfo. //Without// ''--as-needed'' those executables work because they are linked with tinfo from readline/ncurses library. //With// ''--as-needed'' it will not work, because readline/ncurses contain no symbols required by executable so they are not linked, it's dependencies naturally are neither linked. Thats why there is a need to pass ''-ltinfo''. If it requires only symbols from tinfo it's ok to ''s/ncurses/tinfo/''. But if it really requires readline/ncurses but there is some executable (or ./configure) which requires only tinfo both ''-lreadline''/''-lncurses'' and ''-ltinfo'' should be passed. | ||
- | |||
- | For a longer example take a look at real example nr. 1. | ||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | Other common problem is when package produces some shared libraries without linking them with all required libraries, and everything is linked at the end to one binary. So we have: | ||
- | |||
- | <file>$ gcc -Wl,--as-needed -o executable <objects.o> -l2 -l1 | ||
- | </file> | ||
- | It's usual situation when object files require only library 2, and //-l1// is required by //-l2//. As I said linker only checks for symbols from objects, so //-l1// is not linked. Normally it is easy to fix it, simply make sure while linking //-l2// it is linked to //-l1// (take a look at the second real example). | ||
- | |||
- | |||
- | |||
- | ===== Real examples ===== | ||
- | |||
- | |||
- | ==== 1. unresolved symbols in executable ==== | ||
- | Very common situation, [[http://cvs.pld-linux.org/cgi-bin/cvsweb/SPECS/xmoto.spec?rev=HEAD|xmoto.spec]]. Actually, it's nothing new. Every linking behaves this way if some library is missing. | ||
- | |||
- | With ''-Wl,--as-needed'' enabled it stops on something like this: | ||
- | |||
- | |||
- | |||
- | <file>x86_64-pld-linux-g++ -Wl,--as-needed -o xmoto-edit BuiltInFont.o [...object files...] Packager.o -lGL -lcurl -lode -llualib50 -llua50 -lSDL_mixer -lbz2 -lz -lpng -ljpeg | ||
- | |||
- | VApp.o: In function `vapp::App::getRealTime()': | ||
- | src/VApp.cpp:287: undefined reference to `SDL_GetTicks' | ||
- | VApp.o: In function `vapp::App::getTime()': | ||
- | src/VApp.cpp:284: undefined reference to `SDL_GetTicks' | ||
- | |||
- | [... bunch of missing SDL functions ...] | ||
- | |||
- | Editor.o: In function `vapp::EditorApp::viewDrawGrid()': | ||
- | src/Editor.cpp:777: undefined reference to `SDL_GetMouseState' | ||
- | Editor.o:src/Editor.cpp:46: more undefined references to `SDL_GetMouseState' follow | ||
- | EditorMain.o: In function `main': | ||
- | src/EditorMain.cpp:59: undefined reference to `SDL_Quit' | ||
- | |||
- | collect2: ld returned 1 exit status | ||
- | make[1]: *** [xmoto-edit] Error 1 | ||
- | </file> | ||
- | So let's try to find some of missing symbols: | ||
- | |||
- | |||
- | |||
- | <file>$ grep SDL_GetMouseState /usr/lib64/libSDL*.so | ||
- | Binary file /usr/lib64/libSDL.so matches | ||
- | </file> | ||
- | They are in ''-lSDL'', but binary does not link with ''-lSDL''. Edit Makefile by hand and add ''-lSDL'' at the same place ''-lSDL_mixer'' is: ''LIBS = -lcurl -lode -llualib50 -llua50 -lSDL_mixer -lSDL -lbz2 -lz -lpng -ljpeg'' | ||
- | |||
- | What we get after running make in build tree: | ||
- | |||
- | |||
- | |||
- | <file>x86_64-pld-linux-g++ -Wl,--as-needed -o xmoto-edit BuiltInFont.o [...object files...] Packager.o -lcurl -lode -llualib50 -llua50 -lSDL_mixer -lSDL -lbz2 -lz -lpng -ljpeg | ||
- | |||
- | VApp.o: In function `vapp::App::grabScreen()': | ||
- | src/VApp.cpp:667: undefined reference to `glReadBuffer' | ||
- | src/VApp.cpp:671: undefined reference to `glReadPixels' | ||
- | |||
- | [...more missing gl functions...] | ||
- | |||
- | src/Editor.cpp:1280: undefined reference to `glEnable' | ||
- | src/Editor.cpp:1288: undefined reference to `glDisable' | ||
- | |||
- | collect2: ld returned 1 exit status | ||
- | </file> | ||
- | Same story: | ||
- | |||
- | |||
- | |||
- | <file>$ grep glEnableClientState /usr/lib64/lib*.so | ||
- | Binary file /usr/lib64/libGL.so matches | ||
- | </file> | ||
- | ''LIBS = -lcurl -lode -llualib50 -llua50 -lSDL_mixer -lSDL -lGL -lbz2 -lz -lpng -ljpeg'' | ||
- | |||
- | But [[http://cvs.pld-linux.org/cgi-bin/cvsweb/SPECS/xmoto.spec?rev=1.21|take a look at spec file]], ''-lGL'' thing was fixed there already: | ||
- | |||
- | |||
- | |||
- | <file>%{__make} \ | ||
- | GL_LIBS="-lGL" | ||
- | </file> | ||
- | Anyway, lets run make, and what we get ? | ||
- | |||
- | |||
- | |||
- | <file>x86_64-pld-linux-g++ -Wl,--as-needed -o xmoto-edit BuiltInFont.o [...object files...] Packager.o -lcurl -lode -llualib50 -llua50 -lSDL_mixer -lSDL -lGL -lbz2 -lz -lpng -ljpeg | ||
- | |||
- | make[1]: Leaving directory `/home/users/sparky/rpm/BUILD/xmoto-0.2.0' | ||
- | </file> | ||
- | **It worked !** | ||
- | |||
- | |||
- | |||
- | == Why was it working without --as-needed ? == | ||
- | Answer is really easy: libSDL is required by SDL_mixer: | ||
- | |||
- | |||
- | |||
- | <file>$ ldd /usr/lib64/libSDL_mixer-1.2.so.0.2.4 | grep SDL | ||
- | libSDL-1.2.so.0 => /usr/lib64/libSDL-1.2.so.0 (0x00002ab425307000) | ||
- | </file> | ||
- | but xmoto-edit contains no SDL_mixer symbols, that's why it wasn't linked with SDL_mixer, and there was nothing providing SDL library. | ||
- | |||
- | Can you see now why was it stupid to disable ''--as-needed'' ? It worked just perfectly disabling unneeded library ! | ||
- | |||
- | |||
- | |||
- | |||
- | Finally, fix for this may be: | ||
- | |||
- | <file>%configure \ | ||
- | LIBS="-lSDL -lGL" | ||
- | </file> | ||
- | or patching configure.in, and the result is: | ||
- | |||
- | ''Wrote: /home/users/sparky/rpm/RPMS/xmoto-0.2.0-2.x86_64.rpm'' | ||
- | |||
- | |||
- | |||
- | ==== 2. unresolved symbols in library while linking executable ==== | ||
- | That's the most common and a little more difficult case, [[http://cvs.pld-linux.org/cgi-bin/cvsweb/SPECS/evolution-data-server.spec?rev=HEAD|evolution-data-server]]: | ||
- | |||
- | Compilation stops at this place: | ||
- | |||
- | |||
- | |||
- | <file>/bin/sh ../libtool --tag=CC --mode=link x86_64-pld-linux-gcc -Wl,--as-needed -o test-source-selector test-source-selector.o libedataserverui-1.2.la ../libedataserver/libedataserver-1.2.la -pthread -lglade-2.0 [...many -l libraries...] | ||
- | -lgnome-keyring -lpthread | ||
- | |||
- | x86_64-pld-linux-gcc -Wl,--as-needed -o .libs/test-source-selector test-source-selector.o -pthread ./.libs/libedataserverui-1.2.so [...many, many .so and -l libraries...] -lpthread | ||
- | |||
- | ./.libs/libedataserverui-1.2.so: undefined reference to `glade_xml_new' | ||
- | ./.libs/libedataserverui-1.2.so: undefined reference to `gnome_keyring_find_items_sync' | ||
- | ./.libs/libedataserverui-1.2.so: undefined reference to `gnome_keyring_get_default_keyring_sync' | ||
- | ./.libs/libedataserverui-1.2.so: undefined reference to `gnome_keyring_attribute_list_free' | ||
- | ./.libs/libedataserverui-1.2.so: undefined reference to `glade_xml_get_widget' | ||
- | ./.libs/libedataserverui-1.2.so: undefined reference to `gnome_keyring_create_sync' | ||
- | ./.libs/libedataserverui-1.2.so: undefined reference to `gnome_keyring_item_delete_sync' | ||
- | ./.libs/libedataserverui-1.2.so: undefined reference to `gnome_keyring_item_create_sync' | ||
- | |||
- | collect2: ld returned 1 exit status | ||
- | make[2]: *** [test-source-selector] Error 1 | ||
- | </file> | ||
- | It's different case, now it isn't problem with binary file, but with shared library. And the difficulty is it shows up at executable-linking time, because undefined symbols in libraries are permited. | ||
- | |||
- | OK, lets try to fix that library: | ||
- | |||
- | |||
- | |||
- | <file>$ grep gnome_keyring_item_delete_sync /usr/lib64/lib*.so | ||
- | Binary file /usr/lib64/libgnome-keyring.so matches | ||
- | $ grep glade_xml_new /usr/lib64/lib*.so | ||
- | Binary file /usr/lib64/libglade-2.0.so matches | ||
- | </file> | ||
- | Editing Makefile we find: | ||
- | |||
- | ''GNOME_KEYRING_LIBS = -lgnome-keyring -lglib-2.0'' | ||
- | |||
- | So add this one and ''-lglade-2.0'' to that library deps: | ||
- | |||
- | |||
- | |||
- | <file>libedataserverui_1_2_la_LIBADD = \ | ||
- | $(top_builddir)/addressbook/libebook/libebook-1.2.la \ | ||
- | $(GNOME_KEYRING_LIBS) -lglade-2.0 \ | ||
- | $(E_DATA_SERVER_LIBS) | ||
- | </file> | ||
- | As it uses libtool, remove .la to force regeneration: ''$ rm libedataserverui-1.2.la'' and try to build ''$ make'' passes without problems. | ||
- | |||
- | Now we only need to fix it correcly, after looking at configure.in and Makefile.am one can see it was only a typo: | ||
- | |||
- | |||
- | |||
- | <file>libedataserverui_1_2_la_LIBADD = \ | ||
- | $(top_builddir)/addressbook/libebook/libebook-1.2.la \ | ||
- | $(E_DATA_SERVER_UI_LIBS) | ||
- | ^^^ | ||
- | </file> | ||
- | |||
- | |||
- | == Why was it working without --as-needed ? == | ||
- | ''test-source-selector'' binary was linked with all libraries needed by libedataserverui-1.2.so, and the binary was the one who provided missing symbols to libedataserverui-1.2.so | ||
- | |||
- | |||
- | |||
- | === What if broken library comes from other package ? === | ||
- | You can check manually does library has all required symbols, simply running 'gcc //library// ', like this: | ||
- | |||
- | This is an example of correctly linked library: | ||
- | |||
- | <file>$ gcc /usr/lib64/liblftp-tasks.so.0.0.0 | ||
- | /usr/lib64/gcc/x86_64-pld-linux/4.1.2/../../../../lib64/crt1.o: In function `_start': | ||
- | (.text+0x20): undefined reference to `main' | ||
- | collect2: ld returned 1 exit status | ||
- | </file> | ||
- | And inclrrectly linked: | ||
- | |||
- | <file>$ gcc /usr/lib64/liblftp-jobs.so.0.0.0 | ||
- | /usr/lib64/gcc/x86_64-pld-linux/4.1.2/../../../../lib64/crt1.o: In function `_start': | ||
- | (.text+0x20): undefined reference to `main' | ||
- | /usr/lib64/liblftp-jobs.so.0.0.0: undefined reference to `StringSet::Remove(int)' | ||
- | /usr/lib64/liblftp-jobs.so.0.0.0: undefined reference to `Buffer::Format(char const*, ...)' | ||
- | [...] | ||
- | /usr/lib64/liblftp-jobs.so.0.0.0: undefined reference to `SMTask::SuspendSlave()' | ||
- | collect2: ld returned 1 exit status | ||
- | </file> | ||
- | |||
- | |||
- | ==== 3. unresolved symbols caused by incorrect order ==== | ||
- | Two of most difficult of common problems at once, [[http://cvs.pld-linux.org/cgi-bin/cvsweb/SPECS/evolution.spec?rev=HEAD|evolution.spec]]: | ||
- | |||
- | The difficulties are: | ||
- | |||
- | |||
- | - it appears in configure, what may be difficult to fix and difficult to find what the real problem is | ||
- | - the problem is not a missing library, but arguments order | ||
- | configure stops with such message, which says nothing: | ||
- | |||
- | |||
- | |||
- | <file>checking if pilot-link handles UTF-8 conversions... no | ||
- | configure: error: evolution requires pilot-link to have working UTF-8 conversion routines | ||
- | </file> | ||
- | let's look at config.log: | ||
- | |||
- | |||
- | |||
- | <file>x86_64-pld-linux-gcc -o conftest -ggdb -O2 -DORBIT2=1 -pthread -I/usr/include/libgnome-2.0 [...many -I...] -I/usr/include/libxml2 -Wl,--as-needed -pthread -lgpilotd [...many -l...] -lglib-2.0 conftest.c >&5 | ||
- | |||
- | /home/users/sparky/tmp/ccgrL9ll.o: In function `main': | ||
- | /home/users/sparky/rpm/BUILD/evolution-2.7.90/conftest.c:64: undefined reference to `convert_ToPilotChar' | ||
- | |||
- | collect2: ld returned 1 exit status | ||
- | </file> | ||
- | function '''convert_ToPilotChar''', may be found in passed library, but take a look at section about positions, arguments order is incorrect: libraries go before objects (conftest.c) it's very common when someone puts ''-l<>'' in ''LDFLAGS'' instead of ''LIBS'', that was the case too | ||
- | |||
- | just take a look at my fix: | ||
- | |||
- | |||
- | |||
- | <file>- LDFLAGS_save="$LDFLAGS" | ||
- | - LDFLAGS="$LDFLAGS $GNOME_PILOT_LIBS" | ||
- | + LIBS_save="$LIBS" | ||
- | + LIBS="$LIBS $GNOME_PILOT_LIBS" | ||
- | [...] | ||
- | - LDFLAGS="$LDFLAGS_save" | ||
- | + LIBS="$LIBS_save" | ||
- | </file> | ||
- | with this simple change everything works perfectly. | ||
- | |||