RTLD_NEXT无法正常工作

时间:2014-04-08 08:57:37

标签: gcc glibc dlopen dlsym uclibc

我使用以下测试代码减少了我的问题,

main.cc

#include <iostream>

int main(int argc, const char** argv) {
  void init2();
  init2();
  return 0;
}

2.cc

#include <iostream>

int init2() {
  void init1();
  init1();
  std::cout<<"init2 called\n";
  return 0;
}

1.cc

#include <dlfcn.h>
#include <pthread.h>
#include <stdio.h>
#include <iostream>

typedef FILE* (*FopenFunction)(const char* path, const char* mode);

static FopenFunction g_libc_fopen = NULL;

void init1() {
 g_libc_fopen = reinterpret_cast<FopenFunction>(
          dlsym(RTLD_NEXT, "fopen"));

 std::cout<<"init1: fopen addr:"<<(void*)g_libc_fopen<<"\n";
}

__attribute__ ((__visibility__("default")))
FILE* fopen_override(const char* path, const char* mode)  __asm__ ("fopen");

__attribute__ ((__visibility__("default")))
FILE* fopen_override(const char* path, const char* mode) {
  return g_libc_fopen(path, mode);
}

将1.cc编译成lib1.so,将2.cc编译成lib2.so,如下所示,

g++ 1.cc -shared -ldl -fvisibility=default -fPIC -o lib1.so -L.
g++ 2.cc -shared -ldl -fvisibility=default -fPIC -o lib2.so -l1 -L.
g++ main.cc -l2 -l1 -L.

以上步骤将产生lib1.so,lib2.so和a.out。这里的问题是在运行可执行文件a.out时,在使用dlsym(RTLD_NEXT)时无法查找原始的“fread”符号。

输出是,

arunprasadr@demo:~/works/myex/c++/rtdl_next$ LD_LIBRARY_PATH=./ ./a.out
init1: fopen addr:0
init2 called

但是如果更改lib2.so的链接过程(如下所示),它似乎正在工作

g++ 2.cc -shared -ldl -fvisibility=default -fPIC -o lib2.so -L.
g++ main.cc -l2 -l1 -L.
LD_LIBRARY_PATH=./ ./a.out

输出:

arunprasadr@demo:~/works/myex/c++/rtdl_next$ LD_LIBRARY_PATH=./ ./a.out
init1: fopen addr:0x7f9e84a9e2c0
init2 called

有谁能请解释后台发生的事情?提前谢谢。

1 个答案:

答案 0 :(得分:2)

这是一个有趣的(对我而言意外)结果。

首先,使用原始命令,我观察:

LD_DEBUG=symbols,bindings LD_LIBRARY_PATH=./ ./a.out |& grep fopen
     10204: symbol=fopen;  lookup in file=/lib/x86_64-linux-gnu/libm.so.6 [0]
     10204: symbol=fopen;  lookup in file=/lib64/ld-linux-x86-64.so.2 [0]
     10204: symbol=fopen;  lookup in file=/lib/x86_64-linux-gnu/libgcc_s.so.1 [0]
     10204: symbol=fopen;  lookup in file=/lib/x86_64-linux-gnu/libdl.so.2 [0]
init1: fopen addr:0

将此与同一输出进行比较,但从-l1的链接行中删除lib2.so

LD_DEBUG=symbols,bindings LD_LIBRARY_PATH=./ ./a.out |& grep fopen
     10314: symbol=fopen;  lookup in file=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]
     10314: symbol=fopen;  lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
     10314: binding file ./lib1.so [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `fopen'
init1: fopen addr:0x7f03692352c0

接下来的问题是:在第一种情况下,为什么加载器不在libc.so.6搜索fopen

答案:加载器在_r_debug.r_map链接链中有一个线性的库列表,RTLD_NEXT将在调用{{1>}之后搜索库 }}

案例1和案例2的库顺序是否不同?你敢打赌:

案例1:

dlopen

案例2:

LD_LIBRARY_PATH=./ ldd ./a.out
    linux-vdso.so.1 =>  (0x00007fff2f1ff000)
    lib2.so => ./lib2.so (0x00007f54a2b12000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f54a27f1000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f54a2430000)
    lib1.so => ./lib1.so (0x00007f54a222e000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f54a1f32000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f54a2d16000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f54a1d1b000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f54a1b17000)

现在应该清楚的是,案例2 LD_LIBRARY_PATH=./ ldd ./a.out linux-vdso.so.1 => (0x00007fff39fff000) lib2.so => ./lib2.so (0x00007f8502329000) lib1.so => ./lib1.so (0x00007f8502127000) libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f8501e05000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8501a45000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f8501841000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f8501544000) /lib64/ld-linux-x86-64.so.2 (0x00007f850252d000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f850132e000) libc.so.6之后,但案例1则没有。{/ p>

我还不明白是什么导致了这种特殊的排序。我将不得不考虑更多。