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