3

I am compiling a C program on linux with gcc. The program itself links libc (and not much else) at build-time, so that ldd gives this output :

$ ldd myprogram
    linux-vdso.so.1 =>  (0x00007fffd31fe000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7a991c0000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f7a99bba000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f7a98fbb000)

At run-time this program dlopen()s library B, which dependes on a library A, which of course dlopen also loads before returning. A exports a function called re_exec, which B invokes (B is linked against A). libc also exports a function called re_exec. readelf output :

$ readelf -as A.so | grep re_exec
 104: 00000000000044ff   803 FUNC    GLOBAL PROTECTED   11 re_exec
 469: 00000000000044ff   803 FUNC    GLOBAL PROTECTED   11 re_exec

$ readelf -as /lib/x86_64-linux-gnu/libc.so.6 | grep re_exec
 2165: 00000000000e4ae0    39 FUNC    WEAK   DEFAULT   12 re_exec@@GLIBC_2.2.5

The problem is that when B invokes re_exec, the re_exec inside libc is called, NOT the re_exc inside of A.

If, when I invoke the program, I include LD_LIBRARY_PRELOAD=/path/to/A.so, then everything works as expected : Bs invocation of re_exec correctly calls A, and not libc.

The dlopen call passes RTLD_NOW | RTLD_GLOBAL. I have tried with and without DEEPBIND, and get the same behavior in either case.

I have also tried dlopen()ing A directly, before B, both with and without DEEPBIND, which did not affect the behavior.

The question : is it possible to dlopen A/B with higher precedence than libraries that were included at link-time (libc, in this case) ?

(please don't suggest that I rename the call to something other than re_exec ; not useful)

Todd Freed
  • 877
  • 6
  • 19
  • I am not sure that redefining standard `libc` names is a good idea. You want `LD_PRELOAD`; maybe referencing directly `re_exec` from your program might help. – Basile Starynkevitch Jan 04 '15 at 04:13
  • I guess I should say; I may end up having to rename the function. I did not intend to use a name exported by libc and would not have used that name had I known. But if I go that route, it isn't really solving this problem, its just avoiding it. – Todd Freed Jan 04 '15 at 21:03

1 Answers1

1

Well, you know, I can't reproduce your error. Please take a look:

puts.c:

#include <stdio.h>

int puts(const char* _s) {
    return printf("custom puts: %s\n", _s);
}

built with:

cc -Wall -fPIC -c puts.c -o puts.o
cc -shared -o libputs.so -fPIC -Wl,-soname,libputs.so puts.o

foo.c:

#include <stdio.h>

void foo() {
    puts("Hello, world! I'm foo!");
}

built with:

cc -Wall -fPIC -c foo.c -o foo.o
cc -L`pwd` -shared -o libfoo.so -fPIC -Wl,-soname,libfoo.so foo.o -lputs

and rundl.c:

#include <dlfcn.h>
#include <assert.h>
#include <stdio.h>

typedef void (*FooFunc)();

int main(void) {
    void *foolib = dlopen("./libfoo.so", RTLD_NOW | RTLD_GLOBAL | RTLD_DEEPBIND);
    assert(foolib != NULL);
    FooFunc foo = (FooFunc)dlsym(foolib, "foo");
    assert(foo != NULL);
    foo();
    return 0;
}

built with:

cc -c -Wall rundl.c -o rundl.o
cc -o rundl rundl.o -ldl

now we can run rundl with LD_LIBRARY_PATH=$(pwd) (it's needed because libputs.so isn't in the ld.so known paths so libfoo.so can't be loaded w/ dlopen() & Co):

alex@rhyme ~/tmp/dynlib $ LD_LIBRARY_PATH=`pwd` ./rundl
custom puts: Hello, world! I'm foo!
alex@rhyme ~/tmp/dynlib $ _

if we move libputs.so to a directory known to ld.so and (re)run ldconfig to update caches then the code runs without any special environment variables:

alex@rhyme ~/tmp/dynlib $ ldd ./libfoo.so 
    linux-vdso.so.1 (0x00007fff48db8000)
    libputs.so => /usr/local/lib64/libputs.so (0x00007f8595450000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f85950a0000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f8595888000)
alex@rhyme ~/tmp/dynlib $ ./rundl 
custom puts: Hello, world! I'm foo!

If I link libfoo.so w/o -lputs foo() invokes the standard puts() from libc. That's it.

user3159253
  • 16,836
  • 3
  • 30
  • 56
  • Okay thank you for responding. I don't have time right at the moment but I will provide a reproducible test case later on today. – Todd Freed Jan 04 '15 at 16:50
  • Okay I am not able to reproduce it in a small test case, either. In my small test, DEEPBIND does the trick. I have verified that it is still not doing what I want in my application, but I guess there is something more going on. I have sifted through the LD_DEBUG output, and it appears that at times, A comes before libc in the symbol resolution order, and sometimes it does not. – Todd Freed Jan 04 '15 at 23:10