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