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