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/Object/WindowsMachineFlag.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 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 #define UNWRAP_REF_OR_RETURN(Name, Expr) \ 33 auto Name##OrErr = Expr; \ 34 if (!Name##OrErr) \ 35 return Name##OrErr.takeError(); \ 36 const auto &Name = *Name##OrErr; 37 38 #define UNWRAP_OR_RETURN(Name, Expr) \ 39 auto Name##OrErr = Expr; \ 40 if (!Name##OrErr) \ 41 return Name##OrErr.takeError(); \ 42 auto Name = *Name##OrErr; 43 44 const uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t); 45 46 // COFF files seem to be inconsistent with alignment between sections, just use 47 // 8-byte because it makes everyone happy. 48 const uint32_t SECTION_ALIGNMENT = sizeof(uint64_t); 49 50 WindowsResource::WindowsResource(MemoryBufferRef Source) 51 : Binary(Binary::ID_WinRes, Source) { 52 size_t LeadingSize = WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE; 53 BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize), 54 llvm::endianness::little); 55 } 56 57 // static 58 Expected<std::unique_ptr<WindowsResource>> 59 WindowsResource::createWindowsResource(MemoryBufferRef Source) { 60 if (Source.getBufferSize() < WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE) 61 return make_error<GenericBinaryError>( 62 Source.getBufferIdentifier() + ": too small to be a resource file", 63 object_error::invalid_file_type); 64 std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source)); 65 return std::move(Ret); 66 } 67 68 Expected<ResourceEntryRef> WindowsResource::getHeadEntry() { 69 if (BBS.getLength() < sizeof(WinResHeaderPrefix) + sizeof(WinResHeaderSuffix)) 70 return make_error<EmptyResError>(getFileName() + " contains no entries", 71 object_error::unexpected_eof); 72 return ResourceEntryRef::create(BinaryStreamRef(BBS), this); 73 } 74 75 ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref, 76 const WindowsResource *Owner) 77 : Reader(Ref), Owner(Owner) {} 78 79 Expected<ResourceEntryRef> 80 ResourceEntryRef::create(BinaryStreamRef BSR, const WindowsResource *Owner) { 81 auto Ref = ResourceEntryRef(BSR, Owner); 82 if (auto E = Ref.loadNext()) 83 return E; 84 return Ref; 85 } 86 87 Error ResourceEntryRef::moveNext(bool &End) { 88 // Reached end of all the entries. 89 if (Reader.bytesRemaining() == 0) { 90 End = true; 91 return Error::success(); 92 } 93 RETURN_IF_ERROR(loadNext()); 94 95 return Error::success(); 96 } 97 98 static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID, 99 ArrayRef<UTF16> &Str, bool &IsString) { 100 uint16_t IDFlag; 101 RETURN_IF_ERROR(Reader.readInteger(IDFlag)); 102 IsString = IDFlag != 0xffff; 103 104 if (IsString) { 105 Reader.setOffset( 106 Reader.getOffset() - 107 sizeof(uint16_t)); // Re-read the bytes which we used to check the flag. 108 RETURN_IF_ERROR(Reader.readWideString(Str)); 109 } else 110 RETURN_IF_ERROR(Reader.readInteger(ID)); 111 112 return Error::success(); 113 } 114 115 Error ResourceEntryRef::loadNext() { 116 const WinResHeaderPrefix *Prefix; 117 RETURN_IF_ERROR(Reader.readObject(Prefix)); 118 119 if (Prefix->HeaderSize < MIN_HEADER_SIZE) 120 return make_error<GenericBinaryError>(Owner->getFileName() + 121 ": header size too small", 122 object_error::parse_failed); 123 124 RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType)); 125 126 RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName)); 127 128 RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_HEADER_ALIGNMENT)); 129 130 RETURN_IF_ERROR(Reader.readObject(Suffix)); 131 132 RETURN_IF_ERROR(Reader.readArray(Data, Prefix->DataSize)); 133 134 RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_DATA_ALIGNMENT)); 135 136 return Error::success(); 137 } 138 139 WindowsResourceParser::WindowsResourceParser(bool MinGW) 140 : Root(false), MinGW(MinGW) {} 141 142 void printResourceTypeName(uint16_t TypeID, raw_ostream &OS) { 143 switch (TypeID) { 144 case 1: OS << "CURSOR (ID 1)"; break; 145 case 2: OS << "BITMAP (ID 2)"; break; 146 case 3: OS << "ICON (ID 3)"; break; 147 case 4: OS << "MENU (ID 4)"; break; 148 case 5: OS << "DIALOG (ID 5)"; break; 149 case 6: OS << "STRINGTABLE (ID 6)"; break; 150 case 7: OS << "FONTDIR (ID 7)"; break; 151 case 8: OS << "FONT (ID 8)"; break; 152 case 9: OS << "ACCELERATOR (ID 9)"; break; 153 case 10: OS << "RCDATA (ID 10)"; break; 154 case 11: OS << "MESSAGETABLE (ID 11)"; break; 155 case 12: OS << "GROUP_CURSOR (ID 12)"; break; 156 case 14: OS << "GROUP_ICON (ID 14)"; break; 157 case 16: OS << "VERSIONINFO (ID 16)"; break; 158 case 17: OS << "DLGINCLUDE (ID 17)"; break; 159 case 19: OS << "PLUGPLAY (ID 19)"; break; 160 case 20: OS << "VXD (ID 20)"; break; 161 case 21: OS << "ANICURSOR (ID 21)"; break; 162 case 22: OS << "ANIICON (ID 22)"; break; 163 case 23: OS << "HTML (ID 23)"; break; 164 case 24: OS << "MANIFEST (ID 24)"; break; 165 default: OS << "ID " << TypeID; break; 166 } 167 } 168 169 static bool convertUTF16LEToUTF8String(ArrayRef<UTF16> Src, std::string &Out) { 170 if (!sys::IsBigEndianHost) 171 return convertUTF16ToUTF8String(Src, Out); 172 173 std::vector<UTF16> EndianCorrectedSrc; 174 EndianCorrectedSrc.resize(Src.size() + 1); 175 llvm::copy(Src, EndianCorrectedSrc.begin() + 1); 176 EndianCorrectedSrc[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED; 177 return convertUTF16ToUTF8String(ArrayRef(EndianCorrectedSrc), Out); 178 } 179 180 static std::string makeDuplicateResourceError( 181 const ResourceEntryRef &Entry, StringRef File1, StringRef File2) { 182 std::string Ret; 183 raw_string_ostream OS(Ret); 184 185 OS << "duplicate resource:"; 186 187 OS << " type "; 188 if (Entry.checkTypeString()) { 189 std::string UTF8; 190 if (!convertUTF16LEToUTF8String(Entry.getTypeString(), UTF8)) 191 UTF8 = "(failed conversion from UTF16)"; 192 OS << '\"' << UTF8 << '\"'; 193 } else 194 printResourceTypeName(Entry.getTypeID(), OS); 195 196 OS << "/name "; 197 if (Entry.checkNameString()) { 198 std::string UTF8; 199 if (!convertUTF16LEToUTF8String(Entry.getNameString(), UTF8)) 200 UTF8 = "(failed conversion from UTF16)"; 201 OS << '\"' << UTF8 << '\"'; 202 } else { 203 OS << "ID " << Entry.getNameID(); 204 } 205 206 OS << "/language " << Entry.getLanguage() << ", in " << File1 << " and in " 207 << File2; 208 209 return OS.str(); 210 } 211 212 static void printStringOrID(const WindowsResourceParser::StringOrID &S, 213 raw_string_ostream &OS, bool IsType, bool IsID) { 214 if (S.IsString) { 215 std::string UTF8; 216 if (!convertUTF16LEToUTF8String(S.String, UTF8)) 217 UTF8 = "(failed conversion from UTF16)"; 218 OS << '\"' << UTF8 << '\"'; 219 } else if (IsType) 220 printResourceTypeName(S.ID, OS); 221 else if (IsID) 222 OS << "ID " << S.ID; 223 else 224 OS << S.ID; 225 } 226 227 static std::string makeDuplicateResourceError( 228 const std::vector<WindowsResourceParser::StringOrID> &Context, 229 StringRef File1, StringRef File2) { 230 std::string Ret; 231 raw_string_ostream OS(Ret); 232 233 OS << "duplicate resource:"; 234 235 if (Context.size() >= 1) { 236 OS << " type "; 237 printStringOrID(Context[0], OS, /* IsType */ true, /* IsID */ true); 238 } 239 240 if (Context.size() >= 2) { 241 OS << "/name "; 242 printStringOrID(Context[1], OS, /* IsType */ false, /* IsID */ true); 243 } 244 245 if (Context.size() >= 3) { 246 OS << "/language "; 247 printStringOrID(Context[2], OS, /* IsType */ false, /* IsID */ false); 248 } 249 OS << ", in " << File1 << " and in " << File2; 250 251 return OS.str(); 252 } 253 254 // MinGW specific. Remove default manifests (with language zero) if there are 255 // other manifests present, and report an error if there are more than one 256 // manifest with a non-zero language code. 257 // GCC has the concept of a default manifest resource object, which gets 258 // linked in implicitly if present. This default manifest has got language 259 // id zero, and should be dropped silently if there's another manifest present. 260 // If the user resources surprisignly had a manifest with language id zero, 261 // we should also ignore the duplicate default manifest. 262 void WindowsResourceParser::cleanUpManifests( 263 std::vector<std::string> &Duplicates) { 264 auto TypeIt = Root.IDChildren.find(/* RT_MANIFEST */ 24); 265 if (TypeIt == Root.IDChildren.end()) 266 return; 267 268 TreeNode *TypeNode = TypeIt->second.get(); 269 auto NameIt = 270 TypeNode->IDChildren.find(/* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1); 271 if (NameIt == TypeNode->IDChildren.end()) 272 return; 273 274 TreeNode *NameNode = NameIt->second.get(); 275 if (NameNode->IDChildren.size() <= 1) 276 return; // None or one manifest present, all good. 277 278 // If we have more than one manifest, drop the language zero one if present, 279 // and check again. 280 auto LangZeroIt = NameNode->IDChildren.find(0); 281 if (LangZeroIt != NameNode->IDChildren.end() && 282 LangZeroIt->second->IsDataNode) { 283 uint32_t RemovedIndex = LangZeroIt->second->DataIndex; 284 NameNode->IDChildren.erase(LangZeroIt); 285 Data.erase(Data.begin() + RemovedIndex); 286 Root.shiftDataIndexDown(RemovedIndex); 287 288 // If we're now down to one manifest, all is good. 289 if (NameNode->IDChildren.size() <= 1) 290 return; 291 } 292 293 // More than one non-language-zero manifest 294 auto FirstIt = NameNode->IDChildren.begin(); 295 uint32_t FirstLang = FirstIt->first; 296 TreeNode *FirstNode = FirstIt->second.get(); 297 auto LastIt = NameNode->IDChildren.rbegin(); 298 uint32_t LastLang = LastIt->first; 299 TreeNode *LastNode = LastIt->second.get(); 300 Duplicates.push_back( 301 ("duplicate non-default manifests with languages " + Twine(FirstLang) + 302 " in " + InputFilenames[FirstNode->Origin] + " and " + Twine(LastLang) + 303 " in " + InputFilenames[LastNode->Origin]) 304 .str()); 305 } 306 307 // Ignore duplicates of manifests with language zero (the default manifest), 308 // in case the user has provided a manifest with that language id. See 309 // the function comment above for context. Only returns true if MinGW is set 310 // to true. 311 bool WindowsResourceParser::shouldIgnoreDuplicate( 312 const ResourceEntryRef &Entry) const { 313 return MinGW && !Entry.checkTypeString() && 314 Entry.getTypeID() == /* RT_MANIFEST */ 24 && 315 !Entry.checkNameString() && 316 Entry.getNameID() == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 && 317 Entry.getLanguage() == 0; 318 } 319 320 bool WindowsResourceParser::shouldIgnoreDuplicate( 321 const std::vector<StringOrID> &Context) const { 322 return MinGW && Context.size() == 3 && !Context[0].IsString && 323 Context[0].ID == /* RT_MANIFEST */ 24 && !Context[1].IsString && 324 Context[1].ID == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 && 325 !Context[2].IsString && Context[2].ID == 0; 326 } 327 328 Error WindowsResourceParser::parse(WindowsResource *WR, 329 std::vector<std::string> &Duplicates) { 330 auto EntryOrErr = WR->getHeadEntry(); 331 if (!EntryOrErr) { 332 auto E = EntryOrErr.takeError(); 333 if (E.isA<EmptyResError>()) { 334 // Check if the .res file contains no entries. In this case we don't have 335 // to throw an error but can rather just return without parsing anything. 336 // This applies for files which have a valid PE header magic and the 337 // mandatory empty null resource entry. Files which do not fit this 338 // criteria would have already been filtered out by 339 // WindowsResource::createWindowsResource(). 340 consumeError(std::move(E)); 341 return Error::success(); 342 } 343 return E; 344 } 345 346 ResourceEntryRef Entry = EntryOrErr.get(); 347 uint32_t Origin = InputFilenames.size(); 348 InputFilenames.push_back(std::string(WR->getFileName())); 349 bool End = false; 350 while (!End) { 351 352 TreeNode *Node; 353 bool IsNewNode = Root.addEntry(Entry, Origin, Data, StringTable, Node); 354 if (!IsNewNode) { 355 if (!shouldIgnoreDuplicate(Entry)) 356 Duplicates.push_back(makeDuplicateResourceError( 357 Entry, InputFilenames[Node->Origin], WR->getFileName())); 358 } 359 360 RETURN_IF_ERROR(Entry.moveNext(End)); 361 } 362 363 return Error::success(); 364 } 365 366 Error WindowsResourceParser::parse(ResourceSectionRef &RSR, StringRef Filename, 367 std::vector<std::string> &Duplicates) { 368 UNWRAP_REF_OR_RETURN(BaseTable, RSR.getBaseTable()); 369 uint32_t Origin = InputFilenames.size(); 370 InputFilenames.push_back(std::string(Filename)); 371 std::vector<StringOrID> Context; 372 return addChildren(Root, RSR, BaseTable, Origin, Context, Duplicates); 373 } 374 375 void WindowsResourceParser::printTree(raw_ostream &OS) const { 376 ScopedPrinter Writer(OS); 377 Root.print(Writer, "Resource Tree"); 378 } 379 380 bool WindowsResourceParser::TreeNode::addEntry( 381 const ResourceEntryRef &Entry, uint32_t Origin, 382 std::vector<std::vector<uint8_t>> &Data, 383 std::vector<std::vector<UTF16>> &StringTable, TreeNode *&Result) { 384 TreeNode &TypeNode = addTypeNode(Entry, StringTable); 385 TreeNode &NameNode = TypeNode.addNameNode(Entry, StringTable); 386 return NameNode.addLanguageNode(Entry, Origin, Data, Result); 387 } 388 389 Error WindowsResourceParser::addChildren(TreeNode &Node, 390 ResourceSectionRef &RSR, 391 const coff_resource_dir_table &Table, 392 uint32_t Origin, 393 std::vector<StringOrID> &Context, 394 std::vector<std::string> &Duplicates) { 395 396 for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries; 397 i++) { 398 UNWRAP_REF_OR_RETURN(Entry, RSR.getTableEntry(Table, i)); 399 TreeNode *Child; 400 401 if (Entry.Offset.isSubDir()) { 402 403 // Create a new subdirectory and recurse 404 if (i < Table.NumberOfNameEntries) { 405 UNWRAP_OR_RETURN(NameString, RSR.getEntryNameString(Entry)); 406 Child = &Node.addNameChild(NameString, StringTable); 407 Context.push_back(StringOrID(NameString)); 408 } else { 409 Child = &Node.addIDChild(Entry.Identifier.ID); 410 Context.push_back(StringOrID(Entry.Identifier.ID)); 411 } 412 413 UNWRAP_REF_OR_RETURN(NextTable, RSR.getEntrySubDir(Entry)); 414 Error E = 415 addChildren(*Child, RSR, NextTable, Origin, Context, Duplicates); 416 if (E) 417 return E; 418 Context.pop_back(); 419 420 } else { 421 422 // Data leaves are supposed to have a numeric ID as identifier (language). 423 if (Table.NumberOfNameEntries > 0) 424 return createStringError(object_error::parse_failed, 425 "unexpected string key for data object"); 426 427 // Try adding a data leaf 428 UNWRAP_REF_OR_RETURN(DataEntry, RSR.getEntryData(Entry)); 429 TreeNode *Child; 430 Context.push_back(StringOrID(Entry.Identifier.ID)); 431 bool Added = Node.addDataChild(Entry.Identifier.ID, Table.MajorVersion, 432 Table.MinorVersion, Table.Characteristics, 433 Origin, Data.size(), Child); 434 if (Added) { 435 UNWRAP_OR_RETURN(Contents, RSR.getContents(DataEntry)); 436 Data.push_back(ArrayRef<uint8_t>( 437 reinterpret_cast<const uint8_t *>(Contents.data()), 438 Contents.size())); 439 } else { 440 if (!shouldIgnoreDuplicate(Context)) 441 Duplicates.push_back(makeDuplicateResourceError( 442 Context, InputFilenames[Child->Origin], InputFilenames.back())); 443 } 444 Context.pop_back(); 445 446 } 447 } 448 return Error::success(); 449 } 450 451 WindowsResourceParser::TreeNode::TreeNode(uint32_t StringIndex) 452 : StringIndex(StringIndex) {} 453 454 WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion, 455 uint16_t MinorVersion, 456 uint32_t Characteristics, 457 uint32_t Origin, uint32_t DataIndex) 458 : IsDataNode(true), DataIndex(DataIndex), MajorVersion(MajorVersion), 459 MinorVersion(MinorVersion), Characteristics(Characteristics), 460 Origin(Origin) {} 461 462 std::unique_ptr<WindowsResourceParser::TreeNode> 463 WindowsResourceParser::TreeNode::createStringNode(uint32_t Index) { 464 return std::unique_ptr<TreeNode>(new TreeNode(Index)); 465 } 466 467 std::unique_ptr<WindowsResourceParser::TreeNode> 468 WindowsResourceParser::TreeNode::createIDNode() { 469 return std::unique_ptr<TreeNode>(new TreeNode(0)); 470 } 471 472 std::unique_ptr<WindowsResourceParser::TreeNode> 473 WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion, 474 uint16_t MinorVersion, 475 uint32_t Characteristics, 476 uint32_t Origin, 477 uint32_t DataIndex) { 478 return std::unique_ptr<TreeNode>(new TreeNode( 479 MajorVersion, MinorVersion, Characteristics, Origin, DataIndex)); 480 } 481 482 WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addTypeNode( 483 const ResourceEntryRef &Entry, 484 std::vector<std::vector<UTF16>> &StringTable) { 485 if (Entry.checkTypeString()) 486 return addNameChild(Entry.getTypeString(), StringTable); 487 else 488 return addIDChild(Entry.getTypeID()); 489 } 490 491 WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameNode( 492 const ResourceEntryRef &Entry, 493 std::vector<std::vector<UTF16>> &StringTable) { 494 if (Entry.checkNameString()) 495 return addNameChild(Entry.getNameString(), StringTable); 496 else 497 return addIDChild(Entry.getNameID()); 498 } 499 500 bool WindowsResourceParser::TreeNode::addLanguageNode( 501 const ResourceEntryRef &Entry, uint32_t Origin, 502 std::vector<std::vector<uint8_t>> &Data, TreeNode *&Result) { 503 bool Added = addDataChild(Entry.getLanguage(), Entry.getMajorVersion(), 504 Entry.getMinorVersion(), Entry.getCharacteristics(), 505 Origin, Data.size(), Result); 506 if (Added) 507 Data.push_back(Entry.getData()); 508 return Added; 509 } 510 511 bool WindowsResourceParser::TreeNode::addDataChild( 512 uint32_t ID, uint16_t MajorVersion, uint16_t MinorVersion, 513 uint32_t Characteristics, uint32_t Origin, uint32_t DataIndex, 514 TreeNode *&Result) { 515 auto NewChild = createDataNode(MajorVersion, MinorVersion, Characteristics, 516 Origin, DataIndex); 517 auto ElementInserted = IDChildren.emplace(ID, std::move(NewChild)); 518 Result = ElementInserted.first->second.get(); 519 return ElementInserted.second; 520 } 521 522 WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addIDChild( 523 uint32_t ID) { 524 auto Child = IDChildren.find(ID); 525 if (Child == IDChildren.end()) { 526 auto NewChild = createIDNode(); 527 WindowsResourceParser::TreeNode &Node = *NewChild; 528 IDChildren.emplace(ID, std::move(NewChild)); 529 return Node; 530 } else 531 return *(Child->second); 532 } 533 534 WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameChild( 535 ArrayRef<UTF16> NameRef, std::vector<std::vector<UTF16>> &StringTable) { 536 std::string NameString; 537 convertUTF16LEToUTF8String(NameRef, NameString); 538 539 auto Child = StringChildren.find(NameString); 540 if (Child == StringChildren.end()) { 541 auto NewChild = createStringNode(StringTable.size()); 542 StringTable.push_back(NameRef); 543 WindowsResourceParser::TreeNode &Node = *NewChild; 544 StringChildren.emplace(NameString, std::move(NewChild)); 545 return Node; 546 } else 547 return *(Child->second); 548 } 549 550 void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer, 551 StringRef Name) const { 552 ListScope NodeScope(Writer, Name); 553 for (auto const &Child : StringChildren) { 554 Child.second->print(Writer, Child.first); 555 } 556 for (auto const &Child : IDChildren) { 557 Child.second->print(Writer, to_string(Child.first)); 558 } 559 } 560 561 // This function returns the size of the entire resource tree, including 562 // directory tables, directory entries, and data entries. It does not include 563 // the directory strings or the relocations of the .rsrc section. 564 uint32_t WindowsResourceParser::TreeNode::getTreeSize() const { 565 uint32_t Size = (IDChildren.size() + StringChildren.size()) * 566 sizeof(coff_resource_dir_entry); 567 568 // Reached a node pointing to a data entry. 569 if (IsDataNode) { 570 Size += sizeof(coff_resource_data_entry); 571 return Size; 572 } 573 574 // If the node does not point to data, it must have a directory table pointing 575 // to other nodes. 576 Size += sizeof(coff_resource_dir_table); 577 578 for (auto const &Child : StringChildren) { 579 Size += Child.second->getTreeSize(); 580 } 581 for (auto const &Child : IDChildren) { 582 Size += Child.second->getTreeSize(); 583 } 584 return Size; 585 } 586 587 // Shift DataIndex of all data children with an Index greater or equal to the 588 // given one, to fill a gap from removing an entry from the Data vector. 589 void WindowsResourceParser::TreeNode::shiftDataIndexDown(uint32_t Index) { 590 if (IsDataNode && DataIndex >= Index) { 591 DataIndex--; 592 } else { 593 for (auto &Child : IDChildren) 594 Child.second->shiftDataIndexDown(Index); 595 for (auto &Child : StringChildren) 596 Child.second->shiftDataIndexDown(Index); 597 } 598 } 599 600 class WindowsResourceCOFFWriter { 601 public: 602 WindowsResourceCOFFWriter(COFF::MachineTypes MachineType, 603 const WindowsResourceParser &Parser, Error &E); 604 std::unique_ptr<MemoryBuffer> write(uint32_t TimeDateStamp); 605 606 private: 607 void performFileLayout(); 608 void performSectionOneLayout(); 609 void performSectionTwoLayout(); 610 void writeCOFFHeader(uint32_t TimeDateStamp); 611 void writeFirstSectionHeader(); 612 void writeSecondSectionHeader(); 613 void writeFirstSection(); 614 void writeSecondSection(); 615 void writeSymbolTable(); 616 void writeStringTable(); 617 void writeDirectoryTree(); 618 void writeDirectoryStringTable(); 619 void writeFirstSectionRelocations(); 620 std::unique_ptr<WritableMemoryBuffer> OutputBuffer; 621 char *BufferStart; 622 uint64_t CurrentOffset = 0; 623 COFF::MachineTypes MachineType; 624 const WindowsResourceParser::TreeNode &Resources; 625 const ArrayRef<std::vector<uint8_t>> Data; 626 uint64_t FileSize; 627 uint32_t SymbolTableOffset; 628 uint32_t SectionOneSize; 629 uint32_t SectionOneOffset; 630 uint32_t SectionOneRelocations; 631 uint32_t SectionTwoSize; 632 uint32_t SectionTwoOffset; 633 const ArrayRef<std::vector<UTF16>> StringTable; 634 std::vector<uint32_t> StringTableOffsets; 635 std::vector<uint32_t> DataOffsets; 636 std::vector<uint32_t> RelocationAddresses; 637 }; 638 639 WindowsResourceCOFFWriter::WindowsResourceCOFFWriter( 640 COFF::MachineTypes MachineType, const WindowsResourceParser &Parser, 641 Error &E) 642 : MachineType(MachineType), Resources(Parser.getTree()), 643 Data(Parser.getData()), StringTable(Parser.getStringTable()) { 644 performFileLayout(); 645 646 OutputBuffer = WritableMemoryBuffer::getNewMemBuffer( 647 FileSize, "internal .obj file created from .res files"); 648 } 649 650 void WindowsResourceCOFFWriter::performFileLayout() { 651 // Add size of COFF header. 652 FileSize = COFF::Header16Size; 653 654 // one .rsrc section header for directory tree, another for resource data. 655 FileSize += 2 * COFF::SectionSize; 656 657 performSectionOneLayout(); 658 performSectionTwoLayout(); 659 660 // We have reached the address of the symbol table. 661 SymbolTableOffset = FileSize; 662 663 FileSize += COFF::Symbol16Size; // size of the @feat.00 symbol. 664 FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section. 665 FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource. 666 FileSize += 4; // four null bytes for the string table. 667 } 668 669 void WindowsResourceCOFFWriter::performSectionOneLayout() { 670 SectionOneOffset = FileSize; 671 672 SectionOneSize = Resources.getTreeSize(); 673 uint32_t CurrentStringOffset = SectionOneSize; 674 uint32_t TotalStringTableSize = 0; 675 for (auto const &String : StringTable) { 676 StringTableOffsets.push_back(CurrentStringOffset); 677 uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t); 678 CurrentStringOffset += StringSize; 679 TotalStringTableSize += StringSize; 680 } 681 SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t)); 682 683 // account for the relocations of section one. 684 SectionOneRelocations = FileSize + SectionOneSize; 685 FileSize += SectionOneSize; 686 FileSize += 687 Data.size() * COFF::RelocationSize; // one relocation for each resource. 688 FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 689 } 690 691 void WindowsResourceCOFFWriter::performSectionTwoLayout() { 692 // add size of .rsrc$2 section, which contains all resource data on 8-byte 693 // alignment. 694 SectionTwoOffset = FileSize; 695 SectionTwoSize = 0; 696 for (auto const &Entry : Data) { 697 DataOffsets.push_back(SectionTwoSize); 698 SectionTwoSize += alignTo(Entry.size(), sizeof(uint64_t)); 699 } 700 FileSize += SectionTwoSize; 701 FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 702 } 703 704 std::unique_ptr<MemoryBuffer> 705 WindowsResourceCOFFWriter::write(uint32_t TimeDateStamp) { 706 BufferStart = OutputBuffer->getBufferStart(); 707 708 writeCOFFHeader(TimeDateStamp); 709 writeFirstSectionHeader(); 710 writeSecondSectionHeader(); 711 writeFirstSection(); 712 writeSecondSection(); 713 writeSymbolTable(); 714 writeStringTable(); 715 716 return std::move(OutputBuffer); 717 } 718 719 // According to COFF specification, if the Src has a size equal to Dest, 720 // it's okay to *not* copy the trailing zero. 721 static void coffnamecpy(char (&Dest)[COFF::NameSize], StringRef Src) { 722 assert(Src.size() <= COFF::NameSize && 723 "Src is larger than COFF::NameSize"); 724 assert((Src.size() == COFF::NameSize || Dest[Src.size()] == '\0') && 725 "Dest not zeroed upon initialization"); 726 memcpy(Dest, Src.data(), Src.size()); 727 } 728 729 void WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp) { 730 // Write the COFF header. 731 auto *Header = reinterpret_cast<coff_file_header *>(BufferStart); 732 Header->Machine = MachineType; 733 Header->NumberOfSections = 2; 734 Header->TimeDateStamp = TimeDateStamp; 735 Header->PointerToSymbolTable = SymbolTableOffset; 736 // One symbol for every resource plus 2 for each section and 1 for @feat.00 737 Header->NumberOfSymbols = Data.size() + 5; 738 Header->SizeOfOptionalHeader = 0; 739 // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it. 740 Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE; 741 } 742 743 void WindowsResourceCOFFWriter::writeFirstSectionHeader() { 744 // Write the first section header. 745 CurrentOffset += sizeof(coff_file_header); 746 auto *SectionOneHeader = 747 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 748 coffnamecpy(SectionOneHeader->Name, ".rsrc$01"); 749 SectionOneHeader->VirtualSize = 0; 750 SectionOneHeader->VirtualAddress = 0; 751 SectionOneHeader->SizeOfRawData = SectionOneSize; 752 SectionOneHeader->PointerToRawData = SectionOneOffset; 753 SectionOneHeader->PointerToRelocations = SectionOneRelocations; 754 SectionOneHeader->PointerToLinenumbers = 0; 755 SectionOneHeader->NumberOfRelocations = Data.size(); 756 SectionOneHeader->NumberOfLinenumbers = 0; 757 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 758 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 759 } 760 761 void WindowsResourceCOFFWriter::writeSecondSectionHeader() { 762 // Write the second section header. 763 CurrentOffset += sizeof(coff_section); 764 auto *SectionTwoHeader = 765 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 766 coffnamecpy(SectionTwoHeader->Name, ".rsrc$02"); 767 SectionTwoHeader->VirtualSize = 0; 768 SectionTwoHeader->VirtualAddress = 0; 769 SectionTwoHeader->SizeOfRawData = SectionTwoSize; 770 SectionTwoHeader->PointerToRawData = SectionTwoOffset; 771 SectionTwoHeader->PointerToRelocations = 0; 772 SectionTwoHeader->PointerToLinenumbers = 0; 773 SectionTwoHeader->NumberOfRelocations = 0; 774 SectionTwoHeader->NumberOfLinenumbers = 0; 775 SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 776 SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 777 } 778 779 void WindowsResourceCOFFWriter::writeFirstSection() { 780 // Write section one. 781 CurrentOffset += sizeof(coff_section); 782 783 writeDirectoryTree(); 784 writeDirectoryStringTable(); 785 writeFirstSectionRelocations(); 786 787 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 788 } 789 790 void WindowsResourceCOFFWriter::writeSecondSection() { 791 // Now write the .rsrc$02 section. 792 for (auto const &RawDataEntry : Data) { 793 llvm::copy(RawDataEntry, BufferStart + CurrentOffset); 794 CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t)); 795 } 796 797 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 798 } 799 800 void WindowsResourceCOFFWriter::writeSymbolTable() { 801 // Now write the symbol table. 802 // First, the feat symbol. 803 auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 804 coffnamecpy(Symbol->Name.ShortName, "@feat.00"); 805 Symbol->Value = 0x11; 806 Symbol->SectionNumber = 0xffff; 807 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 808 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 809 Symbol->NumberOfAuxSymbols = 0; 810 CurrentOffset += sizeof(coff_symbol16); 811 812 // Now write the .rsrc1 symbol + aux. 813 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 814 coffnamecpy(Symbol->Name.ShortName, ".rsrc$01"); 815 Symbol->Value = 0; 816 Symbol->SectionNumber = 1; 817 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 818 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 819 Symbol->NumberOfAuxSymbols = 1; 820 CurrentOffset += sizeof(coff_symbol16); 821 auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 822 CurrentOffset); 823 Aux->Length = SectionOneSize; 824 Aux->NumberOfRelocations = Data.size(); 825 Aux->NumberOfLinenumbers = 0; 826 Aux->CheckSum = 0; 827 Aux->NumberLowPart = 0; 828 Aux->Selection = 0; 829 CurrentOffset += sizeof(coff_aux_section_definition); 830 831 // Now write the .rsrc2 symbol + aux. 832 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 833 coffnamecpy(Symbol->Name.ShortName, ".rsrc$02"); 834 Symbol->Value = 0; 835 Symbol->SectionNumber = 2; 836 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 837 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 838 Symbol->NumberOfAuxSymbols = 1; 839 CurrentOffset += sizeof(coff_symbol16); 840 Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 841 CurrentOffset); 842 Aux->Length = SectionTwoSize; 843 Aux->NumberOfRelocations = 0; 844 Aux->NumberOfLinenumbers = 0; 845 Aux->CheckSum = 0; 846 Aux->NumberLowPart = 0; 847 Aux->Selection = 0; 848 CurrentOffset += sizeof(coff_aux_section_definition); 849 850 // Now write a symbol for each relocation. 851 for (unsigned i = 0; i < Data.size(); i++) { 852 auto RelocationName = formatv("$R{0:X-6}", i & 0xffffff).sstr<COFF::NameSize>(); 853 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 854 coffnamecpy(Symbol->Name.ShortName, RelocationName); 855 Symbol->Value = DataOffsets[i]; 856 Symbol->SectionNumber = 2; 857 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 858 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 859 Symbol->NumberOfAuxSymbols = 0; 860 CurrentOffset += sizeof(coff_symbol16); 861 } 862 } 863 864 void WindowsResourceCOFFWriter::writeStringTable() { 865 // Just 4 null bytes for the string table. 866 auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset); 867 memset(COFFStringTable, 0, 4); 868 } 869 870 void WindowsResourceCOFFWriter::writeDirectoryTree() { 871 // Traverse parsed resource tree breadth-first and write the corresponding 872 // COFF objects. 873 std::queue<const WindowsResourceParser::TreeNode *> Queue; 874 Queue.push(&Resources); 875 uint32_t NextLevelOffset = 876 sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() + 877 Resources.getIDChildren().size()) * 878 sizeof(coff_resource_dir_entry); 879 std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder; 880 uint32_t CurrentRelativeOffset = 0; 881 882 while (!Queue.empty()) { 883 auto CurrentNode = Queue.front(); 884 Queue.pop(); 885 auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart + 886 CurrentOffset); 887 Table->Characteristics = CurrentNode->getCharacteristics(); 888 Table->TimeDateStamp = 0; 889 Table->MajorVersion = CurrentNode->getMajorVersion(); 890 Table->MinorVersion = CurrentNode->getMinorVersion(); 891 auto &IDChildren = CurrentNode->getIDChildren(); 892 auto &StringChildren = CurrentNode->getStringChildren(); 893 Table->NumberOfNameEntries = StringChildren.size(); 894 Table->NumberOfIDEntries = IDChildren.size(); 895 CurrentOffset += sizeof(coff_resource_dir_table); 896 CurrentRelativeOffset += sizeof(coff_resource_dir_table); 897 898 // Write the directory entries immediately following each directory table. 899 for (auto const &Child : StringChildren) { 900 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 901 CurrentOffset); 902 Entry->Identifier.setNameOffset( 903 StringTableOffsets[Child.second->getStringIndex()]); 904 if (Child.second->checkIsDataNode()) { 905 Entry->Offset.DataEntryOffset = NextLevelOffset; 906 NextLevelOffset += sizeof(coff_resource_data_entry); 907 DataEntriesTreeOrder.push_back(Child.second.get()); 908 } else { 909 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 910 NextLevelOffset += sizeof(coff_resource_dir_table) + 911 (Child.second->getStringChildren().size() + 912 Child.second->getIDChildren().size()) * 913 sizeof(coff_resource_dir_entry); 914 Queue.push(Child.second.get()); 915 } 916 CurrentOffset += sizeof(coff_resource_dir_entry); 917 CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 918 } 919 for (auto const &Child : IDChildren) { 920 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 921 CurrentOffset); 922 Entry->Identifier.ID = Child.first; 923 if (Child.second->checkIsDataNode()) { 924 Entry->Offset.DataEntryOffset = NextLevelOffset; 925 NextLevelOffset += sizeof(coff_resource_data_entry); 926 DataEntriesTreeOrder.push_back(Child.second.get()); 927 } else { 928 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 929 NextLevelOffset += sizeof(coff_resource_dir_table) + 930 (Child.second->getStringChildren().size() + 931 Child.second->getIDChildren().size()) * 932 sizeof(coff_resource_dir_entry); 933 Queue.push(Child.second.get()); 934 } 935 CurrentOffset += sizeof(coff_resource_dir_entry); 936 CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 937 } 938 } 939 940 RelocationAddresses.resize(Data.size()); 941 // Now write all the resource data entries. 942 for (const auto *DataNodes : DataEntriesTreeOrder) { 943 auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart + 944 CurrentOffset); 945 RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset; 946 Entry->DataRVA = 0; // Set to zero because it is a relocation. 947 Entry->DataSize = Data[DataNodes->getDataIndex()].size(); 948 Entry->Codepage = 0; 949 Entry->Reserved = 0; 950 CurrentOffset += sizeof(coff_resource_data_entry); 951 CurrentRelativeOffset += sizeof(coff_resource_data_entry); 952 } 953 } 954 955 void WindowsResourceCOFFWriter::writeDirectoryStringTable() { 956 // Now write the directory string table for .rsrc$01 957 uint32_t TotalStringTableSize = 0; 958 for (auto &String : StringTable) { 959 uint16_t Length = String.size(); 960 support::endian::write16le(BufferStart + CurrentOffset, Length); 961 CurrentOffset += sizeof(uint16_t); 962 auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset); 963 llvm::copy(String, Start); 964 CurrentOffset += Length * sizeof(UTF16); 965 TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t); 966 } 967 CurrentOffset += 968 alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize; 969 } 970 971 void WindowsResourceCOFFWriter::writeFirstSectionRelocations() { 972 973 // Now write the relocations for .rsrc$01 974 // Five symbols already in table before we start, @feat.00 and 2 for each 975 // .rsrc section. 976 uint32_t NextSymbolIndex = 5; 977 for (unsigned i = 0; i < Data.size(); i++) { 978 auto *Reloc = 979 reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset); 980 Reloc->VirtualAddress = RelocationAddresses[i]; 981 Reloc->SymbolTableIndex = NextSymbolIndex++; 982 switch (getMachineArchType(MachineType)) { 983 case Triple::thumb: 984 Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB; 985 break; 986 case Triple::x86_64: 987 Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB; 988 break; 989 case Triple::x86: 990 Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB; 991 break; 992 case Triple::aarch64: 993 Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB; 994 break; 995 default: 996 llvm_unreachable("unknown machine type"); 997 } 998 CurrentOffset += sizeof(coff_relocation); 999 } 1000 } 1001 1002 Expected<std::unique_ptr<MemoryBuffer>> 1003 writeWindowsResourceCOFF(COFF::MachineTypes MachineType, 1004 const WindowsResourceParser &Parser, 1005 uint32_t TimeDateStamp) { 1006 Error E = Error::success(); 1007 WindowsResourceCOFFWriter Writer(MachineType, Parser, E); 1008 if (E) 1009 return E; 1010 return Writer.write(TimeDateStamp); 1011 } 1012 1013 } // namespace object 1014 } // namespace llvm 1015