Hubbry Logo
Portable ExecutablePortable ExecutableMain
Open search
Portable Executable
Community hub
Portable Executable
logo
7 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Contribute something
Portable Executable
Portable Executable
from Wikipedia
Portable Executable
Filename extension
.exe, .com, .dll, .sys, .acm, .ax, .cpl, .drv, .efi, .mui, .ocx, .scr, .tsp, .msstyles
Internet media type
application/vnd.microsoft.portable-executable[1]
Magic number4D 5A (MZ in ASCII)
and
50 45 00 00 (PE)
Developed byCurrently: Microsoft
Type of formatBinary, executable, object, shared libraries
Extended fromDOS MZ executable
COFF

Portable Executable (PE) is a file format for native executable code on 32-bit and 64-bit Windows operating systems, as well as in UEFI environments.[2] It is used for native executables (.exe, .com), dynamic link libraries (.dll, .ocx), system drivers (.sys, .drv) and many other types of files. The PE format supports storing the data required to load and start an operating system process – including references to dynamic link libraries, tables for importing and exporting application programming interface (API) functions, resource management data and thread-local storage (TLS) information.

According to the Unified Extensible Firmware Interface (UEFI) specification, the PE format is also the accepted standard for executables in EFI environments.[3] On Windows NT systems, it currently supports a range of instruction sets, including IA-32, x86-64 (AMD64/Intel 64), IA-64, ARM and ARM64. Before the advent of Windows 2000, Windows NT (and by extension the PE format) also supported MIPS, Alpha, and PowerPC architectures. Moreover, thanks to its use in Windows CE, PE has maintained compatibility with several MIPS, ARM (including Thumb), and SuperH variants.[4]

Functionally, the PE format is similar to other platform-specific executable formats, such as the ELF format used in Linux and most Unix-like systems, and the Mach-O format found in macOS and iOS.

History

[edit]

Microsoft first introduced the PE format with Windows NT 3.1, replacing the older 16-bit New Executable (NE) format. Soon after, Windows 95, 98, ME, and the Win32s extension for Windows 3.1x, all adopted the PE structure. Each PE file includes a DOS executable header, which generally displays the message "This program cannot be run in DOS mode". However, this DOS section can be replaced by a fully functional DOS program, as demonstrated in the Windows 98 SE installer. Developers can add such a program using the /STUB switch with Microsoft's linker, effectively creating a fat binary.[5]

Over time, the PE format has grown with the Windows platform. Notable extensions include the .NET PE format for managed code, PE32+ for 64-bit address space support, and a specialized version for Windows CE.

To determine whether a PE file is intended for 32-bit or 64-bit architectures, one can examine the Machine field in the IMAGE_FILE_HEADER.[6] Common machine values are 0x014c for 32-bit Intel processors and 0x8664 for x64 processors. Additionally, the Magic field in the IMAGE_OPTIONAL_HEADER reveals whether addresses are 32-bit or 64-bit. A value of 0x10B indicates a 32-bit (PE32) file, while 0x20B indicates a 64-bit (PE32+) file.[7]

Technical details

[edit]

Layout

[edit]
Structure of a Portable Executable 32 bit

A PE file consists of several headers and sections that instruct the dynamic linker about on how to map the file into memory. An executable image consists of several different regions, each requiring different memory protection attributes. To ensure proper alignment, the start of each section must align to a page boundary.[8] For instance, the .text section, which contains program code, is typically mapped as an execute/read-only. Conversely, the .data section, which holds global variables, is mapped as no-execute/read write. However, to conserve space, sections are not aligned on disk in this manner. The dynamic linker maps each section to memory individually and assigns the correct permissions based on the information in the headers.[9]

Import table

[edit]

The import address table (IAT) is used as a lookup table when the application calls a function in a different module. The imports can be specified by ordinal or by name. Because a compiled program cannot know the memory locations of its dependent libraries beforehand, an indirect jump is necessary for API calls. As the dynamic linker holds modules and resolves dependencies, it populates the IAT slots with actual addresses of the corresponding library functions. Although this adds an extra jump, incurring a performance penalty compared to intermodular calls, it minimizes the number of memory pages that that require copy-on-write changes, thus conserving memory and disk I/O. If a call is known to be intermodular beforehand (if indicated by a dllimport attribute), the compiler can generate optimized code with a simple indirect call opcode.[9]

Address Space Layout Randomization (ASLR)

[edit]

Modern operating systems use Address space layout randomization (ASLR), a process that makes a PE file's in-memory layout unpredictable and therefore harder to exploit. During ASLR, the loader randomizes the virtual addresses where key components reside. This includes the executable's base, shared libraries, the heap, and the stack. Most PE files are not position-independent because mainstream compilers emit some absolute references relative to an assumed base. To cope with randomized rebasing, the linker stores a .reloc table that lets the loader adjust those references at load time.

.NET, metadata, and the PE format

[edit]

In a .NET executable, the PE code section contains a stub that invokes the CLR virtual machine startup entry, _CorExeMain or _CorDllMain in mscoree.dll, much like it was in Visual Basic executables. The virtual machine then makes use of .NET metadata present, the root of which, IMAGE_COR20_HEADER (also called "CLR header") is pointed to by IMAGE_DIRECTORY_ENTRY_COMHEADER (the entry was previously used for COM+ metadata in COM+ applications, hence the name[citation needed]) entry in the PE header's data directory. IMAGE_COR20_HEADER strongly resembles PE's optional header, essentially playing its role for the CLR loader.[4]

The CLR-related data, including the root structure itself, is typically contained in the common code section, .text. It is composed of a few directories: metadata, embedded resources, strong names and a few for native-code interoperability. Metadata directory is a set of tables that list all the distinct .NET entities in the assembly, including types, methods, fields, constants, events, as well as references between them and to other assemblies.

Use on other operating systems

[edit]

The PE format is also used by ReactOS, an open-source operating system created to be binary-compatible with Windows. Historically, it has also been used by other operating systems such as SkyOS and BeOS R3. However, both SkyOS and BeOS eventually moved to ELF.[citation needed]

The Mono development platform, which aims to be binary compatible with the Microsoft .NET Framework, uses the same PE format as the Microsoft implementation. The same goes for Microsoft's own cross-platform .NET Core.

On x86(-64) Unix-like operating systems, Windows binaries (in PE format) can be executed using Wine. The HX DOS Extender also uses the PE format for native DOS 32-bit binaries, and can execute some Windows binaries in DOS, thus acting like an equivalent of Wine for DOS.

Mac OS X 10.5 has the ability to load and parse PE files, although it does not maintain binary compatibility with Windows.[10]

UEFI and EFI firmware use PE files as well as the Windows ABI x64 calling convention for applications.

See also

[edit]

References

[edit]
[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
The Portable Executable (PE) format is a developed by for files, dynamic-link libraries (DLLs), , and other loadable modules used in Windows operating systems. It specifies the structure of these files to enable the Windows loader to map them into memory, resolve imports, and execute code efficiently across supported hardware architectures. The format emphasizes portability, allowing executables to run on various Windows platforms without modification, provided the architecture matches. Introduced with the original Win32 API specifications in the early 1990s, the PE format extends the Common Object File Format (COFF), which originated from earlier systems like VAX/VMS, to support the demands of 32-bit Windows environments such as and Windows 95. Over time, it has evolved to include 64-bit variants (PE32+), with widened fields for larger address spaces, and adaptations for mobile platforms like Windows CE, as well as integration with .NET assemblies that embed metadata within PE structures. Today, it supports multiple processor architectures, including x86, x64, , ARM64, and , ensuring broad compatibility in modern Windows ecosystems. At its core, a PE file structure begins with an MS-DOS 2.0-compatible stub header for legacy compatibility, allowing older DOS loaders to recognize and potentially execute a simple message if run in a DOS environment. This is followed by the PE signature ("PE\0\0"), the COFF file header (detailing machine type, number of sections, and timestamps), and an optional header that provides essential loader directives like the preferred image base address, entry point, and data directories for features such as imports, exports, resources, and exceptions. The file then includes section headers—up to 96 per file—that describe the virtual size, raw size, characteristics (e.g., readable, writable, executable), and relative virtual addresses (RVAs) for sections like .text (code), .data (initialized data), .rdata (read-only data), .rsrc (resources), and specialized ones such as .tls (thread-local storage). These components collectively facilitate dynamic linking, relocation, and runtime behaviors critical to Windows applications.

Introduction

Definition and Purpose

The (PE) format is the standard for native code executables, dynamic-link libraries (DLLs), object files, and device drivers in 32-bit and 64-bit Windows operating systems. It supports file extensions including .exe for applications, .dll for shared libraries, .obj for unlinked , and .sys for kernel-mode drivers, providing a consistent structure that accommodates both standalone programs and modular components. This format is based on the Common Object File Format (COFF) and is designed to be architecture-independent, allowing compilation and execution across diverse hardware environments without requiring format modifications. The core purpose of the PE format is to supply a structured layout that the Windows loader uses to map the file's sections into , resolve external dependencies like imported functions from DLLs, apply address relocations for , and facilitate program execution while handling embedded resources such as icons, menus, and strings. By encapsulating code, data, and metadata in a predictable manner, it enables efficient loading, linking, and runtime management, reducing overhead during process initialization and supporting features like delayed loading of imports to optimize startup performance. Notable characteristics of the PE format include its multi-architecture compatibility with processors such as x86, x64, and , which is indicated by machine type fields in the COFF header to guide loader behavior. For backward compatibility, every PE file begins with a minimal stub program, marked by the MZ magic number (hexadecimal 4D 5A), which allows older DOS systems to display a basic error message if the file is executed in that environment. The format's true PE structure follows at an offset specified in the DOS header, starting with the PE signature (hexadecimal 50 45 00 00, or "PE\0\0" in ASCII), and it offers extensibility through optional headers and section tables for adding custom metadata like debug symbols or security attributes without disrupting core functionality.

Supported Architectures and Variants

The (PE) format supports several primary hardware , enabling executables to run on diverse processor types within the Windows ecosystem. The core format is designed for portability across these architectures, with the machine type field in the COFF header specifying the target processor. For 32-bit (IA-32), the PE32 variant is used, supporting the x86 instruction set with a 32-bit . This remains the standard for legacy 32-bit Windows applications. For 64-bit extensions of the x86 (x86-64 or AMD64), the PE32+ variant extends the format to accommodate a 64-bit while maintaining with PE32 structures. architectures are also supported, including 32-bit () via the PE32 format for embedded and legacy scenarios, and 64-bit (AArch64) through PE32+ for modern Windows on ARM devices, which emphasize native 64-bit execution with emulation layers for x86 code. Several variants of the PE format exist to address specific use cases and extensions. The original PE32 format targets 32-bit systems, featuring a 32-bit ImageBase and other fields suited to smaller address spaces. PE32+ serves as the 64-bit counterpart, widening key fields such as ImageBase to 64 bits, SizeOfImage to support larger executables up to 16 exabytes in theory, and adjusting stack and heap commit/reserve sizes to 64-bit values, while removing the BaseOfData field as unnecessary in flat address models. For embedded systems, Windows CE employs a compact PE variant optimized for resource-constrained devices, with adjusted default ImageBase values (e.g., 0x00010000 for executables) and support for processors, though it shares the core PE structure. More recently, the Arm64X variant was introduced in the SDK to facilitate hybrid execution on ARM64 platforms, allowing a single binary to contain both native ARM64 code and emulated x64 (AMD64) segments for improved compatibility and performance in mixed workloads. As of 2025, the PE format has seen incremental enhancements rather than fundamental overhauls. In builds using Windows SDK version 10.0.26100.0 (initially released May 2024), the Universal CRT introduces a new optional section named .fptable, which contains an array of function pointers serving as a compatibility cache for Windows API function addresses, populated dynamically to adapt to different Windows versions without hard link failures. This addition supports advanced optimization techniques without altering the core PE layout. Broader compiler improvements in this SDK version include refined support for security-related flags, such as enhanced control flow guard (CFG) and (ASLR) integrations, though the base PE specification remains stable. The PE format also ensures compatibility with firmware environments through a subset of its PE/COFF structure. firmware implementations utilize this subset for bootloaders and drivers, employing PE32+ images with modifications like an altered header signature (TE for Terse Executable in some cases) to minimize overhead while retaining essential loading mechanisms.

History

Origins in COFF

The Common Object File Format (COFF) originated in the early as a portable format developed by for implementations, enabling cross-architecture compatibility for compiled code and libraries. adopted and extended COFF in the late for its own systems, drawing on the format's established structure to support advanced features in emerging operating environments. A key milestone came in 1989, when Microsoft specified COFF as the foundational format for object files in the NT OS/2 project, an internal initiative to create a portable 32-bit operating system. This adaptation built on COFF's Unix roots but incorporated Microsoft-specific extensions for enhanced tool compatibility, such as integration with existing linkers and debuggers. The NT OS/2 specification, drafted in November 1989, outlined COFF's role in handling relocations, symbols, and sections to facilitate efficient compilation and linking processes. The Portable Executable (PE) format emerged directly from this COFF foundation, with introducing it in 1993 as the standard for 32-bit executables in 3.1. PE extended COFF by adding an optional header and image-specific structures, replacing earlier formats such as the (NE) for 16-bit Windows and the Linear Executable (LE) for 32-bit to enable robust 32-bit support on Intel x86 architectures. Initial design goals for PE emphasized portability across multiple CPU types and Windows variants, allowing executables to run without architecture-specific recompilation. It also prioritized support for dynamic linking, which streamlined loading of shared libraries and reduced executable sizes through tables. Additionally, PE maintained backward compatibility with COFF-based tools, ensuring seamless integration with compilers and linkers developed for prior systems.

Evolution and Adoption

The Portable Executable (PE) format debuted with the release of Windows NT 3.1 in 1993, establishing it as the foundational structure for 32-bit executables on the NT kernel and marking a shift from earlier formats like the New Executable (NE). By 1995, PE gained traction in consumer Windows through the Win32s subsystem, which enabled 32-bit application support on Windows 3.1x, and was fully integrated into Windows 95 for native 32-bit programs. This adoption accelerated with Windows 98 in 1998, where PE supplanted the NE format for the majority of executables, unifying the file structure across Windows platforms. In the , PE evolved to accommodate emerging architectures and paradigms. The PE32+ variant, supporting 64-bit addressing, was introduced alongside in 2005, extending the format's longevity for AMD64 systems while maintaining with PE32 tools. Concurrently, the release of the .NET Framework 1.0 in 2002 integrated PE as the container for managed code assemblies, appending CLR metadata to the standard headers to enable without altering the core executable layout. The 2010s and 2020s brought further enhancements for diverse hardware and boot environments. support arrived with in 2012, allowing PE binaries to target processors via the IMAGE_FILE_MACHINE_ARM machine type, initially for mobile devices. integration began with in 2007, leveraging PE/COFF for bootloaders and applications under the EFI subsystem. By 2025, updates in the SDK introduced Arm64X, a hybrid PE variant (IMAGE_FILE_MACHINE_ARM64X) embedding both native and ARM64EC code for seamless x64 emulation on devices. In 2025, machine types for (32-bit, 64-bit, and 128-bit) were added to the PE format, expanding support to this open-source . PE has become the de facto standard for all Windows executables, dynamic-link libraries (DLLs), and object files, powering the vast majority of software in the ecosystem as of 2025, with development tools like mandating compliance for native and managed builds. challenges, such as running 32-bit PE binaries on 64-bit systems, have been addressed through emulation layers like , which transparently translates calls and maintains execution fidelity across architectures.

File Format Structure

High-Level Layout

The Portable Executable (PE) file format employs a linear, sequential organization that enables the Windows loader to parse and map the file into memory systematically. It commences with a header and stub for compatibility with older systems, immediately followed by the PE signature, COFF file header, optional header, section table, and the raw data sections themselves. This arrangement positions all metadata before the actual code and data, streamlining the loading process from disk to . A key distinction in the PE layout lies between file offsets and virtual addresses: on disk, components are stored at specific byte offsets within the file, with sections often padded to maintain alignment (typically 512 bytes), whereas in memory, they are relocated to virtual addresses aligned to page boundaries (commonly 4 KB) relative to the image base address. This separation accommodates efficient file storage without compromising runtime performance or security isolation. The DOS stub, in particular, ensures basic compatibility by executing a minimal program if the file is run under , though its primary role is to point to the PE header. PE files exhibit a wide range of sizes depending on complexity, from minimal executables around 1 KB to several megabytes for comprehensive applications, with the initial headers and section table generally confined to the first or less due to their fixed and variable-length structures. During loading, the operating system maps sections into contiguous regions starting from the preferred base, adjusting alignments as needed; the in the optional header then directs execution flow, initiating the program's runtime behavior. This high-level flow—from disk parsing to execution—relies on the padded, aligned layout to minimize fragmentation and support dynamic linking.

DOS Header and Stub

The Portable Executable (PE) file format incorporates a DOS header and stub at its beginning to maintain with environments, allowing the file to be recognized and handled by legacy 16-bit systems. This design originated from the need to support mixed-use scenarios in early Windows deployments, where executables might be loaded on systems without full Windows support. The DOS header is a fixed 64-byte structure defined as IMAGE_DOS_HEADER, which mimics the MS-DOS 2.0 executable header format. Key fields include e_magic, a 2-byte value set to 0x5A4D (representing "MZ" for , an early developer), confirming the file as a valid DOS executable. Another critical field is e_lfanew, a 4-byte at offset 0x3C, which stores the absolute file offset to the subsequent PE signature ("PE\0\0"). The remaining fields, such as e_cblp (bytes on last page) and e_cp (number of pages), are largely vestigial but preserve the original layout for compatibility with 16-bit tools like older linkers and debuggers. Immediately following the DOS header is the DOS stub, a compact 16-bit MS-DOS executable program, typically 64 bytes in length for the default stub, that functions as a valid MS-DOS executable which, when loaded by a DOS environment, executes its code after relocation by the DOS loader. When loaded by a DOS interpreter, the stub prints an error message such as "This program cannot be run in DOS mode." and terminates gracefully, preventing crashes on incompatible systems. The stub can be customized by developers—for instance, to include branding or additional logic—using the Microsoft linker option /STUB to specify an alternative executable file. After the stub, padding bytes (often zeros) precede the PE header, ensuring the overall DOS-compatible prefix is typically 128 bytes in standard compiler outputs for streamlined file handling. This legacy component persists in modern PE files primarily to support interoperability with historical tools and environments, though Windows loaders ignore it entirely during execution.

Core Headers

COFF File Header

The COFF File Header is a mandatory 20-byte structure in Portable Executable (PE) files, located immediately after the PE signature ("PE\0\0", or bytes 50 45 00 00 in hexadecimal) at the file offset specified by the e_lfanew field in the preceding MS-DOS header. This header provides fundamental metadata about the executable, enabling the Windows loader to identify the target architecture, file attributes, and basic layout before proceeding to subsequent structures. Unlike object files, where the COFF header appears at the file's start and the optional header is absent, PE image files require both for runtime execution. The header consists of seven fields, each with a fixed offset and size, as detailed in the following table. These fields are defined in the Windows SDK header winnt.h and adhere to little-endian byte order.
Offset (bytes)Size (bytes)FieldTypeDescription
02MachineWORD (unsigned short)Specifies the target processor architecture; common values include 0x014C for 386 or later (x86) and 0x8664 for AMD64 (x64).
22NumberOfSectionsWORD (unsigned short)Indicates the number of sections in the file, which determines the size of the following section header table; typical PE files have 5 to 10 sections.
44TimeDateStampDWORD (unsigned long)A Unix (seconds since January 1, 1970, 00:00 UTC) recording when the file was created or linked, often used for versioning or .
84PointerToSymbolTableDWORD (unsigned long)File offset to the COFF for symbols; set to 0 in most PE images, as this feature is deprecated for executables.
124NumberOfSymbolsDWORD (unsigned long)Count of entries in the symbol table; typically 0 for PE images, relevant only for object files.
162SizeOfOptionalHeaderWORD (unsigned short)Length of the following optional header in bytes; 224 for PE32 (32-bit) and 240 for PE32+ (64-bit) formats.
182CharacteristicsWORD (unsigned short)A bitmask of flags defining file properties, such as IMAGE_FILE_EXECUTABLE_IMAGE (0x0002) for runnable images, IMAGE_FILE_DLL (0x2000) for dynamic-link libraries, IMAGE_FILE_RELOCS_STRIPPED (0x0001) indicating no base relocations (must load at preferred base address), and IMAGE_FILE_LARGE_ADDRESS_AWARE (0x0020) for support of addresses larger than 2 GB.
The primary role of the COFF File Header is to establish the file's compatibility with the host system and outline its high-level organization, ensuring the loader can validate and map the executable correctly into memory. For instance, the field dictates processor-specific handling, while Characteristics flags influence loading behaviors like relocation requirements or limits. The Windows loader enforces limits, such as a maximum of 96 sections, to prevent malformed files from consuming excessive resources. This structure's fixed size and simplicity facilitate rapid parsing during process initialization.

Optional Header

The Optional Header in the Portable Executable (PE) format immediately follows the COFF file header and provides essential information to the Windows loader for mapping the executable into memory and initiating execution. Despite its name, this header is mandatory for PE image files (such as executables and DLLs) and is absent in object files; it is termed "optional" only in the context of the broader COFF specification, where it enhances functionality for executable images. The header's size is specified in the COFF header's SizeOfOptionalHeader field and differs between 32-bit (PE32) and 64-bit (PE32+) variants: PE32 totals 224 bytes, while PE32+ extends to 240 bytes due to widened address fields accommodating larger memory spaces. The standard fields, comprising the first 28 bytes in PE32 and 24 bytes in PE32+, establish the basic image characteristics and are common to both variants. The Magic field (2 bytes) identifies the header type: 0x10B for PE32 and 0x20B for PE32+, signaling the loader to use 32-bit or 64-bit addressing accordingly. Following this are the MajorLinkerVersion and MinorLinkerVersion (1 byte each), which record the linker tool's version used to build the image. SizeOfCode (4 bytes) indicates the total size of executable code sections in bytes, while SizeOfInitializedData and SizeOfUninitializedData (4 bytes each) specify the aggregate sizes of initialized and zero-initialized data sections, respectively. The AddressOfEntryPoint field (4 bytes) holds the relative virtual address (RVA) of the image's , where execution begins after loading. BaseOfCode (4 bytes) provides the RVA of the first code section, and in PE32 only, BaseOfData (4 bytes) marks the RVA of the first data section (omitted in PE32+ to streamline the structure). Windows-specific fields, extending the header with platform-dependent details, occupy the subsequent 68 bytes in PE32 and 88 bytes in PE32+, focusing on memory layout and runtime behavior. ImageBase (4 bytes in PE32, 8 bytes in PE32+) defines the preferred virtual base address for loading the image, with a default of 0x00400000 for most PE32 executables and 0x0000000140000000 for PE32+ to align with 64-bit conventions. SectionAlignment (4 bytes) sets the alignment granularity for sections in virtual memory, typically 0x1000 (one page), ensuring efficient memory mapping. FileAlignment (4 bytes) dictates raw data alignment on disk, defaulting to 0x200 (512 bytes) as a power of 2 between 512 and 64 KB for optimal file I/O. MajorOperatingSystemVersion and MinorOperatingSystemVersion (2 bytes each), along with MajorImageVersion, MinorImageVersion, MajorSubsystemVersion, and MinorSubsystemVersion (2 bytes each), encode version information for compatibility, such as 6.02 for Windows 10 and later. SizeOfImage (4 bytes) represents the total virtual size of the loaded image, a multiple of SectionAlignment, while SizeOfHeaders (4 bytes) covers the combined size of all headers plus section headers, aligned to FileAlignment. Stack and heap management fields—SizeOfStackReserve, SizeOfStackCommit, SizeOfHeapReserve, and SizeOfHeapCommit (4/8 bytes each, varying by variant)—reserve and commit initial memory for the thread stack and default heap. The Subsystem field (2 bytes) specifies the runtime environment, with values like 2 for Windows GUI applications and 3 for console applications, influencing how the image interacts with the operating system. DLLCharacteristics (2 bytes) is a bitfield of flags controlling DLL-specific behaviors, such as DYNAMIC_BASE (0x0040) enabling Address Space Layout Randomization (ASLR) for security. Finally, NumberOfRvaAndSizes (4 bytes) indicates the count of data directory entries (typically 16), which follow the header and point to key tables like imports and exports. Overall, the Optional Header directs the loader in establishing the , aligning sections, and configuring subsystems, ensuring seamless execution without delving into the contents of referenced structures.

Sections and Tables

Section Headers and Table

The section table in the Portable Executable (PE) format consists of an array of IMAGE_SECTION_HEADER structures that immediately follows the optional header, with the number of entries specified by the NumberOfSections field in the COFF file header. Each entry is 40 bytes in length for both PE32 and PE32+ formats, defining the properties and locations of individual sections within the file. This table enables the Windows loader to map sections into with appropriate protections and alignments, ensuring efficient execution and access. The IMAGE_SECTION_HEADER structure includes several key fields that describe each section's identity, size, positioning, and attributes. The Name field is an 8-byte ASCII string (null-padded if necessary), such as ".text" for sections. VirtualSize specifies the size of the section in as a relative virtual address (RVA), while VirtualAddress indicates the starting RVA of the section in the image's . SizeOfRawData represents the size of the section's initialized data on disk, and PointerToRawData provides the file offset to that data. Additional fields include PointerToRelocations and PointerToLinenumbers (file offsets to relocation and line-number entries, respectively, typically zero for images), along with NumberOfRelocations and NumberOfLinenumbers (counts of those entries). The Characteristics field is a 32-bit flags value that defines section attributes, such as IMAGE_SCN_CNT_CODE (0x00000020) for executable , IMAGE_SCN_MEM_EXECUTE (0x20000000) for executable , IMAGE_SCN_MEM_READ (0x40000000) for readable , and IMAGE_SCN_MEM_WRITE (0x80000000) for writable .
FieldSize (bytes)Description
Name8Null-padded ASCII section name (e.g., .text).
VirtualSize4Size of the section in (RVA-based).
VirtualAddress4RVA of the first byte of the section in memory.
SizeOfRawData4Size of the initialized data on disk.
PointerToRawData4File offset to the raw section data.
PointerToRelocations4File offset to relocation entries (0 for images).
PointerToLinenumbers4File offset to line-number entries (0 for images).
NumberOfRelocations2Number of relocation entries.
NumberOfLinenumbers2Number of line-number entries.
Characteristics4Flags for section attributes (e.g., code, execute, read, write).
The table plays a critical role in mapping sections from the file to protected memory regions during loading, with entries typically sorted in ascending order by VirtualAddress to reflect their layout in the . Section data on disk is aligned to the FileAlignment value (default 512 bytes) specified in the optional header, while in , alignment follows the SectionAlignment (typically page size). Due to file alignment constraints and the fixed size of the headers, the maximum number of sections is limited to approximately 96.

Common Section Types

The Portable Executable (PE) format organizes a file's content into sections, each serving a distinct purpose and protected by attributes that dictate memory access permissions when loaded. These attributes are bit flags defined in the section header, such as IMAGE_SCN_MEM_EXECUTE for executable regions, IMAGE_SCN_MEM_READ for readable areas, and IMAGE_SCN_MEM_WRITE for writable ones; combinations of these flags map to Windows memory protection constants like PAGE_EXECUTE_READ (executable and readable but not writable). Sections are optional and can vary by compiler or build tool, but several are conventional in PE executables and DLLs. Common sections include those for code, data, and supporting structures. The .text section holds the program's instructions and typically contains the where execution begins; it is marked executable and readable to allow the loader to map it into for running. The .data section stores initialized global and static variables, such as those with non-zero starting values, and is configured as readable and writable to support runtime modifications. In contrast, the .rdata section contains read-only initialized data, like constant strings or lookup tables, ensuring immutability with readable-only attributes. For uninitialized data, the .bss section reserves space for variables that start as zero-filled; unlike other sections, it occupies no space in the file itself and exists only in when loaded, with readable and writable protections. Import-related data resides in the .idata section, which includes thunks and lookup tables for resolving external function calls; it is generally readable, though some implementations add write permissions for loader adjustments. Similarly, the .edata section manages export information for DLLs, listing available functions and their addresses, protected as readable. Resources such as icons, dialog templates, and string tables are bundled in the .rsrc section, which is readable and often compressed or aligned for efficient access during program initialization. Relocation fixes are stored in the .reloc section, enabling the image to load at different base addresses; it is readable and marked discardable, meaning it can be freed after initial processing. Optional sections like .pdata provide data, such as unwind information for stack traces, while .debug holds symbolic debug metadata; both are readable and typically discardable in release builds. Developers can define custom sections for specialized needs, such as .tls for variables that each thread initializes separately (readable and writable) or .gfids$xx for guard function IDs used in guard mechanisms to prevent indirect call hijacking. These custom sections inherit standard attribute flags but may include additional compiler-specific markings.
Section NamePurposeKey Attributes (Flags)
.textExecutable and IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ
.dataInitialized writable (e.g., global variables)IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
.rdataRead-only initialized (e.g., constants)IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
.bssUninitialized (zero-filled at load, virtual only)IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
.idata thunks and lookup tablesIMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ (write optional)
.edata directory and namesIMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
.rsrcEmbedded resources (e.g., icons, menus)IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
.relocBase relocation blocksIMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
.pdata (e.g., unwind info)IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
.debugDebug symbols and line numbersIMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
.tls templateIMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
.gfids guard function IDsCustom; typically IMAGE_SCN_MEM_READ

Data Directories

Import Table

The import table in the Portable Executable (PE) format specifies the external functions and data that an executable or DLL imports from other DLLs, enabling the Windows loader to resolve dependencies at load time. It is located in the .idata section and referenced by the Import Table entry (directory index 1) in the optional header's data directories array, which provides the relative virtual address (RVA) and size of the table. This setup allows the loader to efficiently map imports without scanning the entire file. The core structure is an array of IMAGE_IMPORT_DESCRIPTOR structures, each 20 bytes in size, describing imports from a single DLL and terminated by a null entry (all fields zero). The fields are as follows:
OffsetSize (bytes)FieldDescription
04OriginalFirstThunkRVA to the Import Lookup Table (ILT, also known as the Hint/Name Table or INT); zero if unused.
44TimeDateStampSet to zero if unbound; otherwise, holds the timestamp from binding for cached resolution.
84ForwarderChainIndex of the first forwarder reference or -1 if none; used for chained imports across DLLs.
124NameRVA to the null-terminated ASCII string of the DLL name (e.g., "kernel32.dll").
164FirstThunkRVA to the Import Address Table (IAT).
The IMAGE_IMPORT_DESCRIPTOR structure is 20 bytes in both PE32 and PE32+ formats. In PE32+, the entries in the Import Address Table (IAT) and Import Lookup Table (ILT) are 64-bit, while the RVAs in the descriptor remain 32-bit DWORDs. The Import Address Table (IAT) is an array of 32-bit (PE32) or 64-bit (PE32+) entries, initially mirroring the ILT but updated by the loader with actual procedure addresses or ordinals from the target DLL. Each entry can represent a function by ordinal (high bit set, lower bits as ordinal value) or by name (RVA to an IMAGE_IMPORT_BY_NAME structure in the ILT). The array ends with a zero entry. Bound imports optimize this by pre-filling the IAT with addresses using the TimeDateStamp for validation; if the DLL's timestamp matches, the loader skips rebinding, reducing load time. The Hint/Name Table (INT), referenced by OriginalFirstThunk, pairs with the IAT and contains entries of type IMAGE_IMPORT_BY_NAME: a 2-byte hint (an ordinal index into the exporting DLL's name pointer table for faster lookup) followed by a variable-length null-terminated ASCII function name, optionally padded to DWORD alignment. If the high bit of an IAT entry is set, the lower bits specify the ordinal directly, bypassing the name lookup. During loading, the Windows loader processes the import table sequentially: it loads each referenced DLL, resolves function addresses via the exporting DLL's export table using hints or names from the INT, and overwrites the IAT with these addresses for runtime use. PE supports delay-loaded imports via a separate .delay section and descriptor (directory index 13), where DLLs load only on first access, with a helper routine updating a dedicated delay IAT; this defers non-essential dependencies to improve startup performance. A common example is importing the GetProcAddress function from kernel32.dll, specified in the INT as a 2-byte hint followed by the string "GetProcAddress\0", with the corresponding IAT entry resolved to the function's address (e.g., 0x7FF8A1234567 in a loaded ). This mechanism ensures modular across Windows applications.

Export Table

The export table in the Portable Executable (PE) format defines the functions and data that a (DLL) or (EXE) exposes for use by other modules, facilitating dynamic linking and symbol resolution. Primarily utilized in DLLs, it allows importing modules to access exported symbols either by name for or by ordinal for , with ordinals providing a compact indexing mechanism that avoids string comparisons during runtime loading. EXEs rarely export symbols, as their primary role is execution rather than serving as shared libraries. The export table resides in the .edata section of the PE file and is referenced by the first entry (index 0) in the Optional Header's Data Directory array, which provides the relative virtual address (RVA) and size of the table. This directory entry points to the core structure, known as the IMAGE_EXPORT_DIRECTORY, a fixed 40-byte header that organizes the associated tables. The structure begins with a Characteristics field (DWORD, reserved and must be set to 0), followed by a TimeDateStamp (DWORD) recording the module's build timestamp in seconds since January 1, 1970, which aids in versioning and dependency checks. Subsequent fields include MajorVersion and MinorVersion (both WORD), indicating the export table's version; Name (DWORD), an RVA to an ASCII string of the module's name; and Base (DWORD), the starting ordinal value (typically 1) used as an offset for indexing. The header concludes with NumberOfFunctions (DWORD), specifying the count of entries in the Export Address Table; NumberOfNames (DWORD), the count of named exports; and three RVAs: AddressOfFunctions (DWORD) to the Export Address Table, AddressOfNames (DWORD) to the Export Name Table, and AddressOfNameOrdinals (DWORD) to the Ordinal Table.
FieldOffset (bytes)TypeDescription
Characteristics0DWORDReserved; must be 0.
TimeDateStamp4DWORD of the image.
MajorVersion8WORDMajor version number.
MinorVersion10WORDMinor version number.
Name12DWORDRVA of ASCII string containing the module name.
Base16DWORDStarting (base index for exports).
NumberOfFunctions20DWORDNumber of entries in the Export Address Table.
NumberOfNames24DWORDNumber of entries in the Export Name Table.
AddressOfFunctions28DWORDRVA of the Export Address Table.
AddressOfNames32DWORDRVA of the Export Name Table.
AddressOfNameOrdinals36DWORDRVA of the Ordinal Table.
The Export Address Table (EAT) is an of DWORDs, each holding an RVA to the actual address of an exported function or item within the module; if the value falls within the .edata section, it indicates a forwarder string RVA pointing to another DLL's export. The Export Name Table (ENT) consists of an of DWORD RVAs, each pointing to a null-terminated ASCII string representing an exported symbol's name, sorted alphabetically to enable binary search for efficient lookup. Complementing these, the Ordinal Table is an of WORD values that map each named export to its corresponding index in the EAT, offset by the Base value; this allows ordinal-based access without name resolution, reducing load-time overhead. For example, if Base is 1 and an ordinal table entry is 5, it indexes the 5th function in the EAT (0-based internally). In practice, the export table supports flexible symbol exposure: loaders or linkers traverse the ENT to find a name, use the parallel Ordinal Table to get the index, and then retrieve the address from the EAT. Forwarding enables modular design, such as a DLL re-exporting a function from the Windows kernel (e.g., forwarding HeapAlloc to ntdll.dll), which is indicated by EAT entries containing forwarder RVAs rather than direct function addresses. The TimeDateStamp and version fields, while not enforcing strict semantics, help tools and loaders detect mismatches in linked modules during development or debugging. No specific flag bits are defined in the Characteristics field, maintaining simplicity in the format.

Loading and Security Features

Relocation Table

The base relocation table in the Portable Executable (PE) format enables the Windows loader to adjust absolute addresses within an executable image when it is loaded at a virtual address different from the preferred ImageBase specified in the optional header. This table is essential for supporting position-independent loading, allowing the image to be relocated dynamically without requiring recompilation. It resides in the .reloc section of the PE file and is referenced by the fifth entry (index 5) in the optional header's data directory array, which provides the relative virtual address (RVA) and size of the table. The table consists of one or more contiguous blocks, each described by an IMAGE_BASE_RELOCATION structure aligned on a 32-bit boundary. This structure includes a 4-byte VirtualAddress field, which holds the RVA of the 4 KB page (4096-byte boundary) containing the addresses to be relocated, and a 4-byte SizeOfBlock field, indicating the total size of the block in bytes, including the header and all subsequent relocation entries. Following the header is an array of 16-bit (2-byte) entries packed into DWORDs (four entries per 32 bits), where each entry encodes a 4-bit relocation type in the high bits and a 12-bit offset in the low bits; the offset specifies the byte displacement from the page's VirtualAddress where the adjustment must be applied. The number of entries in a block is calculated as (SizeOfBlock - 8) / 2, excluding any padding entries of type IMAGE_REL_BASED_ABSOLUTE (value 0), which are skipped by the loader. Relocation types are architecture-specific and determine how the base adjustment is applied to the target field. For 32-bit PE32 images (e.g., x86), the primary type is IMAGE_REL_BASED_HIGHLOW (value 3), which adds the full 32-bit signed delta to an absolute address field, effectively combining high and low 16-bit adjustments. In 64-bit PE32+ images (e.g., x64), IMAGE_REL_BASED_DIR64 (value 10) is used instead, applying the 64-bit delta to the address. Other types include IMAGE_REL_BASED_HIGH (1) for the high 16 bits of the delta and IMAGE_REL_BASED_LOW (2) for the low 16 bits, while architecture-specific variants exist for architectures like ARM, such as IMAGE_REL_BASED_ARM_MOV32 (value 5) for adjusting MOV32 instructions. These types ensure compatibility across processors by tailoring the fixup to the instruction set's addressing modes. During loading, the Windows image loader computes the relocation delta as the difference between the actual load base address and the preferred ImageBase from the optional header; if the delta is zero or the image lacks a dynamic base flag, relocation is skipped. The loader locates the relocation directory via its RVA from the data directory and iterates over the contiguous relocation blocks within the specified size. For each block, it processes the type/offset entries, skipping those of type IMAGE_REL_BASED_ABSOLUTE. For valid entries, it computes the fixup virtual address as the block's VirtualAddress plus the 12-bit offset (modulo 4096), converts this RVA to a virtual address, and applies the type-specific adjustment by adding the delta to the value at that memory location—typically a full 32-bit addition for IMAGE_REL_BASED_HIGHLOW or 64-bit for IMAGE_REL_BASED_DIR64—thus modifying pointers, jump targets, or data references. If the delta is zero (i.e., loaded at the preferred base), the table is ignored, optimizing for the common case. The table's size is variable and can be omitted or stripped in release builds where a fixed base address is assumed, but it is required for executables supporting dynamic relocation, such as those enabling address space layout randomization.

c

typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress; DWORD SizeOfBlock; // WORD TypeOffset[1]; // Variable-length array of 16-bit entries } IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION;

typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress; DWORD SizeOfBlock; // WORD TypeOffset[1]; // Variable-length array of 16-bit entries } IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION;

This structure, as defined in the PECOFF specification, forms the basis for each relocation block, with the extending until the block size is exhausted.

Address Space Layout Randomization (ASLR)

(ASLR) is a mitigation in the Portable Executable (PE) format that randomizes the loading addresses of executable images, libraries, stacks, heaps, and process environment block (PEB) structures to hinder memory corruption exploits, such as buffer overflows, by making return-to-libc or similar attacks less predictable. This feature is opt-in for PE files, enabled by setting the IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag (0x0040) in the DLL Characteristics field of the optional header, which signals the Windows loader to rebase the image dynamically at runtime. ASLR operates at varying levels depending on configuration: partial or "bottom-up" randomization applies to allocations like stacks and heaps starting from lower addresses, providing limited but additional unpredictability even for non-opted-in images when system-wide settings are enabled. Full ASLR, triggered by the /DYNAMICBASE linker flag during compilation, randomizes the preferred ImageBase address for the entire PE image and requires the presence of a base relocation table (.reloc section) to adjust absolute addresses, imports, and other fixups after applying the offset. Without relocations, the image loads at its fixed base, rendering ASLR ineffective. During loading, the Windows loader selects a random offset—typically aligned to 64 KB boundaries for 32-bit images and larger for 64-bit—and adds it to the ImageBase, updating the address table (IAT) and applying relocations to ensure correct execution at the new location. This also randomizes stack and heap bases, as well as the PEB, which contains metadata like loaded module lists, to obscure pointers exploitable by attackers. Enhancements introduced in and later include high-entropy ASLR for 64-bit processes, enabled via the /HIGHENTROPYVA linker flag, which expands randomization entropy to up to 28 bits by supporting larger address spaces and coarser alignments, such as 256 MB steps for image bases, significantly increasing the search space for exploits. ASLR is fully compatible with Data Execution Prevention (DEP), another PE security feature that marks non-executable regions as non-writable, allowing layered defenses without conflict. It complements other mitigations like Control Flow Guard (CFG) by randomizing targets, though CFG enforces additional validation. As of 2025, ASLR remains a standard feature in and later, with mandatory system-wide enforcement configurable via Exploit Protection settings in Windows Security, ensuring even legacy modules are rebased when possible. Recent Windows SDK versions provide refined linker flags like /DYNAMICBASE:NO for opt-out in scenarios, but the core PE format and ASLR mechanics have seen no fundamental changes. Limitations include incomplete coverage for statically linked executables or modules lacking relocation tables, which load at fixed addresses and reduce overall ; such cases can be identified and measured using legacy tools like the Enhanced Mitigation Experience Toolkit (EMET), though modern diagnostics rely on built-in cmdlets or Exploit Protection audits.

Managed Code Extensions

.NET and CLR Metadata

The Portable Executable (PE) format is extended for .NET managed code through the use of PE32 or PE32+ executables, which incorporate a CLR-specific header located in data directory entry 14, known as the COM Descriptor directory (IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR). This extension allows the Windows PE loader to recognize and initialize .NET assemblies by pointing to the IMAGE_COR20_HEADER structure, which encapsulates runtime-specific information. The IMAGE_COR20_HEADER structure begins with the cb field, a DWORD indicating the size of the header itself, followed by WORD fields for MajorRuntimeVersion (typically 2) and MinorRuntimeVersion, which specify the required (CLR) version. The Flags DWORD includes bits such as ILONLY (0x00000001), signaling that the image contains only managed code without native x86 instructions, and other flags for aspects like strong naming or tracking. The EntryPointToken is a DWORD representing a metadata token (typically from the MethodDef table) for the assembly's method, enabling the CLR to invoke the appropriate startup routine. This header is immediately followed by an IMAGE_DATA_DIRECTORY for the MetaData, which points to the metadata root containing the MDIR (Metadata Directory) header and associated tables. Additional directories in the header cover resources, strong name signatures, and other managed elements. The metadata structure within the PE file is a self-describing binary format that includes a root header (MDIR) detailing the version, reserved flags, and a list of , followed by the actual data and tables. Key comprise #~ (the primary metadata stream defining table schemas and row counts), #Strings (a heap of encoded constant strings for names), #US (User Strings heap for literal strings in ), and #Blob (a heap for binary large objects like method signatures and constants). The tables, stored in the #~ stream, consist of up to 64 predefined schemas with rows indexed by 2-, 4-, or 8-byte row IDs (RIDs); for example, the Module table holds a single row describing the assembly module with fields like name (indexed into #Strings), the TypeRef table references external types with resolution scopes and names, and the MethodDef table defines methods with RIDs, flags, names, signatures (Blob heap indexes), and RVA pointers to IL . Metadata tokens, 4-byte values with a high indicating the table (e.g., 0x06 for MethodDef) and lower bits as the RID, enable efficient runtime lookups. In the execution process, the PE loader identifies the CLR header and delegates control to the CLR host via mscoree.dll, which loads the metadata into memory and initializes the runtime environment. For traditional JIT-based execution, the just-in-time (JIT) compiler then leverages the metadata tables and heaps to verify, optimize, and translate Microsoft Intermediate Language (MSIL) code—stored in the .text section of the PE file—into native machine code. In native AOT (ahead-of-time) deployments, available since .NET 7, the code is pre-compiled to native machine instructions during build time, eliminating runtime JIT while still using the CLR header for initialization. Resources within the PE file may include an assembly manifest for strong naming, which embeds a digital signature in the CLR header's StrongNameSignature directory to ensure integrity and authenticity. This PE extension for .NET originated with the .NET Framework 1.0 release in February 2002 and has evolved through subsequent versions, including .NET Framework updates and the unified .NET platform up to .NET 10 in November 2025, maintaining backward compatibility for metadata and header structures across releases.

COM Integration

The Portable Executable (PE) format supports components through DLLs and EXEs that implement COM interfaces and classes, identified by unique class identifiers (CLSIDs). These files enable the creation of reusable software components that can be instantiated via COM activation mechanisms, such as CoCreateInstance. Registration of these components occurs via exported entry points like DllRegisterServer, which populates the with CLSID mappings under HKEY_CLASSES_ROOT\CLSID, allowing the COM runtime to locate and load the appropriate PE file. For enhanced COM implementations under COM+, the PE optional header includes data directory entry 14 (IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR), which points to the IMAGE_COR20_HEADER structure describing the COM+ runtime environment. This header, similar to its .NET counterpart, contains fields for metadata location, entry point RVA, and flags such as COMIMAGE_FLAGS_ILONLY to indicate intermediate language-only code execution. An optional .cormeta section in object files (merged into the final PE) stores preliminary CLR metadata for COM+ managed extensions. Type libraries provide binary descriptions of COM interfaces, coclasses, and methods, typically generated from Interface Definition Language (IDL) files. In PE files, these are embedded as resources under the RT_TYPELIB type within the .rsrc section or supplied as standalone .tlb files, facilitating late binding and automation. During registration, DllRegisterServer often loads and registers the type library to enable client discovery of component capabilities. COM originated in the 1990s as an evolution of (OLE), providing a binary standard for software on Windows. It advanced to COM+ with the release of , incorporating Transaction Server (MTS) features like resource pooling, queued components, and role-based security for enterprise applications. COM+ later integrated with .NET through Runtime Callable Wrappers (RCW) for managed clients accessing COM objects and COM Callable Wrappers (CCW) for the reverse, enabling hybrid unmanaged-managed scenarios. As of 2025, COM remains a legacy technology integral to many Win32 applications but is largely supplanted by (WinRT) in (UWP) development, where traditional COM registration and activation are deprecated.

Cross-Platform Use

Compatibility Layers

Wine is an open-source that enables the execution of Windows Portable Executable (PE) files on and macOS systems. It achieves this by parsing the PE file headers to interpret the executable structure, mapping the PE sections into the host operating system's native formats—such as ELF on or on macOS—and emulating the Win32 calls through a user-space implementation. This approach allows many Windows applications to run without native recompilation, though it relies on runtime translation rather than direct hardware execution. ReactOS, an open-source operating system designed as a binary-compatible implementation of , incorporates a native PE loader directly into its kernel module . This loader fully supports the PE/COFF format, enabling the loading and execution of PE executables as part of its core functionality, similar to Windows itself. By replicating the Windows kernel environment, ReactOS provides a more integrated execution path for PE files compared to pure translation layers. For managed code, Mono serves as an open-source implementation of the .NET runtime on non-Windows platforms like . It processes PE files containing (CLR) metadata by loading the assemblies and using a just-in-time () compiler to translate the intermediate language into native executable on the host architecture. This extends PE compatibility specifically to .NET applications, leveraging the ECMA-335 standard for CLI support. Compatibility layers for PE execution on non-Windows systems face several limitations. They generally do not support kernel-mode drivers, as these require direct hardware interaction that user-space emulation cannot fully replicate without risking system instability. Performance overhead arises from the translation and emulation processes, particularly in graphics-intensive or real-time applications, where API interception can introduce latency compared to native execution. Features like (ASLR) and relocations are emulated at the application level, potentially reducing security effectiveness and adding computational cost during loading.

Native Implementations

The utilizes a tailored subset of the Portable Executable/Common Object File Format (PE/COFF) for loading EFI applications, including bootloaders and drivers, in the pre-boot environment. This format supports PE32 and PE32+ structures, with specific fields in the optional header—such as ImageBase, SectionAlignment, FileAlignment, and Subsystem (values 10 for EFI_APPLICATION, 11 for EFI_BOOT_SERVICE_DRIVER, and 12 for EFI_RUNTIME_DRIVER)—adapted for execution. Sections are simplified to essentials like .text for code and .data for initialized data, omitting extraneous Windows-specific elements to ensure modularity and portability across architectures including , x64, , , , LoongArch, EBC, and . The UEFI loader handles PE/COFF images via boot services like LoadImage() and StartImage(), mapping them into memory types such as EfiLoaderCode or EfiBootServicesCode, with runtime drivers persisting post-ExitBootServices(). This enables dynamic loading for hardware initialization and OS handoff without relying on the full Win32 API; instead, custom firmware services manage relocations and execution minimally. The specification emphasizes secure boot integration, with certificates stored in the optional header's data directory for authentication. UEFI Specification version 2.10, finalized in August 2022, provides support for standard (ARM64) architecture in secure boot contexts, alongside other architectures. ARM64X and Arm64EC are Microsoft-specific PE extensions for Windows on ARM with x86 compatibility and are not part of the UEFI standard. As of 2025, remains the primary native PE/COFF implementation outside Windows, focused on and embedded boot scenarios, with limited adoption in new OS kernels due to the format's Windows-centric design. Historically, Windows CE employed a compact PE variant for mobile executables, excluding the stub to minimize overhead in resource-constrained devices, while retaining core COFF and optional headers for native loading. This approach supported embedded applications without full desktop API dependencies, using minimal relocation handling tailored to and x86 processors. BeOS Release 3 (R3) from the late used a PE variant natively for x86 executables, providing structural compatibility with Win32 before switching to ELF in later releases. Similarly, early versions of , a OS from the early 2000s, natively used the PE32 format for applications, leveraging its structure for the crossbar multitasking system with custom loaders bypassing full Windows dependencies, before transitioning to ELF. In modern minimalistic systems, employs PE format for drivers, with tools like pe2kos.exe converting standard PE binaries to its runtime environment for select executables. Certain real-time operating systems (RTOS), such as in its Win32 simulation port, generate PE executables for Windows-hosted testing, allowing native loading on host systems with minimal relocations for task simulation, though primary RTOS deployments favor platform-specific formats. These implementations differ fundamentally from Windows by lacking the complete Win32 API, relying instead on bespoke loaders for basic relocation and execution in constrained or alternative kernels.

References

  1. https://wiki.kolibrios.org/wiki/Writing_drivers_for_KolibriOS
Add your contribution
Related Hubs
Contribute something
User Avatar
No comments yet.