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