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