众所周知,任何正在运行的可执行文件都会加载到 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();
}
}
在这个例子中,当我们执行它并输入always时a
,它dlopen
永远不会被调用,因此mysofile
永远不会被链接,这意味着它mysofile
永远不会被加载到RAM中。我对吗?
如果是这样,除了阅读源代码之外,如何获取可执行文件所需的lib文件?
答案1
众所周知,任何正在运行的可执行文件都会加载到 RAM 中。
错误的 !
一个可执行文件文件被映射到虚拟地址空间的流程运行它,由虚拟内存内核的子系统。物理 RAM 仅由内核管理。读操作系统:三个简单的部分了解更多。
并非所有的代码段该可执行文件的寻呼的(未加载!)到 RAM 中。特别是,从未使用过的一大段代码(例如,因为它包含一些从未被调用过的大函数)将不会进入 RAM。阅读寻呼和页面缓存。
有时,没有足够的物理 RAM 来方便地处理所有所需的页面。在那种情况下你观察到殴打。
动态链接器(参见ld-linux(8)) 并且dlopen(3)用途映射(2)内存映射共享库中的一些段。所以它不会将插件的所有代码段加载到RAM中。另请阅读 Drepper 的如何编写共享库纸。
当我们执行它并输入always a时,dlopen将永远不会被调用,因此mysofile永远不会被链接,这意味着mysofile永远不会被加载到RAM中。
有绝对地决不一般来说预测未来将使用哪些共享库和dlopen
-ed。考虑以下两种情况:
一个持久的程序(可能是您的浏览器)要求其用户获取一些共享库(可能是从网络下载),然后
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 代码并重复)。
附言。还要注意停机问题dlopen
,与预测所有未来路径的理论可能性有关。
答案2
你是对的,如果dlopen
从未被调用,则目标库永远不会加载到(进程的)内存中。
在不阅读源代码的情况下确定必要的库感觉像是停止问题的一种变体。您可以使用一些启发式方法:如果程序没有链接到libdl
,则它不能使用dlopen
;如果是这样,那么您可以使用strace
(参见如何找出运行时加载的动态库可执行文件?)或尝试找出dlopen
使用静态分析的论据。但程序可以libdl
直接包含(通过静态链接或通过构建代码);由于动态链接器并不神奇,因此没有什么可以阻止程序重新实现它本身,所以你不能绝对地确保您已经使用这些启发式方法找到了所需的所有库。也许有些程序发现它们正在被跟踪,并跳过库加载......
列出所有所需库的唯一可靠方法是阅读源代码。