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