Coverage Report

Created: 2019-07-24 05:18

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