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 static Error duplicateResourceError(const ResourceEntryRef& Entry, 131 StringRef File1, StringRef File2) { 132 std::string Ret; 133 raw_string_ostream OS(Ret); 134 135 OS << "duplicate resource:"; 136 137 OS << " type "; 138 if (Entry.checkTypeString()) { 139 std::string UTF8; 140 if (!convertUTF16ToUTF8String(Entry.getTypeString(), UTF8)) 141 UTF8 = "(failed conversion from UTF16)"; 142 OS << '\"' << UTF8 << '\"'; 143 } else { 144 OS << "ID " << Entry.getTypeID(); 145 } 146 147 OS << "/name "; 148 if (Entry.checkNameString()) { 149 std::string UTF8; 150 if (!convertUTF16ToUTF8String(Entry.getNameString(), UTF8)) 151 UTF8 = "(failed conversion from UTF16)"; 152 OS << '\"' << UTF8 << '\"'; 153 } else { 154 OS << "ID " << Entry.getNameID(); 155 } 156 157 OS << "/language " << Entry.getLanguage() << ", in " << File1 << " and in " 158 << File2; 159 160 return make_error<GenericBinaryError>(OS.str(), object_error::parse_failed); 161 } 162 163 Error WindowsResourceParser::parse(WindowsResource *WR) { 164 auto EntryOrErr = WR->getHeadEntry(); 165 if (!EntryOrErr) { 166 auto E = EntryOrErr.takeError(); 167 if (E.isA<EmptyResError>()) { 168 // Check if the .res file contains no entries. In this case we don't have 169 // to throw an error but can rather just return without parsing anything. 170 // This applies for files which have a valid PE header magic and the 171 // mandatory empty null resource entry. Files which do not fit this 172 // criteria would have already been filtered out by 173 // WindowsResource::createWindowsResource(). 174 consumeError(std::move(E)); 175 return Error::success(); 176 } 177 return E; 178 } 179 180 ResourceEntryRef Entry = EntryOrErr.get(); 181 bool End = false; 182 while (!End) { 183 Data.push_back(Entry.getData()); 184 185 bool IsNewTypeString = false; 186 bool IsNewNameString = false; 187 188 TreeNode* Node; 189 bool IsNewNode = Root.addEntry(Entry, InputFilenames.size(), 190 IsNewTypeString, IsNewNameString, Node); 191 InputFilenames.push_back(WR->getFileName()); 192 if (!IsNewNode) 193 return duplicateResourceError(Entry, InputFilenames[Node->Origin], 194 WR->getFileName()); 195 196 if (IsNewTypeString) 197 StringTable.push_back(Entry.getTypeString()); 198 199 if (IsNewNameString) 200 StringTable.push_back(Entry.getNameString()); 201 202 RETURN_IF_ERROR(Entry.moveNext(End)); 203 } 204 205 return Error::success(); 206 } 207 208 void WindowsResourceParser::printTree(raw_ostream &OS) const { 209 ScopedPrinter Writer(OS); 210 Root.print(Writer, "Resource Tree"); 211 } 212 213 bool WindowsResourceParser::TreeNode::addEntry(const ResourceEntryRef &Entry, 214 uint32_t Origin, 215 bool &IsNewTypeString, 216 bool &IsNewNameString, 217 TreeNode *&Result) { 218 TreeNode &TypeNode = addTypeNode(Entry, IsNewTypeString); 219 TreeNode &NameNode = TypeNode.addNameNode(Entry, IsNewNameString); 220 return NameNode.addLanguageNode(Entry, Origin, Result); 221 } 222 223 WindowsResourceParser::TreeNode::TreeNode(bool IsStringNode) { 224 if (IsStringNode) 225 StringIndex = StringCount++; 226 } 227 228 WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion, 229 uint16_t MinorVersion, 230 uint32_t Characteristics, 231 uint32_t Origin) 232 : IsDataNode(true), MajorVersion(MajorVersion), MinorVersion(MinorVersion), 233 Characteristics(Characteristics), Origin(Origin) { 234 DataIndex = DataCount++; 235 } 236 237 std::unique_ptr<WindowsResourceParser::TreeNode> 238 WindowsResourceParser::TreeNode::createStringNode() { 239 return std::unique_ptr<TreeNode>(new TreeNode(true)); 240 } 241 242 std::unique_ptr<WindowsResourceParser::TreeNode> 243 WindowsResourceParser::TreeNode::createIDNode() { 244 return std::unique_ptr<TreeNode>(new TreeNode(false)); 245 } 246 247 std::unique_ptr<WindowsResourceParser::TreeNode> 248 WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion, 249 uint16_t MinorVersion, 250 uint32_t Characteristics, 251 uint32_t Origin) { 252 return std::unique_ptr<TreeNode>( 253 new TreeNode(MajorVersion, MinorVersion, Characteristics, Origin)); 254 } 255 256 WindowsResourceParser::TreeNode & 257 WindowsResourceParser::TreeNode::addTypeNode(const ResourceEntryRef &Entry, 258 bool &IsNewTypeString) { 259 if (Entry.checkTypeString()) 260 return addNameChild(Entry.getTypeString(), IsNewTypeString); 261 else 262 return addIDChild(Entry.getTypeID()); 263 } 264 265 WindowsResourceParser::TreeNode & 266 WindowsResourceParser::TreeNode::addNameNode(const ResourceEntryRef &Entry, 267 bool &IsNewNameString) { 268 if (Entry.checkNameString()) 269 return addNameChild(Entry.getNameString(), IsNewNameString); 270 else 271 return addIDChild(Entry.getNameID()); 272 } 273 274 bool WindowsResourceParser::TreeNode::addLanguageNode( 275 const ResourceEntryRef &Entry, uint32_t Origin, TreeNode *&Result) { 276 return addDataChild(Entry.getLanguage(), Entry.getMajorVersion(), 277 Entry.getMinorVersion(), Entry.getCharacteristics(), 278 Origin, Result); 279 } 280 281 bool WindowsResourceParser::TreeNode::addDataChild( 282 uint32_t ID, uint16_t MajorVersion, uint16_t MinorVersion, 283 uint32_t Characteristics, uint32_t Origin, TreeNode *&Result) { 284 auto NewChild = 285 createDataNode(MajorVersion, MinorVersion, Characteristics, Origin); 286 auto ElementInserted = IDChildren.emplace(ID, std::move(NewChild)); 287 Result = ElementInserted.first->second.get(); 288 return ElementInserted.second; 289 } 290 291 WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addIDChild( 292 uint32_t ID) { 293 auto Child = IDChildren.find(ID); 294 if (Child == IDChildren.end()) { 295 auto NewChild = createIDNode(); 296 WindowsResourceParser::TreeNode &Node = *NewChild; 297 IDChildren.emplace(ID, std::move(NewChild)); 298 return Node; 299 } else 300 return *(Child->second); 301 } 302 303 WindowsResourceParser::TreeNode & 304 WindowsResourceParser::TreeNode::addNameChild(ArrayRef<UTF16> NameRef, 305 bool &IsNewString) { 306 std::string NameString; 307 ArrayRef<UTF16> CorrectedName; 308 std::vector<UTF16> EndianCorrectedName; 309 if (sys::IsBigEndianHost) { 310 EndianCorrectedName.resize(NameRef.size() + 1); 311 llvm::copy(NameRef, EndianCorrectedName.begin() + 1); 312 EndianCorrectedName[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED; 313 CorrectedName = makeArrayRef(EndianCorrectedName); 314 } else 315 CorrectedName = NameRef; 316 convertUTF16ToUTF8String(CorrectedName, NameString); 317 318 auto Child = StringChildren.find(NameString); 319 if (Child == StringChildren.end()) { 320 auto NewChild = createStringNode(); 321 IsNewString = true; 322 WindowsResourceParser::TreeNode &Node = *NewChild; 323 StringChildren.emplace(NameString, std::move(NewChild)); 324 return Node; 325 } else 326 return *(Child->second); 327 } 328 329 void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer, 330 StringRef Name) const { 331 ListScope NodeScope(Writer, Name); 332 for (auto const &Child : StringChildren) { 333 Child.second->print(Writer, Child.first); 334 } 335 for (auto const &Child : IDChildren) { 336 Child.second->print(Writer, to_string(Child.first)); 337 } 338 } 339 340 // This function returns the size of the entire resource tree, including 341 // directory tables, directory entries, and data entries. It does not include 342 // the directory strings or the relocations of the .rsrc section. 343 uint32_t WindowsResourceParser::TreeNode::getTreeSize() const { 344 uint32_t Size = (IDChildren.size() + StringChildren.size()) * 345 sizeof(coff_resource_dir_entry); 346 347 // Reached a node pointing to a data entry. 348 if (IsDataNode) { 349 Size += sizeof(coff_resource_data_entry); 350 return Size; 351 } 352 353 // If the node does not point to data, it must have a directory table pointing 354 // to other nodes. 355 Size += sizeof(coff_resource_dir_table); 356 357 for (auto const &Child : StringChildren) { 358 Size += Child.second->getTreeSize(); 359 } 360 for (auto const &Child : IDChildren) { 361 Size += Child.second->getTreeSize(); 362 } 363 return Size; 364 } 365 366 class WindowsResourceCOFFWriter { 367 public: 368 WindowsResourceCOFFWriter(COFF::MachineTypes MachineType, 369 const WindowsResourceParser &Parser, Error &E); 370 std::unique_ptr<MemoryBuffer> write(); 371 372 private: 373 void performFileLayout(); 374 void performSectionOneLayout(); 375 void performSectionTwoLayout(); 376 void writeCOFFHeader(); 377 void writeFirstSectionHeader(); 378 void writeSecondSectionHeader(); 379 void writeFirstSection(); 380 void writeSecondSection(); 381 void writeSymbolTable(); 382 void writeStringTable(); 383 void writeDirectoryTree(); 384 void writeDirectoryStringTable(); 385 void writeFirstSectionRelocations(); 386 std::unique_ptr<WritableMemoryBuffer> OutputBuffer; 387 char *BufferStart; 388 uint64_t CurrentOffset = 0; 389 COFF::MachineTypes MachineType; 390 const WindowsResourceParser::TreeNode &Resources; 391 const ArrayRef<std::vector<uint8_t>> Data; 392 uint64_t FileSize; 393 uint32_t SymbolTableOffset; 394 uint32_t SectionOneSize; 395 uint32_t SectionOneOffset; 396 uint32_t SectionOneRelocations; 397 uint32_t SectionTwoSize; 398 uint32_t SectionTwoOffset; 399 const ArrayRef<std::vector<UTF16>> StringTable; 400 std::vector<uint32_t> StringTableOffsets; 401 std::vector<uint32_t> DataOffsets; 402 std::vector<uint32_t> RelocationAddresses; 403 }; 404 405 WindowsResourceCOFFWriter::WindowsResourceCOFFWriter( 406 COFF::MachineTypes MachineType, const WindowsResourceParser &Parser, 407 Error &E) 408 : MachineType(MachineType), Resources(Parser.getTree()), 409 Data(Parser.getData()), StringTable(Parser.getStringTable()) { 410 performFileLayout(); 411 412 OutputBuffer = WritableMemoryBuffer::getNewMemBuffer(FileSize); 413 } 414 415 void WindowsResourceCOFFWriter::performFileLayout() { 416 // Add size of COFF header. 417 FileSize = COFF::Header16Size; 418 419 // one .rsrc section header for directory tree, another for resource data. 420 FileSize += 2 * COFF::SectionSize; 421 422 performSectionOneLayout(); 423 performSectionTwoLayout(); 424 425 // We have reached the address of the symbol table. 426 SymbolTableOffset = FileSize; 427 428 FileSize += COFF::Symbol16Size; // size of the @feat.00 symbol. 429 FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section. 430 FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource. 431 FileSize += 4; // four null bytes for the string table. 432 } 433 434 void WindowsResourceCOFFWriter::performSectionOneLayout() { 435 SectionOneOffset = FileSize; 436 437 SectionOneSize = Resources.getTreeSize(); 438 uint32_t CurrentStringOffset = SectionOneSize; 439 uint32_t TotalStringTableSize = 0; 440 for (auto const &String : StringTable) { 441 StringTableOffsets.push_back(CurrentStringOffset); 442 uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t); 443 CurrentStringOffset += StringSize; 444 TotalStringTableSize += StringSize; 445 } 446 SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t)); 447 448 // account for the relocations of section one. 449 SectionOneRelocations = FileSize + SectionOneSize; 450 FileSize += SectionOneSize; 451 FileSize += 452 Data.size() * COFF::RelocationSize; // one relocation for each resource. 453 FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 454 } 455 456 void WindowsResourceCOFFWriter::performSectionTwoLayout() { 457 // add size of .rsrc$2 section, which contains all resource data on 8-byte 458 // alignment. 459 SectionTwoOffset = FileSize; 460 SectionTwoSize = 0; 461 for (auto const &Entry : Data) { 462 DataOffsets.push_back(SectionTwoSize); 463 SectionTwoSize += alignTo(Entry.size(), sizeof(uint64_t)); 464 } 465 FileSize += SectionTwoSize; 466 FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 467 } 468 469 static std::time_t getTime() { 470 std::time_t Now = time(nullptr); 471 if (Now < 0 || !isUInt<32>(Now)) 472 return UINT32_MAX; 473 return Now; 474 } 475 476 std::unique_ptr<MemoryBuffer> WindowsResourceCOFFWriter::write() { 477 BufferStart = OutputBuffer->getBufferStart(); 478 479 writeCOFFHeader(); 480 writeFirstSectionHeader(); 481 writeSecondSectionHeader(); 482 writeFirstSection(); 483 writeSecondSection(); 484 writeSymbolTable(); 485 writeStringTable(); 486 487 return std::move(OutputBuffer); 488 } 489 490 void WindowsResourceCOFFWriter::writeCOFFHeader() { 491 // Write the COFF header. 492 auto *Header = reinterpret_cast<coff_file_header *>(BufferStart); 493 Header->Machine = MachineType; 494 Header->NumberOfSections = 2; 495 Header->TimeDateStamp = getTime(); 496 Header->PointerToSymbolTable = SymbolTableOffset; 497 // One symbol for every resource plus 2 for each section and @feat.00 498 Header->NumberOfSymbols = Data.size() + 5; 499 Header->SizeOfOptionalHeader = 0; 500 Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE; 501 } 502 503 void WindowsResourceCOFFWriter::writeFirstSectionHeader() { 504 // Write the first section header. 505 CurrentOffset += sizeof(coff_file_header); 506 auto *SectionOneHeader = 507 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 508 strncpy(SectionOneHeader->Name, ".rsrc$01", (size_t)COFF::NameSize); 509 SectionOneHeader->VirtualSize = 0; 510 SectionOneHeader->VirtualAddress = 0; 511 SectionOneHeader->SizeOfRawData = SectionOneSize; 512 SectionOneHeader->PointerToRawData = SectionOneOffset; 513 SectionOneHeader->PointerToRelocations = SectionOneRelocations; 514 SectionOneHeader->PointerToLinenumbers = 0; 515 SectionOneHeader->NumberOfRelocations = Data.size(); 516 SectionOneHeader->NumberOfLinenumbers = 0; 517 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 518 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 519 } 520 521 void WindowsResourceCOFFWriter::writeSecondSectionHeader() { 522 // Write the second section header. 523 CurrentOffset += sizeof(coff_section); 524 auto *SectionTwoHeader = 525 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 526 strncpy(SectionTwoHeader->Name, ".rsrc$02", (size_t)COFF::NameSize); 527 SectionTwoHeader->VirtualSize = 0; 528 SectionTwoHeader->VirtualAddress = 0; 529 SectionTwoHeader->SizeOfRawData = SectionTwoSize; 530 SectionTwoHeader->PointerToRawData = SectionTwoOffset; 531 SectionTwoHeader->PointerToRelocations = 0; 532 SectionTwoHeader->PointerToLinenumbers = 0; 533 SectionTwoHeader->NumberOfRelocations = 0; 534 SectionTwoHeader->NumberOfLinenumbers = 0; 535 SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 536 SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 537 } 538 539 void WindowsResourceCOFFWriter::writeFirstSection() { 540 // Write section one. 541 CurrentOffset += sizeof(coff_section); 542 543 writeDirectoryTree(); 544 writeDirectoryStringTable(); 545 writeFirstSectionRelocations(); 546 547 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 548 } 549 550 void WindowsResourceCOFFWriter::writeSecondSection() { 551 // Now write the .rsrc$02 section. 552 for (auto const &RawDataEntry : Data) { 553 llvm::copy(RawDataEntry, BufferStart + CurrentOffset); 554 CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t)); 555 } 556 557 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 558 } 559 560 void WindowsResourceCOFFWriter::writeSymbolTable() { 561 // Now write the symbol table. 562 // First, the feat symbol. 563 auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 564 strncpy(Symbol->Name.ShortName, "@feat.00", (size_t)COFF::NameSize); 565 Symbol->Value = 0x11; 566 Symbol->SectionNumber = 0xffff; 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 // Now write the .rsrc1 symbol + aux. 573 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 574 strncpy(Symbol->Name.ShortName, ".rsrc$01", (size_t)COFF::NameSize); 575 Symbol->Value = 0; 576 Symbol->SectionNumber = 1; 577 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 578 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 579 Symbol->NumberOfAuxSymbols = 1; 580 CurrentOffset += sizeof(coff_symbol16); 581 auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 582 CurrentOffset); 583 Aux->Length = SectionOneSize; 584 Aux->NumberOfRelocations = Data.size(); 585 Aux->NumberOfLinenumbers = 0; 586 Aux->CheckSum = 0; 587 Aux->NumberLowPart = 0; 588 Aux->Selection = 0; 589 CurrentOffset += sizeof(coff_aux_section_definition); 590 591 // Now write the .rsrc2 symbol + aux. 592 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 593 strncpy(Symbol->Name.ShortName, ".rsrc$02", (size_t)COFF::NameSize); 594 Symbol->Value = 0; 595 Symbol->SectionNumber = 2; 596 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 597 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 598 Symbol->NumberOfAuxSymbols = 1; 599 CurrentOffset += sizeof(coff_symbol16); 600 Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 601 CurrentOffset); 602 Aux->Length = SectionTwoSize; 603 Aux->NumberOfRelocations = 0; 604 Aux->NumberOfLinenumbers = 0; 605 Aux->CheckSum = 0; 606 Aux->NumberLowPart = 0; 607 Aux->Selection = 0; 608 CurrentOffset += sizeof(coff_aux_section_definition); 609 610 // Now write a symbol for each relocation. 611 for (unsigned i = 0; i < Data.size(); i++) { 612 auto RelocationName = formatv("$R{0:X-6}", i & 0xffffff).sstr<COFF::NameSize>(); 613 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 614 memcpy(Symbol->Name.ShortName, RelocationName.data(), (size_t) COFF::NameSize); 615 Symbol->Value = DataOffsets[i]; 616 Symbol->SectionNumber = 2; 617 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 618 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 619 Symbol->NumberOfAuxSymbols = 0; 620 CurrentOffset += sizeof(coff_symbol16); 621 } 622 } 623 624 void WindowsResourceCOFFWriter::writeStringTable() { 625 // Just 4 null bytes for the string table. 626 auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset); 627 memset(COFFStringTable, 0, 4); 628 } 629 630 void WindowsResourceCOFFWriter::writeDirectoryTree() { 631 // Traverse parsed resource tree breadth-first and write the corresponding 632 // COFF objects. 633 std::queue<const WindowsResourceParser::TreeNode *> Queue; 634 Queue.push(&Resources); 635 uint32_t NextLevelOffset = 636 sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() + 637 Resources.getIDChildren().size()) * 638 sizeof(coff_resource_dir_entry); 639 std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder; 640 uint32_t CurrentRelativeOffset = 0; 641 642 while (!Queue.empty()) { 643 auto CurrentNode = Queue.front(); 644 Queue.pop(); 645 auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart + 646 CurrentOffset); 647 Table->Characteristics = CurrentNode->getCharacteristics(); 648 Table->TimeDateStamp = 0; 649 Table->MajorVersion = CurrentNode->getMajorVersion(); 650 Table->MinorVersion = CurrentNode->getMinorVersion(); 651 auto &IDChildren = CurrentNode->getIDChildren(); 652 auto &StringChildren = CurrentNode->getStringChildren(); 653 Table->NumberOfNameEntries = StringChildren.size(); 654 Table->NumberOfIDEntries = IDChildren.size(); 655 CurrentOffset += sizeof(coff_resource_dir_table); 656 CurrentRelativeOffset += sizeof(coff_resource_dir_table); 657 658 // Write the directory entries immediately following each directory table. 659 for (auto const &Child : StringChildren) { 660 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 661 CurrentOffset); 662 Entry->Identifier.setNameOffset( 663 StringTableOffsets[Child.second->getStringIndex()]); 664 if (Child.second->checkIsDataNode()) { 665 Entry->Offset.DataEntryOffset = NextLevelOffset; 666 NextLevelOffset += sizeof(coff_resource_data_entry); 667 DataEntriesTreeOrder.push_back(Child.second.get()); 668 } else { 669 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 670 NextLevelOffset += sizeof(coff_resource_dir_table) + 671 (Child.second->getStringChildren().size() + 672 Child.second->getIDChildren().size()) * 673 sizeof(coff_resource_dir_entry); 674 Queue.push(Child.second.get()); 675 } 676 CurrentOffset += sizeof(coff_resource_dir_entry); 677 CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 678 } 679 for (auto const &Child : IDChildren) { 680 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 681 CurrentOffset); 682 Entry->Identifier.ID = Child.first; 683 if (Child.second->checkIsDataNode()) { 684 Entry->Offset.DataEntryOffset = NextLevelOffset; 685 NextLevelOffset += sizeof(coff_resource_data_entry); 686 DataEntriesTreeOrder.push_back(Child.second.get()); 687 } else { 688 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 689 NextLevelOffset += sizeof(coff_resource_dir_table) + 690 (Child.second->getStringChildren().size() + 691 Child.second->getIDChildren().size()) * 692 sizeof(coff_resource_dir_entry); 693 Queue.push(Child.second.get()); 694 } 695 CurrentOffset += sizeof(coff_resource_dir_entry); 696 CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 697 } 698 } 699 700 RelocationAddresses.resize(Data.size()); 701 // Now write all the resource data entries. 702 for (auto DataNodes : DataEntriesTreeOrder) { 703 auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart + 704 CurrentOffset); 705 RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset; 706 Entry->DataRVA = 0; // Set to zero because it is a relocation. 707 Entry->DataSize = Data[DataNodes->getDataIndex()].size(); 708 Entry->Codepage = 0; 709 Entry->Reserved = 0; 710 CurrentOffset += sizeof(coff_resource_data_entry); 711 CurrentRelativeOffset += sizeof(coff_resource_data_entry); 712 } 713 } 714 715 void WindowsResourceCOFFWriter::writeDirectoryStringTable() { 716 // Now write the directory string table for .rsrc$01 717 uint32_t TotalStringTableSize = 0; 718 for (auto &String : StringTable) { 719 uint16_t Length = String.size(); 720 support::endian::write16le(BufferStart + CurrentOffset, Length); 721 CurrentOffset += sizeof(uint16_t); 722 auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset); 723 llvm::copy(String, Start); 724 CurrentOffset += Length * sizeof(UTF16); 725 TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t); 726 } 727 CurrentOffset += 728 alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize; 729 } 730 731 void WindowsResourceCOFFWriter::writeFirstSectionRelocations() { 732 733 // Now write the relocations for .rsrc$01 734 // Five symbols already in table before we start, @feat.00 and 2 for each 735 // .rsrc section. 736 uint32_t NextSymbolIndex = 5; 737 for (unsigned i = 0; i < Data.size(); i++) { 738 auto *Reloc = 739 reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset); 740 Reloc->VirtualAddress = RelocationAddresses[i]; 741 Reloc->SymbolTableIndex = NextSymbolIndex++; 742 switch (MachineType) { 743 case COFF::IMAGE_FILE_MACHINE_ARMNT: 744 Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB; 745 break; 746 case COFF::IMAGE_FILE_MACHINE_AMD64: 747 Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB; 748 break; 749 case COFF::IMAGE_FILE_MACHINE_I386: 750 Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB; 751 break; 752 case COFF::IMAGE_FILE_MACHINE_ARM64: 753 Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB; 754 break; 755 default: 756 llvm_unreachable("unknown machine type"); 757 } 758 CurrentOffset += sizeof(coff_relocation); 759 } 760 } 761 762 Expected<std::unique_ptr<MemoryBuffer>> 763 writeWindowsResourceCOFF(COFF::MachineTypes MachineType, 764 const WindowsResourceParser &Parser) { 765 Error E = Error::success(); 766 WindowsResourceCOFFWriter Writer(MachineType, Parser, E); 767 if (E) 768 return std::move(E); 769 return Writer.write(); 770 } 771 772 } // namespace object 773 } // namespace llvm 774