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