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 <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 std::copy(NameRef.begin(), NameRef.end(), 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<MemoryBuffer> 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 = MemoryBuffer::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 = const_cast<char *>(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 std::copy(RawDataEntry.begin(), RawDataEntry.end(), 504 BufferStart + CurrentOffset); 505 CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t)); 506 } 507 508 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 509 } 510 511 void WindowsResourceCOFFWriter::writeSymbolTable() { 512 // Now write the symbol table. 513 // First, the feat symbol. 514 auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 515 strncpy(Symbol->Name.ShortName, "@feat.00", (size_t)COFF::NameSize); 516 Symbol->Value = 0x11; 517 Symbol->SectionNumber = 0xffff; 518 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 519 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 520 Symbol->NumberOfAuxSymbols = 0; 521 CurrentOffset += sizeof(coff_symbol16); 522 523 // Now write the .rsrc1 symbol + aux. 524 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 525 strncpy(Symbol->Name.ShortName, ".rsrc$01", (size_t)COFF::NameSize); 526 Symbol->Value = 0; 527 Symbol->SectionNumber = 1; 528 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 529 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 530 Symbol->NumberOfAuxSymbols = 1; 531 CurrentOffset += sizeof(coff_symbol16); 532 auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 533 CurrentOffset); 534 Aux->Length = SectionOneSize; 535 Aux->NumberOfRelocations = Data.size(); 536 Aux->NumberOfLinenumbers = 0; 537 Aux->CheckSum = 0; 538 Aux->NumberLowPart = 0; 539 Aux->Selection = 0; 540 CurrentOffset += sizeof(coff_aux_section_definition); 541 542 // Now write the .rsrc2 symbol + aux. 543 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 544 strncpy(Symbol->Name.ShortName, ".rsrc$02", (size_t)COFF::NameSize); 545 Symbol->Value = 0; 546 Symbol->SectionNumber = 2; 547 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 548 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 549 Symbol->NumberOfAuxSymbols = 1; 550 CurrentOffset += sizeof(coff_symbol16); 551 Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 552 CurrentOffset); 553 Aux->Length = SectionTwoSize; 554 Aux->NumberOfRelocations = 0; 555 Aux->NumberOfLinenumbers = 0; 556 Aux->CheckSum = 0; 557 Aux->NumberLowPart = 0; 558 Aux->Selection = 0; 559 CurrentOffset += sizeof(coff_aux_section_definition); 560 561 // Now write a symbol for each relocation. 562 for (unsigned i = 0; i < Data.size(); i++) { 563 char RelocationName[9]; 564 sprintf(RelocationName, "$R%06X", DataOffsets[i]); 565 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 566 strncpy(Symbol->Name.ShortName, RelocationName, (size_t)COFF::NameSize); 567 Symbol->Value = DataOffsets[i]; 568 Symbol->SectionNumber = 2; 569 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 570 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 571 Symbol->NumberOfAuxSymbols = 0; 572 CurrentOffset += sizeof(coff_symbol16); 573 } 574 } 575 576 void WindowsResourceCOFFWriter::writeStringTable() { 577 // Just 4 null bytes for the string table. 578 auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset); 579 memset(COFFStringTable, 0, 4); 580 } 581 582 void WindowsResourceCOFFWriter::writeDirectoryTree() { 583 // Traverse parsed resource tree breadth-first and write the corresponding 584 // COFF objects. 585 std::queue<const WindowsResourceParser::TreeNode *> Queue; 586 Queue.push(&Resources); 587 uint32_t NextLevelOffset = 588 sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() + 589 Resources.getIDChildren().size()) * 590 sizeof(coff_resource_dir_entry); 591 std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder; 592 uint32_t CurrentRelativeOffset = 0; 593 594 while (!Queue.empty()) { 595 auto CurrentNode = Queue.front(); 596 Queue.pop(); 597 auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart + 598 CurrentOffset); 599 Table->Characteristics = CurrentNode->getCharacteristics(); 600 Table->TimeDateStamp = 0; 601 Table->MajorVersion = CurrentNode->getMajorVersion(); 602 Table->MinorVersion = CurrentNode->getMinorVersion(); 603 auto &IDChildren = CurrentNode->getIDChildren(); 604 auto &StringChildren = CurrentNode->getStringChildren(); 605 Table->NumberOfNameEntries = StringChildren.size(); 606 Table->NumberOfIDEntries = IDChildren.size(); 607 CurrentOffset += sizeof(coff_resource_dir_table); 608 CurrentRelativeOffset += sizeof(coff_resource_dir_table); 609 610 // Write the directory entries immediately following each directory table. 611 for (auto const &Child : StringChildren) { 612 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 613 CurrentOffset); 614 Entry->Identifier.setNameOffset( 615 StringTableOffsets[Child.second->getStringIndex()]); 616 if (Child.second->checkIsDataNode()) { 617 Entry->Offset.DataEntryOffset = NextLevelOffset; 618 NextLevelOffset += sizeof(coff_resource_data_entry); 619 DataEntriesTreeOrder.push_back(Child.second.get()); 620 } else { 621 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 622 NextLevelOffset += sizeof(coff_resource_dir_table) + 623 (Child.second->getStringChildren().size() + 624 Child.second->getIDChildren().size()) * 625 sizeof(coff_resource_dir_entry); 626 Queue.push(Child.second.get()); 627 } 628 CurrentOffset += sizeof(coff_resource_dir_entry); 629 CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 630 } 631 for (auto const &Child : IDChildren) { 632 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 633 CurrentOffset); 634 Entry->Identifier.ID = Child.first; 635 if (Child.second->checkIsDataNode()) { 636 Entry->Offset.DataEntryOffset = NextLevelOffset; 637 NextLevelOffset += sizeof(coff_resource_data_entry); 638 DataEntriesTreeOrder.push_back(Child.second.get()); 639 } else { 640 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 641 NextLevelOffset += sizeof(coff_resource_dir_table) + 642 (Child.second->getStringChildren().size() + 643 Child.second->getIDChildren().size()) * 644 sizeof(coff_resource_dir_entry); 645 Queue.push(Child.second.get()); 646 } 647 CurrentOffset += sizeof(coff_resource_dir_entry); 648 CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 649 } 650 } 651 652 RelocationAddresses.resize(Data.size()); 653 // Now write all the resource data entries. 654 for (auto DataNodes : DataEntriesTreeOrder) { 655 auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart + 656 CurrentOffset); 657 RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset; 658 Entry->DataRVA = 0; // Set to zero because it is a relocation. 659 Entry->DataSize = Data[DataNodes->getDataIndex()].size(); 660 Entry->Codepage = 0; 661 Entry->Reserved = 0; 662 CurrentOffset += sizeof(coff_resource_data_entry); 663 CurrentRelativeOffset += sizeof(coff_resource_data_entry); 664 } 665 } 666 667 void WindowsResourceCOFFWriter::writeDirectoryStringTable() { 668 // Now write the directory string table for .rsrc$01 669 uint32_t TotalStringTableSize = 0; 670 for (auto &String : StringTable) { 671 uint16_t Length = String.size(); 672 support::endian::write16le(BufferStart + CurrentOffset, Length); 673 CurrentOffset += sizeof(uint16_t); 674 auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset); 675 std::copy(String.begin(), String.end(), Start); 676 CurrentOffset += Length * sizeof(UTF16); 677 TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t); 678 } 679 CurrentOffset += 680 alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize; 681 } 682 683 void WindowsResourceCOFFWriter::writeFirstSectionRelocations() { 684 685 // Now write the relocations for .rsrc$01 686 // Five symbols already in table before we start, @feat.00 and 2 for each 687 // .rsrc section. 688 uint32_t NextSymbolIndex = 5; 689 for (unsigned i = 0; i < Data.size(); i++) { 690 auto *Reloc = 691 reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset); 692 Reloc->VirtualAddress = RelocationAddresses[i]; 693 Reloc->SymbolTableIndex = NextSymbolIndex++; 694 switch (MachineType) { 695 case COFF::IMAGE_FILE_MACHINE_ARMNT: 696 Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB; 697 break; 698 case COFF::IMAGE_FILE_MACHINE_AMD64: 699 Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB; 700 break; 701 case COFF::IMAGE_FILE_MACHINE_I386: 702 Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB; 703 break; 704 case COFF::IMAGE_FILE_MACHINE_ARM64: 705 Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB; 706 break; 707 default: 708 llvm_unreachable("unknown machine type"); 709 } 710 CurrentOffset += sizeof(coff_relocation); 711 } 712 } 713 714 Expected<std::unique_ptr<MemoryBuffer>> 715 writeWindowsResourceCOFF(COFF::MachineTypes MachineType, 716 const WindowsResourceParser &Parser) { 717 Error E = Error::success(); 718 WindowsResourceCOFFWriter Writer(MachineType, Parser, E); 719 if (E) 720 return std::move(E); 721 return Writer.write(); 722 } 723 724 } // namespace object 725 } // namespace llvm 726