xref: /netbsd-src/external/apache2/llvm/dist/llvm/lib/Object/WindowsResource.cpp (revision 82d56013d7b633d116a93943de88e08335357a7c)
17330f729Sjoerg //===-- WindowsResource.cpp -------------------------------------*- C++ -*-===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This file implements the .res file class.
107330f729Sjoerg //
117330f729Sjoerg //===----------------------------------------------------------------------===//
127330f729Sjoerg 
137330f729Sjoerg #include "llvm/Object/WindowsResource.h"
147330f729Sjoerg #include "llvm/Object/COFF.h"
157330f729Sjoerg #include "llvm/Support/FileOutputBuffer.h"
167330f729Sjoerg #include "llvm/Support/FormatVariadic.h"
177330f729Sjoerg #include "llvm/Support/MathExtras.h"
187330f729Sjoerg #include "llvm/Support/ScopedPrinter.h"
197330f729Sjoerg #include <ctime>
207330f729Sjoerg #include <queue>
217330f729Sjoerg #include <system_error>
227330f729Sjoerg 
237330f729Sjoerg using namespace llvm;
247330f729Sjoerg using namespace object;
257330f729Sjoerg 
267330f729Sjoerg namespace llvm {
277330f729Sjoerg namespace object {
287330f729Sjoerg 
297330f729Sjoerg #define RETURN_IF_ERROR(X)                                                     \
307330f729Sjoerg   if (auto EC = X)                                                             \
317330f729Sjoerg     return EC;
327330f729Sjoerg 
337330f729Sjoerg #define UNWRAP_REF_OR_RETURN(Name, Expr)                                       \
347330f729Sjoerg   auto Name##OrErr = Expr;                                                     \
357330f729Sjoerg   if (!Name##OrErr)                                                            \
367330f729Sjoerg     return Name##OrErr.takeError();                                            \
377330f729Sjoerg   const auto &Name = *Name##OrErr;
387330f729Sjoerg 
397330f729Sjoerg #define UNWRAP_OR_RETURN(Name, Expr)                                           \
407330f729Sjoerg   auto Name##OrErr = Expr;                                                     \
417330f729Sjoerg   if (!Name##OrErr)                                                            \
427330f729Sjoerg     return Name##OrErr.takeError();                                            \
437330f729Sjoerg   auto Name = *Name##OrErr;
447330f729Sjoerg 
457330f729Sjoerg const uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t);
467330f729Sjoerg 
477330f729Sjoerg // COFF files seem to be inconsistent with alignment between sections, just use
487330f729Sjoerg // 8-byte because it makes everyone happy.
497330f729Sjoerg const uint32_t SECTION_ALIGNMENT = sizeof(uint64_t);
507330f729Sjoerg 
WindowsResource(MemoryBufferRef Source)517330f729Sjoerg WindowsResource::WindowsResource(MemoryBufferRef Source)
527330f729Sjoerg     : Binary(Binary::ID_WinRes, Source) {
537330f729Sjoerg   size_t LeadingSize = WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE;
547330f729Sjoerg   BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize),
557330f729Sjoerg                          support::little);
567330f729Sjoerg }
577330f729Sjoerg 
587330f729Sjoerg // static
597330f729Sjoerg Expected<std::unique_ptr<WindowsResource>>
createWindowsResource(MemoryBufferRef Source)607330f729Sjoerg WindowsResource::createWindowsResource(MemoryBufferRef Source) {
617330f729Sjoerg   if (Source.getBufferSize() < WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE)
627330f729Sjoerg     return make_error<GenericBinaryError>(
637330f729Sjoerg         Source.getBufferIdentifier() + ": too small to be a resource file",
647330f729Sjoerg         object_error::invalid_file_type);
657330f729Sjoerg   std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source));
667330f729Sjoerg   return std::move(Ret);
677330f729Sjoerg }
687330f729Sjoerg 
getHeadEntry()697330f729Sjoerg Expected<ResourceEntryRef> WindowsResource::getHeadEntry() {
707330f729Sjoerg   if (BBS.getLength() < sizeof(WinResHeaderPrefix) + sizeof(WinResHeaderSuffix))
717330f729Sjoerg     return make_error<EmptyResError>(getFileName() + " contains no entries",
727330f729Sjoerg                                      object_error::unexpected_eof);
737330f729Sjoerg   return ResourceEntryRef::create(BinaryStreamRef(BBS), this);
747330f729Sjoerg }
757330f729Sjoerg 
ResourceEntryRef(BinaryStreamRef Ref,const WindowsResource * Owner)767330f729Sjoerg ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref,
777330f729Sjoerg                                    const WindowsResource *Owner)
787330f729Sjoerg     : Reader(Ref), Owner(Owner) {}
797330f729Sjoerg 
807330f729Sjoerg Expected<ResourceEntryRef>
create(BinaryStreamRef BSR,const WindowsResource * Owner)817330f729Sjoerg ResourceEntryRef::create(BinaryStreamRef BSR, const WindowsResource *Owner) {
827330f729Sjoerg   auto Ref = ResourceEntryRef(BSR, Owner);
837330f729Sjoerg   if (auto E = Ref.loadNext())
847330f729Sjoerg     return std::move(E);
857330f729Sjoerg   return Ref;
867330f729Sjoerg }
877330f729Sjoerg 
moveNext(bool & End)887330f729Sjoerg Error ResourceEntryRef::moveNext(bool &End) {
897330f729Sjoerg   // Reached end of all the entries.
907330f729Sjoerg   if (Reader.bytesRemaining() == 0) {
917330f729Sjoerg     End = true;
927330f729Sjoerg     return Error::success();
937330f729Sjoerg   }
947330f729Sjoerg   RETURN_IF_ERROR(loadNext());
957330f729Sjoerg 
967330f729Sjoerg   return Error::success();
977330f729Sjoerg }
987330f729Sjoerg 
readStringOrId(BinaryStreamReader & Reader,uint16_t & ID,ArrayRef<UTF16> & Str,bool & IsString)997330f729Sjoerg static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID,
1007330f729Sjoerg                             ArrayRef<UTF16> &Str, bool &IsString) {
1017330f729Sjoerg   uint16_t IDFlag;
1027330f729Sjoerg   RETURN_IF_ERROR(Reader.readInteger(IDFlag));
1037330f729Sjoerg   IsString = IDFlag != 0xffff;
1047330f729Sjoerg 
1057330f729Sjoerg   if (IsString) {
1067330f729Sjoerg     Reader.setOffset(
1077330f729Sjoerg         Reader.getOffset() -
1087330f729Sjoerg         sizeof(uint16_t)); // Re-read the bytes which we used to check the flag.
1097330f729Sjoerg     RETURN_IF_ERROR(Reader.readWideString(Str));
1107330f729Sjoerg   } else
1117330f729Sjoerg     RETURN_IF_ERROR(Reader.readInteger(ID));
1127330f729Sjoerg 
1137330f729Sjoerg   return Error::success();
1147330f729Sjoerg }
1157330f729Sjoerg 
loadNext()1167330f729Sjoerg Error ResourceEntryRef::loadNext() {
1177330f729Sjoerg   const WinResHeaderPrefix *Prefix;
1187330f729Sjoerg   RETURN_IF_ERROR(Reader.readObject(Prefix));
1197330f729Sjoerg 
1207330f729Sjoerg   if (Prefix->HeaderSize < MIN_HEADER_SIZE)
1217330f729Sjoerg     return make_error<GenericBinaryError>(Owner->getFileName() +
1227330f729Sjoerg                                               ": header size too small",
1237330f729Sjoerg                                           object_error::parse_failed);
1247330f729Sjoerg 
1257330f729Sjoerg   RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType));
1267330f729Sjoerg 
1277330f729Sjoerg   RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName));
1287330f729Sjoerg 
1297330f729Sjoerg   RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_HEADER_ALIGNMENT));
1307330f729Sjoerg 
1317330f729Sjoerg   RETURN_IF_ERROR(Reader.readObject(Suffix));
1327330f729Sjoerg 
1337330f729Sjoerg   RETURN_IF_ERROR(Reader.readArray(Data, Prefix->DataSize));
1347330f729Sjoerg 
1357330f729Sjoerg   RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_DATA_ALIGNMENT));
1367330f729Sjoerg 
1377330f729Sjoerg   return Error::success();
1387330f729Sjoerg }
1397330f729Sjoerg 
WindowsResourceParser(bool MinGW)1407330f729Sjoerg WindowsResourceParser::WindowsResourceParser(bool MinGW)
1417330f729Sjoerg     : Root(false), MinGW(MinGW) {}
1427330f729Sjoerg 
printResourceTypeName(uint16_t TypeID,raw_ostream & OS)1437330f729Sjoerg void printResourceTypeName(uint16_t TypeID, raw_ostream &OS) {
1447330f729Sjoerg   switch (TypeID) {
1457330f729Sjoerg   case  1: OS << "CURSOR (ID 1)"; break;
1467330f729Sjoerg   case  2: OS << "BITMAP (ID 2)"; break;
1477330f729Sjoerg   case  3: OS << "ICON (ID 3)"; break;
1487330f729Sjoerg   case  4: OS << "MENU (ID 4)"; break;
1497330f729Sjoerg   case  5: OS << "DIALOG (ID 5)"; break;
1507330f729Sjoerg   case  6: OS << "STRINGTABLE (ID 6)"; break;
1517330f729Sjoerg   case  7: OS << "FONTDIR (ID 7)"; break;
1527330f729Sjoerg   case  8: OS << "FONT (ID 8)"; break;
1537330f729Sjoerg   case  9: OS << "ACCELERATOR (ID 9)"; break;
1547330f729Sjoerg   case 10: OS << "RCDATA (ID 10)"; break;
1557330f729Sjoerg   case 11: OS << "MESSAGETABLE (ID 11)"; break;
1567330f729Sjoerg   case 12: OS << "GROUP_CURSOR (ID 12)"; break;
1577330f729Sjoerg   case 14: OS << "GROUP_ICON (ID 14)"; break;
1587330f729Sjoerg   case 16: OS << "VERSIONINFO (ID 16)"; break;
1597330f729Sjoerg   case 17: OS << "DLGINCLUDE (ID 17)"; break;
1607330f729Sjoerg   case 19: OS << "PLUGPLAY (ID 19)"; break;
1617330f729Sjoerg   case 20: OS << "VXD (ID 20)"; break;
1627330f729Sjoerg   case 21: OS << "ANICURSOR (ID 21)"; break;
1637330f729Sjoerg   case 22: OS << "ANIICON (ID 22)"; break;
1647330f729Sjoerg   case 23: OS << "HTML (ID 23)"; break;
1657330f729Sjoerg   case 24: OS << "MANIFEST (ID 24)"; break;
1667330f729Sjoerg   default: OS << "ID " << TypeID; break;
1677330f729Sjoerg   }
1687330f729Sjoerg }
1697330f729Sjoerg 
convertUTF16LEToUTF8String(ArrayRef<UTF16> Src,std::string & Out)1707330f729Sjoerg static bool convertUTF16LEToUTF8String(ArrayRef<UTF16> Src, std::string &Out) {
1717330f729Sjoerg   if (!sys::IsBigEndianHost)
1727330f729Sjoerg     return convertUTF16ToUTF8String(Src, Out);
1737330f729Sjoerg 
1747330f729Sjoerg   std::vector<UTF16> EndianCorrectedSrc;
1757330f729Sjoerg   EndianCorrectedSrc.resize(Src.size() + 1);
1767330f729Sjoerg   llvm::copy(Src, EndianCorrectedSrc.begin() + 1);
1777330f729Sjoerg   EndianCorrectedSrc[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED;
1787330f729Sjoerg   return convertUTF16ToUTF8String(makeArrayRef(EndianCorrectedSrc), Out);
1797330f729Sjoerg }
1807330f729Sjoerg 
makeDuplicateResourceError(const ResourceEntryRef & Entry,StringRef File1,StringRef File2)1817330f729Sjoerg static std::string makeDuplicateResourceError(
1827330f729Sjoerg     const ResourceEntryRef &Entry, StringRef File1, StringRef File2) {
1837330f729Sjoerg   std::string Ret;
1847330f729Sjoerg   raw_string_ostream OS(Ret);
1857330f729Sjoerg 
1867330f729Sjoerg   OS << "duplicate resource:";
1877330f729Sjoerg 
1887330f729Sjoerg   OS << " type ";
1897330f729Sjoerg   if (Entry.checkTypeString()) {
1907330f729Sjoerg     std::string UTF8;
1917330f729Sjoerg     if (!convertUTF16LEToUTF8String(Entry.getTypeString(), UTF8))
1927330f729Sjoerg       UTF8 = "(failed conversion from UTF16)";
1937330f729Sjoerg     OS << '\"' << UTF8 << '\"';
1947330f729Sjoerg   } else
1957330f729Sjoerg     printResourceTypeName(Entry.getTypeID(), OS);
1967330f729Sjoerg 
1977330f729Sjoerg   OS << "/name ";
1987330f729Sjoerg   if (Entry.checkNameString()) {
1997330f729Sjoerg     std::string UTF8;
2007330f729Sjoerg     if (!convertUTF16LEToUTF8String(Entry.getNameString(), UTF8))
2017330f729Sjoerg       UTF8 = "(failed conversion from UTF16)";
2027330f729Sjoerg     OS << '\"' << UTF8 << '\"';
2037330f729Sjoerg   } else {
2047330f729Sjoerg     OS << "ID " << Entry.getNameID();
2057330f729Sjoerg   }
2067330f729Sjoerg 
2077330f729Sjoerg   OS << "/language " << Entry.getLanguage() << ", in " << File1 << " and in "
2087330f729Sjoerg      << File2;
2097330f729Sjoerg 
2107330f729Sjoerg   return OS.str();
2117330f729Sjoerg }
2127330f729Sjoerg 
printStringOrID(const WindowsResourceParser::StringOrID & S,raw_string_ostream & OS,bool IsType,bool IsID)2137330f729Sjoerg static void printStringOrID(const WindowsResourceParser::StringOrID &S,
2147330f729Sjoerg                             raw_string_ostream &OS, bool IsType, bool IsID) {
2157330f729Sjoerg   if (S.IsString) {
2167330f729Sjoerg     std::string UTF8;
2177330f729Sjoerg     if (!convertUTF16LEToUTF8String(S.String, UTF8))
2187330f729Sjoerg       UTF8 = "(failed conversion from UTF16)";
2197330f729Sjoerg     OS << '\"' << UTF8 << '\"';
2207330f729Sjoerg   } else if (IsType)
2217330f729Sjoerg     printResourceTypeName(S.ID, OS);
2227330f729Sjoerg   else if (IsID)
2237330f729Sjoerg     OS << "ID " << S.ID;
2247330f729Sjoerg   else
2257330f729Sjoerg     OS << S.ID;
2267330f729Sjoerg }
2277330f729Sjoerg 
makeDuplicateResourceError(const std::vector<WindowsResourceParser::StringOrID> & Context,StringRef File1,StringRef File2)2287330f729Sjoerg static std::string makeDuplicateResourceError(
2297330f729Sjoerg     const std::vector<WindowsResourceParser::StringOrID> &Context,
2307330f729Sjoerg     StringRef File1, StringRef File2) {
2317330f729Sjoerg   std::string Ret;
2327330f729Sjoerg   raw_string_ostream OS(Ret);
2337330f729Sjoerg 
2347330f729Sjoerg   OS << "duplicate resource:";
2357330f729Sjoerg 
2367330f729Sjoerg   if (Context.size() >= 1) {
2377330f729Sjoerg     OS << " type ";
2387330f729Sjoerg     printStringOrID(Context[0], OS, /* IsType */ true, /* IsID */ true);
2397330f729Sjoerg   }
2407330f729Sjoerg 
2417330f729Sjoerg   if (Context.size() >= 2) {
2427330f729Sjoerg     OS << "/name ";
2437330f729Sjoerg     printStringOrID(Context[1], OS, /* IsType */ false, /* IsID */ true);
2447330f729Sjoerg   }
2457330f729Sjoerg 
2467330f729Sjoerg   if (Context.size() >= 3) {
2477330f729Sjoerg     OS << "/language ";
2487330f729Sjoerg     printStringOrID(Context[2], OS, /* IsType */ false, /* IsID */ false);
2497330f729Sjoerg   }
2507330f729Sjoerg   OS << ", in " << File1 << " and in " << File2;
2517330f729Sjoerg 
2527330f729Sjoerg   return OS.str();
2537330f729Sjoerg }
2547330f729Sjoerg 
2557330f729Sjoerg // MinGW specific. Remove default manifests (with language zero) if there are
2567330f729Sjoerg // other manifests present, and report an error if there are more than one
2577330f729Sjoerg // manifest with a non-zero language code.
2587330f729Sjoerg // GCC has the concept of a default manifest resource object, which gets
2597330f729Sjoerg // linked in implicitly if present. This default manifest has got language
2607330f729Sjoerg // id zero, and should be dropped silently if there's another manifest present.
2617330f729Sjoerg // If the user resources surprisignly had a manifest with language id zero,
2627330f729Sjoerg // we should also ignore the duplicate default manifest.
cleanUpManifests(std::vector<std::string> & Duplicates)2637330f729Sjoerg void WindowsResourceParser::cleanUpManifests(
2647330f729Sjoerg     std::vector<std::string> &Duplicates) {
2657330f729Sjoerg   auto TypeIt = Root.IDChildren.find(/* RT_MANIFEST */ 24);
2667330f729Sjoerg   if (TypeIt == Root.IDChildren.end())
2677330f729Sjoerg     return;
2687330f729Sjoerg 
2697330f729Sjoerg   TreeNode *TypeNode = TypeIt->second.get();
2707330f729Sjoerg   auto NameIt =
2717330f729Sjoerg       TypeNode->IDChildren.find(/* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1);
2727330f729Sjoerg   if (NameIt == TypeNode->IDChildren.end())
2737330f729Sjoerg     return;
2747330f729Sjoerg 
2757330f729Sjoerg   TreeNode *NameNode = NameIt->second.get();
2767330f729Sjoerg   if (NameNode->IDChildren.size() <= 1)
2777330f729Sjoerg     return; // None or one manifest present, all good.
2787330f729Sjoerg 
2797330f729Sjoerg   // If we have more than one manifest, drop the language zero one if present,
2807330f729Sjoerg   // and check again.
2817330f729Sjoerg   auto LangZeroIt = NameNode->IDChildren.find(0);
2827330f729Sjoerg   if (LangZeroIt != NameNode->IDChildren.end() &&
2837330f729Sjoerg       LangZeroIt->second->IsDataNode) {
2847330f729Sjoerg     uint32_t RemovedIndex = LangZeroIt->second->DataIndex;
2857330f729Sjoerg     NameNode->IDChildren.erase(LangZeroIt);
2867330f729Sjoerg     Data.erase(Data.begin() + RemovedIndex);
2877330f729Sjoerg     Root.shiftDataIndexDown(RemovedIndex);
2887330f729Sjoerg 
2897330f729Sjoerg     // If we're now down to one manifest, all is good.
2907330f729Sjoerg     if (NameNode->IDChildren.size() <= 1)
2917330f729Sjoerg       return;
2927330f729Sjoerg   }
2937330f729Sjoerg 
2947330f729Sjoerg   // More than one non-language-zero manifest
2957330f729Sjoerg   auto FirstIt = NameNode->IDChildren.begin();
2967330f729Sjoerg   uint32_t FirstLang = FirstIt->first;
2977330f729Sjoerg   TreeNode *FirstNode = FirstIt->second.get();
2987330f729Sjoerg   auto LastIt = NameNode->IDChildren.rbegin();
2997330f729Sjoerg   uint32_t LastLang = LastIt->first;
3007330f729Sjoerg   TreeNode *LastNode = LastIt->second.get();
3017330f729Sjoerg   Duplicates.push_back(
3027330f729Sjoerg       ("duplicate non-default manifests with languages " + Twine(FirstLang) +
3037330f729Sjoerg        " in " + InputFilenames[FirstNode->Origin] + " and " + Twine(LastLang) +
3047330f729Sjoerg        " in " + InputFilenames[LastNode->Origin])
3057330f729Sjoerg           .str());
3067330f729Sjoerg }
3077330f729Sjoerg 
3087330f729Sjoerg // Ignore duplicates of manifests with language zero (the default manifest),
3097330f729Sjoerg // in case the user has provided a manifest with that language id. See
3107330f729Sjoerg // the function comment above for context. Only returns true if MinGW is set
3117330f729Sjoerg // to true.
shouldIgnoreDuplicate(const ResourceEntryRef & Entry) const3127330f729Sjoerg bool WindowsResourceParser::shouldIgnoreDuplicate(
3137330f729Sjoerg     const ResourceEntryRef &Entry) const {
3147330f729Sjoerg   return MinGW && !Entry.checkTypeString() &&
3157330f729Sjoerg          Entry.getTypeID() == /* RT_MANIFEST */ 24 &&
3167330f729Sjoerg          !Entry.checkNameString() &&
3177330f729Sjoerg          Entry.getNameID() == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&
3187330f729Sjoerg          Entry.getLanguage() == 0;
3197330f729Sjoerg }
3207330f729Sjoerg 
shouldIgnoreDuplicate(const std::vector<StringOrID> & Context) const3217330f729Sjoerg bool WindowsResourceParser::shouldIgnoreDuplicate(
3227330f729Sjoerg     const std::vector<StringOrID> &Context) const {
3237330f729Sjoerg   return MinGW && Context.size() == 3 && !Context[0].IsString &&
3247330f729Sjoerg          Context[0].ID == /* RT_MANIFEST */ 24 && !Context[1].IsString &&
3257330f729Sjoerg          Context[1].ID == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&
3267330f729Sjoerg          !Context[2].IsString && Context[2].ID == 0;
3277330f729Sjoerg }
3287330f729Sjoerg 
parse(WindowsResource * WR,std::vector<std::string> & Duplicates)3297330f729Sjoerg Error WindowsResourceParser::parse(WindowsResource *WR,
3307330f729Sjoerg                                    std::vector<std::string> &Duplicates) {
3317330f729Sjoerg   auto EntryOrErr = WR->getHeadEntry();
3327330f729Sjoerg   if (!EntryOrErr) {
3337330f729Sjoerg     auto E = EntryOrErr.takeError();
3347330f729Sjoerg     if (E.isA<EmptyResError>()) {
3357330f729Sjoerg       // Check if the .res file contains no entries.  In this case we don't have
3367330f729Sjoerg       // to throw an error but can rather just return without parsing anything.
3377330f729Sjoerg       // This applies for files which have a valid PE header magic and the
3387330f729Sjoerg       // mandatory empty null resource entry.  Files which do not fit this
3397330f729Sjoerg       // criteria would have already been filtered out by
3407330f729Sjoerg       // WindowsResource::createWindowsResource().
3417330f729Sjoerg       consumeError(std::move(E));
3427330f729Sjoerg       return Error::success();
3437330f729Sjoerg     }
3447330f729Sjoerg     return E;
3457330f729Sjoerg   }
3467330f729Sjoerg 
3477330f729Sjoerg   ResourceEntryRef Entry = EntryOrErr.get();
3487330f729Sjoerg   uint32_t Origin = InputFilenames.size();
349*82d56013Sjoerg   InputFilenames.push_back(std::string(WR->getFileName()));
3507330f729Sjoerg   bool End = false;
3517330f729Sjoerg   while (!End) {
3527330f729Sjoerg 
3537330f729Sjoerg     TreeNode *Node;
3547330f729Sjoerg     bool IsNewNode = Root.addEntry(Entry, Origin, Data, StringTable, Node);
3557330f729Sjoerg     if (!IsNewNode) {
3567330f729Sjoerg       if (!shouldIgnoreDuplicate(Entry))
3577330f729Sjoerg         Duplicates.push_back(makeDuplicateResourceError(
3587330f729Sjoerg             Entry, InputFilenames[Node->Origin], WR->getFileName()));
3597330f729Sjoerg     }
3607330f729Sjoerg 
3617330f729Sjoerg     RETURN_IF_ERROR(Entry.moveNext(End));
3627330f729Sjoerg   }
3637330f729Sjoerg 
3647330f729Sjoerg   return Error::success();
3657330f729Sjoerg }
3667330f729Sjoerg 
parse(ResourceSectionRef & RSR,StringRef Filename,std::vector<std::string> & Duplicates)3677330f729Sjoerg Error WindowsResourceParser::parse(ResourceSectionRef &RSR, StringRef Filename,
3687330f729Sjoerg                                    std::vector<std::string> &Duplicates) {
3697330f729Sjoerg   UNWRAP_REF_OR_RETURN(BaseTable, RSR.getBaseTable());
3707330f729Sjoerg   uint32_t Origin = InputFilenames.size();
371*82d56013Sjoerg   InputFilenames.push_back(std::string(Filename));
3727330f729Sjoerg   std::vector<StringOrID> Context;
3737330f729Sjoerg   return addChildren(Root, RSR, BaseTable, Origin, Context, Duplicates);
3747330f729Sjoerg }
3757330f729Sjoerg 
printTree(raw_ostream & OS) const3767330f729Sjoerg void WindowsResourceParser::printTree(raw_ostream &OS) const {
3777330f729Sjoerg   ScopedPrinter Writer(OS);
3787330f729Sjoerg   Root.print(Writer, "Resource Tree");
3797330f729Sjoerg }
3807330f729Sjoerg 
addEntry(const ResourceEntryRef & Entry,uint32_t Origin,std::vector<std::vector<uint8_t>> & Data,std::vector<std::vector<UTF16>> & StringTable,TreeNode * & Result)3817330f729Sjoerg bool WindowsResourceParser::TreeNode::addEntry(
3827330f729Sjoerg     const ResourceEntryRef &Entry, uint32_t Origin,
3837330f729Sjoerg     std::vector<std::vector<uint8_t>> &Data,
3847330f729Sjoerg     std::vector<std::vector<UTF16>> &StringTable, TreeNode *&Result) {
3857330f729Sjoerg   TreeNode &TypeNode = addTypeNode(Entry, StringTable);
3867330f729Sjoerg   TreeNode &NameNode = TypeNode.addNameNode(Entry, StringTable);
3877330f729Sjoerg   return NameNode.addLanguageNode(Entry, Origin, Data, Result);
3887330f729Sjoerg }
3897330f729Sjoerg 
addChildren(TreeNode & Node,ResourceSectionRef & RSR,const coff_resource_dir_table & Table,uint32_t Origin,std::vector<StringOrID> & Context,std::vector<std::string> & Duplicates)3907330f729Sjoerg Error WindowsResourceParser::addChildren(TreeNode &Node,
3917330f729Sjoerg                                          ResourceSectionRef &RSR,
3927330f729Sjoerg                                          const coff_resource_dir_table &Table,
3937330f729Sjoerg                                          uint32_t Origin,
3947330f729Sjoerg                                          std::vector<StringOrID> &Context,
3957330f729Sjoerg                                          std::vector<std::string> &Duplicates) {
3967330f729Sjoerg 
3977330f729Sjoerg   for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries;
3987330f729Sjoerg        i++) {
3997330f729Sjoerg     UNWRAP_REF_OR_RETURN(Entry, RSR.getTableEntry(Table, i));
4007330f729Sjoerg     TreeNode *Child;
4017330f729Sjoerg 
4027330f729Sjoerg     if (Entry.Offset.isSubDir()) {
4037330f729Sjoerg 
4047330f729Sjoerg       // Create a new subdirectory and recurse
4057330f729Sjoerg       if (i < Table.NumberOfNameEntries) {
4067330f729Sjoerg         UNWRAP_OR_RETURN(NameString, RSR.getEntryNameString(Entry));
4077330f729Sjoerg         Child = &Node.addNameChild(NameString, StringTable);
4087330f729Sjoerg         Context.push_back(StringOrID(NameString));
4097330f729Sjoerg       } else {
4107330f729Sjoerg         Child = &Node.addIDChild(Entry.Identifier.ID);
4117330f729Sjoerg         Context.push_back(StringOrID(Entry.Identifier.ID));
4127330f729Sjoerg       }
4137330f729Sjoerg 
4147330f729Sjoerg       UNWRAP_REF_OR_RETURN(NextTable, RSR.getEntrySubDir(Entry));
4157330f729Sjoerg       Error E =
4167330f729Sjoerg           addChildren(*Child, RSR, NextTable, Origin, Context, Duplicates);
4177330f729Sjoerg       if (E)
4187330f729Sjoerg         return E;
4197330f729Sjoerg       Context.pop_back();
4207330f729Sjoerg 
4217330f729Sjoerg     } else {
4227330f729Sjoerg 
4237330f729Sjoerg       // Data leaves are supposed to have a numeric ID as identifier (language).
4247330f729Sjoerg       if (Table.NumberOfNameEntries > 0)
4257330f729Sjoerg         return createStringError(object_error::parse_failed,
4267330f729Sjoerg                                  "unexpected string key for data object");
4277330f729Sjoerg 
4287330f729Sjoerg       // Try adding a data leaf
4297330f729Sjoerg       UNWRAP_REF_OR_RETURN(DataEntry, RSR.getEntryData(Entry));
4307330f729Sjoerg       TreeNode *Child;
4317330f729Sjoerg       Context.push_back(StringOrID(Entry.Identifier.ID));
4327330f729Sjoerg       bool Added = Node.addDataChild(Entry.Identifier.ID, Table.MajorVersion,
4337330f729Sjoerg                                      Table.MinorVersion, Table.Characteristics,
4347330f729Sjoerg                                      Origin, Data.size(), Child);
4357330f729Sjoerg       if (Added) {
4367330f729Sjoerg         UNWRAP_OR_RETURN(Contents, RSR.getContents(DataEntry));
4377330f729Sjoerg         Data.push_back(ArrayRef<uint8_t>(
4387330f729Sjoerg             reinterpret_cast<const uint8_t *>(Contents.data()),
4397330f729Sjoerg             Contents.size()));
4407330f729Sjoerg       } else {
4417330f729Sjoerg         if (!shouldIgnoreDuplicate(Context))
4427330f729Sjoerg           Duplicates.push_back(makeDuplicateResourceError(
4437330f729Sjoerg               Context, InputFilenames[Child->Origin], InputFilenames.back()));
4447330f729Sjoerg       }
4457330f729Sjoerg       Context.pop_back();
4467330f729Sjoerg 
4477330f729Sjoerg     }
4487330f729Sjoerg   }
4497330f729Sjoerg   return Error::success();
4507330f729Sjoerg }
4517330f729Sjoerg 
TreeNode(uint32_t StringIndex)4527330f729Sjoerg WindowsResourceParser::TreeNode::TreeNode(uint32_t StringIndex)
4537330f729Sjoerg     : StringIndex(StringIndex) {}
4547330f729Sjoerg 
TreeNode(uint16_t MajorVersion,uint16_t MinorVersion,uint32_t Characteristics,uint32_t Origin,uint32_t DataIndex)4557330f729Sjoerg WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion,
4567330f729Sjoerg                                           uint16_t MinorVersion,
4577330f729Sjoerg                                           uint32_t Characteristics,
4587330f729Sjoerg                                           uint32_t Origin, uint32_t DataIndex)
4597330f729Sjoerg     : IsDataNode(true), DataIndex(DataIndex), MajorVersion(MajorVersion),
4607330f729Sjoerg       MinorVersion(MinorVersion), Characteristics(Characteristics),
4617330f729Sjoerg       Origin(Origin) {}
4627330f729Sjoerg 
4637330f729Sjoerg std::unique_ptr<WindowsResourceParser::TreeNode>
createStringNode(uint32_t Index)4647330f729Sjoerg WindowsResourceParser::TreeNode::createStringNode(uint32_t Index) {
4657330f729Sjoerg   return std::unique_ptr<TreeNode>(new TreeNode(Index));
4667330f729Sjoerg }
4677330f729Sjoerg 
4687330f729Sjoerg std::unique_ptr<WindowsResourceParser::TreeNode>
createIDNode()4697330f729Sjoerg WindowsResourceParser::TreeNode::createIDNode() {
4707330f729Sjoerg   return std::unique_ptr<TreeNode>(new TreeNode(0));
4717330f729Sjoerg }
4727330f729Sjoerg 
4737330f729Sjoerg std::unique_ptr<WindowsResourceParser::TreeNode>
createDataNode(uint16_t MajorVersion,uint16_t MinorVersion,uint32_t Characteristics,uint32_t Origin,uint32_t DataIndex)4747330f729Sjoerg WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion,
4757330f729Sjoerg                                                 uint16_t MinorVersion,
4767330f729Sjoerg                                                 uint32_t Characteristics,
4777330f729Sjoerg                                                 uint32_t Origin,
4787330f729Sjoerg                                                 uint32_t DataIndex) {
4797330f729Sjoerg   return std::unique_ptr<TreeNode>(new TreeNode(
4807330f729Sjoerg       MajorVersion, MinorVersion, Characteristics, Origin, DataIndex));
4817330f729Sjoerg }
4827330f729Sjoerg 
addTypeNode(const ResourceEntryRef & Entry,std::vector<std::vector<UTF16>> & StringTable)4837330f729Sjoerg WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addTypeNode(
4847330f729Sjoerg     const ResourceEntryRef &Entry,
4857330f729Sjoerg     std::vector<std::vector<UTF16>> &StringTable) {
4867330f729Sjoerg   if (Entry.checkTypeString())
4877330f729Sjoerg     return addNameChild(Entry.getTypeString(), StringTable);
4887330f729Sjoerg   else
4897330f729Sjoerg     return addIDChild(Entry.getTypeID());
4907330f729Sjoerg }
4917330f729Sjoerg 
addNameNode(const ResourceEntryRef & Entry,std::vector<std::vector<UTF16>> & StringTable)4927330f729Sjoerg WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameNode(
4937330f729Sjoerg     const ResourceEntryRef &Entry,
4947330f729Sjoerg     std::vector<std::vector<UTF16>> &StringTable) {
4957330f729Sjoerg   if (Entry.checkNameString())
4967330f729Sjoerg     return addNameChild(Entry.getNameString(), StringTable);
4977330f729Sjoerg   else
4987330f729Sjoerg     return addIDChild(Entry.getNameID());
4997330f729Sjoerg }
5007330f729Sjoerg 
addLanguageNode(const ResourceEntryRef & Entry,uint32_t Origin,std::vector<std::vector<uint8_t>> & Data,TreeNode * & Result)5017330f729Sjoerg bool WindowsResourceParser::TreeNode::addLanguageNode(
5027330f729Sjoerg     const ResourceEntryRef &Entry, uint32_t Origin,
5037330f729Sjoerg     std::vector<std::vector<uint8_t>> &Data, TreeNode *&Result) {
5047330f729Sjoerg   bool Added = addDataChild(Entry.getLanguage(), Entry.getMajorVersion(),
5057330f729Sjoerg                             Entry.getMinorVersion(), Entry.getCharacteristics(),
5067330f729Sjoerg                             Origin, Data.size(), Result);
5077330f729Sjoerg   if (Added)
5087330f729Sjoerg     Data.push_back(Entry.getData());
5097330f729Sjoerg   return Added;
5107330f729Sjoerg }
5117330f729Sjoerg 
addDataChild(uint32_t ID,uint16_t MajorVersion,uint16_t MinorVersion,uint32_t Characteristics,uint32_t Origin,uint32_t DataIndex,TreeNode * & Result)5127330f729Sjoerg bool WindowsResourceParser::TreeNode::addDataChild(
5137330f729Sjoerg     uint32_t ID, uint16_t MajorVersion, uint16_t MinorVersion,
5147330f729Sjoerg     uint32_t Characteristics, uint32_t Origin, uint32_t DataIndex,
5157330f729Sjoerg     TreeNode *&Result) {
5167330f729Sjoerg   auto NewChild = createDataNode(MajorVersion, MinorVersion, Characteristics,
5177330f729Sjoerg                                  Origin, DataIndex);
5187330f729Sjoerg   auto ElementInserted = IDChildren.emplace(ID, std::move(NewChild));
5197330f729Sjoerg   Result = ElementInserted.first->second.get();
5207330f729Sjoerg   return ElementInserted.second;
5217330f729Sjoerg }
5227330f729Sjoerg 
addIDChild(uint32_t ID)5237330f729Sjoerg WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addIDChild(
5247330f729Sjoerg     uint32_t ID) {
5257330f729Sjoerg   auto Child = IDChildren.find(ID);
5267330f729Sjoerg   if (Child == IDChildren.end()) {
5277330f729Sjoerg     auto NewChild = createIDNode();
5287330f729Sjoerg     WindowsResourceParser::TreeNode &Node = *NewChild;
5297330f729Sjoerg     IDChildren.emplace(ID, std::move(NewChild));
5307330f729Sjoerg     return Node;
5317330f729Sjoerg   } else
5327330f729Sjoerg     return *(Child->second);
5337330f729Sjoerg }
5347330f729Sjoerg 
addNameChild(ArrayRef<UTF16> NameRef,std::vector<std::vector<UTF16>> & StringTable)5357330f729Sjoerg WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameChild(
5367330f729Sjoerg     ArrayRef<UTF16> NameRef, std::vector<std::vector<UTF16>> &StringTable) {
5377330f729Sjoerg   std::string NameString;
5387330f729Sjoerg   convertUTF16LEToUTF8String(NameRef, NameString);
5397330f729Sjoerg 
5407330f729Sjoerg   auto Child = StringChildren.find(NameString);
5417330f729Sjoerg   if (Child == StringChildren.end()) {
5427330f729Sjoerg     auto NewChild = createStringNode(StringTable.size());
5437330f729Sjoerg     StringTable.push_back(NameRef);
5447330f729Sjoerg     WindowsResourceParser::TreeNode &Node = *NewChild;
5457330f729Sjoerg     StringChildren.emplace(NameString, std::move(NewChild));
5467330f729Sjoerg     return Node;
5477330f729Sjoerg   } else
5487330f729Sjoerg     return *(Child->second);
5497330f729Sjoerg }
5507330f729Sjoerg 
print(ScopedPrinter & Writer,StringRef Name) const5517330f729Sjoerg void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer,
5527330f729Sjoerg                                             StringRef Name) const {
5537330f729Sjoerg   ListScope NodeScope(Writer, Name);
5547330f729Sjoerg   for (auto const &Child : StringChildren) {
5557330f729Sjoerg     Child.second->print(Writer, Child.first);
5567330f729Sjoerg   }
5577330f729Sjoerg   for (auto const &Child : IDChildren) {
5587330f729Sjoerg     Child.second->print(Writer, to_string(Child.first));
5597330f729Sjoerg   }
5607330f729Sjoerg }
5617330f729Sjoerg 
5627330f729Sjoerg // This function returns the size of the entire resource tree, including
5637330f729Sjoerg // directory tables, directory entries, and data entries.  It does not include
5647330f729Sjoerg // the directory strings or the relocations of the .rsrc section.
getTreeSize() const5657330f729Sjoerg uint32_t WindowsResourceParser::TreeNode::getTreeSize() const {
5667330f729Sjoerg   uint32_t Size = (IDChildren.size() + StringChildren.size()) *
5677330f729Sjoerg                   sizeof(coff_resource_dir_entry);
5687330f729Sjoerg 
5697330f729Sjoerg   // Reached a node pointing to a data entry.
5707330f729Sjoerg   if (IsDataNode) {
5717330f729Sjoerg     Size += sizeof(coff_resource_data_entry);
5727330f729Sjoerg     return Size;
5737330f729Sjoerg   }
5747330f729Sjoerg 
5757330f729Sjoerg   // If the node does not point to data, it must have a directory table pointing
5767330f729Sjoerg   // to other nodes.
5777330f729Sjoerg   Size += sizeof(coff_resource_dir_table);
5787330f729Sjoerg 
5797330f729Sjoerg   for (auto const &Child : StringChildren) {
5807330f729Sjoerg     Size += Child.second->getTreeSize();
5817330f729Sjoerg   }
5827330f729Sjoerg   for (auto const &Child : IDChildren) {
5837330f729Sjoerg     Size += Child.second->getTreeSize();
5847330f729Sjoerg   }
5857330f729Sjoerg   return Size;
5867330f729Sjoerg }
5877330f729Sjoerg 
5887330f729Sjoerg // Shift DataIndex of all data children with an Index greater or equal to the
5897330f729Sjoerg // given one, to fill a gap from removing an entry from the Data vector.
shiftDataIndexDown(uint32_t Index)5907330f729Sjoerg void WindowsResourceParser::TreeNode::shiftDataIndexDown(uint32_t Index) {
5917330f729Sjoerg   if (IsDataNode && DataIndex >= Index) {
5927330f729Sjoerg     DataIndex--;
5937330f729Sjoerg   } else {
5947330f729Sjoerg     for (auto &Child : IDChildren)
5957330f729Sjoerg       Child.second->shiftDataIndexDown(Index);
5967330f729Sjoerg     for (auto &Child : StringChildren)
5977330f729Sjoerg       Child.second->shiftDataIndexDown(Index);
5987330f729Sjoerg   }
5997330f729Sjoerg }
6007330f729Sjoerg 
6017330f729Sjoerg class WindowsResourceCOFFWriter {
6027330f729Sjoerg public:
6037330f729Sjoerg   WindowsResourceCOFFWriter(COFF::MachineTypes MachineType,
6047330f729Sjoerg                             const WindowsResourceParser &Parser, Error &E);
6057330f729Sjoerg   std::unique_ptr<MemoryBuffer> write(uint32_t TimeDateStamp);
6067330f729Sjoerg 
6077330f729Sjoerg private:
6087330f729Sjoerg   void performFileLayout();
6097330f729Sjoerg   void performSectionOneLayout();
6107330f729Sjoerg   void performSectionTwoLayout();
6117330f729Sjoerg   void writeCOFFHeader(uint32_t TimeDateStamp);
6127330f729Sjoerg   void writeFirstSectionHeader();
6137330f729Sjoerg   void writeSecondSectionHeader();
6147330f729Sjoerg   void writeFirstSection();
6157330f729Sjoerg   void writeSecondSection();
6167330f729Sjoerg   void writeSymbolTable();
6177330f729Sjoerg   void writeStringTable();
6187330f729Sjoerg   void writeDirectoryTree();
6197330f729Sjoerg   void writeDirectoryStringTable();
6207330f729Sjoerg   void writeFirstSectionRelocations();
6217330f729Sjoerg   std::unique_ptr<WritableMemoryBuffer> OutputBuffer;
6227330f729Sjoerg   char *BufferStart;
6237330f729Sjoerg   uint64_t CurrentOffset = 0;
6247330f729Sjoerg   COFF::MachineTypes MachineType;
6257330f729Sjoerg   const WindowsResourceParser::TreeNode &Resources;
6267330f729Sjoerg   const ArrayRef<std::vector<uint8_t>> Data;
6277330f729Sjoerg   uint64_t FileSize;
6287330f729Sjoerg   uint32_t SymbolTableOffset;
6297330f729Sjoerg   uint32_t SectionOneSize;
6307330f729Sjoerg   uint32_t SectionOneOffset;
6317330f729Sjoerg   uint32_t SectionOneRelocations;
6327330f729Sjoerg   uint32_t SectionTwoSize;
6337330f729Sjoerg   uint32_t SectionTwoOffset;
6347330f729Sjoerg   const ArrayRef<std::vector<UTF16>> StringTable;
6357330f729Sjoerg   std::vector<uint32_t> StringTableOffsets;
6367330f729Sjoerg   std::vector<uint32_t> DataOffsets;
6377330f729Sjoerg   std::vector<uint32_t> RelocationAddresses;
6387330f729Sjoerg };
6397330f729Sjoerg 
WindowsResourceCOFFWriter(COFF::MachineTypes MachineType,const WindowsResourceParser & Parser,Error & E)6407330f729Sjoerg WindowsResourceCOFFWriter::WindowsResourceCOFFWriter(
6417330f729Sjoerg     COFF::MachineTypes MachineType, const WindowsResourceParser &Parser,
6427330f729Sjoerg     Error &E)
6437330f729Sjoerg     : MachineType(MachineType), Resources(Parser.getTree()),
6447330f729Sjoerg       Data(Parser.getData()), StringTable(Parser.getStringTable()) {
6457330f729Sjoerg   performFileLayout();
6467330f729Sjoerg 
6477330f729Sjoerg   OutputBuffer = WritableMemoryBuffer::getNewMemBuffer(
6487330f729Sjoerg       FileSize, "internal .obj file created from .res files");
6497330f729Sjoerg }
6507330f729Sjoerg 
performFileLayout()6517330f729Sjoerg void WindowsResourceCOFFWriter::performFileLayout() {
6527330f729Sjoerg   // Add size of COFF header.
6537330f729Sjoerg   FileSize = COFF::Header16Size;
6547330f729Sjoerg 
6557330f729Sjoerg   // one .rsrc section header for directory tree, another for resource data.
6567330f729Sjoerg   FileSize += 2 * COFF::SectionSize;
6577330f729Sjoerg 
6587330f729Sjoerg   performSectionOneLayout();
6597330f729Sjoerg   performSectionTwoLayout();
6607330f729Sjoerg 
6617330f729Sjoerg   // We have reached the address of the symbol table.
6627330f729Sjoerg   SymbolTableOffset = FileSize;
6637330f729Sjoerg 
6647330f729Sjoerg   FileSize += COFF::Symbol16Size;     // size of the @feat.00 symbol.
6657330f729Sjoerg   FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section.
6667330f729Sjoerg   FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource.
6677330f729Sjoerg   FileSize += 4; // four null bytes for the string table.
6687330f729Sjoerg }
6697330f729Sjoerg 
performSectionOneLayout()6707330f729Sjoerg void WindowsResourceCOFFWriter::performSectionOneLayout() {
6717330f729Sjoerg   SectionOneOffset = FileSize;
6727330f729Sjoerg 
6737330f729Sjoerg   SectionOneSize = Resources.getTreeSize();
6747330f729Sjoerg   uint32_t CurrentStringOffset = SectionOneSize;
6757330f729Sjoerg   uint32_t TotalStringTableSize = 0;
6767330f729Sjoerg   for (auto const &String : StringTable) {
6777330f729Sjoerg     StringTableOffsets.push_back(CurrentStringOffset);
6787330f729Sjoerg     uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t);
6797330f729Sjoerg     CurrentStringOffset += StringSize;
6807330f729Sjoerg     TotalStringTableSize += StringSize;
6817330f729Sjoerg   }
6827330f729Sjoerg   SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t));
6837330f729Sjoerg 
6847330f729Sjoerg   // account for the relocations of section one.
6857330f729Sjoerg   SectionOneRelocations = FileSize + SectionOneSize;
6867330f729Sjoerg   FileSize += SectionOneSize;
6877330f729Sjoerg   FileSize +=
6887330f729Sjoerg       Data.size() * COFF::RelocationSize; // one relocation for each resource.
6897330f729Sjoerg   FileSize = alignTo(FileSize, SECTION_ALIGNMENT);
6907330f729Sjoerg }
6917330f729Sjoerg 
performSectionTwoLayout()6927330f729Sjoerg void WindowsResourceCOFFWriter::performSectionTwoLayout() {
6937330f729Sjoerg   // add size of .rsrc$2 section, which contains all resource data on 8-byte
6947330f729Sjoerg   // alignment.
6957330f729Sjoerg   SectionTwoOffset = FileSize;
6967330f729Sjoerg   SectionTwoSize = 0;
6977330f729Sjoerg   for (auto const &Entry : Data) {
6987330f729Sjoerg     DataOffsets.push_back(SectionTwoSize);
6997330f729Sjoerg     SectionTwoSize += alignTo(Entry.size(), sizeof(uint64_t));
7007330f729Sjoerg   }
7017330f729Sjoerg   FileSize += SectionTwoSize;
7027330f729Sjoerg   FileSize = alignTo(FileSize, SECTION_ALIGNMENT);
7037330f729Sjoerg }
7047330f729Sjoerg 
7057330f729Sjoerg std::unique_ptr<MemoryBuffer>
write(uint32_t TimeDateStamp)7067330f729Sjoerg WindowsResourceCOFFWriter::write(uint32_t TimeDateStamp) {
7077330f729Sjoerg   BufferStart = OutputBuffer->getBufferStart();
7087330f729Sjoerg 
7097330f729Sjoerg   writeCOFFHeader(TimeDateStamp);
7107330f729Sjoerg   writeFirstSectionHeader();
7117330f729Sjoerg   writeSecondSectionHeader();
7127330f729Sjoerg   writeFirstSection();
7137330f729Sjoerg   writeSecondSection();
7147330f729Sjoerg   writeSymbolTable();
7157330f729Sjoerg   writeStringTable();
7167330f729Sjoerg 
7177330f729Sjoerg   return std::move(OutputBuffer);
7187330f729Sjoerg }
7197330f729Sjoerg 
7207330f729Sjoerg // According to COFF specification, if the Src has a size equal to Dest,
7217330f729Sjoerg // it's okay to *not* copy the trailing zero.
coffnamecpy(char (& Dest)[COFF::NameSize],StringRef Src)7227330f729Sjoerg static void coffnamecpy(char (&Dest)[COFF::NameSize], StringRef Src) {
7237330f729Sjoerg   assert(Src.size() <= COFF::NameSize &&
724*82d56013Sjoerg          "Src is larger than COFF::NameSize");
725*82d56013Sjoerg   assert((Src.size() == COFF::NameSize || Dest[Src.size()] == '\0') &&
726*82d56013Sjoerg          "Dest not zeroed upon initialization");
727*82d56013Sjoerg   memcpy(Dest, Src.data(), Src.size());
7287330f729Sjoerg }
7297330f729Sjoerg 
writeCOFFHeader(uint32_t TimeDateStamp)7307330f729Sjoerg void WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp) {
7317330f729Sjoerg   // Write the COFF header.
7327330f729Sjoerg   auto *Header = reinterpret_cast<coff_file_header *>(BufferStart);
7337330f729Sjoerg   Header->Machine = MachineType;
7347330f729Sjoerg   Header->NumberOfSections = 2;
7357330f729Sjoerg   Header->TimeDateStamp = TimeDateStamp;
7367330f729Sjoerg   Header->PointerToSymbolTable = SymbolTableOffset;
7377330f729Sjoerg   // One symbol for every resource plus 2 for each section and 1 for @feat.00
7387330f729Sjoerg   Header->NumberOfSymbols = Data.size() + 5;
7397330f729Sjoerg   Header->SizeOfOptionalHeader = 0;
7407330f729Sjoerg   // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it.
7417330f729Sjoerg   Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE;
7427330f729Sjoerg }
7437330f729Sjoerg 
writeFirstSectionHeader()7447330f729Sjoerg void WindowsResourceCOFFWriter::writeFirstSectionHeader() {
7457330f729Sjoerg   // Write the first section header.
7467330f729Sjoerg   CurrentOffset += sizeof(coff_file_header);
7477330f729Sjoerg   auto *SectionOneHeader =
7487330f729Sjoerg       reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);
7497330f729Sjoerg   coffnamecpy(SectionOneHeader->Name, ".rsrc$01");
7507330f729Sjoerg   SectionOneHeader->VirtualSize = 0;
7517330f729Sjoerg   SectionOneHeader->VirtualAddress = 0;
7527330f729Sjoerg   SectionOneHeader->SizeOfRawData = SectionOneSize;
7537330f729Sjoerg   SectionOneHeader->PointerToRawData = SectionOneOffset;
7547330f729Sjoerg   SectionOneHeader->PointerToRelocations = SectionOneRelocations;
7557330f729Sjoerg   SectionOneHeader->PointerToLinenumbers = 0;
7567330f729Sjoerg   SectionOneHeader->NumberOfRelocations = Data.size();
7577330f729Sjoerg   SectionOneHeader->NumberOfLinenumbers = 0;
7587330f729Sjoerg   SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
7597330f729Sjoerg   SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;
7607330f729Sjoerg }
7617330f729Sjoerg 
writeSecondSectionHeader()7627330f729Sjoerg void WindowsResourceCOFFWriter::writeSecondSectionHeader() {
7637330f729Sjoerg   // Write the second section header.
7647330f729Sjoerg   CurrentOffset += sizeof(coff_section);
7657330f729Sjoerg   auto *SectionTwoHeader =
7667330f729Sjoerg       reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);
7677330f729Sjoerg   coffnamecpy(SectionTwoHeader->Name, ".rsrc$02");
7687330f729Sjoerg   SectionTwoHeader->VirtualSize = 0;
7697330f729Sjoerg   SectionTwoHeader->VirtualAddress = 0;
7707330f729Sjoerg   SectionTwoHeader->SizeOfRawData = SectionTwoSize;
7717330f729Sjoerg   SectionTwoHeader->PointerToRawData = SectionTwoOffset;
7727330f729Sjoerg   SectionTwoHeader->PointerToRelocations = 0;
7737330f729Sjoerg   SectionTwoHeader->PointerToLinenumbers = 0;
7747330f729Sjoerg   SectionTwoHeader->NumberOfRelocations = 0;
7757330f729Sjoerg   SectionTwoHeader->NumberOfLinenumbers = 0;
7767330f729Sjoerg   SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
7777330f729Sjoerg   SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;
7787330f729Sjoerg }
7797330f729Sjoerg 
writeFirstSection()7807330f729Sjoerg void WindowsResourceCOFFWriter::writeFirstSection() {
7817330f729Sjoerg   // Write section one.
7827330f729Sjoerg   CurrentOffset += sizeof(coff_section);
7837330f729Sjoerg 
7847330f729Sjoerg   writeDirectoryTree();
7857330f729Sjoerg   writeDirectoryStringTable();
7867330f729Sjoerg   writeFirstSectionRelocations();
7877330f729Sjoerg 
7887330f729Sjoerg   CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);
7897330f729Sjoerg }
7907330f729Sjoerg 
writeSecondSection()7917330f729Sjoerg void WindowsResourceCOFFWriter::writeSecondSection() {
7927330f729Sjoerg   // Now write the .rsrc$02 section.
7937330f729Sjoerg   for (auto const &RawDataEntry : Data) {
7947330f729Sjoerg     llvm::copy(RawDataEntry, BufferStart + CurrentOffset);
7957330f729Sjoerg     CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t));
7967330f729Sjoerg   }
7977330f729Sjoerg 
7987330f729Sjoerg   CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);
7997330f729Sjoerg }
8007330f729Sjoerg 
writeSymbolTable()8017330f729Sjoerg void WindowsResourceCOFFWriter::writeSymbolTable() {
8027330f729Sjoerg   // Now write the symbol table.
8037330f729Sjoerg   // First, the feat symbol.
8047330f729Sjoerg   auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
8057330f729Sjoerg   coffnamecpy(Symbol->Name.ShortName, "@feat.00");
8067330f729Sjoerg   Symbol->Value = 0x11;
8077330f729Sjoerg   Symbol->SectionNumber = 0xffff;
8087330f729Sjoerg   Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
8097330f729Sjoerg   Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
8107330f729Sjoerg   Symbol->NumberOfAuxSymbols = 0;
8117330f729Sjoerg   CurrentOffset += sizeof(coff_symbol16);
8127330f729Sjoerg 
8137330f729Sjoerg   // Now write the .rsrc1 symbol + aux.
8147330f729Sjoerg   Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
8157330f729Sjoerg   coffnamecpy(Symbol->Name.ShortName, ".rsrc$01");
8167330f729Sjoerg   Symbol->Value = 0;
8177330f729Sjoerg   Symbol->SectionNumber = 1;
8187330f729Sjoerg   Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
8197330f729Sjoerg   Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
8207330f729Sjoerg   Symbol->NumberOfAuxSymbols = 1;
8217330f729Sjoerg   CurrentOffset += sizeof(coff_symbol16);
8227330f729Sjoerg   auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +
8237330f729Sjoerg                                                               CurrentOffset);
8247330f729Sjoerg   Aux->Length = SectionOneSize;
8257330f729Sjoerg   Aux->NumberOfRelocations = Data.size();
8267330f729Sjoerg   Aux->NumberOfLinenumbers = 0;
8277330f729Sjoerg   Aux->CheckSum = 0;
8287330f729Sjoerg   Aux->NumberLowPart = 0;
8297330f729Sjoerg   Aux->Selection = 0;
8307330f729Sjoerg   CurrentOffset += sizeof(coff_aux_section_definition);
8317330f729Sjoerg 
8327330f729Sjoerg   // Now write the .rsrc2 symbol + aux.
8337330f729Sjoerg   Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
8347330f729Sjoerg   coffnamecpy(Symbol->Name.ShortName, ".rsrc$02");
8357330f729Sjoerg   Symbol->Value = 0;
8367330f729Sjoerg   Symbol->SectionNumber = 2;
8377330f729Sjoerg   Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
8387330f729Sjoerg   Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
8397330f729Sjoerg   Symbol->NumberOfAuxSymbols = 1;
8407330f729Sjoerg   CurrentOffset += sizeof(coff_symbol16);
8417330f729Sjoerg   Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +
8427330f729Sjoerg                                                         CurrentOffset);
8437330f729Sjoerg   Aux->Length = SectionTwoSize;
8447330f729Sjoerg   Aux->NumberOfRelocations = 0;
8457330f729Sjoerg   Aux->NumberOfLinenumbers = 0;
8467330f729Sjoerg   Aux->CheckSum = 0;
8477330f729Sjoerg   Aux->NumberLowPart = 0;
8487330f729Sjoerg   Aux->Selection = 0;
8497330f729Sjoerg   CurrentOffset += sizeof(coff_aux_section_definition);
8507330f729Sjoerg 
8517330f729Sjoerg   // Now write a symbol for each relocation.
8527330f729Sjoerg   for (unsigned i = 0; i < Data.size(); i++) {
8537330f729Sjoerg     auto RelocationName = formatv("$R{0:X-6}", i & 0xffffff).sstr<COFF::NameSize>();
8547330f729Sjoerg     Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
8557330f729Sjoerg     coffnamecpy(Symbol->Name.ShortName, RelocationName);
8567330f729Sjoerg     Symbol->Value = DataOffsets[i];
8577330f729Sjoerg     Symbol->SectionNumber = 2;
8587330f729Sjoerg     Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
8597330f729Sjoerg     Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
8607330f729Sjoerg     Symbol->NumberOfAuxSymbols = 0;
8617330f729Sjoerg     CurrentOffset += sizeof(coff_symbol16);
8627330f729Sjoerg   }
8637330f729Sjoerg }
8647330f729Sjoerg 
writeStringTable()8657330f729Sjoerg void WindowsResourceCOFFWriter::writeStringTable() {
8667330f729Sjoerg   // Just 4 null bytes for the string table.
8677330f729Sjoerg   auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset);
8687330f729Sjoerg   memset(COFFStringTable, 0, 4);
8697330f729Sjoerg }
8707330f729Sjoerg 
writeDirectoryTree()8717330f729Sjoerg void WindowsResourceCOFFWriter::writeDirectoryTree() {
8727330f729Sjoerg   // Traverse parsed resource tree breadth-first and write the corresponding
8737330f729Sjoerg   // COFF objects.
8747330f729Sjoerg   std::queue<const WindowsResourceParser::TreeNode *> Queue;
8757330f729Sjoerg   Queue.push(&Resources);
8767330f729Sjoerg   uint32_t NextLevelOffset =
8777330f729Sjoerg       sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() +
8787330f729Sjoerg                                          Resources.getIDChildren().size()) *
8797330f729Sjoerg                                             sizeof(coff_resource_dir_entry);
8807330f729Sjoerg   std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder;
8817330f729Sjoerg   uint32_t CurrentRelativeOffset = 0;
8827330f729Sjoerg 
8837330f729Sjoerg   while (!Queue.empty()) {
8847330f729Sjoerg     auto CurrentNode = Queue.front();
8857330f729Sjoerg     Queue.pop();
8867330f729Sjoerg     auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart +
8877330f729Sjoerg                                                               CurrentOffset);
8887330f729Sjoerg     Table->Characteristics = CurrentNode->getCharacteristics();
8897330f729Sjoerg     Table->TimeDateStamp = 0;
8907330f729Sjoerg     Table->MajorVersion = CurrentNode->getMajorVersion();
8917330f729Sjoerg     Table->MinorVersion = CurrentNode->getMinorVersion();
8927330f729Sjoerg     auto &IDChildren = CurrentNode->getIDChildren();
8937330f729Sjoerg     auto &StringChildren = CurrentNode->getStringChildren();
8947330f729Sjoerg     Table->NumberOfNameEntries = StringChildren.size();
8957330f729Sjoerg     Table->NumberOfIDEntries = IDChildren.size();
8967330f729Sjoerg     CurrentOffset += sizeof(coff_resource_dir_table);
8977330f729Sjoerg     CurrentRelativeOffset += sizeof(coff_resource_dir_table);
8987330f729Sjoerg 
8997330f729Sjoerg     // Write the directory entries immediately following each directory table.
9007330f729Sjoerg     for (auto const &Child : StringChildren) {
9017330f729Sjoerg       auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +
9027330f729Sjoerg                                                                 CurrentOffset);
9037330f729Sjoerg       Entry->Identifier.setNameOffset(
9047330f729Sjoerg           StringTableOffsets[Child.second->getStringIndex()]);
9057330f729Sjoerg       if (Child.second->checkIsDataNode()) {
9067330f729Sjoerg         Entry->Offset.DataEntryOffset = NextLevelOffset;
9077330f729Sjoerg         NextLevelOffset += sizeof(coff_resource_data_entry);
9087330f729Sjoerg         DataEntriesTreeOrder.push_back(Child.second.get());
9097330f729Sjoerg       } else {
9107330f729Sjoerg         Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);
9117330f729Sjoerg         NextLevelOffset += sizeof(coff_resource_dir_table) +
9127330f729Sjoerg                            (Child.second->getStringChildren().size() +
9137330f729Sjoerg                             Child.second->getIDChildren().size()) *
9147330f729Sjoerg                                sizeof(coff_resource_dir_entry);
9157330f729Sjoerg         Queue.push(Child.second.get());
9167330f729Sjoerg       }
9177330f729Sjoerg       CurrentOffset += sizeof(coff_resource_dir_entry);
9187330f729Sjoerg       CurrentRelativeOffset += sizeof(coff_resource_dir_entry);
9197330f729Sjoerg     }
9207330f729Sjoerg     for (auto const &Child : IDChildren) {
9217330f729Sjoerg       auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +
9227330f729Sjoerg                                                                 CurrentOffset);
9237330f729Sjoerg       Entry->Identifier.ID = Child.first;
9247330f729Sjoerg       if (Child.second->checkIsDataNode()) {
9257330f729Sjoerg         Entry->Offset.DataEntryOffset = NextLevelOffset;
9267330f729Sjoerg         NextLevelOffset += sizeof(coff_resource_data_entry);
9277330f729Sjoerg         DataEntriesTreeOrder.push_back(Child.second.get());
9287330f729Sjoerg       } else {
9297330f729Sjoerg         Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);
9307330f729Sjoerg         NextLevelOffset += sizeof(coff_resource_dir_table) +
9317330f729Sjoerg                            (Child.second->getStringChildren().size() +
9327330f729Sjoerg                             Child.second->getIDChildren().size()) *
9337330f729Sjoerg                                sizeof(coff_resource_dir_entry);
9347330f729Sjoerg         Queue.push(Child.second.get());
9357330f729Sjoerg       }
9367330f729Sjoerg       CurrentOffset += sizeof(coff_resource_dir_entry);
9377330f729Sjoerg       CurrentRelativeOffset += sizeof(coff_resource_dir_entry);
9387330f729Sjoerg     }
9397330f729Sjoerg   }
9407330f729Sjoerg 
9417330f729Sjoerg   RelocationAddresses.resize(Data.size());
9427330f729Sjoerg   // Now write all the resource data entries.
9437330f729Sjoerg   for (auto DataNodes : DataEntriesTreeOrder) {
9447330f729Sjoerg     auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart +
9457330f729Sjoerg                                                                CurrentOffset);
9467330f729Sjoerg     RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset;
9477330f729Sjoerg     Entry->DataRVA = 0; // Set to zero because it is a relocation.
9487330f729Sjoerg     Entry->DataSize = Data[DataNodes->getDataIndex()].size();
9497330f729Sjoerg     Entry->Codepage = 0;
9507330f729Sjoerg     Entry->Reserved = 0;
9517330f729Sjoerg     CurrentOffset += sizeof(coff_resource_data_entry);
9527330f729Sjoerg     CurrentRelativeOffset += sizeof(coff_resource_data_entry);
9537330f729Sjoerg   }
9547330f729Sjoerg }
9557330f729Sjoerg 
writeDirectoryStringTable()9567330f729Sjoerg void WindowsResourceCOFFWriter::writeDirectoryStringTable() {
9577330f729Sjoerg   // Now write the directory string table for .rsrc$01
9587330f729Sjoerg   uint32_t TotalStringTableSize = 0;
9597330f729Sjoerg   for (auto &String : StringTable) {
9607330f729Sjoerg     uint16_t Length = String.size();
9617330f729Sjoerg     support::endian::write16le(BufferStart + CurrentOffset, Length);
9627330f729Sjoerg     CurrentOffset += sizeof(uint16_t);
9637330f729Sjoerg     auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset);
9647330f729Sjoerg     llvm::copy(String, Start);
9657330f729Sjoerg     CurrentOffset += Length * sizeof(UTF16);
9667330f729Sjoerg     TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t);
9677330f729Sjoerg   }
9687330f729Sjoerg   CurrentOffset +=
9697330f729Sjoerg       alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize;
9707330f729Sjoerg }
9717330f729Sjoerg 
writeFirstSectionRelocations()9727330f729Sjoerg void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {
9737330f729Sjoerg 
9747330f729Sjoerg   // Now write the relocations for .rsrc$01
9757330f729Sjoerg   // Five symbols already in table before we start, @feat.00 and 2 for each
9767330f729Sjoerg   // .rsrc section.
9777330f729Sjoerg   uint32_t NextSymbolIndex = 5;
9787330f729Sjoerg   for (unsigned i = 0; i < Data.size(); i++) {
9797330f729Sjoerg     auto *Reloc =
9807330f729Sjoerg         reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset);
9817330f729Sjoerg     Reloc->VirtualAddress = RelocationAddresses[i];
9827330f729Sjoerg     Reloc->SymbolTableIndex = NextSymbolIndex++;
9837330f729Sjoerg     switch (MachineType) {
9847330f729Sjoerg     case COFF::IMAGE_FILE_MACHINE_ARMNT:
9857330f729Sjoerg       Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB;
9867330f729Sjoerg       break;
9877330f729Sjoerg     case COFF::IMAGE_FILE_MACHINE_AMD64:
9887330f729Sjoerg       Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB;
9897330f729Sjoerg       break;
9907330f729Sjoerg     case COFF::IMAGE_FILE_MACHINE_I386:
9917330f729Sjoerg       Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB;
9927330f729Sjoerg       break;
9937330f729Sjoerg     case COFF::IMAGE_FILE_MACHINE_ARM64:
9947330f729Sjoerg       Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB;
9957330f729Sjoerg       break;
9967330f729Sjoerg     default:
9977330f729Sjoerg       llvm_unreachable("unknown machine type");
9987330f729Sjoerg     }
9997330f729Sjoerg     CurrentOffset += sizeof(coff_relocation);
10007330f729Sjoerg   }
10017330f729Sjoerg }
10027330f729Sjoerg 
10037330f729Sjoerg Expected<std::unique_ptr<MemoryBuffer>>
writeWindowsResourceCOFF(COFF::MachineTypes MachineType,const WindowsResourceParser & Parser,uint32_t TimeDateStamp)10047330f729Sjoerg writeWindowsResourceCOFF(COFF::MachineTypes MachineType,
10057330f729Sjoerg                          const WindowsResourceParser &Parser,
10067330f729Sjoerg                          uint32_t TimeDateStamp) {
10077330f729Sjoerg   Error E = Error::success();
10087330f729Sjoerg   WindowsResourceCOFFWriter Writer(MachineType, Parser, E);
10097330f729Sjoerg   if (E)
10107330f729Sjoerg     return std::move(E);
10117330f729Sjoerg   return Writer.write(TimeDateStamp);
10127330f729Sjoerg }
10137330f729Sjoerg 
10147330f729Sjoerg } // namespace object
10157330f729Sjoerg } // namespace llvm
1016