0

我一直在搜索,但没有找到一个假定的编译器标志或其他允许我构建 FORTRAN DLL(使用 Intel Visual Fortran Composer XE 2013 编译器)以便加载随机基地址的东西每一次。我在我的 C++ 代码中显式加载我的 FORTRAN DLL 并且它加载/卸载很好,但我只是注意到它每次加载到的地址都是完全相同的位置。我想知道这是否就是为什么我的 FORTRAN DLL 有时会成功加载,而有时当我同时运行我的程序多次时会失败。英特尔 Fortran 编译器是否存在任何随机基地址编译器选项?我已经阅读了它的发行说明,但也没有运气。

4

1 回答 1

2

回答您的直接问题:是的,可以标记 DLL,以便最近的 Windows 版本将其加载到稍微随机的基地址。这是通过将/DYNAMICBASE选项传递给链接器 ( link.exe) 来实现的。阅读链接页面以获取有关如何在 Visual Studio 中启用此功能的信息。如果ifort在 makefile 的命令行上使用,则该/link选项可用于将标志传递给链接器:

ifort.exe ... /link /DYNAMICBASE

请注意,该/link选项应该是命令行上的最后一个选项,因为后面的所有内容都会传递给链接器。另请注意,/DYNAMICBASE默认情况下为 ON,并且您的 ibrary 应以稍微随机的地址加载(您运行的是 Windows XP 吗?)

但是,这并不是必需的。解释为什么如下。


只是为了清楚地总结评论。Windows 上的每个进程(不仅在 Windows 上,而且在几乎任何现代操作系统上,如 *BSD、Linux、OS X 等)都有自己的虚拟线性地址空间,并且用户空间中的所有内存都使用这些虚拟地址进行操作. 虚拟内存分为页面,这些页面由物理内存的帧支持。一个物理内存帧可能映射到多个虚拟内存页面,甚至是来自不同进程的地址空间,从而促进进程之间的内存共享。虚拟内存页和物理内存帧之间的映射被维护在所谓的页表中。这些对于进程来说是本地的,因此映射是本地的,这意味着两个不同进程中的相同虚拟内存地址很可能会映射到完全不同的物理内存地址。一些操作系统(包括 Windows)将每个进程的虚拟地址空间分成两部分 - 下部和上部。下半部分属于进程,上半部分在所有进程中映射到操作系统的内核内存区域。这对应用程序开发人员来说没什么兴趣,因为内核内存无法从用户空间访问,因为他们缺乏必要的特权,因此只会表现为可用虚拟内存空间的减少(例如,只能从用户空间访问 2 或 3 GiB在 32 位系统上为 4 GiB)。其他操作系统(其中 OS X 是最流行的桌面操作系统)拥有整个进程专用的虚拟地址空间,内核在其自己独立的虚拟内存空间中运行。

Windows(以及大多数其他实现虚拟内存管理的操作系统)上的可执行文件通常由不同的部分组成,其中部分被分组为段。可执行文件格式被设计成可以通过直接将其部分映射到虚拟地址空间来将其加载到内存中 - 这一过程称为内存映射。通常包含程序指令(通常命名.text或类似名称)的可执行文件部分是只读的,因此可以在从相同可执行文件创建或加载相同 DLL 的所有进程之间共享(DLL 也具有相同的结构作为可执行文件但包含不同的部分集并且不能单独运行)以节省物理内存。可能有许多其他部分包含不同的数据,例如.data包含初始化静态(也是全局)变量的.bss部分,包含未初始化静态变量的部分,带有调试信息的部分,重定位部分,导入和导出表等。读/写(数据)部分通常不会在不同进程之间共享,除非采取了明确的措施。

Fortran COMMON 块通常位于该.bss部分中,因为它们只是未初始化的静态数据。如果一个 COMMON 块是使用一个BLOCK DATA结构用数据初始化的,那么它就会被放到这个.data部分中。无论哪种方式,COMMON 块都以在加载 DLL 的不同进程之间不可共享的部分结束。最后,当两个进程加载 DLL 时,无论是作为其依赖项的一部分隐式加载还是显式使用LoadLibrary(),只读部分将在两个进程之间共享,但读写数据部分(包括您的 COMMON 块)会有所不同在每个进程中,对一个进程中的数据的修改在另一个进程中是不可见的,即使在 DLL 在两个进程中加载​​到相同的基地址时也是如此。

Windows DLL 具有称为“首选基地址”的功能。每当操作系统加载此类 DLL 时,它都会尝试将其放置在指定的首选基地址处。如果不能(例如,部分所需的虚拟地址空间已被占用),则它将库重新定位到不同的基地址。这种行为的原因是 DLL 重定位在 Windows 上代价高昂,因为绝对寻址用于访问全局符号,并且每当必须重定位库时,加载程序必须修补(修复)地址。相比之下,许多 Unix 系统将其动态库作为 PIC(与位置无关的代码),并且可以在任何基本虚拟地址处加载。但是 PIC 代码的执行速度比正常的位置相关代码要慢一些。

较旧的 Windows 版本具有最基本的库,例如USER32.DLL并且KERNEL32.DLL始终加载在相同的基地址。它们的加载器是非常可预测的,如果您在启动和可执行时加载相同的库集并以相同的顺序,通常库在每次运行时加载到相同的基本虚拟地址。自从 Vista 引入了地址空间布局的随机化后,这种情况发生了变化——这是一项可选功能,允许在随机虚拟基地址加载特殊标记的可执行文件(和 DLL),以使远程网络攻击更难发现各种操作系统或用户 API 调用的正确地址。

于 2013-01-17T18:24:36.527 回答