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