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