1 /**
2 Copyright: Copyright (c) 2017-2019 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module vox.be.pecoff;
7 
8 import std.bitmanip;
9 import std.stdio;
10 import std.conv;
11 import std.file;
12 import std.path;
13 import core.time;
14 import std.exception;
15 import std.string;
16 import std.format : formattedWrite;
17 
18 import vox.be.amd64asm;
19 import vox.utils;
20 
21 //version = print_info;
22 version = use_mmap;
23 //version = standalone;
24 
25 version (standalone)
26 void main()
27 {
28 	testExeCompilation();
29 	//testObjLinking();
30 	//testObjLibDump();
31 }
32 
33 void testObjLibDump()
34 {
35 	auto file = File("../asm/dump.txt", "w");
36 	//auto output = stdout.lockingTextWriter;
37 	auto output = file.lockingTextWriter;
38 
39 	//PecoffObj.fromFile("../asm/obj/simple.obj").print(output);
40 
41 	//PecoffObj.fromFile("../asm/obj/gcstub64.obj").print(output);
42 	//PecoffLib.fromFile("../asm/lib/phobos64.lib").objs[0].toFile("../asm/obj/zutil.obj");
43 	//PecoffObj.fromFile("../asm/obj/zutil.obj").print(output);
44 
45 	//PecoffLib.fromFile("../asm/lib/oldnames.lib").print(output);
46 
47 	auto time0 = currTime;
48 	auto lib = PecoffLib.fromFile("../asm/lib/phobos64.lib");
49 	//auto lib = PecoffLib.fromFile("../asm/lib/libcmt.lib");
50 	auto time1 = currTime;
51 	//printSectionStats(output, lib);
52 	//printSymbolStats(output, lib);
53 	auto time2 = currTime;
54 	lib.print(output);
55 	auto time3 = currTime;
56 	writefln("Load %ss, dump %ss", scaledNumberFmt(time1 - time0), scaledNumberFmt(time3 - time2));
57 	//PecoffObj.fromFile("../asm/obj/loadcfg.obj").print(output); // reallocation past the end of section
58 
59 	// 1 overflow in loadcfg.obj
60 	//auto lib = PecoffLib.fromFile("../asm/lib/libcmt.lib");
61 	//printSectionStats(output, lib);
62 	//lib.print(output);
63 	//PecoffLib.fromFile("../asm/lib/libcmt.lib").objs[0x17B].toFile("../asm/obj/loadcfg.obj");
64 
65 	//PecoffLib.fromFile("../asm/lib/helloc.lib").print(output);
66 	//PecoffObj.fromFile("../asm/obj/helloc.obj").print(output);
67 	//PecoffObj.fromFile("../asm/obj/extd.obj").print(output);
68 	//PecoffObj.fromFile("../asm/obj/extc.obj").print(output);
69 	//PecoffObj.fromFile("../asm/obj/test.obj").print(output);
70 	//PecoffObj.fromFile("../asm/obj/betterc.obj").print(output);
71 }
72 
73 enum DEFAULT_SECTION_ALIGNMENT = 4096;
74 enum DEFAULT_FILE_ALIGNMENT = 512;
75 
76 alias PC = ubyte*;
77 struct ArraySink {
78 	private ubyte[] mem;
79 	private PC pc;
80 	size_t length() { return pc - mem.ptr; }
81 
82 	void setBuffer(ubyte[] buf) { mem = buf; pc = mem.ptr; }
83 	void resetPC() { pc = mem.ptr; }
84 	ubyte[] data() { return mem[0..pc - mem.ptr]; }
85 
86 	void put(T)(T value) {
87 		*(cast(T*)pc) = value;
88 		pc += value.sizeof;
89 	}
90 	void put(ubyte[] value) {
91 		pc[0..value.length] = value;
92 		pc += value.length;
93 	}
94 	void pad(size_t bytes) {
95 		pc[0..bytes] = 0;
96 		pc += bytes;
97 	}
98 }
99 
100 enum : uint {
101 	STD_INPUT_HANDLE  = 0xFFFFFFF6,
102 	STD_OUTPUT_HANDLE = 0xFFFFFFF5,
103 	STD_ERROR_HANDLE  = 0xFFFFFFF4
104 }
105 
106 enum SectionType : ubyte
107 {
108 	text,
109 	idata,
110 	data,
111 }
112 
113 void testObjLinking()
114 {
115 	auto file = File("../asm/linking.txt", "w");
116 	auto output = file.lockingTextWriter;
117 
118 	auto obj1 = PecoffObj.fromFile("../asm/obj/helloc.obj");
119 	auto obj2 = PecoffObj.fromFile("../asm/obj/extc.obj");
120 	string outname = "../asm/out.exe";
121 	string entryPointName = "main";
122 
123 	//obj1.print(output);
124 	//obj2.print(output);
125 
126 	LinkingContext ctx;
127 
128 	ctx.setEntryPoint(entryPointName);
129 	ctx.put(obj1);
130 	ctx.put(obj2);
131 
132 	// undef syms
133 	// def syms
134 
135 	// push entry point as undef symbol
136 	// push inputs
137 
138 	// iteratively resolve by loading undefined symbols
139 
140 	// after all symbols were resolved
141 	// sort sections
142 }
143 
144 struct LinkingContext
145 {
146 	SymbolName entryPointName;
147 	SymbolNameTable symNameTable;
148 
149 	void setEntryPoint(string symName)
150 	{
151 		SymbolName name = symNameTable.put(symName);
152 		entryPointName = name;
153 	}
154 
155 	void put(PecoffObj obj)
156 	{
157 		foreach (item; CoffSymbolRange(obj.symbols))
158 		{
159 			if (item.sym.isAbsolute) continue;
160 
161 			string nameStr = item.sym.Name.get(obj.stringTable);
162 			SymbolName name = symNameTable.put(nameStr);
163 
164 			string indentation = "    ";
165 			writeln(nameStr);
166 
167 			if (item.sym.isFunction) writeln(indentation, "fun");
168 			else if (item.sym.isNonFunction) writeln(indentation, "non-fun");
169 
170 			switch(item.sym.StorageClass)
171 			{
172 				case CoffSymClass.STATIC: writeln(indentation, "STATIC"); break;
173 				case CoffSymClass.LABEL: writeln(indentation, "LABEL"); break;
174 				case CoffSymClass.EXTERNAL: writeln(indentation, "EXTERNAL"); break;
175 				case CoffSymClass.WEAK_EXTERNAL: writeln(indentation, "WEAK_EXTERNAL"); break;
176 				default: break;
177 			}
178 
179 			if (item.sym.isUndefined) writeln(indentation, "Undef");
180 			writefln("%sDAT [%(%02X %)]", indentation, cast(ubyte[])item.sym[0..1]);
181 			if (item.aux.length)
182 			{
183 				ubyte[] auxBytes = cast(ubyte[])item.aux;
184 				writefln("%sAUX [%(%02X %)]", indentation, auxBytes);
185 			}
186 		}
187 	}
188 }
189 
190 enum SymbolRefType : ubyte
191 {
192 	absolute,
193 	rel32,
194 	rel64
195 }
196 
197 struct SymbolReference
198 {
199 	SymbolIndex target;
200 	uint offset; // start of fixup for current symbol
201 	SymbolRefType type;
202 	long addend; // value added to relocation
203 }
204 
205 enum SymbolType : ubyte
206 {
207 	undefined,
208 	absolute,
209 	sharedLib
210 }
211 
212 struct Symbol
213 {
214 	SymbolType type;
215 	ulong address;
216 	SymbolReference[] references;
217 }
218 
219 struct SymbolName
220 {
221 	uint stringIndex;
222 }
223 
224 struct SymbolNameTable
225 {
226 	Buffer!string stringTable;
227 	SymbolName[string] nameToIndex;
228 
229 	SymbolName put(string strName)
230 	{
231 		if (auto name = strName in nameToIndex)
232 		{
233 			return *name;
234 		}
235 		else
236 		{
237 			auto name = SymbolName(cast(typeof(SymbolName.stringIndex))stringTable.data.length);
238 			stringTable.put(strName);
239 			nameToIndex[strName] = name;
240 			return name;
241 		}
242 	}
243 }
244 
245 struct SymbolTable
246 {
247 	Buffer!Symbol symbols;
248 	//Buffer!SymbolIndex definedSymbols;
249 	//Buffer!SymbolIndex undefinedSymbols;
250 	//Buffer!SymbolIndex sharedSymbols;
251 	//Buffer!SymbolIndex reachableSymbols;
252 
253 	SymbolIndex put(Symbol sym)
254 	{
255 		size_t index = symbols.data.length;
256 		symbols.put(sym);
257 		return cast(SymbolIndex)index;
258 	}
259 }
260 
261 void testExeCompilation()
262 {
263 	auto time0 = currTime;
264 
265 	ubyte[] buf = allocate(0x1000 * 20, null, MemType.RW);
266 	scope(exit) deallocate(buf);
267 	ubyte[] binaryMem = buf[0..2*0x1000];// = allocator.allocate(4096 * 8, MemType.RW);
268 	ubyte[] importMem = buf[2*0x1000..4*0x1000];
269 	ubyte[] dataMem = buf[4*0x1000..6*0x1000];
270 
271 	Arena!ubyte codeMem;
272 	codeMem.setBuffer(buf[6*0x1000..$]);
273 
274 	// ---------------------------------------------------------
275 
276 	// Code section
277 	Section textSection = Section(SectionType.text, ".text");
278 	textSection.header.Characteristics =
279 		SectionFlags.SCN_CNT_CODE |
280 		SectionFlags.SCN_MEM_EXECUTE |
281 		SectionFlags.SCN_MEM_READ;
282 
283 	// Import table section
284 	Section idataSection = Section(SectionType.idata, ".idata");
285 	idataSection.header.Characteristics =
286 		SectionFlags.SCN_CNT_INITIALIZED_DATA |
287 		SectionFlags.SCN_MEM_WRITE |
288 		SectionFlags.SCN_MEM_READ;
289 
290 	// Static data section
291 	Section dataSection = Section(SectionType.data, ".data");
292 	dataSection.header.Characteristics =
293 		SectionFlags.SCN_CNT_INITIALIZED_DATA |
294 		SectionFlags.SCN_MEM_READ;
295 
296 	// ---------------------------------------------------------
297 
298 	// Exe gen
299 	Section*[3] sections;
300 	sections[SectionType.text] = &textSection;
301 	sections[SectionType.idata] = &idataSection;
302 	sections[SectionType.data] = &dataSection;
303 
304 	// ---------------------------------------------------------
305 
306 	Arena!ubyte sink;
307 	sink.setBuffer(binaryMem);
308 
309 	DataSection dataSectionSymbols;
310 	dataSectionSymbols.sink.setBuffer(dataMem);
311 	dataSectionSymbols.section = &dataSection;
312 
313 	ImportSection importSection;
314 	importSection.section = &idataSection;
315 
316 
317 	// ---------------------------------------------------------
318 
319 	ReferenceTable refTable;
320 
321 	// Code gen
322 	CodeGen_x86_64 codeGen;
323 	codeGen.encoder.setBuffer(&codeMem);
324 
325 	void putFixup(SymbolRef symRef)
326 	{
327 		auto fixup = codeGen.getAddressFixup;
328 		refTable.put(RelativeReference(SectionType.text, fixup.fixupOffset, fixup.extraOffset, symRef));
329 	}
330 
331 	// main
332 	SymbolRef msg_str = dataSectionSymbols.putString("Hello world!");
333 	SymbolRef ref_GetStdHandle = importSection.importLibFunction("kernel32", "GetStdHandle");
334 	SymbolRef ref_WriteConsoleA = importSection.importLibFunction("kernel32", "WriteConsoleA");
335 
336 	codeGen.subq(Register.SP, Imm32(0x30));
337 	codeGen.movd(Register.CX, Imm32(STD_OUTPUT_HANDLE));
338 	codeGen.call(memAddrRipDisp32(0)); // RAX = GetStdHandle(STD_OUTPUT_HANDLE)
339 	putFixup(ref_GetStdHandle);
340 
341 	codeGen.movq(Register.CX, Register.AX); // 1 in hConsoleOutput
342 	codeGen.leaq(Register.DX, memAddrRipDisp32(0)); // 2 in lpBuffer
343 	putFixup(msg_str);
344 
345 	codeGen.movq(Register.R8, Imm32(dataSection.symBytes(msg_str.symbolIndex))); // 3 in nNumberOfCharsToWrite
346 	codeGen.leaq(Register.R9, memAddrBaseDisp8(Register.SP, 0x28)); // 4 out lpNumberOfCharsWritten
347 	codeGen.movq(memAddrBaseDisp8(Register.SP, 0x20), Imm32(0)); // 5 in lpReserved
348 
349 	codeGen.call(memAddrRipDisp32(0)); // call WriteConsoleA(RAX, "Hello world!", "Hello world!".length, SP+0x28, 0)
350 	putFixup(ref_WriteConsoleA);
351 
352 	codeGen.addq(Register.SP, Imm32(0x30));
353 	codeGen.xorq(Register.AX, Register.AX);
354 	codeGen.ret();
355 
356 	// ---------------------------------------------------------
357 
358 	auto importMapping = ImportSectionMapping(importMem, importSection.getLibs);
359 
360 	textSection.data = codeGen.encoder.code;
361 	idataSection.data = importMapping.sectionData;
362 	dataSection.data = dataSectionSymbols.sink.data;
363 
364 	// ---------------------------------------------------------
365 
366 	Executable executable;
367 	executable.params = FileParameters();
368 	executable.sections = sections[];
369 	executable.fixup();
370 	importMapping.createImports(sections[SectionType.idata]);
371 	refTable.fixupReferences(sections[]);
372 	executable.write(sink);
373 
374 	// write exe
375 	auto time1 = currTime;
376 	string outputFilename = "out.exe";
377 
378 	std.file.write(outputFilename, sink.data);
379 
380 	// test exe
381 	import std.process;
382 	import std.path;
383 	auto time2 = currTime;
384 	auto result = execute("out.exe");
385 	auto time3 = currTime;
386 
387 	writefln("%s exited with %s, in %ss", outputFilename, result.status, scaledNumberFmt(time3 - time2));
388 	writefln("Compile in %ss, write %ss", scaledNumberFmt(time1 - time0), scaledNumberFmt(time2 - time1));
389 	writeln(absolutePath(outputFilename));
390 	//assert(result.status == 0);
391 }
392 
393 struct CoffSymbolRangeItem
394 {
395 	size_t tableIndex;
396 	SymbolTableEntry* sym;
397 	SymbolTableEntry[] aux;
398 }
399 
400 struct CoffSymbolRange
401 {
402 	SymbolTableEntry[] symbols;
403 
404 	int opApply(scope int delegate(CoffSymbolRangeItem) dg)
405 	{
406 		size_t tableIndex = 0;
407 		while (tableIndex < symbols.length)
408 		{
409 			auto item = CoffSymbolRangeItem(tableIndex, &symbols[tableIndex]);
410 			size_t numAux = symbols[tableIndex].NumberOfAuxSymbols;
411 			if (numAux) item.aux = symbols[tableIndex+1..tableIndex+1+numAux];
412 			if (auto res = dg(item)) return res;
413 			tableIndex += 1 + numAux;
414 		}
415 		return 0;
416 	}
417 }
418 
419 struct PecoffObj
420 {
421 	string filename;
422 	ubyte[] fileData;
423 
424 	bool isLoaded; /// Indicates if members below are usable. Call load if not.
425 	CoffFileHeader* header;
426 	SectionHeader[] sectionHeaders;
427 	Section[] sections;
428 	SymbolTableEntry[] symbols;
429 	string stringTable;
430 	size_t totalRelocations = 0;
431 
432 	static PecoffObj fromFileLazy(string filename)
433 	{
434 		enforce(exists(filename), format("%s does not exist", filename));
435 		enforce(extension(filename) == ".obj", format("%s must have .obj extension", filename));
436 
437 		ubyte[] fileData = cast(ubyte[])std.file.read(filename);
438 		return fromBytesLazy(fileData, filename);
439 	}
440 
441 	static PecoffObj fromFile(string filename)
442 	{
443 		PecoffObj obj = fromFileLazy(filename);
444 		obj.load();
445 		return obj;
446 	}
447 
448 	/// Returns obj without actual parsing. Call load before accessing
449 	static PecoffObj fromBytesLazy(ubyte[] fileData, string filename = null)
450 	{
451 		PecoffObj obj;
452 		obj.filename = filename;
453 		obj.fileData = fileData;
454 
455 		return obj;
456 	}
457 
458 	static PecoffObj fromBytes(ubyte[] fileData, string filename = null)
459 	{
460 		PecoffObj obj = fromBytesLazy(fileData, filename);
461 		obj.load();
462 		return obj;
463 	}
464 
465 	/// Performs actual processing of .obj data stored in fileData if not already loaded
466 	void load()
467 	{
468 		if (isLoaded) return;
469 
470 		auto slicer = FileDataSlicer(fileData);
471 
472 		header = slicer.getPtrTo!CoffFileHeader;
473 		sectionHeaders = slicer.getArrayOf!SectionHeader(header.NumberOfSections);
474 		sections = new Section[sectionHeaders.length];
475 
476 		slicer.fileCursor = header.PointerToSymbolTable;
477 		symbols = slicer.getArrayOf!SymbolTableEntry(header.NumberOfSymbols);
478 
479 		uint stringTableSize = *slicer.getPtrTo!uint();
480 		assert(stringTableSize >= 4);
481 		stringTable = cast(string)slicer.getArrayOf!char(stringTableSize - 4);
482 
483 		// Fill sections
484 		foreach(size_t i, ref section; sections)
485 		{
486 			section.sectionId = cast(uint)i;
487 			section.header = sectionHeaders[i];
488 			section.name = section.header.getName(stringTable);
489 
490 			size_t from = section.header.PointerToRawData;
491 			size_t to = from + section.header.SizeOfRawData;
492 			if (from < fileData.length && to <= fileData.length)
493 			{
494 				section.data = fileData[from..to];
495 			}
496 			else
497 			{
498 				section.data = null;
499 				writefln("ERROR %s Sect %X, SizeOfRawData %X from %X to %X len %X",
500 					filename, i+1, section.header.SizeOfRawData, from, to, fileData.length);
501 			}
502 
503 			slicer.fileCursor = section.header.PointerToRelocations;
504 			section.relocations = slicer.getArrayOf!CoffRelocation(section.header.NumberOfRelocations);
505 			totalRelocations += section.relocations.length;
506 		}
507 
508 		isLoaded = true;
509 	}
510 
511 	void toFile(string filename)
512 	{
513 		toBytes(File(filename, "w").lockingBinaryWriter);
514 	}
515 
516 	void toBytes(Sink)(auto ref Sink sink)
517 	{
518 		sink.put(fileData);
519 	}
520 
521 	void print(Sink)(auto ref Sink sink)
522 	{
523 		if (!isLoaded) load(); // lazy load
524 
525 		formattedWrite(sink, "%s:\n", filename);
526 		sink.put("\n");
527 
528 		header.print(sink);
529 		sink.put("\n");
530 
531 		printSectionTable(sink);
532 		sink.put("\n");
533 
534 		printRelocations(sink);
535 		sink.put("\n");
536 
537 		printSymbolTable(sink);
538 
539 		printStringTable(sink);
540 	}
541 
542 	void printSectionTable(Sink)(auto ref Sink sink)
543 	{
544 		if (!isLoaded) load(); // lazy load
545 
546 		sink.put("Section table:\n");
547 		size_t totalRelocations = 0;
548 		SectionHeader.printTableHeader(sink);
549 		foreach(sectionIndex, ref section; sections)
550 		{
551 			totalRelocations += section.relocations.length;
552 			section.header.print(sink, sectionIndex+1, stringTable);
553 			if (section.isLinkDirective) formattedWrite(sink, "      Link directive:%s\n", cast(string)section.data);
554 		}
555 	}
556 
557 	void printRelocations(Sink)(auto ref Sink sink)
558 	{
559 		if (!isLoaded) load(); // lazy load
560 
561 		formattedWrite(sink, "Relocations: %X records\n", totalRelocations);
562 
563 		if (totalRelocations)
564 		{
565 			formattedWrite(sink, "                                                       Symbol  Symbol\n");
566 			formattedWrite(sink, "Sect  Offset    Type                     Applied To     Index  Name  \n");
567 			formattedWrite(sink, "----  --------  ----------------  -----------------  --------  ------\n");
568 			foreach(sectionIndex, ref section; sections)
569 			{
570 				if (section.relocations.length)
571 				{
572 					foreach(ref reloc; section.relocations)
573 					{
574 						// Read bytes that changed by relocation and display in hex
575 						import std.range : repeat;
576 						size_t bytesToRead = divCeil(reloc.targetSize, 8);
577 						size_t from = reloc.VirtualAddress - section.header.VirtualAddress;
578 						size_t to = from + bytesToRead;
579 						ubyte[8] buf;
580 						size_t appliedTo;
581 
582 						//writefln("%s section.VA %X bytesToRead %s from %X to %X len %X",
583 						//	reloc.typeString, section.header.VirtualAddress, bytesToRead, from, to, section.data.length);
584 						if (from < section.data.length && to <= section.data.length)
585 						{
586 							buf[0..bytesToRead] = section.data[from..to];
587 							appliedTo = *cast(size_t*)buf.ptr;
588 						}
589 						else
590 						{
591 							formattedWrite(sink, "ERROR --------  ----------------  v v  Overflow v v  --------  ------\n");
592 							appliedTo = 0;
593 						}
594 
595 						formattedWrite(sink, "% 4X  %08X  % 16s  %s%0*X  % 8X  %s\n",
596 							sectionIndex+1,
597 							reloc.VirtualAddress,
598 							reloc.typeString,
599 							' '.repeat(17 - bytesToRead*2), bytesToRead*2, appliedTo,
600 							reloc.SymbolTableIndex,
601 							symbols[reloc.SymbolTableIndex].Name.get(stringTable));
602 					}
603 				}
604 			}
605 		}
606 	}
607 
608 	void printSymbolTable(Sink)(auto ref Sink sink)
609 	{
610 		if (!isLoaded) load(); // lazy load
611 
612 		sink.put("Symbol table:\n");
613 		sink.put("       #     Value   Section  Type      Class     Name    \n");
614 		sink.put("--------  --------  --------  --------  --------  --------\n");
615 		foreach(item; CoffSymbolRange(symbols))
616 		{
617 			// # Value
618 			formattedWrite(sink, "% 8X  % 8X  ",//"% 8X  % 8X  %s\n",
619 				item.tableIndex,
620 				item.sym.Value);
621 
622 			// Section
623 			switch(item.sym.SectionNumber) {
624 				case -2: formattedWrite(sink, "Debug     "); break;
625 				case -1: formattedWrite(sink, "Absolute  "); break;
626 				case  0: formattedWrite(sink, "Undef     "); break;
627 				default: formattedWrite(sink, "% 8X  ", item.sym.SectionNumber);
628 			}
629 
630 			// Type
631 			if (item.sym.isFunction) formattedWrite(sink, "function  ");
632 			else if (item.sym.isNonFunction) formattedWrite(sink, "not func  ");
633 			else formattedWrite(sink, "% 8X  ", item.sym.Type);
634 
635 			// Class
636 			formattedWrite(sink, "% 8s  ", item.sym.StorageClass);
637 
638 			// Name
639 			formattedWrite(sink, "%s\n", item.sym.Name.get(stringTable));
640 
641 			foreach(i, ref SymbolTableEntry auxSym; item.aux)
642 			{
643 				// # Value
644 				formattedWrite(sink, "     AUX  % 8X  ",//"% 8X  % 8X  %s\n",
645 					item.tableIndex + 1 + i,
646 					item.sym.Value);
647 
648 				// Section
649 				switch(item.sym.SectionNumber) {
650 					case -2: formattedWrite(sink, "Debug     "); break;
651 					case -1: formattedWrite(sink, "Absolute  "); break;
652 					case  0: formattedWrite(sink, "Undef     "); break;
653 					default: formattedWrite(sink, "% 8X  ", item.sym.SectionNumber);
654 				}
655 
656 				// Type
657 				if (item.sym.isFunction) formattedWrite(sink, "function  ");
658 				else if (item.sym.isNonFunction) formattedWrite(sink, "          ");
659 				else formattedWrite(sink, "% 8X  ", item.sym.Type);
660 
661 				// Class
662 				formattedWrite(sink, "% 8s  \n", item.sym.StorageClass);
663 			}
664 		}
665 	}
666 
667 	void printStringTable(Sink)(auto ref Sink sink)
668 	{
669 		if (!isLoaded) load(); // lazy load
670 
671 		formattedWrite(sink, "\nString table: %s bytes at %08X\n", stringTable.length+4, header.PointerToSymbolTable);
672 		.printStringTable(stringTable, sink, 4);
673 	}
674 }
675 
676 void printStringTable(Sink)(string stringTable, auto ref Sink sink, size_t tableAddrOffset = 0)
677 {
678 	if (stringTable.length != 0)
679 	{
680 		import std.algorithm : splitter, joiner;
681 		size_t i = 0;
682 		sink.put("       #    Offset  String  \n");
683 		sink.put("--------  --------  --------\n");
684 		foreach(string sym; stringTable[0..$-1].splitter('\0'))
685 		{
686 			formattedWrite(sink, "% 8X  % 8X  ", i++, sym.ptr - stringTable.ptr + tableAddrOffset);
687 			sink.put(sym);
688 			sink.put('\n');
689 		}
690 	}
691 }
692 
693 immutable ARCHIVE_FILE_SIGNATURE = "!<arch>\n";
694 
695 struct PecoffLib
696 {
697 	version(use_mmap)
698 	{
699 		import std.mmfile;
700 		MmFile file;
701 	}
702 
703 	string filename;
704 	ubyte[] fileData;
705 	uint[] memberOffsets;
706 	ushort[] indices;
707 	string stringTable;
708 	PecoffObj[] objs;
709 	string longNames;
710 
711 	static PecoffLib fromFile(string filename)
712 	{
713 		enforce(exists(filename), format("%s does not exist", filename));
714 		enforce(extension(filename) == ".lib", format("%s must have .lib extension", filename));
715 
716 		version(use_mmap)
717 		{
718 			MmFile file = new MmFile(filename);
719 			ubyte[] fileData = cast(ubyte[])file[];
720 		}
721 		else
722 		{
723 			ubyte[] fileData = cast(ubyte[])std.file.read(filename);
724 		}
725 
726 		auto lib = fromBytes(fileData, filename);
727 
728 		version(use_mmap) lib.file = file;
729 
730 		return lib;
731 	}
732 
733 	static PecoffLib fromBytes(ubyte[] fileData, string filename = null)
734 	{
735 		auto slicer = FileDataSlicer(fileData);
736 
737 		string signature = cast(string)slicer.getArrayOf!char(ARCHIVE_FILE_SIGNATURE.length);
738 		enforce(signature == ARCHIVE_FILE_SIGNATURE, format("%s has no !<arch> file signature", filename));
739 
740 		PecoffLib lib;
741 		lib.filename = filename;
742 		lib.fileData = fileData;
743 
744 		ParsedLibMemberHeader readMemberHeader()
745 		{
746 			LibMemberHeader* firstMemberText = slicer.getPtrTo!LibMemberHeader;
747 			ParsedLibMemberHeader firstMember = firstMemberText.parse;
748 			firstMemberText.validate();
749 			return firstMember;
750 		}
751 
752 		ParsedLibMemberHeader currentMember;
753 
754 		// 1st member
755 		{
756 			currentMember = readMemberHeader();
757 			enforce(currentMember.Name == "/", format("1st Linker Member (\"/\") expected, got \"%s\"", currentMember.Name));
758 			ubyte[] firstMemberData = slicer.getArrayOf!ubyte(currentMember.Size);
759 			slicer.advanceToAlignment(2);
760 			currentMember = readMemberHeader();
761 		}
762 
763 		// 2nd member
764 		if (currentMember.Name == "/")
765 		{
766 			// enforce(currentMember.Name == "/", format("2nd Linker Member (\"/\") expected, got \"%s\"", currentMember.Name));
767 			ubyte[] secondMemberData = slicer.getArrayOf!ubyte(currentMember.Size);
768 			auto secondSlicer = FileDataSlicer(secondMemberData);
769 			uint numOfMembers = *secondSlicer.getPtrTo!uint;
770 			lib.objs.reserve(numOfMembers);
771 			lib.memberOffsets = secondSlicer.getArrayOf!uint(numOfMembers);
772 			uint numOfSymbols = *secondSlicer.getPtrTo!uint;
773 			lib.indices = secondSlicer.getArrayOf!ushort(numOfSymbols);
774 			ubyte[] stringTableData = secondSlicer.fileData[secondSlicer.fileCursor..$];
775 			lib.stringTable = cast(string)stringTableData;
776 			slicer.advanceToAlignment(2);
777 			currentMember = readMemberHeader();
778 		}
779 
780 		// Long names (optional)
781 		if (currentMember.Name == "//")
782 		{
783 			ubyte[] longNamesData = slicer.getArrayOf!ubyte(currentMember.Size);
784 			lib.longNames = cast(string)longNamesData;
785 			slicer.advanceToAlignment(2);
786 			currentMember = readMemberHeader();
787 		}
788 
789 		while (true)
790 		{
791 			auto dataOffset = slicer.fileCursor;
792 			ubyte[] objData = slicer.getArrayOf!ubyte(currentMember.Size);
793 			string objName = nameFromSlashName(currentMember.Name, lib.longNames);
794 			//writefln("read obj %s, offset %X, length %X", objName, dataOffset, objData.length);
795 			lib.objs ~= PecoffObj.fromBytesLazy(objData, objName);
796 			slicer.advanceToAlignment(2);
797 
798 			if (slicer.fileCursor >= slicer.fileData.length) break;
799 			currentMember = readMemberHeader();
800 		}
801 
802 		return lib;
803 	}
804 
805 	/// Loads all objs
806 	PecoffObj[] getObjs() {
807 		foreach (ref obj; objs) obj.load;
808 		return objs;
809 	}
810 
811 	/// Ensures that obj is loaded
812 	ref PecoffObj getObj(size_t index) {
813 		objs[index].load();
814 		return objs[index];
815 	}
816 
817 	void print(Sink)(auto ref Sink sink)
818 	{
819 		formattedWrite(sink, "%s:\n", filename);
820 		formattedWrite(sink, "  memberOffsets: %s records [%(%X, %)]\n",
821 			memberOffsets.length, memberOffsets);
822 		formattedWrite(sink, "  indices: %s records  [%(%X, %)]\n",
823 			indices.length, indices);
824 		formattedWrite(sink, "\nFile table: %s files\n", objs.length);
825 		foreach(i, ref obj; objs) formattedWrite(sink, "% 8X  %s\n", i, obj.filename);
826 		sink.put("\nSymbol table:\n");
827 		stringTable.printStringTable(sink);
828 		sink.put("\n");
829 		foreach(i, ref obj; objs)
830 		{
831 			formattedWrite(sink, "\nOBJ# %X --------------------------------------------------\n", i);
832 			obj.print(sink);
833 		}
834 	}
835 }
836 
837 struct LibMemberHeader
838 {
839 	static immutable END_OF_HEADER_VALUE = "`\n";
840 
841 	align(1):
842 	char[16] Name;
843 	char[12] Date;
844 	char[6] UserId;
845 	char[6] GroupId;
846 	char[8] Mode;
847 	char[10] Size;
848 	char[2] EndOfHeader;
849 
850 	void validate() {
851 		enforce(EndOfHeader == END_OF_HEADER_VALUE, format("%(%02X%) != %(%02X%)",
852 			cast(ubyte[])EndOfHeader[], cast(ubyte[])END_OF_HEADER_VALUE));
853 	}
854 
855 	ParsedLibMemberHeader parse() const @safe
856 	{
857 		import std.datetime.systime : SysTime;
858 		ParsedLibMemberHeader res;
859 		res.Name = strip(Name[]);
860 		auto dateStripped = Date[].strip;
861 		if (dateStripped.length) res.Date = SysTime.fromUnixTime(to!uint(dateStripped));
862 		auto usedIdStripped = UserId[].strip;
863 		if (usedIdStripped.length) res.UserId = to!uint(usedIdStripped);
864 		auto groupIdStripped = UserId[].strip;
865 		if (groupIdStripped.length) res.GroupId = to!uint(groupIdStripped);
866 		auto modeStripped = Mode[].strip;
867 		if (modeStripped.length) res.Mode = to!uint(modeStripped, 8);
868 		auto sizeStripped = Size[].strip;
869 		res.Size = to!uint(sizeStripped);
870 		return res;
871 	}
872 }
873 static assert(LibMemberHeader.sizeof == 60);
874 
875 struct ParsedLibMemberHeader
876 {
877 	import std.datetime.systime : SysTime;
878 	align(1):
879 	const(char)[] Name;
880 	SysTime Date;
881 	uint UserId;
882 	uint GroupId;
883 	uint Mode;
884 	uint Size;
885 }
886 
887 struct SymbolStats
888 {
889 	static struct Key
890 	{
891 		CoffSymClass symClass;
892 	}
893 
894 	static struct SymbolStats
895 	{
896 		size_t numSymbols;
897 		void visit(ref SymbolTableEntry sym)
898 		{
899 			++numSymbols;
900 		}
901 	}
902 	SymbolStats[Key] stats;
903 	size_t totalSymbols;
904 
905 	void visit(ref PecoffLib lib)
906 	{
907 		foreach(ref obj; lib.getObjs()) visit(obj);
908 	}
909 
910 	void visit(ref PecoffObj obj)
911 	{
912 		foreach (item; CoffSymbolRange(obj.symbols))
913 		{
914 			Key key = Key(item.sym.StorageClass);
915 			if (auto stat = key in stats) {
916 				stat.visit(*item.sym);
917 			} else {
918 				SymbolStats stat;
919 				stat.visit(*item.sym);
920 				stats[key] = stat;
921 			}
922 			++totalSymbols;
923 		}
924 	}
925 
926 	void print(Sink)(auto ref Sink sink)
927 	{
928 		formattedWrite(sink, "Total symbols: %X\n", totalSymbols);
929 		sink.put("Class                   Count   \n");
930 		sink.put("----------------------  --------\n");
931 		foreach(key, stat; stats)
932 			formattedWrite(sink, "  % 16s(%02X)  % 8X\n", key.symClass, cast(ubyte)key.symClass, stat.numSymbols);
933 	}
934 }
935 void printSymbolStats(Sink)(auto ref Sink sink, ref PecoffLib lib) { SymbolStats stats; stats.visit(lib); stats.print(sink); }
936 void printSymbolStats(Sink)(auto ref Sink sink, ref PecoffObj obj) { SymbolStats stats; stats.visit(obj); stats.print(sink); }
937 
938 struct SectionStats
939 {
940 	static struct Key
941 	{
942 		string sectionName;
943 		uint characteristics;
944 	}
945 	static struct SectionStats
946 	{
947 		size_t sectionsTotalSize;
948 		size_t numOfSections;
949 		void visit(ref Section section)
950 		{
951 			sectionsTotalSize += section.data.length;
952 			++numOfSections;
953 		}
954 	}
955 	SectionStats[Key] stats;
956 	size_t totalSymbols;
957 
958 	void visit(ref PecoffLib lib)
959 	{
960 		foreach(ref obj; lib.getObjs()) visit(obj);
961 	}
962 
963 	void visit(ref PecoffObj obj)
964 	{
965 		foreach(ref section; obj.sections) visit(section);
966 		totalSymbols += obj.symbols.length;
967 	}
968 
969 	void visit(ref Section section)
970 	{
971 		Key key = Key(section.name, section.header.Characteristics);
972 		if (auto stat = key in stats)
973 		{
974 			stat.visit(section);
975 		}
976 		else
977 		{
978 			SectionStats stat;
979 			stat.visit(section);
980 			stats[key] = stat;
981 		}
982 	}
983 
984 	void print(Sink)(auto ref Sink sink)
985 	{
986 		formattedWrite(sink, "total symbols: %X\n", totalSymbols);
987 		sink.put("Code|Initialized|Uninitialized|Link info|Remove\n");
988 		sink.put("coMdat|Gprel|Ovfl|Discardable|cacHed|Paged|Shared\n");
989 		sink.put("------------  ---  -----  --------  --------  --------\n");
990 		sink.put("    Flags                              Total          \n");
991 		sink.put("CIULRMGODHPS  RWX  Align     Count      Size  Name    \n");
992 		sink.put("------------  ---  -----  --------  --------  --------\n");
993 		foreach(key, stat; stats)
994 		{
995 			printSectionCharacteristicsFlags(sink, key.characteristics);
996 			sink.put("  ");
997 			printSectionCharacteristicsAlign(sink, key.characteristics);
998 			formattedWrite(sink, "  % 8s  % 8X  %s\n",
999 				stat.numOfSections, stat.sectionsTotalSize, key.sectionName);
1000 		}
1001 	}
1002 }
1003 
1004 void printSectionStats(Sink)(auto ref Sink sink, ref PecoffLib lib) { SectionStats stats; stats.visit(lib); stats.print(sink); }
1005 void printSectionStats(Sink)(auto ref Sink sink, ref PecoffObj obj) { SectionStats stats; stats.visit(obj); stats.print(sink); }
1006 
1007 struct RelativeReference
1008 {
1009 	SectionType fromSection;
1010 	uint fromOffset;
1011 	uint extraOffset;
1012 	SymbolRef referencedSymbol;
1013 }
1014 
1015 struct ReferenceTable
1016 {
1017 	RelativeReference[] relativeRefs;
1018 
1019 	void put(RelativeReference reference)
1020 	{
1021 		relativeRefs ~= reference;
1022 	}
1023 
1024 	void fixupReferences(Section*[] sections)
1025 	{
1026 		foreach(reference; relativeRefs)
1027 		{
1028 			auto from = sections[reference.fromSection];
1029 			auto to = sections[reference.referencedSymbol.sectionId];
1030 
1031 			uint* fixup = cast(uint*)(from.data.ptr + reference.fromOffset);
1032 			// 1000 + 0010 + 0004 = 1014
1033 			uint fromOffset = from.header.VirtualAddress + reference.fromOffset + reference.extraOffset;
1034 			// 2000 + 0040 = 2040
1035 			uint symOffset = to.symOffset(reference.referencedSymbol.symbolIndex);
1036 			uint toOffset = to.header.VirtualAddress + symOffset;
1037 			uint value = toOffset - fromOffset;
1038 
1039 			version(print_info) writefln("VA %x sym off %x", to.header.VirtualAddress, symOffset);
1040 			version(print_info) writefln("Fix %s:%x -> %s:%x with %x", from.name, reference.fromOffset, to.name, symOffset, value);
1041 			version(print_info) writefln("    %x -> %x", fromOffset, toOffset);
1042 
1043 			*fixup = value;
1044 		}
1045 	}
1046 }
1047 
1048 struct DataSection
1049 {
1050 	ArraySink sink;
1051 	Section* section;
1052 
1053 	SymbolRef putString(string str)
1054 	{
1055 		SymbolIndex index = section.addSymbol(cast(uint)sink.length, cast(uint)str.length);
1056 		sink.put(cast(ubyte[])str);
1057 		sink.put!ubyte(0);
1058 		return SymbolRef(index, SectionType.data);
1059 	}
1060 }
1061 
1062 struct SymbolImport
1063 {
1064 	string name;
1065 	SymbolIndex symbolIndex;
1066 }
1067 
1068 struct ImportSection
1069 {
1070 	SymbolImport[][string] importedLibs;
1071 	Section* section;
1072 
1073 	SymbolRef importLibFunction(string library, string functionName)
1074 	{
1075 		SymbolIndex funcIndex = section.addSymbol(0, 0);
1076 		SymbolImport[] functions = importedLibs.get(library, null);
1077 		functions ~= SymbolImport(functionName, funcIndex);
1078 		importedLibs[library] = functions;
1079 		return SymbolRef(funcIndex, SectionType.idata);
1080 	}
1081 
1082 	ImportedDlls getLibs() {
1083 		DllImports[] libs;
1084 		libs.length = importedLibs.length;
1085 		size_t i;
1086 		foreach(libName, functions; importedLibs)
1087 			libs[i++] = DllImports(libName, functions);
1088 		return ImportedDlls(libs);
1089 	}
1090 }
1091 
1092 // register function in library
1093 // insert external in code
1094 
1095 enum IAT_ILT_ENTRY_BYTES = 8;
1096 
1097 /// Calculates the size of import section from DllImports
1098 struct ImportedDlls
1099 {
1100 	this(DllImports[] dlls)
1101 	{
1102 		this.libs = dlls;
1103 		foreach(dll; libs)
1104 		{
1105 			totalDllNamesBytes = alignValue(totalDllNamesBytes + dll.libName.length + 1, 2);
1106 			foreach(func; dll.importedFunctions)
1107 			{
1108 				// Hint/Name length
1109 				totalDllHintsBytes = alignValue(totalDllHintsBytes + HintNameEntry.Hint.sizeof + func.name.length + 1, 2);
1110 			}
1111 			totalImportEntries += dll.numTableEntries; // include null entry
1112 		}
1113 	}
1114 
1115 	DllImports[] libs;
1116 	size_t importDirectoryTableBytes() { return (libs.length + 1) * ImportDirectoryTableEntry.sizeof; }
1117 	size_t totalImportEntries; // num of entries for both IAT and ILT, with null entries
1118 	size_t totalTableBytes() { return totalImportEntries * IAT_ILT_ENTRY_BYTES; }
1119 	size_t totalDllNamesBytes; // with leading zero and 2 byte alignment padding
1120 	size_t totalDllHintsBytes; // with leading zero and 2 byte alignment padding
1121 	size_t totalSectionBytes() { return importDirectoryTableBytes + totalTableBytes * 2 + totalDllHintsBytes + totalDllNamesBytes; }
1122 }
1123 
1124 struct DllImports
1125 {
1126 	this(string lib, SymbolImport[] funcs) {
1127 		libName = lib;
1128 		importedFunctions = funcs;
1129 	}
1130 	string libName;
1131 	SymbolImport[] importedFunctions;
1132 	size_t numTableEntries() { return importedFunctions.length + 1; }
1133 	size_t totalTableBytes() { return numTableEntries * IAT_ILT_ENTRY_BYTES; }
1134 }
1135 
1136 /// A set of slices on top of single memory buffer
1137 struct ImportSectionMapping
1138 {
1139 	this(ubyte[] _sectionBuffer, ImportedDlls _importedLibs)
1140 	{
1141 		this.importedLibs = _importedLibs;
1142 		this.sectionData = _sectionBuffer[0..importedLibs.totalSectionBytes];
1143 		assert(sectionData.length == importedLibs.totalSectionBytes);
1144 
1145 		size_t dirsEnd = importedLibs.importDirectoryTableBytes;
1146 		directories = cast(ImportDirectoryTableEntry[])(sectionData[0..dirsEnd]);
1147 
1148 		ilt_rva = cast(uint)(dirsEnd);
1149 		size_t ILT_end = cast(uint)(ilt_rva + importedLibs.totalTableBytes);
1150 		ILTs = cast(ImportLookupEntry[])(sectionData[ilt_rva..ILT_end]);
1151 
1152 		iat_rva = cast(uint)(ILT_end);
1153 		size_t IAT_end = cast(uint)(iat_rva + importedLibs.totalTableBytes);
1154 		IATs = cast(ImportLookupEntry[])(sectionData[iat_rva..IAT_end]);
1155 
1156 		str_rva = cast(uint)IAT_end;
1157 		stringData = sectionData[str_rva..$];
1158 	}
1159 
1160 	ubyte[] sectionData;
1161 
1162 	// initial info
1163 	ImportedDlls importedLibs;
1164 
1165 	// dir entries
1166 	ImportDirectoryTableEntry[] directories; // includes null entry
1167 	// import lookup tables (ILTs)
1168 	ImportLookupEntry[] ILTs; // includes null entry
1169 	uint ilt_rva;
1170 	// import address tables (IATs)
1171 	ImportLookupEntry[] IATs; // includes null entry
1172 	uint iat_rva;
1173 	// list of (lib name + hint/names)
1174 	ubyte[] stringData;
1175 	uint str_rva;
1176 }
1177 
1178 void createImports(ref ImportSectionMapping mapping, Section* importSection)
1179 {
1180 	uint sectionRVA = importSection.header.VirtualAddress;
1181 	// here, sink already has reserved space for Directory entries and IA, IL tables
1182 	// we will write strings and set address at the same time. Relative to section start
1183 	size_t tableIndex;
1184 	immutable uint str_rva = sectionRVA + mapping.str_rva;
1185 	immutable uint ilt_rva = sectionRVA + mapping.ilt_rva;
1186 	immutable uint iat_rva = sectionRVA + mapping.iat_rva;
1187 
1188 	Arena!ubyte strSink;
1189 	strSink.setBuffer(mapping.stringData);
1190 
1191 	foreach(i, ref DllImports dll; mapping.importedLibs.libs)
1192 	{
1193 		mapping.directories[i].importLookupTableRVA = cast(uint)(ilt_rva + tableIndex * IAT_ILT_ENTRY_BYTES);
1194 		mapping.directories[i].importAddressTableRVA = cast(uint)(iat_rva + tableIndex * IAT_ILT_ENTRY_BYTES);
1195 		mapping.directories[i].nameRVA = cast(uint)(str_rva + strSink.length);
1196 		//writefln("importLookupTableRVA %X", mapping.directories[i].importLookupTableRVA);
1197 		//writefln("importAddressTableRVA %X", mapping.directories[i].importAddressTableRVA);
1198 		//writefln("nameRVA %X", mapping.directories[i].nameRVA);
1199 
1200 		auto pre1 = strSink.length;
1201 		strSink.writeStringAligned2(dll.libName);
1202 		version(print_info) writefln("write '%s' len %s sink %s", dll.libName, strSink.length - pre1, strSink.length);
1203 
1204 		foreach(func; dll.importedFunctions)
1205 		{
1206 			uint hintRVA = cast(uint)(str_rva + strSink.length);
1207 			auto hint = ImportLookupEntry.fromHintNameTableRVA(hintRVA);
1208 
1209 			mapping.ILTs[tableIndex] = hint;
1210 			mapping.IATs[tableIndex] = hint;
1211 
1212 			uint sym_rva = cast(uint)(mapping.iat_rva + tableIndex * IAT_ILT_ENTRY_BYTES);
1213 			importSection.symbols[func.symbolIndex] = SymbolSectionInfo(sym_rva, IAT_ILT_ENTRY_BYTES);
1214 
1215 			auto pre2 = strSink.length;
1216 			HintNameEntry(0, func.name).write(strSink);
1217 			version(print_info) writefln("write '%s' len %s RVA %x", func.name, strSink.length - pre2, sym_rva);
1218 
1219 			++tableIndex;
1220 		}
1221 
1222 		// account for null entry
1223 		++tableIndex;
1224 	}
1225 
1226 	assert(strSink.length == mapping.stringData.length);
1227 }
1228 
1229 struct FileParameters
1230 {
1231 	uint sectionAlignment = DEFAULT_SECTION_ALIGNMENT;
1232 	uint fileAlignment = DEFAULT_FILE_ALIGNMENT;
1233 }
1234 
1235 struct Executable
1236 {
1237 	FileParameters params;
1238 
1239 	DosHeader dosHeader;
1240 	DosStub dosStub;
1241 	PeSignature peSignature;
1242 	CoffFileHeader coffFileHeader;
1243 	OptionalHeader optionalHeader;
1244 	Section*[] sections;
1245 
1246 	uint unalignedHeadersSize;
1247 
1248 	void fixup()
1249 	{
1250 		calculateFileSizes();
1251 		fixupInMemorySizes();
1252 		collectCodeInfo();
1253 		fixupInvariants();
1254 	}
1255 
1256 	void calculateFileSizes()
1257 	{
1258 		uint fileSize = 0;
1259 		fileSize += DosHeader.sizeof;
1260 		fileSize += DosStub.sizeof;
1261 		fileSize += PeSignature.sizeof;
1262 		fileSize += CoffFileHeader.sizeof;
1263 		fileSize += OptionalHeader.sizeof;
1264 		fileSize += SectionHeader.sizeof * sections.length;
1265 
1266 		unalignedHeadersSize = fileSize;
1267 		optionalHeader.SizeOfHeaders = alignValue(unalignedHeadersSize, params.fileAlignment);
1268 		uint imageFileSize = optionalHeader.SizeOfHeaders;
1269 
1270 		foreach (Section* section; sections)
1271 		{
1272 			section.header.PointerToRawData = imageFileSize;
1273 			uint sectionFileSize = alignValue(section.initializedSize, params.fileAlignment);
1274 			section.header.SizeOfRawData = sectionFileSize;
1275 			imageFileSize += sectionFileSize;
1276 		}
1277 		version(print_info) writefln("Image file size is %s bytes", imageFileSize);
1278 	}
1279 
1280 	void fixupInMemorySizes()
1281 	{
1282 		uint headersInMemorySize = alignValue(unalignedHeadersSize, params.sectionAlignment);
1283 		uint imageVirtualSize = headersInMemorySize;
1284 
1285 		foreach (section; sections)
1286 		{
1287 			section.header.VirtualAddress = imageVirtualSize;
1288 			uint sectionVirtualSize = alignValue(section.totalSize, params.sectionAlignment);
1289 			section.header.VirtualSize = section.totalSize;
1290 			imageVirtualSize += sectionVirtualSize;
1291 		}
1292 
1293 		optionalHeader.SizeOfImage = imageVirtualSize;
1294 	}
1295 
1296 	void collectCodeInfo()
1297 	{
1298 		bool codeSectionDetected = false;
1299 		bool importSectionDetected = false;
1300 
1301 		foreach (section; sections)
1302 		{
1303 			if (section.isCodeSection)
1304 			{
1305 				if (!codeSectionDetected)
1306 				{
1307 					codeSectionDetected = true;
1308 					optionalHeader.BaseOfCode = section.header.VirtualAddress;
1309 					optionalHeader.AddressOfEntryPoint = section.header.VirtualAddress;
1310 					version(print_info) writefln("First code section. BaseOfCode is %s", optionalHeader.BaseOfCode);
1311 				}
1312 				optionalHeader.SizeOfCode += section.header.SizeOfRawData;
1313 				version(print_info) writefln("Code section %s", section.header);
1314 			}
1315 			else if (section.isImportSection)
1316 			{
1317 				if (!importSectionDetected)
1318 				{
1319 					importSectionDetected = true;
1320 					optionalHeader.ImportTable.VirtualAddress = section.header.VirtualAddress;
1321 				}
1322 			}
1323 
1324 			optionalHeader.SizeOfInitializedData += section.header.SizeOfRawData;
1325 		}
1326 		version(print_info) writefln("Total code size is %sB", optionalHeader.SizeOfCode);
1327 	}
1328 
1329 	void fixupInvariants()
1330 	{
1331 		// COFF Header
1332 		coffFileHeader.Machine = MachineType.amd64;
1333 		coffFileHeader.NumberOfSections = cast(ushort)sections.length;
1334 		coffFileHeader.TimeDateStamp = 0;
1335 		coffFileHeader.PointerToSymbolTable = 0;
1336 		coffFileHeader.NumberOfSymbols = 0;
1337 		coffFileHeader.Characteristics =
1338 			CoffFlags.EXECUTABLE_IMAGE |
1339 			CoffFlags.LARGE_ADDRESS_AWARE;
1340 
1341 		// Optional Header (Image Only)
1342 		optionalHeader.MajorLinkerVersion = 1;
1343 		optionalHeader.SizeOfUninitializedData = 0; // FIXUP
1344 		optionalHeader.SectionAlignment = params.sectionAlignment;
1345 		optionalHeader.FileAlignment = params.fileAlignment;
1346 		optionalHeader.MajorOperatingSystemVersion = 6;
1347 		optionalHeader.MinorOperatingSystemVersion = 0;
1348 		optionalHeader.MajorImageVersion = 0;
1349 		optionalHeader.MinorImageVersion = 0;
1350 		optionalHeader.MajorSubsystemVersion = 6;
1351 		optionalHeader.MinorSubsystemVersion = 0;
1352 		optionalHeader.CheckSum = 0;
1353 		optionalHeader.Subsystem = 3; // CUI
1354 		optionalHeader.DllCharacteristics = 0;
1355 		optionalHeader.SizeOfStackReserve = 0x100000;
1356 		optionalHeader.SizeOfStackCommit = 0x1000;
1357 		optionalHeader.SizeOfHeapReserve = 0x100000;
1358 		optionalHeader.SizeOfHeapCommit = 0x1000;
1359 
1360 		// Section headers
1361 		foreach (section; sections)
1362 		{
1363 			section.header.PointerToRelocations = 0;
1364 			section.header.NumberOfRelocations = 0;
1365 		}
1366 	}
1367 
1368 	void write(ref Arena!ubyte sink)
1369 	{
1370 		// DOS Header
1371 		dosHeader.write(sink);
1372 		// DOS Stub
1373 		dosStub.write(sink);
1374 		// PE signature
1375 		peSignature.write(sink);
1376 		// COFF Header
1377 		coffFileHeader.write(sink);
1378 		// Optional Header (Image Only)
1379 		optionalHeader.write(sink);
1380 		// Section Headers
1381 		foreach (section; sections)
1382 		{
1383 			section.header.Name[0..section.name.length] = section.name;
1384 			section.header.Name[section.name.length..$] = '\0';
1385 			section.header.write(sink);
1386 		}
1387 		uint headersPadding = paddingSize(unalignedHeadersSize, params.fileAlignment);
1388 		sink.pad(headersPadding);
1389 
1390 		// Section Datas
1391 		foreach (section; sections)
1392 		{
1393 			version(print_info) writefln("%s RVA %x\t%s len\t%s bytes", section.name, sink.length, section.header.VirtualAddress, section.data.length);
1394 			sink.put(section.data);
1395 			size_t sectionPadding = section.header.SizeOfRawData - section.data.length;
1396 			sink.pad(sectionPadding);
1397 		}
1398 	}
1399 }
1400 
1401 struct DosHeader
1402 {
1403 	char[2] signature = ['M', 'Z'];
1404 	short lastsize = 0x90;
1405 	short nblocks = 0x03;
1406 	short nreloc = 0;
1407 	short hdrsize = 0x04;
1408 	short minalloc = 0;
1409 	short maxalloc = -1;
1410 	ushort ss = 0;
1411 	ushort sp = 0xB8;
1412 	short checksum = 0;
1413 	ushort ip = 0;
1414 	ushort cs = 0;
1415 	short relocpos;
1416 	short noverlay;
1417 	short[4] reserved1;
1418 	short oem_id;
1419 	short oem_info;
1420 	short[10] reserved2;
1421 	int   e_lfanew = 0xA8; // Offset to the 'PE\0\0' signature relative to the beginning of the file
1422 
1423 	static ubyte[64] hexData = [
1424 		0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
1425 		0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1426 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1427 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00
1428 	];
1429 
1430 	void write(ref Arena!ubyte sink) {
1431 		sink.put(hexData);
1432 	}
1433 }
1434 static assert(DosHeader.sizeof == 64);
1435 
1436 /// MS-DOS Stub (Image Only)
1437 ///
1438 /// The MS-DOS stub is a valid application that runs under MS-DOS. It is placed at the
1439 /// front of the EXE image. The linker places a default stub here, which prints out the
1440 /// message “This program cannot be run in DOS mode” when the image is run in
1441 /// MS-DOS. The user can specify a different stub by using the /STUB linker option.
1442 /// At location 0x3c, the stub has the file offset to the PE signature. This information
1443 /// enables Windows to properly execute the image file, even though it has an
1444 /// MS-DOS stub. This file offset is placed at location 0x3c during linking.
1445 struct DosStub
1446 {
1447 	ubyte[104] hexData = [
1448 		0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68,
1449 		0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F,
1450 		0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20,
1451 		0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1452 		0x39, 0xCF, 0x32, 0xDF, 0x7D, 0xAE, 0x5C, 0x8C, 0x7D, 0xAE, 0x5C, 0x8C, 0x7D, 0xAE, 0x5C, 0x8C,
1453 		0xEA, 0xF0, 0x58, 0x8D, 0x7C, 0xAE, 0x5C, 0x8C, 0xEA, 0xF0, 0x5E, 0x8D, 0x7C, 0xAE, 0x5C, 0x8C,
1454 		0x52, 0x69, 0x63, 0x68, 0x7D, 0xAE, 0x5C, 0x8C
1455 	];
1456 
1457 	void write(ref Arena!ubyte sink) {
1458 		sink.put(hexData);
1459 	}
1460 }
1461 
1462 struct PeSignature
1463 {
1464 	immutable char[4] signature = "PE\0\0";
1465 	void write(ref Arena!ubyte sink) {
1466 		sink.put(cast(ubyte[])signature);
1467 	}
1468 }
1469 static assert(PeSignature.sizeof == 4);
1470 
1471 /// The Machine field has one of the following values that specifies its CPU type. An
1472 /// image file can be run only on the specified machine or on a system that emulates
1473 /// the specified machine.
1474 enum MachineType : ushort {
1475 	amd64 = 0x8664, /// x64
1476 	i386 = 0x14C, /// Intel 386 or later processors and compatible processors
1477 	arm = 0x1C0, /// ARM little endian
1478 	arm64 = 0xAA64, /// ARM64 little endian
1479 }
1480 
1481 /// The Characteristics field contains flags that indicate attributes of the object or image
1482 /// file. The following flags are currently defined:
1483 enum CoffFlags : ushort
1484 {
1485 	/// Image only, Windows CE, and Microsoft Windows NT® and later.
1486 	/// This indicates that the file does not contain base relocations and must
1487 	/// therefore be loaded at its preferred base address. If the base address is
1488 	/// not available, the loader reports an error. The default behavior of the
1489 	/// linker is to strip base relocations from executable (EXE) files.
1490 	RELOCS_STRIPPED = 0x0001,
1491 
1492 	/// Image only. This indicates that the image file is valid and can be run. If
1493 	/// this flag is not set, it indicates a linker error.
1494 	EXECUTABLE_IMAGE = 0x0002,
1495 
1496 	//  COFF line numbers have been removed. This flag is deprecated and should be zero.
1497 	LINE_NUMS_STRIPPED = 0x0004,
1498 
1499 	//  COFF symbol table entries for local symbols have been removed. This
1500 	//  flag is deprecated and should be zero.
1501 	LOCAL_SYMS_STRIPPED = 0x0008,
1502 
1503 	//  Obsolete. Aggressively trim working set. This flag is deprecated for
1504 	//  Windows 2000 and later and must be zero.
1505 	AGGRESSIVE_WS_TRIM = 0x0010,
1506 
1507 	/// Application can handle > 2-GB addresses.
1508 	LARGE_ADDRESS_AWARE = 0x0020,
1509 
1510 	//  Little endian: the least significant bit (LSB) precedes the most significant
1511 	//  bit (MSB) in memory. This flag is deprecated and should be zero.
1512 	BYTES_REVERSED_LO = 0x0080,
1513 
1514 	/// Machine is based on a 32-bit-word architecture.
1515 	_32BIT_MACHINE = 0x0100,
1516 
1517 	/// Debugging information is removed from the image file.
1518 	DEBUG_STRIPPED = 0x0200,
1519 
1520 	/// If the image is on removable media, fully load it and copy it to the swap file.
1521 	REMOVABLE_RUN_FROM_SWAP = 0x0400,
1522 
1523 	/// If the image is on network media, fully load it and copy it to the swap file.
1524 	NET_RUN_FROM_SWAP = 0x0800,
1525 
1526 	/// The image file is a system file, not a user program.
1527 	SYSTEM = 0x1000,
1528 
1529 	/// The image file is a dynamic-link library (DLL). Such files are
1530 	/// considered executable files for almost all purposes, although they cannot be directly run.
1531 	DLL = 0x2000,
1532 
1533 	/// The file should be run only on a uniprocessor machine.
1534 	UP_SYSTEM_ONLY = 0x4000,
1535 
1536 	//  Big endian: the MSB precedes the LSB in memory. This flag is deprecated and should be zero
1537 	BYTES_REVERSED_HI = 0x8000,
1538 }
1539 
1540 /// COFF File Header (Object and Image)
1541 ///
1542 /// At the beginning of an object file, or immediately after the signature of an image file,
1543 /// is a standard COFF file header in the following format. Note that the Windows
1544 /// loader limits the number of sections to 96.
1545 struct CoffFileHeader
1546 {
1547 	/// The number that identifies the type of target machine.
1548 	MachineType Machine;
1549 
1550 	/// The number of sections. This indicates the size of
1551 	/// the section table, which immediately follows the headers.
1552 	ushort NumberOfSections;
1553 
1554 	/// The low 32 bits of the number of seconds since
1555 	/// 00:00 January 1, 1970 (a C run-time time_t
1556 	/// value), that indicates when the file was created.
1557 	uint TimeDateStamp;
1558 
1559 	/// The file offset of the COFF symbol table, or zero
1560 	/// if no COFF symbol table is present. This value
1561 	/// should be zero for an image because COFF
1562 	/// debugging information is deprecated.
1563 	uint PointerToSymbolTable;
1564 
1565 	/// The number of entries in the symbol table. This
1566 	/// data can be used to locate the string table, which
1567 	/// immediately follows the symbol table. This value
1568 	/// should be zero for an image because COFF
1569 	/// debugging information is deprecated.
1570 	uint NumberOfSymbols;
1571 
1572 	/// The size of the optional header, which is required
1573 	/// for executable files but not for object files. This
1574 	/// value should be zero for an object file. For a
1575 	/// description of the header format, see section 3.4,
1576 	/// “Optional Header (Image Only).
1577 	ushort SizeOfOptionalHeader = 240;
1578 
1579 	/// The flags that indicate the attributes of the file.
1580 	/// See CoffFlags.
1581 	ushort Characteristics;
1582 
1583 	size_t write(ref Arena!ubyte sink) {
1584 		auto offset = sink.length;
1585 		sink.put(Machine);
1586 		sink.put(NumberOfSections);
1587 		sink.put(TimeDateStamp);
1588 		sink.put(PointerToSymbolTable);
1589 		sink.put(NumberOfSymbols);
1590 		sink.put(SizeOfOptionalHeader);
1591 		sink.put(Characteristics);
1592 		return offset;
1593 	}
1594 
1595 	void print(Sink)(auto ref Sink sink)
1596 	{
1597 		import std.datetime.systime : SysTime;
1598 		formattedWrite(sink, "COFF header:\n");
1599 		formattedWrite(sink, "  Machine type: %s\n", Machine);
1600 		formattedWrite(sink, "  Number of sections: %X\n", NumberOfSections);
1601 		formattedWrite(sink, "  TimeDateStamp: %s\n", SysTime.fromUnixTime(TimeDateStamp));
1602 		formattedWrite(sink, "  Pointer to symbol table: 0x%08X\n", PointerToSymbolTable);
1603 		formattedWrite(sink, "  Number of symbols: %X\n", NumberOfSymbols);
1604 		formattedWrite(sink, "  Size of optional header: %X\n", SizeOfOptionalHeader);
1605 		formattedWrite(sink, "  Characteristics: 0x%04X\n", Characteristics);
1606 		if(Characteristics) with(CoffFlags)
1607 		{
1608 			auto c = Characteristics;
1609 			if(c & RELOCS_STRIPPED) formattedWrite(sink, "    Relocations stripped\n");
1610 			if(c & EXECUTABLE_IMAGE) formattedWrite(sink, "    Executable image\n");
1611 			if(c & LINE_NUMS_STRIPPED) formattedWrite(sink, "    Line numbers stripped\n");
1612 			if(c & LOCAL_SYMS_STRIPPED) formattedWrite(sink, "    Local symbols stripped\n");
1613 			if(c & AGGRESSIVE_WS_TRIM) formattedWrite(sink, "    Aggressively trim working set\n");
1614 			if(c & LARGE_ADDRESS_AWARE) formattedWrite(sink, "    Large address aware\n");
1615 			if(c & BYTES_REVERSED_LO) formattedWrite(sink, "    Bytes reversed low\n");
1616 			if(c & _32BIT_MACHINE) formattedWrite(sink, "    32bit machine\n");
1617 			if(c & DEBUG_STRIPPED) formattedWrite(sink, "    Debug stripped\n");
1618 			if(c & REMOVABLE_RUN_FROM_SWAP) formattedWrite(sink, "    Removable run from swap\n");
1619 			if(c & NET_RUN_FROM_SWAP) formattedWrite(sink, "    Network run from swap\n");
1620 			if(c & SYSTEM) formattedWrite(sink, "    System\n");
1621 			if(c & DLL) formattedWrite(sink, "    DLL\n");
1622 			if(c & UP_SYSTEM_ONLY) formattedWrite(sink, "    Up system only\n");
1623 			if(c & BYTES_REVERSED_HI) formattedWrite(sink, "    Bytes reversed high\n");
1624 		}
1625 	}
1626 }
1627 static assert(CoffFileHeader.sizeof == 20);
1628 
1629 ///
1630 enum WindowsSubsystem : ushort {
1631 	UNKNOWN = 0,                   // An unknown subsystem
1632 	NATIVE = 1,                    // Device drivers and native Windows processes
1633 	WINDOWS_GUI = 2,               // The Windows graphical user interface (GUI) subsystem
1634 	WINDOWS_CUI = 3,               // The Windows character subsystem
1635 	OS2_CUI = 5,                   // The OS/2 character subsystem
1636 	POSIX_CUI = 7,                 // The Posix character subsystem
1637 	NATIVE_WINDOWS = 8,            // Native Win9x driver
1638 	WINDOWS_CE_GUI = 9,            // Windows CE
1639 	EFI_APPLICATION = 10,          // An Extensible Firmware Interface (EFI) application
1640 	EFI_BOOT_SERVICE_DRIVER = 11,  // An EFI driver with boot services
1641 	EFI_RUNTIME_DRIVER = 12,       // An EFI driver with runtime services
1642 	EFI_ROM = 13,                  // An EFI ROM image
1643 	XBOX = 14,                     // XBOX
1644 	WINDOWS_BOOT_APPLICATION = 16, // Windows boot application.
1645 }
1646 
1647 
1648 /// Optional Header (Image Only)
1649 ///
1650 /// Every image file has an optional header that provides information to the loader. This
1651 /// header is optional in the sense that some files (specifically, object files) do not have
1652 /// it. For image files, this header is required. An object file can have an optional
1653 /// header, but generally this header has no function in an object file except to increase
1654 /// its size.
1655 /// Note that the size of the optional header is not fixed. The SizeOfOptionalHeader
1656 /// field in the COFF header must be used to validate that a probe into the file for a
1657 /// particular data directory does not go beyond SizeOfOptionalHeader. For more
1658 /// information, see section 3.3, “COFF File Header (Object and Image).”
1659 /// The NumberOfRvaAndSizes field of the optional header should also be used to
1660 /// ensure that no probe for a particular data directory entry goes beyond the optional
1661 /// header. In addition, it is important to validate the optional header magic number for
1662 /// format compatibility.
1663 /// The optional header magic number determines whether an image is a PE32 or PE32+ executable.
1664 /// | Magic number + PE format
1665 /// | 0x10b        | PE32
1666 /// | 0x20b        | PE32+
1667 /// PE32+ images allow for a 64-bit address space while limiting the image size to
1668 /// 2 gigabytes. Other PE32+ modifications are addressed in their respective sections.
1669 /// The optional header itself has three major parts
1670 ///
1671 /// struct defines PE32+
1672 struct OptionalHeader
1673 {
1674 	/// The unsigned integer that identifies the
1675 	/// state of the image file. The most common
1676 	/// number is 0x10B, which identifies it as a
1677 	/// normal executable file. 0x107 identifies it as
1678 	/// a ROM image, and 0x20B identifies it as a
1679 	/// PE32+ executable.
1680 	ushort Magic = 0x20B;
1681 
1682 	/// The linker major version number.
1683 	ubyte MajorLinkerVersion;
1684 
1685 	/// The linker minor version number.
1686 	ubyte MinorLinkerVersion;
1687 
1688 	/// The size of the code (text) section, or the
1689 	/// sum of all code sections if there are multiple
1690 	/// sections.
1691 	uint SizeOfCode;
1692 
1693 	/// The size of the initialized data section, or
1694 	/// the sum of all such sections if there are
1695 	/// multiple data sections.
1696 	uint SizeOfInitializedData;
1697 
1698 	/// The size of the uninitialized data section
1699 	/// (BSS), or the sum of all such sections if
1700 	/// there are multiple BSS sections.
1701 	uint SizeOfUninitializedData;
1702 
1703 	/// The address of the entry point relative to the
1704 	/// image base when the executable file is
1705 	/// loaded into memory. For program images,
1706 	/// this is the starting address. For device
1707 	/// drivers, this is the address of the
1708 	/// initialization function. An entry point is
1709 	/// optional for DLLs. When no entry point is
1710 	/// present, this field must be zero.
1711 	uint AddressOfEntryPoint;
1712 
1713 	/// The address that is relative to the image
1714 	/// base of the beginning-of-code section when
1715 	/// it is loaded into memory.
1716 	uint BaseOfCode;
1717 
1718 	/// The preferred address of the first
1719 	/// byte of image when loaded into
1720 	/// memory; must be a multiple of 64 K.
1721 	/// The default for DLLs is 0x10000000.
1722 	/// The default for Windows CE EXEs is
1723 	/// 0x00010000. The default for
1724 	/// Windows NT, Windows 2000,
1725 	/// Windows XP, Windows 95,
1726 	/// Windows 98, and Windows Me is
1727 	/// 0x00400000.
1728 	ulong ImageBase = 0x00400000;
1729 
1730 	/// The alignment (in bytes) of sections
1731 	/// when they are loaded into memory. It
1732 	/// must be greater than or equal to
1733 	/// FileAlignment. The default is the
1734 	/// page size for the architecture.
1735 	uint SectionAlignment = DEFAULT_SECTION_ALIGNMENT;
1736 
1737 	/// The alignment factor (in bytes) that is
1738 	/// used to align the raw data of sections
1739 	/// in the image file. The value should be
1740 	/// a power of 2 between 512 and 64 K,
1741 	/// inclusive. The default is 512. If the
1742 	/// SectionAlignment is less than the
1743 	/// architecture’s page size, then
1744 	/// FileAlignment must match
1745 	/// SectionAlignment.
1746 	uint FileAlignment = DEFAULT_FILE_ALIGNMENT;
1747 
1748 	/// The major version number of the
1749 	/// required operating system.
1750 	ushort MajorOperatingSystemVersion;
1751 
1752 	/// The minor version number of the
1753 	/// required operating system.
1754 	ushort MinorOperatingSystemVersion;
1755 
1756 	/// The major version number of the
1757 	/// image.
1758 	ushort MajorImageVersion;
1759 
1760 	/// The minor version number of the
1761 	/// image.
1762 	ushort MinorImageVersion;
1763 
1764 	/// The major version number of the
1765 	/// subsystem.
1766 	ushort MajorSubsystemVersion;
1767 
1768 	/// The minor version number of the
1769 	/// subsystem.
1770 	ushort MinorSubsystemVersion;
1771 
1772 	/// Reserved, must be zero.
1773 	uint Win32VersionValue = 0;
1774 
1775 	/// The size (in bytes) of the image,
1776 	/// including all headers, as the image is
1777 	/// loaded in memory. It must be a
1778 	/// multiple of SectionAlignment.
1779 	uint SizeOfImage;
1780 
1781 	/// The combined size of an MS-DOS
1782 	/// stub, PE header, and section
1783 	/// headers rounded up to a multiple of
1784 	/// FileAlignment.
1785 	uint SizeOfHeaders;
1786 
1787 	/// The image file checksum. The
1788 	/// algorithm for computing the
1789 	/// checksum is incorporated into
1790 	/// IMAGHELP.DLL. The following are
1791 	/// checked for validation at load time:
1792 	/// all drivers, any DLL loaded at boot
1793 	/// time, and any DLL that is loaded into
1794 	/// a critical Windows process.
1795 	uint CheckSum;
1796 
1797 	/// The subsystem that is required to run
1798 	/// this image. For more information, see
1799 	/// “Windows Subsystem” later in this
1800 	/// specification.
1801 	ushort Subsystem;
1802 
1803 	/// For more information, see “DLL
1804 	/// Characteristics” later in this
1805 	/// specification.
1806 	ushort DllCharacteristics;
1807 
1808 	/// The size of the stack to reserve. Only
1809 	/// SizeOfStackCommit is committed;
1810 	/// the rest is made available one page
1811 	/// at a time until the reserve size is
1812 	/// reached.
1813 	ulong SizeOfStackReserve;
1814 
1815 	/// The size of the stack to commit.
1816 	ulong SizeOfStackCommit;
1817 
1818 	/// The size of the local heap space to
1819 	/// reserve. Only SizeOfHeapCommit is
1820 	/// committed; the rest is made available
1821 	/// one page at a time until the reserve
1822 	/// size is reached.
1823 	ulong SizeOfHeapReserve;
1824 
1825 	/// The size of the local heap space to
1826 	/// commit.
1827 	ulong SizeOfHeapCommit;
1828 
1829 	/// Reserved, must be zero.
1830 	uint LoaderFlags = 0;
1831 
1832 	/// The number of data-directory entries
1833 	/// in the remainder of the optional
1834 	/// header. Each describes a location
1835 	/// and size.
1836 	uint NumberOfRvaAndSizes = 16;
1837 
1838 	/// The export table address and size. For more
1839 	/// information see section 6.3, “The .edata Section
1840 	/// (Image Only).”
1841 	ImageDataDirectory ExportTable;
1842 
1843 	/// The import table address and size. For more
1844 	/// information, see section 6.4, “The .idata
1845 	/// Section.”
1846 	ImageDataDirectory ImportTable;
1847 
1848 	/// The resource table address and size. For more
1849 	/// information, see section 6.9, “The .rsrc Section.”
1850 	ImageDataDirectory ResourceTable;
1851 
1852 	/// The exception table address and size. For more
1853 	/// information, see section 6.5, “The .pdata
1854 	/// Section.”
1855 	ImageDataDirectory ExceptionTable;
1856 
1857 	/// The attribute certificate table address and size.
1858 	/// For more information, see section 5.7, “The
1859 	/// Attribute Certificate Table (Image Only).”
1860 	ImageDataDirectory CertificateTable;
1861 
1862 	/// The base relocation table address and size. For
1863 	/// more information, see section 6.6, "The .reloc
1864 	/// Section (Image Only)."
1865 	ImageDataDirectory BaseRelocationTable;
1866 
1867 	/// The debug data starting address and size. For
1868 	/// more information, see section 6.1, “The .debug
1869 	/// Section.”
1870 	ImageDataDirectory Debug;
1871 
1872 	/// Reserved, must be 0
1873 	ImageDataDirectory Architecture;
1874 
1875 	/// The RVA of the value to be stored in the global
1876 	/// pointer register. The size member of this
1877 	/// structure must be set to zero.
1878 	ImageDataDirectory GlobalPtr;
1879 
1880 	/// The thread local storage (TLS) table address
1881 	/// and size. For more information, see section 6.7,
1882 	/// “The .tls Section.”
1883 	ImageDataDirectory TLSTable;
1884 
1885 	/// The load configuration table address and size.
1886 	/// For more information, see section 6.8, “The Load
1887 	/// Configuration Structure (Image Only).”
1888 	ImageDataDirectory LoadConfigTable;
1889 
1890 	/// The bound import table address and size.
1891 	ImageDataDirectory BoundImport;
1892 
1893 	/// The import address table address and size. For
1894 	/// more information, see section 6.4.4, “Import
1895 	/// Address Table.”
1896 	ImageDataDirectory IAT;
1897 
1898 	/// The delay import descriptor address and size.
1899 	/// For more information, see section 5.8, “DelayLoad Import Tables (Image Only).”
1900 	ImageDataDirectory DelayImportDescriptor;
1901 
1902 	/// The CLR runtime header address and size. For
1903 	/// more information, see section 6.10, “The
1904 	/// .cormeta Section (Object Only).”
1905 	ImageDataDirectory CLRRuntimeHeader;
1906 
1907 	ulong _reserved; /// Reserved, must be zero
1908 
1909 	size_t write(ref Arena!ubyte sink) {
1910 		auto offset = sink.length;
1911 		sink.put(Magic);
1912 		sink.put(MajorLinkerVersion);
1913 		sink.put(MinorLinkerVersion);
1914 		sink.put(SizeOfCode);
1915 		sink.put(SizeOfInitializedData);
1916 		sink.put(SizeOfUninitializedData);
1917 		sink.put(AddressOfEntryPoint);
1918 		sink.put(BaseOfCode);
1919 		sink.put(ImageBase);
1920 		sink.put(SectionAlignment);
1921 		sink.put(FileAlignment);
1922 		sink.put(MajorOperatingSystemVersion);
1923 		sink.put(MinorOperatingSystemVersion);
1924 		sink.put(MajorImageVersion);
1925 		sink.put(MinorImageVersion);
1926 		sink.put(MajorSubsystemVersion);
1927 		sink.put(MinorSubsystemVersion);
1928 		sink.put(Win32VersionValue);
1929 		sink.put(SizeOfImage);
1930 		sink.put(SizeOfHeaders);
1931 		sink.put(CheckSum);
1932 		sink.put(Subsystem);
1933 		sink.put(DllCharacteristics);
1934 		sink.put(SizeOfStackReserve);
1935 		sink.put(SizeOfStackCommit);
1936 		sink.put(SizeOfHeapReserve);
1937 		sink.put(SizeOfHeapCommit);
1938 		sink.put(LoaderFlags);
1939 		sink.put(NumberOfRvaAndSizes);
1940 		sink.put(ExportTable);
1941 		sink.put(ImportTable);
1942 		sink.put(ResourceTable);
1943 		sink.put(ExceptionTable);
1944 		sink.put(CertificateTable);
1945 		sink.put(BaseRelocationTable);
1946 		sink.put(Debug);
1947 		sink.put(Architecture);
1948 		sink.put(GlobalPtr);
1949 		sink.put(TLSTable);
1950 		sink.put(LoadConfigTable);
1951 		sink.put(BoundImport);
1952 		sink.put(IAT);
1953 		sink.put(DelayImportDescriptor);
1954 		sink.put(CLRRuntimeHeader);
1955 		sink.put(_reserved);
1956 		return offset;
1957 	}
1958 
1959 	/// True if member is within SizeOfOptionalHeader bytes
1960 	bool isValidMember(string member)(ushort SizeOfOptionalHeader)
1961 	{
1962 		return (mixin(member).offsetof + mixin(member).sizeof) <= SizeOfOptionalHeader;
1963 	}
1964 }
1965 static assert(OptionalHeader.sizeof == 240);
1966 
1967 struct ImageDataDirectory
1968 {
1969 	uint VirtualAddress;
1970 	uint Size;
1971 }
1972 
1973 enum SectionFlags : uint
1974 {
1975 	/// The section contains executable code.
1976 	SCN_CNT_CODE = 0x00000020,
1977 	/// The section contains initialized data.
1978 	SCN_CNT_INITIALIZED_DATA = 0x00000040,
1979 	/// The section contains uninitialized data.
1980 	SCN_CNT_UNINITIALIZED_DATA = 0x00000080,
1981 	/// The section contains comments or
1982 	/// other information. The .drectve section has this type. This is valid
1983 	/// for object files only.
1984 	SCN_LNK_INFO = 0x00000200,
1985 	/// The section will not become part
1986 	/// of the image. This is valid only for object files.
1987 	SCN_LNK_REMOVE = 0x00000800,
1988 	/// The section contains COMDAT data. For more information, see
1989 	/// section 5.5.6, “COMDAT Sections (Object Only).” This is valid only for object files.
1990 	SCN_LNK_COMDAT = 0x00001000,
1991 	/// The section contains data referenced through the global pointer (GP).
1992 	SCN_GPREL = 0x00008000,
1993 	/// Align data on a    1-byte boundary. Valid only for object files.
1994 	SCN_ALIGN_1BYTES = 0x00100000,
1995 	/// Align data on a    2-byte boundary. Valid only for object files.
1996 	SCN_ALIGN_2BYTES = 0x00200000,
1997 	/// Align data on a    4-byte boundary. Valid only for object files.
1998 	SCN_ALIGN_4BYTES = 0x00300000,
1999 	/// Align data on a    8-byte boundary. Valid only for object files.
2000 	SCN_ALIGN_8BYTES = 0x00400000,
2001 	/// Align data on a   16-byte boundary. Valid only for object files.
2002 	SCN_ALIGN_16BYTES = 0x00500000,
2003 	/// Align data on a   32-byte boundary. Valid only for object files.
2004 	SCN_ALIGN_32BYTES = 0x00600000,
2005 	/// Align data on a   64-byte boundary. Valid only for object files.
2006 	SCN_ALIGN_64BYTES = 0x00700000,
2007 	/// Align data on a  128-byte boundary. Valid only for object files.
2008 	SCN_ALIGN_128BYTES = 0x00800000,
2009 	/// Align data on a  256-byte boundary. Valid only for object files.
2010 	SCN_ALIGN_256BYTES = 0x00900000,
2011 	/// Align data on a  512-byte boundary. Valid only for object files.
2012 	SCN_ALIGN_512BYTES = 0x00A00000,
2013 	/// Align data on a 1024-byte boundary. Valid only for object files.
2014 	SCN_ALIGN_1024BYTES = 0x00B00000,
2015 	/// Align data on a 2048-byte boundary. Valid only for object files.
2016 	SCN_ALIGN_2048BYTES = 0x00C00000,
2017 	/// Align data on a 4096-byte boundary. Valid only for object files.
2018 	SCN_ALIGN_4096BYTES = 0x00D00000,
2019 	/// Align data on a 8192-byte boundary. Valid only for object files.
2020 	SCN_ALIGN_8192BYTES = 0x00E00000,
2021 	/// The section contains extended relocations.
2022 	SCN_LNK_NRELOC_OVFL = 0x01000000,
2023 	/// The section can be discarded as needed.
2024 	SCN_MEM_DISCARDABLE = 0x02000000,
2025 	/// The section cannot be cached.
2026 	SCN_MEM_NOT_CACHED = 0x04000000,
2027 	/// The section is not pageable.
2028 	SCN_MEM_NOT_PAGED = 0x08000000,
2029 	/// The section can be shared in memory.
2030 	SCN_MEM_SHARED = 0x10000000,
2031 	/// The section can be executed as code.
2032 	SCN_MEM_EXECUTE = 0x20000000,
2033 	/// The section can be read.
2034 	SCN_MEM_READ = 0x40000000,
2035 	/// The section can be written to.
2036 	SCN_MEM_WRITE = 0x80000000,
2037 }
2038 
2039 struct SectionHeader
2040 {
2041 	/// An 8-byte, null-padded UTF-8 encoded string. If
2042 	/// the string is exactly 8 characters long, there is no
2043 	/// terminating null. For longer names, this field
2044 	/// contains a slash (/) that is followed by an ASCII
2045 	/// representation of a decimal number that is an
2046 	/// offset into the string table. Executable images do
2047 	/// not use a string table and do not support section
2048 	/// names longer than 8 characters. Long names in
2049 	/// object files are truncated if they are emitted to an
2050 	/// executable file.
2051 	char[8] Name;
2052 
2053 	/// The total size of the section when loaded into
2054 	/// memory. If this value is greater than
2055 	/// SizeOfRawData, the section is zero-padded. This
2056 	/// field is valid only for executable images and
2057 	/// should be set to zero for object files.
2058 	uint VirtualSize;
2059 
2060 	/// For executable images, the address of the first
2061 	/// byte of the section relative to the image base
2062 	/// when the section is loaded into memory. For
2063 	/// object files, this field is the address of the first
2064 	/// byte before relocation is applied; for simplicity,
2065 	/// compilers should set this to zero. Otherwise, it is
2066 	/// an arbitrary value that is subtracted from offsets
2067 	/// during relocation.
2068 	uint VirtualAddress;
2069 
2070 	/// The size of the section (for object files) or the
2071 	/// size of the initialized data on disk (for image
2072 	/// files). For executable images, this must be a
2073 	/// multiple of FileAlignment from the optional
2074 	/// header. If this is less than VirtualSize, the
2075 	/// remainder of the section is zero-filled. Because
2076 	/// the SizeOfRawData field is rounded but the
2077 	/// VirtualSize field is not, it is possible for
2078 	/// SizeOfRawData to be greater than VirtualSize as
2079 	/// well. When a section contains only uninitialized
2080 	/// data, this field should be zero.
2081 	uint SizeOfRawData;
2082 
2083 	/// The file pointer to the first page of the section
2084 	/// within the COFF file. For executable images, this
2085 	/// must be a multiple of FileAlignment from the
2086 	/// optional header. For object files, the value should
2087 	/// be aligned on a 4-byte boundary for best
2088 	/// performance. When a section contains only
2089 	/// uninitialized data, this field should be zero.
2090 	uint PointerToRawData;
2091 
2092 	/// The file pointer to the beginning of relocation
2093 	/// entries for the section. This is set to zero for
2094 	/// executable images or if there are no relocations.
2095 	uint PointerToRelocations;
2096 
2097 	/// The file pointer to the beginning of line-number
2098 	/// entries for the section. This is set to zero if there
2099 	/// are no COFF line numbers. This value should be
2100 	/// zero for an image because COFF debugging
2101 	/// information is deprecated.
2102 	uint PointerToLinenumbers = 0;
2103 
2104 	/// The number of relocation entries for the section.
2105 	/// This is set to zero for executable images.
2106 	ushort NumberOfRelocations;
2107 
2108 	/// The number of line-number entries for the
2109 	/// section. This value should be zero for an image
2110 	/// because COFF debugging information is
2111 	/// deprecated.
2112 	ushort NumberOfLinenumbers = 0;
2113 
2114 	/// The flags that describe the characteristics of the
2115 	/// section. See SectionFlags.
2116 	uint Characteristics;
2117 
2118 	string getName(string stringTable)
2119 	{
2120 		return nameFromSlashName(Name, stringTable);
2121 	}
2122 
2123 	size_t write(ref Arena!ubyte sink) {
2124 		auto offset = sink.length;
2125 		sink.put(cast(ubyte[])Name);
2126 		sink.put(VirtualSize);
2127 		sink.put(VirtualAddress);
2128 		sink.put(SizeOfRawData);
2129 		sink.put(PointerToRawData);
2130 		sink.put(PointerToRelocations);
2131 		sink.put(PointerToLinenumbers);
2132 		sink.put(NumberOfRelocations);
2133 		sink.put(NumberOfLinenumbers);
2134 		sink.put(Characteristics);
2135 		return offset;
2136 	}
2137 
2138 	static void printTableHeader(Sink)(auto ref Sink sink)
2139 	{
2140 		formattedWrite(sink, "Code|Initialized|Uninitialized|Link info|Remove|coMdat|Gprel|Ovfl|Discardable|cacHed|Paged|Shared\n");
2141 		formattedWrite(sink, "----  --------  --------  --------  --------  --------  --------  -----  ------------  ---  --------\n");
2142 		formattedWrite(sink, "      Virtual    Virtual      File      File    Relocs    Num of             Flags                  \n");
2143 		formattedWrite(sink, "   #  Address       Size    Offset      Size    Offset    Relocs  Align  CIULRMGODHPS  RWX  Name    \n");
2144 		formattedWrite(sink, "----  --------  --------  --------  --------  --------  --------  -----  ------------  ---  --------\n");
2145 	}
2146 
2147 	void print(Sink)(auto ref Sink sink, size_t index, string stringTable)
2148 	{
2149 		formattedWrite(sink, "% 4X  % 8X  % 8X  % 8X  % 8X  % 8X  % 8X",
2150 			index, VirtualAddress, VirtualSize, PointerToRawData, SizeOfRawData,
2151 			PointerToRelocations, NumberOfRelocations);
2152 		sink.put("  ");
2153 
2154 		// Align
2155 		printSectionCharacteristicsAlign(sink, Characteristics);
2156 		sink.put("  ");
2157 
2158 		// Flags
2159 		printSectionCharacteristicsFlags(sink, Characteristics);
2160 		sink.put("  ");
2161 
2162 		// Name
2163 		formattedWrite(sink, "%s\n", getName(stringTable));
2164 	}
2165 }
2166 static assert(SectionHeader.sizeof == 40);
2167 
2168 void printSectionCharacteristicsAlign(Sink)(auto ref Sink sink, uint Characteristics)
2169 {
2170 	if(Characteristics & 0x00F00000) {
2171 		size_t alignment = 1 << (((Characteristics & 0x00F00000) >> 20) - 1);
2172 		formattedWrite(sink, "% 5s", alignment);
2173 	} else formattedWrite(sink, "     ");
2174 }
2175 
2176 void printSectionCharacteristicsFlags(Sink)(auto ref Sink sink, uint Characteristics)
2177 {
2178 	if(Characteristics) with(SectionFlags)
2179 	{
2180 		void printFlag(char chr, SectionFlags flag) {
2181 			if(Characteristics & flag) sink.put(chr);
2182 			else sink.put(' ');
2183 		}
2184 		printFlag('C', SCN_CNT_CODE);
2185 		printFlag('I', SCN_CNT_INITIALIZED_DATA);
2186 		printFlag('U', SCN_CNT_UNINITIALIZED_DATA);
2187 		printFlag('L', SCN_LNK_INFO);
2188 		printFlag('R', SCN_LNK_REMOVE);
2189 		printFlag('M', SCN_LNK_COMDAT);
2190 		printFlag('G', SCN_GPREL);
2191 		printFlag('O', SCN_LNK_NRELOC_OVFL);
2192 		printFlag('D', SCN_MEM_DISCARDABLE);
2193 		printFlag('H', SCN_MEM_NOT_CACHED);
2194 		printFlag('P', SCN_MEM_NOT_PAGED);
2195 		printFlag('S', SCN_MEM_SHARED);
2196 		sink.put("  ");
2197 		printFlag('R', SCN_MEM_READ);
2198 		printFlag('W', SCN_MEM_WRITE);
2199 		printFlag('X', SCN_MEM_EXECUTE);
2200 	}
2201 }
2202 
2203 // Converts name that optionally refers to string table with "/n" format
2204 string nameFromSlashName(const char[] name, string stringTable)
2205 {
2206 	import std.string : fromStringz;
2207 	import std.conv : parse;
2208 	if (name[0] == '/')
2209 	{
2210 		string offsetDecimalString = fromFixedString(name[1..$]);
2211 		size_t offset = parse!size_t(offsetDecimalString);
2212 		return fromStringz(stringTable[offset..$].ptr);
2213 	}
2214 	else
2215 		return fromFixedString(name);
2216 }
2217 
2218 enum SymbolSectionNumber : short
2219 {
2220 	/// The symbol record is not yet assigned a section. A
2221 	/// value of zero indicates that a reference to an external
2222 	/// symbol is defined elsewhere. A value of non-zero is a
2223 	/// common symbol with a size that is specified by the
2224 	/// value.
2225 	UNDEFINED = 0,
2226 
2227 	/// The symbol has an absolute (non-relocatable) value
2228 	/// and is not an address.
2229 	ABSOLUTE = -1,
2230 
2231 	/// The symbol provides general type or debugging
2232 	/// information but does not correspond to a section.
2233 	/// Microsoft tools use this setting along with .file
2234 	/// records (storage class FILE).
2235 	DEBUG = -2,
2236 }
2237 
2238 enum CoffSymClass : ubyte
2239 {
2240 	END_OF_FUNCTION = 0xFF,
2241 	NULL = 0,
2242 	AUTOMATIC = 1,
2243 	EXTERNAL = 2,
2244 	STATIC = 3,
2245 	REGISTER = 4,
2246 	EXTERNAL_DEF = 5,
2247 	LABEL = 6,
2248 	UNDEFINED_LABEL = 7,
2249 	MEMBER_OF_STRUCT = 8,
2250 	ARGUMENT = 9,
2251 	STRUCT_TAG = 10,
2252 	MEMBER_OF_UNION = 11,
2253 	UNION_TAG = 12,
2254 	TYPE_DEFINITION = 13,
2255 	UNDEFINED_STATIC = 14,
2256 	ENUM_TAG = 15,
2257 	MEMBER_OF_ENUM = 16,
2258 	REGISTER_PARAM = 17,
2259 	BIT_FIELD = 18,
2260 	BLOCK = 100,
2261 	FUNCTION = 101,
2262 	END_OF_STRUCT = 102,
2263 	FILE = 103,
2264 	SECTION = 104,
2265 	WEAK_EXTERNAL = 105,
2266 	CLR_TOKEN = 107,
2267 }
2268 
2269 
2270 
2271 /// The symbol table in this section is inherited from the traditional COFF format. It is
2272 /// distinct from Microsoft Visual C++® debug information. A file can contain both a
2273 /// COFF symbol table and Visual C++ debug information, and the two are kept
2274 /// separate. Some Microsoft tools use the symbol table for limited but important
2275 /// purposes, such as communicating COMDAT information to the linker. Section
2276 /// names and file names, as well as code and data symbols, are listed in the symbol table.
2277 /// The location of the symbol table is indicated in the COFF header.
2278 /// The symbol table is an array of records, each 18 bytes long. Each record is either a
2279 /// standard or auxiliary symbol-table record. A standard record defines a symbol or
2280 /// name and has the following format.
2281 struct SymbolTableEntry
2282 {
2283 	align(1):
2284 	/// The name of the symbol, represented by a union
2285 	/// of three structures. An array of 8 bytes is used if
2286 	/// the name is not more than 8 bytes long. For more
2287 	/// information, see section 5.4.1, “Symbol Name Representation.”
2288 	PeSymbolName Name;
2289 
2290 	/// The value that is associated with the symbol. The
2291 	/// interpretation of this field depends on
2292 	/// SectionNumber and StorageClass. A typical
2293 	/// meaning is the relocatable address.
2294 	uint Value;
2295 
2296 	/// The signed integer that identifies the section,
2297 	/// using a one-based index into the section table.
2298 	/// Some values have special meaning, as defined in
2299 	/// section 5.4.2, “Section Number Values.”
2300 	short SectionNumber; /// See enum SymbolSectionNumber
2301 
2302 	/// A number that represents type. Microsoft tools set
2303 	/// this field to 0x20 (function) or 0x0 (not a function).
2304 	/// For more information, see section 5.4.3, “Type
2305 	/// Representation.”
2306 	ushort Type;
2307 
2308 	bool isUndefined() { return SectionNumber == SymbolSectionNumber.UNDEFINED; }
2309 	bool isAbsolute() { return SectionNumber == SymbolSectionNumber.ABSOLUTE; }
2310 	bool isDebug() { return SectionNumber == SymbolSectionNumber.DEBUG; }
2311 	bool isFunction() { return Type == 0x20; }
2312 	bool isNonFunction() { return Type == 0x0; }
2313 
2314 	/// An enumerated value that represents storage
2315 	/// class. For more information, see section 5.4.4,
2316 	/// “Storage Class.”
2317 	CoffSymClass StorageClass;
2318 
2319 	/// The number of auxiliary symbol table entries that
2320 	/// follow this record.
2321 	ubyte NumberOfAuxSymbols;
2322 
2323 	void print(Sink)(auto ref Sink sink, string stringTable)
2324 	{
2325 		formattedWrite(sink, "  Name: %s\n", Name.get(stringTable));
2326 		//formattedWrite(sink, "  Name: %s %s\n", Name.Zeroes, Name.Offset);
2327 		formattedWrite(sink, "    Value: %08X\n", Value);
2328 		switch(SectionNumber)
2329 		{
2330 			case -2: formattedWrite(sink, "    Section: Debug\n"); break;
2331 			case -1: formattedWrite(sink, "    Section: Absolute\n"); break;
2332 			case  0: formattedWrite(sink, "    Section: Undefined\n"); break;
2333 			default: formattedWrite(sink, "    Section: %s\n", SectionNumber);
2334 		}
2335 		formattedWrite(sink, "    Type: %s\n", Type);
2336 		formattedWrite(sink, "    Storage class: %s\n", StorageClass);
2337 		formattedWrite(sink, "    Number of aux symbols: %s\n", NumberOfAuxSymbols);
2338 	}
2339 }
2340 static assert(SymbolTableEntry.sizeof == 18);
2341 
2342 struct ImportDirectoryTableEntry
2343 {
2344 	/// The RVA of the import lookup table. This table contains
2345 	/// a name or ordinal for each import. (The name
2346 	/// “Characteristics” is used in Winnt.h, but no longer
2347 	/// describes this field.)
2348 	uint importLookupTableRVA;
2349 
2350 	/// The stamp that is set to zero until the image is bound.
2351 	/// After the image is bound, this field is set to the
2352 	/// time/data stamp of the DLL.
2353 	uint timestamp;
2354 
2355 	/// The index of the first forwarder reference.
2356 	uint forwarderChain;
2357 
2358 	/// The address of an ASCII string that contains the name
2359 	/// of the DLL. This address is relative to the image base.
2360 	uint nameRVA;
2361 
2362 	/// The RVA of the import address table. The contents of
2363 	/// this table are identical to the contents of the import
2364 	/// lookup table until the image is bound.
2365 	uint importAddressTableRVA;
2366 
2367 	size_t write(ref Arena!ubyte sink) {
2368 		auto offset = sink.length;
2369 		sink.put(this);
2370 		return offset;
2371 	}
2372 }
2373 
2374 struct ImportLookupTable
2375 {
2376 	ImportLookupEntry[] entries;
2377 	size_t write(ref Arena!ubyte sink) {
2378 		auto offset = sink.length;
2379 		foreach(ref entry; entries) entry.write(sink);
2380 		sink.pad(ImportLookupEntry.sizeof); // Null Import Lookup Entry
2381 		return offset;
2382 	}
2383 }
2384 
2385 struct ImportLookupEntry
2386 {
2387 	enum ulong ORDINAL_FLAG = 0x8000_0000_0000_0000;
2388 	static ImportLookupEntry fromOrdinal(ushort ordinal) {
2389 		return ImportLookupEntry(ORDINAL_FLAG | ordinal);
2390 	}
2391 	static ImportLookupEntry fromHintNameTableRVA(uint hintRVA) {
2392 		return ImportLookupEntry(hintRVA & 0x7FFF_FFFF);
2393 	}
2394 	ulong value;
2395 	size_t write(ref Arena!ubyte sink) {
2396 		auto offset = sink.length;
2397 		sink.put(this);
2398 		return offset;
2399 	}
2400 }
2401 
2402 struct ExportDirectoryEntry
2403 {
2404 	uint Characteristics;
2405 	uint TimeDateStamp;
2406 	ushort MajorVersion;
2407 	ushort MinorVersion;
2408 	uint Name;
2409 	uint Base;
2410 	uint NumberOfFunctions;
2411 	uint NumberOfNames;
2412 	uint AddressOfFunctions;
2413 	uint AddressOfNames;
2414 	uint AddressOfNameOrdinals;
2415 }
2416 
2417 struct HintNameTable
2418 {
2419 	HintNameEntry[] entries;
2420 
2421 	size_t write(ref Arena!ubyte sink) {
2422 		auto offset = sink.length;
2423 		foreach(ref entry; entries) entry.write(sink);
2424 		return offset;
2425 	}
2426 }
2427 
2428 struct HintNameEntry
2429 {
2430 	/// An index into the export name pointer table. A match is
2431 	/// attempted first with this value. If it fails, a binary search is
2432 	/// performed on the DLL’s export name pointer table.
2433 	ushort Hint;
2434 
2435 	/// An ASCII string that contains the name to import. This is the
2436 	/// string that must be matched to the public name in the DLL.
2437 	/// This string is case sensitive and terminated by a null byte.
2438 	string Name;
2439 
2440 	size_t write(ref Arena!ubyte sink) {
2441 		auto offset = sink.length;
2442 
2443 		sink.put(Hint);
2444 		sink.writeStringAligned2(Name);
2445 
2446 		return offset;
2447 	}
2448 }
2449 
2450 void writeStringAligned2(ref Arena!ubyte sink, string str)
2451 {
2452 	sink.put(cast(ubyte[])str);
2453 
2454 	// put trailing null byte and align on 2 bytes boundary
2455 	if (sink.length % 2 == 0)
2456 		sink.put!ushort(0); // 2 null bytes
2457 	else
2458 		sink.put!ubyte(0); // 1 null byte
2459 }
2460 
2461 struct SymbolRef
2462 {
2463 	SymbolIndex symbolIndex;
2464 	uint sectionId;
2465 }
2466 
2467 alias SymbolIndex = uint;
2468 
2469 struct SymbolSectionInfo
2470 {
2471 	uint sectionOffset;
2472 	uint length;
2473 }
2474 
2475 struct PeSymbolName
2476 {
2477 	union
2478 	{
2479 		char[8] ShortName;
2480 		struct {
2481 			uint Zeroes;
2482 			uint Offset; // includes 4 size bytes at the beginning of table
2483 		}
2484 	}
2485 
2486 	string get(string stringTable)
2487 	{
2488 		import std.string : fromStringz;
2489 		if (Zeroes == 0)
2490 			return fromStringz(stringTable[Offset-4..$].ptr);
2491 		else
2492 			return fromFixedString(ShortName);
2493 	}
2494 }
2495 
2496 string fromFixedString(const char[] fixedStr) {
2497 	foreach_reverse(i, chr; fixedStr)
2498 		if (chr != '\0') return cast(string)fixedStr[0..i+1];
2499 	return null;
2500 }
2501 
2502 string[] IMAGE_REL_AMD64_names = ["ABSOLUTE","ADDR64","ADDR32","ADDR32NB","REL32","REL32_1",
2503 	"REL32_2", "REL32_3", "REL32_4", "REL32_5", "SECTION", "SECREL", "SECREL7",
2504 	"TOKEN", "SREL32", "PAIR", "SSPAN32"];
2505 
2506 enum IMAGE_REL_AMD64 : ushort {
2507 	ABSOLUTE = 0x00, /// The relocation is ignored.
2508 	ADDR64 = 0x01, /// The 64-bit VA of the relocation target.
2509 	ADDR32 = 0x02, /// The 32-bit VA of the relocation target.
2510 	ADDR32NB = 0x03, /// The 32-bit address without an image base (RVA).
2511 	REL32 = 0x04, /// The 32-bit relative address from the byte following the relocation.
2512 	REL32_1 = 0x05, /// The 32-bit address relative to byte distance 1 from the relocation.
2513 	REL32_2 = 0x06, /// The 32-bit address relative to byte distance 2 from the relocation.
2514 	REL32_3 = 0x07, /// The 32-bit address relative to byte distance 3 from the relocation.
2515 	REL32_4 = 0x08, /// The 32-bit address relative to byte distance 4 from the relocation.
2516 	REL32_5 = 0x09, /// The 32-bit address relative to byte distance 5 from the relocation.
2517 	SECTION = 0x0A, /// The 16-bit section index of the section that contains the target. This is used to support debugging information.
2518 	SECREL = 0x0B, /// The 32-bit offset of the target from the beginning of its section. This is used to support debugging information and static thread local storage.
2519 	SECREL7 = 0x0C, /// A 7-bit unsigned offset from the base of the section that contains the target.
2520 	TOKEN = 0x0D, /// CLR tokens.
2521 	SREL32 = 0x0E, /// A 32-bit signed span-dependent value emitted into the object.
2522 	PAIR = 0x0F, /// A pair that must immediately follow every span-dependent value.
2523 	SSPAN32 = 0x10 /// A 32-bit signed span-dependent value that is applied at link time.
2524 }
2525 
2526 ubyte[] relTargetSizeTable = [0,64,32,32,32,32,32,32,32,32,16,32,7,0,32,0,32];
2527 
2528 struct CoffRelocation
2529 {
2530 	align(1):
2531 	/// The address of the item to which relocation is
2532 	/// applied. This is the offset from the beginning of the
2533 	/// section, plus the value of the section’s RVA/Offset
2534 	/// field. See section 4, “Section Table (Section
2535 	/// Headers).” For example, if the first byte of the
2536 	/// section has an address of 0x10, the third byte has
2537 	/// an address of 0x12.
2538 	uint VirtualAddress;
2539 
2540 	/// A zero-based index into the symbol table. This
2541 	/// symbol gives the address that is to be used for the
2542 	/// relocation. If the specified symbol has section
2543 	/// storage class, then the symbol’s address is the
2544 	/// address with the first section of the same name.
2545 	uint SymbolTableIndex;
2546 
2547 	/// A value that indicates the kind of relocation that
2548 	/// should be performed. Valid relocation types
2549 	/// depend on machine type. See section 5.2.1, “Type
2550 	/// Indicators.
2551 	ushort Type;
2552 
2553 	size_t targetSize()
2554 	{
2555 		return relTargetSizeTable[Type];
2556 	}
2557 
2558 	string typeString()
2559 	{
2560 		return IMAGE_REL_AMD64_names[Type];
2561 	}
2562 }
2563 static assert(CoffRelocation.sizeof == 10);
2564 
2565 struct Section
2566 {
2567 	uint sectionId; // 0 based index
2568 	// Can be slice of SectionHeader.Name, slice of String table or separate string
2569 	// In the latter case, header.Name needs to be set before writing to the file.
2570 	string name;
2571 	SectionHeader header;
2572 	uint zeroedBytes;
2573 	ubyte[] data;
2574 	CoffRelocation[] relocations;
2575 	SymbolSectionInfo[] symbols;
2576 
2577 	// string name() { return fromFixedString(header.Name); }
2578 
2579 	SymbolIndex addSymbol(uint offset, uint length)
2580 	{
2581 		SymbolIndex index = cast(uint)symbols.length;
2582 		symbols ~= SymbolSectionInfo(offset, length);
2583 		return index;
2584 	}
2585 
2586 	uint symOffset(SymbolIndex symbol)
2587 	{
2588 		return symbols[symbol].sectionOffset;
2589 	}
2590 
2591 	uint symBytes(SymbolIndex symbol)
2592 	{
2593 		return symbols[symbol].length;
2594 	}
2595 
2596 	void setSectionZeroLength(uint length) {
2597 		zeroedBytes = length;
2598 	}
2599 	uint initializedSize() {
2600 		return cast(uint)data.length;
2601 	}
2602 	uint totalSize() {
2603 		return cast(uint)data.length + zeroedBytes;
2604 	}
2605 
2606 	bool isCodeSection()
2607 	{
2608 		return name == ".text";
2609 	}
2610 
2611 	bool isImportSection()
2612 	{
2613 		return name == ".idata";
2614 	}
2615 
2616 	bool isLinkDirective()
2617 	{
2618 		return header.Characteristics & SectionFlags.SCN_LNK_INFO && name == ".drectve";
2619 	}
2620 }