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 }