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