blob: a7e126da52ae2e19c410f58c997acd5ec6ba3117 [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
David Blaikief1c3bee2017-05-20 03:32:51 +000033static const size_t ResourceMagicSize = 16;
Eric Beckmanna6bdf752017-05-20 01:49:19 +000034
David Blaikief1c3bee2017-05-20 03:32:51 +000035static const size_t NullEntrySize = 16;
Eric Beckmanna6bdf752017-05-20 01:49:19 +000036
Eric Beckmannd9de6382017-06-09 17:34:30 +000037uint32_t WindowsResourceParser::TreeNode::StringCount = 0;
38uint32_t WindowsResourceParser::TreeNode::DataCount = 0;
39
Eric Beckmanna6bdf752017-05-20 01:49:19 +000040WindowsResource::WindowsResource(MemoryBufferRef Source)
41 : Binary(Binary::ID_WinRes, Source) {
David Blaikief1c3bee2017-05-20 03:32:51 +000042 size_t LeadingSize = ResourceMagicSize + NullEntrySize;
Eric Beckmanna6bdf752017-05-20 01:49:19 +000043 BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize),
44 support::little);
45}
46
Eric Beckmanna6bdf752017-05-20 01:49:19 +000047Expected<std::unique_ptr<WindowsResource>>
48WindowsResource::createWindowsResource(MemoryBufferRef Source) {
David Blaikief1c3bee2017-05-20 03:32:51 +000049 if (Source.getBufferSize() < ResourceMagicSize + NullEntrySize)
Eric Beckmanna6bdf752017-05-20 01:49:19 +000050 return make_error<GenericBinaryError>(
51 "File too small to be a resource file",
52 object_error::invalid_file_type);
53 std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source));
54 return std::move(Ret);
55}
56
57Expected<ResourceEntryRef> WindowsResource::getHeadEntry() {
58 Error Err = Error::success();
59 auto Ref = ResourceEntryRef(BinaryStreamRef(BBS), this, Err);
60 if (Err)
61 return std::move(Err);
62 return Ref;
63}
64
65ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref,
66 const WindowsResource *Owner, Error &Err)
67 : Reader(Ref), OwningRes(Owner) {
68 if (loadNext())
69 Err = make_error<GenericBinaryError>("Could not read first entry.",
70 object_error::unexpected_eof);
71}
72
73Error ResourceEntryRef::moveNext(bool &End) {
74 // Reached end of all the entries.
75 if (Reader.bytesRemaining() == 0) {
76 End = true;
77 return Error::success();
78 }
79 RETURN_IF_ERROR(loadNext());
80
81 return Error::success();
82}
83
Eric Beckmann72fb6a82017-05-30 18:19:06 +000084static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID,
85 ArrayRef<UTF16> &Str, bool &IsString) {
86 uint16_t IDFlag;
87 RETURN_IF_ERROR(Reader.readInteger(IDFlag));
88 IsString = IDFlag != 0xffff;
89
90 if (IsString) {
91 Reader.setOffset(
92 Reader.getOffset() -
93 sizeof(uint16_t)); // Re-read the bytes which we used to check the flag.
94 RETURN_IF_ERROR(Reader.readWideString(Str));
95 } else
96 RETURN_IF_ERROR(Reader.readInteger(ID));
97
98 return Error::success();
99}
100
Eric Beckmanna6bdf752017-05-20 01:49:19 +0000101Error ResourceEntryRef::loadNext() {
102 uint32_t DataSize;
103 RETURN_IF_ERROR(Reader.readInteger(DataSize));
104 uint32_t HeaderSize;
105 RETURN_IF_ERROR(Reader.readInteger(HeaderSize));
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000106
107 if (HeaderSize < MIN_HEADER_SIZE)
108 return make_error<GenericBinaryError>("Header size is too small.",
109 object_error::parse_failed);
110
111 RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType));
112
113 RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName));
114
Eric Beckmanna6bdf752017-05-20 01:49:19 +0000115 RETURN_IF_ERROR(Reader.padToAlignment(sizeof(uint32_t)));
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000116
117 RETURN_IF_ERROR(Reader.readObject(Suffix));
118
119 RETURN_IF_ERROR(Reader.readArray(Data, DataSize));
120
121 RETURN_IF_ERROR(Reader.padToAlignment(sizeof(uint32_t)));
122
Eric Beckmanna6bdf752017-05-20 01:49:19 +0000123 return Error::success();
124}
125
Eric Beckmannd9de6382017-06-09 17:34:30 +0000126WindowsResourceParser::WindowsResourceParser() : Root(false) {}
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000127
128Error WindowsResourceParser::parse(WindowsResource *WR) {
129 auto EntryOrErr = WR->getHeadEntry();
130 if (!EntryOrErr)
131 return EntryOrErr.takeError();
132
133 ResourceEntryRef Entry = EntryOrErr.get();
134 bool End = false;
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000135 while (!End) {
Eric Beckmannd9de6382017-06-09 17:34:30 +0000136 Data.push_back(Entry.getData());
137
Eric Beckmann13017592017-06-13 00:16:32 +0000138 bool IsNewTypeString = false;
139 bool IsNewNameString = false;
140
141 Root.addEntry(Entry, IsNewTypeString, IsNewNameString);
142
143 if (IsNewTypeString)
Eric Beckmannd9de6382017-06-09 17:34:30 +0000144 StringTable.push_back(Entry.getTypeString());
145
Eric Beckmann13017592017-06-13 00:16:32 +0000146 if (IsNewNameString)
Eric Beckmannd9de6382017-06-09 17:34:30 +0000147 StringTable.push_back(Entry.getNameString());
148
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000149 RETURN_IF_ERROR(Entry.moveNext(End));
150 }
151
152 return Error::success();
153}
154
155void WindowsResourceParser::printTree() const {
156 ScopedPrinter Writer(outs());
157 Root.print(Writer, "Resource Tree");
158}
159
Eric Beckmann13017592017-06-13 00:16:32 +0000160void WindowsResourceParser::TreeNode::addEntry(const ResourceEntryRef &Entry,
161 bool &IsNewTypeString,
162 bool &IsNewNameString) {
163 TreeNode &TypeNode = addTypeNode(Entry, IsNewTypeString);
164 TreeNode &NameNode = TypeNode.addNameNode(Entry, IsNewNameString);
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000165 NameNode.addLanguageNode(Entry);
166}
167
Eric Beckmannd9de6382017-06-09 17:34:30 +0000168WindowsResourceParser::TreeNode::TreeNode(bool IsStringNode) {
169 if (IsStringNode)
170 StringIndex = StringCount++;
171}
172
173WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion,
174 uint16_t MinorVersion,
175 uint32_t Characteristics)
176 : IsDataNode(true), MajorVersion(MajorVersion), MinorVersion(MinorVersion),
177 Characteristics(Characteristics) {
Eric Beckmannd9de6382017-06-09 17:34:30 +0000178 DataIndex = DataCount++;
179}
180
181std::unique_ptr<WindowsResourceParser::TreeNode>
182WindowsResourceParser::TreeNode::createStringNode() {
183 return std::unique_ptr<TreeNode>(new TreeNode(true));
184}
185
186std::unique_ptr<WindowsResourceParser::TreeNode>
187WindowsResourceParser::TreeNode::createIDNode() {
188 return std::unique_ptr<TreeNode>(new TreeNode(false));
189}
190
191std::unique_ptr<WindowsResourceParser::TreeNode>
192WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion,
193 uint16_t MinorVersion,
194 uint32_t Characteristics) {
195 return std::unique_ptr<TreeNode>(
196 new TreeNode(MajorVersion, MinorVersion, Characteristics));
197}
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000198
199WindowsResourceParser::TreeNode &
Eric Beckmann13017592017-06-13 00:16:32 +0000200WindowsResourceParser::TreeNode::addTypeNode(const ResourceEntryRef &Entry,
201 bool &IsNewTypeString) {
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000202 if (Entry.checkTypeString())
Eric Beckmann13017592017-06-13 00:16:32 +0000203 return addChild(Entry.getTypeString(), IsNewTypeString);
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000204 else
205 return addChild(Entry.getTypeID());
206}
207
208WindowsResourceParser::TreeNode &
Eric Beckmann13017592017-06-13 00:16:32 +0000209WindowsResourceParser::TreeNode::addNameNode(const ResourceEntryRef &Entry,
210 bool &IsNewNameString) {
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000211 if (Entry.checkNameString())
Eric Beckmann13017592017-06-13 00:16:32 +0000212 return addChild(Entry.getNameString(), IsNewNameString);
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000213 else
214 return addChild(Entry.getNameID());
215}
216
217WindowsResourceParser::TreeNode &
218WindowsResourceParser::TreeNode::addLanguageNode(
219 const ResourceEntryRef &Entry) {
Eric Beckmannd9de6382017-06-09 17:34:30 +0000220 return addChild(Entry.getLanguage(), true, Entry.getMajorVersion(),
221 Entry.getMinorVersion(), Entry.getCharacteristics());
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000222}
223
Eric Beckmannd9de6382017-06-09 17:34:30 +0000224WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addChild(
225 uint32_t ID, bool IsDataNode, uint16_t MajorVersion, uint16_t MinorVersion,
226 uint32_t Characteristics) {
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000227 auto Child = IDChildren.find(ID);
228 if (Child == IDChildren.end()) {
Eric Beckmannd9de6382017-06-09 17:34:30 +0000229 auto NewChild =
230 IsDataNode ? createDataNode(MajorVersion, MinorVersion, Characteristics)
231 : createIDNode();
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000232 WindowsResourceParser::TreeNode &Node = *NewChild;
233 IDChildren.emplace(ID, std::move(NewChild));
234 return Node;
235 } else
236 return *(Child->second);
237}
238
239WindowsResourceParser::TreeNode &
Eric Beckmann13017592017-06-13 00:16:32 +0000240WindowsResourceParser::TreeNode::addChild(ArrayRef<UTF16> NameRef,
241 bool &IsNewString) {
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000242 std::string NameString;
243 ArrayRef<UTF16> CorrectedName;
Eric Beckmann025e82b2017-05-30 23:10:57 +0000244 std::vector<UTF16> EndianCorrectedName;
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000245 if (llvm::sys::IsBigEndianHost) {
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000246 EndianCorrectedName.resize(NameRef.size() + 1);
247 std::copy(NameRef.begin(), NameRef.end(), EndianCorrectedName.begin() + 1);
248 EndianCorrectedName[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED;
249 CorrectedName = makeArrayRef(EndianCorrectedName);
250 } else
251 CorrectedName = NameRef;
252 llvm::convertUTF16ToUTF8String(CorrectedName, NameString);
253
254 auto Child = StringChildren.find(NameString);
255 if (Child == StringChildren.end()) {
Eric Beckmannd9de6382017-06-09 17:34:30 +0000256 auto NewChild = createStringNode();
Eric Beckmann13017592017-06-13 00:16:32 +0000257 IsNewString = true;
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000258 WindowsResourceParser::TreeNode &Node = *NewChild;
259 StringChildren.emplace(NameString, std::move(NewChild));
260 return Node;
261 } else
262 return *(Child->second);
263}
264
265void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer,
266 StringRef Name) const {
267 ListScope NodeScope(Writer, Name);
268 for (auto const &Child : StringChildren) {
269 Child.second->print(Writer, Child.first);
270 }
271 for (auto const &Child : IDChildren) {
Eric Beckmannba395ef2017-05-30 22:29:06 +0000272 Child.second->print(Writer, to_string(Child.first));
Eric Beckmann72fb6a82017-05-30 18:19:06 +0000273 }
274}
275
Eric Beckmannd9de6382017-06-09 17:34:30 +0000276// This function returns the size of the entire resource tree, including
277// directory tables, directory entries, and data entries. It does not include
278// the directory strings or the relocations of the .rsrc section.
279uint32_t WindowsResourceParser::TreeNode::getTreeSize() const {
280 uint32_t Size = (IDChildren.size() + StringChildren.size()) *
281 sizeof(llvm::object::coff_resource_dir_entry);
282
283 // Reached a node pointing to a data entry.
284 if (IsDataNode) {
285 Size += sizeof(llvm::object::coff_resource_data_entry);
286 return Size;
287 }
288
289 // If the node does not point to data, it must have a directory table pointing
290 // to other nodes.
291 Size += sizeof(llvm::object::coff_resource_dir_table);
292
293 for (auto const &Child : StringChildren) {
294 Size += Child.second->getTreeSize();
295 }
296 for (auto const &Child : IDChildren) {
297 Size += Child.second->getTreeSize();
298 }
299 return Size;
300}
301
302class WindowsResourceCOFFWriter {
303public:
304 WindowsResourceCOFFWriter(StringRef OutputFile, Machine MachineType,
305 const WindowsResourceParser &Parser, Error &E);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000306 Error write();
307
308private:
309 void performFileLayout();
310 void performSectionOneLayout();
311 void performSectionTwoLayout();
312 void writeCOFFHeader();
313 void writeFirstSectionHeader();
314 void writeSecondSectionHeader();
315 void writeFirstSection();
316 void writeSecondSection();
317 void writeSymbolTable();
318 void writeStringTable();
319 void writeDirectoryTree();
320 void writeDirectoryStringTable();
321 void writeFirstSectionRelocations();
322 std::unique_ptr<FileOutputBuffer> Buffer;
Eric Beckmann56951cb2017-06-13 00:15:47 +0000323 uint8_t *Current;
Eric Beckmannd9de6382017-06-09 17:34:30 +0000324 Machine MachineType;
325 const WindowsResourceParser::TreeNode &Resources;
326 const ArrayRef<std::vector<uint8_t>> Data;
327 uint64_t FileSize;
328 uint32_t SymbolTableOffset;
329 uint32_t SectionOneSize;
330 uint32_t SectionOneOffset;
331 uint32_t SectionOneRelocations;
332 uint32_t SectionTwoSize;
333 uint32_t SectionTwoOffset;
334 const ArrayRef<std::vector<UTF16>> StringTable;
335 std::vector<uint32_t> StringTableOffsets;
336 std::vector<uint32_t> DataOffsets;
337 std::vector<uint32_t> RelocationAddresses;
338};
339
340WindowsResourceCOFFWriter::WindowsResourceCOFFWriter(
341 StringRef OutputFile, Machine MachineType,
342 const WindowsResourceParser &Parser, Error &E)
343 : MachineType(MachineType), Resources(Parser.getTree()),
344 Data(Parser.getData()), StringTable(Parser.getStringTable()) {
345 performFileLayout();
346
347 ErrorOr<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
348 FileOutputBuffer::create(OutputFile, FileSize);
349 if (!BufferOrErr) {
350 E = errorCodeToError(BufferOrErr.getError());
351 return;
352 }
353
354 Buffer = std::move(*BufferOrErr);
355}
356
357void WindowsResourceCOFFWriter::performFileLayout() {
358 // Add size of COFF header.
359 FileSize = llvm::COFF::Header16Size;
360
361 // one .rsrc section header for directory tree, another for resource data.
362 FileSize += 2 * llvm::COFF::SectionSize;
363
364 performSectionOneLayout();
365 performSectionTwoLayout();
366
367 // We have reached the address of the symbol table.
368 SymbolTableOffset = FileSize;
369
370 FileSize += llvm::COFF::Symbol16Size; // size of the @feat.00 symbol.
371 FileSize += 4 * llvm::COFF::Symbol16Size; // symbol + aux for each section.
372 FileSize += Data.size() * llvm::COFF::Symbol16Size; // 1 symbol per resource.
373 FileSize += 4; // four null bytes for the string table.
374}
375
376void WindowsResourceCOFFWriter::performSectionOneLayout() {
377 SectionOneOffset = FileSize;
378
379 SectionOneSize = Resources.getTreeSize();
380 uint32_t CurrentStringOffset = SectionOneSize;
381 uint32_t TotalStringTableSize = 0;
382 for (auto const &String : StringTable) {
383 StringTableOffsets.push_back(CurrentStringOffset);
384 uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t);
385 CurrentStringOffset += StringSize;
386 TotalStringTableSize += StringSize;
387 }
388 SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t));
389
390 // account for the relocations of section one.
391 SectionOneRelocations = FileSize + SectionOneSize;
392 FileSize += SectionOneSize;
393 FileSize += Data.size() *
394 llvm::COFF::RelocationSize; // one relocation for each resource.
395}
396
397void WindowsResourceCOFFWriter::performSectionTwoLayout() {
398 // add size of .rsrc$2 section, which contains all resource data on 8-byte
399 // alignment.
400 SectionTwoOffset = FileSize;
401 SectionTwoSize = 0;
402 for (auto const &Entry : Data) {
403 DataOffsets.push_back(SectionTwoSize);
404 SectionTwoSize += llvm::alignTo(Entry.size(), sizeof(uint64_t));
405 }
406 FileSize += SectionTwoSize;
407}
408
409static std::time_t getTime() {
410 std::time_t Now = time(nullptr);
411 if (Now < 0 || !isUInt<32>(Now))
412 return UINT32_MAX;
413 return Now;
414}
415
416Error WindowsResourceCOFFWriter::write() {
Eric Beckmann56951cb2017-06-13 00:15:47 +0000417 Current = Buffer->getBufferStart();
Eric Beckmannd9de6382017-06-09 17:34:30 +0000418
419 writeCOFFHeader();
420 writeFirstSectionHeader();
421 writeSecondSectionHeader();
422 writeFirstSection();
423 writeSecondSection();
424 writeSymbolTable();
425 writeStringTable();
426
427 if (auto EC = Buffer->commit()) {
428 return errorCodeToError(EC);
429 }
430
431 return Error::success();
432}
433
434void WindowsResourceCOFFWriter::writeCOFFHeader() {
435 // Write the COFF header.
Eric Beckmann56951cb2017-06-13 00:15:47 +0000436 auto *Header = reinterpret_cast<llvm::object::coff_file_header *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000437 switch (MachineType) {
438 case Machine::ARM:
439 Header->Machine = llvm::COFF::IMAGE_FILE_MACHINE_ARMNT;
440 break;
441 case Machine::X64:
442 Header->Machine = llvm::COFF::IMAGE_FILE_MACHINE_AMD64;
443 break;
444 case Machine::X86:
445 Header->Machine = llvm::COFF::IMAGE_FILE_MACHINE_I386;
446 break;
447 default:
448 Header->Machine = llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN;
449 }
450 Header->NumberOfSections = 2;
451 Header->TimeDateStamp = getTime();
452 Header->PointerToSymbolTable = SymbolTableOffset;
453 // One symbol for every resource plus 2 for each section and @feat.00
454 Header->NumberOfSymbols = Data.size() + 5;
455 Header->SizeOfOptionalHeader = 0;
456 Header->Characteristics = llvm::COFF::IMAGE_FILE_32BIT_MACHINE;
457}
458
459void WindowsResourceCOFFWriter::writeFirstSectionHeader() {
460 // Write the first section header.
Eric Beckmann56951cb2017-06-13 00:15:47 +0000461 Current += sizeof(llvm::object::coff_file_header);
462 auto *SectionOneHeader =
463 reinterpret_cast<llvm::object::coff_section *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000464 strncpy(SectionOneHeader->Name, ".rsrc$01", (size_t)llvm::COFF::NameSize);
465 SectionOneHeader->VirtualSize = 0;
466 SectionOneHeader->VirtualAddress = 0;
467 SectionOneHeader->SizeOfRawData = SectionOneSize;
468 SectionOneHeader->PointerToRawData = SectionOneOffset;
469 SectionOneHeader->PointerToRelocations = SectionOneRelocations;
470 SectionOneHeader->PointerToLinenumbers = 0;
471 SectionOneHeader->NumberOfRelocations = Data.size();
472 SectionOneHeader->NumberOfLinenumbers = 0;
473 SectionOneHeader->Characteristics = llvm::COFF::IMAGE_SCN_ALIGN_1BYTES;
474 SectionOneHeader->Characteristics +=
475 llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
476 SectionOneHeader->Characteristics += llvm::COFF::IMAGE_SCN_MEM_DISCARDABLE;
477 SectionOneHeader->Characteristics += llvm::COFF::IMAGE_SCN_MEM_READ;
478}
479
480void WindowsResourceCOFFWriter::writeSecondSectionHeader() {
481 // Write the second section header.
Eric Beckmann56951cb2017-06-13 00:15:47 +0000482 Current += sizeof(llvm::object::coff_section);
483 auto *SectionTwoHeader =
484 reinterpret_cast<llvm::object::coff_section *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000485 strncpy(SectionTwoHeader->Name, ".rsrc$02", (size_t)llvm::COFF::NameSize);
486 SectionTwoHeader->VirtualSize = 0;
487 SectionTwoHeader->VirtualAddress = 0;
488 SectionTwoHeader->SizeOfRawData = SectionTwoSize;
489 SectionTwoHeader->PointerToRawData = SectionTwoOffset;
490 SectionTwoHeader->PointerToRelocations = 0;
491 SectionTwoHeader->PointerToLinenumbers = 0;
492 SectionTwoHeader->NumberOfRelocations = 0;
493 SectionTwoHeader->NumberOfLinenumbers = 0;
494 SectionTwoHeader->Characteristics =
495 llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
496 SectionTwoHeader->Characteristics += llvm::COFF::IMAGE_SCN_MEM_READ;
497}
498
499void WindowsResourceCOFFWriter::writeFirstSection() {
500 // Write section one.
Eric Beckmann56951cb2017-06-13 00:15:47 +0000501 Current += sizeof(llvm::object::coff_section);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000502
503 writeDirectoryTree();
504 writeDirectoryStringTable();
505 writeFirstSectionRelocations();
506}
507
508void WindowsResourceCOFFWriter::writeSecondSection() {
509 // Now write the .rsrc$02 section.
510 for (auto const &RawDataEntry : Data) {
Eric Beckmann56951cb2017-06-13 00:15:47 +0000511 std::copy(RawDataEntry.begin(), RawDataEntry.end(), Current);
512 Current += alignTo(RawDataEntry.size(), sizeof(uint64_t));
Eric Beckmannd9de6382017-06-09 17:34:30 +0000513 }
514}
515
516void WindowsResourceCOFFWriter::writeSymbolTable() {
517 // Now write the symbol table.
518 // First, the feat symbol.
Eric Beckmann56951cb2017-06-13 00:15:47 +0000519 auto *Symbol = reinterpret_cast<llvm::object::coff_symbol16 *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000520 strncpy(Symbol->Name.ShortName, "@feat.00", (size_t)llvm::COFF::NameSize);
521 Symbol->Value = 0x11;
522 Symbol->SectionNumber = 0xffff;
523 Symbol->Type = llvm::COFF::IMAGE_SYM_DTYPE_NULL;
524 Symbol->StorageClass = llvm::COFF::IMAGE_SYM_CLASS_STATIC;
525 Symbol->NumberOfAuxSymbols = 0;
Eric Beckmann56951cb2017-06-13 00:15:47 +0000526 Current += sizeof(llvm::object::coff_symbol16);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000527
528 // Now write the .rsrc1 symbol + aux.
Eric Beckmann56951cb2017-06-13 00:15:47 +0000529 Symbol = reinterpret_cast<llvm::object::coff_symbol16 *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000530 strncpy(Symbol->Name.ShortName, ".rsrc$01", (size_t)llvm::COFF::NameSize);
531 Symbol->Value = 0;
532 Symbol->SectionNumber = 1;
533 Symbol->Type = llvm::COFF::IMAGE_SYM_DTYPE_NULL;
534 Symbol->StorageClass = llvm::COFF::IMAGE_SYM_CLASS_STATIC;
535 Symbol->NumberOfAuxSymbols = 1;
Eric Beckmann56951cb2017-06-13 00:15:47 +0000536 Current += sizeof(llvm::object::coff_symbol16);
537 auto *Aux =
538 reinterpret_cast<llvm::object::coff_aux_section_definition *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000539 Aux->Length = SectionOneSize;
540 Aux->NumberOfRelocations = Data.size();
541 Aux->NumberOfLinenumbers = 0;
542 Aux->CheckSum = 0;
543 Aux->NumberLowPart = 0;
544 Aux->Selection = 0;
Eric Beckmann56951cb2017-06-13 00:15:47 +0000545 Current += sizeof(llvm::object::coff_aux_section_definition);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000546
547 // Now write the .rsrc2 symbol + aux.
Eric Beckmann56951cb2017-06-13 00:15:47 +0000548 Symbol = reinterpret_cast<llvm::object::coff_symbol16 *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000549 strncpy(Symbol->Name.ShortName, ".rsrc$02", (size_t)llvm::COFF::NameSize);
550 Symbol->Value = 0;
551 Symbol->SectionNumber = 2;
552 Symbol->Type = llvm::COFF::IMAGE_SYM_DTYPE_NULL;
553 Symbol->StorageClass = llvm::COFF::IMAGE_SYM_CLASS_STATIC;
554 Symbol->NumberOfAuxSymbols = 1;
Eric Beckmann56951cb2017-06-13 00:15:47 +0000555 Current += sizeof(llvm::object::coff_symbol16);
556 Aux = reinterpret_cast<llvm::object::coff_aux_section_definition *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000557 Aux->Length = SectionTwoSize;
558 Aux->NumberOfRelocations = 0;
559 Aux->NumberOfLinenumbers = 0;
560 Aux->CheckSum = 0;
561 Aux->NumberLowPart = 0;
562 Aux->Selection = 0;
Eric Beckmann56951cb2017-06-13 00:15:47 +0000563 Current += sizeof(llvm::object::coff_aux_section_definition);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000564
565 // Now write a symbol for each relocation.
566 for (unsigned i = 0; i < Data.size(); i++) {
567 char RelocationName[9];
568 sprintf(RelocationName, "$R%06X", DataOffsets[i]);
Eric Beckmann56951cb2017-06-13 00:15:47 +0000569 Symbol = reinterpret_cast<llvm::object::coff_symbol16 *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000570 strncpy(Symbol->Name.ShortName, RelocationName,
571 (size_t)llvm::COFF::NameSize);
572 Symbol->Value = DataOffsets[i];
573 Symbol->SectionNumber = 1;
574 Symbol->Type = llvm::COFF::IMAGE_SYM_DTYPE_NULL;
575 Symbol->StorageClass = llvm::COFF::IMAGE_SYM_CLASS_STATIC;
576 Symbol->NumberOfAuxSymbols = 0;
Eric Beckmann56951cb2017-06-13 00:15:47 +0000577 Current += sizeof(llvm::object::coff_symbol16);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000578 }
579}
580
581void WindowsResourceCOFFWriter::writeStringTable() {
582 // Just 4 null bytes for the string table.
Eric Beckmann56951cb2017-06-13 00:15:47 +0000583 auto COFFStringTable = reinterpret_cast<void *>(Current);
584 memset(COFFStringTable, 0, 4);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000585}
586
587void WindowsResourceCOFFWriter::writeDirectoryTree() {
588 // Traverse parsed resource tree breadth-first and write the corresponding
589 // COFF objects.
590 std::queue<const WindowsResourceParser::TreeNode *> Queue;
591 Queue.push(&Resources);
592 uint32_t NextLevelOffset = sizeof(llvm::object::coff_resource_dir_table) +
593 (Resources.getStringChildren().size() +
594 Resources.getIDChildren().size()) *
595 sizeof(llvm::object::coff_resource_dir_entry);
596 std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder;
597 uint32_t CurrentRelativeOffset = 0;
598
599 while (!Queue.empty()) {
600 auto CurrentNode = Queue.front();
601 Queue.pop();
Eric Beckmann56951cb2017-06-13 00:15:47 +0000602 auto *Table =
603 reinterpret_cast<llvm::object::coff_resource_dir_table *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000604 Table->Characteristics = CurrentNode->getCharacteristics();
605 Table->TimeDateStamp = 0;
606 Table->MajorVersion = CurrentNode->getMajorVersion();
607 Table->MinorVersion = CurrentNode->getMinorVersion();
608 auto &IDChildren = CurrentNode->getIDChildren();
609 auto &StringChildren = CurrentNode->getStringChildren();
610 Table->NumberOfNameEntries = StringChildren.size();
611 Table->NumberOfIDEntries = IDChildren.size();
Eric Beckmann56951cb2017-06-13 00:15:47 +0000612 Current += sizeof(llvm::object::coff_resource_dir_table);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000613 CurrentRelativeOffset += sizeof(llvm::object::coff_resource_dir_table);
614
615 // Write the directory entries immediately following each directory table.
616 for (auto const &Child : StringChildren) {
Eric Beckmann56951cb2017-06-13 00:15:47 +0000617 auto *Entry =
618 reinterpret_cast<llvm::object::coff_resource_dir_entry *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000619 Entry->Identifier.NameOffset =
620 StringTableOffsets[Child.second->getStringIndex()];
621 if (Child.second->checkIsDataNode()) {
622 Entry->Offset.DataEntryOffset = NextLevelOffset;
623 NextLevelOffset += sizeof(llvm::object::coff_resource_data_entry);
624 DataEntriesTreeOrder.push_back(Child.second.get());
625 } else {
626 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);
627 NextLevelOffset += sizeof(llvm::object::coff_resource_dir_table) +
628 (Child.second->getStringChildren().size() +
629 Child.second->getIDChildren().size()) *
630 sizeof(llvm::object::coff_resource_dir_entry);
631 Queue.push(Child.second.get());
632 }
Eric Beckmann56951cb2017-06-13 00:15:47 +0000633 Current += sizeof(llvm::object::coff_resource_dir_entry);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000634 CurrentRelativeOffset += sizeof(llvm::object::coff_resource_dir_entry);
635 }
636 for (auto const &Child : IDChildren) {
Eric Beckmann56951cb2017-06-13 00:15:47 +0000637 auto *Entry =
638 reinterpret_cast<llvm::object::coff_resource_dir_entry *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000639 Entry->Identifier.ID = Child.first;
640 if (Child.second->checkIsDataNode()) {
641 Entry->Offset.DataEntryOffset = NextLevelOffset;
642 NextLevelOffset += sizeof(llvm::object::coff_resource_data_entry);
643 DataEntriesTreeOrder.push_back(Child.second.get());
644 } else {
645 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);
646 NextLevelOffset += sizeof(llvm::object::coff_resource_dir_table) +
647 (Child.second->getStringChildren().size() +
648 Child.second->getIDChildren().size()) *
649 sizeof(llvm::object::coff_resource_dir_entry);
650 Queue.push(Child.second.get());
651 }
Eric Beckmann56951cb2017-06-13 00:15:47 +0000652 Current += sizeof(llvm::object::coff_resource_dir_entry);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000653 CurrentRelativeOffset += sizeof(llvm::object::coff_resource_dir_entry);
654 }
655 }
656
657 RelocationAddresses.resize(Data.size());
658 // Now write all the resource data entries.
659 for (auto DataNodes : DataEntriesTreeOrder) {
Eric Beckmann56951cb2017-06-13 00:15:47 +0000660 auto *Entry =
661 reinterpret_cast<llvm::object::coff_resource_data_entry *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000662 RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset;
663 Entry->DataRVA = 0; // Set to zero because it is a relocation.
664 Entry->DataSize = Data[DataNodes->getDataIndex()].size();
665 Entry->Codepage = 0;
666 Entry->Reserved = 0;
Eric Beckmann56951cb2017-06-13 00:15:47 +0000667 Current += sizeof(llvm::object::coff_resource_data_entry);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000668 CurrentRelativeOffset += sizeof(llvm::object::coff_resource_data_entry);
669 }
670}
671
672void WindowsResourceCOFFWriter::writeDirectoryStringTable() {
673 // Now write the directory string table for .rsrc$01
674 uint32_t TotalStringTableSize = 0;
675 for (auto String : StringTable) {
Eric Beckmann56951cb2017-06-13 00:15:47 +0000676 auto *LengthField = reinterpret_cast<uint16_t *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000677 uint16_t Length = String.size();
678 *LengthField = Length;
Eric Beckmann56951cb2017-06-13 00:15:47 +0000679 Current += sizeof(uint16_t);
680 auto *Start = reinterpret_cast<UTF16 *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000681 std::copy(String.begin(), String.end(), Start);
Eric Beckmann56951cb2017-06-13 00:15:47 +0000682 Current += Length * sizeof(UTF16);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000683 TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t);
684 }
Eric Beckmann56951cb2017-06-13 00:15:47 +0000685 Current +=
Eric Beckmannd9de6382017-06-09 17:34:30 +0000686 alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize;
687}
688
689void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {
690
691 // Now write the relocations for .rsrc$01
692 // Five symbols already in table before we start, @feat.00 and 2 for each
693 // .rsrc section.
694 uint32_t NextSymbolIndex = 5;
695 for (unsigned i = 0; i < Data.size(); i++) {
Eric Beckmann56951cb2017-06-13 00:15:47 +0000696 auto *Reloc = reinterpret_cast<llvm::object::coff_relocation *>(Current);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000697 Reloc->VirtualAddress = RelocationAddresses[i];
698 Reloc->SymbolTableIndex = NextSymbolIndex++;
699 switch (MachineType) {
700 case Machine::ARM:
701 Reloc->Type = llvm::COFF::IMAGE_REL_ARM_ADDR32NB;
702 break;
703 case Machine::X64:
704 Reloc->Type = llvm::COFF::IMAGE_REL_AMD64_ADDR32NB;
705 break;
706 case Machine::X86:
707 Reloc->Type = llvm::COFF::IMAGE_REL_I386_DIR32NB;
708 break;
709 default:
710 Reloc->Type = 0;
711 }
Eric Beckmann56951cb2017-06-13 00:15:47 +0000712 Current += sizeof(llvm::object::coff_relocation);
Eric Beckmannd9de6382017-06-09 17:34:30 +0000713 }
714}
715
716Error writeWindowsResourceCOFF(StringRef OutputFile, Machine MachineType,
717 const WindowsResourceParser &Parser) {
718 Error E = Error::success();
719 WindowsResourceCOFFWriter Writer(OutputFile, MachineType, Parser, E);
720 if (E)
721 return E;
722 return Writer.write();
723}
724
Eric Beckmanna6bdf752017-05-20 01:49:19 +0000725} // namespace object
726} // namespace llvm