Re: strange behavior of glibc/libdl function chaining on linux.

From: Peter T. Breuer (ptb_at_oboe.it.uc3m.es)
Date: 08/18/05


Date: Thu, 18 Aug 2005 21:21:43 +0200

In comp.os.linux.development.system Yann LANGLAIS <langlais@ilay.org> wrote:
> different libraries using dlsym/RTLD_NEXT :

RTLD_NEXT is not documented in my man page (for dlsym), but
/usr/include/dlfcn.h has this:

 /* If the first argument of `dlsym' or `dlvsym' is set to RTLD_NEXT
    the run-time address of the symbol called NAME in the next shared
    object is returned. The "next" relation is defined by the order
    the shared objects were loaded. */
 #define RTLD_NEXT ((void *) -1l)

So I guess the order is not defined as you think it is defined! It
appears in particular not to be defined at all between dynamically
linked libraries opened with dlopen, or perhaps the notion of "current"
library is different to what you think when several are open.

Mind you, the man page is clearly "wrong" in places too .. it says that
_init() in the shared dynamically loaded object will be executed
autmatically. All it does for me is provoke an error about a name clash
in crt0.o or something like.

> Here is a compile time link of the chain components :
> for i in 1 2 3 4
> do
> cat > lib$i.c <<EOF
> #include <stdio.h>
> #include <dlfcn.h>
> void foo() {
> void (*next_foo)(void);
> printf("lib$i.foo()\n");
> if (next_foo = (void (*)(void)) dlsym(RTLD_NEXT, "foo")) next_foo();
> }
> EOF
> gcc -shared -fPIC lib$i.c -o lib$i.so -D_GNU_SOURCE
> done
> cat > chain.c <<EOF
> #include <dlfcn.h>
> extern void foo();
> int main() {
> foo();
> return 0;
> }
> EOF
> gcc chain.c -o chain -L. -l1 -l2 -l3 -l4 -ldl
> LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./chain

> And the result :

> lib1.foo()
> lib2.foo()
> lib3.foo()
> lib4.foo()

> And now a runtime linking version:

> cat > chain2.c <<EOF
> #include <dlfcn.h>
> int main() {
> void *l1, *l2, *l3, *l4;
> void (*bar)();
> l1 = dlopen("lib1.so", RTLD_NOW | RTLD_GLOBAL);
> l2 = dlopen("lib2.so", RTLD_NOW | RTLD_GLOBAL);
> l3 = dlopen("lib3.so", RTLD_NOW | RTLD_GLOBAL);
> l4 = dlopen("lib4.so", RTLD_NOW | RTLD_GLOBAL);
> bar = (void (*)()) dlsym(RTLD_DEFAULT, "foo");
> bar();
> dlclose(l4);
> dlclose(l3);
> dlclose(l2);
> dlclose(l1);
> return 0;
> }
> EOF

Hmmm.

> gcc chain2.c -o chain2 -ldl -D_GNU_SOURCE

> LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./chain2

> And the result :

> lib1.foo()

What? No other output at all? So the lookups yield NULL?

I would guess that the libraries are chained in reverse order. Try
testing for that.

> Period. Other functions in other libs are ignored.

> Is this behavior normal under glibc (which on linux implements dlfcn
> functions) ?

> On other platforms, such as Solaris 8 using native "forte" compiler and
> native libdl.so, the result of chain2 is the same as the one of chain.

Peter