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