c ++ - WinAPI: Er det nødvendigt at kalde FlushInstructionCache på en eksekverbar memory-mapped fil?

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg har skrevet et kort program til at læse en windows obj fil og finde .text sektionen og køre koden i den. For at gøre dette foretager jeg følgende Windows API-funktionsopkald (Fuld kode [[gist.github.com]], for de interesserede): [5]


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);


Jeg finder derefter sektionen .text i filen og kaster markøren til koden til en funktionspeger i C ++ og kalder blot funktionen. Dette syntes faktisk at fungere for mig.


Har jeg lavet en fejl, der ikke kalder FlushInstructonCache på rækkevidden af ​​virtuel hukommelse kortlagt til filen?


Jeg spørger dette fordi jeg for nylig læste VirtualAlloc dokumentationen og det noterer sig nederst: [6]



  Når du opretter en region, der vil blive eksekverbar, har opkaldsprogrammet ansvaret for at sikre cache-sammenhæng via et passende opkald til FlushInstructionCache, når koden er blevet indstillet. Ellers kan forsøg på at udføre kode ud af det nyligt eksekverbare område producere uforudsigelige resultater. [7]



Er det muligt, at min kode vil medføre, at CPU'en udfører gamle instruktioner i instruktionscachen?


Der er ingen sådan note på MapViewOfFile eller CreateFileMapping-siderne. [8] [9]

Bedste reference


Hvis du kun indlæser filindholdet i hukommelse med MapViewOfFile, skal det være fint uden.


Hvis du ændrer indholdet i hukommelsen, skal du skubbe instruktionscache før koden udføres, da det muligvis findes i cache i den umodificerede form, og kan derefter udføres uden dine ændringer.


Jeg bruger ordet MAY på grund af to ting:



  1. Det afhænger af processorarkitekturen, om processoren registrerer, skriver til den hukommelse, den skal udføre [[nogle processorer har ikke engang hardware til at registrere skriver til data, der er under instruktion caches - fordi det er så sjældent, at det s meget usandsynligt).

  2. fordi det er svært at forudsige, hvad der kan være i en cache-processorer
    har alle slags 'kloge' måder at prefetch og generelt 'fylde'
    caches.



Det er klart, at VirtualAlloc har ingen chance for at indeholde de data, du ønskede, så det er nævnt der, fordi du altid skriver til det, før du gennemfører det.


Ændringer omfatter f.eks. 'Fix up for absolute addresses' (noget du skal gøre, hvis du vil fuldføre et projekt, der læser noget komplekst for at udføre det), eller hvis du skriver en debugger, når du sætter et breakpoint ved at erstatte en instruktion med instruktionen INT 3 på x86.


Et andet tilfælde af 'modifikation' er, hvis du læser filen, og indlæser en anden fil (måske 'samme' fil, men genopbygget). I så fald kan den tidligere udførte kode stadig være i cachen, og du får den mystiske 'hvorfor gjorde ikke mine ændringer, hvad jeg forventer'