3

我正在使用 gcc 在 linux 上编译 C 程序。该程序本身在构建时链接 libc(而不是其他),因此 ldd 给出以下输出:

$ 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)

在运行时,这个程序 dlopen() 的库 B,它依赖于库 A,当然 dlopen 在返回之前也会加载。A 导出一个名为 re_exec 的函数,B 调用该函数(B 与 A 链接)。libc 还导出了一个名为 re_exec 的函数。自述输出:

$ 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

问题是当 B 调用 re_exec 时,调用的是 libc 中的 re_exec,而不是 A 中的 re_exc。

如果,当我调用程序时,我包含 LD_LIBRARY_PRELOAD=/path/to/A.so,那么一切都按预期工作:Bs 调用 re_exec 正确调用 A,而不是 libc。

dlopen 调用通过 RTLD_NOW | RTLD_GLOBAL。我尝试过使用和不使用 DEEPBIND,并且在任何一种情况下都得到相同的行为。

我还尝试在 B 之前直接 dlopen()ing A,无论是否使用 DEEPBIND,这都不会影响行为。

问题:是否可以比链接时包含的库(在本例中为 libc)具有更高优先级的 dlopen A/B?

(请不要建议我将调用重命名为 re_exec 以外的名称;没用)

4

1 回答 1

1

好吧,你知道,我无法重现你的错误。请看一下:

puts.c

#include <stdio.h>

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

内置:

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!");
}

内置:

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

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;
}

内置:

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

现在我们可以运行rundlLD_LIBRARY_PATH=$(pwd)(它是必需的,因为libputs.so它不在ld.so已知的路径中,所以libfoo.so不能用dlopen()& Co 加载):

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

如果我们将 libputs.so 移动到已知目录ld.so并(重新)运行ldconfig以更新缓存,则代码在没有任何特殊环境变量的情况下运行:

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!

如果我链接libfoo.sow/o-lputs foo()调用puts()来自 libc 的标准。而已。

于 2015-01-04T09:03:01.023 回答