WindowsResource.cpp revision 320572
1//===-- WindowsResource.cpp -------------------------------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file implements the .res file class. 11// 12//===----------------------------------------------------------------------===// 13 14#include "llvm/Object/WindowsResource.h" 15#include "llvm/Object/COFF.h" 16#include "llvm/Support/FileOutputBuffer.h" 17#include "llvm/Support/MathExtras.h" 18#include <ctime> 19#include <queue> 20#include <sstream> 21#include <system_error> 22 23using namespace llvm; 24using namespace object; 25 26namespace llvm { 27namespace object { 28 29#define RETURN_IF_ERROR(X) \ 30 if (auto EC = X) \ 31 return EC; 32 33const uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t); 34 35// COFF files seem to be inconsistent with alignment between sections, just use 36// 8-byte because it makes everyone happy. 37const uint32_t SECTION_ALIGNMENT = sizeof(uint64_t); 38 39uint32_t WindowsResourceParser::TreeNode::StringCount = 0; 40uint32_t WindowsResourceParser::TreeNode::DataCount = 0; 41 42WindowsResource::WindowsResource(MemoryBufferRef Source) 43 : Binary(Binary::ID_WinRes, Source) { 44 size_t LeadingSize = WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE; 45 BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize), 46 support::little); 47} 48 49Expected<std::unique_ptr<WindowsResource>> 50WindowsResource::createWindowsResource(MemoryBufferRef Source) { 51 if (Source.getBufferSize() < WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE) 52 return make_error<GenericBinaryError>( 53 "File too small to be a resource file", 54 object_error::invalid_file_type); 55 std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source)); 56 return std::move(Ret); 57} 58 59Expected<ResourceEntryRef> WindowsResource::getHeadEntry() { 60 Error Err = Error::success(); 61 auto Ref = ResourceEntryRef(BinaryStreamRef(BBS), this, Err); 62 if (Err) 63 return std::move(Err); 64 return Ref; 65} 66 67ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref, 68 const WindowsResource *Owner, Error &Err) 69 : Reader(Ref), OwningRes(Owner) { 70 if (loadNext()) 71 Err = make_error<GenericBinaryError>("Could not read first entry.\n", 72 object_error::unexpected_eof); 73} 74 75Error ResourceEntryRef::moveNext(bool &End) { 76 // Reached end of all the entries. 77 if (Reader.bytesRemaining() == 0) { 78 End = true; 79 return Error::success(); 80 } 81 RETURN_IF_ERROR(loadNext()); 82 83 return Error::success(); 84} 85 86static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID, 87 ArrayRef<UTF16> &Str, bool &IsString) { 88 uint16_t IDFlag; 89 RETURN_IF_ERROR(Reader.readInteger(IDFlag)); 90 IsString = IDFlag != 0xffff; 91 92 if (IsString) { 93 Reader.setOffset( 94 Reader.getOffset() - 95 sizeof(uint16_t)); // Re-read the bytes which we used to check the flag. 96 RETURN_IF_ERROR(Reader.readWideString(Str)); 97 } else 98 RETURN_IF_ERROR(Reader.readInteger(ID)); 99 100 return Error::success(); 101} 102 103Error ResourceEntryRef::loadNext() { 104 const WinResHeaderPrefix *Prefix; 105 RETURN_IF_ERROR(Reader.readObject(Prefix)); 106 107 if (Prefix->HeaderSize < MIN_HEADER_SIZE) 108 return make_error<GenericBinaryError>("Header size is too small.", 109 object_error::parse_failed); 110 111 RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType)); 112 113 RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName)); 114 115 RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_HEADER_ALIGNMENT)); 116 117 RETURN_IF_ERROR(Reader.readObject(Suffix)); 118 119 RETURN_IF_ERROR(Reader.readArray(Data, Prefix->DataSize)); 120 121 RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_DATA_ALIGNMENT)); 122 123 return Error::success(); 124} 125 126WindowsResourceParser::WindowsResourceParser() : Root(false) {} 127 128Error WindowsResourceParser::parse(WindowsResource *WR) { 129 auto EntryOrErr = WR->getHeadEntry(); 130 if (!EntryOrErr) 131 return EntryOrErr.takeError(); 132 133 ResourceEntryRef Entry = EntryOrErr.get(); 134 bool End = false; 135 while (!End) { 136 Data.push_back(Entry.getData()); 137 138 bool IsNewTypeString = false; 139 bool IsNewNameString = false; 140 141 Root.addEntry(Entry, IsNewTypeString, IsNewNameString); 142 143 if (IsNewTypeString) 144 StringTable.push_back(Entry.getTypeString()); 145 146 if (IsNewNameString) 147 StringTable.push_back(Entry.getNameString()); 148 149 RETURN_IF_ERROR(Entry.moveNext(End)); 150 } 151 152 return Error::success(); 153} 154 155void WindowsResourceParser::printTree(raw_ostream &OS) const { 156 ScopedPrinter Writer(OS); 157 Root.print(Writer, "Resource Tree"); 158} 159 160void WindowsResourceParser::TreeNode::addEntry(const ResourceEntryRef &Entry, 161 bool &IsNewTypeString, 162 bool &IsNewNameString) { 163 TreeNode &TypeNode = addTypeNode(Entry, IsNewTypeString); 164 TreeNode &NameNode = TypeNode.addNameNode(Entry, IsNewNameString); 165 NameNode.addLanguageNode(Entry); 166} 167 168WindowsResourceParser::TreeNode::TreeNode(bool IsStringNode) { 169 if (IsStringNode) 170 StringIndex = StringCount++; 171} 172 173WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion, 174 uint16_t MinorVersion, 175 uint32_t Characteristics) 176 : IsDataNode(true), MajorVersion(MajorVersion), MinorVersion(MinorVersion), 177 Characteristics(Characteristics) { 178 DataIndex = DataCount++; 179} 180 181std::unique_ptr<WindowsResourceParser::TreeNode> 182WindowsResourceParser::TreeNode::createStringNode() { 183 return std::unique_ptr<TreeNode>(new TreeNode(true)); 184} 185 186std::unique_ptr<WindowsResourceParser::TreeNode> 187WindowsResourceParser::TreeNode::createIDNode() { 188 return std::unique_ptr<TreeNode>(new TreeNode(false)); 189} 190 191std::unique_ptr<WindowsResourceParser::TreeNode> 192WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion, 193 uint16_t MinorVersion, 194 uint32_t Characteristics) { 195 return std::unique_ptr<TreeNode>( 196 new TreeNode(MajorVersion, MinorVersion, Characteristics)); 197} 198 199WindowsResourceParser::TreeNode & 200WindowsResourceParser::TreeNode::addTypeNode(const ResourceEntryRef &Entry, 201 bool &IsNewTypeString) { 202 if (Entry.checkTypeString()) 203 return addChild(Entry.getTypeString(), IsNewTypeString); 204 else 205 return addChild(Entry.getTypeID()); 206} 207 208WindowsResourceParser::TreeNode & 209WindowsResourceParser::TreeNode::addNameNode(const ResourceEntryRef &Entry, 210 bool &IsNewNameString) { 211 if (Entry.checkNameString()) 212 return addChild(Entry.getNameString(), IsNewNameString); 213 else 214 return addChild(Entry.getNameID()); 215} 216 217WindowsResourceParser::TreeNode & 218WindowsResourceParser::TreeNode::addLanguageNode( 219 const ResourceEntryRef &Entry) { 220 return addChild(Entry.getLanguage(), true, Entry.getMajorVersion(), 221 Entry.getMinorVersion(), Entry.getCharacteristics()); 222} 223 224WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addChild( 225 uint32_t ID, bool IsDataNode, uint16_t MajorVersion, uint16_t MinorVersion, 226 uint32_t Characteristics) { 227 auto Child = IDChildren.find(ID); 228 if (Child == IDChildren.end()) { 229 auto NewChild = 230 IsDataNode ? createDataNode(MajorVersion, MinorVersion, Characteristics) 231 : createIDNode(); 232 WindowsResourceParser::TreeNode &Node = *NewChild; 233 IDChildren.emplace(ID, std::move(NewChild)); 234 return Node; 235 } else 236 return *(Child->second); 237} 238 239WindowsResourceParser::TreeNode & 240WindowsResourceParser::TreeNode::addChild(ArrayRef<UTF16> NameRef, 241 bool &IsNewString) { 242 std::string NameString; 243 ArrayRef<UTF16> CorrectedName; 244 std::vector<UTF16> EndianCorrectedName; 245 if (sys::IsBigEndianHost) { 246 EndianCorrectedName.resize(NameRef.size() + 1); 247 std::copy(NameRef.begin(), NameRef.end(), EndianCorrectedName.begin() + 1); 248 EndianCorrectedName[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED; 249 CorrectedName = makeArrayRef(EndianCorrectedName); 250 } else 251 CorrectedName = NameRef; 252 convertUTF16ToUTF8String(CorrectedName, NameString); 253 254 auto Child = StringChildren.find(NameString); 255 if (Child == StringChildren.end()) { 256 auto NewChild = createStringNode(); 257 IsNewString = true; 258 WindowsResourceParser::TreeNode &Node = *NewChild; 259 StringChildren.emplace(NameString, std::move(NewChild)); 260 return Node; 261 } else 262 return *(Child->second); 263} 264 265void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer, 266 StringRef Name) const { 267 ListScope NodeScope(Writer, Name); 268 for (auto const &Child : StringChildren) { 269 Child.second->print(Writer, Child.first); 270 } 271 for (auto const &Child : IDChildren) { 272 Child.second->print(Writer, to_string(Child.first)); 273 } 274} 275 276// This function returns the size of the entire resource tree, including 277// directory tables, directory entries, and data entries. It does not include 278// the directory strings or the relocations of the .rsrc section. 279uint32_t WindowsResourceParser::TreeNode::getTreeSize() const { 280 uint32_t Size = (IDChildren.size() + StringChildren.size()) * 281 sizeof(coff_resource_dir_entry); 282 283 // Reached a node pointing to a data entry. 284 if (IsDataNode) { 285 Size += sizeof(coff_resource_data_entry); 286 return Size; 287 } 288 289 // If the node does not point to data, it must have a directory table pointing 290 // to other nodes. 291 Size += sizeof(coff_resource_dir_table); 292 293 for (auto const &Child : StringChildren) { 294 Size += Child.second->getTreeSize(); 295 } 296 for (auto const &Child : IDChildren) { 297 Size += Child.second->getTreeSize(); 298 } 299 return Size; 300} 301 302class WindowsResourceCOFFWriter { 303public: 304 WindowsResourceCOFFWriter(COFF::MachineTypes MachineType, 305 const WindowsResourceParser &Parser, Error &E); 306 std::unique_ptr<MemoryBuffer> write(); 307 308private: 309 void performFileLayout(); 310 void performSectionOneLayout(); 311 void performSectionTwoLayout(); 312 void writeCOFFHeader(); 313 void writeFirstSectionHeader(); 314 void writeSecondSectionHeader(); 315 void writeFirstSection(); 316 void writeSecondSection(); 317 void writeSymbolTable(); 318 void writeStringTable(); 319 void writeDirectoryTree(); 320 void writeDirectoryStringTable(); 321 void writeFirstSectionRelocations(); 322 std::unique_ptr<MemoryBuffer> OutputBuffer; 323 char *BufferStart; 324 uint64_t CurrentOffset = 0; 325 COFF::MachineTypes MachineType; 326 const WindowsResourceParser::TreeNode &Resources; 327 const ArrayRef<std::vector<uint8_t>> Data; 328 uint64_t FileSize; 329 uint32_t SymbolTableOffset; 330 uint32_t SectionOneSize; 331 uint32_t SectionOneOffset; 332 uint32_t SectionOneRelocations; 333 uint32_t SectionTwoSize; 334 uint32_t SectionTwoOffset; 335 const ArrayRef<std::vector<UTF16>> StringTable; 336 std::vector<uint32_t> StringTableOffsets; 337 std::vector<uint32_t> DataOffsets; 338 std::vector<uint32_t> RelocationAddresses; 339}; 340 341WindowsResourceCOFFWriter::WindowsResourceCOFFWriter( 342 COFF::MachineTypes MachineType, const WindowsResourceParser &Parser, 343 Error &E) 344 : MachineType(MachineType), Resources(Parser.getTree()), 345 Data(Parser.getData()), StringTable(Parser.getStringTable()) { 346 performFileLayout(); 347 348 OutputBuffer = MemoryBuffer::getNewMemBuffer(FileSize); 349} 350 351void WindowsResourceCOFFWriter::performFileLayout() { 352 // Add size of COFF header. 353 FileSize = COFF::Header16Size; 354 355 // one .rsrc section header for directory tree, another for resource data. 356 FileSize += 2 * COFF::SectionSize; 357 358 performSectionOneLayout(); 359 performSectionTwoLayout(); 360 361 // We have reached the address of the symbol table. 362 SymbolTableOffset = FileSize; 363 364 FileSize += COFF::Symbol16Size; // size of the @feat.00 symbol. 365 FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section. 366 FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource. 367 FileSize += 4; // four null bytes for the string table. 368} 369 370void WindowsResourceCOFFWriter::performSectionOneLayout() { 371 SectionOneOffset = FileSize; 372 373 SectionOneSize = Resources.getTreeSize(); 374 uint32_t CurrentStringOffset = SectionOneSize; 375 uint32_t TotalStringTableSize = 0; 376 for (auto const &String : StringTable) { 377 StringTableOffsets.push_back(CurrentStringOffset); 378 uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t); 379 CurrentStringOffset += StringSize; 380 TotalStringTableSize += StringSize; 381 } 382 SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t)); 383 384 // account for the relocations of section one. 385 SectionOneRelocations = FileSize + SectionOneSize; 386 FileSize += SectionOneSize; 387 FileSize += 388 Data.size() * COFF::RelocationSize; // one relocation for each resource. 389 FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 390} 391 392void WindowsResourceCOFFWriter::performSectionTwoLayout() { 393 // add size of .rsrc$2 section, which contains all resource data on 8-byte 394 // alignment. 395 SectionTwoOffset = FileSize; 396 SectionTwoSize = 0; 397 for (auto const &Entry : Data) { 398 DataOffsets.push_back(SectionTwoSize); 399 SectionTwoSize += alignTo(Entry.size(), sizeof(uint64_t)); 400 } 401 FileSize += SectionTwoSize; 402 FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 403} 404 405static std::time_t getTime() { 406 std::time_t Now = time(nullptr); 407 if (Now < 0 || !isUInt<32>(Now)) 408 return UINT32_MAX; 409 return Now; 410} 411 412std::unique_ptr<MemoryBuffer> WindowsResourceCOFFWriter::write() { 413 BufferStart = const_cast<char *>(OutputBuffer->getBufferStart()); 414 415 writeCOFFHeader(); 416 writeFirstSectionHeader(); 417 writeSecondSectionHeader(); 418 writeFirstSection(); 419 writeSecondSection(); 420 writeSymbolTable(); 421 writeStringTable(); 422 423 return std::move(OutputBuffer); 424} 425 426void WindowsResourceCOFFWriter::writeCOFFHeader() { 427 // Write the COFF header. 428 auto *Header = reinterpret_cast<coff_file_header *>(BufferStart); 429 switch (MachineType) { 430 case COFF::IMAGE_FILE_MACHINE_ARMNT: 431 Header->Machine = COFF::IMAGE_FILE_MACHINE_ARMNT; 432 break; 433 case COFF::IMAGE_FILE_MACHINE_AMD64: 434 Header->Machine = COFF::IMAGE_FILE_MACHINE_AMD64; 435 break; 436 case COFF::IMAGE_FILE_MACHINE_I386: 437 Header->Machine = COFF::IMAGE_FILE_MACHINE_I386; 438 break; 439 default: 440 Header->Machine = COFF::IMAGE_FILE_MACHINE_UNKNOWN; 441 } 442 Header->NumberOfSections = 2; 443 Header->TimeDateStamp = getTime(); 444 Header->PointerToSymbolTable = SymbolTableOffset; 445 // One symbol for every resource plus 2 for each section and @feat.00 446 Header->NumberOfSymbols = Data.size() + 5; 447 Header->SizeOfOptionalHeader = 0; 448 Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE; 449} 450 451void WindowsResourceCOFFWriter::writeFirstSectionHeader() { 452 // Write the first section header. 453 CurrentOffset += sizeof(coff_file_header); 454 auto *SectionOneHeader = 455 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 456 strncpy(SectionOneHeader->Name, ".rsrc$01", (size_t)COFF::NameSize); 457 SectionOneHeader->VirtualSize = 0; 458 SectionOneHeader->VirtualAddress = 0; 459 SectionOneHeader->SizeOfRawData = SectionOneSize; 460 SectionOneHeader->PointerToRawData = SectionOneOffset; 461 SectionOneHeader->PointerToRelocations = SectionOneRelocations; 462 SectionOneHeader->PointerToLinenumbers = 0; 463 SectionOneHeader->NumberOfRelocations = Data.size(); 464 SectionOneHeader->NumberOfLinenumbers = 0; 465 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 466 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 467} 468 469void WindowsResourceCOFFWriter::writeSecondSectionHeader() { 470 // Write the second section header. 471 CurrentOffset += sizeof(coff_section); 472 auto *SectionTwoHeader = 473 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 474 strncpy(SectionTwoHeader->Name, ".rsrc$02", (size_t)COFF::NameSize); 475 SectionTwoHeader->VirtualSize = 0; 476 SectionTwoHeader->VirtualAddress = 0; 477 SectionTwoHeader->SizeOfRawData = SectionTwoSize; 478 SectionTwoHeader->PointerToRawData = SectionTwoOffset; 479 SectionTwoHeader->PointerToRelocations = 0; 480 SectionTwoHeader->PointerToLinenumbers = 0; 481 SectionTwoHeader->NumberOfRelocations = 0; 482 SectionTwoHeader->NumberOfLinenumbers = 0; 483 SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 484 SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 485} 486 487void WindowsResourceCOFFWriter::writeFirstSection() { 488 // Write section one. 489 CurrentOffset += sizeof(coff_section); 490 491 writeDirectoryTree(); 492 writeDirectoryStringTable(); 493 writeFirstSectionRelocations(); 494 495 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 496} 497 498void WindowsResourceCOFFWriter::writeSecondSection() { 499 // Now write the .rsrc$02 section. 500 for (auto const &RawDataEntry : Data) { 501 std::copy(RawDataEntry.begin(), RawDataEntry.end(), 502 BufferStart + CurrentOffset); 503 CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t)); 504 } 505 506 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 507} 508 509void WindowsResourceCOFFWriter::writeSymbolTable() { 510 // Now write the symbol table. 511 // First, the feat symbol. 512 auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 513 strncpy(Symbol->Name.ShortName, "@feat.00", (size_t)COFF::NameSize); 514 Symbol->Value = 0x11; 515 Symbol->SectionNumber = 0xffff; 516 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 517 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 518 Symbol->NumberOfAuxSymbols = 0; 519 CurrentOffset += sizeof(coff_symbol16); 520 521 // Now write the .rsrc1 symbol + aux. 522 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 523 strncpy(Symbol->Name.ShortName, ".rsrc$01", (size_t)COFF::NameSize); 524 Symbol->Value = 0; 525 Symbol->SectionNumber = 1; 526 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 527 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 528 Symbol->NumberOfAuxSymbols = 1; 529 CurrentOffset += sizeof(coff_symbol16); 530 auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 531 CurrentOffset); 532 Aux->Length = SectionOneSize; 533 Aux->NumberOfRelocations = Data.size(); 534 Aux->NumberOfLinenumbers = 0; 535 Aux->CheckSum = 0; 536 Aux->NumberLowPart = 0; 537 Aux->Selection = 0; 538 CurrentOffset += sizeof(coff_aux_section_definition); 539 540 // Now write the .rsrc2 symbol + aux. 541 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 542 strncpy(Symbol->Name.ShortName, ".rsrc$02", (size_t)COFF::NameSize); 543 Symbol->Value = 0; 544 Symbol->SectionNumber = 2; 545 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 546 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 547 Symbol->NumberOfAuxSymbols = 1; 548 CurrentOffset += sizeof(coff_symbol16); 549 Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 550 CurrentOffset); 551 Aux->Length = SectionTwoSize; 552 Aux->NumberOfRelocations = 0; 553 Aux->NumberOfLinenumbers = 0; 554 Aux->CheckSum = 0; 555 Aux->NumberLowPart = 0; 556 Aux->Selection = 0; 557 CurrentOffset += sizeof(coff_aux_section_definition); 558 559 // Now write a symbol for each relocation. 560 for (unsigned i = 0; i < Data.size(); i++) { 561 char RelocationName[9]; 562 sprintf(RelocationName, "$R%06X", DataOffsets[i]); 563 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 564 strncpy(Symbol->Name.ShortName, RelocationName, (size_t)COFF::NameSize); 565 Symbol->Value = DataOffsets[i]; 566 Symbol->SectionNumber = 2; 567 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 568 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 569 Symbol->NumberOfAuxSymbols = 0; 570 CurrentOffset += sizeof(coff_symbol16); 571 } 572} 573 574void WindowsResourceCOFFWriter::writeStringTable() { 575 // Just 4 null bytes for the string table. 576 auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset); 577 memset(COFFStringTable, 0, 4); 578} 579 580void WindowsResourceCOFFWriter::writeDirectoryTree() { 581 // Traverse parsed resource tree breadth-first and write the corresponding 582 // COFF objects. 583 std::queue<const WindowsResourceParser::TreeNode *> Queue; 584 Queue.push(&Resources); 585 uint32_t NextLevelOffset = 586 sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() + 587 Resources.getIDChildren().size()) * 588 sizeof(coff_resource_dir_entry); 589 std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder; 590 uint32_t CurrentRelativeOffset = 0; 591 592 while (!Queue.empty()) { 593 auto CurrentNode = Queue.front(); 594 Queue.pop(); 595 auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart + 596 CurrentOffset); 597 Table->Characteristics = CurrentNode->getCharacteristics(); 598 Table->TimeDateStamp = 0; 599 Table->MajorVersion = CurrentNode->getMajorVersion(); 600 Table->MinorVersion = CurrentNode->getMinorVersion(); 601 auto &IDChildren = CurrentNode->getIDChildren(); 602 auto &StringChildren = CurrentNode->getStringChildren(); 603 Table->NumberOfNameEntries = StringChildren.size(); 604 Table->NumberOfIDEntries = IDChildren.size(); 605 CurrentOffset += sizeof(coff_resource_dir_table); 606 CurrentRelativeOffset += sizeof(coff_resource_dir_table); 607 608 // Write the directory entries immediately following each directory table. 609 for (auto const &Child : StringChildren) { 610 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 611 CurrentOffset); 612 Entry->Identifier.NameOffset = 613 StringTableOffsets[Child.second->getStringIndex()]; 614 if (Child.second->checkIsDataNode()) { 615 Entry->Offset.DataEntryOffset = NextLevelOffset; 616 NextLevelOffset += sizeof(coff_resource_data_entry); 617 DataEntriesTreeOrder.push_back(Child.second.get()); 618 } else { 619 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 620 NextLevelOffset += sizeof(coff_resource_dir_table) + 621 (Child.second->getStringChildren().size() + 622 Child.second->getIDChildren().size()) * 623 sizeof(coff_resource_dir_entry); 624 Queue.push(Child.second.get()); 625 } 626 CurrentOffset += sizeof(coff_resource_dir_entry); 627 CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 628 } 629 for (auto const &Child : IDChildren) { 630 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 631 CurrentOffset); 632 Entry->Identifier.ID = Child.first; 633 if (Child.second->checkIsDataNode()) { 634 Entry->Offset.DataEntryOffset = NextLevelOffset; 635 NextLevelOffset += sizeof(coff_resource_data_entry); 636 DataEntriesTreeOrder.push_back(Child.second.get()); 637 } else { 638 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 639 NextLevelOffset += sizeof(coff_resource_dir_table) + 640 (Child.second->getStringChildren().size() + 641 Child.second->getIDChildren().size()) * 642 sizeof(coff_resource_dir_entry); 643 Queue.push(Child.second.get()); 644 } 645 CurrentOffset += sizeof(coff_resource_dir_entry); 646 CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 647 } 648 } 649 650 RelocationAddresses.resize(Data.size()); 651 // Now write all the resource data entries. 652 for (auto DataNodes : DataEntriesTreeOrder) { 653 auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart + 654 CurrentOffset); 655 RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset; 656 Entry->DataRVA = 0; // Set to zero because it is a relocation. 657 Entry->DataSize = Data[DataNodes->getDataIndex()].size(); 658 Entry->Codepage = 0; 659 Entry->Reserved = 0; 660 CurrentOffset += sizeof(coff_resource_data_entry); 661 CurrentRelativeOffset += sizeof(coff_resource_data_entry); 662 } 663} 664 665void WindowsResourceCOFFWriter::writeDirectoryStringTable() { 666 // Now write the directory string table for .rsrc$01 667 uint32_t TotalStringTableSize = 0; 668 for (auto &String : StringTable) { 669 uint16_t Length = String.size(); 670 support::endian::write16le(BufferStart + CurrentOffset, Length); 671 CurrentOffset += sizeof(uint16_t); 672 auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset); 673 std::copy(String.begin(), String.end(), Start); 674 CurrentOffset += Length * sizeof(UTF16); 675 TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t); 676 } 677 CurrentOffset += 678 alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize; 679} 680 681void WindowsResourceCOFFWriter::writeFirstSectionRelocations() { 682 683 // Now write the relocations for .rsrc$01 684 // Five symbols already in table before we start, @feat.00 and 2 for each 685 // .rsrc section. 686 uint32_t NextSymbolIndex = 5; 687 for (unsigned i = 0; i < Data.size(); i++) { 688 auto *Reloc = 689 reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset); 690 Reloc->VirtualAddress = RelocationAddresses[i]; 691 Reloc->SymbolTableIndex = NextSymbolIndex++; 692 switch (MachineType) { 693 case COFF::IMAGE_FILE_MACHINE_ARMNT: 694 Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB; 695 break; 696 case COFF::IMAGE_FILE_MACHINE_AMD64: 697 Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB; 698 break; 699 case COFF::IMAGE_FILE_MACHINE_I386: 700 Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB; 701 break; 702 default: 703 Reloc->Type = 0; 704 } 705 CurrentOffset += sizeof(coff_relocation); 706 } 707} 708 709Expected<std::unique_ptr<MemoryBuffer>> 710writeWindowsResourceCOFF(COFF::MachineTypes MachineType, 711 const WindowsResourceParser &Parser) { 712 Error E = Error::success(); 713 WindowsResourceCOFFWriter Writer(MachineType, Parser, E); 714 if (E) 715 return std::move(E); 716 return Writer.write(); 717} 718 719} // namespace object 720} // namespace llvm 721