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

我正在尝试LD_PRELOAD/dlopen并面临一个关于符号查找的困惑。考虑以下两个库:
libshar
shared.h

int sum(int a, int b);

shared.c
int sum(int a, int b){
    return a + b;
}

libshar2
shared.h
int sum(int a, int b);

shared.c
int sum(int a, int b){
    return a + b + 10000;
}

和可执行文件:
#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);
}

将二进制代码与bin_sharedlibshar连接起来,我考虑了以下两种情况:
libdl为空
程序打印LD_PRELOAD
为什么动态链接器决定在5而不是sum中查找libshar函数?它们都已加载并包含所需的符号:
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

libshar2
程序打印LD_PRELOAD = /path/to/libshar2.so。这是意料之中的,但我再次注意到10005libshar.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

libshar2.so一案似乎在LD_PRELOAD中得到了解释:
ld.so(8)
要加载的其他用户指定的ELF共享对象列表
在所有人之前。列表中的项目可以用空格分隔
或者冒号。这可用于有选择地重写
其他共享对象。使用规则搜索对象
在描述下给出。


最佳答案:

dlopen无法(也无法更改)调用时已存在的(全局)符号的定义。它只能制造以前不存在的新的。
这个(草率的)形式化在specification for dlopen中:
通过调用dlopen()引入进程映像的符号可用于重新定位活动。这样引入的符号可能与程序或以前的dlopen()操作定义的符号重复。为了解决这种情况下可能出现的歧义,对符号定义的符号引用的解析基于符号解析顺序。定义了两个这样的解析顺序:加载顺序和依赖顺序。加载顺序在符号定义之间建立顺序,以便加载的第一个定义(包括来自进程映像文件及其加载的任何从属可执行对象文件的定义)比以后添加的可执行对象文件(由dlopen()创建)具有优先级。重定位处理中使用了负载排序。依赖项排序使用广度优先顺序,从给定的可执行对象文件开始,然后是其所有依赖项,然后是这些依赖项的任何依赖项,迭代直到满足所有依赖项。除了通过使用空指针作为文件参数的dlopen()操作获得的全局符号表句柄外,dlsym()函数使用依赖项排序。在全局符号表句柄的dlsym()操作中使用了加载顺序。
请注意,LD_PRELOAD是非标准功能,因此这里不作描述,但在提供它的实现中,LD_PRELOAD在主程序之后,但在作为依赖项加载的任何共享库之前,使用加载顺序。

    公众号
    关注公众号订阅更多技术干货!