tdlrali
tdlrali

Reputation: 78

Telling ld where to look for dependent shared libraries during compilation

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

Answers (1)

Konstantin Vladimirov
Konstantin Vladimirov

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

Related Questions