动态链接器如何知道在哪个库中搜索符号?

Som*_*ame 5 c linux shared-libraries dynamic-linking

我正在尝试LD_PRELOAD/dlopen并在符号查找方面遇到了困惑。考虑以下 2 个库:

  1. libshar

shared.h

int sum(int a, int b);
Run Code Online (Sandbox Code Playgroud)

shared.c

int sum(int a, int b){
    return a + b;
}
Run Code Online (Sandbox Code Playgroud)
  1. libshar2

shared.h

int sum(int a, int b);
Run Code Online (Sandbox Code Playgroud)

shared.c

int sum(int a, int b){
    return a + b + 10000;
}
Run Code Online (Sandbox Code Playgroud)

和可执行文件bin_shared

#include <dlfcn.h>
#include "shared.h"

int main(void){
    void *handle = dlopen("/home/me/c/build/libshar2.so", RTLD_NOW | RTLD_GLOBAL);
    int s = sum(2 + 3);
    printf("s = %d", s);
}
Run Code Online (Sandbox Code Playgroud)

将二进制文件与 链接起来libsharlibdl我考虑了以下两种情况:

  1. LD_PRELOAD是空的

程序打印5.

sum为什么动态链接器决定在 中查找函数libshar,而不是libshar2?它们都已加载并包含所需的符号:

0x7ffff73dc000     0x7ffff73dd000     0x1000        0x0 /home/me/c/build/libshar2.so
0x7ffff73dd000     0x7ffff75dc000   0x1ff000     0x1000 /home/me/c/build/libshar2.so
0x7ffff75dc000     0x7ffff75dd000     0x1000        0x0 /home/me/c/build/libshar2.so
0x7ffff75dd000     0x7ffff75de000     0x1000     0x1000 /home/me/c/build/libshar2.so
#...
0x7ffff7bd3000     0x7ffff7bd4000     0x1000        0x0 /home/me/c/build/libshar.so
0x7ffff7bd4000     0x7ffff7dd3000   0x1ff000     0x1000 /home/me/c/build/libshar.so
0x7ffff7dd3000     0x7ffff7dd4000     0x1000        0x0 /home/me/c/build/libshar.so
0x7ffff7dd4000     0x7ffff7dd5000     0x1000     0x1000 /home/me/c/build/libshar.so
Run Code Online (Sandbox Code Playgroud)
  1. LD_PRELOAD = /path/to/libshar2.so

程序打印10005. 这是预料之中的,但我再次注意到 和libshar.so都已libshar2.so加载:

0x7ffff79d1000     0x7ffff79d2000     0x1000        0x0 /home/me/c/build/libshar.so
0x7ffff79d2000     0x7ffff7bd1000   0x1ff000     0x1000 /home/me/c/build/libshar.so
0x7ffff7bd1000     0x7ffff7bd2000     0x1000        0x0 /home/me/c/build/libshar.so
0x7ffff7bd2000     0x7ffff7bd3000     0x1000     0x1000 /home/me/c/build/libshar.so
0x7ffff7bd3000     0x7ffff7bd4000     0x1000        0x0 /home/me/c/build/libshar2.so
0x7ffff7bd4000     0x7ffff7dd3000   0x1ff000     0x1000 /home/me/c/build/libshar2.so
0x7ffff7dd3000     0x7ffff7dd4000     0x1000        0x0 /home/me/c/build/libshar2.so
0x7ffff7dd4000     0x7ffff7dd5000     0x1000     0x1000 /home/me/c/build/libshar2.so
Run Code Online (Sandbox Code Playgroud)

这个LD_PRELOAD案例似乎是这样解释的ld.so(8)

LD_PRELOAD

在所有其他对象之前加载的附加的、用户指定的 ELF 共享对象的列表。列表中的项目可以用空格或冒号分隔。 这可用于有选择地覆盖 其他共享对象中的函数。使用描述下给出的规则来搜索对象。

Emp*_*ian 5

为什么动态链接器决定在 libshar 中查找 sum 函数,而不是在 libshar2 中查找?

UNIX 上的动态链接器尝试模拟与存档库链接时会发生的情况

在空的情况下LD_PRELOAD,符号搜索顺序为(当符号被主二进制文件引用时;当符号被 DSO 引用时,规则变得更加复杂):主二进制文件、直接链接的 DSO,按照它们列出的顺序排列链接线,dlopen按 DSO 的编辑顺序进行dlopen编辑。

LD_PRELOAD = /path/to/libshar2.so 程序打印 10005。这是预期的,

非空LD_PRELOAD通过在主可执行文件之后和任何直接链接的 DSO 之前插入列出的任何库来修改搜索顺序。

但我再次注意到 libshar.so 和 libshar2.so 都已加载:

为什么这是一个惊喜?动态链接器加载 中列出的所有库LD_PRELOAD,然后加载您直接链接的所有库(如前所述


R..*_*R.. 1

dlopen不能(也不能)更改调用时已存在的(全局)符号的定义。它只能提供以前不存在的新内容。

其(草率的)形式化在规范dlopen

通过调用 dlopen() 引入到过程映像中的符号可用于重定位活动。如此引入的符号可能会与程序或之前的 dlopen() 操作已定义的符号重复。为了解决这种情况可能出现的歧义,符号引用到符号定义的解析基于符号解析顺序。定义了两个这样的解析顺序:加载顺序和依赖顺序。加载顺序在符号定义之间建立顺序,以便加载的第一个定义(包括进程映像文件中的定义以及随其加载的任何相关可执行目标文件)优先于稍后添加的可执行目标文件(通过 dlopen())。负载排序用于重定位处理。依赖关系排序使用广度优先顺序,从给定的可执行对象文件开始,然后是其所有依赖关系,然后是这些依赖关系的任何依赖关系,迭代直到满足所有依赖关系。除了通过 dlopen() 操作以空指针作为文件参数获得的全局符号表句柄之外,dlsym() 函数使用依赖顺序。加载顺序用于全局符号表句柄上的 dlsym() 操作。

请注意,这LD_PRELOAD是非标准功能,因此此处未进行描述,但在提供它的实现上,它LD_PRELOAD按加载顺序在主程序之后但在作为依赖项加载的任何共享库之前执行。