10

I am observing a difference when trying to do the same operation on GCC 4.4 and GCC 4.5. Because the code I am doing this with is proprietary, I am unable to provide it, but I am observing a similar failure with this simple test case.

What I am basically trying to do is have one shared library (libb) depend on another shared library (liba). When loading libb, I assume that liba should be loaded as well - even though libb is not necessarily using the symbols in liba.

What I am observing is when I compile with GCC 4.4, I observe that the liba is loaded, but if I compile with GCC 4.5, libb is not loaded.

I have a small test case that consists of two files, a.c and b.c . The contents of the files:

//a.c
int a(){ 
    return 0; 
}

//b.c
int b(){
    return 0;
}
//c.c
#include <stdio.h>
int a();
int b();

int main()
{
    printf("%d\n", a()+b());
    return 0;
}
//test.sh    
$CC -o liba.so a.c -shared
$CC -o libb.so b.c -shared -L. -la -Wl,-rpath-link .
$CC c.c -L. -lb -Wl,-rpath-link .
LD_LIBRARY_PATH=. ./a.out

This is my output with different versions of GCC

$ CC=gcc-4.4 ./test.sh
1
$ CC=gcc-4.5 ./test.sh
/tmp/cceJhAqy.o: In function `main':
c.c:(.text+0xf): undefined reference to `a'
collect2: ld returned 1 exit status
./test.sh: line 4: ./a.out: No such file or directory
$ CC=gcc-4.6 ./test.sh
/tmp/ccoovR0x.o: In function `main':
c.c:(.text+0xf): undefined reference to `a'
collect2: ld returned 1 exit status
./test.sh: line 4: ./a.out: No such file or directory
$ 

Can anyone explain what is happening? Another extra bit of information is that ldd on libb.so does show liba.so on GCC 4.4 but not on GCC 4.5.

EDIT

I changed test.sh to the following:

$CC -shared -o liba.so a.c
$CC -L. -Wl,--no-as-needed -Wl,--copy-dt-needed-entries -la -shared -o libb.so b.c -Wl,-rpath-link . 
$CC -L. c.c -lb -Wl,-rpath-link . 
LD_LIBRARY_PATH=. ./a.out

This gave the following output with GCC 4.5:

/usr/bin/ld: /tmp/cc5IJ8Ks.o: undefined reference to symbol 'a'
/usr/bin/ld: note: 'a' is defined in DSO ./liba.so so try adding it to the linker command line
./liba.so: could not read symbols: Invalid operation
collect2: ld returned 1 exit status
./test.sh: line 4: ./a.out: No such file or directory

1 Answers1

12

There seems to have been changes in how DT_NEEDED libraries are treated during linking by ld. Here's the relevant part of current man ld:

With --copy-dt-needed-entries dynamic libraries mentioned on the command line will be recursively searched, following their DT_NEEDED tags to other libraries, in order to resolve symbols required by the output binary. With the default setting however the searching of dynamic libraries that follow it will stop with the dynamic library itself. No DT_NEEDED links will be traversed to resolve symbols.

(part of the --copy-dt-needed-entries section).

Some time between GCC 4.4 and GCC 4.5 (apparently, see some reference here - can't find anything really authoritative), the default was changed from the recursive search, to no recursive search (as you are seeing with the newer GCCs).

In any case, you can (and should) fix it by specifying liba in your final link step:

$CC c.c -L. -lb -la -Wl,-rpath-link .

You can check that this linker setting is indeed (at least part of) the issue by running with your newer compilers and this command line:

$CC c.c -L. -Wl,--copy-dt-needed-entries -lb -Wl,--no-copy-dt-needed-entries \
         -Wl,-rpath-link .
Mat
  • 202,337
  • 40
  • 393
  • 406
  • That would be a very good explanation, but adding the flags does not change the outcome. I have tried adding them both when building libb.so and when building c.c – Arne Bergene Fossaa Jan 04 '12 at 12:13
  • For reference, my version of LD is GNU ld (GNU Binutils for Ubuntu) 2.21.53.20110810 – Arne Bergene Fossaa Jan 04 '12 at 12:16
  • The flags are only needed on the final link, not when building the `.so` files. In any case, the real fix is to link your executable with both `libb` **and** `liba`. (I'm on ld 2.22.) – Mat Jan 04 '12 at 12:25
  • 1
    That is really not an option, since c.c is possibly customer code, and they will only see libb. What I _really_ want is some way to force that liba will be loaded when load libb. – Arne Bergene Fossaa Jan 04 '12 at 12:34
  • Whatever exe you build out of `c.c` **needs** both liba and libb at runtime. Why wouldn't liba be available? – Mat Jan 04 '12 at 12:36
  • Sorry, I canoot go any further on this without exposing too much internal information. The issue is that some version of liba (that does not contain the libb link) is available at link time, while _another_ version of liba (which has the libb link) is available at run time. Think of liba as a "shim" library that loads all of its symbols from libb. – Arne Bergene Fossaa Jan 04 '12 at 12:46
  • If your fake liba doesn't expose the same symbols as the "real" one, your setup shouldn't work at "real" runtime. If it does expose the same, then linking both libb and the fake liba should work. – Mat Jan 04 '12 at 12:48
  • Sorry, misinterpreted the results, using --copy-dt-needed-entries and --no-as-needed works as expected. Thank you!! – Arne Bergene Fossaa Jan 04 '12 at 13:05
  • 1
    This answer is priceless, solved [my problem](http://stackoverflow.com/questions/8042426/changes-introduced-in-gcc-4-5-with-respect-to-linking) too. Thanks so much. – Philippe Jan 04 '12 at 19:09