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