WinAPI:在可执行的内存映射文件上是否需要调用FlushInstructionCache?

7

我写了一个简短的程序来读取Windows OBJ文件,查找.text节(section)并运行其中的代码。为此,我调用了以下Windows API函数(对于那些感兴趣的人,完整代码[gist.github.com]):

HANDLE FileHandle = CreateFile("lib.obj",
                               GENERIC_READ | GENERIC_EXECUTE,
                               FILE_SHARE_READ, 0,
                               OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

HANDLE MappingHandle = CreateFileMapping(FileHandle, 0, PAGE_EXECUTE_READ, 0, 0, 0);

void *Address = MapViewOfFile(MappingHandle, FILE_MAP_EXECUTE | FILE_MAP_READ,
                              0, 0, 0);

我在文件中找到了.text部分,并将指向代码的指针转换为C ++中的函数指针,然后简单地调用该函数。这实际上对我有用。

如果我没有在映射到文件的虚拟内存范围上调用FlushInstructonCache,我犯了错误吗?

我提出这个问题是因为最近我正在阅读VirtualAlloc文档,它在底部注明:

当创建可执行区域时,调用程序要通过使用适当的FlushInstructionCache调用来确保缓存一致性,一旦代码放置在正确位置。否则,尝试从新的可执行区域执行代码可能会产生不可预测的结果。

我的代码是否可能导致CPU在指令缓存中执行旧指令?

MapViewOfFileCreateFileMapping页面上没有这样的说明。


1
我认为在写入内存区域并执行之前,您必须调用FlushInstructionCache。由于您没有对其进行写入操作,因此这对您无关。 - user253751
我找到了这个comp.lang.asm.x86页面。它似乎表明这些指令缓存问题可能与平台有关。 - Fsmv
1个回答

10
如果您只是使用MapViewOfFile将文件内容加载到内存中,则不需要刷新指令缓存。但如果您在内存中修改了内容,则需要在执行代码之前刷新指令缓存,因为修改前的指令可能存在于缓存中,并且可能在没有进行修改的情况下被执行。
我使用“可能”这个词有两个原因:
1. 它取决于处理器架构是否检测到要执行的内存的写入操作(一些处理器甚至没有硬件来记录指令缓存中数据的写入操作,因为这种情况非常罕见)。 2. 因为很难预测缓存中会出现什么 - 处理器有各种“聪明”的方式来预取和填充缓存。
显然,VirtualAlloc没有包含您所需的数据的机会,因此提到它是因为您在执行之前必须始终对其进行写入。
修改包括例如“修复绝对地址”(如果要完成加载并执行某些复杂内容的项目,则必须执行此操作),或者如果您编写调试器,则通过用x86上的INT 3指令替换指令以设置断点时进行修改。
第二种“修改”情况是如果您卸载文件并加载一个不同的文件(例如,可能是“相同”的文件,但已重建),则之前执行的代码仍可能存在于缓存中,因此您可能会遇到神秘的“为什么我的更改没有产生我预期的效果”的情况。

关于调试器的那个观点很有趣,我不知道/不曾知道它们是如何工作的。 - Fsmv
4
其实是在我为一款小型(公司内部使用)操作系统实现调试器时,我第一次遇到了这个问题 - 大约20年前!它不会在我设置的断点处停下来 - 直到我刷新了I-cache! :) - Mats Petersson
1
我不明白为什么使用MapViewOfFile写入内存的结果与显式写入使用VirtualAlloc分配的内存不同。为什么在后者中需要调用FlushInstructionCache(),而在前者中不需要呢?新分配的VirtualAlloc内存如何进入CPU的指令缓存? - IInspectable
@IInspectable:每当可执行模块的页面首次读入内存时,内存管理器必须负责刷新缓存。理论上,内存管理器对于使用MapViewOfFile创建的文件映射可能会与使用Windows加载程序创建的文件映射有所不同,但在实践中这似乎不太可能发生。至于VirtualAlloc,如果您的程序将新分配的内存写入并直接跳转到它,则CPU可能会预先查看并开始缓存跳转另一端的代码,即使它还没有被写入。 - Harry Johnston
@conio,说得好,我的陈述比应该的更强烈了。我的意图是解释为什么MapViewOfFile可能会合理地与VirtualAlloc不同,但我表达的方式使它听起来好像必须有所不同。值得一提的是,我认为可以提出这样的论点,即为了与Windows 95/98/ME兼容(我相信它们不支持SEC_IMAGE),它应该以这种方式运行,但这仍然不是决定性的。 - Harry Johnston
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接