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