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