reassign 291641 gcc-3.3
retitle 291641 Checks dependencies of shared libraries without honouring RPATH
thanks

On Sat, 2005-01-22 at 12:47 +0000, Scott James Remnant wrote:

> On Sat, 2005-01-22 at 01:01 +0000, Philip Martin wrote:
> 
> > Sorry this is so long, a quick summary: Debian's libtool appears to resolve
> > inter-library dependencies to the install tree rather than the build tree,
> > this is different from the upstream GNU behaviour and can be awkward for
> > people using Debian as a development platform.
> > 
> I think I know what's causing this, can you just confirm something for
> me though.
> 
Yeah, I've put together a test case and confirmed what's happening here.
First let's go through the test case:

Create a binary and two levels of shared libraries beneath it.

----8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----
 
$ mkdir a b c
$ echo "int this_is_c() { return 0; }" > c/test.c
$ echo "int this_is_b() { this_is_c(); return 0; }" > b/test.c
$ echo "int main() { this_is_b(); return 0; }" > a/test.c
---->8-------->8-------->8-------->8-------->8-------->8-------->8-------->8----

Compile and link a shared library in 'c':

----8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----
$ cd c
$ libtool --mode=compile gcc -c test.c
mkdir .libs
 gcc -c test.c  -fPIC -DPIC -o .libs/test.o
 gcc -c test.c -o test.o >/dev/null 2>&1
$ libtool --mode=link gcc -o libtestc.la -rpath /usr/local/lib test.lo
gcc -shared  .libs/test.o   -Wl,-soname -Wl,libtestc.so.0 -o 
.libs/libtestc.so.0.0.0
(cd .libs && rm -f libtestc.so.0 && ln -s libtestc.so.0.0.0 libtestc.so.0)
(cd .libs && rm -f libtestc.so && ln -s libtestc.so.0.0.0 libtestc.so)
ar cru .libs/libtestc.a  test.o
ranlib .libs/libtestc.a
creating libtestc.la
(cd .libs && rm -f libtestc.la && ln -s ../libtestc.la libtestc.la)
---->8-------->8-------->8-------->8-------->8-------->8-------->8-------->8----

Do the same in 'b', and link that shared library to the libtestc.la
we've just built:

----8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----
$ cd ../b
$ libtool --mode=compile gcc -c test.c
mkdir .libs
 gcc -c test.c  -fPIC -DPIC -o .libs/test.o
 gcc -c test.c -o test.o >/dev/null 2>&1
$ libtool --mode=link gcc -o libtestb.la -rpath /usr/local/lib test.lo 
../c/libtestc.la
gcc -shared  .libs/test.o  -Wl,--rpath -Wl,/home/scott/tmp/lt-test/c/.libs 
-Wl,--rpath -Wl,/usr/local/lib ../c/.libs/libtestc.so  -Wl,-soname 
-Wl,libtestb.so.0 -o .libs/libtestb.so.0.0.0
(cd .libs && rm -f libtestb.so.0 && ln -s libtestb.so.0.0.0 libtestb.so.0)
(cd .libs && rm -f libtestb.so && ln -s libtestb.so.0.0.0 libtestb.so)
ar cru .libs/libtestb.a  test.o
ranlib .libs/libtestb.a
creating libtestb.la
(cd .libs && rm -f libtestb.la && ln -s ../libtestb.la libtestb.la)
---->8-------->8-------->8-------->8-------->8-------->8-------->8-------->8----

And now compile and link a binary in 'a', linking it to *just*
libtestb.la (after all, it uses nothing from libtestc.la itself).

----8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----
 
$ cd ../a
$ libtool --mode=compile gcc -c test.c
mkdir .libs
 gcc -c test.c  -fPIC -DPIC -o .libs/test.o
 gcc -c test.c -o test.o >/dev/null 2>&1
$ libtool --mode=link gcc -o test test.lo ../b/libtestb.la
gcc -o .libs/test .libs/test.o  ../b/.libs/libtestb.so  -Wl,--rpath 
-Wl,/usr/local/lib
creating test
---->8-------->8-------->8-------->8-------->8-------->8-------->8-------->8----


Notice that this works, I theorise that it's because gcc assumes because
it can't find the libtestc.so dependency of libtestb.so, it doesn't
bother to check the symbols match, rather than warning.

Now, let's install both shared libraries and the binary (we'll skip the
output here because Libtool relinks things to take away the rpath --
more on that in a moment).


Now let's make a change to libtestc, we'll rename the function,
recompile and relink it:

----8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----
 
$ cd ../c
$ echo "int this_is_new_c() { return 0; }" > test.c
$ libtool --mode=compile gcc -c test.c
 gcc -c test.c  -fPIC -DPIC -o .libs/test.o
 gcc -c test.c -o test.o >/dev/null 2>&1
$ libtool --mode=link gcc -o libtestc.la -rpath /usr/local/lib test.lo rm -fr  
.libs/libtestc.a .libs/libtestc.la .libs/libtestc.lai .libs/libtestc.so 
.libs/libtestc.so.0 .libs/libtestc.so.0.0.0
gcc -shared  .libs/test.o   -Wl,-soname -Wl,libtestc.so.0 -o 
.libs/libtestc.so.0.0.0
(cd .libs && rm -f libtestc.so.0 && ln -s libtestc.so.0.0.0 libtestc.so.0)
(cd .libs && rm -f libtestc.so && ln -s libtestc.so.0.0.0 libtestc.so)
ar cru .libs/libtestc.a  test.o
ranlib .libs/libtestc.a
creating libtestc.la
(cd .libs && rm -f libtestc.la && ln -s ../libtestc.la libtestc.la)
---->8-------->8-------->8-------->8-------->8-------->8-------->8-------->8----

Obviously we now have to change libtestc, recompile and relink that too:

----8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----
 
$ cd ../b
$ echo "int this_is_b() { this_is_new_c(); return 0; }" > test.c
$ libtool --mode=compile gcc -c test.c
 gcc -c test.c  -fPIC -DPIC -o .libs/test.o
 gcc -c test.c -o test.o >/dev/null 2>&1
$ libtool --mode=link gcc -o libtestb.la -rpath /usr/local/lib test.lo 
../c/libtestc.la
rm -fr  .libs/libtestb.a .libs/libtestb.la .libs/libtestb.lai .libs/libtestb.so 
.libs/libtestb.so.0 .libs/libtestb.so.0.0.0
gcc -shared  .libs/test.o  -Wl,--rpath -Wl,/home/scott/tmp/lt-test/c/.libs 
-Wl,--rpath -Wl,/usr/local/lib ../c/.libs/libtestc.so  -Wl,-soname 
-Wl,libtestb.so.0 -o .libs/libtestb.so.0.0.0
(cd .libs && rm -f libtestb.so.0 && ln -s libtestb.so.0.0.0 libtestb.so.0)
(cd .libs && rm -f libtestb.so && ln -s libtestb.so.0.0.0 libtestb.so)
ar cru .libs/libtestb.a  test.o
ranlib .libs/libtestb.a
creating libtestb.la
(cd .libs && rm -f libtestb.la && ln -s ../libtestb.la libtestb.la)
---->8-------->8-------->8-------->8-------->8-------->8-------->8-------->8----

Now, because we only linked the 'test' binary to libtestb, and we
haven't changed the SONAME, we shouldn't need to recompile or relink the
binary.  But what happens if we do?

----8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----
 
$ cd ../a
$ libtool --mode=link gcc -o test test.lo ../b/libtestb.la
gcc -o .libs/test .libs/test.o  ../b/.libs/libtestb.so  -Wl,--rpath 
-Wl,/usr/local/lib
../b/.libs/libtestb.so: undefined reference to `this_is_new_c'
collect2: ld returned 1 exit status
---->8-------->8-------->8-------->8-------->8-------->8-------->8-------->8----

This fails, because it cannot find a reference to this_is_new_c; I
theorise that because gcc *can* find a libtestc.so in /usr/local/lib, it
checks libtestb.so against that one, rather than the copy in the build
directory.

Note that gcc is clearly checking the correct libtestb.so, which is good
because it's been given the path to the one we want.


So why does this work with the upstream Libtool?  Because the upstream
Libtool, in this test case, would link the binary to *both* shared
libraries.  This is because it doesn't trust the Linux link loader to do
the right thing, when it does.

It actually turns out that all gcc needs is to know the path to the
libtestc.so in the build directory, adding an RPATH to the binary is
sufficient:

----8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----
 
$ libtool --mode=link gcc -o test test.lo ../b/libtestb.la -Wl,--rpath 
-Wl,../c/.libs
gcc -o .libs/test .libs/test.o -Wl,--rpath -Wl,../c/.libs  
../b/.libs/libtestb.so  -Wl,--rpath -Wl,/usr/local/lib
creating test
---->8-------->8-------->8-------->8-------->8-------->8-------->8-------->8----

Success.


So now, why do I think this is a gcc bug?

gcc is being given, without doubt, the correct libtestb.so to link -- we
pass it in directly on the command line without using -L or -l to
influence it.

And it's clearly using that library (and not the one in /usr/local/lib)
as it's the new symbol that it's failing on.

This is the library it's looking at:

----8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<----
 
$ objdump -p b/.libs/libtestb.so

b/.libs/libtestb.so:     file format elf32-i386

Program Header:
    LOAD off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**12
         filesz 0x000007b0 memsz 0x000007b0 flags r-x
    LOAD off    0x000007b0 vaddr 0x000017b0 paddr 0x000017b0 align 2**12
         filesz 0x0000011c memsz 0x00000120 flags rw-
 DYNAMIC off    0x000007bc vaddr 0x000017bc paddr 0x000017bc align 2**2
         filesz 0x000000d8 memsz 0x000000d8 flags rw-
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2
         filesz 0x00000000 memsz 0x00000000 flags rw-

Dynamic Section:
  NEEDED      libtestc.so.0
  NEEDED      libc.so.6
  SONAME      libtestb.so.0
  RPATH       /home/scott/tmp/lt-test/c/.libs:/usr/local/lib
  INIT        0x5d0
  FINI        0x790
  HASH        0xb4
  STRTAB      0x434
  SYMTAB      0x1e4
  STRSZ       0xef
  SYMENT      0x10
  PLTGOT      0x18a8
  PLTRELSZ    0x18
  PLTREL      0x11
  JMPREL      0x5b8
  REL         0x590
  RELSZ       0x28
  RELENT      0x8
  VERNEED     0x570
  VERNEEDNUM  0x1
  VERSYM      0x524
  RELCOUNT    0x2

Version References:
  required from libc.so.6:
    0x09691f73 0x00 02 GLIBC_2.1.3

---->8-------->8-------->8-------->8-------->8-------->8-------->8-------->8----


The library clearly indicates that it's linked to libtestc.so.0 with its
NEEDED line, gcc looks for that in /usr/local/lib (after all, it hasn't
been told anywhere better on the link line) and picks the old version.

Except gcc *does* know where the version it's supposed to be looking at
is located, the path to it is sitting in the shared library's RPATH
field:

  NEEDED      libtestc.so.0
  RPATH       /home/scott/tmp/lt-test/c/.libs:/usr/local/lib


At this point, gcc is checking the symbol references of libtestb.so
against those libraries listed in its NEEDED lines *without* honouring
the RPATH line.


In summary:

  When finding/loading a dependency of a shared library, gcc should
  honour the RPATH of that shared library.

Scott
-- 
Have you ever, ever felt like this?
Had strange things happen?  Are you going round the twist?

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to