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