| //===-- WindowsResource.cpp -------------------------------------*- C++ -*-===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | // | 
 | // This file implements the .res file class. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "llvm/Object/WindowsResource.h" | 
 | #include "llvm/Object/COFF.h" | 
 | #include "llvm/Support/FileOutputBuffer.h" | 
 | #include "llvm/Support/FormatVariadic.h" | 
 | #include "llvm/Support/MathExtras.h" | 
 | #include "llvm/Support/ScopedPrinter.h" | 
 | #include <ctime> | 
 | #include <queue> | 
 | #include <system_error> | 
 |  | 
 | using namespace llvm; | 
 | using namespace object; | 
 |  | 
 | namespace llvm { | 
 | namespace object { | 
 |  | 
 | #define RETURN_IF_ERROR(X)                                                     \ | 
 |   if (auto EC = X)                                                             \ | 
 |     return EC; | 
 |  | 
 | #define UNWRAP_REF_OR_RETURN(Name, Expr)                                       \ | 
 |   auto Name##OrErr = Expr;                                                     \ | 
 |   if (!Name##OrErr)                                                            \ | 
 |     return Name##OrErr.takeError();                                            \ | 
 |   const auto &Name = *Name##OrErr; | 
 |  | 
 | #define UNWRAP_OR_RETURN(Name, Expr)                                           \ | 
 |   auto Name##OrErr = Expr;                                                     \ | 
 |   if (!Name##OrErr)                                                            \ | 
 |     return Name##OrErr.takeError();                                            \ | 
 |   auto Name = *Name##OrErr; | 
 |  | 
 | const uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t); | 
 |  | 
 | // COFF files seem to be inconsistent with alignment between sections, just use | 
 | // 8-byte because it makes everyone happy. | 
 | const uint32_t SECTION_ALIGNMENT = sizeof(uint64_t); | 
 |  | 
 | WindowsResource::WindowsResource(MemoryBufferRef Source) | 
 |     : Binary(Binary::ID_WinRes, Source) { | 
 |   size_t LeadingSize = WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE; | 
 |   BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize), | 
 |                          support::little); | 
 | } | 
 |  | 
 | // static | 
 | Expected<std::unique_ptr<WindowsResource>> | 
 | WindowsResource::createWindowsResource(MemoryBufferRef Source) { | 
 |   if (Source.getBufferSize() < WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE) | 
 |     return make_error<GenericBinaryError>( | 
 |         Source.getBufferIdentifier() + ": too small to be a resource file", | 
 |         object_error::invalid_file_type); | 
 |   std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source)); | 
 |   return std::move(Ret); | 
 | } | 
 |  | 
 | Expected<ResourceEntryRef> WindowsResource::getHeadEntry() { | 
 |   if (BBS.getLength() < sizeof(WinResHeaderPrefix) + sizeof(WinResHeaderSuffix)) | 
 |     return make_error<EmptyResError>(getFileName() + " contains no entries", | 
 |                                      object_error::unexpected_eof); | 
 |   return ResourceEntryRef::create(BinaryStreamRef(BBS), this); | 
 | } | 
 |  | 
 | ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref, | 
 |                                    const WindowsResource *Owner) | 
 |     : Reader(Ref), Owner(Owner) {} | 
 |  | 
 | Expected<ResourceEntryRef> | 
 | ResourceEntryRef::create(BinaryStreamRef BSR, const WindowsResource *Owner) { | 
 |   auto Ref = ResourceEntryRef(BSR, Owner); | 
 |   if (auto E = Ref.loadNext()) | 
 |     return std::move(E); | 
 |   return Ref; | 
 | } | 
 |  | 
 | Error ResourceEntryRef::moveNext(bool &End) { | 
 |   // Reached end of all the entries. | 
 |   if (Reader.bytesRemaining() == 0) { | 
 |     End = true; | 
 |     return Error::success(); | 
 |   } | 
 |   RETURN_IF_ERROR(loadNext()); | 
 |  | 
 |   return Error::success(); | 
 | } | 
 |  | 
 | static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID, | 
 |                             ArrayRef<UTF16> &Str, bool &IsString) { | 
 |   uint16_t IDFlag; | 
 |   RETURN_IF_ERROR(Reader.readInteger(IDFlag)); | 
 |   IsString = IDFlag != 0xffff; | 
 |  | 
 |   if (IsString) { | 
 |     Reader.setOffset( | 
 |         Reader.getOffset() - | 
 |         sizeof(uint16_t)); // Re-read the bytes which we used to check the flag. | 
 |     RETURN_IF_ERROR(Reader.readWideString(Str)); | 
 |   } else | 
 |     RETURN_IF_ERROR(Reader.readInteger(ID)); | 
 |  | 
 |   return Error::success(); | 
 | } | 
 |  | 
 | Error ResourceEntryRef::loadNext() { | 
 |   const WinResHeaderPrefix *Prefix; | 
 |   RETURN_IF_ERROR(Reader.readObject(Prefix)); | 
 |  | 
 |   if (Prefix->HeaderSize < MIN_HEADER_SIZE) | 
 |     return make_error<GenericBinaryError>(Owner->getFileName() + | 
 |                                               ": header size too small", | 
 |                                           object_error::parse_failed); | 
 |  | 
 |   RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType)); | 
 |  | 
 |   RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName)); | 
 |  | 
 |   RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_HEADER_ALIGNMENT)); | 
 |  | 
 |   RETURN_IF_ERROR(Reader.readObject(Suffix)); | 
 |  | 
 |   RETURN_IF_ERROR(Reader.readArray(Data, Prefix->DataSize)); | 
 |  | 
 |   RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_DATA_ALIGNMENT)); | 
 |  | 
 |   return Error::success(); | 
 | } | 
 |  | 
 | WindowsResourceParser::WindowsResourceParser(bool MinGW) | 
 |     : Root(false), MinGW(MinGW) {} | 
 |  | 
 | void printResourceTypeName(uint16_t TypeID, raw_ostream &OS) { | 
 |   switch (TypeID) { | 
 |   case  1: OS << "CURSOR (ID 1)"; break; | 
 |   case  2: OS << "BITMAP (ID 2)"; break; | 
 |   case  3: OS << "ICON (ID 3)"; break; | 
 |   case  4: OS << "MENU (ID 4)"; break; | 
 |   case  5: OS << "DIALOG (ID 5)"; break; | 
 |   case  6: OS << "STRINGTABLE (ID 6)"; break; | 
 |   case  7: OS << "FONTDIR (ID 7)"; break; | 
 |   case  8: OS << "FONT (ID 8)"; break; | 
 |   case  9: OS << "ACCELERATOR (ID 9)"; break; | 
 |   case 10: OS << "RCDATA (ID 10)"; break; | 
 |   case 11: OS << "MESSAGETABLE (ID 11)"; break; | 
 |   case 12: OS << "GROUP_CURSOR (ID 12)"; break; | 
 |   case 14: OS << "GROUP_ICON (ID 14)"; break; | 
 |   case 16: OS << "VERSIONINFO (ID 16)"; break; | 
 |   case 17: OS << "DLGINCLUDE (ID 17)"; break; | 
 |   case 19: OS << "PLUGPLAY (ID 19)"; break; | 
 |   case 20: OS << "VXD (ID 20)"; break; | 
 |   case 21: OS << "ANICURSOR (ID 21)"; break; | 
 |   case 22: OS << "ANIICON (ID 22)"; break; | 
 |   case 23: OS << "HTML (ID 23)"; break; | 
 |   case 24: OS << "MANIFEST (ID 24)"; break; | 
 |   default: OS << "ID " << TypeID; break; | 
 |   } | 
 | } | 
 |  | 
 | static bool convertUTF16LEToUTF8String(ArrayRef<UTF16> Src, std::string &Out) { | 
 |   if (!sys::IsBigEndianHost) | 
 |     return convertUTF16ToUTF8String(Src, Out); | 
 |  | 
 |   std::vector<UTF16> EndianCorrectedSrc; | 
 |   EndianCorrectedSrc.resize(Src.size() + 1); | 
 |   llvm::copy(Src, EndianCorrectedSrc.begin() + 1); | 
 |   EndianCorrectedSrc[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED; | 
 |   return convertUTF16ToUTF8String(makeArrayRef(EndianCorrectedSrc), Out); | 
 | } | 
 |  | 
 | static std::string makeDuplicateResourceError( | 
 |     const ResourceEntryRef &Entry, StringRef File1, StringRef File2) { | 
 |   std::string Ret; | 
 |   raw_string_ostream OS(Ret); | 
 |  | 
 |   OS << "duplicate resource:"; | 
 |  | 
 |   OS << " type "; | 
 |   if (Entry.checkTypeString()) { | 
 |     std::string UTF8; | 
 |     if (!convertUTF16LEToUTF8String(Entry.getTypeString(), UTF8)) | 
 |       UTF8 = "(failed conversion from UTF16)"; | 
 |     OS << '\"' << UTF8 << '\"'; | 
 |   } else | 
 |     printResourceTypeName(Entry.getTypeID(), OS); | 
 |  | 
 |   OS << "/name "; | 
 |   if (Entry.checkNameString()) { | 
 |     std::string UTF8; | 
 |     if (!convertUTF16LEToUTF8String(Entry.getNameString(), UTF8)) | 
 |       UTF8 = "(failed conversion from UTF16)"; | 
 |     OS << '\"' << UTF8 << '\"'; | 
 |   } else { | 
 |     OS << "ID " << Entry.getNameID(); | 
 |   } | 
 |  | 
 |   OS << "/language " << Entry.getLanguage() << ", in " << File1 << " and in " | 
 |      << File2; | 
 |  | 
 |   return OS.str(); | 
 | } | 
 |  | 
 | static void printStringOrID(const WindowsResourceParser::StringOrID &S, | 
 |                             raw_string_ostream &OS, bool IsType, bool IsID) { | 
 |   if (S.IsString) { | 
 |     std::string UTF8; | 
 |     if (!convertUTF16LEToUTF8String(S.String, UTF8)) | 
 |       UTF8 = "(failed conversion from UTF16)"; | 
 |     OS << '\"' << UTF8 << '\"'; | 
 |   } else if (IsType) | 
 |     printResourceTypeName(S.ID, OS); | 
 |   else if (IsID) | 
 |     OS << "ID " << S.ID; | 
 |   else | 
 |     OS << S.ID; | 
 | } | 
 |  | 
 | static std::string makeDuplicateResourceError( | 
 |     const std::vector<WindowsResourceParser::StringOrID> &Context, | 
 |     StringRef File1, StringRef File2) { | 
 |   std::string Ret; | 
 |   raw_string_ostream OS(Ret); | 
 |  | 
 |   OS << "duplicate resource:"; | 
 |  | 
 |   if (Context.size() >= 1) { | 
 |     OS << " type "; | 
 |     printStringOrID(Context[0], OS, /* IsType */ true, /* IsID */ true); | 
 |   } | 
 |  | 
 |   if (Context.size() >= 2) { | 
 |     OS << "/name "; | 
 |     printStringOrID(Context[1], OS, /* IsType */ false, /* IsID */ true); | 
 |   } | 
 |  | 
 |   if (Context.size() >= 3) { | 
 |     OS << "/language "; | 
 |     printStringOrID(Context[2], OS, /* IsType */ false, /* IsID */ false); | 
 |   } | 
 |   OS << ", in " << File1 << " and in " << File2; | 
 |  | 
 |   return OS.str(); | 
 | } | 
 |  | 
 | // MinGW specific. Remove default manifests (with language zero) if there are | 
 | // other manifests present, and report an error if there are more than one | 
 | // manifest with a non-zero language code. | 
 | // GCC has the concept of a default manifest resource object, which gets | 
 | // linked in implicitly if present. This default manifest has got language | 
 | // id zero, and should be dropped silently if there's another manifest present. | 
 | // If the user resources surprisignly had a manifest with language id zero, | 
 | // we should also ignore the duplicate default manifest. | 
 | void WindowsResourceParser::cleanUpManifests( | 
 |     std::vector<std::string> &Duplicates) { | 
 |   auto TypeIt = Root.IDChildren.find(/* RT_MANIFEST */ 24); | 
 |   if (TypeIt == Root.IDChildren.end()) | 
 |     return; | 
 |  | 
 |   TreeNode *TypeNode = TypeIt->second.get(); | 
 |   auto NameIt = | 
 |       TypeNode->IDChildren.find(/* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1); | 
 |   if (NameIt == TypeNode->IDChildren.end()) | 
 |     return; | 
 |  | 
 |   TreeNode *NameNode = NameIt->second.get(); | 
 |   if (NameNode->IDChildren.size() <= 1) | 
 |     return; // None or one manifest present, all good. | 
 |  | 
 |   // If we have more than one manifest, drop the language zero one if present, | 
 |   // and check again. | 
 |   auto LangZeroIt = NameNode->IDChildren.find(0); | 
 |   if (LangZeroIt != NameNode->IDChildren.end() && | 
 |       LangZeroIt->second->IsDataNode) { | 
 |     uint32_t RemovedIndex = LangZeroIt->second->DataIndex; | 
 |     NameNode->IDChildren.erase(LangZeroIt); | 
 |     Data.erase(Data.begin() + RemovedIndex); | 
 |     Root.shiftDataIndexDown(RemovedIndex); | 
 |  | 
 |     // If we're now down to one manifest, all is good. | 
 |     if (NameNode->IDChildren.size() <= 1) | 
 |       return; | 
 |   } | 
 |  | 
 |   // More than one non-language-zero manifest | 
 |   auto FirstIt = NameNode->IDChildren.begin(); | 
 |   uint32_t FirstLang = FirstIt->first; | 
 |   TreeNode *FirstNode = FirstIt->second.get(); | 
 |   auto LastIt = NameNode->IDChildren.rbegin(); | 
 |   uint32_t LastLang = LastIt->first; | 
 |   TreeNode *LastNode = LastIt->second.get(); | 
 |   Duplicates.push_back( | 
 |       ("duplicate non-default manifests with languages " + Twine(FirstLang) + | 
 |        " in " + InputFilenames[FirstNode->Origin] + " and " + Twine(LastLang) + | 
 |        " in " + InputFilenames[LastNode->Origin]) | 
 |           .str()); | 
 | } | 
 |  | 
 | // Ignore duplicates of manifests with language zero (the default manifest), | 
 | // in case the user has provided a manifest with that language id. See | 
 | // the function comment above for context. Only returns true if MinGW is set | 
 | // to true. | 
 | bool WindowsResourceParser::shouldIgnoreDuplicate( | 
 |     const ResourceEntryRef &Entry) const { | 
 |   return MinGW && !Entry.checkTypeString() && | 
 |          Entry.getTypeID() == /* RT_MANIFEST */ 24 && | 
 |          !Entry.checkNameString() && | 
 |          Entry.getNameID() == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 && | 
 |          Entry.getLanguage() == 0; | 
 | } | 
 |  | 
 | bool WindowsResourceParser::shouldIgnoreDuplicate( | 
 |     const std::vector<StringOrID> &Context) const { | 
 |   return MinGW && Context.size() == 3 && !Context[0].IsString && | 
 |          Context[0].ID == /* RT_MANIFEST */ 24 && !Context[1].IsString && | 
 |          Context[1].ID == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 && | 
 |          !Context[2].IsString && Context[2].ID == 0; | 
 | } | 
 |  | 
 | Error WindowsResourceParser::parse(WindowsResource *WR, | 
 |                                    std::vector<std::string> &Duplicates) { | 
 |   auto EntryOrErr = WR->getHeadEntry(); | 
 |   if (!EntryOrErr) { | 
 |     auto E = EntryOrErr.takeError(); | 
 |     if (E.isA<EmptyResError>()) { | 
 |       // Check if the .res file contains no entries.  In this case we don't have | 
 |       // to throw an error but can rather just return without parsing anything. | 
 |       // This applies for files which have a valid PE header magic and the | 
 |       // mandatory empty null resource entry.  Files which do not fit this | 
 |       // criteria would have already been filtered out by | 
 |       // WindowsResource::createWindowsResource(). | 
 |       consumeError(std::move(E)); | 
 |       return Error::success(); | 
 |     } | 
 |     return E; | 
 |   } | 
 |  | 
 |   ResourceEntryRef Entry = EntryOrErr.get(); | 
 |   uint32_t Origin = InputFilenames.size(); | 
 |   InputFilenames.push_back(WR->getFileName()); | 
 |   bool End = false; | 
 |   while (!End) { | 
 |  | 
 |     TreeNode *Node; | 
 |     bool IsNewNode = Root.addEntry(Entry, Origin, Data, StringTable, Node); | 
 |     if (!IsNewNode) { | 
 |       if (!shouldIgnoreDuplicate(Entry)) | 
 |         Duplicates.push_back(makeDuplicateResourceError( | 
 |             Entry, InputFilenames[Node->Origin], WR->getFileName())); | 
 |     } | 
 |  | 
 |     RETURN_IF_ERROR(Entry.moveNext(End)); | 
 |   } | 
 |  | 
 |   return Error::success(); | 
 | } | 
 |  | 
 | Error WindowsResourceParser::parse(ResourceSectionRef &RSR, StringRef Filename, | 
 |                                    std::vector<std::string> &Duplicates) { | 
 |   UNWRAP_REF_OR_RETURN(BaseTable, RSR.getBaseTable()); | 
 |   uint32_t Origin = InputFilenames.size(); | 
 |   InputFilenames.push_back(Filename); | 
 |   std::vector<StringOrID> Context; | 
 |   return addChildren(Root, RSR, BaseTable, Origin, Context, Duplicates); | 
 | } | 
 |  | 
 | void WindowsResourceParser::printTree(raw_ostream &OS) const { | 
 |   ScopedPrinter Writer(OS); | 
 |   Root.print(Writer, "Resource Tree"); | 
 | } | 
 |  | 
 | bool WindowsResourceParser::TreeNode::addEntry( | 
 |     const ResourceEntryRef &Entry, uint32_t Origin, | 
 |     std::vector<std::vector<uint8_t>> &Data, | 
 |     std::vector<std::vector<UTF16>> &StringTable, TreeNode *&Result) { | 
 |   TreeNode &TypeNode = addTypeNode(Entry, StringTable); | 
 |   TreeNode &NameNode = TypeNode.addNameNode(Entry, StringTable); | 
 |   return NameNode.addLanguageNode(Entry, Origin, Data, Result); | 
 | } | 
 |  | 
 | Error WindowsResourceParser::addChildren(TreeNode &Node, | 
 |                                          ResourceSectionRef &RSR, | 
 |                                          const coff_resource_dir_table &Table, | 
 |                                          uint32_t Origin, | 
 |                                          std::vector<StringOrID> &Context, | 
 |                                          std::vector<std::string> &Duplicates) { | 
 |  | 
 |   for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries; | 
 |        i++) { | 
 |     UNWRAP_REF_OR_RETURN(Entry, RSR.getTableEntry(Table, i)); | 
 |     TreeNode *Child; | 
 |  | 
 |     if (Entry.Offset.isSubDir()) { | 
 |  | 
 |       // Create a new subdirectory and recurse | 
 |       if (i < Table.NumberOfNameEntries) { | 
 |         UNWRAP_OR_RETURN(NameString, RSR.getEntryNameString(Entry)); | 
 |         Child = &Node.addNameChild(NameString, StringTable); | 
 |         Context.push_back(StringOrID(NameString)); | 
 |       } else { | 
 |         Child = &Node.addIDChild(Entry.Identifier.ID); | 
 |         Context.push_back(StringOrID(Entry.Identifier.ID)); | 
 |       } | 
 |  | 
 |       UNWRAP_REF_OR_RETURN(NextTable, RSR.getEntrySubDir(Entry)); | 
 |       Error E = | 
 |           addChildren(*Child, RSR, NextTable, Origin, Context, Duplicates); | 
 |       if (E) | 
 |         return E; | 
 |       Context.pop_back(); | 
 |  | 
 |     } else { | 
 |  | 
 |       // Data leaves are supposed to have a numeric ID as identifier (language). | 
 |       if (Table.NumberOfNameEntries > 0) | 
 |         return createStringError(object_error::parse_failed, | 
 |                                  "unexpected string key for data object"); | 
 |  | 
 |       // Try adding a data leaf | 
 |       UNWRAP_REF_OR_RETURN(DataEntry, RSR.getEntryData(Entry)); | 
 |       TreeNode *Child; | 
 |       Context.push_back(StringOrID(Entry.Identifier.ID)); | 
 |       bool Added = Node.addDataChild(Entry.Identifier.ID, Table.MajorVersion, | 
 |                                      Table.MinorVersion, Table.Characteristics, | 
 |                                      Origin, Data.size(), Child); | 
 |       if (Added) { | 
 |         UNWRAP_OR_RETURN(Contents, RSR.getContents(DataEntry)); | 
 |         Data.push_back(ArrayRef<uint8_t>( | 
 |             reinterpret_cast<const uint8_t *>(Contents.data()), | 
 |             Contents.size())); | 
 |       } else { | 
 |         if (!shouldIgnoreDuplicate(Context)) | 
 |           Duplicates.push_back(makeDuplicateResourceError( | 
 |               Context, InputFilenames[Child->Origin], InputFilenames.back())); | 
 |       } | 
 |       Context.pop_back(); | 
 |  | 
 |     } | 
 |   } | 
 |   return Error::success(); | 
 | } | 
 |  | 
 | WindowsResourceParser::TreeNode::TreeNode(uint32_t StringIndex) | 
 |     : StringIndex(StringIndex) {} | 
 |  | 
 | WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion, | 
 |                                           uint16_t MinorVersion, | 
 |                                           uint32_t Characteristics, | 
 |                                           uint32_t Origin, uint32_t DataIndex) | 
 |     : IsDataNode(true), DataIndex(DataIndex), MajorVersion(MajorVersion), | 
 |       MinorVersion(MinorVersion), Characteristics(Characteristics), | 
 |       Origin(Origin) {} | 
 |  | 
 | std::unique_ptr<WindowsResourceParser::TreeNode> | 
 | WindowsResourceParser::TreeNode::createStringNode(uint32_t Index) { | 
 |   return std::unique_ptr<TreeNode>(new TreeNode(Index)); | 
 | } | 
 |  | 
 | std::unique_ptr<WindowsResourceParser::TreeNode> | 
 | WindowsResourceParser::TreeNode::createIDNode() { | 
 |   return std::unique_ptr<TreeNode>(new TreeNode(0)); | 
 | } | 
 |  | 
 | std::unique_ptr<WindowsResourceParser::TreeNode> | 
 | WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion, | 
 |                                                 uint16_t MinorVersion, | 
 |                                                 uint32_t Characteristics, | 
 |                                                 uint32_t Origin, | 
 |                                                 uint32_t DataIndex) { | 
 |   return std::unique_ptr<TreeNode>(new TreeNode( | 
 |       MajorVersion, MinorVersion, Characteristics, Origin, DataIndex)); | 
 | } | 
 |  | 
 | WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addTypeNode( | 
 |     const ResourceEntryRef &Entry, | 
 |     std::vector<std::vector<UTF16>> &StringTable) { | 
 |   if (Entry.checkTypeString()) | 
 |     return addNameChild(Entry.getTypeString(), StringTable); | 
 |   else | 
 |     return addIDChild(Entry.getTypeID()); | 
 | } | 
 |  | 
 | WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameNode( | 
 |     const ResourceEntryRef &Entry, | 
 |     std::vector<std::vector<UTF16>> &StringTable) { | 
 |   if (Entry.checkNameString()) | 
 |     return addNameChild(Entry.getNameString(), StringTable); | 
 |   else | 
 |     return addIDChild(Entry.getNameID()); | 
 | } | 
 |  | 
 | bool WindowsResourceParser::TreeNode::addLanguageNode( | 
 |     const ResourceEntryRef &Entry, uint32_t Origin, | 
 |     std::vector<std::vector<uint8_t>> &Data, TreeNode *&Result) { | 
 |   bool Added = addDataChild(Entry.getLanguage(), Entry.getMajorVersion(), | 
 |                             Entry.getMinorVersion(), Entry.getCharacteristics(), | 
 |                             Origin, Data.size(), Result); | 
 |   if (Added) | 
 |     Data.push_back(Entry.getData()); | 
 |   return Added; | 
 | } | 
 |  | 
 | bool WindowsResourceParser::TreeNode::addDataChild( | 
 |     uint32_t ID, uint16_t MajorVersion, uint16_t MinorVersion, | 
 |     uint32_t Characteristics, uint32_t Origin, uint32_t DataIndex, | 
 |     TreeNode *&Result) { | 
 |   auto NewChild = createDataNode(MajorVersion, MinorVersion, Characteristics, | 
 |                                  Origin, DataIndex); | 
 |   auto ElementInserted = IDChildren.emplace(ID, std::move(NewChild)); | 
 |   Result = ElementInserted.first->second.get(); | 
 |   return ElementInserted.second; | 
 | } | 
 |  | 
 | WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addIDChild( | 
 |     uint32_t ID) { | 
 |   auto Child = IDChildren.find(ID); | 
 |   if (Child == IDChildren.end()) { | 
 |     auto NewChild = createIDNode(); | 
 |     WindowsResourceParser::TreeNode &Node = *NewChild; | 
 |     IDChildren.emplace(ID, std::move(NewChild)); | 
 |     return Node; | 
 |   } else | 
 |     return *(Child->second); | 
 | } | 
 |  | 
 | WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameChild( | 
 |     ArrayRef<UTF16> NameRef, std::vector<std::vector<UTF16>> &StringTable) { | 
 |   std::string NameString; | 
 |   convertUTF16LEToUTF8String(NameRef, NameString); | 
 |  | 
 |   auto Child = StringChildren.find(NameString); | 
 |   if (Child == StringChildren.end()) { | 
 |     auto NewChild = createStringNode(StringTable.size()); | 
 |     StringTable.push_back(NameRef); | 
 |     WindowsResourceParser::TreeNode &Node = *NewChild; | 
 |     StringChildren.emplace(NameString, std::move(NewChild)); | 
 |     return Node; | 
 |   } else | 
 |     return *(Child->second); | 
 | } | 
 |  | 
 | void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer, | 
 |                                             StringRef Name) const { | 
 |   ListScope NodeScope(Writer, Name); | 
 |   for (auto const &Child : StringChildren) { | 
 |     Child.second->print(Writer, Child.first); | 
 |   } | 
 |   for (auto const &Child : IDChildren) { | 
 |     Child.second->print(Writer, to_string(Child.first)); | 
 |   } | 
 | } | 
 |  | 
 | // This function returns the size of the entire resource tree, including | 
 | // directory tables, directory entries, and data entries.  It does not include | 
 | // the directory strings or the relocations of the .rsrc section. | 
 | uint32_t WindowsResourceParser::TreeNode::getTreeSize() const { | 
 |   uint32_t Size = (IDChildren.size() + StringChildren.size()) * | 
 |                   sizeof(coff_resource_dir_entry); | 
 |  | 
 |   // Reached a node pointing to a data entry. | 
 |   if (IsDataNode) { | 
 |     Size += sizeof(coff_resource_data_entry); | 
 |     return Size; | 
 |   } | 
 |  | 
 |   // If the node does not point to data, it must have a directory table pointing | 
 |   // to other nodes. | 
 |   Size += sizeof(coff_resource_dir_table); | 
 |  | 
 |   for (auto const &Child : StringChildren) { | 
 |     Size += Child.second->getTreeSize(); | 
 |   } | 
 |   for (auto const &Child : IDChildren) { | 
 |     Size += Child.second->getTreeSize(); | 
 |   } | 
 |   return Size; | 
 | } | 
 |  | 
 | // Shift DataIndex of all data children with an Index greater or equal to the | 
 | // given one, to fill a gap from removing an entry from the Data vector. | 
 | void WindowsResourceParser::TreeNode::shiftDataIndexDown(uint32_t Index) { | 
 |   if (IsDataNode && DataIndex >= Index) { | 
 |     DataIndex--; | 
 |   } else { | 
 |     for (auto &Child : IDChildren) | 
 |       Child.second->shiftDataIndexDown(Index); | 
 |     for (auto &Child : StringChildren) | 
 |       Child.second->shiftDataIndexDown(Index); | 
 |   } | 
 | } | 
 |  | 
 | class WindowsResourceCOFFWriter { | 
 | public: | 
 |   WindowsResourceCOFFWriter(COFF::MachineTypes MachineType, | 
 |                             const WindowsResourceParser &Parser, Error &E); | 
 |   std::unique_ptr<MemoryBuffer> write(uint32_t TimeDateStamp); | 
 |  | 
 | private: | 
 |   void performFileLayout(); | 
 |   void performSectionOneLayout(); | 
 |   void performSectionTwoLayout(); | 
 |   void writeCOFFHeader(uint32_t TimeDateStamp); | 
 |   void writeFirstSectionHeader(); | 
 |   void writeSecondSectionHeader(); | 
 |   void writeFirstSection(); | 
 |   void writeSecondSection(); | 
 |   void writeSymbolTable(); | 
 |   void writeStringTable(); | 
 |   void writeDirectoryTree(); | 
 |   void writeDirectoryStringTable(); | 
 |   void writeFirstSectionRelocations(); | 
 |   std::unique_ptr<WritableMemoryBuffer> OutputBuffer; | 
 |   char *BufferStart; | 
 |   uint64_t CurrentOffset = 0; | 
 |   COFF::MachineTypes MachineType; | 
 |   const WindowsResourceParser::TreeNode &Resources; | 
 |   const ArrayRef<std::vector<uint8_t>> Data; | 
 |   uint64_t FileSize; | 
 |   uint32_t SymbolTableOffset; | 
 |   uint32_t SectionOneSize; | 
 |   uint32_t SectionOneOffset; | 
 |   uint32_t SectionOneRelocations; | 
 |   uint32_t SectionTwoSize; | 
 |   uint32_t SectionTwoOffset; | 
 |   const ArrayRef<std::vector<UTF16>> StringTable; | 
 |   std::vector<uint32_t> StringTableOffsets; | 
 |   std::vector<uint32_t> DataOffsets; | 
 |   std::vector<uint32_t> RelocationAddresses; | 
 | }; | 
 |  | 
 | WindowsResourceCOFFWriter::WindowsResourceCOFFWriter( | 
 |     COFF::MachineTypes MachineType, const WindowsResourceParser &Parser, | 
 |     Error &E) | 
 |     : MachineType(MachineType), Resources(Parser.getTree()), | 
 |       Data(Parser.getData()), StringTable(Parser.getStringTable()) { | 
 |   performFileLayout(); | 
 |  | 
 |   OutputBuffer = WritableMemoryBuffer::getNewMemBuffer( | 
 |       FileSize, "internal .obj file created from .res files"); | 
 | } | 
 |  | 
 | void WindowsResourceCOFFWriter::performFileLayout() { | 
 |   // Add size of COFF header. | 
 |   FileSize = COFF::Header16Size; | 
 |  | 
 |   // one .rsrc section header for directory tree, another for resource data. | 
 |   FileSize += 2 * COFF::SectionSize; | 
 |  | 
 |   performSectionOneLayout(); | 
 |   performSectionTwoLayout(); | 
 |  | 
 |   // We have reached the address of the symbol table. | 
 |   SymbolTableOffset = FileSize; | 
 |  | 
 |   FileSize += COFF::Symbol16Size;     // size of the @feat.00 symbol. | 
 |   FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section. | 
 |   FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource. | 
 |   FileSize += 4; // four null bytes for the string table. | 
 | } | 
 |  | 
 | void WindowsResourceCOFFWriter::performSectionOneLayout() { | 
 |   SectionOneOffset = FileSize; | 
 |  | 
 |   SectionOneSize = Resources.getTreeSize(); | 
 |   uint32_t CurrentStringOffset = SectionOneSize; | 
 |   uint32_t TotalStringTableSize = 0; | 
 |   for (auto const &String : StringTable) { | 
 |     StringTableOffsets.push_back(CurrentStringOffset); | 
 |     uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t); | 
 |     CurrentStringOffset += StringSize; | 
 |     TotalStringTableSize += StringSize; | 
 |   } | 
 |   SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t)); | 
 |  | 
 |   // account for the relocations of section one. | 
 |   SectionOneRelocations = FileSize + SectionOneSize; | 
 |   FileSize += SectionOneSize; | 
 |   FileSize += | 
 |       Data.size() * COFF::RelocationSize; // one relocation for each resource. | 
 |   FileSize = alignTo(FileSize, SECTION_ALIGNMENT); | 
 | } | 
 |  | 
 | void WindowsResourceCOFFWriter::performSectionTwoLayout() { | 
 |   // add size of .rsrc$2 section, which contains all resource data on 8-byte | 
 |   // alignment. | 
 |   SectionTwoOffset = FileSize; | 
 |   SectionTwoSize = 0; | 
 |   for (auto const &Entry : Data) { | 
 |     DataOffsets.push_back(SectionTwoSize); | 
 |     SectionTwoSize += alignTo(Entry.size(), sizeof(uint64_t)); | 
 |   } | 
 |   FileSize += SectionTwoSize; | 
 |   FileSize = alignTo(FileSize, SECTION_ALIGNMENT); | 
 | } | 
 |  | 
 | std::unique_ptr<MemoryBuffer> | 
 | WindowsResourceCOFFWriter::write(uint32_t TimeDateStamp) { | 
 |   BufferStart = OutputBuffer->getBufferStart(); | 
 |  | 
 |   writeCOFFHeader(TimeDateStamp); | 
 |   writeFirstSectionHeader(); | 
 |   writeSecondSectionHeader(); | 
 |   writeFirstSection(); | 
 |   writeSecondSection(); | 
 |   writeSymbolTable(); | 
 |   writeStringTable(); | 
 |  | 
 |   return std::move(OutputBuffer); | 
 | } | 
 |  | 
 | // According to COFF specification, if the Src has a size equal to Dest, | 
 | // it's okay to *not* copy the trailing zero. | 
 | static void coffnamecpy(char (&Dest)[COFF::NameSize], StringRef Src) { | 
 |   assert(Src.size() <= COFF::NameSize && | 
 |          "Src is not larger than COFF::NameSize"); | 
 |   strncpy(Dest, Src.data(), (size_t)COFF::NameSize); | 
 | } | 
 |  | 
 | void WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp) { | 
 |   // Write the COFF header. | 
 |   auto *Header = reinterpret_cast<coff_file_header *>(BufferStart); | 
 |   Header->Machine = MachineType; | 
 |   Header->NumberOfSections = 2; | 
 |   Header->TimeDateStamp = TimeDateStamp; | 
 |   Header->PointerToSymbolTable = SymbolTableOffset; | 
 |   // One symbol for every resource plus 2 for each section and 1 for @feat.00 | 
 |   Header->NumberOfSymbols = Data.size() + 5; | 
 |   Header->SizeOfOptionalHeader = 0; | 
 |   // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it. | 
 |   Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE; | 
 | } | 
 |  | 
 | void WindowsResourceCOFFWriter::writeFirstSectionHeader() { | 
 |   // Write the first section header. | 
 |   CurrentOffset += sizeof(coff_file_header); | 
 |   auto *SectionOneHeader = | 
 |       reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); | 
 |   coffnamecpy(SectionOneHeader->Name, ".rsrc$01"); | 
 |   SectionOneHeader->VirtualSize = 0; | 
 |   SectionOneHeader->VirtualAddress = 0; | 
 |   SectionOneHeader->SizeOfRawData = SectionOneSize; | 
 |   SectionOneHeader->PointerToRawData = SectionOneOffset; | 
 |   SectionOneHeader->PointerToRelocations = SectionOneRelocations; | 
 |   SectionOneHeader->PointerToLinenumbers = 0; | 
 |   SectionOneHeader->NumberOfRelocations = Data.size(); | 
 |   SectionOneHeader->NumberOfLinenumbers = 0; | 
 |   SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; | 
 |   SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; | 
 | } | 
 |  | 
 | void WindowsResourceCOFFWriter::writeSecondSectionHeader() { | 
 |   // Write the second section header. | 
 |   CurrentOffset += sizeof(coff_section); | 
 |   auto *SectionTwoHeader = | 
 |       reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); | 
 |   coffnamecpy(SectionTwoHeader->Name, ".rsrc$02"); | 
 |   SectionTwoHeader->VirtualSize = 0; | 
 |   SectionTwoHeader->VirtualAddress = 0; | 
 |   SectionTwoHeader->SizeOfRawData = SectionTwoSize; | 
 |   SectionTwoHeader->PointerToRawData = SectionTwoOffset; | 
 |   SectionTwoHeader->PointerToRelocations = 0; | 
 |   SectionTwoHeader->PointerToLinenumbers = 0; | 
 |   SectionTwoHeader->NumberOfRelocations = 0; | 
 |   SectionTwoHeader->NumberOfLinenumbers = 0; | 
 |   SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; | 
 |   SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; | 
 | } | 
 |  | 
 | void WindowsResourceCOFFWriter::writeFirstSection() { | 
 |   // Write section one. | 
 |   CurrentOffset += sizeof(coff_section); | 
 |  | 
 |   writeDirectoryTree(); | 
 |   writeDirectoryStringTable(); | 
 |   writeFirstSectionRelocations(); | 
 |  | 
 |   CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); | 
 | } | 
 |  | 
 | void WindowsResourceCOFFWriter::writeSecondSection() { | 
 |   // Now write the .rsrc$02 section. | 
 |   for (auto const &RawDataEntry : Data) { | 
 |     llvm::copy(RawDataEntry, BufferStart + CurrentOffset); | 
 |     CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t)); | 
 |   } | 
 |  | 
 |   CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); | 
 | } | 
 |  | 
 | void WindowsResourceCOFFWriter::writeSymbolTable() { | 
 |   // Now write the symbol table. | 
 |   // First, the feat symbol. | 
 |   auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); | 
 |   coffnamecpy(Symbol->Name.ShortName, "@feat.00"); | 
 |   Symbol->Value = 0x11; | 
 |   Symbol->SectionNumber = 0xffff; | 
 |   Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; | 
 |   Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; | 
 |   Symbol->NumberOfAuxSymbols = 0; | 
 |   CurrentOffset += sizeof(coff_symbol16); | 
 |  | 
 |   // Now write the .rsrc1 symbol + aux. | 
 |   Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); | 
 |   coffnamecpy(Symbol->Name.ShortName, ".rsrc$01"); | 
 |   Symbol->Value = 0; | 
 |   Symbol->SectionNumber = 1; | 
 |   Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; | 
 |   Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; | 
 |   Symbol->NumberOfAuxSymbols = 1; | 
 |   CurrentOffset += sizeof(coff_symbol16); | 
 |   auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + | 
 |                                                               CurrentOffset); | 
 |   Aux->Length = SectionOneSize; | 
 |   Aux->NumberOfRelocations = Data.size(); | 
 |   Aux->NumberOfLinenumbers = 0; | 
 |   Aux->CheckSum = 0; | 
 |   Aux->NumberLowPart = 0; | 
 |   Aux->Selection = 0; | 
 |   CurrentOffset += sizeof(coff_aux_section_definition); | 
 |  | 
 |   // Now write the .rsrc2 symbol + aux. | 
 |   Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); | 
 |   coffnamecpy(Symbol->Name.ShortName, ".rsrc$02"); | 
 |   Symbol->Value = 0; | 
 |   Symbol->SectionNumber = 2; | 
 |   Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; | 
 |   Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; | 
 |   Symbol->NumberOfAuxSymbols = 1; | 
 |   CurrentOffset += sizeof(coff_symbol16); | 
 |   Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + | 
 |                                                         CurrentOffset); | 
 |   Aux->Length = SectionTwoSize; | 
 |   Aux->NumberOfRelocations = 0; | 
 |   Aux->NumberOfLinenumbers = 0; | 
 |   Aux->CheckSum = 0; | 
 |   Aux->NumberLowPart = 0; | 
 |   Aux->Selection = 0; | 
 |   CurrentOffset += sizeof(coff_aux_section_definition); | 
 |  | 
 |   // Now write a symbol for each relocation. | 
 |   for (unsigned i = 0; i < Data.size(); i++) { | 
 |     auto RelocationName = formatv("$R{0:X-6}", i & 0xffffff).sstr<COFF::NameSize>(); | 
 |     Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); | 
 |     coffnamecpy(Symbol->Name.ShortName, RelocationName); | 
 |     Symbol->Value = DataOffsets[i]; | 
 |     Symbol->SectionNumber = 2; | 
 |     Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; | 
 |     Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; | 
 |     Symbol->NumberOfAuxSymbols = 0; | 
 |     CurrentOffset += sizeof(coff_symbol16); | 
 |   } | 
 | } | 
 |  | 
 | void WindowsResourceCOFFWriter::writeStringTable() { | 
 |   // Just 4 null bytes for the string table. | 
 |   auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset); | 
 |   memset(COFFStringTable, 0, 4); | 
 | } | 
 |  | 
 | void WindowsResourceCOFFWriter::writeDirectoryTree() { | 
 |   // Traverse parsed resource tree breadth-first and write the corresponding | 
 |   // COFF objects. | 
 |   std::queue<const WindowsResourceParser::TreeNode *> Queue; | 
 |   Queue.push(&Resources); | 
 |   uint32_t NextLevelOffset = | 
 |       sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() + | 
 |                                          Resources.getIDChildren().size()) * | 
 |                                             sizeof(coff_resource_dir_entry); | 
 |   std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder; | 
 |   uint32_t CurrentRelativeOffset = 0; | 
 |  | 
 |   while (!Queue.empty()) { | 
 |     auto CurrentNode = Queue.front(); | 
 |     Queue.pop(); | 
 |     auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart + | 
 |                                                               CurrentOffset); | 
 |     Table->Characteristics = CurrentNode->getCharacteristics(); | 
 |     Table->TimeDateStamp = 0; | 
 |     Table->MajorVersion = CurrentNode->getMajorVersion(); | 
 |     Table->MinorVersion = CurrentNode->getMinorVersion(); | 
 |     auto &IDChildren = CurrentNode->getIDChildren(); | 
 |     auto &StringChildren = CurrentNode->getStringChildren(); | 
 |     Table->NumberOfNameEntries = StringChildren.size(); | 
 |     Table->NumberOfIDEntries = IDChildren.size(); | 
 |     CurrentOffset += sizeof(coff_resource_dir_table); | 
 |     CurrentRelativeOffset += sizeof(coff_resource_dir_table); | 
 |  | 
 |     // Write the directory entries immediately following each directory table. | 
 |     for (auto const &Child : StringChildren) { | 
 |       auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + | 
 |                                                                 CurrentOffset); | 
 |       Entry->Identifier.setNameOffset( | 
 |           StringTableOffsets[Child.second->getStringIndex()]); | 
 |       if (Child.second->checkIsDataNode()) { | 
 |         Entry->Offset.DataEntryOffset = NextLevelOffset; | 
 |         NextLevelOffset += sizeof(coff_resource_data_entry); | 
 |         DataEntriesTreeOrder.push_back(Child.second.get()); | 
 |       } else { | 
 |         Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); | 
 |         NextLevelOffset += sizeof(coff_resource_dir_table) + | 
 |                            (Child.second->getStringChildren().size() + | 
 |                             Child.second->getIDChildren().size()) * | 
 |                                sizeof(coff_resource_dir_entry); | 
 |         Queue.push(Child.second.get()); | 
 |       } | 
 |       CurrentOffset += sizeof(coff_resource_dir_entry); | 
 |       CurrentRelativeOffset += sizeof(coff_resource_dir_entry); | 
 |     } | 
 |     for (auto const &Child : IDChildren) { | 
 |       auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + | 
 |                                                                 CurrentOffset); | 
 |       Entry->Identifier.ID = Child.first; | 
 |       if (Child.second->checkIsDataNode()) { | 
 |         Entry->Offset.DataEntryOffset = NextLevelOffset; | 
 |         NextLevelOffset += sizeof(coff_resource_data_entry); | 
 |         DataEntriesTreeOrder.push_back(Child.second.get()); | 
 |       } else { | 
 |         Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); | 
 |         NextLevelOffset += sizeof(coff_resource_dir_table) + | 
 |                            (Child.second->getStringChildren().size() + | 
 |                             Child.second->getIDChildren().size()) * | 
 |                                sizeof(coff_resource_dir_entry); | 
 |         Queue.push(Child.second.get()); | 
 |       } | 
 |       CurrentOffset += sizeof(coff_resource_dir_entry); | 
 |       CurrentRelativeOffset += sizeof(coff_resource_dir_entry); | 
 |     } | 
 |   } | 
 |  | 
 |   RelocationAddresses.resize(Data.size()); | 
 |   // Now write all the resource data entries. | 
 |   for (auto DataNodes : DataEntriesTreeOrder) { | 
 |     auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart + | 
 |                                                                CurrentOffset); | 
 |     RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset; | 
 |     Entry->DataRVA = 0; // Set to zero because it is a relocation. | 
 |     Entry->DataSize = Data[DataNodes->getDataIndex()].size(); | 
 |     Entry->Codepage = 0; | 
 |     Entry->Reserved = 0; | 
 |     CurrentOffset += sizeof(coff_resource_data_entry); | 
 |     CurrentRelativeOffset += sizeof(coff_resource_data_entry); | 
 |   } | 
 | } | 
 |  | 
 | void WindowsResourceCOFFWriter::writeDirectoryStringTable() { | 
 |   // Now write the directory string table for .rsrc$01 | 
 |   uint32_t TotalStringTableSize = 0; | 
 |   for (auto &String : StringTable) { | 
 |     uint16_t Length = String.size(); | 
 |     support::endian::write16le(BufferStart + CurrentOffset, Length); | 
 |     CurrentOffset += sizeof(uint16_t); | 
 |     auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset); | 
 |     llvm::copy(String, Start); | 
 |     CurrentOffset += Length * sizeof(UTF16); | 
 |     TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t); | 
 |   } | 
 |   CurrentOffset += | 
 |       alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize; | 
 | } | 
 |  | 
 | void WindowsResourceCOFFWriter::writeFirstSectionRelocations() { | 
 |  | 
 |   // Now write the relocations for .rsrc$01 | 
 |   // Five symbols already in table before we start, @feat.00 and 2 for each | 
 |   // .rsrc section. | 
 |   uint32_t NextSymbolIndex = 5; | 
 |   for (unsigned i = 0; i < Data.size(); i++) { | 
 |     auto *Reloc = | 
 |         reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset); | 
 |     Reloc->VirtualAddress = RelocationAddresses[i]; | 
 |     Reloc->SymbolTableIndex = NextSymbolIndex++; | 
 |     switch (MachineType) { | 
 |     case COFF::IMAGE_FILE_MACHINE_ARMNT: | 
 |       Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB; | 
 |       break; | 
 |     case COFF::IMAGE_FILE_MACHINE_AMD64: | 
 |       Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB; | 
 |       break; | 
 |     case COFF::IMAGE_FILE_MACHINE_I386: | 
 |       Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB; | 
 |       break; | 
 |     case COFF::IMAGE_FILE_MACHINE_ARM64: | 
 |       Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB; | 
 |       break; | 
 |     default: | 
 |       llvm_unreachable("unknown machine type"); | 
 |     } | 
 |     CurrentOffset += sizeof(coff_relocation); | 
 |   } | 
 | } | 
 |  | 
 | Expected<std::unique_ptr<MemoryBuffer>> | 
 | writeWindowsResourceCOFF(COFF::MachineTypes MachineType, | 
 |                          const WindowsResourceParser &Parser, | 
 |                          uint32_t TimeDateStamp) { | 
 |   Error E = Error::success(); | 
 |   WindowsResourceCOFFWriter Writer(MachineType, Parser, E); | 
 |   if (E) | 
 |     return std::move(E); | 
 |   return Writer.write(TimeDateStamp); | 
 | } | 
 |  | 
 | } // namespace object | 
 | } // namespace llvm |