WinAPI: Faut-il appeler FlushInstructionCache sur un fichier mappé exécutable?

J’ai écrit un petit programme pour lire un fichier obj Windows et trouver la section .text et exécuter le code qu’il contient. Pour ce faire, je fais les appels de fonction API Windows suivants ( code complet [gist.github.com] , pour les personnes intéressées):

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

Je trouve ensuite la section .text dans le fichier et jette le pointeur sur le code dans un pointeur de fonction en C ++ et appelle simplement la fonction. Cela a effectivement semblé fonctionner pour moi.

Ai-je fait une erreur en n’appelant pas FlushInstructonCache sur la plage de mémoire virtuelle mappée au fichier?

Je demande ceci parce que je lisais récemment la documentation de VirtualAlloc et il note en bas:

Lors de la création d’une région qui sera exécutable, il incombe au programme appelant d’assurer la cohérence du cache via un appel approprié à FlushInstructionCache une fois le code défini. Sinon, les tentatives d’exécution de code en dehors de la nouvelle région exécutable peuvent produire des résultats imprévisibles.

Est-il possible que mon code entraîne le CPU à exécuter d’anciennes instructions dans le cache d’instructions?

Il n’y a pas de telle note sur les pages MapViewOfFile ou CreateFileMapping .

Si vous ne chargez que le contenu du fichier en mémoire à l’aide de MapViewOfFile , cela devrait aller sans.

Si vous MODIFIEZ le contenu en mémoire, vous devez vider l’instructioncache avant d’exécuter le code, car il PEUT exister en cache sous la forme non modifiée, et PEUT alors être exécuté sans vos modifications.

J’utilise le mot MAY à cause de deux choses:

  1. il dépend de l’architecture du processeur si le processeur détecte les écritures dans la mémoire qu’il est sur le point d’exécuter [certains processeurs n’ont même pas de matériel pour enregistrer les données dans les caches d’instructions – parce que c’est très rare].

  2. Parce qu’il est difficile de prédire ce qui peut se trouver dans un cache – les processeurs ont toutes sortes de manières “intelligentes” de pré-récupérer et en général de “remplir” les caches.

De toute évidence, VirtualAlloc n’a aucune chance de contenir les données souhaitées, il est donc mentionné ici car vous y écrivez TOUJOURS avant de l’exécuter.

Les modifications comprennent par exemple “réparer les adresses absolues” (ce que vous devez faire si vous souhaitez terminer un projet qui charge quelque chose de complexe pour l’exécuter) ou si vous écrivez un débogueur lorsque vous définissez un point d’arrêt en remplaçant un projet. instruction avec l’instruction INT 3 sur x86.

Un second cas de “modification” est si vous déchargez le fichier et chargez un fichier différent (peut-être le “même” fichier, mais reconstruit, par exemple), auquel cas, le code précédemment exécuté peut toujours se trouver dans le cache, et vous obtenez le mystérieux “pourquoi mes modifications ne font-elles pas ce que j’attend”