1 //===-- WindowsResource.cpp -------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file implements the .res file class. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/Object/WindowsResource.h" 14 #include "llvm/Object/COFF.h" 15 #include "llvm/Support/FileOutputBuffer.h" 16 #include "llvm/Support/FormatVariadic.h" 17 #include "llvm/Support/MathExtras.h" 18 #include <ctime> 19 #include <queue> 20 #include <system_error> 21 22 using namespace llvm; 23 using namespace object; 24 25 namespace llvm { 26 namespace object { 27 28 #define RETURN_IF_ERROR(X) \ 29 if (auto EC = X) \ 30 return EC; 31 32 const uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t); 33 34 // COFF files seem to be inconsistent with alignment between sections, just use 35 // 8-byte because it makes everyone happy. 36 const uint32_t SECTION_ALIGNMENT = sizeof(uint64_t); 37 38 uint32_t WindowsResourceParser::TreeNode::StringCount = 0; 39 uint32_t WindowsResourceParser::TreeNode::DataCount = 0; 40 41 WindowsResource::WindowsResource(MemoryBufferRef Source) 42 : Binary(Binary::ID_WinRes, Source) { 43 size_t LeadingSize = WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE; 44 BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize), 45 support::little); 46 } 47 48 Expected<std::unique_ptr<WindowsResource>> 49 WindowsResource::createWindowsResource(MemoryBufferRef Source) { 50 if (Source.getBufferSize() < WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE) 51 return make_error<GenericBinaryError>( 52 "File too small to be a resource file", 53 object_error::invalid_file_type); 54 std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source)); 55 return std::move(Ret); 56 } 57 58 Expected<ResourceEntryRef> WindowsResource::getHeadEntry() { 59 if (BBS.getLength() < sizeof(WinResHeaderPrefix) + sizeof(WinResHeaderSuffix)) 60 return make_error<EmptyResError>(".res contains no entries", 61 object_error::unexpected_eof); 62 return ResourceEntryRef::create(BinaryStreamRef(BBS), this); 63 } 64 65 ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref, 66 const WindowsResource *Owner) 67 : Reader(Ref) {} 68 69 Expected<ResourceEntryRef> 70 ResourceEntryRef::create(BinaryStreamRef BSR, const WindowsResource *Owner) { 71 auto Ref = ResourceEntryRef(BSR, Owner); 72 if (auto E = Ref.loadNext()) 73 return std::move(E); 74 return Ref; 75 } 76 77 Error ResourceEntryRef::moveNext(bool &End) { 78 // Reached end of all the entries. 79 if (Reader.bytesRemaining() == 0) { 80 End = true; 81 return Error::success(); 82 } 83 RETURN_IF_ERROR(loadNext()); 84 85 return Error::success(); 86 } 87 88 static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID, 89 ArrayRef<UTF16> &Str, bool &IsString) { 90 uint16_t IDFlag; 91 RETURN_IF_ERROR(Reader.readInteger(IDFlag)); 92 IsString = IDFlag != 0xffff; 93 94 if (IsString) { 95 Reader.setOffset( 96 Reader.getOffset() - 97 sizeof(uint16_t)); // Re-read the bytes which we used to check the flag. 98 RETURN_IF_ERROR(Reader.readWideString(Str)); 99 } else 100 RETURN_IF_ERROR(Reader.readInteger(ID)); 101 102 return Error::success(); 103 } 104 105 Error ResourceEntryRef::loadNext() { 106 const WinResHeaderPrefix *Prefix; 107 RETURN_IF_ERROR(Reader.readObject(Prefix)); 108 109 if (Prefix->HeaderSize < MIN_HEADER_SIZE) 110 return make_error<GenericBinaryError>("Header size is too small.", 111 object_error::parse_failed); 112 113 RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType)); 114 115 RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName)); 116 117 RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_HEADER_ALIGNMENT)); 118 119 RETURN_IF_ERROR(Reader.readObject(Suffix)); 120 121 RETURN_IF_ERROR(Reader.readArray(Data, Prefix->DataSize)); 122 123 RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_DATA_ALIGNMENT)); 124 125 return Error::success(); 126 } 127 128 WindowsResourceParser::WindowsResourceParser() : Root(false) {} 129 130 Error WindowsResourceParser::parse(WindowsResource *WR) { 131 auto EntryOrErr = WR->getHeadEntry(); 132 if (!EntryOrErr) { 133 auto E = EntryOrErr.takeError(); 134 if (E.isA<EmptyResError>()) { 135 // Check if the .res file contains no entries. In this case we don't have 136 // to throw an error but can rather just return without parsing anything. 137 // This applies for files which have a valid PE header magic and the 138 // mandatory empty null resource entry. Files which do not fit this 139 // criteria would have already been filtered out by 140 // WindowsResource::createWindowsResource(). 141 consumeError(std::move(E)); 142 return Error::success(); 143 } 144 return E; 145 } 146 147 ResourceEntryRef Entry = EntryOrErr.get(); 148 bool End = false; 149 while (!End) { 150 Data.push_back(Entry.getData()); 151 152 bool IsNewTypeString = false; 153 bool IsNewNameString = false; 154 155 Root.addEntry(Entry, IsNewTypeString, IsNewNameString); 156 157 if (IsNewTypeString) 158 StringTable.push_back(Entry.getTypeString()); 159 160 if (IsNewNameString) 161 StringTable.push_back(Entry.getNameString()); 162 163 RETURN_IF_ERROR(Entry.moveNext(End)); 164 } 165 166 return Error::success(); 167 } 168 169 void WindowsResourceParser::printTree(raw_ostream &OS) const { 170 ScopedPrinter Writer(OS); 171 Root.print(Writer, "Resource Tree"); 172 } 173 174 void WindowsResourceParser::TreeNode::addEntry(const ResourceEntryRef &Entry, 175 bool &IsNewTypeString, 176 bool &IsNewNameString) { 177 TreeNode &TypeNode = addTypeNode(Entry, IsNewTypeString); 178 TreeNode &NameNode = TypeNode.addNameNode(Entry, IsNewNameString); 179 NameNode.addLanguageNode(Entry); 180 } 181 182 WindowsResourceParser::TreeNode::TreeNode(bool IsStringNode) { 183 if (IsStringNode) 184 StringIndex = StringCount++; 185 } 186 187 WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion, 188 uint16_t MinorVersion, 189 uint32_t Characteristics) 190 : IsDataNode(true), MajorVersion(MajorVersion), MinorVersion(MinorVersion), 191 Characteristics(Characteristics) { 192 DataIndex = DataCount++; 193 } 194 195 std::unique_ptr<WindowsResourceParser::TreeNode> 196 WindowsResourceParser::TreeNode::createStringNode() { 197 return std::unique_ptr<TreeNode>(new TreeNode(true)); 198 } 199 200 std::unique_ptr<WindowsResourceParser::TreeNode> 201 WindowsResourceParser::TreeNode::createIDNode() { 202 return std::unique_ptr<TreeNode>(new TreeNode(false)); 203 } 204 205 std::unique_ptr<WindowsResourceParser::TreeNode> 206 WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion, 207 uint16_t MinorVersion, 208 uint32_t Characteristics) { 209 return std::unique_ptr<TreeNode>( 210 new TreeNode(MajorVersion, MinorVersion, Characteristics)); 211 } 212 213 WindowsResourceParser::TreeNode & 214 WindowsResourceParser::TreeNode::addTypeNode(const ResourceEntryRef &Entry, 215 bool &IsNewTypeString) { 216 if (Entry.checkTypeString()) 217 return addChild(Entry.getTypeString(), IsNewTypeString); 218 else 219 return addChild(Entry.getTypeID()); 220 } 221 222 WindowsResourceParser::TreeNode & 223 WindowsResourceParser::TreeNode::addNameNode(const ResourceEntryRef &Entry, 224 bool &IsNewNameString) { 225 if (Entry.checkNameString()) 226 return addChild(Entry.getNameString(), IsNewNameString); 227 else 228 return addChild(Entry.getNameID()); 229 } 230 231 WindowsResourceParser::TreeNode & 232 WindowsResourceParser::TreeNode::addLanguageNode( 233 const ResourceEntryRef &Entry) { 234 return addChild(Entry.getLanguage(), true, Entry.getMajorVersion(), 235 Entry.getMinorVersion(), Entry.getCharacteristics()); 236 } 237 238 WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addChild( 239 uint32_t ID, bool IsDataNode, uint16_t MajorVersion, uint16_t MinorVersion, 240 uint32_t Characteristics) { 241 auto Child = IDChildren.find(ID); 242 if (Child == IDChildren.end()) { 243 auto NewChild = 244 IsDataNode ? createDataNode(MajorVersion, MinorVersion, Characteristics) 245 : createIDNode(); 246 WindowsResourceParser::TreeNode &Node = *NewChild; 247 IDChildren.emplace(ID, std::move(NewChild)); 248 return Node; 249 } else 250 return *(Child->second); 251 } 252 253 WindowsResourceParser::TreeNode & 254 WindowsResourceParser::TreeNode::addChild(ArrayRef<UTF16> NameRef, 255 bool &IsNewString) { 256 std::string NameString; 257 ArrayRef<UTF16> CorrectedName; 258 std::vector<UTF16> EndianCorrectedName; 259 if (sys::IsBigEndianHost) { 260 EndianCorrectedName.resize(NameRef.size() + 1); 261 llvm::copy(NameRef, EndianCorrectedName.begin() + 1); 262 EndianCorrectedName[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED; 263 CorrectedName = makeArrayRef(EndianCorrectedName); 264 } else 265 CorrectedName = NameRef; 266 convertUTF16ToUTF8String(CorrectedName, NameString); 267 268 auto Child = StringChildren.find(NameString); 269 if (Child == StringChildren.end()) { 270 auto NewChild = createStringNode(); 271 IsNewString = true; 272 WindowsResourceParser::TreeNode &Node = *NewChild; 273 StringChildren.emplace(NameString, std::move(NewChild)); 274 return Node; 275 } else 276 return *(Child->second); 277 } 278 279 void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer, 280 StringRef Name) const { 281 ListScope NodeScope(Writer, Name); 282 for (auto const &Child : StringChildren) { 283 Child.second->print(Writer, Child.first); 284 } 285 for (auto const &Child : IDChildren) { 286 Child.second->print(Writer, to_string(Child.first)); 287 } 288 } 289 290 // This function returns the size of the entire resource tree, including 291 // directory tables, directory entries, and data entries. It does not include 292 // the directory strings or the relocations of the .rsrc section. 293 uint32_t WindowsResourceParser::TreeNode::getTreeSize() const { 294 uint32_t Size = (IDChildren.size() + StringChildren.size()) * 295 sizeof(coff_resource_dir_entry); 296 297 // Reached a node pointing to a data entry. 298 if (IsDataNode) { 299 Size += sizeof(coff_resource_data_entry); 300 return Size; 301 } 302 303 // If the node does not point to data, it must have a directory table pointing 304 // to other nodes. 305 Size += sizeof(coff_resource_dir_table); 306 307 for (auto const &Child : StringChildren) { 308 Size += Child.second->getTreeSize(); 309 } 310 for (auto const &Child : IDChildren) { 311 Size += Child.second->getTreeSize(); 312 } 313 return Size; 314 } 315 316 class WindowsResourceCOFFWriter { 317 public: 318 WindowsResourceCOFFWriter(COFF::MachineTypes MachineType, 319 const WindowsResourceParser &Parser, Error &E); 320 std::unique_ptr<MemoryBuffer> write(); 321 322 private: 323 void performFileLayout(); 324 void performSectionOneLayout(); 325 void performSectionTwoLayout(); 326 void writeCOFFHeader(); 327 void writeFirstSectionHeader(); 328 void writeSecondSectionHeader(); 329 void writeFirstSection(); 330 void writeSecondSection(); 331 void writeSymbolTable(); 332 void writeStringTable(); 333 void writeDirectoryTree(); 334 void writeDirectoryStringTable(); 335 void writeFirstSectionRelocations(); 336 std::unique_ptr<WritableMemoryBuffer> OutputBuffer; 337 char *BufferStart; 338 uint64_t CurrentOffset = 0; 339 COFF::MachineTypes MachineType; 340 const WindowsResourceParser::TreeNode &Resources; 341 const ArrayRef<std::vector<uint8_t>> Data; 342 uint64_t FileSize; 343 uint32_t SymbolTableOffset; 344 uint32_t SectionOneSize; 345 uint32_t SectionOneOffset; 346 uint32_t SectionOneRelocations; 347 uint32_t SectionTwoSize; 348 uint32_t SectionTwoOffset; 349 const ArrayRef<std::vector<UTF16>> StringTable; 350 std::vector<uint32_t> StringTableOffsets; 351 std::vector<uint32_t> DataOffsets; 352 std::vector<uint32_t> RelocationAddresses; 353 }; 354 355 WindowsResourceCOFFWriter::WindowsResourceCOFFWriter( 356 COFF::MachineTypes MachineType, const WindowsResourceParser &Parser, 357 Error &E) 358 : MachineType(MachineType), Resources(Parser.getTree()), 359 Data(Parser.getData()), StringTable(Parser.getStringTable()) { 360 performFileLayout(); 361 362 OutputBuffer = WritableMemoryBuffer::getNewMemBuffer(FileSize); 363 } 364 365 void WindowsResourceCOFFWriter::performFileLayout() { 366 // Add size of COFF header. 367 FileSize = COFF::Header16Size; 368 369 // one .rsrc section header for directory tree, another for resource data. 370 FileSize += 2 * COFF::SectionSize; 371 372 performSectionOneLayout(); 373 performSectionTwoLayout(); 374 375 // We have reached the address of the symbol table. 376 SymbolTableOffset = FileSize; 377 378 FileSize += COFF::Symbol16Size; // size of the @feat.00 symbol. 379 FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section. 380 FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource. 381 FileSize += 4; // four null bytes for the string table. 382 } 383 384 void WindowsResourceCOFFWriter::performSectionOneLayout() { 385 SectionOneOffset = FileSize; 386 387 SectionOneSize = Resources.getTreeSize(); 388 uint32_t CurrentStringOffset = SectionOneSize; 389 uint32_t TotalStringTableSize = 0; 390 for (auto const &String : StringTable) { 391 StringTableOffsets.push_back(CurrentStringOffset); 392 uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t); 393 CurrentStringOffset += StringSize; 394 TotalStringTableSize += StringSize; 395 } 396 SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t)); 397 398 // account for the relocations of section one. 399 SectionOneRelocations = FileSize + SectionOneSize; 400 FileSize += SectionOneSize; 401 FileSize += 402 Data.size() * COFF::RelocationSize; // one relocation for each resource. 403 FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 404 } 405 406 void WindowsResourceCOFFWriter::performSectionTwoLayout() { 407 // add size of .rsrc$2 section, which contains all resource data on 8-byte 408 // alignment. 409 SectionTwoOffset = FileSize; 410 SectionTwoSize = 0; 411 for (auto const &Entry : Data) { 412 DataOffsets.push_back(SectionTwoSize); 413 SectionTwoSize += alignTo(Entry.size(), sizeof(uint64_t)); 414 } 415 FileSize += SectionTwoSize; 416 FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 417 } 418 419 static std::time_t getTime() { 420 std::time_t Now = time(nullptr); 421 if (Now < 0 || !isUInt<32>(Now)) 422 return UINT32_MAX; 423 return Now; 424 } 425 426 std::unique_ptr<MemoryBuffer> WindowsResourceCOFFWriter::write() { 427 BufferStart = OutputBuffer->getBufferStart(); 428 429 writeCOFFHeader(); 430 writeFirstSectionHeader(); 431 writeSecondSectionHeader(); 432 writeFirstSection(); 433 writeSecondSection(); 434 writeSymbolTable(); 435 writeStringTable(); 436 437 return std::move(OutputBuffer); 438 } 439 440 void WindowsResourceCOFFWriter::writeCOFFHeader() { 441 // Write the COFF header. 442 auto *Header = reinterpret_cast<coff_file_header *>(BufferStart); 443 Header->Machine = MachineType; 444 Header->NumberOfSections = 2; 445 Header->TimeDateStamp = getTime(); 446 Header->PointerToSymbolTable = SymbolTableOffset; 447 // One symbol for every resource plus 2 for each section and @feat.00 448 Header->NumberOfSymbols = Data.size() + 5; 449 Header->SizeOfOptionalHeader = 0; 450 Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE; 451 } 452 453 void WindowsResourceCOFFWriter::writeFirstSectionHeader() { 454 // Write the first section header. 455 CurrentOffset += sizeof(coff_file_header); 456 auto *SectionOneHeader = 457 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 458 strncpy(SectionOneHeader->Name, ".rsrc$01", (size_t)COFF::NameSize); 459 SectionOneHeader->VirtualSize = 0; 460 SectionOneHeader->VirtualAddress = 0; 461 SectionOneHeader->SizeOfRawData = SectionOneSize; 462 SectionOneHeader->PointerToRawData = SectionOneOffset; 463 SectionOneHeader->PointerToRelocations = SectionOneRelocations; 464 SectionOneHeader->PointerToLinenumbers = 0; 465 SectionOneHeader->NumberOfRelocations = Data.size(); 466 SectionOneHeader->NumberOfLinenumbers = 0; 467 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 468 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 469 } 470 471 void WindowsResourceCOFFWriter::writeSecondSectionHeader() { 472 // Write the second section header. 473 CurrentOffset += sizeof(coff_section); 474 auto *SectionTwoHeader = 475 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 476 strncpy(SectionTwoHeader->Name, ".rsrc$02", (size_t)COFF::NameSize); 477 SectionTwoHeader->VirtualSize = 0; 478 SectionTwoHeader->VirtualAddress = 0; 479 SectionTwoHeader->SizeOfRawData = SectionTwoSize; 480 SectionTwoHeader->PointerToRawData = SectionTwoOffset; 481 SectionTwoHeader->PointerToRelocations = 0; 482 SectionTwoHeader->PointerToLinenumbers = 0; 483 SectionTwoHeader->NumberOfRelocations = 0; 484 SectionTwoHeader->NumberOfLinenumbers = 0; 485 SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 486 SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 487 } 488 489 void WindowsResourceCOFFWriter::writeFirstSection() { 490 // Write section one. 491 CurrentOffset += sizeof(coff_section); 492 493 writeDirectoryTree(); 494 writeDirectoryStringTable(); 495 writeFirstSectionRelocations(); 496 497 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 498 } 499 500 void WindowsResourceCOFFWriter::writeSecondSection() { 501 // Now write the .rsrc$02 section. 502 for (auto const &RawDataEntry : Data) { 503 llvm::copy(RawDataEntry, BufferStart + CurrentOffset); 504 CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t)); 505 } 506 507 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 508 } 509 510 void WindowsResourceCOFFWriter::writeSymbolTable() { 511 // Now write the symbol table. 512 // First, the feat symbol. 513 auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 514 strncpy(Symbol->Name.ShortName, "@feat.00", (size_t)COFF::NameSize); 515 Symbol->Value = 0x11; 516 Symbol->SectionNumber = 0xffff; 517 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 518 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 519 Symbol->NumberOfAuxSymbols = 0; 520 CurrentOffset += sizeof(coff_symbol16); 521 522 // Now write the .rsrc1 symbol + aux. 523 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 524 strncpy(Symbol->Name.ShortName, ".rsrc$01", (size_t)COFF::NameSize); 525 Symbol->Value = 0; 526 Symbol->SectionNumber = 1; 527 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 528 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 529 Symbol->NumberOfAuxSymbols = 1; 530 CurrentOffset += sizeof(coff_symbol16); 531 auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 532 CurrentOffset); 533 Aux->Length = SectionOneSize; 534 Aux->NumberOfRelocations = Data.size(); 535 Aux->NumberOfLinenumbers = 0; 536 Aux->CheckSum = 0; 537 Aux->NumberLowPart = 0; 538 Aux->Selection = 0; 539 CurrentOffset += sizeof(coff_aux_section_definition); 540 541 // Now write the .rsrc2 symbol + aux. 542 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 543 strncpy(Symbol->Name.ShortName, ".rsrc$02", (size_t)COFF::NameSize); 544 Symbol->Value = 0; 545 Symbol->SectionNumber = 2; 546 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 547 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 548 Symbol->NumberOfAuxSymbols = 1; 549 CurrentOffset += sizeof(coff_symbol16); 550 Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 551 CurrentOffset); 552 Aux->Length = SectionTwoSize; 553 Aux->NumberOfRelocations = 0; 554 Aux->NumberOfLinenumbers = 0; 555 Aux->CheckSum = 0; 556 Aux->NumberLowPart = 0; 557 Aux->Selection = 0; 558 CurrentOffset += sizeof(coff_aux_section_definition); 559 560 // Now write a symbol for each relocation. 561 for (unsigned i = 0; i < Data.size(); i++) { 562 auto RelocationName = formatv("$R{0:X-6}", i & 0xffffff).sstr<COFF::NameSize>(); 563 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 564 memcpy(Symbol->Name.ShortName, RelocationName.data(), (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 574 void 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 580 void 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.setNameOffset( 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 665 void 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 llvm::copy(String, 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 681 void 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 case COFF::IMAGE_FILE_MACHINE_ARM64: 703 Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB; 704 break; 705 default: 706 llvm_unreachable("unknown machine type"); 707 } 708 CurrentOffset += sizeof(coff_relocation); 709 } 710 } 711 712 Expected<std::unique_ptr<MemoryBuffer>> 713 writeWindowsResourceCOFF(COFF::MachineTypes MachineType, 714 const WindowsResourceParser &Parser) { 715 Error E = Error::success(); 716 WindowsResourceCOFFWriter Writer(MachineType, Parser, E); 717 if (E) 718 return std::move(E); 719 return Writer.write(); 720 } 721 722 } // namespace object 723 } // namespace llvm 724