Home | History | Annotate | Download | only in Object
      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/FormatVariadic.h"
     18 #include "llvm/Support/MathExtras.h"
     19 #include <ctime>
     20 #include <queue>
     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) {}
     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<WritableMemoryBuffer> 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 = WritableMemoryBuffer::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 = 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   Header->Machine = MachineType;
    445   Header->NumberOfSections = 2;
    446   Header->TimeDateStamp = getTime();
    447   Header->PointerToSymbolTable = SymbolTableOffset;
    448   // One symbol for every resource plus 2 for each section and @feat.00
    449   Header->NumberOfSymbols = Data.size() + 5;
    450   Header->SizeOfOptionalHeader = 0;
    451   Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE;
    452 }
    453 
    454 void WindowsResourceCOFFWriter::writeFirstSectionHeader() {
    455   // Write the first section header.
    456   CurrentOffset += sizeof(coff_file_header);
    457   auto *SectionOneHeader =
    458       reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);
    459   strncpy(SectionOneHeader->Name, ".rsrc$01", (size_t)COFF::NameSize);
    460   SectionOneHeader->VirtualSize = 0;
    461   SectionOneHeader->VirtualAddress = 0;
    462   SectionOneHeader->SizeOfRawData = SectionOneSize;
    463   SectionOneHeader->PointerToRawData = SectionOneOffset;
    464   SectionOneHeader->PointerToRelocations = SectionOneRelocations;
    465   SectionOneHeader->PointerToLinenumbers = 0;
    466   SectionOneHeader->NumberOfRelocations = Data.size();
    467   SectionOneHeader->NumberOfLinenumbers = 0;
    468   SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
    469   SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;
    470 }
    471 
    472 void WindowsResourceCOFFWriter::writeSecondSectionHeader() {
    473   // Write the second section header.
    474   CurrentOffset += sizeof(coff_section);
    475   auto *SectionTwoHeader =
    476       reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);
    477   strncpy(SectionTwoHeader->Name, ".rsrc$02", (size_t)COFF::NameSize);
    478   SectionTwoHeader->VirtualSize = 0;
    479   SectionTwoHeader->VirtualAddress = 0;
    480   SectionTwoHeader->SizeOfRawData = SectionTwoSize;
    481   SectionTwoHeader->PointerToRawData = SectionTwoOffset;
    482   SectionTwoHeader->PointerToRelocations = 0;
    483   SectionTwoHeader->PointerToLinenumbers = 0;
    484   SectionTwoHeader->NumberOfRelocations = 0;
    485   SectionTwoHeader->NumberOfLinenumbers = 0;
    486   SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
    487   SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;
    488 }
    489 
    490 void WindowsResourceCOFFWriter::writeFirstSection() {
    491   // Write section one.
    492   CurrentOffset += sizeof(coff_section);
    493 
    494   writeDirectoryTree();
    495   writeDirectoryStringTable();
    496   writeFirstSectionRelocations();
    497 
    498   CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);
    499 }
    500 
    501 void WindowsResourceCOFFWriter::writeSecondSection() {
    502   // Now write the .rsrc$02 section.
    503   for (auto const &RawDataEntry : Data) {
    504     std::copy(RawDataEntry.begin(), RawDataEntry.end(),
    505               BufferStart + CurrentOffset);
    506     CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t));
    507   }
    508 
    509   CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);
    510 }
    511 
    512 void WindowsResourceCOFFWriter::writeSymbolTable() {
    513   // Now write the symbol table.
    514   // First, the feat symbol.
    515   auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
    516   strncpy(Symbol->Name.ShortName, "@feat.00", (size_t)COFF::NameSize);
    517   Symbol->Value = 0x11;
    518   Symbol->SectionNumber = 0xffff;
    519   Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
    520   Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
    521   Symbol->NumberOfAuxSymbols = 0;
    522   CurrentOffset += sizeof(coff_symbol16);
    523 
    524   // Now write the .rsrc1 symbol + aux.
    525   Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
    526   strncpy(Symbol->Name.ShortName, ".rsrc$01", (size_t)COFF::NameSize);
    527   Symbol->Value = 0;
    528   Symbol->SectionNumber = 1;
    529   Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
    530   Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
    531   Symbol->NumberOfAuxSymbols = 1;
    532   CurrentOffset += sizeof(coff_symbol16);
    533   auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +
    534                                                               CurrentOffset);
    535   Aux->Length = SectionOneSize;
    536   Aux->NumberOfRelocations = Data.size();
    537   Aux->NumberOfLinenumbers = 0;
    538   Aux->CheckSum = 0;
    539   Aux->NumberLowPart = 0;
    540   Aux->Selection = 0;
    541   CurrentOffset += sizeof(coff_aux_section_definition);
    542 
    543   // Now write the .rsrc2 symbol + aux.
    544   Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
    545   strncpy(Symbol->Name.ShortName, ".rsrc$02", (size_t)COFF::NameSize);
    546   Symbol->Value = 0;
    547   Symbol->SectionNumber = 2;
    548   Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
    549   Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
    550   Symbol->NumberOfAuxSymbols = 1;
    551   CurrentOffset += sizeof(coff_symbol16);
    552   Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +
    553                                                         CurrentOffset);
    554   Aux->Length = SectionTwoSize;
    555   Aux->NumberOfRelocations = 0;
    556   Aux->NumberOfLinenumbers = 0;
    557   Aux->CheckSum = 0;
    558   Aux->NumberLowPart = 0;
    559   Aux->Selection = 0;
    560   CurrentOffset += sizeof(coff_aux_section_definition);
    561 
    562   // Now write a symbol for each relocation.
    563   for (unsigned i = 0; i < Data.size(); i++) {
    564     auto RelocationName = formatv("$R{0:X-6}", i & 0xffffff).sstr<COFF::NameSize>();
    565     Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
    566     memcpy(Symbol->Name.ShortName, RelocationName.data(), (size_t) COFF::NameSize);
    567     Symbol->Value = DataOffsets[i];
    568     Symbol->SectionNumber = 2;
    569     Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
    570     Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
    571     Symbol->NumberOfAuxSymbols = 0;
    572     CurrentOffset += sizeof(coff_symbol16);
    573   }
    574 }
    575 
    576 void WindowsResourceCOFFWriter::writeStringTable() {
    577   // Just 4 null bytes for the string table.
    578   auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset);
    579   memset(COFFStringTable, 0, 4);
    580 }
    581 
    582 void WindowsResourceCOFFWriter::writeDirectoryTree() {
    583   // Traverse parsed resource tree breadth-first and write the corresponding
    584   // COFF objects.
    585   std::queue<const WindowsResourceParser::TreeNode *> Queue;
    586   Queue.push(&Resources);
    587   uint32_t NextLevelOffset =
    588       sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() +
    589                                          Resources.getIDChildren().size()) *
    590                                             sizeof(coff_resource_dir_entry);
    591   std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder;
    592   uint32_t CurrentRelativeOffset = 0;
    593 
    594   while (!Queue.empty()) {
    595     auto CurrentNode = Queue.front();
    596     Queue.pop();
    597     auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart +
    598                                                               CurrentOffset);
    599     Table->Characteristics = CurrentNode->getCharacteristics();
    600     Table->TimeDateStamp = 0;
    601     Table->MajorVersion = CurrentNode->getMajorVersion();
    602     Table->MinorVersion = CurrentNode->getMinorVersion();
    603     auto &IDChildren = CurrentNode->getIDChildren();
    604     auto &StringChildren = CurrentNode->getStringChildren();
    605     Table->NumberOfNameEntries = StringChildren.size();
    606     Table->NumberOfIDEntries = IDChildren.size();
    607     CurrentOffset += sizeof(coff_resource_dir_table);
    608     CurrentRelativeOffset += sizeof(coff_resource_dir_table);
    609 
    610     // Write the directory entries immediately following each directory table.
    611     for (auto const &Child : StringChildren) {
    612       auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +
    613                                                                 CurrentOffset);
    614       Entry->Identifier.setNameOffset(
    615           StringTableOffsets[Child.second->getStringIndex()]);
    616       if (Child.second->checkIsDataNode()) {
    617         Entry->Offset.DataEntryOffset = NextLevelOffset;
    618         NextLevelOffset += sizeof(coff_resource_data_entry);
    619         DataEntriesTreeOrder.push_back(Child.second.get());
    620       } else {
    621         Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);
    622         NextLevelOffset += sizeof(coff_resource_dir_table) +
    623                            (Child.second->getStringChildren().size() +
    624                             Child.second->getIDChildren().size()) *
    625                                sizeof(coff_resource_dir_entry);
    626         Queue.push(Child.second.get());
    627       }
    628       CurrentOffset += sizeof(coff_resource_dir_entry);
    629       CurrentRelativeOffset += sizeof(coff_resource_dir_entry);
    630     }
    631     for (auto const &Child : IDChildren) {
    632       auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +
    633                                                                 CurrentOffset);
    634       Entry->Identifier.ID = Child.first;
    635       if (Child.second->checkIsDataNode()) {
    636         Entry->Offset.DataEntryOffset = NextLevelOffset;
    637         NextLevelOffset += sizeof(coff_resource_data_entry);
    638         DataEntriesTreeOrder.push_back(Child.second.get());
    639       } else {
    640         Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);
    641         NextLevelOffset += sizeof(coff_resource_dir_table) +
    642                            (Child.second->getStringChildren().size() +
    643                             Child.second->getIDChildren().size()) *
    644                                sizeof(coff_resource_dir_entry);
    645         Queue.push(Child.second.get());
    646       }
    647       CurrentOffset += sizeof(coff_resource_dir_entry);
    648       CurrentRelativeOffset += sizeof(coff_resource_dir_entry);
    649     }
    650   }
    651 
    652   RelocationAddresses.resize(Data.size());
    653   // Now write all the resource data entries.
    654   for (auto DataNodes : DataEntriesTreeOrder) {
    655     auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart +
    656                                                                CurrentOffset);
    657     RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset;
    658     Entry->DataRVA = 0; // Set to zero because it is a relocation.
    659     Entry->DataSize = Data[DataNodes->getDataIndex()].size();
    660     Entry->Codepage = 0;
    661     Entry->Reserved = 0;
    662     CurrentOffset += sizeof(coff_resource_data_entry);
    663     CurrentRelativeOffset += sizeof(coff_resource_data_entry);
    664   }
    665 }
    666 
    667 void WindowsResourceCOFFWriter::writeDirectoryStringTable() {
    668   // Now write the directory string table for .rsrc$01
    669   uint32_t TotalStringTableSize = 0;
    670   for (auto &String : StringTable) {
    671     uint16_t Length = String.size();
    672     support::endian::write16le(BufferStart + CurrentOffset, Length);
    673     CurrentOffset += sizeof(uint16_t);
    674     auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset);
    675     std::copy(String.begin(), String.end(), Start);
    676     CurrentOffset += Length * sizeof(UTF16);
    677     TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t);
    678   }
    679   CurrentOffset +=
    680       alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize;
    681 }
    682 
    683 void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {
    684 
    685   // Now write the relocations for .rsrc$01
    686   // Five symbols already in table before we start, @feat.00 and 2 for each
    687   // .rsrc section.
    688   uint32_t NextSymbolIndex = 5;
    689   for (unsigned i = 0; i < Data.size(); i++) {
    690     auto *Reloc =
    691         reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset);
    692     Reloc->VirtualAddress = RelocationAddresses[i];
    693     Reloc->SymbolTableIndex = NextSymbolIndex++;
    694     switch (MachineType) {
    695     case COFF::IMAGE_FILE_MACHINE_ARMNT:
    696       Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB;
    697       break;
    698     case COFF::IMAGE_FILE_MACHINE_AMD64:
    699       Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB;
    700       break;
    701     case COFF::IMAGE_FILE_MACHINE_I386:
    702       Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB;
    703       break;
    704     case COFF::IMAGE_FILE_MACHINE_ARM64:
    705       Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB;
    706       break;
    707     default:
    708       llvm_unreachable("unknown machine type");
    709     }
    710     CurrentOffset += sizeof(coff_relocation);
    711   }
    712 }
    713 
    714 Expected<std::unique_ptr<MemoryBuffer>>
    715 writeWindowsResourceCOFF(COFF::MachineTypes MachineType,
    716                          const WindowsResourceParser &Parser) {
    717   Error E = Error::success();
    718   WindowsResourceCOFFWriter Writer(MachineType, Parser, E);
    719   if (E)
    720     return std::move(E);
    721   return Writer.write();
    722 }
    723 
    724 } // namespace object
    725 } // namespace llvm
    726