1 module libd.data.coff_pe; 2 3 import libd.datastructures : Array, Shared, makeShared; 4 5 @nogc nothrow: 6 7 enum DOS_HEADER_MAGIC = 0x5A4D; // MZ 8 enum COFF_HEADER_MAGIC = "PE\0\0"; 9 10 enum CoffMachineType : ushort 11 { 12 IMAGE_FILE_MACHINE_UNKNOWN = 0, 13 IMAGE_FILE_MACHINE_AM33 = 0x1d3, 14 IMAGE_FILE_MACHINE_AMD64 = 0x8664, 15 IMAGE_FILE_MACHINE_ARM = 0x1c0, 16 IMAGE_FILE_MACHINE_ARM64 = 0xaa64, 17 IMAGE_FILE_MACHINE_ARMNT = 0x1c4, 18 IMAGE_FILE_MACHINE_EBC = 0xebc, 19 IMAGE_FILE_MACHINE_I386 = 0x14c, 20 IMAGE_FILE_MACHINE_IA64 = 0x200, 21 IMAGE_FILE_MACHINE_M32R = 0x9041, 22 IMAGE_FILE_MACHINE_MIPS16 = 0x266, 23 IMAGE_FILE_MACHINE_MIPSFPU = 0x366, 24 IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466, 25 IMAGE_FILE_MACHINE_POWERPC = 0x1f0, 26 IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1, 27 IMAGE_FILE_MACHINE_R4000 = 0x166, 28 IMAGE_FILE_MACHINE_RISCV32 = 0x5032, 29 IMAGE_FILE_MACHINE_RISCV64 = 0x5064, 30 IMAGE_FILE_MACHINE_RISCV128 = 0x5128, 31 IMAGE_FILE_MACHINE_SH3 = 0x1a2, 32 IMAGE_FILE_MACHINE_SH3DSP = 0x1a3, 33 IMAGE_FILE_MACHINE_SH4 = 0x1a6, 34 IMAGE_FILE_MACHINE_SH5 = 0x1a8, 35 IMAGE_FILE_MACHINE_THUMB = 0x1c2, 36 IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169 37 } 38 39 enum CoffCharacteristics : ushort 40 { 41 IMAGE_FILE_RELOCS_STRIPPED = 0x0001, 42 IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002, 43 IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004, 44 IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008, 45 IMAGE_FILE_AGGRESSIVE_WS_TRIM = 0x0010, 46 IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020, 47 IMAGE_FILE_BYTES_REVERSED_LO = 0x0080, 48 IMAGE_FILE_32BIT_MACHINE = 0x0100, 49 IMAGE_FILE_DEBUG_STRIPPED = 0x0200, 50 IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400, 51 IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800, 52 IMAGE_FILE_SYSTEM = 0x1000, 53 IMAGE_FILE_DLL = 0x2000, 54 IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000, 55 IMAGE_FILE_BYTES_REVERSED_HI = 0x8000, 56 } 57 58 enum CoffPeSubsystem : ushort 59 { 60 IMAGE_SUBSYSTEM_UNKNOWN = 0, 61 IMAGE_SUBSYSTEM_NATIVE = 1, 62 IMAGE_SUBSYSTEM_WINDOWS_GUI = 2, 63 IMAGE_SUBSYSTEM_WINDOWS_CUI = 3, 64 IMAGE_SUBSYSTEM_OS2_CUI = 5, 65 IMAGE_SUBSYSTEM_POSIX_CUI = 7, 66 IMAGE_SUBSYSTEM_NATIVE_WINDOWS = 8, 67 IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9, 68 IMAGE_SUBSYSTEM_EFI_APPLICATION = 10, 69 IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11, 70 IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12, 71 IMAGE_SUBSYSTEM_EFI_ROM = 13, 72 IMAGE_SUBSYSTEM_XBOX = 14, 73 IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16, 74 } 75 76 enum CoffPeDllCharacteristics : ushort 77 { 78 IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020, 79 IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040, 80 IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY = 0x0080, 81 IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100, 82 IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200, 83 IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400, 84 IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800, 85 IMAGE_DLLCHARACTERISTICS_APPCONTAINER = 0x1000, 86 IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000, 87 IMAGE_DLLCHARACTERISTICS_GUARD_CF = 0x4000, 88 IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000, 89 } 90 91 enum CoffOptionalHeaderMagic : ushort 92 { 93 PE32 = 0x10B, 94 PE32Plus = 0x20B 95 } 96 97 enum CoffSectionTableCharacteristics : uint 98 { 99 IMAGE_SCN_TYPE_NO_PAD = 0x00000008, 100 IMAGE_SCN_CNT_CODE = 0x00000020, 101 IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040, 102 IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080, 103 IMAGE_SCN_LNK_OTHER = 0x00000100, 104 IMAGE_SCN_LNK_INFO = 0x00000200, 105 IMAGE_SCN_LNK_REMOVE = 0x00000800, 106 IMAGE_SCN_LNK_COMDAT = 0x00001000, 107 IMAGE_SCN_GPREL = 0x00008000, 108 IMAGE_SCN_MEM_PURGEABLE = 0x00020000, 109 IMAGE_SCN_MEM_16BIT = 0x00020000, 110 IMAGE_SCN_MEM_LOCKED = 0x00040000, 111 IMAGE_SCN_MEM_PRELOAD = 0x00080000, 112 IMAGE_SCN_ALIGN_1BYTES = 0x00100000, 113 IMAGE_SCN_ALIGN_2BYTES = 0x00200000, 114 IMAGE_SCN_ALIGN_4BYTES = 0x00300000, 115 IMAGE_SCN_ALIGN_8BYTES = 0x00400000, 116 IMAGE_SCN_ALIGN_16BYTES = 0x00500000, 117 IMAGE_SCN_ALIGN_32BYTES = 0x00600000, 118 IMAGE_SCN_ALIGN_64BYTES = 0x00700000, 119 IMAGE_SCN_ALIGN_128BYTES = 0x00800000, 120 IMAGE_SCN_ALIGN_256BYTES = 0x00900000, 121 IMAGE_SCN_ALIGN_512BYTES = 0x00A00000, 122 IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000, 123 IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000, 124 IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000, 125 IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000, 126 IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000, 127 IMAGE_SCN_MEM_DISCARDABLE = 0x02000000, 128 IMAGE_SCN_MEM_NOT_CACHED = 0x04000000, 129 IMAGE_SCN_MEM_NOT_PAGED = 0x08000000, 130 IMAGE_SCN_MEM_SHARED = 0x10000000, 131 IMAGE_SCN_MEM_EXECUTE = 0x20000000, 132 IMAGE_SCN_MEM_READ = 0x40000000, 133 IMAGE_SCN_MEM_WRITE = 0x80000000, 134 } 135 136 struct CoffPe 137 { 138 DosHeader dosHeader; 139 CoffHeader coffHeader; 140 CoffOptionalHeader optionalHeader; 141 Shared!(Array!CoffSectionTable) sectionTables; 142 143 // D goes "grrr" sometimes. 144 @nogc nothrow 145 this(scope ref return typeof(this) copy) 146 { 147 this.dosHeader = copy.dosHeader; 148 this.coffHeader = copy.coffHeader; 149 this.optionalHeader = copy.optionalHeader; 150 this.sectionTables.__ctor(copy.sectionTables); 151 } 152 } 153 154 struct DosHeader 155 { 156 ushort magic; 157 ushort[29] reserved; 158 uint newExeHeaderPtr; 159 } 160 static assert(DosHeader.sizeof == 64); 161 162 struct CoffHeader 163 { 164 char[4] magic; 165 CoffMachineType machine; 166 ushort sectionCount; 167 uint timestamp; 168 uint symbolTablePtr_deprecated; 169 uint numberOfSymbolTable_deprecated; 170 ushort sizeOfOptionalHeader; 171 CoffCharacteristics characteristics; 172 } 173 static assert(CoffHeader.sizeof == 24); 174 175 struct CoffOptionalHeader 176 { 177 CoffStandardFields coffFields; 178 CoffPeFields peFields; 179 CoffPeDataDirectories peDataDirectories; 180 } 181 182 struct CoffStandardFields 183 { 184 CoffOptionalHeaderMagic magic; 185 ubyte linkerMajor; 186 ubyte linkerMinor; 187 uint sizeOfCode; // Sum of all sections 188 uint sizeOfInitialisedData; // .data 189 uint sizeOfUninitialisedData; // .bss 190 uint entryPointRvaPtr; 191 uint baseOfCodeRvaPtr; 192 uint baseOfDataRvaPtr; // PE32 ONLY! 193 } 194 static assert(CoffStandardFields.sizeof == 28); 195 196 struct CoffPeFields 197 { 198 ulong imageBase; 199 uint sectionAlignment; 200 uint fileAlignment; 201 ushort osMajor; 202 ushort osMinor; 203 ushort imageMajor; 204 ushort imageMinor; 205 ushort subsystemMajor; 206 ushort subsytemMinor; 207 uint _; 208 uint sizeOfImage; 209 uint sizeOfHeaders; 210 uint checksum; 211 CoffPeSubsystem subsystem; 212 CoffPeDllCharacteristics dllCharacteristics; 213 ulong sizeOfStackReserve; 214 ulong sizeOfStackCommit; 215 ulong sizeOfHeapReserve; 216 ulong sizeOfHeapCommit; 217 uint loaderFlags; 218 uint numberOfRvaAndSizes; 219 } 220 static assert(CoffPeFields.sizeof == 88); 221 222 struct CoffPeDataDirectory 223 { 224 uint tableRvaPtr; 225 uint size; 226 } 227 228 struct CoffPeDataDirectories 229 { 230 CoffPeDataDirectory exportTable; 231 CoffPeDataDirectory importTable; 232 CoffPeDataDirectory resourceTable; 233 CoffPeDataDirectory exceptionTable; 234 CoffPeDataDirectory certificateTable; 235 CoffPeDataDirectory baseRelocationTable; 236 CoffPeDataDirectory debug_; 237 CoffPeDataDirectory architecture; 238 CoffPeDataDirectory globalPtr; 239 CoffPeDataDirectory tlsTable; 240 CoffPeDataDirectory loadConfigTable; 241 CoffPeDataDirectory boundImport; 242 CoffPeDataDirectory importAddressTable; 243 CoffPeDataDirectory delayImportDescriptor; 244 CoffPeDataDirectory clrRuntimeHeader; 245 CoffPeDataDirectory reservedMustBeZeroWinkyFaceSmileBlush; 246 } 247 248 struct CoffSectionTable 249 { 250 char[8] name; 251 uint virtualSize; 252 uint virtualAddress; 253 uint sizeOfRawData; 254 uint pointerToRawData; 255 uint pointerToRelocations; 256 uint pointerToLineNumbers; 257 ushort numberOfRelocations; 258 ushort numberOfLineNumbers; 259 CoffSectionTableCharacteristics characteristics; 260 } 261 262 SimpleResult!CoffPe coffpeParseHeader(const ubyte[] data) 263 { 264 import libd.io.memory; 265 266 CoffPe pe; 267 auto stream = MemoryReaderStream(data); 268 269 auto result = stream.read((&pe.dosHeader)[0..1]); 270 if(!result.isValid) return typeof(return)(result.error); 271 if(result.value != DosHeader.sizeof) return typeof(return)(raise("Unexpected EOF when reading DOS header")); 272 if(pe.dosHeader.magic != DOS_HEADER_MAGIC) return typeof(return)(raise("Invalid DOS magic number")); 273 274 stream.setPosition(pe.dosHeader.newExeHeaderPtr).assumeValid; 275 result = stream.read((&pe.coffHeader)[0..1]); 276 if(!result.isValid) return typeof(return)(result.error); 277 if(result.value != CoffHeader.sizeof) return typeof(return)(raise("Unexpected EOF when reading COFF header")); 278 if(pe.coffHeader.magic != COFF_HEADER_MAGIC) return typeof(return)(raise("Invalid COFF magic number")); 279 280 if(pe.coffHeader.sizeOfOptionalHeader) 281 { 282 ubyte[CoffStandardFields.sizeof] standardFieldsBuffer; // this has a dynamic size depending on optional header type 283 scope asStandardFields = cast(CoffStandardFields*)standardFieldsBuffer.ptr; 284 stream.read(standardFieldsBuffer[0..2]); // read magic 285 286 auto size = (CoffStandardFields.sizeof - 2) - ( 287 asStandardFields.magic == CoffOptionalHeaderMagic.PE32 288 ? 0 289 : 4 290 ); 291 assert(asStandardFields.magic == CoffOptionalHeaderMagic.PE32Plus, "TODO"); 292 293 result = stream.read(standardFieldsBuffer[2..2+size]); 294 if(!result.isValid) return typeof(return)(result.error); 295 if(result.value != size) return typeof(return)(raise("Unexpected EOF when reading COFF standard fields")); 296 297 pe.optionalHeader.coffFields = *asStandardFields; 298 299 result = stream.read((&pe.optionalHeader.peFields)[0..1]); 300 if(!result.isValid) return typeof(return)(result.error); 301 if(result.value != CoffPeFields.sizeof) return typeof(return)(raise("Unexpected EOF when reading COFF PE fields")); 302 303 // again, this is dynamically sized 304 const directories = pe.optionalHeader.peFields.numberOfRvaAndSizes; 305 const dirSize = directories * CoffPeDataDirectory.sizeof; 306 const maxDirSize = CoffPeDataDirectories.sizeof; 307 ubyte[maxDirSize] dirBuffer; 308 scope asDataDirectories = cast(CoffPeDataDirectories*)dirBuffer.ptr; 309 310 result = stream.read(dirBuffer[0..dirSize]); 311 if(!result.isValid) return typeof(return)(result.error); 312 if(result.value != dirSize) return typeof(return)(raise("Unexpected EOF when reading COFF PE data directories")); 313 314 pe.optionalHeader.peDataDirectories = *asDataDirectories; 315 } 316 317 pe.sectionTables = makeShared(Array!CoffSectionTable.init); 318 auto tables = pe.sectionTables.ptrUnsafe; 319 tables.length = pe.coffHeader.sectionCount; 320 321 foreach(i, ref CoffSectionTable table; tables.range) 322 { 323 result = stream.read((&table)[0..1]); 324 if(!result.isValid) return typeof(return)(result.error); 325 if(result.value != CoffSectionTable.sizeof) return typeof(return)(raise("Unexpected EOF when reading COFF section table")); 326 } 327 328 return typeof(return)(pe); 329 }