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