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 std::unique_ptr<MemoryBuffer> OutputBuffer; 329 char *BufferStart; 330 uint64_t CurrentOffset = 0; 331 COFF::MachineTypes MachineType; 332 const WindowsResourceParser::TreeNode &Resources; 333 const ArrayRef<std::vector<uint8_t>> Data; 334 uint64_t FileSize; 335 uint32_t SymbolTableOffset; 336 uint32_t SectionOneSize; 337 uint32_t SectionOneOffset; 338 uint32_t SectionOneRelocations; 339 uint32_t SectionTwoSize; 340 uint32_t SectionTwoOffset; 341 const ArrayRef<std::vector<UTF16>> StringTable; 342 std::vector<uint32_t> StringTableOffsets; 343 std::vector<uint32_t> DataOffsets; 344 std::vector<uint32_t> RelocationAddresses; 345 }; 346 347 WindowsResourceCOFFWriter::WindowsResourceCOFFWriter( 348 COFF::MachineTypes MachineType, const WindowsResourceParser &Parser, 349 Error &E) 350 : MachineType(MachineType), Resources(Parser.getTree()), 351 Data(Parser.getData()), StringTable(Parser.getStringTable()) { 352 performFileLayout(); 353 354 OutputBuffer = MemoryBuffer::getNewMemBuffer(FileSize); 355 } 356 357 void WindowsResourceCOFFWriter::performFileLayout() { 358 // Add size of COFF header. 359 FileSize = COFF::Header16Size; 360 361 // one .rsrc section header for directory tree, another for resource data. 362 FileSize += 2 * COFF::SectionSize; 363 364 performSectionOneLayout(); 365 performSectionTwoLayout(); 366 367 // We have reached the address of the symbol table. 368 SymbolTableOffset = FileSize; 369 370 FileSize += COFF::Symbol16Size; // size of the @feat.00 symbol. 371 FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section. 372 FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource. 373 FileSize += 4; // four null bytes for the string table. 374 } 375 376 void WindowsResourceCOFFWriter::performSectionOneLayout() { 377 SectionOneOffset = FileSize; 378 379 SectionOneSize = Resources.getTreeSize(); 380 uint32_t CurrentStringOffset = SectionOneSize; 381 uint32_t TotalStringTableSize = 0; 382 for (auto const &String : StringTable) { 383 StringTableOffsets.push_back(CurrentStringOffset); 384 uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t); 385 CurrentStringOffset += StringSize; 386 TotalStringTableSize += StringSize; 387 } 388 SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t)); 389 390 // account for the relocations of section one. 391 SectionOneRelocations = FileSize + SectionOneSize; 392 FileSize += SectionOneSize; 393 FileSize += 394 Data.size() * COFF::RelocationSize; // one relocation for each resource. 395 FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 396 } 397 398 void WindowsResourceCOFFWriter::performSectionTwoLayout() { 399 // add size of .rsrc$2 section, which contains all resource data on 8-byte 400 // alignment. 401 SectionTwoOffset = FileSize; 402 SectionTwoSize = 0; 403 for (auto const &Entry : Data) { 404 DataOffsets.push_back(SectionTwoSize); 405 SectionTwoSize += alignTo(Entry.size(), sizeof(uint64_t)); 406 } 407 FileSize += SectionTwoSize; 408 FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 409 } 410 411 static std::time_t getTime() { 412 std::time_t Now = time(nullptr); 413 if (Now < 0 || !isUInt<32>(Now)) 414 return UINT32_MAX; 415 return Now; 416 } 417 418 std::unique_ptr<MemoryBuffer> WindowsResourceCOFFWriter::write() { 419 BufferStart = const_cast<char *>(OutputBuffer->getBufferStart()); 420 421 writeCOFFHeader(); 422 writeFirstSectionHeader(); 423 writeSecondSectionHeader(); 424 writeFirstSection(); 425 writeSecondSection(); 426 writeSymbolTable(); 427 writeStringTable(); 428 429 return std::move(OutputBuffer); 430 } 431 432 void WindowsResourceCOFFWriter::writeCOFFHeader() { 433 // Write the COFF header. 434 auto *Header = reinterpret_cast<coff_file_header *>(BufferStart); 435 switch (MachineType) { 436 case COFF::IMAGE_FILE_MACHINE_ARMNT: 437 Header->Machine = COFF::IMAGE_FILE_MACHINE_ARMNT; 438 break; 439 case COFF::IMAGE_FILE_MACHINE_AMD64: 440 Header->Machine = COFF::IMAGE_FILE_MACHINE_AMD64; 441 break; 442 case COFF::IMAGE_FILE_MACHINE_I386: 443 Header->Machine = COFF::IMAGE_FILE_MACHINE_I386; 444 break; 445 default: 446 Header->Machine = COFF::IMAGE_FILE_MACHINE_UNKNOWN; 447 } 448 Header->NumberOfSections = 2; 449 Header->TimeDateStamp = getTime(); 450 Header->PointerToSymbolTable = SymbolTableOffset; 451 // One symbol for every resource plus 2 for each section and @feat.00 452 Header->NumberOfSymbols = Data.size() + 5; 453 Header->SizeOfOptionalHeader = 0; 454 Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE; 455 } 456 457 void WindowsResourceCOFFWriter::writeFirstSectionHeader() { 458 // Write the first section header. 459 CurrentOffset += sizeof(coff_file_header); 460 auto *SectionOneHeader = 461 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 462 strncpy(SectionOneHeader->Name, ".rsrc$01", (size_t)COFF::NameSize); 463 SectionOneHeader->VirtualSize = 0; 464 SectionOneHeader->VirtualAddress = 0; 465 SectionOneHeader->SizeOfRawData = SectionOneSize; 466 SectionOneHeader->PointerToRawData = SectionOneOffset; 467 SectionOneHeader->PointerToRelocations = SectionOneRelocations; 468 SectionOneHeader->PointerToLinenumbers = 0; 469 SectionOneHeader->NumberOfRelocations = Data.size(); 470 SectionOneHeader->NumberOfLinenumbers = 0; 471 SectionOneHeader->Characteristics = COFF::IMAGE_SCN_ALIGN_1BYTES; 472 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 473 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 474 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 475 } 476 477 void WindowsResourceCOFFWriter::writeSecondSectionHeader() { 478 // Write the second section header. 479 CurrentOffset += sizeof(coff_section); 480 auto *SectionTwoHeader = 481 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 482 strncpy(SectionTwoHeader->Name, ".rsrc$02", (size_t)COFF::NameSize); 483 SectionTwoHeader->VirtualSize = 0; 484 SectionTwoHeader->VirtualAddress = 0; 485 SectionTwoHeader->SizeOfRawData = SectionTwoSize; 486 SectionTwoHeader->PointerToRawData = SectionTwoOffset; 487 SectionTwoHeader->PointerToRelocations = 0; 488 SectionTwoHeader->PointerToLinenumbers = 0; 489 SectionTwoHeader->NumberOfRelocations = 0; 490 SectionTwoHeader->NumberOfLinenumbers = 0; 491 SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 492 SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 493 } 494 495 void WindowsResourceCOFFWriter::writeFirstSection() { 496 // Write section one. 497 CurrentOffset += sizeof(coff_section); 498 499 writeDirectoryTree(); 500 writeDirectoryStringTable(); 501 writeFirstSectionRelocations(); 502 503 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 504 } 505 506 void WindowsResourceCOFFWriter::writeSecondSection() { 507 // Now write the .rsrc$02 section. 508 for (auto const &RawDataEntry : Data) { 509 std::copy(RawDataEntry.begin(), RawDataEntry.end(), 510 BufferStart + CurrentOffset); 511 CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t)); 512 } 513 514 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 515 } 516 517 void WindowsResourceCOFFWriter::writeSymbolTable() { 518 // Now write the symbol table. 519 // First, the feat symbol. 520 auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 521 strncpy(Symbol->Name.ShortName, "@feat.00", (size_t)COFF::NameSize); 522 Symbol->Value = 0x11; 523 Symbol->SectionNumber = 0xffff; 524 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 525 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 526 Symbol->NumberOfAuxSymbols = 0; 527 CurrentOffset += sizeof(coff_symbol16); 528 529 // Now write the .rsrc1 symbol + aux. 530 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 531 strncpy(Symbol->Name.ShortName, ".rsrc$01", (size_t)COFF::NameSize); 532 Symbol->Value = 0; 533 Symbol->SectionNumber = 1; 534 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 535 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 536 Symbol->NumberOfAuxSymbols = 1; 537 CurrentOffset += sizeof(coff_symbol16); 538 auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 539 CurrentOffset); 540 Aux->Length = SectionOneSize; 541 Aux->NumberOfRelocations = Data.size(); 542 Aux->NumberOfLinenumbers = 0; 543 Aux->CheckSum = 0; 544 Aux->NumberLowPart = 0; 545 Aux->Selection = 0; 546 CurrentOffset += sizeof(coff_aux_section_definition); 547 548 // Now write the .rsrc2 symbol + aux. 549 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 550 strncpy(Symbol->Name.ShortName, ".rsrc$02", (size_t)COFF::NameSize); 551 Symbol->Value = 0; 552 Symbol->SectionNumber = 2; 553 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 554 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 555 Symbol->NumberOfAuxSymbols = 1; 556 CurrentOffset += sizeof(coff_symbol16); 557 Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 558 CurrentOffset); 559 Aux->Length = SectionTwoSize; 560 Aux->NumberOfRelocations = 0; 561 Aux->NumberOfLinenumbers = 0; 562 Aux->CheckSum = 0; 563 Aux->NumberLowPart = 0; 564 Aux->Selection = 0; 565 CurrentOffset += sizeof(coff_aux_section_definition); 566 567 // Now write a symbol for each relocation. 568 for (unsigned i = 0; i < Data.size(); i++) { 569 char RelocationName[9]; 570 sprintf(RelocationName, "$R%06X", DataOffsets[i]); 571 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 572 strncpy(Symbol->Name.ShortName, RelocationName, (size_t)COFF::NameSize); 573 Symbol->Value = DataOffsets[i]; 574 Symbol->SectionNumber = 1; 575 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 576 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 577 Symbol->NumberOfAuxSymbols = 0; 578 CurrentOffset += sizeof(coff_symbol16); 579 } 580 } 581 582 void WindowsResourceCOFFWriter::writeStringTable() { 583 // Just 4 null bytes for the string table. 584 auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset); 585 memset(COFFStringTable, 0, 4); 586 } 587 588 void WindowsResourceCOFFWriter::writeDirectoryTree() { 589 // Traverse parsed resource tree breadth-first and write the corresponding 590 // COFF objects. 591 std::queue<const WindowsResourceParser::TreeNode *> Queue; 592 Queue.push(&Resources); 593 uint32_t NextLevelOffset = 594 sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() + 595 Resources.getIDChildren().size()) * 596 sizeof(coff_resource_dir_entry); 597 std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder; 598 uint32_t CurrentRelativeOffset = 0; 599 600 while (!Queue.empty()) { 601 auto CurrentNode = Queue.front(); 602 Queue.pop(); 603 auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart + 604 CurrentOffset); 605 Table->Characteristics = CurrentNode->getCharacteristics(); 606 Table->TimeDateStamp = 0; 607 Table->MajorVersion = CurrentNode->getMajorVersion(); 608 Table->MinorVersion = CurrentNode->getMinorVersion(); 609 auto &IDChildren = CurrentNode->getIDChildren(); 610 auto &StringChildren = CurrentNode->getStringChildren(); 611 Table->NumberOfNameEntries = StringChildren.size(); 612 Table->NumberOfIDEntries = IDChildren.size(); 613 CurrentOffset += sizeof(coff_resource_dir_table); 614 CurrentRelativeOffset += sizeof(coff_resource_dir_table); 615 616 // Write the directory entries immediately following each directory table. 617 for (auto const &Child : StringChildren) { 618 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 619 CurrentOffset); 620 Entry->Identifier.NameOffset = 621 StringTableOffsets[Child.second->getStringIndex()]; 622 if (Child.second->checkIsDataNode()) { 623 Entry->Offset.DataEntryOffset = NextLevelOffset; 624 NextLevelOffset += sizeof(coff_resource_data_entry); 625 DataEntriesTreeOrder.push_back(Child.second.get()); 626 } else { 627 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 628 NextLevelOffset += sizeof(coff_resource_dir_table) + 629 (Child.second->getStringChildren().size() + 630 Child.second->getIDChildren().size()) * 631 sizeof(coff_resource_dir_entry); 632 Queue.push(Child.second.get()); 633 } 634 CurrentOffset += sizeof(coff_resource_dir_entry); 635 CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 636 } 637 for (auto const &Child : IDChildren) { 638 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 639 CurrentOffset); 640 Entry->Identifier.ID = Child.first; 641 if (Child.second->checkIsDataNode()) { 642 Entry->Offset.DataEntryOffset = NextLevelOffset; 643 NextLevelOffset += sizeof(coff_resource_data_entry); 644 DataEntriesTreeOrder.push_back(Child.second.get()); 645 } else { 646 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 647 NextLevelOffset += sizeof(coff_resource_dir_table) + 648 (Child.second->getStringChildren().size() + 649 Child.second->getIDChildren().size()) * 650 sizeof(coff_resource_dir_entry); 651 Queue.push(Child.second.get()); 652 } 653 CurrentOffset += sizeof(coff_resource_dir_entry); 654 CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 655 } 656 } 657 658 RelocationAddresses.resize(Data.size()); 659 // Now write all the resource data entries. 660 for (auto DataNodes : DataEntriesTreeOrder) { 661 auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart + 662 CurrentOffset); 663 RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset; 664 Entry->DataRVA = 0; // Set to zero because it is a relocation. 665 Entry->DataSize = Data[DataNodes->getDataIndex()].size(); 666 Entry->Codepage = 0; 667 Entry->Reserved = 0; 668 CurrentOffset += sizeof(coff_resource_data_entry); 669 CurrentRelativeOffset += sizeof(coff_resource_data_entry); 670 } 671 } 672 673 void WindowsResourceCOFFWriter::writeDirectoryStringTable() { 674 // Now write the directory string table for .rsrc$01 675 uint32_t TotalStringTableSize = 0; 676 for (auto &String : StringTable) { 677 uint16_t Length = String.size(); 678 support::endian::write16le(BufferStart + CurrentOffset, Length); 679 CurrentOffset += sizeof(uint16_t); 680 auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset); 681 std::copy(String.begin(), String.end(), Start); 682 CurrentOffset += Length * sizeof(UTF16); 683 TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t); 684 } 685 CurrentOffset += 686 alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize; 687 } 688 689 void WindowsResourceCOFFWriter::writeFirstSectionRelocations() { 690 691 // Now write the relocations for .rsrc$01 692 // Five symbols already in table before we start, @feat.00 and 2 for each 693 // .rsrc section. 694 uint32_t NextSymbolIndex = 5; 695 for (unsigned i = 0; i < Data.size(); i++) { 696 auto *Reloc = 697 reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset); 698 Reloc->VirtualAddress = RelocationAddresses[i]; 699 Reloc->SymbolTableIndex = NextSymbolIndex++; 700 switch (MachineType) { 701 case COFF::IMAGE_FILE_MACHINE_ARMNT: 702 Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB; 703 break; 704 case COFF::IMAGE_FILE_MACHINE_AMD64: 705 Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB; 706 break; 707 case COFF::IMAGE_FILE_MACHINE_I386: 708 Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB; 709 break; 710 default: 711 Reloc->Type = 0; 712 } 713 CurrentOffset += sizeof(coff_relocation); 714 } 715 } 716 717 Expected<std::unique_ptr<MemoryBuffer>> 718 writeWindowsResourceCOFF(COFF::MachineTypes MachineType, 719 const WindowsResourceParser &Parser) { 720 Error E = Error::success(); 721 WindowsResourceCOFFWriter Writer(MachineType, Parser, E); 722 if (E) 723 return std::move(E); 724 return Writer.write(); 725 } 726 727 } // namespace object 728 } // namespace llvm 729