我一直在搜索,但没有找到一个假定的编译器标志或某种东西,它允许我构建我的FORTRAN DLL (使用英特尔Visual Fortran Composer XE 2013编译器),以便每次加载随机基址。我在C++代码中显式地加载了我的FORTRAN DLL,加载/卸载都很好,但我只是注意到它每次加载到的地址是完全相同的位置。我想知道这是否就是为什么当我同时多次运行我的程序时,有时我的FORTRAN DLL加载成功,而另一些时候加载失败。英特尔Fortran编译器是否有随机基址编译器选项?我已经阅读了它的发行说明,但也没有运气。
发布于 2013-01-17 18:24:37
回答你直接的问题:是的,可以标记一个DLL,这样最近的Windows版本就可以在稍微随机的基地址加载它。这是通过向链接器(link.exe
)传递/DYNAMICBASE选项来实现的。有关如何在Visual Studio中启用此功能的信息,请阅读链接页面。如果在makefile中的命令行上使用ifort
,则可以使用/link
选项将标志传递给链接器:
ifort.exe ... /link /DYNAMICBASE
请注意,/link
选项应该是命令行中的最后一个选项,因为之后的所有内容都会传递给链接器。还要注意,默认情况下/DYNAMICBASE
是打开的,您的库应该以稍微随机的地址加载(您运行的是Windows XP吗?)
然而,这并不是真正必要的。解释原因如下。
只是为了清楚地总结一下评论。Windows上的每个进程(不仅在Windows上,而且几乎在任何现代操作系统上,如*BSD、Linux、OS X等)具有自己的虚拟线性地址空间,并且用户空间中的所有内存都使用这些虚拟地址进行操作。虚拟内存被划分为多个页,这些页由物理内存帧支持。一个物理存储器帧可以映射到多个虚拟存储器页,甚至可以从不同进程的地址空间映射到许多虚拟存储器页,从而促进进程之间的存储器共享。在所谓的页面表中维护虚拟存储器页面和物理存储器帧之间的映射。它们对于进程是本地的,因此映射是本地的,这意味着两个不同进程中的相同虚拟内存地址很可能映射到完全不同的物理内存地址。一些操作系统(包括Windows)将每个进程的虚拟地址空间分成两部分--下层和上层。下半部分属于进程,而所有进程中的上半部分映射到操作系统的内核内存区。应用程序开发人员对此不感兴趣,因为内核内存不能从用户空间访问,因为他们缺乏必要的特权,因此只表现为可用虚拟内存空间的减少(例如,在32位系统上,4个GiB中只有2个或3个GiB可访问)。其他操作系统(其中OS是最流行的桌面操作系统)拥有进程专用的整个虚拟地址空间,内核在自己的单独虚拟内存空间中运行。
Window(和大多数其他实现虚拟内存管理的OSes )上的可执行文件通常由不同的部分组成,这些部分被分组为多个段。可执行文件格式的设计使得它可以通过将部分文件直接映射到虚拟地址空间来加载到内存中-这是一个称为内存映射的过程。通常,包含程序指令的可执行文件部分(通常命名为.text
或类似的内容)是只读的,因此可以在从相同的可执行文件创建的或加载了相同DLL的所有进程之间共享(DLL也与可执行文件具有相同的结构,但包含不同的部分集,并且不能单独运行),以节省物理内存。可以有许多包含不同数据段的其它段,例如包含初始化的静态(也是全局)变量的.data
段、包含未初始化的静态变量的.bss
段、具有调试信息的段、重定位段、导入和导出表等。除非采取显式措施,否则读/写(数据)段通常不会在不同的进程之间共享。
Fortran公共块通常位于.bss
部分,因为它们只是未初始化的静态数据。如果使用BLOCK DATA
结构使用数据初始化公共块,则将其放入.data
部分。无论采用哪种方式,公共块都以一个不能在加载DLL的不同进程之间共享的节结束。最后,当两个进程加载DLL时,无论是隐式地作为其依赖项的一部分,还是显式地使用LoadLibrary()
,只读部分将在两个进程之间共享,但读写数据部分(包括公共块)在每个进程中将是不同的,并且对一个进程中的数据所做的修改在另一个进程中将不可见,即使在两个进程中的DLL是在相同的基址加载的。
Windows DLL有一个称为“首选基址”的功能。每当操作系统加载这样的DLL时,它都会尝试将其放在指定的首选基址。如果不能(例如,所需虚拟地址空间的一部分已经被占用),则它将库重新定位到不同的基地址。这种行为的原因是,DLL重新定位在Windows上代价很高,因为绝对寻址用于访问全局符号,并且无论何时必须重新定位库,都必须由加载器修补(修复)地址。相比之下,许多Unix系统的动态库都是PIC(位置无关代码),可以在任何基本虚拟地址加载。但是PIC代码的执行速度比普通的依赖于位置的代码要慢一些。
较老的Windows版本总是在相同的基址加载最基本的库,如USER32.DLL
和KERNEL32.DLL
。它们的加载器是非常可预测的,如果你在启动和执行时以相同的顺序加载相同的库集,通常每次运行时库都会加载到相同的基本虚拟地址。自从Vista引入了地址空间布局的随机化后,这种情况发生了变化-这是一种可选功能,允许在随机化的虚拟基址加载特殊标记的可执行文件(和DLL),以使远程网络攻击更难找到各种操作系统或用户API调用的正确地址。
https://stackoverflow.com/questions/14381049
复制