问题描述
我编写了一个简单的 C 程序,它对共享对象执行 dlopen:
handle = dlopen ("./MySharedobject.so",RTLD_LAZY);
if (!handle) {
fputs (dlerror(),stderr);
exit(1);
}
sleep(20)
dlclose(..)
现在我将我的程序编译为可执行文件,并在同一台机器上同时运行多个可执行文件实例。我认为将在所有运行中返回的句柄将是相同的。但是,似乎 ld.so.cache 中加载的共享对象是唯一可以返回相同句柄的对象。
我的目标不是使用 dlopen 两次加载相同的代码。有没有人知道如何实现这一目标?
解决方法
我认为在所有运行中返回的句柄都是一样的。
它不会是,但这并不意味着你似乎认为它意味着什么。
即使句柄不同,共享库仍然有效并且仍然在多个进程之间共享。换句话说,这里没有问题需要您解决。
但是,似乎 ld.so.cache 中加载的共享对象是唯一可以返回相同句柄的对象。
这也是错误的:绝对不能保证 ld.so.cache
中列出的对象将在不同进程中使用相同的句柄。
更新:
我假设句柄是指向共享库加载内容的指针。
这是不正确的:在 GLIBC 下,句柄是指向描述特定库的堆分配块的指针。它不指向库本身。
如果我 dlsym 一个符号,理想情况下它应该返回符号定义加载位置的开始。
正确。
在这种情况下,我得到不同的 dlsym 值。这是否意味着符号被加载了两次?
不,它没有。通过虚拟内存的魔力,RAM 的相同物理页可以出现在不同进程中的不同地址。当 ASLR 开启时,这对于共享库来说非常常见。
例如,这里是同一个 /bin/date
二进制文件的两次执行:
$ ldd /bin/date | grep libc.so.6
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f60ff32f000)
$ ldd /bin/date | grep libc.so.6
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffa38e2a000)
请注意,libc.so.6
已加载到不同的地址(同时仍与系统上当前正在运行的所有其他进程共享)。
也很容易造成相同的物理 RAM 页面出现在单个进程内的不同地址的情况:只需在同一个文件描述符上调用 mmap(fd,...,0)
两次即可。