brintos

brintos / llvm-project-archived public Read only

0
0
Text · 37.5 KiB · 306e8ec Raw
1015 lines · cpp
1//===-- WindowsResource.cpp -------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the .res file class.10//11//===----------------------------------------------------------------------===//12 13#include "llvm/Object/WindowsResource.h"14#include "llvm/Object/COFF.h"15#include "llvm/Object/WindowsMachineFlag.h"16#include "llvm/Support/FormatVariadic.h"17#include "llvm/Support/MathExtras.h"18#include "llvm/Support/ScopedPrinter.h"19#include <ctime>20#include <queue>21 22using namespace llvm;23using namespace object;24 25namespace llvm {26namespace object {27 28#define RETURN_IF_ERROR(X)                                                     \29  if (auto EC = X)                                                             \30    return EC;31 32#define UNWRAP_REF_OR_RETURN(Name, Expr)                                       \33  auto Name##OrErr = Expr;                                                     \34  if (!Name##OrErr)                                                            \35    return Name##OrErr.takeError();                                            \36  const auto &Name = *Name##OrErr;37 38#define UNWRAP_OR_RETURN(Name, Expr)                                           \39  auto Name##OrErr = Expr;                                                     \40  if (!Name##OrErr)                                                            \41    return Name##OrErr.takeError();                                            \42  auto Name = *Name##OrErr;43 44const uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t);45 46// COFF files seem to be inconsistent with alignment between sections, just use47// 8-byte because it makes everyone happy.48const uint32_t SECTION_ALIGNMENT = sizeof(uint64_t);49 50WindowsResource::WindowsResource(MemoryBufferRef Source)51    : Binary(Binary::ID_WinRes, Source) {52  size_t LeadingSize = WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE;53  BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize),54                         llvm::endianness::little);55}56 57// static58Expected<std::unique_ptr<WindowsResource>>59WindowsResource::createWindowsResource(MemoryBufferRef Source) {60  if (Source.getBufferSize() < WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE)61    return make_error<GenericBinaryError>(62        Source.getBufferIdentifier() + ": too small to be a resource file",63        object_error::invalid_file_type);64  std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source));65  return std::move(Ret);66}67 68Expected<ResourceEntryRef> WindowsResource::getHeadEntry() {69  if (BBS.getLength() < sizeof(WinResHeaderPrefix) + sizeof(WinResHeaderSuffix))70    return make_error<EmptyResError>(getFileName() + " contains no entries",71                                     object_error::unexpected_eof);72  return ResourceEntryRef::create(BinaryStreamRef(BBS), this);73}74 75ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref,76                                   const WindowsResource *Owner)77    : Reader(Ref), Owner(Owner) {}78 79Expected<ResourceEntryRef>80ResourceEntryRef::create(BinaryStreamRef BSR, const WindowsResource *Owner) {81  auto Ref = ResourceEntryRef(BSR, Owner);82  if (auto E = Ref.loadNext())83    return E;84  return Ref;85}86 87Error ResourceEntryRef::moveNext(bool &End) {88  // Reached end of all the entries.89  if (Reader.bytesRemaining() == 0) {90    End = true;91    return Error::success();92  }93  RETURN_IF_ERROR(loadNext());94 95  return Error::success();96}97 98static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID,99                            ArrayRef<UTF16> &Str, bool &IsString) {100  uint16_t IDFlag;101  RETURN_IF_ERROR(Reader.readInteger(IDFlag));102  IsString = IDFlag != 0xffff;103 104  if (IsString) {105    Reader.setOffset(106        Reader.getOffset() -107        sizeof(uint16_t)); // Re-read the bytes which we used to check the flag.108    RETURN_IF_ERROR(Reader.readWideString(Str));109  } else110    RETURN_IF_ERROR(Reader.readInteger(ID));111 112  return Error::success();113}114 115Error ResourceEntryRef::loadNext() {116  const WinResHeaderPrefix *Prefix;117  RETURN_IF_ERROR(Reader.readObject(Prefix));118 119  if (Prefix->HeaderSize < MIN_HEADER_SIZE)120    return make_error<GenericBinaryError>(Owner->getFileName() +121                                              ": header size too small",122                                          object_error::parse_failed);123 124  RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType));125 126  RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName));127 128  RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_HEADER_ALIGNMENT));129 130  RETURN_IF_ERROR(Reader.readObject(Suffix));131 132  RETURN_IF_ERROR(Reader.readArray(Data, Prefix->DataSize));133 134  RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_DATA_ALIGNMENT));135 136  return Error::success();137}138 139WindowsResourceParser::WindowsResourceParser(bool MinGW)140    : Root(false), MinGW(MinGW) {}141 142void printResourceTypeName(uint16_t TypeID, raw_ostream &OS) {143  switch (TypeID) {144  case  1: OS << "CURSOR (ID 1)"; break;145  case  2: OS << "BITMAP (ID 2)"; break;146  case  3: OS << "ICON (ID 3)"; break;147  case  4: OS << "MENU (ID 4)"; break;148  case  5: OS << "DIALOG (ID 5)"; break;149  case  6: OS << "STRINGTABLE (ID 6)"; break;150  case  7: OS << "FONTDIR (ID 7)"; break;151  case  8: OS << "FONT (ID 8)"; break;152  case  9: OS << "ACCELERATOR (ID 9)"; break;153  case 10: OS << "RCDATA (ID 10)"; break;154  case 11: OS << "MESSAGETABLE (ID 11)"; break;155  case 12: OS << "GROUP_CURSOR (ID 12)"; break;156  case 14: OS << "GROUP_ICON (ID 14)"; break;157  case 16: OS << "VERSIONINFO (ID 16)"; break;158  case 17: OS << "DLGINCLUDE (ID 17)"; break;159  case 19: OS << "PLUGPLAY (ID 19)"; break;160  case 20: OS << "VXD (ID 20)"; break;161  case 21: OS << "ANICURSOR (ID 21)"; break;162  case 22: OS << "ANIICON (ID 22)"; break;163  case 23: OS << "HTML (ID 23)"; break;164  case 24: OS << "MANIFEST (ID 24)"; break;165  default: OS << "ID " << TypeID; break;166  }167}168 169static bool convertUTF16LEToUTF8String(ArrayRef<UTF16> Src, std::string &Out) {170  if (!sys::IsBigEndianHost)171    return convertUTF16ToUTF8String(Src, Out);172 173  std::vector<UTF16> EndianCorrectedSrc;174  EndianCorrectedSrc.resize(Src.size() + 1);175  llvm::copy(Src, EndianCorrectedSrc.begin() + 1);176  EndianCorrectedSrc[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED;177  return convertUTF16ToUTF8String(ArrayRef(EndianCorrectedSrc), Out);178}179 180static std::string makeDuplicateResourceError(181    const ResourceEntryRef &Entry, StringRef File1, StringRef File2) {182  std::string Ret;183  raw_string_ostream OS(Ret);184 185  OS << "duplicate resource:";186 187  OS << " type ";188  if (Entry.checkTypeString()) {189    std::string UTF8;190    if (!convertUTF16LEToUTF8String(Entry.getTypeString(), UTF8))191      UTF8 = "(failed conversion from UTF16)";192    OS << '\"' << UTF8 << '\"';193  } else194    printResourceTypeName(Entry.getTypeID(), OS);195 196  OS << "/name ";197  if (Entry.checkNameString()) {198    std::string UTF8;199    if (!convertUTF16LEToUTF8String(Entry.getNameString(), UTF8))200      UTF8 = "(failed conversion from UTF16)";201    OS << '\"' << UTF8 << '\"';202  } else {203    OS << "ID " << Entry.getNameID();204  }205 206  OS << "/language " << Entry.getLanguage() << ", in " << File1 << " and in "207     << File2;208 209  return OS.str();210}211 212static void printStringOrID(const WindowsResourceParser::StringOrID &S,213                            raw_string_ostream &OS, bool IsType, bool IsID) {214  if (S.IsString) {215    std::string UTF8;216    if (!convertUTF16LEToUTF8String(S.String, UTF8))217      UTF8 = "(failed conversion from UTF16)";218    OS << '\"' << UTF8 << '\"';219  } else if (IsType)220    printResourceTypeName(S.ID, OS);221  else if (IsID)222    OS << "ID " << S.ID;223  else224    OS << S.ID;225}226 227static std::string makeDuplicateResourceError(228    const std::vector<WindowsResourceParser::StringOrID> &Context,229    StringRef File1, StringRef File2) {230  std::string Ret;231  raw_string_ostream OS(Ret);232 233  OS << "duplicate resource:";234 235  if (Context.size() >= 1) {236    OS << " type ";237    printStringOrID(Context[0], OS, /* IsType */ true, /* IsID */ true);238  }239 240  if (Context.size() >= 2) {241    OS << "/name ";242    printStringOrID(Context[1], OS, /* IsType */ false, /* IsID */ true);243  }244 245  if (Context.size() >= 3) {246    OS << "/language ";247    printStringOrID(Context[2], OS, /* IsType */ false, /* IsID */ false);248  }249  OS << ", in " << File1 << " and in " << File2;250 251  return OS.str();252}253 254// MinGW specific. Remove default manifests (with language zero) if there are255// other manifests present, and report an error if there are more than one256// manifest with a non-zero language code.257// GCC has the concept of a default manifest resource object, which gets258// linked in implicitly if present. This default manifest has got language259// id zero, and should be dropped silently if there's another manifest present.260// If the user resources surprisignly had a manifest with language id zero,261// we should also ignore the duplicate default manifest.262void WindowsResourceParser::cleanUpManifests(263    std::vector<std::string> &Duplicates) {264  auto TypeIt = Root.IDChildren.find(/* RT_MANIFEST */ 24);265  if (TypeIt == Root.IDChildren.end())266    return;267 268  TreeNode *TypeNode = TypeIt->second.get();269  auto NameIt =270      TypeNode->IDChildren.find(/* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1);271  if (NameIt == TypeNode->IDChildren.end())272    return;273 274  TreeNode *NameNode = NameIt->second.get();275  if (NameNode->IDChildren.size() <= 1)276    return; // None or one manifest present, all good.277 278  // If we have more than one manifest, drop the language zero one if present,279  // and check again.280  auto LangZeroIt = NameNode->IDChildren.find(0);281  if (LangZeroIt != NameNode->IDChildren.end() &&282      LangZeroIt->second->IsDataNode) {283    uint32_t RemovedIndex = LangZeroIt->second->DataIndex;284    NameNode->IDChildren.erase(LangZeroIt);285    Data.erase(Data.begin() + RemovedIndex);286    Root.shiftDataIndexDown(RemovedIndex);287 288    // If we're now down to one manifest, all is good.289    if (NameNode->IDChildren.size() <= 1)290      return;291  }292 293  // More than one non-language-zero manifest294  auto FirstIt = NameNode->IDChildren.begin();295  uint32_t FirstLang = FirstIt->first;296  TreeNode *FirstNode = FirstIt->second.get();297  auto LastIt = NameNode->IDChildren.rbegin();298  uint32_t LastLang = LastIt->first;299  TreeNode *LastNode = LastIt->second.get();300  Duplicates.push_back(301      ("duplicate non-default manifests with languages " + Twine(FirstLang) +302       " in " + InputFilenames[FirstNode->Origin] + " and " + Twine(LastLang) +303       " in " + InputFilenames[LastNode->Origin])304          .str());305}306 307// Ignore duplicates of manifests with language zero (the default manifest),308// in case the user has provided a manifest with that language id. See309// the function comment above for context. Only returns true if MinGW is set310// to true.311bool WindowsResourceParser::shouldIgnoreDuplicate(312    const ResourceEntryRef &Entry) const {313  return MinGW && !Entry.checkTypeString() &&314         Entry.getTypeID() == /* RT_MANIFEST */ 24 &&315         !Entry.checkNameString() &&316         Entry.getNameID() == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&317         Entry.getLanguage() == 0;318}319 320bool WindowsResourceParser::shouldIgnoreDuplicate(321    const std::vector<StringOrID> &Context) const {322  return MinGW && Context.size() == 3 && !Context[0].IsString &&323         Context[0].ID == /* RT_MANIFEST */ 24 && !Context[1].IsString &&324         Context[1].ID == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&325         !Context[2].IsString && Context[2].ID == 0;326}327 328Error WindowsResourceParser::parse(WindowsResource *WR,329                                   std::vector<std::string> &Duplicates) {330  auto EntryOrErr = WR->getHeadEntry();331  if (!EntryOrErr) {332    auto E = EntryOrErr.takeError();333    if (E.isA<EmptyResError>()) {334      // Check if the .res file contains no entries.  In this case we don't have335      // to throw an error but can rather just return without parsing anything.336      // This applies for files which have a valid PE header magic and the337      // mandatory empty null resource entry.  Files which do not fit this338      // criteria would have already been filtered out by339      // WindowsResource::createWindowsResource().340      consumeError(std::move(E));341      return Error::success();342    }343    return E;344  }345 346  ResourceEntryRef Entry = EntryOrErr.get();347  uint32_t Origin = InputFilenames.size();348  InputFilenames.push_back(std::string(WR->getFileName()));349  bool End = false;350  while (!End) {351 352    TreeNode *Node;353    bool IsNewNode = Root.addEntry(Entry, Origin, Data, StringTable, Node);354    if (!IsNewNode) {355      if (!shouldIgnoreDuplicate(Entry))356        Duplicates.push_back(makeDuplicateResourceError(357            Entry, InputFilenames[Node->Origin], WR->getFileName()));358    }359 360    RETURN_IF_ERROR(Entry.moveNext(End));361  }362 363  return Error::success();364}365 366Error WindowsResourceParser::parse(ResourceSectionRef &RSR, StringRef Filename,367                                   std::vector<std::string> &Duplicates) {368  UNWRAP_REF_OR_RETURN(BaseTable, RSR.getBaseTable());369  uint32_t Origin = InputFilenames.size();370  InputFilenames.push_back(std::string(Filename));371  std::vector<StringOrID> Context;372  return addChildren(Root, RSR, BaseTable, Origin, Context, Duplicates);373}374 375void WindowsResourceParser::printTree(raw_ostream &OS) const {376  ScopedPrinter Writer(OS);377  Root.print(Writer, "Resource Tree");378}379 380bool WindowsResourceParser::TreeNode::addEntry(381    const ResourceEntryRef &Entry, uint32_t Origin,382    std::vector<std::vector<uint8_t>> &Data,383    std::vector<std::vector<UTF16>> &StringTable, TreeNode *&Result) {384  TreeNode &TypeNode = addTypeNode(Entry, StringTable);385  TreeNode &NameNode = TypeNode.addNameNode(Entry, StringTable);386  return NameNode.addLanguageNode(Entry, Origin, Data, Result);387}388 389Error WindowsResourceParser::addChildren(TreeNode &Node,390                                         ResourceSectionRef &RSR,391                                         const coff_resource_dir_table &Table,392                                         uint32_t Origin,393                                         std::vector<StringOrID> &Context,394                                         std::vector<std::string> &Duplicates) {395 396  for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries;397       i++) {398    UNWRAP_REF_OR_RETURN(Entry, RSR.getTableEntry(Table, i));399    TreeNode *Child;400 401    if (Entry.Offset.isSubDir()) {402 403      // Create a new subdirectory and recurse404      if (i < Table.NumberOfNameEntries) {405        UNWRAP_OR_RETURN(NameString, RSR.getEntryNameString(Entry));406        Child = &Node.addNameChild(NameString, StringTable);407        Context.push_back(StringOrID(NameString));408      } else {409        Child = &Node.addIDChild(Entry.Identifier.ID);410        Context.push_back(StringOrID(Entry.Identifier.ID));411      }412 413      UNWRAP_REF_OR_RETURN(NextTable, RSR.getEntrySubDir(Entry));414      Error E =415          addChildren(*Child, RSR, NextTable, Origin, Context, Duplicates);416      if (E)417        return E;418      Context.pop_back();419 420    } else {421 422      // Data leaves are supposed to have a numeric ID as identifier (language).423      if (Table.NumberOfNameEntries > 0)424        return createStringError(object_error::parse_failed,425                                 "unexpected string key for data object");426 427      // Try adding a data leaf428      UNWRAP_REF_OR_RETURN(DataEntry, RSR.getEntryData(Entry));429      TreeNode *Child;430      Context.push_back(StringOrID(Entry.Identifier.ID));431      bool Added = Node.addDataChild(Entry.Identifier.ID, Table.MajorVersion,432                                     Table.MinorVersion, Table.Characteristics,433                                     Origin, Data.size(), Child);434      if (Added) {435        UNWRAP_OR_RETURN(Contents, RSR.getContents(DataEntry));436        Data.push_back(ArrayRef<uint8_t>(437            reinterpret_cast<const uint8_t *>(Contents.data()),438            Contents.size()));439      } else {440        if (!shouldIgnoreDuplicate(Context))441          Duplicates.push_back(makeDuplicateResourceError(442              Context, InputFilenames[Child->Origin], InputFilenames.back()));443      }444      Context.pop_back();445 446    }447  }448  return Error::success();449}450 451WindowsResourceParser::TreeNode::TreeNode(uint32_t StringIndex)452    : StringIndex(StringIndex) {}453 454WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion,455                                          uint16_t MinorVersion,456                                          uint32_t Characteristics,457                                          uint32_t Origin, uint32_t DataIndex)458    : IsDataNode(true), DataIndex(DataIndex), MajorVersion(MajorVersion),459      MinorVersion(MinorVersion), Characteristics(Characteristics),460      Origin(Origin) {}461 462std::unique_ptr<WindowsResourceParser::TreeNode>463WindowsResourceParser::TreeNode::createStringNode(uint32_t Index) {464  return std::unique_ptr<TreeNode>(new TreeNode(Index));465}466 467std::unique_ptr<WindowsResourceParser::TreeNode>468WindowsResourceParser::TreeNode::createIDNode() {469  return std::unique_ptr<TreeNode>(new TreeNode(0));470}471 472std::unique_ptr<WindowsResourceParser::TreeNode>473WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion,474                                                uint16_t MinorVersion,475                                                uint32_t Characteristics,476                                                uint32_t Origin,477                                                uint32_t DataIndex) {478  return std::unique_ptr<TreeNode>(new TreeNode(479      MajorVersion, MinorVersion, Characteristics, Origin, DataIndex));480}481 482WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addTypeNode(483    const ResourceEntryRef &Entry,484    std::vector<std::vector<UTF16>> &StringTable) {485  if (Entry.checkTypeString())486    return addNameChild(Entry.getTypeString(), StringTable);487  else488    return addIDChild(Entry.getTypeID());489}490 491WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameNode(492    const ResourceEntryRef &Entry,493    std::vector<std::vector<UTF16>> &StringTable) {494  if (Entry.checkNameString())495    return addNameChild(Entry.getNameString(), StringTable);496  else497    return addIDChild(Entry.getNameID());498}499 500bool WindowsResourceParser::TreeNode::addLanguageNode(501    const ResourceEntryRef &Entry, uint32_t Origin,502    std::vector<std::vector<uint8_t>> &Data, TreeNode *&Result) {503  bool Added = addDataChild(Entry.getLanguage(), Entry.getMajorVersion(),504                            Entry.getMinorVersion(), Entry.getCharacteristics(),505                            Origin, Data.size(), Result);506  if (Added)507    Data.push_back(Entry.getData());508  return Added;509}510 511bool WindowsResourceParser::TreeNode::addDataChild(512    uint32_t ID, uint16_t MajorVersion, uint16_t MinorVersion,513    uint32_t Characteristics, uint32_t Origin, uint32_t DataIndex,514    TreeNode *&Result) {515  auto NewChild = createDataNode(MajorVersion, MinorVersion, Characteristics,516                                 Origin, DataIndex);517  auto ElementInserted = IDChildren.emplace(ID, std::move(NewChild));518  Result = ElementInserted.first->second.get();519  return ElementInserted.second;520}521 522WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addIDChild(523    uint32_t ID) {524  auto Child = IDChildren.find(ID);525  if (Child == IDChildren.end()) {526    auto NewChild = createIDNode();527    WindowsResourceParser::TreeNode &Node = *NewChild;528    IDChildren.emplace(ID, std::move(NewChild));529    return Node;530  } else531    return *(Child->second);532}533 534WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameChild(535    ArrayRef<UTF16> NameRef, std::vector<std::vector<UTF16>> &StringTable) {536  std::string NameString;537  convertUTF16LEToUTF8String(NameRef, NameString);538 539  auto Child = StringChildren.find(NameString);540  if (Child == StringChildren.end()) {541    auto NewChild = createStringNode(StringTable.size());542    StringTable.push_back(NameRef);543    WindowsResourceParser::TreeNode &Node = *NewChild;544    StringChildren.emplace(NameString, std::move(NewChild));545    return Node;546  } else547    return *(Child->second);548}549 550void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer,551                                            StringRef Name) const {552  ListScope NodeScope(Writer, Name);553  for (auto const &Child : StringChildren) {554    Child.second->print(Writer, Child.first);555  }556  for (auto const &Child : IDChildren) {557    Child.second->print(Writer, to_string(Child.first));558  }559}560 561// This function returns the size of the entire resource tree, including562// directory tables, directory entries, and data entries.  It does not include563// the directory strings or the relocations of the .rsrc section.564uint32_t WindowsResourceParser::TreeNode::getTreeSize() const {565  uint32_t Size = (IDChildren.size() + StringChildren.size()) *566                  sizeof(coff_resource_dir_entry);567 568  // Reached a node pointing to a data entry.569  if (IsDataNode) {570    Size += sizeof(coff_resource_data_entry);571    return Size;572  }573 574  // If the node does not point to data, it must have a directory table pointing575  // to other nodes.576  Size += sizeof(coff_resource_dir_table);577 578  for (auto const &Child : StringChildren) {579    Size += Child.second->getTreeSize();580  }581  for (auto const &Child : IDChildren) {582    Size += Child.second->getTreeSize();583  }584  return Size;585}586 587// Shift DataIndex of all data children with an Index greater or equal to the588// given one, to fill a gap from removing an entry from the Data vector.589void WindowsResourceParser::TreeNode::shiftDataIndexDown(uint32_t Index) {590  if (IsDataNode && DataIndex >= Index) {591    DataIndex--;592  } else {593    for (auto &Child : IDChildren)594      Child.second->shiftDataIndexDown(Index);595    for (auto &Child : StringChildren)596      Child.second->shiftDataIndexDown(Index);597  }598}599 600class WindowsResourceCOFFWriter {601public:602  WindowsResourceCOFFWriter(COFF::MachineTypes MachineType,603                            const WindowsResourceParser &Parser, Error &E);604  std::unique_ptr<MemoryBuffer> write(uint32_t TimeDateStamp);605 606private:607  void performFileLayout();608  void performSectionOneLayout();609  void performSectionTwoLayout();610  void writeCOFFHeader(uint32_t TimeDateStamp);611  void writeFirstSectionHeader();612  void writeSecondSectionHeader();613  void writeFirstSection();614  void writeSecondSection();615  void writeSymbolTable();616  void writeStringTable();617  void writeDirectoryTree();618  void writeDirectoryStringTable();619  void writeFirstSectionRelocations();620  std::unique_ptr<WritableMemoryBuffer> OutputBuffer;621  char *BufferStart;622  uint64_t CurrentOffset = 0;623  COFF::MachineTypes MachineType;624  const WindowsResourceParser::TreeNode &Resources;625  const ArrayRef<std::vector<uint8_t>> Data;626  uint64_t FileSize;627  uint32_t SymbolTableOffset;628  uint32_t SectionOneSize;629  uint32_t SectionOneOffset;630  uint32_t SectionOneRelocations;631  uint32_t SectionTwoSize;632  uint32_t SectionTwoOffset;633  const ArrayRef<std::vector<UTF16>> StringTable;634  std::vector<uint32_t> StringTableOffsets;635  std::vector<uint32_t> DataOffsets;636  std::vector<uint32_t> RelocationAddresses;637};638 639WindowsResourceCOFFWriter::WindowsResourceCOFFWriter(640    COFF::MachineTypes MachineType, const WindowsResourceParser &Parser,641    Error &E)642    : MachineType(MachineType), Resources(Parser.getTree()),643      Data(Parser.getData()), StringTable(Parser.getStringTable()) {644  performFileLayout();645 646  OutputBuffer = WritableMemoryBuffer::getNewMemBuffer(647      FileSize, "internal .obj file created from .res files");648}649 650void WindowsResourceCOFFWriter::performFileLayout() {651  // Add size of COFF header.652  FileSize = COFF::Header16Size;653 654  // one .rsrc section header for directory tree, another for resource data.655  FileSize += 2 * COFF::SectionSize;656 657  performSectionOneLayout();658  performSectionTwoLayout();659 660  // We have reached the address of the symbol table.661  SymbolTableOffset = FileSize;662 663  FileSize += COFF::Symbol16Size;     // size of the @feat.00 symbol.664  FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section.665  FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource.666  FileSize += 4; // four null bytes for the string table.667}668 669void WindowsResourceCOFFWriter::performSectionOneLayout() {670  SectionOneOffset = FileSize;671 672  SectionOneSize = Resources.getTreeSize();673  uint32_t CurrentStringOffset = SectionOneSize;674  uint32_t TotalStringTableSize = 0;675  for (auto const &String : StringTable) {676    StringTableOffsets.push_back(CurrentStringOffset);677    uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t);678    CurrentStringOffset += StringSize;679    TotalStringTableSize += StringSize;680  }681  SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t));682 683  // account for the relocations of section one.684  SectionOneRelocations = FileSize + SectionOneSize;685  FileSize += SectionOneSize;686  FileSize +=687      Data.size() * COFF::RelocationSize; // one relocation for each resource.688  FileSize = alignTo(FileSize, SECTION_ALIGNMENT);689}690 691void WindowsResourceCOFFWriter::performSectionTwoLayout() {692  // add size of .rsrc$2 section, which contains all resource data on 8-byte693  // alignment.694  SectionTwoOffset = FileSize;695  SectionTwoSize = 0;696  for (auto const &Entry : Data) {697    DataOffsets.push_back(SectionTwoSize);698    SectionTwoSize += alignTo(Entry.size(), sizeof(uint64_t));699  }700  FileSize += SectionTwoSize;701  FileSize = alignTo(FileSize, SECTION_ALIGNMENT);702}703 704std::unique_ptr<MemoryBuffer>705WindowsResourceCOFFWriter::write(uint32_t TimeDateStamp) {706  BufferStart = OutputBuffer->getBufferStart();707 708  writeCOFFHeader(TimeDateStamp);709  writeFirstSectionHeader();710  writeSecondSectionHeader();711  writeFirstSection();712  writeSecondSection();713  writeSymbolTable();714  writeStringTable();715 716  return std::move(OutputBuffer);717}718 719// According to COFF specification, if the Src has a size equal to Dest,720// it's okay to *not* copy the trailing zero.721static void coffnamecpy(char (&Dest)[COFF::NameSize], StringRef Src) {722  assert(Src.size() <= COFF::NameSize &&723         "Src is larger than COFF::NameSize");724  assert((Src.size() == COFF::NameSize || Dest[Src.size()] == '\0') &&725         "Dest not zeroed upon initialization");726  memcpy(Dest, Src.data(), Src.size());727}728 729void WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp) {730  // Write the COFF header.731  auto *Header = reinterpret_cast<coff_file_header *>(BufferStart);732  Header->Machine = MachineType;733  Header->NumberOfSections = 2;734  Header->TimeDateStamp = TimeDateStamp;735  Header->PointerToSymbolTable = SymbolTableOffset;736  // One symbol for every resource plus 2 for each section and 1 for @feat.00737  Header->NumberOfSymbols = Data.size() + 5;738  Header->SizeOfOptionalHeader = 0;739  // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it.740  Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE;741}742 743void WindowsResourceCOFFWriter::writeFirstSectionHeader() {744  // Write the first section header.745  CurrentOffset += sizeof(coff_file_header);746  auto *SectionOneHeader =747      reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);748  coffnamecpy(SectionOneHeader->Name, ".rsrc$01");749  SectionOneHeader->VirtualSize = 0;750  SectionOneHeader->VirtualAddress = 0;751  SectionOneHeader->SizeOfRawData = SectionOneSize;752  SectionOneHeader->PointerToRawData = SectionOneOffset;753  SectionOneHeader->PointerToRelocations = SectionOneRelocations;754  SectionOneHeader->PointerToLinenumbers = 0;755  SectionOneHeader->NumberOfRelocations = Data.size();756  SectionOneHeader->NumberOfLinenumbers = 0;757  SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;758  SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;759}760 761void WindowsResourceCOFFWriter::writeSecondSectionHeader() {762  // Write the second section header.763  CurrentOffset += sizeof(coff_section);764  auto *SectionTwoHeader =765      reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);766  coffnamecpy(SectionTwoHeader->Name, ".rsrc$02");767  SectionTwoHeader->VirtualSize = 0;768  SectionTwoHeader->VirtualAddress = 0;769  SectionTwoHeader->SizeOfRawData = SectionTwoSize;770  SectionTwoHeader->PointerToRawData = SectionTwoOffset;771  SectionTwoHeader->PointerToRelocations = 0;772  SectionTwoHeader->PointerToLinenumbers = 0;773  SectionTwoHeader->NumberOfRelocations = 0;774  SectionTwoHeader->NumberOfLinenumbers = 0;775  SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;776  SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;777}778 779void WindowsResourceCOFFWriter::writeFirstSection() {780  // Write section one.781  CurrentOffset += sizeof(coff_section);782 783  writeDirectoryTree();784  writeDirectoryStringTable();785  writeFirstSectionRelocations();786 787  CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);788}789 790void WindowsResourceCOFFWriter::writeSecondSection() {791  // Now write the .rsrc$02 section.792  for (auto const &RawDataEntry : Data) {793    llvm::copy(RawDataEntry, BufferStart + CurrentOffset);794    CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t));795  }796 797  CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);798}799 800void WindowsResourceCOFFWriter::writeSymbolTable() {801  // Now write the symbol table.802  // First, the feat symbol.803  auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);804  coffnamecpy(Symbol->Name.ShortName, "@feat.00");805  Symbol->Value = 0x11;806  Symbol->SectionNumber = 0xffff;807  Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;808  Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;809  Symbol->NumberOfAuxSymbols = 0;810  CurrentOffset += sizeof(coff_symbol16);811 812  // Now write the .rsrc1 symbol + aux.813  Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);814  coffnamecpy(Symbol->Name.ShortName, ".rsrc$01");815  Symbol->Value = 0;816  Symbol->SectionNumber = 1;817  Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;818  Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;819  Symbol->NumberOfAuxSymbols = 1;820  CurrentOffset += sizeof(coff_symbol16);821  auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +822                                                              CurrentOffset);823  Aux->Length = SectionOneSize;824  Aux->NumberOfRelocations = Data.size();825  Aux->NumberOfLinenumbers = 0;826  Aux->CheckSum = 0;827  Aux->NumberLowPart = 0;828  Aux->Selection = 0;829  CurrentOffset += sizeof(coff_aux_section_definition);830 831  // Now write the .rsrc2 symbol + aux.832  Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);833  coffnamecpy(Symbol->Name.ShortName, ".rsrc$02");834  Symbol->Value = 0;835  Symbol->SectionNumber = 2;836  Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;837  Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;838  Symbol->NumberOfAuxSymbols = 1;839  CurrentOffset += sizeof(coff_symbol16);840  Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +841                                                        CurrentOffset);842  Aux->Length = SectionTwoSize;843  Aux->NumberOfRelocations = 0;844  Aux->NumberOfLinenumbers = 0;845  Aux->CheckSum = 0;846  Aux->NumberLowPart = 0;847  Aux->Selection = 0;848  CurrentOffset += sizeof(coff_aux_section_definition);849 850  // Now write a symbol for each relocation.851  for (unsigned i = 0; i < Data.size(); i++) {852    auto RelocationName = formatv("$R{0:X-6}", i & 0xffffff).sstr<COFF::NameSize>();853    Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);854    coffnamecpy(Symbol->Name.ShortName, RelocationName);855    Symbol->Value = DataOffsets[i];856    Symbol->SectionNumber = 2;857    Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;858    Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;859    Symbol->NumberOfAuxSymbols = 0;860    CurrentOffset += sizeof(coff_symbol16);861  }862}863 864void WindowsResourceCOFFWriter::writeStringTable() {865  // Just 4 null bytes for the string table.866  auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset);867  memset(COFFStringTable, 0, 4);868}869 870void WindowsResourceCOFFWriter::writeDirectoryTree() {871  // Traverse parsed resource tree breadth-first and write the corresponding872  // COFF objects.873  std::queue<const WindowsResourceParser::TreeNode *> Queue;874  Queue.push(&Resources);875  uint32_t NextLevelOffset =876      sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() +877                                         Resources.getIDChildren().size()) *878                                            sizeof(coff_resource_dir_entry);879  std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder;880  uint32_t CurrentRelativeOffset = 0;881 882  while (!Queue.empty()) {883    auto CurrentNode = Queue.front();884    Queue.pop();885    auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart +886                                                              CurrentOffset);887    Table->Characteristics = CurrentNode->getCharacteristics();888    Table->TimeDateStamp = 0;889    Table->MajorVersion = CurrentNode->getMajorVersion();890    Table->MinorVersion = CurrentNode->getMinorVersion();891    auto &IDChildren = CurrentNode->getIDChildren();892    auto &StringChildren = CurrentNode->getStringChildren();893    Table->NumberOfNameEntries = StringChildren.size();894    Table->NumberOfIDEntries = IDChildren.size();895    CurrentOffset += sizeof(coff_resource_dir_table);896    CurrentRelativeOffset += sizeof(coff_resource_dir_table);897 898    // Write the directory entries immediately following each directory table.899    for (auto const &Child : StringChildren) {900      auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +901                                                                CurrentOffset);902      Entry->Identifier.setNameOffset(903          StringTableOffsets[Child.second->getStringIndex()]);904      if (Child.second->checkIsDataNode()) {905        Entry->Offset.DataEntryOffset = NextLevelOffset;906        NextLevelOffset += sizeof(coff_resource_data_entry);907        DataEntriesTreeOrder.push_back(Child.second.get());908      } else {909        Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);910        NextLevelOffset += sizeof(coff_resource_dir_table) +911                           (Child.second->getStringChildren().size() +912                            Child.second->getIDChildren().size()) *913                               sizeof(coff_resource_dir_entry);914        Queue.push(Child.second.get());915      }916      CurrentOffset += sizeof(coff_resource_dir_entry);917      CurrentRelativeOffset += sizeof(coff_resource_dir_entry);918    }919    for (auto const &Child : IDChildren) {920      auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +921                                                                CurrentOffset);922      Entry->Identifier.ID = Child.first;923      if (Child.second->checkIsDataNode()) {924        Entry->Offset.DataEntryOffset = NextLevelOffset;925        NextLevelOffset += sizeof(coff_resource_data_entry);926        DataEntriesTreeOrder.push_back(Child.second.get());927      } else {928        Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);929        NextLevelOffset += sizeof(coff_resource_dir_table) +930                           (Child.second->getStringChildren().size() +931                            Child.second->getIDChildren().size()) *932                               sizeof(coff_resource_dir_entry);933        Queue.push(Child.second.get());934      }935      CurrentOffset += sizeof(coff_resource_dir_entry);936      CurrentRelativeOffset += sizeof(coff_resource_dir_entry);937    }938  }939 940  RelocationAddresses.resize(Data.size());941  // Now write all the resource data entries.942  for (const auto *DataNodes : DataEntriesTreeOrder) {943    auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart +944                                                               CurrentOffset);945    RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset;946    Entry->DataRVA = 0; // Set to zero because it is a relocation.947    Entry->DataSize = Data[DataNodes->getDataIndex()].size();948    Entry->Codepage = 0;949    Entry->Reserved = 0;950    CurrentOffset += sizeof(coff_resource_data_entry);951    CurrentRelativeOffset += sizeof(coff_resource_data_entry);952  }953}954 955void WindowsResourceCOFFWriter::writeDirectoryStringTable() {956  // Now write the directory string table for .rsrc$01957  uint32_t TotalStringTableSize = 0;958  for (auto &String : StringTable) {959    uint16_t Length = String.size();960    support::endian::write16le(BufferStart + CurrentOffset, Length);961    CurrentOffset += sizeof(uint16_t);962    auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset);963    llvm::copy(String, Start);964    CurrentOffset += Length * sizeof(UTF16);965    TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t);966  }967  CurrentOffset +=968      alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize;969}970 971void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {972 973  // Now write the relocations for .rsrc$01974  // Five symbols already in table before we start, @feat.00 and 2 for each975  // .rsrc section.976  uint32_t NextSymbolIndex = 5;977  for (unsigned i = 0; i < Data.size(); i++) {978    auto *Reloc =979        reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset);980    Reloc->VirtualAddress = RelocationAddresses[i];981    Reloc->SymbolTableIndex = NextSymbolIndex++;982    switch (getMachineArchType(MachineType)) {983    case Triple::thumb:984      Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB;985      break;986    case Triple::x86_64:987      Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB;988      break;989    case Triple::x86:990      Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB;991      break;992    case Triple::aarch64:993      Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB;994      break;995    default:996      llvm_unreachable("unknown machine type");997    }998    CurrentOffset += sizeof(coff_relocation);999  }1000}1001 1002Expected<std::unique_ptr<MemoryBuffer>>1003writeWindowsResourceCOFF(COFF::MachineTypes MachineType,1004                         const WindowsResourceParser &Parser,1005                         uint32_t TimeDateStamp) {1006  Error E = Error::success();1007  WindowsResourceCOFFWriter Writer(MachineType, Parser, E);1008  if (E)1009    return E;1010  return Writer.write(TimeDateStamp);1011}1012 1013} // namespace object1014} // namespace llvm1015