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 }