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 #define UNWRAP_REF_OR_RETURN(Name, Expr) \ 34 auto Name##OrErr = Expr; \ 35 if (!Name##OrErr) \ 36 return Name##OrErr.takeError(); \ 37 const auto &Name = *Name##OrErr; 38 39 #define UNWRAP_OR_RETURN(Name, Expr) \ 40 auto Name##OrErr = Expr; \ 41 if (!Name##OrErr) \ 42 return Name##OrErr.takeError(); \ 43 auto Name = *Name##OrErr; 44 45 const uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t); 46 47 // COFF files seem to be inconsistent with alignment between sections, just use 48 // 8-byte because it makes everyone happy. 49 const uint32_t SECTION_ALIGNMENT = sizeof(uint64_t); 50 51 WindowsResource::WindowsResource(MemoryBufferRef Source) 52 : Binary(Binary::ID_WinRes, Source) { 53 size_t LeadingSize = WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE; 54 BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize), 55 support::little); 56 } 57 58 // static 59 Expected<std::unique_ptr<WindowsResource>> 60 WindowsResource::createWindowsResource(MemoryBufferRef Source) { 61 if (Source.getBufferSize() < WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE) 62 return make_error<GenericBinaryError>( 63 Source.getBufferIdentifier() + ": too small to be a resource file", 64 object_error::invalid_file_type); 65 std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source)); 66 return std::move(Ret); 67 } 68 69 Expected<ResourceEntryRef> WindowsResource::getHeadEntry() { 70 if (BBS.getLength() < sizeof(WinResHeaderPrefix) + sizeof(WinResHeaderSuffix)) 71 return make_error<EmptyResError>(getFileName() + " contains no entries", 72 object_error::unexpected_eof); 73 return ResourceEntryRef::create(BinaryStreamRef(BBS), this); 74 } 75 76 ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref, 77 const WindowsResource *Owner) 78 : Reader(Ref), Owner(Owner) {} 79 80 Expected<ResourceEntryRef> 81 ResourceEntryRef::create(BinaryStreamRef BSR, const WindowsResource *Owner) { 82 auto Ref = ResourceEntryRef(BSR, Owner); 83 if (auto E = Ref.loadNext()) 84 return std::move(E); 85 return Ref; 86 } 87 88 Error ResourceEntryRef::moveNext(bool &End) { 89 // Reached end of all the entries. 90 if (Reader.bytesRemaining() == 0) { 91 End = true; 92 return Error::success(); 93 } 94 RETURN_IF_ERROR(loadNext()); 95 96 return Error::success(); 97 } 98 99 static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID, 100 ArrayRef<UTF16> &Str, bool &IsString) { 101 uint16_t IDFlag; 102 RETURN_IF_ERROR(Reader.readInteger(IDFlag)); 103 IsString = IDFlag != 0xffff; 104 105 if (IsString) { 106 Reader.setOffset( 107 Reader.getOffset() - 108 sizeof(uint16_t)); // Re-read the bytes which we used to check the flag. 109 RETURN_IF_ERROR(Reader.readWideString(Str)); 110 } else 111 RETURN_IF_ERROR(Reader.readInteger(ID)); 112 113 return Error::success(); 114 } 115 116 Error ResourceEntryRef::loadNext() { 117 const WinResHeaderPrefix *Prefix; 118 RETURN_IF_ERROR(Reader.readObject(Prefix)); 119 120 if (Prefix->HeaderSize < MIN_HEADER_SIZE) 121 return make_error<GenericBinaryError>(Owner->getFileName() + 122 ": header size too small", 123 object_error::parse_failed); 124 125 RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType)); 126 127 RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName)); 128 129 RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_HEADER_ALIGNMENT)); 130 131 RETURN_IF_ERROR(Reader.readObject(Suffix)); 132 133 RETURN_IF_ERROR(Reader.readArray(Data, Prefix->DataSize)); 134 135 RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_DATA_ALIGNMENT)); 136 137 return Error::success(); 138 } 139 140 WindowsResourceParser::WindowsResourceParser() : Root(false) {} 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(makeArrayRef(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 Error WindowsResourceParser::parse(WindowsResource *WR, 255 std::vector<std::string> &Duplicates) { 256 auto EntryOrErr = WR->getHeadEntry(); 257 if (!EntryOrErr) { 258 auto E = EntryOrErr.takeError(); 259 if (E.isA<EmptyResError>()) { 260 // Check if the .res file contains no entries. In this case we don't have 261 // to throw an error but can rather just return without parsing anything. 262 // This applies for files which have a valid PE header magic and the 263 // mandatory empty null resource entry. Files which do not fit this 264 // criteria would have already been filtered out by 265 // WindowsResource::createWindowsResource(). 266 consumeError(std::move(E)); 267 return Error::success(); 268 } 269 return E; 270 } 271 272 ResourceEntryRef Entry = EntryOrErr.get(); 273 uint32_t Origin = InputFilenames.size(); 274 InputFilenames.push_back(WR->getFileName()); 275 bool End = false; 276 while (!End) { 277 278 TreeNode *Node; 279 bool IsNewNode = Root.addEntry(Entry, Origin, Data, StringTable, Node); 280 if (!IsNewNode) { 281 Duplicates.push_back(makeDuplicateResourceError( 282 Entry, InputFilenames[Node->Origin], WR->getFileName())); 283 } 284 285 RETURN_IF_ERROR(Entry.moveNext(End)); 286 } 287 288 return Error::success(); 289 } 290 291 Error WindowsResourceParser::parse(ResourceSectionRef &RSR, StringRef Filename, 292 std::vector<std::string> &Duplicates) { 293 UNWRAP_REF_OR_RETURN(BaseTable, RSR.getBaseTable()); 294 uint32_t Origin = InputFilenames.size(); 295 InputFilenames.push_back(Filename); 296 std::vector<StringOrID> Context; 297 return addChildren(Root, RSR, BaseTable, Origin, Context, Duplicates); 298 } 299 300 void WindowsResourceParser::printTree(raw_ostream &OS) const { 301 ScopedPrinter Writer(OS); 302 Root.print(Writer, "Resource Tree"); 303 } 304 305 bool WindowsResourceParser::TreeNode::addEntry( 306 const ResourceEntryRef &Entry, uint32_t Origin, 307 std::vector<std::vector<uint8_t>> &Data, 308 std::vector<std::vector<UTF16>> &StringTable, TreeNode *&Result) { 309 TreeNode &TypeNode = addTypeNode(Entry, StringTable); 310 TreeNode &NameNode = TypeNode.addNameNode(Entry, StringTable); 311 return NameNode.addLanguageNode(Entry, Origin, Data, Result); 312 } 313 314 Error WindowsResourceParser::addChildren(TreeNode &Node, 315 ResourceSectionRef &RSR, 316 const coff_resource_dir_table &Table, 317 uint32_t Origin, 318 std::vector<StringOrID> &Context, 319 std::vector<std::string> &Duplicates) { 320 321 for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries; 322 i++) { 323 UNWRAP_REF_OR_RETURN(Entry, RSR.getTableEntry(Table, i)); 324 TreeNode *Child; 325 326 if (Entry.Offset.isSubDir()) { 327 328 // Create a new subdirectory and recurse 329 if (i < Table.NumberOfNameEntries) { 330 UNWRAP_OR_RETURN(NameString, RSR.getEntryNameString(Entry)); 331 Child = &Node.addNameChild(NameString, StringTable); 332 Context.push_back(StringOrID(NameString)); 333 } else { 334 Child = &Node.addIDChild(Entry.Identifier.ID); 335 Context.push_back(StringOrID(Entry.Identifier.ID)); 336 } 337 338 UNWRAP_REF_OR_RETURN(NextTable, RSR.getEntrySubDir(Entry)); 339 Error E = 340 addChildren(*Child, RSR, NextTable, Origin, Context, Duplicates); 341 if (E) 342 return E; 343 Context.pop_back(); 344 345 } else { 346 347 // Data leaves are supposed to have a numeric ID as identifier (language). 348 if (Table.NumberOfNameEntries > 0) 349 return createStringError(object_error::parse_failed, 350 "unexpected string key for data object"); 351 352 // Try adding a data leaf 353 UNWRAP_REF_OR_RETURN(DataEntry, RSR.getEntryData(Entry)); 354 TreeNode *Child; 355 Context.push_back(StringOrID(Entry.Identifier.ID)); 356 bool Added = Node.addDataChild(Entry.Identifier.ID, Table.MajorVersion, 357 Table.MinorVersion, Table.Characteristics, 358 Origin, Data.size(), Child); 359 if (Added) { 360 UNWRAP_OR_RETURN(Contents, RSR.getContents(DataEntry)); 361 Data.push_back(ArrayRef<uint8_t>( 362 reinterpret_cast<const uint8_t *>(Contents.data()), 363 Contents.size())); 364 } else { 365 Duplicates.push_back(makeDuplicateResourceError( 366 Context, InputFilenames[Child->Origin], InputFilenames.back())); 367 } 368 Context.pop_back(); 369 370 } 371 } 372 return Error::success(); 373 } 374 375 WindowsResourceParser::TreeNode::TreeNode(uint32_t StringIndex) 376 : StringIndex(StringIndex) {} 377 378 WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion, 379 uint16_t MinorVersion, 380 uint32_t Characteristics, 381 uint32_t Origin, uint32_t DataIndex) 382 : IsDataNode(true), DataIndex(DataIndex), MajorVersion(MajorVersion), 383 MinorVersion(MinorVersion), Characteristics(Characteristics), 384 Origin(Origin) {} 385 386 std::unique_ptr<WindowsResourceParser::TreeNode> 387 WindowsResourceParser::TreeNode::createStringNode(uint32_t Index) { 388 return std::unique_ptr<TreeNode>(new TreeNode(Index)); 389 } 390 391 std::unique_ptr<WindowsResourceParser::TreeNode> 392 WindowsResourceParser::TreeNode::createIDNode() { 393 return std::unique_ptr<TreeNode>(new TreeNode(0)); 394 } 395 396 std::unique_ptr<WindowsResourceParser::TreeNode> 397 WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion, 398 uint16_t MinorVersion, 399 uint32_t Characteristics, 400 uint32_t Origin, 401 uint32_t DataIndex) { 402 return std::unique_ptr<TreeNode>(new TreeNode( 403 MajorVersion, MinorVersion, Characteristics, Origin, DataIndex)); 404 } 405 406 WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addTypeNode( 407 const ResourceEntryRef &Entry, 408 std::vector<std::vector<UTF16>> &StringTable) { 409 if (Entry.checkTypeString()) 410 return addNameChild(Entry.getTypeString(), StringTable); 411 else 412 return addIDChild(Entry.getTypeID()); 413 } 414 415 WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameNode( 416 const ResourceEntryRef &Entry, 417 std::vector<std::vector<UTF16>> &StringTable) { 418 if (Entry.checkNameString()) 419 return addNameChild(Entry.getNameString(), StringTable); 420 else 421 return addIDChild(Entry.getNameID()); 422 } 423 424 bool WindowsResourceParser::TreeNode::addLanguageNode( 425 const ResourceEntryRef &Entry, uint32_t Origin, 426 std::vector<std::vector<uint8_t>> &Data, TreeNode *&Result) { 427 bool Added = addDataChild(Entry.getLanguage(), Entry.getMajorVersion(), 428 Entry.getMinorVersion(), Entry.getCharacteristics(), 429 Origin, Data.size(), Result); 430 if (Added) 431 Data.push_back(Entry.getData()); 432 return Added; 433 } 434 435 bool WindowsResourceParser::TreeNode::addDataChild( 436 uint32_t ID, uint16_t MajorVersion, uint16_t MinorVersion, 437 uint32_t Characteristics, uint32_t Origin, uint32_t DataIndex, 438 TreeNode *&Result) { 439 auto NewChild = createDataNode(MajorVersion, MinorVersion, Characteristics, 440 Origin, DataIndex); 441 auto ElementInserted = IDChildren.emplace(ID, std::move(NewChild)); 442 Result = ElementInserted.first->second.get(); 443 return ElementInserted.second; 444 } 445 446 WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addIDChild( 447 uint32_t ID) { 448 auto Child = IDChildren.find(ID); 449 if (Child == IDChildren.end()) { 450 auto NewChild = createIDNode(); 451 WindowsResourceParser::TreeNode &Node = *NewChild; 452 IDChildren.emplace(ID, std::move(NewChild)); 453 return Node; 454 } else 455 return *(Child->second); 456 } 457 458 WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameChild( 459 ArrayRef<UTF16> NameRef, std::vector<std::vector<UTF16>> &StringTable) { 460 std::string NameString; 461 convertUTF16LEToUTF8String(NameRef, NameString); 462 463 auto Child = StringChildren.find(NameString); 464 if (Child == StringChildren.end()) { 465 auto NewChild = createStringNode(StringTable.size()); 466 StringTable.push_back(NameRef); 467 WindowsResourceParser::TreeNode &Node = *NewChild; 468 StringChildren.emplace(NameString, std::move(NewChild)); 469 return Node; 470 } else 471 return *(Child->second); 472 } 473 474 void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer, 475 StringRef Name) const { 476 ListScope NodeScope(Writer, Name); 477 for (auto const &Child : StringChildren) { 478 Child.second->print(Writer, Child.first); 479 } 480 for (auto const &Child : IDChildren) { 481 Child.second->print(Writer, to_string(Child.first)); 482 } 483 } 484 485 // This function returns the size of the entire resource tree, including 486 // directory tables, directory entries, and data entries. It does not include 487 // the directory strings or the relocations of the .rsrc section. 488 uint32_t WindowsResourceParser::TreeNode::getTreeSize() const { 489 uint32_t Size = (IDChildren.size() + StringChildren.size()) * 490 sizeof(coff_resource_dir_entry); 491 492 // Reached a node pointing to a data entry. 493 if (IsDataNode) { 494 Size += sizeof(coff_resource_data_entry); 495 return Size; 496 } 497 498 // If the node does not point to data, it must have a directory table pointing 499 // to other nodes. 500 Size += sizeof(coff_resource_dir_table); 501 502 for (auto const &Child : StringChildren) { 503 Size += Child.second->getTreeSize(); 504 } 505 for (auto const &Child : IDChildren) { 506 Size += Child.second->getTreeSize(); 507 } 508 return Size; 509 } 510 511 class WindowsResourceCOFFWriter { 512 public: 513 WindowsResourceCOFFWriter(COFF::MachineTypes MachineType, 514 const WindowsResourceParser &Parser, Error &E); 515 std::unique_ptr<MemoryBuffer> write(uint32_t TimeDateStamp); 516 517 private: 518 void performFileLayout(); 519 void performSectionOneLayout(); 520 void performSectionTwoLayout(); 521 void writeCOFFHeader(uint32_t TimeDateStamp); 522 void writeFirstSectionHeader(); 523 void writeSecondSectionHeader(); 524 void writeFirstSection(); 525 void writeSecondSection(); 526 void writeSymbolTable(); 527 void writeStringTable(); 528 void writeDirectoryTree(); 529 void writeDirectoryStringTable(); 530 void writeFirstSectionRelocations(); 531 std::unique_ptr<WritableMemoryBuffer> OutputBuffer; 532 char *BufferStart; 533 uint64_t CurrentOffset = 0; 534 COFF::MachineTypes MachineType; 535 const WindowsResourceParser::TreeNode &Resources; 536 const ArrayRef<std::vector<uint8_t>> Data; 537 uint64_t FileSize; 538 uint32_t SymbolTableOffset; 539 uint32_t SectionOneSize; 540 uint32_t SectionOneOffset; 541 uint32_t SectionOneRelocations; 542 uint32_t SectionTwoSize; 543 uint32_t SectionTwoOffset; 544 const ArrayRef<std::vector<UTF16>> StringTable; 545 std::vector<uint32_t> StringTableOffsets; 546 std::vector<uint32_t> DataOffsets; 547 std::vector<uint32_t> RelocationAddresses; 548 }; 549 550 WindowsResourceCOFFWriter::WindowsResourceCOFFWriter( 551 COFF::MachineTypes MachineType, const WindowsResourceParser &Parser, 552 Error &E) 553 : MachineType(MachineType), Resources(Parser.getTree()), 554 Data(Parser.getData()), StringTable(Parser.getStringTable()) { 555 performFileLayout(); 556 557 OutputBuffer = WritableMemoryBuffer::getNewMemBuffer( 558 FileSize, "internal .obj file created from .res files"); 559 } 560 561 void WindowsResourceCOFFWriter::performFileLayout() { 562 // Add size of COFF header. 563 FileSize = COFF::Header16Size; 564 565 // one .rsrc section header for directory tree, another for resource data. 566 FileSize += 2 * COFF::SectionSize; 567 568 performSectionOneLayout(); 569 performSectionTwoLayout(); 570 571 // We have reached the address of the symbol table. 572 SymbolTableOffset = FileSize; 573 574 FileSize += COFF::Symbol16Size; // size of the @feat.00 symbol. 575 FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section. 576 FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource. 577 FileSize += 4; // four null bytes for the string table. 578 } 579 580 void WindowsResourceCOFFWriter::performSectionOneLayout() { 581 SectionOneOffset = FileSize; 582 583 SectionOneSize = Resources.getTreeSize(); 584 uint32_t CurrentStringOffset = SectionOneSize; 585 uint32_t TotalStringTableSize = 0; 586 for (auto const &String : StringTable) { 587 StringTableOffsets.push_back(CurrentStringOffset); 588 uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t); 589 CurrentStringOffset += StringSize; 590 TotalStringTableSize += StringSize; 591 } 592 SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t)); 593 594 // account for the relocations of section one. 595 SectionOneRelocations = FileSize + SectionOneSize; 596 FileSize += SectionOneSize; 597 FileSize += 598 Data.size() * COFF::RelocationSize; // one relocation for each resource. 599 FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 600 } 601 602 void WindowsResourceCOFFWriter::performSectionTwoLayout() { 603 // add size of .rsrc$2 section, which contains all resource data on 8-byte 604 // alignment. 605 SectionTwoOffset = FileSize; 606 SectionTwoSize = 0; 607 for (auto const &Entry : Data) { 608 DataOffsets.push_back(SectionTwoSize); 609 SectionTwoSize += alignTo(Entry.size(), sizeof(uint64_t)); 610 } 611 FileSize += SectionTwoSize; 612 FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 613 } 614 615 std::unique_ptr<MemoryBuffer> 616 WindowsResourceCOFFWriter::write(uint32_t TimeDateStamp) { 617 BufferStart = OutputBuffer->getBufferStart(); 618 619 writeCOFFHeader(TimeDateStamp); 620 writeFirstSectionHeader(); 621 writeSecondSectionHeader(); 622 writeFirstSection(); 623 writeSecondSection(); 624 writeSymbolTable(); 625 writeStringTable(); 626 627 return std::move(OutputBuffer); 628 } 629 630 // According to COFF specification, if the Src has a size equal to Dest, 631 // it's okay to *not* copy the trailing zero. 632 static void coffnamecpy(char (&Dest)[COFF::NameSize], StringRef Src) { 633 assert(Src.size() <= COFF::NameSize && 634 "Src is not larger than COFF::NameSize"); 635 strncpy(Dest, Src.data(), (size_t)COFF::NameSize); 636 } 637 638 void WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp) { 639 // Write the COFF header. 640 auto *Header = reinterpret_cast<coff_file_header *>(BufferStart); 641 Header->Machine = MachineType; 642 Header->NumberOfSections = 2; 643 Header->TimeDateStamp = TimeDateStamp; 644 Header->PointerToSymbolTable = SymbolTableOffset; 645 // One symbol for every resource plus 2 for each section and 1 for @feat.00 646 Header->NumberOfSymbols = Data.size() + 5; 647 Header->SizeOfOptionalHeader = 0; 648 // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it. 649 Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE; 650 } 651 652 void WindowsResourceCOFFWriter::writeFirstSectionHeader() { 653 // Write the first section header. 654 CurrentOffset += sizeof(coff_file_header); 655 auto *SectionOneHeader = 656 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 657 coffnamecpy(SectionOneHeader->Name, ".rsrc$01"); 658 SectionOneHeader->VirtualSize = 0; 659 SectionOneHeader->VirtualAddress = 0; 660 SectionOneHeader->SizeOfRawData = SectionOneSize; 661 SectionOneHeader->PointerToRawData = SectionOneOffset; 662 SectionOneHeader->PointerToRelocations = SectionOneRelocations; 663 SectionOneHeader->PointerToLinenumbers = 0; 664 SectionOneHeader->NumberOfRelocations = Data.size(); 665 SectionOneHeader->NumberOfLinenumbers = 0; 666 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 667 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 668 } 669 670 void WindowsResourceCOFFWriter::writeSecondSectionHeader() { 671 // Write the second section header. 672 CurrentOffset += sizeof(coff_section); 673 auto *SectionTwoHeader = 674 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 675 coffnamecpy(SectionTwoHeader->Name, ".rsrc$02"); 676 SectionTwoHeader->VirtualSize = 0; 677 SectionTwoHeader->VirtualAddress = 0; 678 SectionTwoHeader->SizeOfRawData = SectionTwoSize; 679 SectionTwoHeader->PointerToRawData = SectionTwoOffset; 680 SectionTwoHeader->PointerToRelocations = 0; 681 SectionTwoHeader->PointerToLinenumbers = 0; 682 SectionTwoHeader->NumberOfRelocations = 0; 683 SectionTwoHeader->NumberOfLinenumbers = 0; 684 SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 685 SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 686 } 687 688 void WindowsResourceCOFFWriter::writeFirstSection() { 689 // Write section one. 690 CurrentOffset += sizeof(coff_section); 691 692 writeDirectoryTree(); 693 writeDirectoryStringTable(); 694 writeFirstSectionRelocations(); 695 696 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 697 } 698 699 void WindowsResourceCOFFWriter::writeSecondSection() { 700 // Now write the .rsrc$02 section. 701 for (auto const &RawDataEntry : Data) { 702 llvm::copy(RawDataEntry, BufferStart + CurrentOffset); 703 CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t)); 704 } 705 706 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 707 } 708 709 void WindowsResourceCOFFWriter::writeSymbolTable() { 710 // Now write the symbol table. 711 // First, the feat symbol. 712 auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 713 coffnamecpy(Symbol->Name.ShortName, "@feat.00"); 714 Symbol->Value = 0x11; 715 Symbol->SectionNumber = 0xffff; 716 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 717 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 718 Symbol->NumberOfAuxSymbols = 0; 719 CurrentOffset += sizeof(coff_symbol16); 720 721 // Now write the .rsrc1 symbol + aux. 722 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 723 coffnamecpy(Symbol->Name.ShortName, ".rsrc$01"); 724 Symbol->Value = 0; 725 Symbol->SectionNumber = 1; 726 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 727 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 728 Symbol->NumberOfAuxSymbols = 1; 729 CurrentOffset += sizeof(coff_symbol16); 730 auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 731 CurrentOffset); 732 Aux->Length = SectionOneSize; 733 Aux->NumberOfRelocations = Data.size(); 734 Aux->NumberOfLinenumbers = 0; 735 Aux->CheckSum = 0; 736 Aux->NumberLowPart = 0; 737 Aux->Selection = 0; 738 CurrentOffset += sizeof(coff_aux_section_definition); 739 740 // Now write the .rsrc2 symbol + aux. 741 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 742 coffnamecpy(Symbol->Name.ShortName, ".rsrc$02"); 743 Symbol->Value = 0; 744 Symbol->SectionNumber = 2; 745 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 746 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 747 Symbol->NumberOfAuxSymbols = 1; 748 CurrentOffset += sizeof(coff_symbol16); 749 Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 750 CurrentOffset); 751 Aux->Length = SectionTwoSize; 752 Aux->NumberOfRelocations = 0; 753 Aux->NumberOfLinenumbers = 0; 754 Aux->CheckSum = 0; 755 Aux->NumberLowPart = 0; 756 Aux->Selection = 0; 757 CurrentOffset += sizeof(coff_aux_section_definition); 758 759 // Now write a symbol for each relocation. 760 for (unsigned i = 0; i < Data.size(); i++) { 761 auto RelocationName = formatv("$R{0:X-6}", i & 0xffffff).sstr<COFF::NameSize>(); 762 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 763 coffnamecpy(Symbol->Name.ShortName, RelocationName); 764 Symbol->Value = DataOffsets[i]; 765 Symbol->SectionNumber = 2; 766 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 767 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 768 Symbol->NumberOfAuxSymbols = 0; 769 CurrentOffset += sizeof(coff_symbol16); 770 } 771 } 772 773 void WindowsResourceCOFFWriter::writeStringTable() { 774 // Just 4 null bytes for the string table. 775 auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset); 776 memset(COFFStringTable, 0, 4); 777 } 778 779 void WindowsResourceCOFFWriter::writeDirectoryTree() { 780 // Traverse parsed resource tree breadth-first and write the corresponding 781 // COFF objects. 782 std::queue<const WindowsResourceParser::TreeNode *> Queue; 783 Queue.push(&Resources); 784 uint32_t NextLevelOffset = 785 sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() + 786 Resources.getIDChildren().size()) * 787 sizeof(coff_resource_dir_entry); 788 std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder; 789 uint32_t CurrentRelativeOffset = 0; 790 791 while (!Queue.empty()) { 792 auto CurrentNode = Queue.front(); 793 Queue.pop(); 794 auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart + 795 CurrentOffset); 796 Table->Characteristics = CurrentNode->getCharacteristics(); 797 Table->TimeDateStamp = 0; 798 Table->MajorVersion = CurrentNode->getMajorVersion(); 799 Table->MinorVersion = CurrentNode->getMinorVersion(); 800 auto &IDChildren = CurrentNode->getIDChildren(); 801 auto &StringChildren = CurrentNode->getStringChildren(); 802 Table->NumberOfNameEntries = StringChildren.size(); 803 Table->NumberOfIDEntries = IDChildren.size(); 804 CurrentOffset += sizeof(coff_resource_dir_table); 805 CurrentRelativeOffset += sizeof(coff_resource_dir_table); 806 807 // Write the directory entries immediately following each directory table. 808 for (auto const &Child : StringChildren) { 809 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 810 CurrentOffset); 811 Entry->Identifier.setNameOffset( 812 StringTableOffsets[Child.second->getStringIndex()]); 813 if (Child.second->checkIsDataNode()) { 814 Entry->Offset.DataEntryOffset = NextLevelOffset; 815 NextLevelOffset += sizeof(coff_resource_data_entry); 816 DataEntriesTreeOrder.push_back(Child.second.get()); 817 } else { 818 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 819 NextLevelOffset += sizeof(coff_resource_dir_table) + 820 (Child.second->getStringChildren().size() + 821 Child.second->getIDChildren().size()) * 822 sizeof(coff_resource_dir_entry); 823 Queue.push(Child.second.get()); 824 } 825 CurrentOffset += sizeof(coff_resource_dir_entry); 826 CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 827 } 828 for (auto const &Child : IDChildren) { 829 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 830 CurrentOffset); 831 Entry->Identifier.ID = Child.first; 832 if (Child.second->checkIsDataNode()) { 833 Entry->Offset.DataEntryOffset = NextLevelOffset; 834 NextLevelOffset += sizeof(coff_resource_data_entry); 835 DataEntriesTreeOrder.push_back(Child.second.get()); 836 } else { 837 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 838 NextLevelOffset += sizeof(coff_resource_dir_table) + 839 (Child.second->getStringChildren().size() + 840 Child.second->getIDChildren().size()) * 841 sizeof(coff_resource_dir_entry); 842 Queue.push(Child.second.get()); 843 } 844 CurrentOffset += sizeof(coff_resource_dir_entry); 845 CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 846 } 847 } 848 849 RelocationAddresses.resize(Data.size()); 850 // Now write all the resource data entries. 851 for (auto DataNodes : DataEntriesTreeOrder) { 852 auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart + 853 CurrentOffset); 854 RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset; 855 Entry->DataRVA = 0; // Set to zero because it is a relocation. 856 Entry->DataSize = Data[DataNodes->getDataIndex()].size(); 857 Entry->Codepage = 0; 858 Entry->Reserved = 0; 859 CurrentOffset += sizeof(coff_resource_data_entry); 860 CurrentRelativeOffset += sizeof(coff_resource_data_entry); 861 } 862 } 863 864 void WindowsResourceCOFFWriter::writeDirectoryStringTable() { 865 // Now write the directory string table for .rsrc$01 866 uint32_t TotalStringTableSize = 0; 867 for (auto &String : StringTable) { 868 uint16_t Length = String.size(); 869 support::endian::write16le(BufferStart + CurrentOffset, Length); 870 CurrentOffset += sizeof(uint16_t); 871 auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset); 872 llvm::copy(String, Start); 873 CurrentOffset += Length * sizeof(UTF16); 874 TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t); 875 } 876 CurrentOffset += 877 alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize; 878 } 879 880 void WindowsResourceCOFFWriter::writeFirstSectionRelocations() { 881 882 // Now write the relocations for .rsrc$01 883 // Five symbols already in table before we start, @feat.00 and 2 for each 884 // .rsrc section. 885 uint32_t NextSymbolIndex = 5; 886 for (unsigned i = 0; i < Data.size(); i++) { 887 auto *Reloc = 888 reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset); 889 Reloc->VirtualAddress = RelocationAddresses[i]; 890 Reloc->SymbolTableIndex = NextSymbolIndex++; 891 switch (MachineType) { 892 case COFF::IMAGE_FILE_MACHINE_ARMNT: 893 Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB; 894 break; 895 case COFF::IMAGE_FILE_MACHINE_AMD64: 896 Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB; 897 break; 898 case COFF::IMAGE_FILE_MACHINE_I386: 899 Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB; 900 break; 901 case COFF::IMAGE_FILE_MACHINE_ARM64: 902 Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB; 903 break; 904 default: 905 llvm_unreachable("unknown machine type"); 906 } 907 CurrentOffset += sizeof(coff_relocation); 908 } 909 } 910 911 Expected<std::unique_ptr<MemoryBuffer>> 912 writeWindowsResourceCOFF(COFF::MachineTypes MachineType, 913 const WindowsResourceParser &Parser, 914 uint32_t TimeDateStamp) { 915 Error E = Error::success(); 916 WindowsResourceCOFFWriter Writer(MachineType, Parser, E); 917 if (E) 918 return std::move(E); 919 return Writer.write(TimeDateStamp); 920 } 921 922 } // namespace object 923 } // namespace llvm 924