分类: 嵌入式
2009-11-15 15:33:38
If you've ever tried to cross-compile a free software project for a
different architecture on GNU/Linux, you may well have run into the
situation where you compile a library with your cross toolchain,
install it in a “staging” directory that will hold your
root filesystem (for example, you compile the library to go into
/usr/lib
, but you install it
in /tmp/rootfs/usr/lib
, because that's where you're
building your new root filesystem. You do this correctly: specify
your --prefix=/usr
when you configure
, but
provide DESTDIR=/tmp/rootfs
when you make
install
), and you try to compile another library that links
against that library. Everything goes fine (the author of the
program may have even written his configure script properly, so you
can configure it to use the correct toolchain), but at the link
step, you get this:
/bin/sh libtool --mode=link target-gcc -c -O2 -o libbar.so ... -lfoo
target-gcc -c -O2 -o libbar.so ... /usr/lib/libfoo.so
/usr/lib/libfoo.so: could not read symbols: File in wrong format
collect2: ld returned 1 exit status
Now, why is the command trying to link
against /usr/lib/libfoo.so
, which is your system's version
of libfoo
, and not /tmp/rootfs/usr/lib/libfoo.so
?
You might play with compiler and linker flags, try to hack and
understand the libtool shell script (best of luck if you can
decipher it), and end up cursing the ancestors of those who ever
came up with such a dumb system.
The problem turns out to be that libfoo
and libbar
in this example both use libtool, and that the
prefix happens to conflict with the system's library locations; when
libtool installs a library, you'll see it
installs libfoo.la
.
What's with that file? You may, reasonably, expect to see one or
two different kinds of library file: libfoo.a
, which is
just an archive of object files, and which you can statically link
into your program; and libfoo.so
, a dynamically loadable,
sharable library. What's a .la
? Just cat
one, and
you'll see:
$ cat /usr/lib/libesd.la
# libesd.la - a libtool library file
# Generated by ltmain.sh - GNU libtool 1.5.6 (1.1220.2.95 2004/04/11 05:50:42) Debian: 224 $
#
# Please DO NOT delete this file!
# It is necessary for linking the library.
# The name that we can dlopen(3).
dlname='libesd.so.0'
# Names of this library.
library_names='libesd.so.0.2.36 libesd.so.0 libesd.so'
# The name of the static archive.
old_library='libesd.a'
# Libraries that this one depends upon.
dependency_libs=' -L/usr/lib /usr/lib/libaudiofile.la -lm'
# Version information for libesd.
current=2
age=2
revision=36
# Is this an already installed library?
installed=yes
# Should we warn about portability when linking against -modules?
shouldnotlink=no
# Files to dlopen/dlpreopen
dlopen=''
dlpreopen=''
# Directory that this library needs to be installed in:
libdir='/usr/lib'
Notice the last line? It says that libesd
is installed
in /usr/lib
, so when libtool finds your libfoo.la
in your temporary root filesystem tree, that file tells it to go
look in /usr/lib
. So, you have to prevent the libtool file
from confusing the libtool script at link time.
You can probably just remove the libtool library file, and rely on
your linker to figure out what you mean by -lfoo
(even
though the .la
strongly admonishes you not to delete
it), but perhaps better is to just update libdir
in
this file to point to where your library is temporarily
installed. Using GNU sed you can do this:
sed -i~ -e "s;/usr;/tmp/rootfs/usr;" /tmp/rootfs/usr/lib/libfoo.la
I do this just after installing the cross-compiled library into my temporary root filesystem tree.
You likely won't need the libtool files any more when you install (if you're targeting an embedded Linux system), so you don't care if those files are correct once installed on the target. You'll probably just remove them from the final image, anyway.
I wrote this because I spent a long night of fighting libtool, Googling for help and finding none, and finally figuring this out. This is the second time I've even figured this out, having forgotten the solution the first time. Hopefully, this will help you out if you encounter this issue.
For some packages, you seem to also run into trouble where libtool can't figure out what you're doing:
$ libtool --mode=compile target-gcc -g -O2 -c foo.c
libtool: compile: unable to infer tagged configuration
libtool: compile: specify a tag with `--tag'
OK, so it couldn't figure out that we're compiling C code. No
problem, just add the tag --tag=CC
. Works when we
compile:
$ libtool --tag=CC --mode=compile target-gcc -g -O2 -c foo.c
target-gcc -g -O2 -c foo.c -fPIC -DPIC -o .libs/foo.o
target-gcc -g -O2 -c foo.c -o foo.o >/dev/null 2>&1
But when we link, libtool tries to call the native gcc:
$ libtool --tag=CC --mode=link target-gcc -rpath /usr/lib -o libfoo.la foo.lo
gcc -shared .libs/foo.o -Wl,-soname -Wl,libfoo.so.0 -o .libs/libfoo.so.0.0.0
/usr/bin/ld: .libs/foo.o: Relocations in generic ELF (EM: 40)
/usr/bin/ld: .libs/foo.o: Relocations in generic ELF (EM: 40)
/usr/bin/ld: .libs/foo.o: Relocations in generic ELF (EM: 40)
/usr/bin/ld: .libs/foo.o: Relocations in generic ELF (EM: 40)
/usr/bin/ld: .libs/foo.o: Relocations in generic ELF (EM: 40)
/usr/bin/ld: .libs/foo.o: Relocations in generic ELF (EM: 40)
.libs/foo.o: could not read symbols: File in wrong format
collect2: ld returned 1 exit status
One rumored workaround (I haven't tried this, since I haven't had
to cross compile anything in a while) is to specify a proper rpath
in LDFLAGS
:
-Wl,-rpath -Wl,/usr/lib -Wl,-rpath-link -Wl,${STAGING_DIR}/usr/lib -L${STAGING_DIR}/lib -L${STAGING_DIR}/usr/lib
Thanks to Waldemar Brodkorb for this workaround. Let me know if this works for you.
Another solution to this problem is to just install a cross libtool, which you'll use in preference to your system's libtool. It's easy enough to do; go , unpack it, and install it with:
$ tar xzf libtool-1.5.22.tar.gz
$ cd libtool-1.5.22
libtool-1.5.22 $ ./configure --prefix=/opt/Toolchain --host=target --program-prefix=target-
configure output...
libtool-1.5.22 $ make
make output...
libtool-1.5.22 $ sudo make install
Then, you can just use target-libtool
when cross
compiling. That libtool (which is still just a shell script, so you
needn't worry about running it on the host) will be set up properly
for using your target-gcc
. You usually just have to add
an environment variable LIBTOOL=target-libtool
when
you configure
your package.
Happy Hacking.