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