1

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

主文件

#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

谁能解释一下后台发生了什么?提前致谢。

4

1 回答 1

3

这是一个有趣的(对我来说也是意料之外的)结果。

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

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

将此与具有相同输出但从 s 链接行中删除的 进行-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.6fopen第一种情况下不搜索?

答案:加载器在链接链中有一个库的线性列表_r_debug.r_map,并且 for将在调用的库之后RTLD_NEXT搜索库。dlopen

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

情况1:

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)

现在应该很清楚,对于案例 2,情况libc.so.6如下lib1.so,但对于案例 1,情况并非如此。

我还不明白是什么导致了这种特殊的排序。我得再考虑一下。

于 2014-04-12T23:14:25.017 回答