WindowsResource.cpp revision 320572
1//===-- WindowsResource.cpp -------------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements the .res file class.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/Object/WindowsResource.h"
15#include "llvm/Object/COFF.h"
16#include "llvm/Support/FileOutputBuffer.h"
17#include "llvm/Support/MathExtras.h"
18#include <ctime>
19#include <queue>
20#include <sstream>
21#include <system_error>
22
23using namespace llvm;
24using namespace object;
25
26namespace llvm {
27namespace object {
28
29#define RETURN_IF_ERROR(X)                                                     \
30  if (auto EC = X)                                                             \
31    return EC;
32
33const uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t);
34
35// COFF files seem to be inconsistent with alignment between sections, just use
36// 8-byte because it makes everyone happy.
37const uint32_t SECTION_ALIGNMENT = sizeof(uint64_t);
38
39uint32_t WindowsResourceParser::TreeNode::StringCount = 0;
40uint32_t WindowsResourceParser::TreeNode::DataCount = 0;
41
42WindowsResource::WindowsResource(MemoryBufferRef Source)
43    : Binary(Binary::ID_WinRes, Source) {
44  size_t LeadingSize = WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE;
45  BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize),
46                         support::little);
47}
48
49Expected<std::unique_ptr<WindowsResource>>
50WindowsResource::createWindowsResource(MemoryBufferRef Source) {
51  if (Source.getBufferSize() < WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE)
52    return make_error<GenericBinaryError>(
53        "File too small to be a resource file",
54        object_error::invalid_file_type);
55  std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source));
56  return std::move(Ret);
57}
58
59Expected<ResourceEntryRef> WindowsResource::getHeadEntry() {
60  Error Err = Error::success();
61  auto Ref = ResourceEntryRef(BinaryStreamRef(BBS), this, Err);
62  if (Err)
63    return std::move(Err);
64  return Ref;
65}
66
67ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref,
68                                   const WindowsResource *Owner, Error &Err)
69    : Reader(Ref), OwningRes(Owner) {
70  if (loadNext())
71    Err = make_error<GenericBinaryError>("Could not read first entry.\n",
72                                         object_error::unexpected_eof);
73}
74
75Error ResourceEntryRef::moveNext(bool &End) {
76  // Reached end of all the entries.
77  if (Reader.bytesRemaining() == 0) {
78    End = true;
79    return Error::success();
80  }
81  RETURN_IF_ERROR(loadNext());
82
83  return Error::success();
84}
85
86static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID,
87                            ArrayRef<UTF16> &Str, bool &IsString) {
88  uint16_t IDFlag;
89  RETURN_IF_ERROR(Reader.readInteger(IDFlag));
90  IsString = IDFlag != 0xffff;
91
92  if (IsString) {
93    Reader.setOffset(
94        Reader.getOffset() -
95        sizeof(uint16_t)); // Re-read the bytes which we used to check the flag.
96    RETURN_IF_ERROR(Reader.readWideString(Str));
97  } else
98    RETURN_IF_ERROR(Reader.readInteger(ID));
99
100  return Error::success();
101}
102
103Error ResourceEntryRef::loadNext() {
104  const WinResHeaderPrefix *Prefix;
105  RETURN_IF_ERROR(Reader.readObject(Prefix));
106
107  if (Prefix->HeaderSize < MIN_HEADER_SIZE)
108    return make_error<GenericBinaryError>("Header size is too small.",
109                                          object_error::parse_failed);
110
111  RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType));
112
113  RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName));
114
115  RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_HEADER_ALIGNMENT));
116
117  RETURN_IF_ERROR(Reader.readObject(Suffix));
118
119  RETURN_IF_ERROR(Reader.readArray(Data, Prefix->DataSize));
120
121  RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_DATA_ALIGNMENT));
122
123  return Error::success();
124}
125
126WindowsResourceParser::WindowsResourceParser() : Root(false) {}
127
128Error WindowsResourceParser::parse(WindowsResource *WR) {
129  auto EntryOrErr = WR->getHeadEntry();
130  if (!EntryOrErr)
131    return EntryOrErr.takeError();
132
133  ResourceEntryRef Entry = EntryOrErr.get();
134  bool End = false;
135  while (!End) {
136    Data.push_back(Entry.getData());
137
138    bool IsNewTypeString = false;
139    bool IsNewNameString = false;
140
141    Root.addEntry(Entry, IsNewTypeString, IsNewNameString);
142
143    if (IsNewTypeString)
144      StringTable.push_back(Entry.getTypeString());
145
146    if (IsNewNameString)
147      StringTable.push_back(Entry.getNameString());
148
149    RETURN_IF_ERROR(Entry.moveNext(End));
150  }
151
152  return Error::success();
153}
154
155void WindowsResourceParser::printTree(raw_ostream &OS) const {
156  ScopedPrinter Writer(OS);
157  Root.print(Writer, "Resource Tree");
158}
159
160void WindowsResourceParser::TreeNode::addEntry(const ResourceEntryRef &Entry,
161                                               bool &IsNewTypeString,
162                                               bool &IsNewNameString) {
163  TreeNode &TypeNode = addTypeNode(Entry, IsNewTypeString);
164  TreeNode &NameNode = TypeNode.addNameNode(Entry, IsNewNameString);
165  NameNode.addLanguageNode(Entry);
166}
167
168WindowsResourceParser::TreeNode::TreeNode(bool IsStringNode) {
169  if (IsStringNode)
170    StringIndex = StringCount++;
171}
172
173WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion,
174                                          uint16_t MinorVersion,
175                                          uint32_t Characteristics)
176    : IsDataNode(true), MajorVersion(MajorVersion), MinorVersion(MinorVersion),
177      Characteristics(Characteristics) {
178    DataIndex = DataCount++;
179}
180
181std::unique_ptr<WindowsResourceParser::TreeNode>
182WindowsResourceParser::TreeNode::createStringNode() {
183  return std::unique_ptr<TreeNode>(new TreeNode(true));
184}
185
186std::unique_ptr<WindowsResourceParser::TreeNode>
187WindowsResourceParser::TreeNode::createIDNode() {
188  return std::unique_ptr<TreeNode>(new TreeNode(false));
189}
190
191std::unique_ptr<WindowsResourceParser::TreeNode>
192WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion,
193                                                uint16_t MinorVersion,
194                                                uint32_t Characteristics) {
195  return std::unique_ptr<TreeNode>(
196      new TreeNode(MajorVersion, MinorVersion, Characteristics));
197}
198
199WindowsResourceParser::TreeNode &
200WindowsResourceParser::TreeNode::addTypeNode(const ResourceEntryRef &Entry,
201                                             bool &IsNewTypeString) {
202  if (Entry.checkTypeString())
203    return addChild(Entry.getTypeString(), IsNewTypeString);
204  else
205    return addChild(Entry.getTypeID());
206}
207
208WindowsResourceParser::TreeNode &
209WindowsResourceParser::TreeNode::addNameNode(const ResourceEntryRef &Entry,
210                                             bool &IsNewNameString) {
211  if (Entry.checkNameString())
212    return addChild(Entry.getNameString(), IsNewNameString);
213  else
214    return addChild(Entry.getNameID());
215}
216
217WindowsResourceParser::TreeNode &
218WindowsResourceParser::TreeNode::addLanguageNode(
219    const ResourceEntryRef &Entry) {
220  return addChild(Entry.getLanguage(), true, Entry.getMajorVersion(),
221                  Entry.getMinorVersion(), Entry.getCharacteristics());
222}
223
224WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addChild(
225    uint32_t ID, bool IsDataNode, uint16_t MajorVersion, uint16_t MinorVersion,
226    uint32_t Characteristics) {
227  auto Child = IDChildren.find(ID);
228  if (Child == IDChildren.end()) {
229    auto NewChild =
230        IsDataNode ? createDataNode(MajorVersion, MinorVersion, Characteristics)
231                   : createIDNode();
232    WindowsResourceParser::TreeNode &Node = *NewChild;
233    IDChildren.emplace(ID, std::move(NewChild));
234    return Node;
235  } else
236    return *(Child->second);
237}
238
239WindowsResourceParser::TreeNode &
240WindowsResourceParser::TreeNode::addChild(ArrayRef<UTF16> NameRef,
241                                          bool &IsNewString) {
242  std::string NameString;
243  ArrayRef<UTF16> CorrectedName;
244  std::vector<UTF16> EndianCorrectedName;
245  if (sys::IsBigEndianHost) {
246    EndianCorrectedName.resize(NameRef.size() + 1);
247    std::copy(NameRef.begin(), NameRef.end(), EndianCorrectedName.begin() + 1);
248    EndianCorrectedName[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED;
249    CorrectedName = makeArrayRef(EndianCorrectedName);
250  } else
251    CorrectedName = NameRef;
252  convertUTF16ToUTF8String(CorrectedName, NameString);
253
254  auto Child = StringChildren.find(NameString);
255  if (Child == StringChildren.end()) {
256    auto NewChild = createStringNode();
257    IsNewString = true;
258    WindowsResourceParser::TreeNode &Node = *NewChild;
259    StringChildren.emplace(NameString, std::move(NewChild));
260    return Node;
261  } else
262    return *(Child->second);
263}
264
265void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer,
266                                            StringRef Name) const {
267  ListScope NodeScope(Writer, Name);
268  for (auto const &Child : StringChildren) {
269    Child.second->print(Writer, Child.first);
270  }
271  for (auto const &Child : IDChildren) {
272    Child.second->print(Writer, to_string(Child.first));
273  }
274}
275
276// This function returns the size of the entire resource tree, including
277// directory tables, directory entries, and data entries.  It does not include
278// the directory strings or the relocations of the .rsrc section.
279uint32_t WindowsResourceParser::TreeNode::getTreeSize() const {
280  uint32_t Size = (IDChildren.size() + StringChildren.size()) *
281                  sizeof(coff_resource_dir_entry);
282
283  // Reached a node pointing to a data entry.
284  if (IsDataNode) {
285    Size += sizeof(coff_resource_data_entry);
286    return Size;
287  }
288
289  // If the node does not point to data, it must have a directory table pointing
290  // to other nodes.
291  Size += sizeof(coff_resource_dir_table);
292
293  for (auto const &Child : StringChildren) {
294    Size += Child.second->getTreeSize();
295  }
296  for (auto const &Child : IDChildren) {
297    Size += Child.second->getTreeSize();
298  }
299  return Size;
300}
301
302class WindowsResourceCOFFWriter {
303public:
304  WindowsResourceCOFFWriter(COFF::MachineTypes MachineType,
305                            const WindowsResourceParser &Parser, Error &E);
306  std::unique_ptr<MemoryBuffer> write();
307
308private:
309  void performFileLayout();
310  void performSectionOneLayout();
311  void performSectionTwoLayout();
312  void writeCOFFHeader();
313  void writeFirstSectionHeader();
314  void writeSecondSectionHeader();
315  void writeFirstSection();
316  void writeSecondSection();
317  void writeSymbolTable();
318  void writeStringTable();
319  void writeDirectoryTree();
320  void writeDirectoryStringTable();
321  void writeFirstSectionRelocations();
322  std::unique_ptr<MemoryBuffer> OutputBuffer;
323  char *BufferStart;
324  uint64_t CurrentOffset = 0;
325  COFF::MachineTypes MachineType;
326  const WindowsResourceParser::TreeNode &Resources;
327  const ArrayRef<std::vector<uint8_t>> Data;
328  uint64_t FileSize;
329  uint32_t SymbolTableOffset;
330  uint32_t SectionOneSize;
331  uint32_t SectionOneOffset;
332  uint32_t SectionOneRelocations;
333  uint32_t SectionTwoSize;
334  uint32_t SectionTwoOffset;
335  const ArrayRef<std::vector<UTF16>> StringTable;
336  std::vector<uint32_t> StringTableOffsets;
337  std::vector<uint32_t> DataOffsets;
338  std::vector<uint32_t> RelocationAddresses;
339};
340
341WindowsResourceCOFFWriter::WindowsResourceCOFFWriter(
342    COFF::MachineTypes MachineType, const WindowsResourceParser &Parser,
343    Error &E)
344    : MachineType(MachineType), Resources(Parser.getTree()),
345      Data(Parser.getData()), StringTable(Parser.getStringTable()) {
346  performFileLayout();
347
348  OutputBuffer = MemoryBuffer::getNewMemBuffer(FileSize);
349}
350
351void WindowsResourceCOFFWriter::performFileLayout() {
352  // Add size of COFF header.
353  FileSize = COFF::Header16Size;
354
355  // one .rsrc section header for directory tree, another for resource data.
356  FileSize += 2 * COFF::SectionSize;
357
358  performSectionOneLayout();
359  performSectionTwoLayout();
360
361  // We have reached the address of the symbol table.
362  SymbolTableOffset = FileSize;
363
364  FileSize += COFF::Symbol16Size;     // size of the @feat.00 symbol.
365  FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section.
366  FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource.
367  FileSize += 4; // four null bytes for the string table.
368}
369
370void WindowsResourceCOFFWriter::performSectionOneLayout() {
371  SectionOneOffset = FileSize;
372
373  SectionOneSize = Resources.getTreeSize();
374  uint32_t CurrentStringOffset = SectionOneSize;
375  uint32_t TotalStringTableSize = 0;
376  for (auto const &String : StringTable) {
377    StringTableOffsets.push_back(CurrentStringOffset);
378    uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t);
379    CurrentStringOffset += StringSize;
380    TotalStringTableSize += StringSize;
381  }
382  SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t));
383
384  // account for the relocations of section one.
385  SectionOneRelocations = FileSize + SectionOneSize;
386  FileSize += SectionOneSize;
387  FileSize +=
388      Data.size() * COFF::RelocationSize; // one relocation for each resource.
389  FileSize = alignTo(FileSize, SECTION_ALIGNMENT);
390}
391
392void WindowsResourceCOFFWriter::performSectionTwoLayout() {
393  // add size of .rsrc$2 section, which contains all resource data on 8-byte
394  // alignment.
395  SectionTwoOffset = FileSize;
396  SectionTwoSize = 0;
397  for (auto const &Entry : Data) {
398    DataOffsets.push_back(SectionTwoSize);
399    SectionTwoSize += alignTo(Entry.size(), sizeof(uint64_t));
400  }
401  FileSize += SectionTwoSize;
402  FileSize = alignTo(FileSize, SECTION_ALIGNMENT);
403}
404
405static std::time_t getTime() {
406  std::time_t Now = time(nullptr);
407  if (Now < 0 || !isUInt<32>(Now))
408    return UINT32_MAX;
409  return Now;
410}
411
412std::unique_ptr<MemoryBuffer> WindowsResourceCOFFWriter::write() {
413  BufferStart = const_cast<char *>(OutputBuffer->getBufferStart());
414
415  writeCOFFHeader();
416  writeFirstSectionHeader();
417  writeSecondSectionHeader();
418  writeFirstSection();
419  writeSecondSection();
420  writeSymbolTable();
421  writeStringTable();
422
423  return std::move(OutputBuffer);
424}
425
426void WindowsResourceCOFFWriter::writeCOFFHeader() {
427  // Write the COFF header.
428  auto *Header = reinterpret_cast<coff_file_header *>(BufferStart);
429  switch (MachineType) {
430  case COFF::IMAGE_FILE_MACHINE_ARMNT:
431    Header->Machine = COFF::IMAGE_FILE_MACHINE_ARMNT;
432    break;
433  case COFF::IMAGE_FILE_MACHINE_AMD64:
434    Header->Machine = COFF::IMAGE_FILE_MACHINE_AMD64;
435    break;
436  case COFF::IMAGE_FILE_MACHINE_I386:
437    Header->Machine = COFF::IMAGE_FILE_MACHINE_I386;
438    break;
439  default:
440    Header->Machine = COFF::IMAGE_FILE_MACHINE_UNKNOWN;
441  }
442  Header->NumberOfSections = 2;
443  Header->TimeDateStamp = getTime();
444  Header->PointerToSymbolTable = SymbolTableOffset;
445  // One symbol for every resource plus 2 for each section and @feat.00
446  Header->NumberOfSymbols = Data.size() + 5;
447  Header->SizeOfOptionalHeader = 0;
448  Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE;
449}
450
451void WindowsResourceCOFFWriter::writeFirstSectionHeader() {
452  // Write the first section header.
453  CurrentOffset += sizeof(coff_file_header);
454  auto *SectionOneHeader =
455      reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);
456  strncpy(SectionOneHeader->Name, ".rsrc$01", (size_t)COFF::NameSize);
457  SectionOneHeader->VirtualSize = 0;
458  SectionOneHeader->VirtualAddress = 0;
459  SectionOneHeader->SizeOfRawData = SectionOneSize;
460  SectionOneHeader->PointerToRawData = SectionOneOffset;
461  SectionOneHeader->PointerToRelocations = SectionOneRelocations;
462  SectionOneHeader->PointerToLinenumbers = 0;
463  SectionOneHeader->NumberOfRelocations = Data.size();
464  SectionOneHeader->NumberOfLinenumbers = 0;
465  SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
466  SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;
467}
468
469void WindowsResourceCOFFWriter::writeSecondSectionHeader() {
470  // Write the second section header.
471  CurrentOffset += sizeof(coff_section);
472  auto *SectionTwoHeader =
473      reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);
474  strncpy(SectionTwoHeader->Name, ".rsrc$02", (size_t)COFF::NameSize);
475  SectionTwoHeader->VirtualSize = 0;
476  SectionTwoHeader->VirtualAddress = 0;
477  SectionTwoHeader->SizeOfRawData = SectionTwoSize;
478  SectionTwoHeader->PointerToRawData = SectionTwoOffset;
479  SectionTwoHeader->PointerToRelocations = 0;
480  SectionTwoHeader->PointerToLinenumbers = 0;
481  SectionTwoHeader->NumberOfRelocations = 0;
482  SectionTwoHeader->NumberOfLinenumbers = 0;
483  SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
484  SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;
485}
486
487void WindowsResourceCOFFWriter::writeFirstSection() {
488  // Write section one.
489  CurrentOffset += sizeof(coff_section);
490
491  writeDirectoryTree();
492  writeDirectoryStringTable();
493  writeFirstSectionRelocations();
494
495  CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);
496}
497
498void WindowsResourceCOFFWriter::writeSecondSection() {
499  // Now write the .rsrc$02 section.
500  for (auto const &RawDataEntry : Data) {
501    std::copy(RawDataEntry.begin(), RawDataEntry.end(),
502              BufferStart + CurrentOffset);
503    CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t));
504  }
505
506  CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);
507}
508
509void WindowsResourceCOFFWriter::writeSymbolTable() {
510  // Now write the symbol table.
511  // First, the feat symbol.
512  auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
513  strncpy(Symbol->Name.ShortName, "@feat.00", (size_t)COFF::NameSize);
514  Symbol->Value = 0x11;
515  Symbol->SectionNumber = 0xffff;
516  Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
517  Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
518  Symbol->NumberOfAuxSymbols = 0;
519  CurrentOffset += sizeof(coff_symbol16);
520
521  // Now write the .rsrc1 symbol + aux.
522  Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
523  strncpy(Symbol->Name.ShortName, ".rsrc$01", (size_t)COFF::NameSize);
524  Symbol->Value = 0;
525  Symbol->SectionNumber = 1;
526  Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
527  Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
528  Symbol->NumberOfAuxSymbols = 1;
529  CurrentOffset += sizeof(coff_symbol16);
530  auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +
531                                                              CurrentOffset);
532  Aux->Length = SectionOneSize;
533  Aux->NumberOfRelocations = Data.size();
534  Aux->NumberOfLinenumbers = 0;
535  Aux->CheckSum = 0;
536  Aux->NumberLowPart = 0;
537  Aux->Selection = 0;
538  CurrentOffset += sizeof(coff_aux_section_definition);
539
540  // Now write the .rsrc2 symbol + aux.
541  Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
542  strncpy(Symbol->Name.ShortName, ".rsrc$02", (size_t)COFF::NameSize);
543  Symbol->Value = 0;
544  Symbol->SectionNumber = 2;
545  Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
546  Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
547  Symbol->NumberOfAuxSymbols = 1;
548  CurrentOffset += sizeof(coff_symbol16);
549  Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +
550                                                        CurrentOffset);
551  Aux->Length = SectionTwoSize;
552  Aux->NumberOfRelocations = 0;
553  Aux->NumberOfLinenumbers = 0;
554  Aux->CheckSum = 0;
555  Aux->NumberLowPart = 0;
556  Aux->Selection = 0;
557  CurrentOffset += sizeof(coff_aux_section_definition);
558
559  // Now write a symbol for each relocation.
560  for (unsigned i = 0; i < Data.size(); i++) {
561    char RelocationName[9];
562    sprintf(RelocationName, "$R%06X", DataOffsets[i]);
563    Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
564    strncpy(Symbol->Name.ShortName, RelocationName, (size_t)COFF::NameSize);
565    Symbol->Value = DataOffsets[i];
566    Symbol->SectionNumber = 2;
567    Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
568    Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
569    Symbol->NumberOfAuxSymbols = 0;
570    CurrentOffset += sizeof(coff_symbol16);
571  }
572}
573
574void WindowsResourceCOFFWriter::writeStringTable() {
575  // Just 4 null bytes for the string table.
576  auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset);
577  memset(COFFStringTable, 0, 4);
578}
579
580void WindowsResourceCOFFWriter::writeDirectoryTree() {
581  // Traverse parsed resource tree breadth-first and write the corresponding
582  // COFF objects.
583  std::queue<const WindowsResourceParser::TreeNode *> Queue;
584  Queue.push(&Resources);
585  uint32_t NextLevelOffset =
586      sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() +
587                                         Resources.getIDChildren().size()) *
588                                            sizeof(coff_resource_dir_entry);
589  std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder;
590  uint32_t CurrentRelativeOffset = 0;
591
592  while (!Queue.empty()) {
593    auto CurrentNode = Queue.front();
594    Queue.pop();
595    auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart +
596                                                              CurrentOffset);
597    Table->Characteristics = CurrentNode->getCharacteristics();
598    Table->TimeDateStamp = 0;
599    Table->MajorVersion = CurrentNode->getMajorVersion();
600    Table->MinorVersion = CurrentNode->getMinorVersion();
601    auto &IDChildren = CurrentNode->getIDChildren();
602    auto &StringChildren = CurrentNode->getStringChildren();
603    Table->NumberOfNameEntries = StringChildren.size();
604    Table->NumberOfIDEntries = IDChildren.size();
605    CurrentOffset += sizeof(coff_resource_dir_table);
606    CurrentRelativeOffset += sizeof(coff_resource_dir_table);
607
608    // Write the directory entries immediately following each directory table.
609    for (auto const &Child : StringChildren) {
610      auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +
611                                                                CurrentOffset);
612      Entry->Identifier.NameOffset =
613          StringTableOffsets[Child.second->getStringIndex()];
614      if (Child.second->checkIsDataNode()) {
615        Entry->Offset.DataEntryOffset = NextLevelOffset;
616        NextLevelOffset += sizeof(coff_resource_data_entry);
617        DataEntriesTreeOrder.push_back(Child.second.get());
618      } else {
619        Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);
620        NextLevelOffset += sizeof(coff_resource_dir_table) +
621                           (Child.second->getStringChildren().size() +
622                            Child.second->getIDChildren().size()) *
623                               sizeof(coff_resource_dir_entry);
624        Queue.push(Child.second.get());
625      }
626      CurrentOffset += sizeof(coff_resource_dir_entry);
627      CurrentRelativeOffset += sizeof(coff_resource_dir_entry);
628    }
629    for (auto const &Child : IDChildren) {
630      auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +
631                                                                CurrentOffset);
632      Entry->Identifier.ID = Child.first;
633      if (Child.second->checkIsDataNode()) {
634        Entry->Offset.DataEntryOffset = NextLevelOffset;
635        NextLevelOffset += sizeof(coff_resource_data_entry);
636        DataEntriesTreeOrder.push_back(Child.second.get());
637      } else {
638        Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);
639        NextLevelOffset += sizeof(coff_resource_dir_table) +
640                           (Child.second->getStringChildren().size() +
641                            Child.second->getIDChildren().size()) *
642                               sizeof(coff_resource_dir_entry);
643        Queue.push(Child.second.get());
644      }
645      CurrentOffset += sizeof(coff_resource_dir_entry);
646      CurrentRelativeOffset += sizeof(coff_resource_dir_entry);
647    }
648  }
649
650  RelocationAddresses.resize(Data.size());
651  // Now write all the resource data entries.
652  for (auto DataNodes : DataEntriesTreeOrder) {
653    auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart +
654                                                               CurrentOffset);
655    RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset;
656    Entry->DataRVA = 0; // Set to zero because it is a relocation.
657    Entry->DataSize = Data[DataNodes->getDataIndex()].size();
658    Entry->Codepage = 0;
659    Entry->Reserved = 0;
660    CurrentOffset += sizeof(coff_resource_data_entry);
661    CurrentRelativeOffset += sizeof(coff_resource_data_entry);
662  }
663}
664
665void WindowsResourceCOFFWriter::writeDirectoryStringTable() {
666  // Now write the directory string table for .rsrc$01
667  uint32_t TotalStringTableSize = 0;
668  for (auto &String : StringTable) {
669    uint16_t Length = String.size();
670    support::endian::write16le(BufferStart + CurrentOffset, Length);
671    CurrentOffset += sizeof(uint16_t);
672    auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset);
673    std::copy(String.begin(), String.end(), Start);
674    CurrentOffset += Length * sizeof(UTF16);
675    TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t);
676  }
677  CurrentOffset +=
678      alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize;
679}
680
681void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {
682
683  // Now write the relocations for .rsrc$01
684  // Five symbols already in table before we start, @feat.00 and 2 for each
685  // .rsrc section.
686  uint32_t NextSymbolIndex = 5;
687  for (unsigned i = 0; i < Data.size(); i++) {
688    auto *Reloc =
689        reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset);
690    Reloc->VirtualAddress = RelocationAddresses[i];
691    Reloc->SymbolTableIndex = NextSymbolIndex++;
692    switch (MachineType) {
693    case COFF::IMAGE_FILE_MACHINE_ARMNT:
694      Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB;
695      break;
696    case COFF::IMAGE_FILE_MACHINE_AMD64:
697      Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB;
698      break;
699    case COFF::IMAGE_FILE_MACHINE_I386:
700      Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB;
701      break;
702    default:
703      Reloc->Type = 0;
704    }
705    CurrentOffset += sizeof(coff_relocation);
706  }
707}
708
709Expected<std::unique_ptr<MemoryBuffer>>
710writeWindowsResourceCOFF(COFF::MachineTypes MachineType,
711                         const WindowsResourceParser &Parser) {
712  Error E = Error::success();
713  WindowsResourceCOFFWriter Writer(MachineType, Parser, E);
714  if (E)
715    return std::move(E);
716  return Writer.write();
717}
718
719} // namespace object
720} // namespace llvm
721