Path: blob/main/contrib/llvm-project/llvm/lib/Object/WindowsResource.cpp
35232 views
//===-- WindowsResource.cpp -------------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the .res file class.9//10//===----------------------------------------------------------------------===//1112#include "llvm/Object/WindowsResource.h"13#include "llvm/Object/COFF.h"14#include "llvm/Object/WindowsMachineFlag.h"15#include "llvm/Support/FormatVariadic.h"16#include "llvm/Support/MathExtras.h"17#include "llvm/Support/ScopedPrinter.h"18#include <ctime>19#include <queue>2021using namespace llvm;22using namespace object;2324namespace llvm {25namespace object {2627#define RETURN_IF_ERROR(X) \28if (auto EC = X) \29return EC;3031#define UNWRAP_REF_OR_RETURN(Name, Expr) \32auto Name##OrErr = Expr; \33if (!Name##OrErr) \34return Name##OrErr.takeError(); \35const auto &Name = *Name##OrErr;3637#define UNWRAP_OR_RETURN(Name, Expr) \38auto Name##OrErr = Expr; \39if (!Name##OrErr) \40return Name##OrErr.takeError(); \41auto Name = *Name##OrErr;4243const uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t);4445// COFF files seem to be inconsistent with alignment between sections, just use46// 8-byte because it makes everyone happy.47const uint32_t SECTION_ALIGNMENT = sizeof(uint64_t);4849WindowsResource::WindowsResource(MemoryBufferRef Source)50: Binary(Binary::ID_WinRes, Source) {51size_t LeadingSize = WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE;52BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize),53llvm::endianness::little);54}5556// static57Expected<std::unique_ptr<WindowsResource>>58WindowsResource::createWindowsResource(MemoryBufferRef Source) {59if (Source.getBufferSize() < WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE)60return make_error<GenericBinaryError>(61Source.getBufferIdentifier() + ": too small to be a resource file",62object_error::invalid_file_type);63std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source));64return std::move(Ret);65}6667Expected<ResourceEntryRef> WindowsResource::getHeadEntry() {68if (BBS.getLength() < sizeof(WinResHeaderPrefix) + sizeof(WinResHeaderSuffix))69return make_error<EmptyResError>(getFileName() + " contains no entries",70object_error::unexpected_eof);71return ResourceEntryRef::create(BinaryStreamRef(BBS), this);72}7374ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref,75const WindowsResource *Owner)76: Reader(Ref), Owner(Owner) {}7778Expected<ResourceEntryRef>79ResourceEntryRef::create(BinaryStreamRef BSR, const WindowsResource *Owner) {80auto Ref = ResourceEntryRef(BSR, Owner);81if (auto E = Ref.loadNext())82return E;83return Ref;84}8586Error ResourceEntryRef::moveNext(bool &End) {87// Reached end of all the entries.88if (Reader.bytesRemaining() == 0) {89End = true;90return Error::success();91}92RETURN_IF_ERROR(loadNext());9394return Error::success();95}9697static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID,98ArrayRef<UTF16> &Str, bool &IsString) {99uint16_t IDFlag;100RETURN_IF_ERROR(Reader.readInteger(IDFlag));101IsString = IDFlag != 0xffff;102103if (IsString) {104Reader.setOffset(105Reader.getOffset() -106sizeof(uint16_t)); // Re-read the bytes which we used to check the flag.107RETURN_IF_ERROR(Reader.readWideString(Str));108} else109RETURN_IF_ERROR(Reader.readInteger(ID));110111return Error::success();112}113114Error ResourceEntryRef::loadNext() {115const WinResHeaderPrefix *Prefix;116RETURN_IF_ERROR(Reader.readObject(Prefix));117118if (Prefix->HeaderSize < MIN_HEADER_SIZE)119return make_error<GenericBinaryError>(Owner->getFileName() +120": header size too small",121object_error::parse_failed);122123RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType));124125RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName));126127RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_HEADER_ALIGNMENT));128129RETURN_IF_ERROR(Reader.readObject(Suffix));130131RETURN_IF_ERROR(Reader.readArray(Data, Prefix->DataSize));132133RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_DATA_ALIGNMENT));134135return Error::success();136}137138WindowsResourceParser::WindowsResourceParser(bool MinGW)139: Root(false), MinGW(MinGW) {}140141void printResourceTypeName(uint16_t TypeID, raw_ostream &OS) {142switch (TypeID) {143case 1: OS << "CURSOR (ID 1)"; break;144case 2: OS << "BITMAP (ID 2)"; break;145case 3: OS << "ICON (ID 3)"; break;146case 4: OS << "MENU (ID 4)"; break;147case 5: OS << "DIALOG (ID 5)"; break;148case 6: OS << "STRINGTABLE (ID 6)"; break;149case 7: OS << "FONTDIR (ID 7)"; break;150case 8: OS << "FONT (ID 8)"; break;151case 9: OS << "ACCELERATOR (ID 9)"; break;152case 10: OS << "RCDATA (ID 10)"; break;153case 11: OS << "MESSAGETABLE (ID 11)"; break;154case 12: OS << "GROUP_CURSOR (ID 12)"; break;155case 14: OS << "GROUP_ICON (ID 14)"; break;156case 16: OS << "VERSIONINFO (ID 16)"; break;157case 17: OS << "DLGINCLUDE (ID 17)"; break;158case 19: OS << "PLUGPLAY (ID 19)"; break;159case 20: OS << "VXD (ID 20)"; break;160case 21: OS << "ANICURSOR (ID 21)"; break;161case 22: OS << "ANIICON (ID 22)"; break;162case 23: OS << "HTML (ID 23)"; break;163case 24: OS << "MANIFEST (ID 24)"; break;164default: OS << "ID " << TypeID; break;165}166}167168static bool convertUTF16LEToUTF8String(ArrayRef<UTF16> Src, std::string &Out) {169if (!sys::IsBigEndianHost)170return convertUTF16ToUTF8String(Src, Out);171172std::vector<UTF16> EndianCorrectedSrc;173EndianCorrectedSrc.resize(Src.size() + 1);174llvm::copy(Src, EndianCorrectedSrc.begin() + 1);175EndianCorrectedSrc[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED;176return convertUTF16ToUTF8String(ArrayRef(EndianCorrectedSrc), Out);177}178179static std::string makeDuplicateResourceError(180const ResourceEntryRef &Entry, StringRef File1, StringRef File2) {181std::string Ret;182raw_string_ostream OS(Ret);183184OS << "duplicate resource:";185186OS << " type ";187if (Entry.checkTypeString()) {188std::string UTF8;189if (!convertUTF16LEToUTF8String(Entry.getTypeString(), UTF8))190UTF8 = "(failed conversion from UTF16)";191OS << '\"' << UTF8 << '\"';192} else193printResourceTypeName(Entry.getTypeID(), OS);194195OS << "/name ";196if (Entry.checkNameString()) {197std::string UTF8;198if (!convertUTF16LEToUTF8String(Entry.getNameString(), UTF8))199UTF8 = "(failed conversion from UTF16)";200OS << '\"' << UTF8 << '\"';201} else {202OS << "ID " << Entry.getNameID();203}204205OS << "/language " << Entry.getLanguage() << ", in " << File1 << " and in "206<< File2;207208return OS.str();209}210211static void printStringOrID(const WindowsResourceParser::StringOrID &S,212raw_string_ostream &OS, bool IsType, bool IsID) {213if (S.IsString) {214std::string UTF8;215if (!convertUTF16LEToUTF8String(S.String, UTF8))216UTF8 = "(failed conversion from UTF16)";217OS << '\"' << UTF8 << '\"';218} else if (IsType)219printResourceTypeName(S.ID, OS);220else if (IsID)221OS << "ID " << S.ID;222else223OS << S.ID;224}225226static std::string makeDuplicateResourceError(227const std::vector<WindowsResourceParser::StringOrID> &Context,228StringRef File1, StringRef File2) {229std::string Ret;230raw_string_ostream OS(Ret);231232OS << "duplicate resource:";233234if (Context.size() >= 1) {235OS << " type ";236printStringOrID(Context[0], OS, /* IsType */ true, /* IsID */ true);237}238239if (Context.size() >= 2) {240OS << "/name ";241printStringOrID(Context[1], OS, /* IsType */ false, /* IsID */ true);242}243244if (Context.size() >= 3) {245OS << "/language ";246printStringOrID(Context[2], OS, /* IsType */ false, /* IsID */ false);247}248OS << ", in " << File1 << " and in " << File2;249250return OS.str();251}252253// MinGW specific. Remove default manifests (with language zero) if there are254// other manifests present, and report an error if there are more than one255// manifest with a non-zero language code.256// GCC has the concept of a default manifest resource object, which gets257// linked in implicitly if present. This default manifest has got language258// id zero, and should be dropped silently if there's another manifest present.259// If the user resources surprisignly had a manifest with language id zero,260// we should also ignore the duplicate default manifest.261void WindowsResourceParser::cleanUpManifests(262std::vector<std::string> &Duplicates) {263auto TypeIt = Root.IDChildren.find(/* RT_MANIFEST */ 24);264if (TypeIt == Root.IDChildren.end())265return;266267TreeNode *TypeNode = TypeIt->second.get();268auto NameIt =269TypeNode->IDChildren.find(/* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1);270if (NameIt == TypeNode->IDChildren.end())271return;272273TreeNode *NameNode = NameIt->second.get();274if (NameNode->IDChildren.size() <= 1)275return; // None or one manifest present, all good.276277// If we have more than one manifest, drop the language zero one if present,278// and check again.279auto LangZeroIt = NameNode->IDChildren.find(0);280if (LangZeroIt != NameNode->IDChildren.end() &&281LangZeroIt->second->IsDataNode) {282uint32_t RemovedIndex = LangZeroIt->second->DataIndex;283NameNode->IDChildren.erase(LangZeroIt);284Data.erase(Data.begin() + RemovedIndex);285Root.shiftDataIndexDown(RemovedIndex);286287// If we're now down to one manifest, all is good.288if (NameNode->IDChildren.size() <= 1)289return;290}291292// More than one non-language-zero manifest293auto FirstIt = NameNode->IDChildren.begin();294uint32_t FirstLang = FirstIt->first;295TreeNode *FirstNode = FirstIt->second.get();296auto LastIt = NameNode->IDChildren.rbegin();297uint32_t LastLang = LastIt->first;298TreeNode *LastNode = LastIt->second.get();299Duplicates.push_back(300("duplicate non-default manifests with languages " + Twine(FirstLang) +301" in " + InputFilenames[FirstNode->Origin] + " and " + Twine(LastLang) +302" in " + InputFilenames[LastNode->Origin])303.str());304}305306// Ignore duplicates of manifests with language zero (the default manifest),307// in case the user has provided a manifest with that language id. See308// the function comment above for context. Only returns true if MinGW is set309// to true.310bool WindowsResourceParser::shouldIgnoreDuplicate(311const ResourceEntryRef &Entry) const {312return MinGW && !Entry.checkTypeString() &&313Entry.getTypeID() == /* RT_MANIFEST */ 24 &&314!Entry.checkNameString() &&315Entry.getNameID() == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&316Entry.getLanguage() == 0;317}318319bool WindowsResourceParser::shouldIgnoreDuplicate(320const std::vector<StringOrID> &Context) const {321return MinGW && Context.size() == 3 && !Context[0].IsString &&322Context[0].ID == /* RT_MANIFEST */ 24 && !Context[1].IsString &&323Context[1].ID == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&324!Context[2].IsString && Context[2].ID == 0;325}326327Error WindowsResourceParser::parse(WindowsResource *WR,328std::vector<std::string> &Duplicates) {329auto EntryOrErr = WR->getHeadEntry();330if (!EntryOrErr) {331auto E = EntryOrErr.takeError();332if (E.isA<EmptyResError>()) {333// Check if the .res file contains no entries. In this case we don't have334// to throw an error but can rather just return without parsing anything.335// This applies for files which have a valid PE header magic and the336// mandatory empty null resource entry. Files which do not fit this337// criteria would have already been filtered out by338// WindowsResource::createWindowsResource().339consumeError(std::move(E));340return Error::success();341}342return E;343}344345ResourceEntryRef Entry = EntryOrErr.get();346uint32_t Origin = InputFilenames.size();347InputFilenames.push_back(std::string(WR->getFileName()));348bool End = false;349while (!End) {350351TreeNode *Node;352bool IsNewNode = Root.addEntry(Entry, Origin, Data, StringTable, Node);353if (!IsNewNode) {354if (!shouldIgnoreDuplicate(Entry))355Duplicates.push_back(makeDuplicateResourceError(356Entry, InputFilenames[Node->Origin], WR->getFileName()));357}358359RETURN_IF_ERROR(Entry.moveNext(End));360}361362return Error::success();363}364365Error WindowsResourceParser::parse(ResourceSectionRef &RSR, StringRef Filename,366std::vector<std::string> &Duplicates) {367UNWRAP_REF_OR_RETURN(BaseTable, RSR.getBaseTable());368uint32_t Origin = InputFilenames.size();369InputFilenames.push_back(std::string(Filename));370std::vector<StringOrID> Context;371return addChildren(Root, RSR, BaseTable, Origin, Context, Duplicates);372}373374void WindowsResourceParser::printTree(raw_ostream &OS) const {375ScopedPrinter Writer(OS);376Root.print(Writer, "Resource Tree");377}378379bool WindowsResourceParser::TreeNode::addEntry(380const ResourceEntryRef &Entry, uint32_t Origin,381std::vector<std::vector<uint8_t>> &Data,382std::vector<std::vector<UTF16>> &StringTable, TreeNode *&Result) {383TreeNode &TypeNode = addTypeNode(Entry, StringTable);384TreeNode &NameNode = TypeNode.addNameNode(Entry, StringTable);385return NameNode.addLanguageNode(Entry, Origin, Data, Result);386}387388Error WindowsResourceParser::addChildren(TreeNode &Node,389ResourceSectionRef &RSR,390const coff_resource_dir_table &Table,391uint32_t Origin,392std::vector<StringOrID> &Context,393std::vector<std::string> &Duplicates) {394395for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries;396i++) {397UNWRAP_REF_OR_RETURN(Entry, RSR.getTableEntry(Table, i));398TreeNode *Child;399400if (Entry.Offset.isSubDir()) {401402// Create a new subdirectory and recurse403if (i < Table.NumberOfNameEntries) {404UNWRAP_OR_RETURN(NameString, RSR.getEntryNameString(Entry));405Child = &Node.addNameChild(NameString, StringTable);406Context.push_back(StringOrID(NameString));407} else {408Child = &Node.addIDChild(Entry.Identifier.ID);409Context.push_back(StringOrID(Entry.Identifier.ID));410}411412UNWRAP_REF_OR_RETURN(NextTable, RSR.getEntrySubDir(Entry));413Error E =414addChildren(*Child, RSR, NextTable, Origin, Context, Duplicates);415if (E)416return E;417Context.pop_back();418419} else {420421// Data leaves are supposed to have a numeric ID as identifier (language).422if (Table.NumberOfNameEntries > 0)423return createStringError(object_error::parse_failed,424"unexpected string key for data object");425426// Try adding a data leaf427UNWRAP_REF_OR_RETURN(DataEntry, RSR.getEntryData(Entry));428TreeNode *Child;429Context.push_back(StringOrID(Entry.Identifier.ID));430bool Added = Node.addDataChild(Entry.Identifier.ID, Table.MajorVersion,431Table.MinorVersion, Table.Characteristics,432Origin, Data.size(), Child);433if (Added) {434UNWRAP_OR_RETURN(Contents, RSR.getContents(DataEntry));435Data.push_back(ArrayRef<uint8_t>(436reinterpret_cast<const uint8_t *>(Contents.data()),437Contents.size()));438} else {439if (!shouldIgnoreDuplicate(Context))440Duplicates.push_back(makeDuplicateResourceError(441Context, InputFilenames[Child->Origin], InputFilenames.back()));442}443Context.pop_back();444445}446}447return Error::success();448}449450WindowsResourceParser::TreeNode::TreeNode(uint32_t StringIndex)451: StringIndex(StringIndex) {}452453WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion,454uint16_t MinorVersion,455uint32_t Characteristics,456uint32_t Origin, uint32_t DataIndex)457: IsDataNode(true), DataIndex(DataIndex), MajorVersion(MajorVersion),458MinorVersion(MinorVersion), Characteristics(Characteristics),459Origin(Origin) {}460461std::unique_ptr<WindowsResourceParser::TreeNode>462WindowsResourceParser::TreeNode::createStringNode(uint32_t Index) {463return std::unique_ptr<TreeNode>(new TreeNode(Index));464}465466std::unique_ptr<WindowsResourceParser::TreeNode>467WindowsResourceParser::TreeNode::createIDNode() {468return std::unique_ptr<TreeNode>(new TreeNode(0));469}470471std::unique_ptr<WindowsResourceParser::TreeNode>472WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion,473uint16_t MinorVersion,474uint32_t Characteristics,475uint32_t Origin,476uint32_t DataIndex) {477return std::unique_ptr<TreeNode>(new TreeNode(478MajorVersion, MinorVersion, Characteristics, Origin, DataIndex));479}480481WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addTypeNode(482const ResourceEntryRef &Entry,483std::vector<std::vector<UTF16>> &StringTable) {484if (Entry.checkTypeString())485return addNameChild(Entry.getTypeString(), StringTable);486else487return addIDChild(Entry.getTypeID());488}489490WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameNode(491const ResourceEntryRef &Entry,492std::vector<std::vector<UTF16>> &StringTable) {493if (Entry.checkNameString())494return addNameChild(Entry.getNameString(), StringTable);495else496return addIDChild(Entry.getNameID());497}498499bool WindowsResourceParser::TreeNode::addLanguageNode(500const ResourceEntryRef &Entry, uint32_t Origin,501std::vector<std::vector<uint8_t>> &Data, TreeNode *&Result) {502bool Added = addDataChild(Entry.getLanguage(), Entry.getMajorVersion(),503Entry.getMinorVersion(), Entry.getCharacteristics(),504Origin, Data.size(), Result);505if (Added)506Data.push_back(Entry.getData());507return Added;508}509510bool WindowsResourceParser::TreeNode::addDataChild(511uint32_t ID, uint16_t MajorVersion, uint16_t MinorVersion,512uint32_t Characteristics, uint32_t Origin, uint32_t DataIndex,513TreeNode *&Result) {514auto NewChild = createDataNode(MajorVersion, MinorVersion, Characteristics,515Origin, DataIndex);516auto ElementInserted = IDChildren.emplace(ID, std::move(NewChild));517Result = ElementInserted.first->second.get();518return ElementInserted.second;519}520521WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addIDChild(522uint32_t ID) {523auto Child = IDChildren.find(ID);524if (Child == IDChildren.end()) {525auto NewChild = createIDNode();526WindowsResourceParser::TreeNode &Node = *NewChild;527IDChildren.emplace(ID, std::move(NewChild));528return Node;529} else530return *(Child->second);531}532533WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameChild(534ArrayRef<UTF16> NameRef, std::vector<std::vector<UTF16>> &StringTable) {535std::string NameString;536convertUTF16LEToUTF8String(NameRef, NameString);537538auto Child = StringChildren.find(NameString);539if (Child == StringChildren.end()) {540auto NewChild = createStringNode(StringTable.size());541StringTable.push_back(NameRef);542WindowsResourceParser::TreeNode &Node = *NewChild;543StringChildren.emplace(NameString, std::move(NewChild));544return Node;545} else546return *(Child->second);547}548549void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer,550StringRef Name) const {551ListScope NodeScope(Writer, Name);552for (auto const &Child : StringChildren) {553Child.second->print(Writer, Child.first);554}555for (auto const &Child : IDChildren) {556Child.second->print(Writer, to_string(Child.first));557}558}559560// This function returns the size of the entire resource tree, including561// directory tables, directory entries, and data entries. It does not include562// the directory strings or the relocations of the .rsrc section.563uint32_t WindowsResourceParser::TreeNode::getTreeSize() const {564uint32_t Size = (IDChildren.size() + StringChildren.size()) *565sizeof(coff_resource_dir_entry);566567// Reached a node pointing to a data entry.568if (IsDataNode) {569Size += sizeof(coff_resource_data_entry);570return Size;571}572573// If the node does not point to data, it must have a directory table pointing574// to other nodes.575Size += sizeof(coff_resource_dir_table);576577for (auto const &Child : StringChildren) {578Size += Child.second->getTreeSize();579}580for (auto const &Child : IDChildren) {581Size += Child.second->getTreeSize();582}583return Size;584}585586// Shift DataIndex of all data children with an Index greater or equal to the587// given one, to fill a gap from removing an entry from the Data vector.588void WindowsResourceParser::TreeNode::shiftDataIndexDown(uint32_t Index) {589if (IsDataNode && DataIndex >= Index) {590DataIndex--;591} else {592for (auto &Child : IDChildren)593Child.second->shiftDataIndexDown(Index);594for (auto &Child : StringChildren)595Child.second->shiftDataIndexDown(Index);596}597}598599class WindowsResourceCOFFWriter {600public:601WindowsResourceCOFFWriter(COFF::MachineTypes MachineType,602const WindowsResourceParser &Parser, Error &E);603std::unique_ptr<MemoryBuffer> write(uint32_t TimeDateStamp);604605private:606void performFileLayout();607void performSectionOneLayout();608void performSectionTwoLayout();609void writeCOFFHeader(uint32_t TimeDateStamp);610void writeFirstSectionHeader();611void writeSecondSectionHeader();612void writeFirstSection();613void writeSecondSection();614void writeSymbolTable();615void writeStringTable();616void writeDirectoryTree();617void writeDirectoryStringTable();618void writeFirstSectionRelocations();619std::unique_ptr<WritableMemoryBuffer> OutputBuffer;620char *BufferStart;621uint64_t CurrentOffset = 0;622COFF::MachineTypes MachineType;623const WindowsResourceParser::TreeNode &Resources;624const ArrayRef<std::vector<uint8_t>> Data;625uint64_t FileSize;626uint32_t SymbolTableOffset;627uint32_t SectionOneSize;628uint32_t SectionOneOffset;629uint32_t SectionOneRelocations;630uint32_t SectionTwoSize;631uint32_t SectionTwoOffset;632const ArrayRef<std::vector<UTF16>> StringTable;633std::vector<uint32_t> StringTableOffsets;634std::vector<uint32_t> DataOffsets;635std::vector<uint32_t> RelocationAddresses;636};637638WindowsResourceCOFFWriter::WindowsResourceCOFFWriter(639COFF::MachineTypes MachineType, const WindowsResourceParser &Parser,640Error &E)641: MachineType(MachineType), Resources(Parser.getTree()),642Data(Parser.getData()), StringTable(Parser.getStringTable()) {643performFileLayout();644645OutputBuffer = WritableMemoryBuffer::getNewMemBuffer(646FileSize, "internal .obj file created from .res files");647}648649void WindowsResourceCOFFWriter::performFileLayout() {650// Add size of COFF header.651FileSize = COFF::Header16Size;652653// one .rsrc section header for directory tree, another for resource data.654FileSize += 2 * COFF::SectionSize;655656performSectionOneLayout();657performSectionTwoLayout();658659// We have reached the address of the symbol table.660SymbolTableOffset = FileSize;661662FileSize += COFF::Symbol16Size; // size of the @feat.00 symbol.663FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section.664FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource.665FileSize += 4; // four null bytes for the string table.666}667668void WindowsResourceCOFFWriter::performSectionOneLayout() {669SectionOneOffset = FileSize;670671SectionOneSize = Resources.getTreeSize();672uint32_t CurrentStringOffset = SectionOneSize;673uint32_t TotalStringTableSize = 0;674for (auto const &String : StringTable) {675StringTableOffsets.push_back(CurrentStringOffset);676uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t);677CurrentStringOffset += StringSize;678TotalStringTableSize += StringSize;679}680SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t));681682// account for the relocations of section one.683SectionOneRelocations = FileSize + SectionOneSize;684FileSize += SectionOneSize;685FileSize +=686Data.size() * COFF::RelocationSize; // one relocation for each resource.687FileSize = alignTo(FileSize, SECTION_ALIGNMENT);688}689690void WindowsResourceCOFFWriter::performSectionTwoLayout() {691// add size of .rsrc$2 section, which contains all resource data on 8-byte692// alignment.693SectionTwoOffset = FileSize;694SectionTwoSize = 0;695for (auto const &Entry : Data) {696DataOffsets.push_back(SectionTwoSize);697SectionTwoSize += alignTo(Entry.size(), sizeof(uint64_t));698}699FileSize += SectionTwoSize;700FileSize = alignTo(FileSize, SECTION_ALIGNMENT);701}702703std::unique_ptr<MemoryBuffer>704WindowsResourceCOFFWriter::write(uint32_t TimeDateStamp) {705BufferStart = OutputBuffer->getBufferStart();706707writeCOFFHeader(TimeDateStamp);708writeFirstSectionHeader();709writeSecondSectionHeader();710writeFirstSection();711writeSecondSection();712writeSymbolTable();713writeStringTable();714715return std::move(OutputBuffer);716}717718// According to COFF specification, if the Src has a size equal to Dest,719// it's okay to *not* copy the trailing zero.720static void coffnamecpy(char (&Dest)[COFF::NameSize], StringRef Src) {721assert(Src.size() <= COFF::NameSize &&722"Src is larger than COFF::NameSize");723assert((Src.size() == COFF::NameSize || Dest[Src.size()] == '\0') &&724"Dest not zeroed upon initialization");725memcpy(Dest, Src.data(), Src.size());726}727728void WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp) {729// Write the COFF header.730auto *Header = reinterpret_cast<coff_file_header *>(BufferStart);731Header->Machine = MachineType;732Header->NumberOfSections = 2;733Header->TimeDateStamp = TimeDateStamp;734Header->PointerToSymbolTable = SymbolTableOffset;735// One symbol for every resource plus 2 for each section and 1 for @feat.00736Header->NumberOfSymbols = Data.size() + 5;737Header->SizeOfOptionalHeader = 0;738// cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it.739Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE;740}741742void WindowsResourceCOFFWriter::writeFirstSectionHeader() {743// Write the first section header.744CurrentOffset += sizeof(coff_file_header);745auto *SectionOneHeader =746reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);747coffnamecpy(SectionOneHeader->Name, ".rsrc$01");748SectionOneHeader->VirtualSize = 0;749SectionOneHeader->VirtualAddress = 0;750SectionOneHeader->SizeOfRawData = SectionOneSize;751SectionOneHeader->PointerToRawData = SectionOneOffset;752SectionOneHeader->PointerToRelocations = SectionOneRelocations;753SectionOneHeader->PointerToLinenumbers = 0;754SectionOneHeader->NumberOfRelocations = Data.size();755SectionOneHeader->NumberOfLinenumbers = 0;756SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;757SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;758}759760void WindowsResourceCOFFWriter::writeSecondSectionHeader() {761// Write the second section header.762CurrentOffset += sizeof(coff_section);763auto *SectionTwoHeader =764reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);765coffnamecpy(SectionTwoHeader->Name, ".rsrc$02");766SectionTwoHeader->VirtualSize = 0;767SectionTwoHeader->VirtualAddress = 0;768SectionTwoHeader->SizeOfRawData = SectionTwoSize;769SectionTwoHeader->PointerToRawData = SectionTwoOffset;770SectionTwoHeader->PointerToRelocations = 0;771SectionTwoHeader->PointerToLinenumbers = 0;772SectionTwoHeader->NumberOfRelocations = 0;773SectionTwoHeader->NumberOfLinenumbers = 0;774SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;775SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;776}777778void WindowsResourceCOFFWriter::writeFirstSection() {779// Write section one.780CurrentOffset += sizeof(coff_section);781782writeDirectoryTree();783writeDirectoryStringTable();784writeFirstSectionRelocations();785786CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);787}788789void WindowsResourceCOFFWriter::writeSecondSection() {790// Now write the .rsrc$02 section.791for (auto const &RawDataEntry : Data) {792llvm::copy(RawDataEntry, BufferStart + CurrentOffset);793CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t));794}795796CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);797}798799void WindowsResourceCOFFWriter::writeSymbolTable() {800// Now write the symbol table.801// First, the feat symbol.802auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);803coffnamecpy(Symbol->Name.ShortName, "@feat.00");804Symbol->Value = 0x11;805Symbol->SectionNumber = 0xffff;806Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;807Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;808Symbol->NumberOfAuxSymbols = 0;809CurrentOffset += sizeof(coff_symbol16);810811// Now write the .rsrc1 symbol + aux.812Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);813coffnamecpy(Symbol->Name.ShortName, ".rsrc$01");814Symbol->Value = 0;815Symbol->SectionNumber = 1;816Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;817Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;818Symbol->NumberOfAuxSymbols = 1;819CurrentOffset += sizeof(coff_symbol16);820auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +821CurrentOffset);822Aux->Length = SectionOneSize;823Aux->NumberOfRelocations = Data.size();824Aux->NumberOfLinenumbers = 0;825Aux->CheckSum = 0;826Aux->NumberLowPart = 0;827Aux->Selection = 0;828CurrentOffset += sizeof(coff_aux_section_definition);829830// Now write the .rsrc2 symbol + aux.831Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);832coffnamecpy(Symbol->Name.ShortName, ".rsrc$02");833Symbol->Value = 0;834Symbol->SectionNumber = 2;835Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;836Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;837Symbol->NumberOfAuxSymbols = 1;838CurrentOffset += sizeof(coff_symbol16);839Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +840CurrentOffset);841Aux->Length = SectionTwoSize;842Aux->NumberOfRelocations = 0;843Aux->NumberOfLinenumbers = 0;844Aux->CheckSum = 0;845Aux->NumberLowPart = 0;846Aux->Selection = 0;847CurrentOffset += sizeof(coff_aux_section_definition);848849// Now write a symbol for each relocation.850for (unsigned i = 0; i < Data.size(); i++) {851auto RelocationName = formatv("$R{0:X-6}", i & 0xffffff).sstr<COFF::NameSize>();852Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);853coffnamecpy(Symbol->Name.ShortName, RelocationName);854Symbol->Value = DataOffsets[i];855Symbol->SectionNumber = 2;856Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;857Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;858Symbol->NumberOfAuxSymbols = 0;859CurrentOffset += sizeof(coff_symbol16);860}861}862863void WindowsResourceCOFFWriter::writeStringTable() {864// Just 4 null bytes for the string table.865auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset);866memset(COFFStringTable, 0, 4);867}868869void WindowsResourceCOFFWriter::writeDirectoryTree() {870// Traverse parsed resource tree breadth-first and write the corresponding871// COFF objects.872std::queue<const WindowsResourceParser::TreeNode *> Queue;873Queue.push(&Resources);874uint32_t NextLevelOffset =875sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() +876Resources.getIDChildren().size()) *877sizeof(coff_resource_dir_entry);878std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder;879uint32_t CurrentRelativeOffset = 0;880881while (!Queue.empty()) {882auto CurrentNode = Queue.front();883Queue.pop();884auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart +885CurrentOffset);886Table->Characteristics = CurrentNode->getCharacteristics();887Table->TimeDateStamp = 0;888Table->MajorVersion = CurrentNode->getMajorVersion();889Table->MinorVersion = CurrentNode->getMinorVersion();890auto &IDChildren = CurrentNode->getIDChildren();891auto &StringChildren = CurrentNode->getStringChildren();892Table->NumberOfNameEntries = StringChildren.size();893Table->NumberOfIDEntries = IDChildren.size();894CurrentOffset += sizeof(coff_resource_dir_table);895CurrentRelativeOffset += sizeof(coff_resource_dir_table);896897// Write the directory entries immediately following each directory table.898for (auto const &Child : StringChildren) {899auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +900CurrentOffset);901Entry->Identifier.setNameOffset(902StringTableOffsets[Child.second->getStringIndex()]);903if (Child.second->checkIsDataNode()) {904Entry->Offset.DataEntryOffset = NextLevelOffset;905NextLevelOffset += sizeof(coff_resource_data_entry);906DataEntriesTreeOrder.push_back(Child.second.get());907} else {908Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);909NextLevelOffset += sizeof(coff_resource_dir_table) +910(Child.second->getStringChildren().size() +911Child.second->getIDChildren().size()) *912sizeof(coff_resource_dir_entry);913Queue.push(Child.second.get());914}915CurrentOffset += sizeof(coff_resource_dir_entry);916CurrentRelativeOffset += sizeof(coff_resource_dir_entry);917}918for (auto const &Child : IDChildren) {919auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +920CurrentOffset);921Entry->Identifier.ID = Child.first;922if (Child.second->checkIsDataNode()) {923Entry->Offset.DataEntryOffset = NextLevelOffset;924NextLevelOffset += sizeof(coff_resource_data_entry);925DataEntriesTreeOrder.push_back(Child.second.get());926} else {927Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);928NextLevelOffset += sizeof(coff_resource_dir_table) +929(Child.second->getStringChildren().size() +930Child.second->getIDChildren().size()) *931sizeof(coff_resource_dir_entry);932Queue.push(Child.second.get());933}934CurrentOffset += sizeof(coff_resource_dir_entry);935CurrentRelativeOffset += sizeof(coff_resource_dir_entry);936}937}938939RelocationAddresses.resize(Data.size());940// Now write all the resource data entries.941for (const auto *DataNodes : DataEntriesTreeOrder) {942auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart +943CurrentOffset);944RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset;945Entry->DataRVA = 0; // Set to zero because it is a relocation.946Entry->DataSize = Data[DataNodes->getDataIndex()].size();947Entry->Codepage = 0;948Entry->Reserved = 0;949CurrentOffset += sizeof(coff_resource_data_entry);950CurrentRelativeOffset += sizeof(coff_resource_data_entry);951}952}953954void WindowsResourceCOFFWriter::writeDirectoryStringTable() {955// Now write the directory string table for .rsrc$01956uint32_t TotalStringTableSize = 0;957for (auto &String : StringTable) {958uint16_t Length = String.size();959support::endian::write16le(BufferStart + CurrentOffset, Length);960CurrentOffset += sizeof(uint16_t);961auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset);962llvm::copy(String, Start);963CurrentOffset += Length * sizeof(UTF16);964TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t);965}966CurrentOffset +=967alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize;968}969970void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {971972// Now write the relocations for .rsrc$01973// Five symbols already in table before we start, @feat.00 and 2 for each974// .rsrc section.975uint32_t NextSymbolIndex = 5;976for (unsigned i = 0; i < Data.size(); i++) {977auto *Reloc =978reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset);979Reloc->VirtualAddress = RelocationAddresses[i];980Reloc->SymbolTableIndex = NextSymbolIndex++;981switch (getMachineArchType(MachineType)) {982case Triple::thumb:983Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB;984break;985case Triple::x86_64:986Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB;987break;988case Triple::x86:989Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB;990break;991case Triple::aarch64:992Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB;993break;994default:995llvm_unreachable("unknown machine type");996}997CurrentOffset += sizeof(coff_relocation);998}999}10001001Expected<std::unique_ptr<MemoryBuffer>>1002writeWindowsResourceCOFF(COFF::MachineTypes MachineType,1003const WindowsResourceParser &Parser,1004uint32_t TimeDateStamp) {1005Error E = Error::success();1006WindowsResourceCOFFWriter Writer(MachineType, Parser, E);1007if (E)1008return E;1009return Writer.write(TimeDateStamp);1010}10111012} // namespace object1013} // namespace llvm101410151016