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