Введение
Формат файла
Формат Portable Executable (PE) — это формат файла для исполняемых файлов, объектного кода, DLL и других файлов, используемых в 32-битных и 64-битных версиях операционных систем Windows и в средах UEFI. Формат PE представляет собой структуру данных, содержащую информацию, необходимую загрузчику ОС Windows для управления заключенным в него исполняемым кодом. Эта информация включает динамические ссылки на библиотеки для компоновки, таблицы экспорта и импорта API, данные управления ресурсами и данные локального хранилища потоков (TLS). В операционных системах NT формат PE используется для файлов EXE, DLL, SYS (драйверов устройств), MUI и других типов файлов. Спецификация Unified Extensible Firmware Interface (UEFI) определяет PE как стандартный исполняемый формат в средах EFI. В операционных системах Windows NT формат PE в настоящее время поддерживает архитектуры наборов команд IA 32, x86 64 (AMD64/Intel 64), IA 64, ARM и ARM64. До Windows 2000 в Windows NT (и, следовательно, PE) поддерживались наборы команд MIPS, Alpha и PowerPC. Поскольку PE используется в Windows CE, он продолжает поддерживать различные варианты наборов команд MIPS, ARM (включая Thumb) и SuperH. Аналогичными форматами PE являются ELF (используется в Linux и большинстве других версий Unix) и Mach-O (используется в macOS и iOS).
История
Microsoft перешла на формат PE из 16-битных форматов NE с выпуском операционной системы Windows NT 3.1. Все последующие версии Windows, включая Windows 95/98/ME и дополнение Win32s для Windows 3.1x, поддерживают эту структуру файлов. Формат сохранил ограниченную обратную совместимость для обеспечения взаимодействия между системами на базе DOS и NT. Например, заголовки PE/COFF по-прежнему содержат исполняемый модуль DOS, который по умолчанию является коротким DOS-загрузчиком, выводящим сообщение вроде "Эта программа не может быть запущена в режиме DOS" (или аналогичное), хотя он может представлять собой и полноценную версию программы для DOS (ярким примером является установщик Windows 98 SE). Это можно рассматривать как разновидность "толстого" бинарного файла. PE продолжает адаптироваться к изменяющейся платформе Windows. Некоторые расширения включают формат NET PE (см. ниже), версию с поддержкой 64-битного адресного пространства под названием PE32+ и спецификацию для Windows CE.
Планировка
Файл PE состоит из ряда заголовков и секций, которые сообщают динамическому связчику, как отобразить файл в память. Исполняемый образ состоит из нескольких различных областей, каждая из которых требует различной защиты памяти; поэтому начало каждой секции должно быть выровнено по границе страницы. Например, обычно секция кода (которая содержит программный код) отображается как исполняемая/для чтения, а секция данных (содержащая глобальные переменные) отображается как без исполнения/для чтения и записи. Однако, чтобы не тратить место, различные секции не выровнены по границе страницы на диске. Часть работы динамического связчика заключается в том, чтобы отображать каждую секцию в память индивидуально и назначать правильные права доступа результирующим областям в соответствии с инструкциями, содержащимися в заголовках.
Таблица импорта
Одним из важных разделов является таблица импорта адресов (IAT), которая используется как таблица поиска при вызове функцией приложения функции из другого модуля. Импорт может осуществляться как по порядковому номеру, так и по имени. Поскольку скомпилированная программа не может знать адрес в памяти библиотек, от которых она зависит, при каждом вызове API требуется косвенный переход. Когда динамический компоновщик загружает модули и объединяет их, он записывает фактические адреса в слоты IAT, чтобы они указывали на адреса соответствующих функций библиотеки в памяти. Хотя это добавляет дополнительный переход по сравнению со внутримодульным вызовом, что приводит к снижению производительности, это дает ключевое преимущество: минимизируется количество страниц памяти, которые необходимо копировать при записи загрузчиком, что экономит память и время дискового ввода-вывода. Если компилятор заранее знает, что вызов будет межмодульным (например, через атрибут `dllimport`), он может сгенерировать более оптимизированный код, который просто приводит к команде косвенного вызова. Это запись в каталог данных заголовка PE. Заголовок IMAGE COR20 HEADER сильно напоминает необязательный заголовок PE, фактически выполняя его роль для загрузчика CLR. Mac OS X 10.5 способна загружать и разбирать PE-файлы, но не имеет двоичной совместимости с Windows. UEFI и EFI используют Portable Executable файлы, а также соглашение о вызовах Windows ABI x64 для приложений.