I have written a short program to read a windows obj file and find the .text section and run the code in it. To do this I make the following Windows API function calls (Full code [gist.github.com], for those interested):
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);
I then find the .text section in the file and cast the pointer to the code to a function pointer in C++ and simply call the function. This actually appeared to work for me.
Have I made a mistake not calling FlushInstructonCache on the range of virtual memory mapped to the file?
I ask this because I was recently reading the VirtualAlloc documentation and it notes at the bottom:
When creating a region that will be executable, the calling program bears responsibility for ensuring cache coherency via an appropriate call to FlushInstructionCache once the code has been set in place. Otherwise attempts to execute code out of the newly executable region may produce unpredictable results.
Is it possible that my code will cause the CPU to execute old instructions in the instruction cache?
There is no such note on the MapViewOfFile or CreateFileMapping pages.
If you only load the file-content into memory using MapViewOfFile
, it should be fine without.
If you MODIFY the content in memory, you need to flush the instructioncache before executing the code, as it MAY exist in cache in the unmodified form, and MAY then be executed without your modifications.
I use the word MAY because of two things:
it depends on the processor architecture whether the processor detects writes to the memory it is about to execute [some processors don't even have hardware to register writes to data that is in instruction caches - because it's so rare that it's very unlikely].
because it's hard to predict what may be in a cache - processors have all manner of "clever" ways to prefetch and in general "fill" caches.
Obviously, VirtualAlloc
has zero chance of containing the data you wanted, so it's mentioned there because you'd ALWAYS write to it before executing.
Modifications include "fix up for absolute addresses" for example (something you'd have to do if you want to complete a project that loads something complex to execute it), or if you write a debugger, when you set a breakpoint by replacing an instruction with the INT 3
instruction on x86.
A second case of "modification" is if you unload the file, and load a different file (perhaps the "same" file, but rebuilt, for example), in which case, the previously executed code may still be in the cache, and you get the mysterious "why didn't my changes do what I expect"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With