1318663Sdim//===-- WindowsResource.cpp -------------------------------------*- C++ -*-===//
2318663Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6318663Sdim//
7318663Sdim//===----------------------------------------------------------------------===//
8318663Sdim//
9318663Sdim// This file implements the .res file class.
10318663Sdim//
11318663Sdim//===----------------------------------------------------------------------===//
12318663Sdim
13318663Sdim#include "llvm/Object/WindowsResource.h"
14319799Sdim#include "llvm/Object/COFF.h"
15319799Sdim#include "llvm/Support/FileOutputBuffer.h"
16327952Sdim#include "llvm/Support/FormatVariadic.h"
17319799Sdim#include "llvm/Support/MathExtras.h"
18353358Sdim#include "llvm/Support/ScopedPrinter.h"
19319799Sdim#include <ctime>
20319799Sdim#include <queue>
21318663Sdim#include <system_error>
22318663Sdim
23320397Sdimusing namespace llvm;
24320397Sdimusing namespace object;
25320397Sdim
26318663Sdimnamespace llvm {
27318663Sdimnamespace object {
28318663Sdim
29319479Sdim#define RETURN_IF_ERROR(X)                                                     \
30319479Sdim  if (auto EC = X)                                                             \
31319479Sdim    return EC;
32319479Sdim
33360784Sdim#define UNWRAP_REF_OR_RETURN(Name, Expr)                                       \
34360784Sdim  auto Name##OrErr = Expr;                                                     \
35360784Sdim  if (!Name##OrErr)                                                            \
36360784Sdim    return Name##OrErr.takeError();                                            \
37360784Sdim  const auto &Name = *Name##OrErr;
38360784Sdim
39360784Sdim#define UNWRAP_OR_RETURN(Name, Expr)                                           \
40360784Sdim  auto Name##OrErr = Expr;                                                     \
41360784Sdim  if (!Name##OrErr)                                                            \
42360784Sdim    return Name##OrErr.takeError();                                            \
43360784Sdim  auto Name = *Name##OrErr;
44360784Sdim
45319479Sdimconst uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t);
46319479Sdim
47320041Sdim// COFF files seem to be inconsistent with alignment between sections, just use
48320041Sdim// 8-byte because it makes everyone happy.
49320041Sdimconst uint32_t SECTION_ALIGNMENT = sizeof(uint64_t);
50320041Sdim
51318663SdimWindowsResource::WindowsResource(MemoryBufferRef Source)
52318663Sdim    : Binary(Binary::ID_WinRes, Source) {
53320397Sdim  size_t LeadingSize = WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE;
54318663Sdim  BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize),
55318663Sdim                         support::little);
56318663Sdim}
57318663Sdim
58353358Sdim// static
59318663SdimExpected<std::unique_ptr<WindowsResource>>
60318663SdimWindowsResource::createWindowsResource(MemoryBufferRef Source) {
61320397Sdim  if (Source.getBufferSize() < WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE)
62318663Sdim    return make_error<GenericBinaryError>(
63353358Sdim        Source.getBufferIdentifier() + ": too small to be a resource file",
64318663Sdim        object_error::invalid_file_type);
65318663Sdim  std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source));
66318663Sdim  return std::move(Ret);
67318663Sdim}
68318663Sdim
69318663SdimExpected<ResourceEntryRef> WindowsResource::getHeadEntry() {
70327952Sdim  if (BBS.getLength() < sizeof(WinResHeaderPrefix) + sizeof(WinResHeaderSuffix))
71353358Sdim    return make_error<EmptyResError>(getFileName() + " contains no entries",
72327952Sdim                                     object_error::unexpected_eof);
73327952Sdim  return ResourceEntryRef::create(BinaryStreamRef(BBS), this);
74318663Sdim}
75318663Sdim
76318663SdimResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref,
77327952Sdim                                   const WindowsResource *Owner)
78353358Sdim    : Reader(Ref), Owner(Owner) {}
79327952Sdim
80327952SdimExpected<ResourceEntryRef>
81327952SdimResourceEntryRef::create(BinaryStreamRef BSR, const WindowsResource *Owner) {
82327952Sdim  auto Ref = ResourceEntryRef(BSR, Owner);
83327952Sdim  if (auto E = Ref.loadNext())
84327952Sdim    return std::move(E);
85327952Sdim  return Ref;
86318663Sdim}
87318663Sdim
88318663SdimError ResourceEntryRef::moveNext(bool &End) {
89318663Sdim  // Reached end of all the entries.
90318663Sdim  if (Reader.bytesRemaining() == 0) {
91318663Sdim    End = true;
92318663Sdim    return Error::success();
93318663Sdim  }
94318663Sdim  RETURN_IF_ERROR(loadNext());
95318663Sdim
96318663Sdim  return Error::success();
97318663Sdim}
98318663Sdim
99319479Sdimstatic Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID,
100319479Sdim                            ArrayRef<UTF16> &Str, bool &IsString) {
101319479Sdim  uint16_t IDFlag;
102319479Sdim  RETURN_IF_ERROR(Reader.readInteger(IDFlag));
103319479Sdim  IsString = IDFlag != 0xffff;
104319479Sdim
105319479Sdim  if (IsString) {
106319479Sdim    Reader.setOffset(
107319479Sdim        Reader.getOffset() -
108319479Sdim        sizeof(uint16_t)); // Re-read the bytes which we used to check the flag.
109319479Sdim    RETURN_IF_ERROR(Reader.readWideString(Str));
110319479Sdim  } else
111319479Sdim    RETURN_IF_ERROR(Reader.readInteger(ID));
112319479Sdim
113319479Sdim  return Error::success();
114319479Sdim}
115319479Sdim
116318663SdimError ResourceEntryRef::loadNext() {
117320397Sdim  const WinResHeaderPrefix *Prefix;
118320397Sdim  RETURN_IF_ERROR(Reader.readObject(Prefix));
119319479Sdim
120320397Sdim  if (Prefix->HeaderSize < MIN_HEADER_SIZE)
121353358Sdim    return make_error<GenericBinaryError>(Owner->getFileName() +
122353358Sdim                                              ": header size too small",
123319479Sdim                                          object_error::parse_failed);
124319479Sdim
125319479Sdim  RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType));
126319479Sdim
127319479Sdim  RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName));
128319479Sdim
129320397Sdim  RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_HEADER_ALIGNMENT));
130319479Sdim
131319479Sdim  RETURN_IF_ERROR(Reader.readObject(Suffix));
132319479Sdim
133320397Sdim  RETURN_IF_ERROR(Reader.readArray(Data, Prefix->DataSize));
134319479Sdim
135320397Sdim  RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_DATA_ALIGNMENT));
136319479Sdim
137318663Sdim  return Error::success();
138318663Sdim}
139318663Sdim
140360784SdimWindowsResourceParser::WindowsResourceParser(bool MinGW)
141360784Sdim    : Root(false), MinGW(MinGW) {}
142319479Sdim
143353358Sdimvoid printResourceTypeName(uint16_t TypeID, raw_ostream &OS) {
144353358Sdim  switch (TypeID) {
145353358Sdim  case  1: OS << "CURSOR (ID 1)"; break;
146353358Sdim  case  2: OS << "BITMAP (ID 2)"; break;
147353358Sdim  case  3: OS << "ICON (ID 3)"; break;
148353358Sdim  case  4: OS << "MENU (ID 4)"; break;
149353358Sdim  case  5: OS << "DIALOG (ID 5)"; break;
150353358Sdim  case  6: OS << "STRINGTABLE (ID 6)"; break;
151353358Sdim  case  7: OS << "FONTDIR (ID 7)"; break;
152353358Sdim  case  8: OS << "FONT (ID 8)"; break;
153353358Sdim  case  9: OS << "ACCELERATOR (ID 9)"; break;
154353358Sdim  case 10: OS << "RCDATA (ID 10)"; break;
155353358Sdim  case 11: OS << "MESSAGETABLE (ID 11)"; break;
156353358Sdim  case 12: OS << "GROUP_CURSOR (ID 12)"; break;
157353358Sdim  case 14: OS << "GROUP_ICON (ID 14)"; break;
158353358Sdim  case 16: OS << "VERSIONINFO (ID 16)"; break;
159353358Sdim  case 17: OS << "DLGINCLUDE (ID 17)"; break;
160353358Sdim  case 19: OS << "PLUGPLAY (ID 19)"; break;
161353358Sdim  case 20: OS << "VXD (ID 20)"; break;
162353358Sdim  case 21: OS << "ANICURSOR (ID 21)"; break;
163353358Sdim  case 22: OS << "ANIICON (ID 22)"; break;
164353358Sdim  case 23: OS << "HTML (ID 23)"; break;
165353358Sdim  case 24: OS << "MANIFEST (ID 24)"; break;
166353358Sdim  default: OS << "ID " << TypeID; break;
167353358Sdim  }
168353358Sdim}
169353358Sdim
170353358Sdimstatic bool convertUTF16LEToUTF8String(ArrayRef<UTF16> Src, std::string &Out) {
171353358Sdim  if (!sys::IsBigEndianHost)
172353358Sdim    return convertUTF16ToUTF8String(Src, Out);
173353358Sdim
174353358Sdim  std::vector<UTF16> EndianCorrectedSrc;
175353358Sdim  EndianCorrectedSrc.resize(Src.size() + 1);
176353358Sdim  llvm::copy(Src, EndianCorrectedSrc.begin() + 1);
177353358Sdim  EndianCorrectedSrc[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED;
178353358Sdim  return convertUTF16ToUTF8String(makeArrayRef(EndianCorrectedSrc), Out);
179353358Sdim}
180353358Sdim
181353358Sdimstatic std::string makeDuplicateResourceError(
182353358Sdim    const ResourceEntryRef &Entry, StringRef File1, StringRef File2) {
183353358Sdim  std::string Ret;
184353358Sdim  raw_string_ostream OS(Ret);
185353358Sdim
186353358Sdim  OS << "duplicate resource:";
187353358Sdim
188353358Sdim  OS << " type ";
189353358Sdim  if (Entry.checkTypeString()) {
190353358Sdim    std::string UTF8;
191353358Sdim    if (!convertUTF16LEToUTF8String(Entry.getTypeString(), UTF8))
192353358Sdim      UTF8 = "(failed conversion from UTF16)";
193353358Sdim    OS << '\"' << UTF8 << '\"';
194353358Sdim  } else
195353358Sdim    printResourceTypeName(Entry.getTypeID(), OS);
196353358Sdim
197353358Sdim  OS << "/name ";
198353358Sdim  if (Entry.checkNameString()) {
199353358Sdim    std::string UTF8;
200353358Sdim    if (!convertUTF16LEToUTF8String(Entry.getNameString(), UTF8))
201353358Sdim      UTF8 = "(failed conversion from UTF16)";
202353358Sdim    OS << '\"' << UTF8 << '\"';
203353358Sdim  } else {
204353358Sdim    OS << "ID " << Entry.getNameID();
205353358Sdim  }
206353358Sdim
207353358Sdim  OS << "/language " << Entry.getLanguage() << ", in " << File1 << " and in "
208353358Sdim     << File2;
209353358Sdim
210353358Sdim  return OS.str();
211353358Sdim}
212353358Sdim
213360784Sdimstatic void printStringOrID(const WindowsResourceParser::StringOrID &S,
214360784Sdim                            raw_string_ostream &OS, bool IsType, bool IsID) {
215360784Sdim  if (S.IsString) {
216360784Sdim    std::string UTF8;
217360784Sdim    if (!convertUTF16LEToUTF8String(S.String, UTF8))
218360784Sdim      UTF8 = "(failed conversion from UTF16)";
219360784Sdim    OS << '\"' << UTF8 << '\"';
220360784Sdim  } else if (IsType)
221360784Sdim    printResourceTypeName(S.ID, OS);
222360784Sdim  else if (IsID)
223360784Sdim    OS << "ID " << S.ID;
224360784Sdim  else
225360784Sdim    OS << S.ID;
226360784Sdim}
227360784Sdim
228360784Sdimstatic std::string makeDuplicateResourceError(
229360784Sdim    const std::vector<WindowsResourceParser::StringOrID> &Context,
230360784Sdim    StringRef File1, StringRef File2) {
231360784Sdim  std::string Ret;
232360784Sdim  raw_string_ostream OS(Ret);
233360784Sdim
234360784Sdim  OS << "duplicate resource:";
235360784Sdim
236360784Sdim  if (Context.size() >= 1) {
237360784Sdim    OS << " type ";
238360784Sdim    printStringOrID(Context[0], OS, /* IsType */ true, /* IsID */ true);
239360784Sdim  }
240360784Sdim
241360784Sdim  if (Context.size() >= 2) {
242360784Sdim    OS << "/name ";
243360784Sdim    printStringOrID(Context[1], OS, /* IsType */ false, /* IsID */ true);
244360784Sdim  }
245360784Sdim
246360784Sdim  if (Context.size() >= 3) {
247360784Sdim    OS << "/language ";
248360784Sdim    printStringOrID(Context[2], OS, /* IsType */ false, /* IsID */ false);
249360784Sdim  }
250360784Sdim  OS << ", in " << File1 << " and in " << File2;
251360784Sdim
252360784Sdim  return OS.str();
253360784Sdim}
254360784Sdim
255360784Sdim// MinGW specific. Remove default manifests (with language zero) if there are
256360784Sdim// other manifests present, and report an error if there are more than one
257360784Sdim// manifest with a non-zero language code.
258360784Sdim// GCC has the concept of a default manifest resource object, which gets
259360784Sdim// linked in implicitly if present. This default manifest has got language
260360784Sdim// id zero, and should be dropped silently if there's another manifest present.
261360784Sdim// If the user resources surprisignly had a manifest with language id zero,
262360784Sdim// we should also ignore the duplicate default manifest.
263360784Sdimvoid WindowsResourceParser::cleanUpManifests(
264360784Sdim    std::vector<std::string> &Duplicates) {
265360784Sdim  auto TypeIt = Root.IDChildren.find(/* RT_MANIFEST */ 24);
266360784Sdim  if (TypeIt == Root.IDChildren.end())
267360784Sdim    return;
268360784Sdim
269360784Sdim  TreeNode *TypeNode = TypeIt->second.get();
270360784Sdim  auto NameIt =
271360784Sdim      TypeNode->IDChildren.find(/* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1);
272360784Sdim  if (NameIt == TypeNode->IDChildren.end())
273360784Sdim    return;
274360784Sdim
275360784Sdim  TreeNode *NameNode = NameIt->second.get();
276360784Sdim  if (NameNode->IDChildren.size() <= 1)
277360784Sdim    return; // None or one manifest present, all good.
278360784Sdim
279360784Sdim  // If we have more than one manifest, drop the language zero one if present,
280360784Sdim  // and check again.
281360784Sdim  auto LangZeroIt = NameNode->IDChildren.find(0);
282360784Sdim  if (LangZeroIt != NameNode->IDChildren.end() &&
283360784Sdim      LangZeroIt->second->IsDataNode) {
284360784Sdim    uint32_t RemovedIndex = LangZeroIt->second->DataIndex;
285360784Sdim    NameNode->IDChildren.erase(LangZeroIt);
286360784Sdim    Data.erase(Data.begin() + RemovedIndex);
287360784Sdim    Root.shiftDataIndexDown(RemovedIndex);
288360784Sdim
289360784Sdim    // If we're now down to one manifest, all is good.
290360784Sdim    if (NameNode->IDChildren.size() <= 1)
291360784Sdim      return;
292360784Sdim  }
293360784Sdim
294360784Sdim  // More than one non-language-zero manifest
295360784Sdim  auto FirstIt = NameNode->IDChildren.begin();
296360784Sdim  uint32_t FirstLang = FirstIt->first;
297360784Sdim  TreeNode *FirstNode = FirstIt->second.get();
298360784Sdim  auto LastIt = NameNode->IDChildren.rbegin();
299360784Sdim  uint32_t LastLang = LastIt->first;
300360784Sdim  TreeNode *LastNode = LastIt->second.get();
301360784Sdim  Duplicates.push_back(
302360784Sdim      ("duplicate non-default manifests with languages " + Twine(FirstLang) +
303360784Sdim       " in " + InputFilenames[FirstNode->Origin] + " and " + Twine(LastLang) +
304360784Sdim       " in " + InputFilenames[LastNode->Origin])
305360784Sdim          .str());
306360784Sdim}
307360784Sdim
308360784Sdim// Ignore duplicates of manifests with language zero (the default manifest),
309360784Sdim// in case the user has provided a manifest with that language id. See
310360784Sdim// the function comment above for context. Only returns true if MinGW is set
311360784Sdim// to true.
312360784Sdimbool WindowsResourceParser::shouldIgnoreDuplicate(
313360784Sdim    const ResourceEntryRef &Entry) const {
314360784Sdim  return MinGW && !Entry.checkTypeString() &&
315360784Sdim         Entry.getTypeID() == /* RT_MANIFEST */ 24 &&
316360784Sdim         !Entry.checkNameString() &&
317360784Sdim         Entry.getNameID() == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&
318360784Sdim         Entry.getLanguage() == 0;
319360784Sdim}
320360784Sdim
321360784Sdimbool WindowsResourceParser::shouldIgnoreDuplicate(
322360784Sdim    const std::vector<StringOrID> &Context) const {
323360784Sdim  return MinGW && Context.size() == 3 && !Context[0].IsString &&
324360784Sdim         Context[0].ID == /* RT_MANIFEST */ 24 && !Context[1].IsString &&
325360784Sdim         Context[1].ID == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&
326360784Sdim         !Context[2].IsString && Context[2].ID == 0;
327360784Sdim}
328360784Sdim
329353358SdimError WindowsResourceParser::parse(WindowsResource *WR,
330353358Sdim                                   std::vector<std::string> &Duplicates) {
331319479Sdim  auto EntryOrErr = WR->getHeadEntry();
332327952Sdim  if (!EntryOrErr) {
333327952Sdim    auto E = EntryOrErr.takeError();
334327952Sdim    if (E.isA<EmptyResError>()) {
335327952Sdim      // Check if the .res file contains no entries.  In this case we don't have
336327952Sdim      // to throw an error but can rather just return without parsing anything.
337327952Sdim      // This applies for files which have a valid PE header magic and the
338327952Sdim      // mandatory empty null resource entry.  Files which do not fit this
339327952Sdim      // criteria would have already been filtered out by
340327952Sdim      // WindowsResource::createWindowsResource().
341327952Sdim      consumeError(std::move(E));
342327952Sdim      return Error::success();
343327952Sdim    }
344327952Sdim    return E;
345327952Sdim  }
346319479Sdim
347319479Sdim  ResourceEntryRef Entry = EntryOrErr.get();
348360784Sdim  uint32_t Origin = InputFilenames.size();
349360784Sdim  InputFilenames.push_back(WR->getFileName());
350319479Sdim  bool End = false;
351319479Sdim  while (!End) {
352319799Sdim
353360784Sdim    TreeNode *Node;
354360784Sdim    bool IsNewNode = Root.addEntry(Entry, Origin, Data, StringTable, Node);
355353358Sdim    if (!IsNewNode) {
356360784Sdim      if (!shouldIgnoreDuplicate(Entry))
357360784Sdim        Duplicates.push_back(makeDuplicateResourceError(
358360784Sdim            Entry, InputFilenames[Node->Origin], WR->getFileName()));
359353358Sdim    }
360320041Sdim
361319479Sdim    RETURN_IF_ERROR(Entry.moveNext(End));
362319479Sdim  }
363319479Sdim
364319479Sdim  return Error::success();
365319479Sdim}
366319479Sdim
367360784SdimError WindowsResourceParser::parse(ResourceSectionRef &RSR, StringRef Filename,
368360784Sdim                                   std::vector<std::string> &Duplicates) {
369360784Sdim  UNWRAP_REF_OR_RETURN(BaseTable, RSR.getBaseTable());
370360784Sdim  uint32_t Origin = InputFilenames.size();
371360784Sdim  InputFilenames.push_back(Filename);
372360784Sdim  std::vector<StringOrID> Context;
373360784Sdim  return addChildren(Root, RSR, BaseTable, Origin, Context, Duplicates);
374360784Sdim}
375360784Sdim
376320041Sdimvoid WindowsResourceParser::printTree(raw_ostream &OS) const {
377320041Sdim  ScopedPrinter Writer(OS);
378319479Sdim  Root.print(Writer, "Resource Tree");
379319479Sdim}
380319479Sdim
381360784Sdimbool WindowsResourceParser::TreeNode::addEntry(
382360784Sdim    const ResourceEntryRef &Entry, uint32_t Origin,
383360784Sdim    std::vector<std::vector<uint8_t>> &Data,
384360784Sdim    std::vector<std::vector<UTF16>> &StringTable, TreeNode *&Result) {
385360784Sdim  TreeNode &TypeNode = addTypeNode(Entry, StringTable);
386360784Sdim  TreeNode &NameNode = TypeNode.addNameNode(Entry, StringTable);
387360784Sdim  return NameNode.addLanguageNode(Entry, Origin, Data, Result);
388319479Sdim}
389319479Sdim
390360784SdimError WindowsResourceParser::addChildren(TreeNode &Node,
391360784Sdim                                         ResourceSectionRef &RSR,
392360784Sdim                                         const coff_resource_dir_table &Table,
393360784Sdim                                         uint32_t Origin,
394360784Sdim                                         std::vector<StringOrID> &Context,
395360784Sdim                                         std::vector<std::string> &Duplicates) {
396360784Sdim
397360784Sdim  for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries;
398360784Sdim       i++) {
399360784Sdim    UNWRAP_REF_OR_RETURN(Entry, RSR.getTableEntry(Table, i));
400360784Sdim    TreeNode *Child;
401360784Sdim
402360784Sdim    if (Entry.Offset.isSubDir()) {
403360784Sdim
404360784Sdim      // Create a new subdirectory and recurse
405360784Sdim      if (i < Table.NumberOfNameEntries) {
406360784Sdim        UNWRAP_OR_RETURN(NameString, RSR.getEntryNameString(Entry));
407360784Sdim        Child = &Node.addNameChild(NameString, StringTable);
408360784Sdim        Context.push_back(StringOrID(NameString));
409360784Sdim      } else {
410360784Sdim        Child = &Node.addIDChild(Entry.Identifier.ID);
411360784Sdim        Context.push_back(StringOrID(Entry.Identifier.ID));
412360784Sdim      }
413360784Sdim
414360784Sdim      UNWRAP_REF_OR_RETURN(NextTable, RSR.getEntrySubDir(Entry));
415360784Sdim      Error E =
416360784Sdim          addChildren(*Child, RSR, NextTable, Origin, Context, Duplicates);
417360784Sdim      if (E)
418360784Sdim        return E;
419360784Sdim      Context.pop_back();
420360784Sdim
421360784Sdim    } else {
422360784Sdim
423360784Sdim      // Data leaves are supposed to have a numeric ID as identifier (language).
424360784Sdim      if (Table.NumberOfNameEntries > 0)
425360784Sdim        return createStringError(object_error::parse_failed,
426360784Sdim                                 "unexpected string key for data object");
427360784Sdim
428360784Sdim      // Try adding a data leaf
429360784Sdim      UNWRAP_REF_OR_RETURN(DataEntry, RSR.getEntryData(Entry));
430360784Sdim      TreeNode *Child;
431360784Sdim      Context.push_back(StringOrID(Entry.Identifier.ID));
432360784Sdim      bool Added = Node.addDataChild(Entry.Identifier.ID, Table.MajorVersion,
433360784Sdim                                     Table.MinorVersion, Table.Characteristics,
434360784Sdim                                     Origin, Data.size(), Child);
435360784Sdim      if (Added) {
436360784Sdim        UNWRAP_OR_RETURN(Contents, RSR.getContents(DataEntry));
437360784Sdim        Data.push_back(ArrayRef<uint8_t>(
438360784Sdim            reinterpret_cast<const uint8_t *>(Contents.data()),
439360784Sdim            Contents.size()));
440360784Sdim      } else {
441360784Sdim        if (!shouldIgnoreDuplicate(Context))
442360784Sdim          Duplicates.push_back(makeDuplicateResourceError(
443360784Sdim              Context, InputFilenames[Child->Origin], InputFilenames.back()));
444360784Sdim      }
445360784Sdim      Context.pop_back();
446360784Sdim
447360784Sdim    }
448360784Sdim  }
449360784Sdim  return Error::success();
450319799Sdim}
451319479Sdim
452360784SdimWindowsResourceParser::TreeNode::TreeNode(uint32_t StringIndex)
453360784Sdim    : StringIndex(StringIndex) {}
454360784Sdim
455319799SdimWindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion,
456319799Sdim                                          uint16_t MinorVersion,
457353358Sdim                                          uint32_t Characteristics,
458360784Sdim                                          uint32_t Origin, uint32_t DataIndex)
459360784Sdim    : IsDataNode(true), DataIndex(DataIndex), MajorVersion(MajorVersion),
460360784Sdim      MinorVersion(MinorVersion), Characteristics(Characteristics),
461360784Sdim      Origin(Origin) {}
462319799Sdim
463319799Sdimstd::unique_ptr<WindowsResourceParser::TreeNode>
464360784SdimWindowsResourceParser::TreeNode::createStringNode(uint32_t Index) {
465360784Sdim  return std::unique_ptr<TreeNode>(new TreeNode(Index));
466319799Sdim}
467319799Sdim
468319799Sdimstd::unique_ptr<WindowsResourceParser::TreeNode>
469319799SdimWindowsResourceParser::TreeNode::createIDNode() {
470360784Sdim  return std::unique_ptr<TreeNode>(new TreeNode(0));
471319799Sdim}
472319799Sdim
473319799Sdimstd::unique_ptr<WindowsResourceParser::TreeNode>
474319799SdimWindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion,
475319799Sdim                                                uint16_t MinorVersion,
476353358Sdim                                                uint32_t Characteristics,
477360784Sdim                                                uint32_t Origin,
478360784Sdim                                                uint32_t DataIndex) {
479360784Sdim  return std::unique_ptr<TreeNode>(new TreeNode(
480360784Sdim      MajorVersion, MinorVersion, Characteristics, Origin, DataIndex));
481319799Sdim}
482319799Sdim
483360784SdimWindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addTypeNode(
484360784Sdim    const ResourceEntryRef &Entry,
485360784Sdim    std::vector<std::vector<UTF16>> &StringTable) {
486319479Sdim  if (Entry.checkTypeString())
487360784Sdim    return addNameChild(Entry.getTypeString(), StringTable);
488319479Sdim  else
489353358Sdim    return addIDChild(Entry.getTypeID());
490319479Sdim}
491319479Sdim
492360784SdimWindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameNode(
493360784Sdim    const ResourceEntryRef &Entry,
494360784Sdim    std::vector<std::vector<UTF16>> &StringTable) {
495319479Sdim  if (Entry.checkNameString())
496360784Sdim    return addNameChild(Entry.getNameString(), StringTable);
497319479Sdim  else
498353358Sdim    return addIDChild(Entry.getNameID());
499319479Sdim}
500319479Sdim
501353358Sdimbool WindowsResourceParser::TreeNode::addLanguageNode(
502360784Sdim    const ResourceEntryRef &Entry, uint32_t Origin,
503360784Sdim    std::vector<std::vector<uint8_t>> &Data, TreeNode *&Result) {
504360784Sdim  bool Added = addDataChild(Entry.getLanguage(), Entry.getMajorVersion(),
505360784Sdim                            Entry.getMinorVersion(), Entry.getCharacteristics(),
506360784Sdim                            Origin, Data.size(), Result);
507360784Sdim  if (Added)
508360784Sdim    Data.push_back(Entry.getData());
509360784Sdim  return Added;
510319479Sdim}
511319479Sdim
512353358Sdimbool WindowsResourceParser::TreeNode::addDataChild(
513353358Sdim    uint32_t ID, uint16_t MajorVersion, uint16_t MinorVersion,
514360784Sdim    uint32_t Characteristics, uint32_t Origin, uint32_t DataIndex,
515360784Sdim    TreeNode *&Result) {
516360784Sdim  auto NewChild = createDataNode(MajorVersion, MinorVersion, Characteristics,
517360784Sdim                                 Origin, DataIndex);
518353358Sdim  auto ElementInserted = IDChildren.emplace(ID, std::move(NewChild));
519353358Sdim  Result = ElementInserted.first->second.get();
520353358Sdim  return ElementInserted.second;
521353358Sdim}
522353358Sdim
523353358SdimWindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addIDChild(
524353358Sdim    uint32_t ID) {
525319479Sdim  auto Child = IDChildren.find(ID);
526319479Sdim  if (Child == IDChildren.end()) {
527353358Sdim    auto NewChild = createIDNode();
528319479Sdim    WindowsResourceParser::TreeNode &Node = *NewChild;
529319479Sdim    IDChildren.emplace(ID, std::move(NewChild));
530319479Sdim    return Node;
531319479Sdim  } else
532319479Sdim    return *(Child->second);
533319479Sdim}
534319479Sdim
535360784SdimWindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameChild(
536360784Sdim    ArrayRef<UTF16> NameRef, std::vector<std::vector<UTF16>> &StringTable) {
537319479Sdim  std::string NameString;
538353358Sdim  convertUTF16LEToUTF8String(NameRef, NameString);
539319479Sdim
540319479Sdim  auto Child = StringChildren.find(NameString);
541319479Sdim  if (Child == StringChildren.end()) {
542360784Sdim    auto NewChild = createStringNode(StringTable.size());
543360784Sdim    StringTable.push_back(NameRef);
544319479Sdim    WindowsResourceParser::TreeNode &Node = *NewChild;
545319479Sdim    StringChildren.emplace(NameString, std::move(NewChild));
546319479Sdim    return Node;
547319479Sdim  } else
548319479Sdim    return *(Child->second);
549319479Sdim}
550319479Sdim
551319479Sdimvoid WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer,
552319479Sdim                                            StringRef Name) const {
553319479Sdim  ListScope NodeScope(Writer, Name);
554319479Sdim  for (auto const &Child : StringChildren) {
555319479Sdim    Child.second->print(Writer, Child.first);
556319479Sdim  }
557319479Sdim  for (auto const &Child : IDChildren) {
558319479Sdim    Child.second->print(Writer, to_string(Child.first));
559319479Sdim  }
560319479Sdim}
561319479Sdim
562319799Sdim// This function returns the size of the entire resource tree, including
563319799Sdim// directory tables, directory entries, and data entries.  It does not include
564319799Sdim// the directory strings or the relocations of the .rsrc section.
565319799Sdimuint32_t WindowsResourceParser::TreeNode::getTreeSize() const {
566319799Sdim  uint32_t Size = (IDChildren.size() + StringChildren.size()) *
567320397Sdim                  sizeof(coff_resource_dir_entry);
568319799Sdim
569319799Sdim  // Reached a node pointing to a data entry.
570319799Sdim  if (IsDataNode) {
571320397Sdim    Size += sizeof(coff_resource_data_entry);
572319799Sdim    return Size;
573319799Sdim  }
574319799Sdim
575319799Sdim  // If the node does not point to data, it must have a directory table pointing
576319799Sdim  // to other nodes.
577320397Sdim  Size += sizeof(coff_resource_dir_table);
578319799Sdim
579319799Sdim  for (auto const &Child : StringChildren) {
580319799Sdim    Size += Child.second->getTreeSize();
581319799Sdim  }
582319799Sdim  for (auto const &Child : IDChildren) {
583319799Sdim    Size += Child.second->getTreeSize();
584319799Sdim  }
585319799Sdim  return Size;
586319799Sdim}
587319799Sdim
588360784Sdim// Shift DataIndex of all data children with an Index greater or equal to the
589360784Sdim// given one, to fill a gap from removing an entry from the Data vector.
590360784Sdimvoid WindowsResourceParser::TreeNode::shiftDataIndexDown(uint32_t Index) {
591360784Sdim  if (IsDataNode && DataIndex >= Index) {
592360784Sdim    DataIndex--;
593360784Sdim  } else {
594360784Sdim    for (auto &Child : IDChildren)
595360784Sdim      Child.second->shiftDataIndexDown(Index);
596360784Sdim    for (auto &Child : StringChildren)
597360784Sdim      Child.second->shiftDataIndexDown(Index);
598360784Sdim  }
599360784Sdim}
600360784Sdim
601319799Sdimclass WindowsResourceCOFFWriter {
602319799Sdimpublic:
603320397Sdim  WindowsResourceCOFFWriter(COFF::MachineTypes MachineType,
604319799Sdim                            const WindowsResourceParser &Parser, Error &E);
605353358Sdim  std::unique_ptr<MemoryBuffer> write(uint32_t TimeDateStamp);
606319799Sdim
607319799Sdimprivate:
608319799Sdim  void performFileLayout();
609319799Sdim  void performSectionOneLayout();
610319799Sdim  void performSectionTwoLayout();
611353358Sdim  void writeCOFFHeader(uint32_t TimeDateStamp);
612319799Sdim  void writeFirstSectionHeader();
613319799Sdim  void writeSecondSectionHeader();
614319799Sdim  void writeFirstSection();
615319799Sdim  void writeSecondSection();
616319799Sdim  void writeSymbolTable();
617319799Sdim  void writeStringTable();
618319799Sdim  void writeDirectoryTree();
619319799Sdim  void writeDirectoryStringTable();
620319799Sdim  void writeFirstSectionRelocations();
621341825Sdim  std::unique_ptr<WritableMemoryBuffer> OutputBuffer;
622320397Sdim  char *BufferStart;
623320041Sdim  uint64_t CurrentOffset = 0;
624320397Sdim  COFF::MachineTypes MachineType;
625319799Sdim  const WindowsResourceParser::TreeNode &Resources;
626319799Sdim  const ArrayRef<std::vector<uint8_t>> Data;
627319799Sdim  uint64_t FileSize;
628319799Sdim  uint32_t SymbolTableOffset;
629319799Sdim  uint32_t SectionOneSize;
630319799Sdim  uint32_t SectionOneOffset;
631319799Sdim  uint32_t SectionOneRelocations;
632319799Sdim  uint32_t SectionTwoSize;
633319799Sdim  uint32_t SectionTwoOffset;
634319799Sdim  const ArrayRef<std::vector<UTF16>> StringTable;
635319799Sdim  std::vector<uint32_t> StringTableOffsets;
636319799Sdim  std::vector<uint32_t> DataOffsets;
637319799Sdim  std::vector<uint32_t> RelocationAddresses;
638319799Sdim};
639319799Sdim
640319799SdimWindowsResourceCOFFWriter::WindowsResourceCOFFWriter(
641320397Sdim    COFF::MachineTypes MachineType, const WindowsResourceParser &Parser,
642320397Sdim    Error &E)
643319799Sdim    : MachineType(MachineType), Resources(Parser.getTree()),
644319799Sdim      Data(Parser.getData()), StringTable(Parser.getStringTable()) {
645319799Sdim  performFileLayout();
646319799Sdim
647353358Sdim  OutputBuffer = WritableMemoryBuffer::getNewMemBuffer(
648353358Sdim      FileSize, "internal .obj file created from .res files");
649319799Sdim}
650319799Sdim
651319799Sdimvoid WindowsResourceCOFFWriter::performFileLayout() {
652319799Sdim  // Add size of COFF header.
653320397Sdim  FileSize = COFF::Header16Size;
654319799Sdim
655319799Sdim  // one .rsrc section header for directory tree, another for resource data.
656320397Sdim  FileSize += 2 * COFF::SectionSize;
657319799Sdim
658319799Sdim  performSectionOneLayout();
659319799Sdim  performSectionTwoLayout();
660319799Sdim
661319799Sdim  // We have reached the address of the symbol table.
662319799Sdim  SymbolTableOffset = FileSize;
663319799Sdim
664320397Sdim  FileSize += COFF::Symbol16Size;     // size of the @feat.00 symbol.
665320397Sdim  FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section.
666320397Sdim  FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource.
667319799Sdim  FileSize += 4; // four null bytes for the string table.
668319799Sdim}
669319799Sdim
670319799Sdimvoid WindowsResourceCOFFWriter::performSectionOneLayout() {
671319799Sdim  SectionOneOffset = FileSize;
672319799Sdim
673319799Sdim  SectionOneSize = Resources.getTreeSize();
674319799Sdim  uint32_t CurrentStringOffset = SectionOneSize;
675319799Sdim  uint32_t TotalStringTableSize = 0;
676319799Sdim  for (auto const &String : StringTable) {
677319799Sdim    StringTableOffsets.push_back(CurrentStringOffset);
678319799Sdim    uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t);
679319799Sdim    CurrentStringOffset += StringSize;
680319799Sdim    TotalStringTableSize += StringSize;
681319799Sdim  }
682319799Sdim  SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t));
683319799Sdim
684319799Sdim  // account for the relocations of section one.
685319799Sdim  SectionOneRelocations = FileSize + SectionOneSize;
686319799Sdim  FileSize += SectionOneSize;
687320397Sdim  FileSize +=
688320397Sdim      Data.size() * COFF::RelocationSize; // one relocation for each resource.
689320041Sdim  FileSize = alignTo(FileSize, SECTION_ALIGNMENT);
690319799Sdim}
691319799Sdim
692319799Sdimvoid WindowsResourceCOFFWriter::performSectionTwoLayout() {
693319799Sdim  // add size of .rsrc$2 section, which contains all resource data on 8-byte
694319799Sdim  // alignment.
695319799Sdim  SectionTwoOffset = FileSize;
696319799Sdim  SectionTwoSize = 0;
697319799Sdim  for (auto const &Entry : Data) {
698319799Sdim    DataOffsets.push_back(SectionTwoSize);
699320397Sdim    SectionTwoSize += alignTo(Entry.size(), sizeof(uint64_t));
700319799Sdim  }
701319799Sdim  FileSize += SectionTwoSize;
702320041Sdim  FileSize = alignTo(FileSize, SECTION_ALIGNMENT);
703319799Sdim}
704319799Sdim
705353358Sdimstd::unique_ptr<MemoryBuffer>
706353358SdimWindowsResourceCOFFWriter::write(uint32_t TimeDateStamp) {
707341825Sdim  BufferStart = OutputBuffer->getBufferStart();
708319799Sdim
709353358Sdim  writeCOFFHeader(TimeDateStamp);
710319799Sdim  writeFirstSectionHeader();
711319799Sdim  writeSecondSectionHeader();
712319799Sdim  writeFirstSection();
713319799Sdim  writeSecondSection();
714319799Sdim  writeSymbolTable();
715319799Sdim  writeStringTable();
716319799Sdim
717320397Sdim  return std::move(OutputBuffer);
718319799Sdim}
719319799Sdim
720360784Sdim// According to COFF specification, if the Src has a size equal to Dest,
721360784Sdim// it's okay to *not* copy the trailing zero.
722360784Sdimstatic void coffnamecpy(char (&Dest)[COFF::NameSize], StringRef Src) {
723360784Sdim  assert(Src.size() <= COFF::NameSize &&
724360784Sdim         "Src is not larger than COFF::NameSize");
725360784Sdim  strncpy(Dest, Src.data(), (size_t)COFF::NameSize);
726360784Sdim}
727360784Sdim
728353358Sdimvoid WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp) {
729319799Sdim  // Write the COFF header.
730320397Sdim  auto *Header = reinterpret_cast<coff_file_header *>(BufferStart);
731327952Sdim  Header->Machine = MachineType;
732319799Sdim  Header->NumberOfSections = 2;
733353358Sdim  Header->TimeDateStamp = TimeDateStamp;
734319799Sdim  Header->PointerToSymbolTable = SymbolTableOffset;
735353358Sdim  // One symbol for every resource plus 2 for each section and 1 for @feat.00
736319799Sdim  Header->NumberOfSymbols = Data.size() + 5;
737319799Sdim  Header->SizeOfOptionalHeader = 0;
738353358Sdim  // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it.
739320397Sdim  Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE;
740319799Sdim}
741319799Sdim
742319799Sdimvoid WindowsResourceCOFFWriter::writeFirstSectionHeader() {
743319799Sdim  // Write the first section header.
744320397Sdim  CurrentOffset += sizeof(coff_file_header);
745320397Sdim  auto *SectionOneHeader =
746320397Sdim      reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);
747360784Sdim  coffnamecpy(SectionOneHeader->Name, ".rsrc$01");
748319799Sdim  SectionOneHeader->VirtualSize = 0;
749319799Sdim  SectionOneHeader->VirtualAddress = 0;
750319799Sdim  SectionOneHeader->SizeOfRawData = SectionOneSize;
751319799Sdim  SectionOneHeader->PointerToRawData = SectionOneOffset;
752319799Sdim  SectionOneHeader->PointerToRelocations = SectionOneRelocations;
753319799Sdim  SectionOneHeader->PointerToLinenumbers = 0;
754319799Sdim  SectionOneHeader->NumberOfRelocations = Data.size();
755319799Sdim  SectionOneHeader->NumberOfLinenumbers = 0;
756320397Sdim  SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
757320397Sdim  SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;
758319799Sdim}
759319799Sdim
760319799Sdimvoid WindowsResourceCOFFWriter::writeSecondSectionHeader() {
761319799Sdim  // Write the second section header.
762320397Sdim  CurrentOffset += sizeof(coff_section);
763320397Sdim  auto *SectionTwoHeader =
764320397Sdim      reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);
765360784Sdim  coffnamecpy(SectionTwoHeader->Name, ".rsrc$02");
766319799Sdim  SectionTwoHeader->VirtualSize = 0;
767319799Sdim  SectionTwoHeader->VirtualAddress = 0;
768319799Sdim  SectionTwoHeader->SizeOfRawData = SectionTwoSize;
769319799Sdim  SectionTwoHeader->PointerToRawData = SectionTwoOffset;
770319799Sdim  SectionTwoHeader->PointerToRelocations = 0;
771319799Sdim  SectionTwoHeader->PointerToLinenumbers = 0;
772319799Sdim  SectionTwoHeader->NumberOfRelocations = 0;
773319799Sdim  SectionTwoHeader->NumberOfLinenumbers = 0;
774320397Sdim  SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
775320397Sdim  SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;
776319799Sdim}
777319799Sdim
778319799Sdimvoid WindowsResourceCOFFWriter::writeFirstSection() {
779319799Sdim  // Write section one.
780320397Sdim  CurrentOffset += sizeof(coff_section);
781319799Sdim
782319799Sdim  writeDirectoryTree();
783319799Sdim  writeDirectoryStringTable();
784319799Sdim  writeFirstSectionRelocations();
785320041Sdim
786320041Sdim  CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);
787319799Sdim}
788319799Sdim
789319799Sdimvoid WindowsResourceCOFFWriter::writeSecondSection() {
790319799Sdim  // Now write the .rsrc$02 section.
791319799Sdim  for (auto const &RawDataEntry : Data) {
792344779Sdim    llvm::copy(RawDataEntry, BufferStart + CurrentOffset);
793320041Sdim    CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t));
794319799Sdim  }
795320041Sdim
796320041Sdim  CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);
797319799Sdim}
798319799Sdim
799319799Sdimvoid WindowsResourceCOFFWriter::writeSymbolTable() {
800319799Sdim  // Now write the symbol table.
801319799Sdim  // First, the feat symbol.
802320397Sdim  auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
803360784Sdim  coffnamecpy(Symbol->Name.ShortName, "@feat.00");
804319799Sdim  Symbol->Value = 0x11;
805319799Sdim  Symbol->SectionNumber = 0xffff;
806320397Sdim  Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
807320397Sdim  Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
808319799Sdim  Symbol->NumberOfAuxSymbols = 0;
809320397Sdim  CurrentOffset += sizeof(coff_symbol16);
810319799Sdim
811319799Sdim  // Now write the .rsrc1 symbol + aux.
812320397Sdim  Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
813360784Sdim  coffnamecpy(Symbol->Name.ShortName, ".rsrc$01");
814319799Sdim  Symbol->Value = 0;
815319799Sdim  Symbol->SectionNumber = 1;
816320397Sdim  Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
817320397Sdim  Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
818319799Sdim  Symbol->NumberOfAuxSymbols = 1;
819320397Sdim  CurrentOffset += sizeof(coff_symbol16);
820320397Sdim  auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +
821320397Sdim                                                              CurrentOffset);
822319799Sdim  Aux->Length = SectionOneSize;
823319799Sdim  Aux->NumberOfRelocations = Data.size();
824319799Sdim  Aux->NumberOfLinenumbers = 0;
825319799Sdim  Aux->CheckSum = 0;
826319799Sdim  Aux->NumberLowPart = 0;
827319799Sdim  Aux->Selection = 0;
828320397Sdim  CurrentOffset += sizeof(coff_aux_section_definition);
829319799Sdim
830319799Sdim  // Now write the .rsrc2 symbol + aux.
831320397Sdim  Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
832360784Sdim  coffnamecpy(Symbol->Name.ShortName, ".rsrc$02");
833319799Sdim  Symbol->Value = 0;
834319799Sdim  Symbol->SectionNumber = 2;
835320397Sdim  Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
836320397Sdim  Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
837319799Sdim  Symbol->NumberOfAuxSymbols = 1;
838320397Sdim  CurrentOffset += sizeof(coff_symbol16);
839320397Sdim  Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +
840320397Sdim                                                        CurrentOffset);
841319799Sdim  Aux->Length = SectionTwoSize;
842319799Sdim  Aux->NumberOfRelocations = 0;
843319799Sdim  Aux->NumberOfLinenumbers = 0;
844319799Sdim  Aux->CheckSum = 0;
845319799Sdim  Aux->NumberLowPart = 0;
846319799Sdim  Aux->Selection = 0;
847320397Sdim  CurrentOffset += sizeof(coff_aux_section_definition);
848319799Sdim
849319799Sdim  // Now write a symbol for each relocation.
850319799Sdim  for (unsigned i = 0; i < Data.size(); i++) {
851327952Sdim    auto RelocationName = formatv("$R{0:X-6}", i & 0xffffff).sstr<COFF::NameSize>();
852320397Sdim    Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
853360784Sdim    coffnamecpy(Symbol->Name.ShortName, RelocationName);
854319799Sdim    Symbol->Value = DataOffsets[i];
855320572Sdim    Symbol->SectionNumber = 2;
856320397Sdim    Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
857320397Sdim    Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
858319799Sdim    Symbol->NumberOfAuxSymbols = 0;
859320397Sdim    CurrentOffset += sizeof(coff_symbol16);
860319799Sdim  }
861319799Sdim}
862319799Sdim
863319799Sdimvoid WindowsResourceCOFFWriter::writeStringTable() {
864319799Sdim  // Just 4 null bytes for the string table.
865320041Sdim  auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset);
866320041Sdim  memset(COFFStringTable, 0, 4);
867319799Sdim}
868319799Sdim
869319799Sdimvoid WindowsResourceCOFFWriter::writeDirectoryTree() {
870319799Sdim  // Traverse parsed resource tree breadth-first and write the corresponding
871319799Sdim  // COFF objects.
872319799Sdim  std::queue<const WindowsResourceParser::TreeNode *> Queue;
873319799Sdim  Queue.push(&Resources);
874320397Sdim  uint32_t NextLevelOffset =
875320397Sdim      sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() +
876320397Sdim                                         Resources.getIDChildren().size()) *
877320397Sdim                                            sizeof(coff_resource_dir_entry);
878319799Sdim  std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder;
879319799Sdim  uint32_t CurrentRelativeOffset = 0;
880319799Sdim
881319799Sdim  while (!Queue.empty()) {
882319799Sdim    auto CurrentNode = Queue.front();
883319799Sdim    Queue.pop();
884320397Sdim    auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart +
885320397Sdim                                                              CurrentOffset);
886319799Sdim    Table->Characteristics = CurrentNode->getCharacteristics();
887319799Sdim    Table->TimeDateStamp = 0;
888319799Sdim    Table->MajorVersion = CurrentNode->getMajorVersion();
889319799Sdim    Table->MinorVersion = CurrentNode->getMinorVersion();
890319799Sdim    auto &IDChildren = CurrentNode->getIDChildren();
891319799Sdim    auto &StringChildren = CurrentNode->getStringChildren();
892319799Sdim    Table->NumberOfNameEntries = StringChildren.size();
893319799Sdim    Table->NumberOfIDEntries = IDChildren.size();
894320397Sdim    CurrentOffset += sizeof(coff_resource_dir_table);
895320397Sdim    CurrentRelativeOffset += sizeof(coff_resource_dir_table);
896319799Sdim
897319799Sdim    // Write the directory entries immediately following each directory table.
898319799Sdim    for (auto const &Child : StringChildren) {
899320397Sdim      auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +
900320397Sdim                                                                CurrentOffset);
901320970Sdim      Entry->Identifier.setNameOffset(
902320970Sdim          StringTableOffsets[Child.second->getStringIndex()]);
903319799Sdim      if (Child.second->checkIsDataNode()) {
904319799Sdim        Entry->Offset.DataEntryOffset = NextLevelOffset;
905320397Sdim        NextLevelOffset += sizeof(coff_resource_data_entry);
906319799Sdim        DataEntriesTreeOrder.push_back(Child.second.get());
907319799Sdim      } else {
908319799Sdim        Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);
909320397Sdim        NextLevelOffset += sizeof(coff_resource_dir_table) +
910319799Sdim                           (Child.second->getStringChildren().size() +
911319799Sdim                            Child.second->getIDChildren().size()) *
912320397Sdim                               sizeof(coff_resource_dir_entry);
913319799Sdim        Queue.push(Child.second.get());
914319799Sdim      }
915320397Sdim      CurrentOffset += sizeof(coff_resource_dir_entry);
916320397Sdim      CurrentRelativeOffset += sizeof(coff_resource_dir_entry);
917319799Sdim    }
918319799Sdim    for (auto const &Child : IDChildren) {
919320397Sdim      auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +
920320397Sdim                                                                CurrentOffset);
921319799Sdim      Entry->Identifier.ID = Child.first;
922319799Sdim      if (Child.second->checkIsDataNode()) {
923319799Sdim        Entry->Offset.DataEntryOffset = NextLevelOffset;
924320397Sdim        NextLevelOffset += sizeof(coff_resource_data_entry);
925319799Sdim        DataEntriesTreeOrder.push_back(Child.second.get());
926319799Sdim      } else {
927319799Sdim        Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);
928320397Sdim        NextLevelOffset += sizeof(coff_resource_dir_table) +
929319799Sdim                           (Child.second->getStringChildren().size() +
930319799Sdim                            Child.second->getIDChildren().size()) *
931320397Sdim                               sizeof(coff_resource_dir_entry);
932319799Sdim        Queue.push(Child.second.get());
933319799Sdim      }
934320397Sdim      CurrentOffset += sizeof(coff_resource_dir_entry);
935320397Sdim      CurrentRelativeOffset += sizeof(coff_resource_dir_entry);
936319799Sdim    }
937319799Sdim  }
938319799Sdim
939319799Sdim  RelocationAddresses.resize(Data.size());
940319799Sdim  // Now write all the resource data entries.
941319799Sdim  for (auto DataNodes : DataEntriesTreeOrder) {
942320397Sdim    auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart +
943320397Sdim                                                               CurrentOffset);
944319799Sdim    RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset;
945319799Sdim    Entry->DataRVA = 0; // Set to zero because it is a relocation.
946319799Sdim    Entry->DataSize = Data[DataNodes->getDataIndex()].size();
947319799Sdim    Entry->Codepage = 0;
948319799Sdim    Entry->Reserved = 0;
949320397Sdim    CurrentOffset += sizeof(coff_resource_data_entry);
950320397Sdim    CurrentRelativeOffset += sizeof(coff_resource_data_entry);
951319799Sdim  }
952319799Sdim}
953319799Sdim
954319799Sdimvoid WindowsResourceCOFFWriter::writeDirectoryStringTable() {
955319799Sdim  // Now write the directory string table for .rsrc$01
956319799Sdim  uint32_t TotalStringTableSize = 0;
957320041Sdim  for (auto &String : StringTable) {
958319799Sdim    uint16_t Length = String.size();
959320041Sdim    support::endian::write16le(BufferStart + CurrentOffset, Length);
960320041Sdim    CurrentOffset += sizeof(uint16_t);
961320041Sdim    auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset);
962344779Sdim    llvm::copy(String, Start);
963320041Sdim    CurrentOffset += Length * sizeof(UTF16);
964319799Sdim    TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t);
965319799Sdim  }
966320041Sdim  CurrentOffset +=
967319799Sdim      alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize;
968319799Sdim}
969319799Sdim
970319799Sdimvoid WindowsResourceCOFFWriter::writeFirstSectionRelocations() {
971319799Sdim
972319799Sdim  // Now write the relocations for .rsrc$01
973319799Sdim  // Five symbols already in table before we start, @feat.00 and 2 for each
974319799Sdim  // .rsrc section.
975319799Sdim  uint32_t NextSymbolIndex = 5;
976319799Sdim  for (unsigned i = 0; i < Data.size(); i++) {
977320397Sdim    auto *Reloc =
978320397Sdim        reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset);
979319799Sdim    Reloc->VirtualAddress = RelocationAddresses[i];
980319799Sdim    Reloc->SymbolTableIndex = NextSymbolIndex++;
981319799Sdim    switch (MachineType) {
982320397Sdim    case COFF::IMAGE_FILE_MACHINE_ARMNT:
983320397Sdim      Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB;
984319799Sdim      break;
985320397Sdim    case COFF::IMAGE_FILE_MACHINE_AMD64:
986320397Sdim      Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB;
987319799Sdim      break;
988320397Sdim    case COFF::IMAGE_FILE_MACHINE_I386:
989320397Sdim      Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB;
990319799Sdim      break;
991327952Sdim    case COFF::IMAGE_FILE_MACHINE_ARM64:
992327952Sdim      Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB;
993327952Sdim      break;
994319799Sdim    default:
995327952Sdim      llvm_unreachable("unknown machine type");
996319799Sdim    }
997320397Sdim    CurrentOffset += sizeof(coff_relocation);
998319799Sdim  }
999319799Sdim}
1000319799Sdim
1001320397SdimExpected<std::unique_ptr<MemoryBuffer>>
1002320397SdimwriteWindowsResourceCOFF(COFF::MachineTypes MachineType,
1003353358Sdim                         const WindowsResourceParser &Parser,
1004353358Sdim                         uint32_t TimeDateStamp) {
1005319799Sdim  Error E = Error::success();
1006320397Sdim  WindowsResourceCOFFWriter Writer(MachineType, Parser, E);
1007319799Sdim  if (E)
1008320397Sdim    return std::move(E);
1009353358Sdim  return Writer.write(TimeDateStamp);
1010319799Sdim}
1011319799Sdim
1012318663Sdim} // namespace object
1013318663Sdim} // namespace llvm
1014