Reputation: 78
I'm trying to crosscompile some libraries for an ARM processor, specifically:
DirectFB, which depends on libpng, which depends on zlib.
Libpng is linked against zlib, but since the paths on the build system don't match the paths on the target system, ld can't find the zlib that libpng is linked against during the compilation of DirectFB.
I've reduced the issue to the following example: testb depends on libb, which depends on liba.
Build liba:
gcc -Wall -fPIC -c a.c
gcc -shared -Wl,-soname,liba.so.1 -o liba.so.1.0 a.o
ln -fs liba.so.1.0 liba.so.1
ln -fs liba.so.1 liba.so
Built testa:
gcc -Wall -I. -L. testa.c -la -o testa
LD_LIBRARY_PATH=. ./testa
a: 0
Build libb:
gcc -Wall -fPIC -I. -L. -c b.c -la
gcc -shared -Wl,-soname,libb.so.1 -o libb.so.1.0 b.o
ln -fs libb.so.1.0 libb.so.1
ln -fs libb.so.1 libb.so
So far, so good. However, building testb fails:
gcc -Wall -I. -L. testb.c -lb -o testb
Results in:
./libb.so: undefined reference to `a'
collect2: ld returned 1 exit status
make: *** [testb] Error 1
So ld can't find the definition of a in liba, even though libb is linked against liba, and both are in the current directory, which is being passed with "-L."
Running gcc with -v results in:
gcc -v -Wall -I. -L. testb.c -lb -o testb
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.4.3-4ubuntu5.1' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --program-suffix=-4.4 --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-plugin --enable-objc-gc --enable-targets=all --disable-werror --with-arch-32=i486 --with-tune=generic --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5.1)
COLLECT_GCC_OPTIONS='-v' '-Wall' '-I.' '-L.' '-o' 'testb' '-mtune=generic' '-march=i486'
/usr/lib/gcc/i486-linux-gnu/4.4.3/cc1 -quiet -v -I. testb.c -D_FORTIFY_SOURCE=2 -quiet -dumpbase testb.c -mtune=generic -march=i486 -auxbase testb -Wall -version -fstack-protector -o /tmp/ccqAkMFb.s
GNU C (Ubuntu 4.4.3-4ubuntu5.1) version 4.4.3 (i486-linux-gnu)
compiled by GNU C version 4.4.3, GMP version 4.3.2, MPFR version 2.4.2-p1.
GGC heuristics: --param ggc-min-expand=98 --param ggc-min-heapsize=128244
ignoring nonexistent directory "/usr/local/include/i486-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../i486-linux-gnu/include"
ignoring nonexistent directory "/usr/include/i486-linux-gnu"
#include "..." search starts here:
#include <...> search starts here:
.
/usr/local/include
/usr/lib/gcc/i486-linux-gnu/4.4.3/include
/usr/lib/gcc/i486-linux-gnu/4.4.3/include-fixed
/usr/include
End of search list.
GNU C (Ubuntu 4.4.3-4ubuntu5.1) version 4.4.3 (i486-linux-gnu)
compiled by GNU C version 4.4.3, GMP version 4.3.2, MPFR version 2.4.2-p1.
GGC heuristics: --param ggc-min-expand=98 --param ggc-min-heapsize=128244
Compiler executable checksum: 2349e080d2b2f3f970047e63bbe98cb2
COLLECT_GCC_OPTIONS='-v' '-Wall' '-I.' '-L.' '-o' 'testb' '-mtune=generic' '-march=i486'
as -V -Qy -o /tmp/ccc6ux7S.o /tmp/ccqAkMFb.s
GNU assembler version 2.20.1 (i486-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.20.1-system.20100303
COMPILER_PATH=/usr/lib/gcc/i486-linux-gnu/4.4.3/:/usr/lib/gcc/i486-linux-gnu/4.4.3/:/usr/lib/gcc/i486-linux-gnu/:/usr/lib/gcc/i486-linux-gnu/4.4.3/:/usr/lib/gcc/i486-linux-gnu/:/usr/lib/gcc/i486-linux-gnu/4.4.3/:/usr/lib/gcc/i486-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/i486-linux-gnu/4.4.3/:/usr/lib/gcc/i486-linux-gnu/4.4.3/:/usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib/:/lib/../lib/:/usr/lib/../lib/:/usr/lib/gcc/i486-linux-gnu/4.4.3/../../../:/lib/:/usr/lib/:/usr/lib/i486-linux-gnu/
COLLECT_GCC_OPTIONS='-v' '-Wall' '-I.' '-L.' '-o' 'testb' '-mtune=generic' '-march=i486'
/usr/lib/gcc/i486-linux-gnu/4.4.3/collect2 --build-id --eh-frame-hdr -m elf_i386 --hash-style=both -dynamic-linker /lib/ld-linux.so.2 -o testb -z relro /usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib/crt1.o /usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib/crti.o /usr/lib/gcc/i486-linux-gnu/4.4.3/crtbegin.o -L. -L/usr/lib/gcc/i486-linux-gnu/4.4.3 -L/usr/lib/gcc/i486-linux-gnu/4.4.3 -L/usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/i486-linux-gnu/4.4.3/../../.. -L/usr/lib/i486-linux-gnu /tmp/ccc6ux7S.o -lb -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i486-linux-gnu/4.4.3/crtend.o /usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib/crtn.o
./libb.so: undefined reference to `a'
collect2: ld returned 1 exit status
make: *** [testb] Error 1
Setting LD_LIBRARY_PATH and/or LIBRARY_PATH to the current directory doesn't resolve the problem.
Compiling with -la works, but it shouldn't be necessary, since libb is linked against liba. For example, when linking against libpng in a native build environment (correct paths, library paths in ld.so.conf) doesn't require -lz.
Also, setting additional libs to link against is a pain, because I would have to patch the Makefiles for every compilation that depends on a lib that depends on another lib.
In summary, how do I tell ld where to look for dependent shared libraries during compilation?
My problem is similar to these two: Cross-linking for ARM/QNX fails with indirect/transitive dependencies How to resolve shared library linking during compilation( GCC )?
Neither suggests a solution which doesn't require the addition of -la, which in my opinion shouldn't be necessary.
Files used:
a.c:
int a() {
return 0;
}
liba.h:
#ifndef LIBA_H
#define LIBA_H
int a();
#endif
testa.c:
#include <stdio.h>
#include <liba.h>
int main() {
printf("a: %d\r\n", a());
return 0;
}
b.c:
#include <liba.h>
int b() {
return a();
}
libb.h:
#ifndef LIBB_H
#define LIBB_H
int b();
#endif
testb.c:
#include <stdio.h>
#include <libb.h>
int main() {
printf("b: %d\r\n", b());
return 0;
}
Makefile:
# Makefile for shared library test
all:
liba:
gcc -Wall -fPIC -c a.c
gcc -shared -Wl,-soname,liba.so.1 -o liba.so.1.0 a.o
ln -fs liba.so.1.0 liba.so.1
ln -fs liba.so.1 liba.so
testa: liba
gcc -Wall -I. -L. testa.c -la -o testa
runtesta: testa
LD_LIBRARY_PATH=. ./testa
libb: liba
gcc -Wall -fPIC -I. -L. -c b.c -la
gcc -shared -Wl,-soname,libb.so.1 -o libb.so.1.0 b.o
ln -fs libb.so.1.0 libb.so.1
ln -fs libb.so.1 libb.so
testb: libb
gcc -v -Wall -I. -L. testb.c -lb -o testb
runtestb: testb
LD_LIBRARY_PATH=. ./testb
clean:
# clean up
rm -rf *.o
rm -rf liba.s*
rm -rf libb.s*
rm -rf testa testb
Upvotes: 4
Views: 3852
Reputation: 6999
You must add -la to linking phase of lb (second line below is essential):
$ gcc -Wall -fPIC -I. -L. -c b.c -la
$ gcc -L. -shared -o libb.so b.o -la
$ LD_LIBRARY_PATH=. gcc -Wall -I. -L. testb.c -lb -o testb
$ LD_LIBRARY_PATH=. ./testb
b: 0
Also -la on compilation isn't really required, I left it only because it was in your original example. Instead first line
$ gcc -Wall -fPIC -I. -L. -c b.c
will be enough.
Common rule is -- linker is guy, who need libraries, not compiler.
Upvotes: 2