众所周知,任何正在运行的可执行文件都会加载到 RAM 中。
另外,我们有两种库:静态链接库和动态链接库。
这两种库也应该在需要时加载到 RAM 中。
据我所知,我们有两种加载动态库的方法:
- 在编译时链接它,例如
g++ -lsofile
- 在代码中动态加载,我们必须
dlopen
这样做
我已经发布了这个问题,但我仍然不能确保我们可以列出所有的 lib 文件。对于上面的第一种情况,我认为我们可以使用ldd
,或检查来获取链接文件/proc/{PID}/maps
。但是对于第二种情况,我在想是否可以通过某种方法获取链接文件,这是一个示例:
void SomeModule()
{
//dlopen here to link mysofile
}
int main()
{
if (user_enter == 'a')
{
printf("hello world");
}
else
{
SomeModule();
}
}
在这个例子中,当我们执行它并输入 alwaysa
时,dlopen
将永远不会被调用,因此mysofile
永远不会被链接,这意味着mysofile
永远不会被加载到 RAM 中。我对吗?
如果是这样,除了阅读源代码之外,我怎样才能获得可执行文件所需的lib文件?
错误的 !
一个可执行文件被内核的虚拟内存子系统映射到运行它的进程的虚拟地址空间中。物理 RAM 仅由内核管理。阅读操作系统:三个简单的部分了解更多信息。
并非该可执行文件的所有代码段都被分页(未加载!)到 RAM 中。特别是,一大段从未使用过的代码(例如,因为它包含一些从未调用过的大函数)不会进入 RAM。阅读有关分页和页面缓存的信息。
有时,没有足够的物理 RAM 来方便地处理所有需要的页面。在那种情况下,您会观察到颠簸。
动态链接器(参见ld-linux(8))和dlopen(3)使用mmap(2)从共享库中映射一些段。因此它不会将插件的所有代码段加载到 RAM 中。另请阅读 Drepper 的如何编写共享库论文。
一般来说,绝对没有办法预测未来将使用和编辑哪些共享库
dlopen
。考虑以下两种情况:一个持久的程序(可能是您的浏览器)要求它的用户获取一些共享库(可能从网络下载它)然后
dlopen
它。一个进程在一个临时文件中生成一些 C 代码,将该文件
/tmp/emittedcode.c
编译(通过fork
运行一些适当的进程gcc -O -Wall -fPIC /tmp/emittedcode.c -shared -o /tmp/emittedcode.so
)到一个临时插件中/tmp/emittedcode.so
,然后dlopen
-s 那个临时插件(当然稍后dlsym
在那里 -ing 适当的符号)。我非常喜欢第二种方法。请注意,编译为 C是一个很好的习惯。当前的编译器速度足够快,甚至可以在某些 REPL 交互中实现这一点。
顺便说一句,在 Linux 桌面上,一个进程可能
dlopen
有很多共享对象,即插件(至少数十万,可能还有数百万)。请参阅我的manydl.c
示例(在临时文件中生成“随机”C 代码并重复)。PS。还要注意Halting Problem,它与预测所有未来
dlopen
路径的理论不可能性有关。你是对的,如果
dlopen
从不调用,则目标库永远不会加载到(进程的)内存中。在不阅读源代码的情况下确定必要的库感觉就像是停止问题的变体。您可以使用一些启发式方法:如果程序没有链接到
libdl
,那么它就不能使用dlopen
; 如果是这样,那么您可以使用strace
(请参阅如何找出运行时加载的动态库可执行文件?)或尝试找出dlopen
使用静态分析的参数。但是程序可以libdl
直接包含(通过静态链接或构建代码);并且由于动态链接器不是魔术,没有什么可以阻止程序自己重新实现它,所以你不能绝对确保您已经使用这些启发式方法捕获了所有需要的库。也许有些程序发现它们正在被跟踪,并跳过库加载......列出所有所需库的唯一可靠方法是阅读源代码。