blob: fdf4690e60422c7821faafdd800fccc754e0d36c [file] [log] [blame]
Eric Beckmanna6bdf752017-05-20 01:49:19 +00001//===-- WindowsResource.cpp -------------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements the .res file class.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/Object/WindowsResource.h"
Zachary Turner264b5d92017-06-07 03:48:56 +000015#include "llvm/BinaryFormat/COFF.h"
Eric Beckmannd9de6382017-06-09 17:34:30 +000016#include "llvm/Object/COFF.h"
17#include "llvm/Support/FileOutputBuffer.h"
18#include "llvm/Support/MathExtras.h"
19#include <ctime>
20#include <queue>
Eric Beckmannba395ef2017-05-30 22:29:06 +000021#include <sstream>
Eric Beckmanna6bdf752017-05-20 01:49:19 +000022#include <system_error>
23
24namespace llvm {
25namespace object {
26
Eric Beckmann72fb6a82017-05-30 18:19:06 +000027#define RETURN_IF_ERROR(X) \
28 if (auto EC = X) \
Benjamin Kramer14ea1222017-05-30 19:36:58 +000029 return EC;
Eric Beckmann72fb6a82017-05-30 18:19:06 +000030
31const uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t);
32
Eric Beckmann382eaab2017-06-13 00:19:43 +000033// COFF files seem to be inconsistent with alignment between sections, just use
34// 8-byte because it makes everyone happy.
35const uint32_t SECTION_ALIGNMENT = sizeof(uint64_t);
36
David Blaikief1c3bee2017-05-20 03:32:51 +000037static const size_t ResourceMagicSize = 16;
Eric Beckmanna6bdf752017-05-20 01:49:19 +000038
David Blaikief1c3bee2017-05-20 03:32:51 +000039static const size_t NullEntrySize = 16;
Eric Beckmanna6bdf752017-05-20 01:49:19 +000040
Eric Beckmannd9de6382017-06-09 17:34:30 +000041uint32_t WindowsResourceParser::TreeNode::StringCount = 0;
42uint32_t WindowsResourceParser::TreeNode::DataCount = 0;
43
Eric Beckmanna6bdf752017-05-20 01:49:19 +000044WindowsResource::WindowsResource(MemoryBufferRef Source)
45 : Binary(Binary::ID_WinRes, Source) {
David Blaikief1c3bee2017-05-20 03:32:51 +000046 size_t LeadingSize = ResourceMagicSize + NullEntrySize;
Eric Beckmanna6bdf752017-05-20 01:49:19 +000047 BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize),
48 support::little);
49}
50
Eric Beckmanna6bdf752017-05-20 01:49:19 +000051Expected<std::unique_ptr<WindowsResource>>
52WindowsResource::createWindowsResource(MemoryBufferRef Source) {
David Blaikief1c3bee2017-05-20 03:32:51 +000053 if (Source.getBufferSize() < ResourceMagicSize + NullEntrySize)
Eric Beckmanna6bdf752017-05-20 01:49:19 +000054 return make_error<GenericBinaryError>(
55 "File too small to be a resource file",
56 object_error::invalid_file_type);
57 std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source));
58 return std::move(Ret);
59}
60
61Expected<ResourceEntryRef> WindowsResource::getHeadEntry() {
62 Error Err = Error::success();
63 auto Ref = ResourceEntryRef(BinaryStreamRef(BBS), this, Err);
64 if (Err)
65 return std::move(Err);
66 return Ref;
67}
68
69ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref,
70 const WindowsResource *Owner, Error &Err)
71 : Reader(Ref), OwningRes(Owner) {
72 if (loadNext())
Eric Beckmann907fb8132017-06-13 18:17:36 +000073 Err = make_error<GenericBinaryError>("Could not read first entry.\n",
Eric Beckmanna6bdf752017-05-20 01:49:19 +000074 object_error::unexpected_eof);
75}
76
77Error ResourceEntryRef::moveNext(bool &End) {
78 // Reached end of all the entries.
79 if (Reader.bytesRemaining() == 0) {
80 End = true;
81 return Error::success();
82 }
83 RETURN_IF_ERROR(loadNext());
84
85 return Error::success();
86}
87
Eric Beckmann72fb6a82017-05-30 18:19:06 +000088static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID,
89 ArrayRef<UTF16> &Str, bool &IsString) {
90 uint16_t IDFlag;
91 RETURN_IF_ERROR(Reader.readInteger(IDFlag));
92 IsString = IDFlag != 0xffff;
93
94 if (IsString) {
95 Reader.setOffset(
96 Reader.getOffset() -
97 sizeof(uint16_t)); // Re-read the bytes which we used to check the flag.
98 RETURN_IF_ERROR(Reader.readWideString(Str));
99 } else
100 RETURN_IF_ERROR(Reader.readInteger(ID));
101
102 return Error::success();
103}
104
Eric Beckmanna6bdf752017-05-20 01:49:19 +0000105Error ResourceEntryRef::loadNext() {
106 uint32_t DataSize;
107 RETURN_IF_ERROR(Reader.readInteger(DataSize));
108 uint32_t HeaderSize;
109 RETURN_IF_ERROR(Reader.readInteger(HeaderSize));
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000110
111 if (HeaderSize < MIN_HEADER_SIZE)
112 return make_error<GenericBinaryError>("Header size is too small.",
113 object_error::parse_failed);
114
115 RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType));
116
117 RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName));
118
Eric Beckmanna6bdf752017-05-20 01:49:19 +0000119 RETURN_IF_ERROR(Reader.padToAlignment(sizeof(uint32_t)));
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000120
121 RETURN_IF_ERROR(Reader.readObject(Suffix));
122
123 RETURN_IF_ERROR(Reader.readArray(Data, DataSize));
124
125 RETURN_IF_ERROR(Reader.padToAlignment(sizeof(uint32_t)));
126
Eric Beckmanna6bdf752017-05-20 01:49:19 +0000127 return Error::success();
128}
129
Eric Beckmannd9de6382017-06-09 17:34:30 +0000130WindowsResourceParser::WindowsResourceParser() : Root(false) {}
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000131
132Error WindowsResourceParser::parse(WindowsResource *WR) {
133 auto EntryOrErr = WR->getHeadEntry();
134 if (!EntryOrErr)
135 return EntryOrErr.takeError();
136
137 ResourceEntryRef Entry = EntryOrErr.get();
138 bool End = false;
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000139 while (!End) {
Eric Beckmannd9de6382017-06-09 17:34:30 +0000140 Data.push_back(Entry.getData());
141
Eric Beckmann13017592017-06-13 00:16:32 +0000142 bool IsNewTypeString = false;
143 bool IsNewNameString = false;
144
145 Root.addEntry(Entry, IsNewTypeString, IsNewNameString);
146
147 if (IsNewTypeString)
Eric Beckmannd9de6382017-06-09 17:34:30 +0000148 StringTable.push_back(Entry.getTypeString());
149
Eric Beckmann13017592017-06-13 00:16:32 +0000150 if (IsNewNameString)
Eric Beckmannd9de6382017-06-09 17:34:30 +0000151 StringTable.push_back(Entry.getNameString());
152
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000153 RETURN_IF_ERROR(Entry.moveNext(End));
154 }
155
156 return Error::success();
157}
158
Eric Beckmann907fb8132017-06-13 18:17:36 +0000159void WindowsResourceParser::printTree(raw_ostream &OS) const {
160 ScopedPrinter Writer(OS);
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000161 Root.print(Writer, "Resource Tree");
162}
163
Eric Beckmann13017592017-06-13 00:16:32 +0000164void WindowsResourceParser::TreeNode::addEntry(const ResourceEntryRef &Entry,
165 bool &IsNewTypeString,
166 bool &IsNewNameString) {
167 TreeNode &TypeNode = addTypeNode(Entry, IsNewTypeString);
168 TreeNode &NameNode = TypeNode.addNameNode(Entry, IsNewNameString);
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000169 NameNode.addLanguageNode(Entry);
170}
171
Eric Beckmannd9de6382017-06-09 17:34:30 +0000172WindowsResourceParser::TreeNode::TreeNode(bool IsStringNode) {
173 if (IsStringNode)
174 StringIndex = StringCount++;
175}
176
177WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion,
178 uint16_t MinorVersion,
179 uint32_t Characteristics)
180 : IsDataNode(true), MajorVersion(MajorVersion), MinorVersion(MinorVersion),
181 Characteristics(Characteristics) {
Eric Beckmannd9de6382017-06-09 17:34:30 +0000182 DataIndex = DataCount++;
183}
184
185std::unique_ptr<WindowsResourceParser::TreeNode>
186WindowsResourceParser::TreeNode::createStringNode() {
187 return std::unique_ptr<TreeNode>(new TreeNode(true));
188}
189
190std::unique_ptr<WindowsResourceParser::TreeNode>
191WindowsResourceParser::TreeNode::createIDNode() {
192 return std::unique_ptr<TreeNode>(new TreeNode(false));
193}
194
195std::unique_ptr<WindowsResourceParser::TreeNode>
196WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion,
197 uint16_t MinorVersion,
198 uint32_t Characteristics) {
199 return std::unique_ptr<TreeNode>(
200 new TreeNode(MajorVersion, MinorVersion, Characteristics));
201}
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000202
203WindowsResourceParser::TreeNode &
Eric Beckmann13017592017-06-13 00:16:32 +0000204WindowsResourceParser::TreeNode::addTypeNode(const ResourceEntryRef &Entry,
205 bool &IsNewTypeString) {
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000206 if (Entry.checkTypeString())
Eric Beckmann13017592017-06-13 00:16:32 +0000207 return addChild(Entry.getTypeString(), IsNewTypeString);
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000208 else
209 return addChild(Entry.getTypeID());
210}
211
212WindowsResourceParser::TreeNode &
Eric Beckmann13017592017-06-13 00:16:32 +0000213WindowsResourceParser::TreeNode::addNameNode(const ResourceEntryRef &Entry,
214 bool &IsNewNameString) {
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000215 if (Entry.checkNameString())
Eric Beckmann13017592017-06-13 00:16:32 +0000216 return addChild(Entry.getNameString(), IsNewNameString);
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000217 else
218 return addChild(Entry.getNameID());
219}
220
221WindowsResourceParser::TreeNode &
222WindowsResourceParser::TreeNode::addLanguageNode(
223 const ResourceEntryRef &Entry) {
Eric Beckmannd9de6382017-06-09 17:34:30 +0000224 return addChild(Entry.getLanguage(), true, Entry.getMajorVersion(),
225 Entry.getMinorVersion(), Entry.getCharacteristics());
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000226}
227
Eric Beckmannd9de6382017-06-09 17:34:30 +0000228WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addChild(
229 uint32_t ID, bool IsDataNode, uint16_t MajorVersion, uint16_t MinorVersion,
230 uint32_t Characteristics) {
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000231 auto Child = IDChildren.find(ID);
232 if (Child == IDChildren.end()) {
Eric Beckmannd9de6382017-06-09 17:34:30 +0000233 auto NewChild =
234 IsDataNode ? createDataNode(MajorVersion, MinorVersion, Characteristics)
235 : createIDNode();
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000236 WindowsResourceParser::TreeNode &Node = *NewChild;
237 IDChildren.emplace(ID, std::move(NewChild));
238 return Node;
239 } else
240 return *(Child->second);
241}
242
243WindowsResourceParser::TreeNode &
Eric Beckmann13017592017-06-13 00:16:32 +0000244WindowsResourceParser::TreeNode::addChild(ArrayRef<UTF16> NameRef,
245 bool &IsNewString) {
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000246 std::string NameString;
247 ArrayRef<UTF16> CorrectedName;
Eric Beckmann025e82b2017-05-30 23:10:57 +0000248 std::vector<UTF16> EndianCorrectedName;
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000249 if (llvm::sys::IsBigEndianHost) {
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000250 EndianCorrectedName.resize(NameRef.size() + 1);
251 std::copy(NameRef.begin(), NameRef.end(), EndianCorrectedName.begin() + 1);
252 EndianCorrectedName[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED;
253 CorrectedName = makeArrayRef(EndianCorrectedName);
254 } else
255 CorrectedName = NameRef;
256 llvm::convertUTF16ToUTF8String(CorrectedName, NameString);
257
258 auto Child = StringChildren.find(NameString);
259 if (Child == StringChildren.end()) {
Eric Beckmannd9de6382017-06-09 17:34:30 +0000260 auto NewChild = createStringNode();
Eric Beckmann13017592017-06-13 00:16:32 +0000261 IsNewString = true;
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000262 WindowsResourceParser::TreeNode &Node = *NewChild;
263 StringChildren.emplace(NameString, std::move(NewChild));
264 return Node;
265 } else
266 return *(Child->second);
267}
268
269void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer,
270 StringRef Name) const {
271 ListScope NodeScope(Writer, Name);
272 for (auto const &Child : StringChildren) {
273 Child.second->print(Writer, Child.first);
274 }
275 for (auto const &Child : IDChildren) {
Eric Beckmannba395ef2017-05-30 22:29:06 +0000276 Child.second->print(Writer, to_string(Child.first));
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000277 }
278}
279
Eric Beckmannd9de6382017-06-09 17:34:30 +0000280// This function returns the size of the entire resource tree, including
281// directory tables, directory entries, and data entries. It does not include
282// the directory strings or the relocations of the .rsrc section.
283uint32_t WindowsResourceParser::TreeNode::getTreeSize() const {
284 uint32_t Size = (IDChildren.size() + StringChildren.size()) *
285 sizeof(llvm::object::coff_resource_dir_entry);
286
287 // Reached a node pointing to a data entry.
288 if (IsDataNode) {
289 Size += sizeof(llvm::object::coff_resource_data_entry);
290 return Size;
291 }
292
293 // If the node does not point to data, it must have a directory table pointing
294 // to other nodes.
295 Size += sizeof(llvm::object::coff_resource_dir_table);
296
297 for (auto const &Child : StringChildren) {
298 Size += Child.second->getTreeSize();
299 }
300 for (auto const &Child : IDChildren) {
301 Size += Child.second->getTreeSize();
302 }
303 return Size;
304}
305
306class WindowsResourceCOFFWriter {
307public:
308 WindowsResourceCOFFWriter(StringRef OutputFile, Machine MachineType,
309 const WindowsResourceParser &Parser, Error &E);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000310 Error write();
311
312private:
313 void performFileLayout();
314 void performSectionOneLayout();
315 void performSectionTwoLayout();
316 void writeCOFFHeader();
317 void writeFirstSectionHeader();
318 void writeSecondSectionHeader();
319 void writeFirstSection();
320 void writeSecondSection();
321 void writeSymbolTable();
322 void writeStringTable();
323 void writeDirectoryTree();
324 void writeDirectoryStringTable();
325 void writeFirstSectionRelocations();
326 std::unique_ptr<FileOutputBuffer> Buffer;
Eric Beckmann382eaab2017-06-13 00:19:43 +0000327 uint8_t *BufferStart;
328 uint64_t CurrentOffset = 0;
Eric Beckmannd9de6382017-06-09 17:34:30 +0000329 Machine MachineType;
330 const WindowsResourceParser::TreeNode &Resources;
331 const ArrayRef<std::vector<uint8_t>> Data;
332 uint64_t FileSize;
333 uint32_t SymbolTableOffset;
334 uint32_t SectionOneSize;
335 uint32_t SectionOneOffset;
336 uint32_t SectionOneRelocations;
337 uint32_t SectionTwoSize;
338 uint32_t SectionTwoOffset;
339 const ArrayRef<std::vector<UTF16>> StringTable;
340 std::vector<uint32_t> StringTableOffsets;
341 std::vector<uint32_t> DataOffsets;
342 std::vector<uint32_t> RelocationAddresses;
343};
344
345WindowsResourceCOFFWriter::WindowsResourceCOFFWriter(
346 StringRef OutputFile, Machine MachineType,
347 const WindowsResourceParser &Parser, Error &E)
348 : MachineType(MachineType), Resources(Parser.getTree()),
349 Data(Parser.getData()), StringTable(Parser.getStringTable()) {
350 performFileLayout();
351
352 ErrorOr<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
353 FileOutputBuffer::create(OutputFile, FileSize);
354 if (!BufferOrErr) {
355 E = errorCodeToError(BufferOrErr.getError());
356 return;
357 }
358
359 Buffer = std::move(*BufferOrErr);
360}
361
362void WindowsResourceCOFFWriter::performFileLayout() {
363 // Add size of COFF header.
364 FileSize = llvm::COFF::Header16Size;
365
366 // one .rsrc section header for directory tree, another for resource data.
367 FileSize += 2 * llvm::COFF::SectionSize;
368
369 performSectionOneLayout();
370 performSectionTwoLayout();
371
372 // We have reached the address of the symbol table.
373 SymbolTableOffset = FileSize;
374
375 FileSize += llvm::COFF::Symbol16Size; // size of the @feat.00 symbol.
376 FileSize += 4 * llvm::COFF::Symbol16Size; // symbol + aux for each section.
377 FileSize += Data.size() * llvm::COFF::Symbol16Size; // 1 symbol per resource.
378 FileSize += 4; // four null bytes for the string table.
379}
380
381void WindowsResourceCOFFWriter::performSectionOneLayout() {
382 SectionOneOffset = FileSize;
383
384 SectionOneSize = Resources.getTreeSize();
385 uint32_t CurrentStringOffset = SectionOneSize;
386 uint32_t TotalStringTableSize = 0;
387 for (auto const &String : StringTable) {
388 StringTableOffsets.push_back(CurrentStringOffset);
389 uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t);
390 CurrentStringOffset += StringSize;
391 TotalStringTableSize += StringSize;
392 }
393 SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t));
394
395 // account for the relocations of section one.
396 SectionOneRelocations = FileSize + SectionOneSize;
397 FileSize += SectionOneSize;
398 FileSize += Data.size() *
399 llvm::COFF::RelocationSize; // one relocation for each resource.
Eric Beckmann382eaab2017-06-13 00:19:43 +0000400 FileSize = alignTo(FileSize, SECTION_ALIGNMENT);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000401}
402
403void WindowsResourceCOFFWriter::performSectionTwoLayout() {
404 // add size of .rsrc$2 section, which contains all resource data on 8-byte
405 // alignment.
406 SectionTwoOffset = FileSize;
407 SectionTwoSize = 0;
408 for (auto const &Entry : Data) {
409 DataOffsets.push_back(SectionTwoSize);
410 SectionTwoSize += llvm::alignTo(Entry.size(), sizeof(uint64_t));
411 }
412 FileSize += SectionTwoSize;
Eric Beckmann382eaab2017-06-13 00:19:43 +0000413 FileSize = alignTo(FileSize, SECTION_ALIGNMENT);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000414}
415
416static std::time_t getTime() {
417 std::time_t Now = time(nullptr);
418 if (Now < 0 || !isUInt<32>(Now))
419 return UINT32_MAX;
420 return Now;
421}
422
423Error WindowsResourceCOFFWriter::write() {
Eric Beckmann382eaab2017-06-13 00:19:43 +0000424 BufferStart = Buffer->getBufferStart();
Eric Beckmannd9de6382017-06-09 17:34:30 +0000425
426 writeCOFFHeader();
427 writeFirstSectionHeader();
428 writeSecondSectionHeader();
429 writeFirstSection();
430 writeSecondSection();
431 writeSymbolTable();
432 writeStringTable();
433
434 if (auto EC = Buffer->commit()) {
435 return errorCodeToError(EC);
436 }
437
438 return Error::success();
439}
440
441void WindowsResourceCOFFWriter::writeCOFFHeader() {
442 // Write the COFF header.
Eric Beckmann382eaab2017-06-13 00:19:43 +0000443 auto *Header =
444 reinterpret_cast<llvm::object::coff_file_header *>(BufferStart);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000445 switch (MachineType) {
446 case Machine::ARM:
447 Header->Machine = llvm::COFF::IMAGE_FILE_MACHINE_ARMNT;
448 break;
449 case Machine::X64:
450 Header->Machine = llvm::COFF::IMAGE_FILE_MACHINE_AMD64;
451 break;
452 case Machine::X86:
453 Header->Machine = llvm::COFF::IMAGE_FILE_MACHINE_I386;
454 break;
455 default:
456 Header->Machine = llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN;
457 }
458 Header->NumberOfSections = 2;
459 Header->TimeDateStamp = getTime();
460 Header->PointerToSymbolTable = SymbolTableOffset;
461 // One symbol for every resource plus 2 for each section and @feat.00
462 Header->NumberOfSymbols = Data.size() + 5;
463 Header->SizeOfOptionalHeader = 0;
464 Header->Characteristics = llvm::COFF::IMAGE_FILE_32BIT_MACHINE;
465}
466
467void WindowsResourceCOFFWriter::writeFirstSectionHeader() {
468 // Write the first section header.
Eric Beckmann382eaab2017-06-13 00:19:43 +0000469 CurrentOffset += sizeof(llvm::object::coff_file_header);
470 auto *SectionOneHeader = reinterpret_cast<llvm::object::coff_section *>(
471 BufferStart + CurrentOffset);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000472 strncpy(SectionOneHeader->Name, ".rsrc$01", (size_t)llvm::COFF::NameSize);
473 SectionOneHeader->VirtualSize = 0;
474 SectionOneHeader->VirtualAddress = 0;
475 SectionOneHeader->SizeOfRawData = SectionOneSize;
476 SectionOneHeader->PointerToRawData = SectionOneOffset;
477 SectionOneHeader->PointerToRelocations = SectionOneRelocations;
478 SectionOneHeader->PointerToLinenumbers = 0;
479 SectionOneHeader->NumberOfRelocations = Data.size();
480 SectionOneHeader->NumberOfLinenumbers = 0;
481 SectionOneHeader->Characteristics = llvm::COFF::IMAGE_SCN_ALIGN_1BYTES;
482 SectionOneHeader->Characteristics +=
483 llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
484 SectionOneHeader->Characteristics += llvm::COFF::IMAGE_SCN_MEM_DISCARDABLE;
485 SectionOneHeader->Characteristics += llvm::COFF::IMAGE_SCN_MEM_READ;
486}
487
488void WindowsResourceCOFFWriter::writeSecondSectionHeader() {
489 // Write the second section header.
Eric Beckmann382eaab2017-06-13 00:19:43 +0000490 CurrentOffset += sizeof(llvm::object::coff_section);
491 auto *SectionTwoHeader = reinterpret_cast<llvm::object::coff_section *>(
492 BufferStart + CurrentOffset);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000493 strncpy(SectionTwoHeader->Name, ".rsrc$02", (size_t)llvm::COFF::NameSize);
494 SectionTwoHeader->VirtualSize = 0;
495 SectionTwoHeader->VirtualAddress = 0;
496 SectionTwoHeader->SizeOfRawData = SectionTwoSize;
497 SectionTwoHeader->PointerToRawData = SectionTwoOffset;
498 SectionTwoHeader->PointerToRelocations = 0;
499 SectionTwoHeader->PointerToLinenumbers = 0;
500 SectionTwoHeader->NumberOfRelocations = 0;
501 SectionTwoHeader->NumberOfLinenumbers = 0;
502 SectionTwoHeader->Characteristics =
503 llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
504 SectionTwoHeader->Characteristics += llvm::COFF::IMAGE_SCN_MEM_READ;
505}
506
507void WindowsResourceCOFFWriter::writeFirstSection() {
508 // Write section one.
Eric Beckmann382eaab2017-06-13 00:19:43 +0000509 CurrentOffset += sizeof(llvm::object::coff_section);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000510
511 writeDirectoryTree();
512 writeDirectoryStringTable();
513 writeFirstSectionRelocations();
Eric Beckmann382eaab2017-06-13 00:19:43 +0000514
515 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000516}
517
518void WindowsResourceCOFFWriter::writeSecondSection() {
519 // Now write the .rsrc$02 section.
520 for (auto const &RawDataEntry : Data) {
Eric Beckmann382eaab2017-06-13 00:19:43 +0000521 std::copy(RawDataEntry.begin(), RawDataEntry.end(),
522 BufferStart + CurrentOffset);
523 CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t));
Eric Beckmannd9de6382017-06-09 17:34:30 +0000524 }
Eric Beckmann382eaab2017-06-13 00:19:43 +0000525
526 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000527}
528
529void WindowsResourceCOFFWriter::writeSymbolTable() {
530 // Now write the symbol table.
531 // First, the feat symbol.
Eric Beckmann382eaab2017-06-13 00:19:43 +0000532 auto *Symbol = reinterpret_cast<llvm::object::coff_symbol16 *>(BufferStart +
533 CurrentOffset);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000534 strncpy(Symbol->Name.ShortName, "@feat.00", (size_t)llvm::COFF::NameSize);
535 Symbol->Value = 0x11;
536 Symbol->SectionNumber = 0xffff;
537 Symbol->Type = llvm::COFF::IMAGE_SYM_DTYPE_NULL;
538 Symbol->StorageClass = llvm::COFF::IMAGE_SYM_CLASS_STATIC;
539 Symbol->NumberOfAuxSymbols = 0;
Eric Beckmann382eaab2017-06-13 00:19:43 +0000540 CurrentOffset += sizeof(llvm::object::coff_symbol16);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000541
542 // Now write the .rsrc1 symbol + aux.
Eric Beckmann382eaab2017-06-13 00:19:43 +0000543 Symbol = reinterpret_cast<llvm::object::coff_symbol16 *>(BufferStart +
544 CurrentOffset);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000545 strncpy(Symbol->Name.ShortName, ".rsrc$01", (size_t)llvm::COFF::NameSize);
546 Symbol->Value = 0;
547 Symbol->SectionNumber = 1;
548 Symbol->Type = llvm::COFF::IMAGE_SYM_DTYPE_NULL;
549 Symbol->StorageClass = llvm::COFF::IMAGE_SYM_CLASS_STATIC;
550 Symbol->NumberOfAuxSymbols = 1;
Eric Beckmann382eaab2017-06-13 00:19:43 +0000551 CurrentOffset += sizeof(llvm::object::coff_symbol16);
552 auto *Aux = reinterpret_cast<llvm::object::coff_aux_section_definition *>(
553 BufferStart + CurrentOffset);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000554 Aux->Length = SectionOneSize;
555 Aux->NumberOfRelocations = Data.size();
556 Aux->NumberOfLinenumbers = 0;
557 Aux->CheckSum = 0;
558 Aux->NumberLowPart = 0;
559 Aux->Selection = 0;
Eric Beckmann382eaab2017-06-13 00:19:43 +0000560 CurrentOffset += sizeof(llvm::object::coff_aux_section_definition);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000561
562 // Now write the .rsrc2 symbol + aux.
Eric Beckmann382eaab2017-06-13 00:19:43 +0000563 Symbol = reinterpret_cast<llvm::object::coff_symbol16 *>(BufferStart +
564 CurrentOffset);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000565 strncpy(Symbol->Name.ShortName, ".rsrc$02", (size_t)llvm::COFF::NameSize);
566 Symbol->Value = 0;
567 Symbol->SectionNumber = 2;
568 Symbol->Type = llvm::COFF::IMAGE_SYM_DTYPE_NULL;
569 Symbol->StorageClass = llvm::COFF::IMAGE_SYM_CLASS_STATIC;
570 Symbol->NumberOfAuxSymbols = 1;
Eric Beckmann382eaab2017-06-13 00:19:43 +0000571 CurrentOffset += sizeof(llvm::object::coff_symbol16);
572 Aux = reinterpret_cast<llvm::object::coff_aux_section_definition *>(
573 BufferStart + CurrentOffset);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000574 Aux->Length = SectionTwoSize;
575 Aux->NumberOfRelocations = 0;
576 Aux->NumberOfLinenumbers = 0;
577 Aux->CheckSum = 0;
578 Aux->NumberLowPart = 0;
579 Aux->Selection = 0;
Eric Beckmann382eaab2017-06-13 00:19:43 +0000580 CurrentOffset += sizeof(llvm::object::coff_aux_section_definition);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000581
582 // Now write a symbol for each relocation.
583 for (unsigned i = 0; i < Data.size(); i++) {
584 char RelocationName[9];
585 sprintf(RelocationName, "$R%06X", DataOffsets[i]);
Eric Beckmann382eaab2017-06-13 00:19:43 +0000586 Symbol = reinterpret_cast<llvm::object::coff_symbol16 *>(BufferStart +
587 CurrentOffset);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000588 strncpy(Symbol->Name.ShortName, RelocationName,
589 (size_t)llvm::COFF::NameSize);
590 Symbol->Value = DataOffsets[i];
591 Symbol->SectionNumber = 1;
592 Symbol->Type = llvm::COFF::IMAGE_SYM_DTYPE_NULL;
593 Symbol->StorageClass = llvm::COFF::IMAGE_SYM_CLASS_STATIC;
594 Symbol->NumberOfAuxSymbols = 0;
Eric Beckmann382eaab2017-06-13 00:19:43 +0000595 CurrentOffset += sizeof(llvm::object::coff_symbol16);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000596 }
597}
598
599void WindowsResourceCOFFWriter::writeStringTable() {
600 // Just 4 null bytes for the string table.
Eric Beckmann33866332017-06-13 20:36:19 +0000601 auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset);
602 memset(COFFStringTable, 0, 4);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000603}
604
605void WindowsResourceCOFFWriter::writeDirectoryTree() {
606 // Traverse parsed resource tree breadth-first and write the corresponding
607 // COFF objects.
608 std::queue<const WindowsResourceParser::TreeNode *> Queue;
609 Queue.push(&Resources);
610 uint32_t NextLevelOffset = sizeof(llvm::object::coff_resource_dir_table) +
611 (Resources.getStringChildren().size() +
612 Resources.getIDChildren().size()) *
613 sizeof(llvm::object::coff_resource_dir_entry);
614 std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder;
615 uint32_t CurrentRelativeOffset = 0;
616
617 while (!Queue.empty()) {
618 auto CurrentNode = Queue.front();
619 Queue.pop();
Eric Beckmann382eaab2017-06-13 00:19:43 +0000620 auto *Table = reinterpret_cast<llvm::object::coff_resource_dir_table *>(
621 BufferStart + CurrentOffset);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000622 Table->Characteristics = CurrentNode->getCharacteristics();
623 Table->TimeDateStamp = 0;
624 Table->MajorVersion = CurrentNode->getMajorVersion();
625 Table->MinorVersion = CurrentNode->getMinorVersion();
626 auto &IDChildren = CurrentNode->getIDChildren();
627 auto &StringChildren = CurrentNode->getStringChildren();
628 Table->NumberOfNameEntries = StringChildren.size();
629 Table->NumberOfIDEntries = IDChildren.size();
Eric Beckmann382eaab2017-06-13 00:19:43 +0000630 CurrentOffset += sizeof(llvm::object::coff_resource_dir_table);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000631 CurrentRelativeOffset += sizeof(llvm::object::coff_resource_dir_table);
632
633 // Write the directory entries immediately following each directory table.
634 for (auto const &Child : StringChildren) {
Eric Beckmann382eaab2017-06-13 00:19:43 +0000635 auto *Entry = reinterpret_cast<llvm::object::coff_resource_dir_entry *>(
636 BufferStart + CurrentOffset);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000637 Entry->Identifier.NameOffset =
638 StringTableOffsets[Child.second->getStringIndex()];
639 if (Child.second->checkIsDataNode()) {
640 Entry->Offset.DataEntryOffset = NextLevelOffset;
641 NextLevelOffset += sizeof(llvm::object::coff_resource_data_entry);
642 DataEntriesTreeOrder.push_back(Child.second.get());
643 } else {
644 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);
645 NextLevelOffset += sizeof(llvm::object::coff_resource_dir_table) +
646 (Child.second->getStringChildren().size() +
647 Child.second->getIDChildren().size()) *
648 sizeof(llvm::object::coff_resource_dir_entry);
649 Queue.push(Child.second.get());
650 }
Eric Beckmann382eaab2017-06-13 00:19:43 +0000651 CurrentOffset += sizeof(llvm::object::coff_resource_dir_entry);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000652 CurrentRelativeOffset += sizeof(llvm::object::coff_resource_dir_entry);
653 }
654 for (auto const &Child : IDChildren) {
Eric Beckmann382eaab2017-06-13 00:19:43 +0000655 auto *Entry = reinterpret_cast<llvm::object::coff_resource_dir_entry *>(
656 BufferStart + CurrentOffset);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000657 Entry->Identifier.ID = Child.first;
658 if (Child.second->checkIsDataNode()) {
659 Entry->Offset.DataEntryOffset = NextLevelOffset;
660 NextLevelOffset += sizeof(llvm::object::coff_resource_data_entry);
661 DataEntriesTreeOrder.push_back(Child.second.get());
662 } else {
663 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);
664 NextLevelOffset += sizeof(llvm::object::coff_resource_dir_table) +
665 (Child.second->getStringChildren().size() +
666 Child.second->getIDChildren().size()) *
667 sizeof(llvm::object::coff_resource_dir_entry);
668 Queue.push(Child.second.get());
669 }
Eric Beckmann382eaab2017-06-13 00:19:43 +0000670 CurrentOffset += sizeof(llvm::object::coff_resource_dir_entry);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000671 CurrentRelativeOffset += sizeof(llvm::object::coff_resource_dir_entry);
672 }
673 }
674
675 RelocationAddresses.resize(Data.size());
676 // Now write all the resource data entries.
677 for (auto DataNodes : DataEntriesTreeOrder) {
Eric Beckmann382eaab2017-06-13 00:19:43 +0000678 auto *Entry = reinterpret_cast<llvm::object::coff_resource_data_entry *>(
679 BufferStart + CurrentOffset);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000680 RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset;
681 Entry->DataRVA = 0; // Set to zero because it is a relocation.
682 Entry->DataSize = Data[DataNodes->getDataIndex()].size();
683 Entry->Codepage = 0;
684 Entry->Reserved = 0;
Eric Beckmann382eaab2017-06-13 00:19:43 +0000685 CurrentOffset += sizeof(llvm::object::coff_resource_data_entry);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000686 CurrentRelativeOffset += sizeof(llvm::object::coff_resource_data_entry);
687 }
688}
689
690void WindowsResourceCOFFWriter::writeDirectoryStringTable() {
691 // Now write the directory string table for .rsrc$01
692 uint32_t TotalStringTableSize = 0;
693 for (auto String : StringTable) {
Eric Beckmann382eaab2017-06-13 00:19:43 +0000694 auto *LengthField =
695 reinterpret_cast<uint16_t *>(BufferStart + CurrentOffset);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000696 uint16_t Length = String.size();
697 *LengthField = Length;
Eric Beckmann382eaab2017-06-13 00:19:43 +0000698 CurrentOffset += sizeof(uint16_t);
699 auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000700 std::copy(String.begin(), String.end(), Start);
Eric Beckmann382eaab2017-06-13 00:19:43 +0000701 CurrentOffset += Length * sizeof(UTF16);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000702 TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t);
703 }
Eric Beckmann382eaab2017-06-13 00:19:43 +0000704 CurrentOffset +=
Eric Beckmannd9de6382017-06-09 17:34:30 +0000705 alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize;
706}
707
708void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {
709
710 // Now write the relocations for .rsrc$01
711 // Five symbols already in table before we start, @feat.00 and 2 for each
712 // .rsrc section.
713 uint32_t NextSymbolIndex = 5;
714 for (unsigned i = 0; i < Data.size(); i++) {
Eric Beckmann382eaab2017-06-13 00:19:43 +0000715 auto *Reloc = reinterpret_cast<llvm::object::coff_relocation *>(
716 BufferStart + CurrentOffset);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000717 Reloc->VirtualAddress = RelocationAddresses[i];
718 Reloc->SymbolTableIndex = NextSymbolIndex++;
719 switch (MachineType) {
720 case Machine::ARM:
721 Reloc->Type = llvm::COFF::IMAGE_REL_ARM_ADDR32NB;
722 break;
723 case Machine::X64:
724 Reloc->Type = llvm::COFF::IMAGE_REL_AMD64_ADDR32NB;
725 break;
726 case Machine::X86:
727 Reloc->Type = llvm::COFF::IMAGE_REL_I386_DIR32NB;
728 break;
729 default:
730 Reloc->Type = 0;
731 }
Eric Beckmann382eaab2017-06-13 00:19:43 +0000732 CurrentOffset += sizeof(llvm::object::coff_relocation);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000733 }
734}
735
736Error writeWindowsResourceCOFF(StringRef OutputFile, Machine MachineType,
737 const WindowsResourceParser &Parser) {
738 Error E = Error::success();
739 WindowsResourceCOFFWriter Writer(OutputFile, MachineType, Parser, E);
740 if (E)
741 return E;
742 return Writer.write();
743}
744
Eric Beckmanna6bdf752017-05-20 01:49:19 +0000745} // namespace object
746} // namespace llvm