gpt4 book ai didi

c++ - Windows DLL实际上是如何共享的?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:43:00 24 4
gpt4 key购买 nike

通过检查我的 Windows 机器中的几个 DLL(例如 KERNEL32.DLL),我注意到它们的任何部分,甚至只读数据部分都没有设置 IMAGE_SCN_MEM_SHARED 标志。

DLL 是从 .dll 文件映射的,因此只有当您读取文件的一页时,它才会被复制到物理内存,但是,如果进程 A 和进程 B 都访问了 kernel32.dll 的同一页,那么该页面将在物理内存中存在两次。 我要求最后声明的真实性。

如果共享的 .text 或 .rodata 段,它们只会被复制到物理内存一次,即使启用了 ASLR,因为 ASLR 所做的是在模块首次加载时随机化模块的基址(应用相应的重定位) ) 但在系统重启之前加载此模块的下一个进程将在同一地址获取模块,因此 .text 和 .rodata 可以以相同的方式共享。

这些都是我的假设,请指正。

谢谢!

最佳答案

操作系统肯定能够将多个虚拟地址映射到同一个物理内存页面,只要页面内容不(需要)改变[以不同的方式针对不同的进程]。但是,如果代码使用绝对地址(在 DLL 内部或外部),例如 vtable/函数指针、指向全局数据(常量或非常量)的指针或使用绝对地址的简单函数调用,则地址必须是修改以匹配操作系统给该内存部分的实际地址。这称为“搬迁”。

因此,至少在理论上,即使使用地址空间随机化,您也可以共享同一个 DLL,只是需要编译器和/或程序员多做一些工作。特别是,它要求没有重定位(在大块代码中)。如果代码具有基于代码地址重定位的绝对地址,则每个 DLL 都需要一个拷贝。

我实际上不知道操作系统如何处理这个问题。一个简单的解决方案显然是对每个 DLL 仅随机化一次地址(直到卸载该特定 DLL),而不管有多少应用程序使用相同的 DLL。它仍然让局外人很难知道 DLL 加载到哪个地址,因为每次第一次加载时它都会加载到不同的地址(更重要的是,它不会是所有机器的静态值使用相同版本的操作系统,如果没有此功能就会出现这种情况)。但是,它确实意味着可以通过从具有已知内容的堆栈中复制内容来“检查”长时间运行的进程。 Web 服务器、数据库服务器和系统服务通常是长时间运行的进程,因此只有在系统“关闭”(或至少重新启动长时间运行的进程)时才会有不同的地址。

第二个稍微棘手的版本是检查特定页面(通常为 4KB 内存区域)是否有重定位,并共享所有没有重定位的页面。重定位的页面需要每个基地址有一个拷贝。通常在 DLL 的一个 block (“thunk 部分”)中具有“对外部资源的所有引用”,因此无论代码的基地址是什么,DLL 的典型大部分都不会,这意味着绝对是一个可行的解决方案。

如果这些方案在操作系统中都“不起作用”,那么您必须多次加载同一个 DLL。无论如何,从操作系统的角度来看,这显然是可行的,因为在 ASLR 之前,如果两个 DLL 试图加载到同一地址(例如不同供应商生产的 DLL,恰好为代码选择相同的基地址,或者经典且常见的“我从未给出基地址,所以它使用默认地址”) - 操作系统将通过更改加载的基地址来解决此类冲突首先。

至于IMAGE_SCN_MEM_SHARED的含义,我还以为开发者会这样要求,DLL中的页面共享是自动完成的。换句话说,IMAGE_SCN_MEM_SHARED 将由特定 DLL 或 EXE 的开发人员设置,以表示该内容应该与相同内容的其他用户共享,而不是“操作系统如果可以在内容用户不注意的情况下完成,则可以共享它”(共享代码肯定是这种情况,并且(可写)数据通常不在 DLL 之间共享。只读数据,只要它没有重定位,当然可以隐式共享[该内容的用户无法判断它是否共享]。

关于c++ - Windows DLL实际上是如何共享的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41315835/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com