WinAPI: нужно ли вызывать FlushInstructionCache для исполняемого файла с отображением в памяти?

Я написал короткую программу для чтения файла obj для Windows, поиска раздела .text и запуска в нем кода. Для этого я выполняю следующие вызовы функций 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 как только код был установлен на месте. В противном случае попытки выполнить код из новой исполняемой области могут привести к непредсказуемым результатам.

Возможно ли, что мой код заставит процессор выполнять старые инструкции в кеше команд?

На MapViewOfFile или же CreateFileMapping страницы.

6

Решение

Если вы загружаете только содержимое файла в память, используя MapViewOfFile, должно быть хорошо без.

Если вы изменяете содержимое в памяти, вам необходимо очистить кэш инструкций перед выполнением кода, так как он МОЖЕТ существовать в кеше в неизмененном виде, а затем МОЖЕТ выполняться без ваших изменений.

Я использую слово МОЖЕТ из-за двух вещей:

  1. от архитектуры процессора зависит, обнаружит ли процессор записи в памяти, которую он собирается выполнить [некоторые процессоры даже не имеют оборудования для регистрации записей в данных, которые находятся в кэшах инструкций — потому что это настолько редко, что это очень маловероятно].

  2. потому что трудно предсказать, что может быть в кеше — процессоры
    иметь всевозможные «умные» способы предварительной выборки и вообще «заполнять» кэши.

Очевидно, что VirtualAlloc имеет нулевой шанс на сохранение данных, которые вы хотели, поэтому он упоминается там, потому что вы ВСЕГДА пишете в него перед выполнением.

Модификации включают, например, «исправление абсолютных адресов» (то, что вам нужно сделать, если вы хотите завершить проект, который загружает что-то сложное, чтобы выполнить его), или если вы пишете отладчик, когда вы устанавливаете точку останова путем замены инструкция с INT 3 инструкция по x86.

Второй случай «модификации» — это если вы выгружаете файл и загружаете другой файл (например, «тот же» файл, но перестроенный, например), и в этом случае ранее выполненный код все еще может находиться в кэше, и вы получаете таинственное «почему мои изменения не сделали то, что я ожидаю»

7

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]