[llvm-objdump] - Remove one overload of reportError. NFCI.
[llvm-complete.git] / lib / Object / WindowsResource.cpp
blob762e8bc81d85322d183edad9a528d36bf933b8be
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 // According to COFF specification, if the Src has a size equal to Dest,
519 // it's okay to *not* copy the trailing zero.
520 static void coffnamecpy(char (&Dest)[COFF::NameSize], StringRef Src) {
521 assert(Src.size() <= COFF::NameSize &&
522 "Src is not larger than COFF::NameSize");
523 strncpy(Dest, Src.data(), (size_t)COFF::NameSize);
526 void WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp) {
527 // Write the COFF header.
528 auto *Header = reinterpret_cast<coff_file_header *>(BufferStart);
529 Header->Machine = MachineType;
530 Header->NumberOfSections = 2;
531 Header->TimeDateStamp = TimeDateStamp;
532 Header->PointerToSymbolTable = SymbolTableOffset;
533 // One symbol for every resource plus 2 for each section and 1 for @feat.00
534 Header->NumberOfSymbols = Data.size() + 5;
535 Header->SizeOfOptionalHeader = 0;
536 // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it.
537 Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE;
540 void WindowsResourceCOFFWriter::writeFirstSectionHeader() {
541 // Write the first section header.
542 CurrentOffset += sizeof(coff_file_header);
543 auto *SectionOneHeader =
544 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);
545 coffnamecpy(SectionOneHeader->Name, ".rsrc$01");
546 SectionOneHeader->VirtualSize = 0;
547 SectionOneHeader->VirtualAddress = 0;
548 SectionOneHeader->SizeOfRawData = SectionOneSize;
549 SectionOneHeader->PointerToRawData = SectionOneOffset;
550 SectionOneHeader->PointerToRelocations = SectionOneRelocations;
551 SectionOneHeader->PointerToLinenumbers = 0;
552 SectionOneHeader->NumberOfRelocations = Data.size();
553 SectionOneHeader->NumberOfLinenumbers = 0;
554 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
555 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;
558 void WindowsResourceCOFFWriter::writeSecondSectionHeader() {
559 // Write the second section header.
560 CurrentOffset += sizeof(coff_section);
561 auto *SectionTwoHeader =
562 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);
563 coffnamecpy(SectionTwoHeader->Name, ".rsrc$02");
564 SectionTwoHeader->VirtualSize = 0;
565 SectionTwoHeader->VirtualAddress = 0;
566 SectionTwoHeader->SizeOfRawData = SectionTwoSize;
567 SectionTwoHeader->PointerToRawData = SectionTwoOffset;
568 SectionTwoHeader->PointerToRelocations = 0;
569 SectionTwoHeader->PointerToLinenumbers = 0;
570 SectionTwoHeader->NumberOfRelocations = 0;
571 SectionTwoHeader->NumberOfLinenumbers = 0;
572 SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
573 SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;
576 void WindowsResourceCOFFWriter::writeFirstSection() {
577 // Write section one.
578 CurrentOffset += sizeof(coff_section);
580 writeDirectoryTree();
581 writeDirectoryStringTable();
582 writeFirstSectionRelocations();
584 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);
587 void WindowsResourceCOFFWriter::writeSecondSection() {
588 // Now write the .rsrc$02 section.
589 for (auto const &RawDataEntry : Data) {
590 llvm::copy(RawDataEntry, BufferStart + CurrentOffset);
591 CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t));
594 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);
597 void WindowsResourceCOFFWriter::writeSymbolTable() {
598 // Now write the symbol table.
599 // First, the feat symbol.
600 auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
601 coffnamecpy(Symbol->Name.ShortName, "@feat.00");
602 Symbol->Value = 0x11;
603 Symbol->SectionNumber = 0xffff;
604 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
605 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
606 Symbol->NumberOfAuxSymbols = 0;
607 CurrentOffset += sizeof(coff_symbol16);
609 // Now write the .rsrc1 symbol + aux.
610 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
611 coffnamecpy(Symbol->Name.ShortName, ".rsrc$01");
612 Symbol->Value = 0;
613 Symbol->SectionNumber = 1;
614 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
615 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
616 Symbol->NumberOfAuxSymbols = 1;
617 CurrentOffset += sizeof(coff_symbol16);
618 auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +
619 CurrentOffset);
620 Aux->Length = SectionOneSize;
621 Aux->NumberOfRelocations = Data.size();
622 Aux->NumberOfLinenumbers = 0;
623 Aux->CheckSum = 0;
624 Aux->NumberLowPart = 0;
625 Aux->Selection = 0;
626 CurrentOffset += sizeof(coff_aux_section_definition);
628 // Now write the .rsrc2 symbol + aux.
629 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
630 coffnamecpy(Symbol->Name.ShortName, ".rsrc$02");
631 Symbol->Value = 0;
632 Symbol->SectionNumber = 2;
633 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
634 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
635 Symbol->NumberOfAuxSymbols = 1;
636 CurrentOffset += sizeof(coff_symbol16);
637 Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +
638 CurrentOffset);
639 Aux->Length = SectionTwoSize;
640 Aux->NumberOfRelocations = 0;
641 Aux->NumberOfLinenumbers = 0;
642 Aux->CheckSum = 0;
643 Aux->NumberLowPart = 0;
644 Aux->Selection = 0;
645 CurrentOffset += sizeof(coff_aux_section_definition);
647 // Now write a symbol for each relocation.
648 for (unsigned i = 0; i < Data.size(); i++) {
649 auto RelocationName = formatv("$R{0:X-6}", i & 0xffffff).sstr<COFF::NameSize>();
650 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);
651 coffnamecpy(Symbol->Name.ShortName, RelocationName);
652 Symbol->Value = DataOffsets[i];
653 Symbol->SectionNumber = 2;
654 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;
655 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
656 Symbol->NumberOfAuxSymbols = 0;
657 CurrentOffset += sizeof(coff_symbol16);
661 void WindowsResourceCOFFWriter::writeStringTable() {
662 // Just 4 null bytes for the string table.
663 auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset);
664 memset(COFFStringTable, 0, 4);
667 void WindowsResourceCOFFWriter::writeDirectoryTree() {
668 // Traverse parsed resource tree breadth-first and write the corresponding
669 // COFF objects.
670 std::queue<const WindowsResourceParser::TreeNode *> Queue;
671 Queue.push(&Resources);
672 uint32_t NextLevelOffset =
673 sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() +
674 Resources.getIDChildren().size()) *
675 sizeof(coff_resource_dir_entry);
676 std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder;
677 uint32_t CurrentRelativeOffset = 0;
679 while (!Queue.empty()) {
680 auto CurrentNode = Queue.front();
681 Queue.pop();
682 auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart +
683 CurrentOffset);
684 Table->Characteristics = CurrentNode->getCharacteristics();
685 Table->TimeDateStamp = 0;
686 Table->MajorVersion = CurrentNode->getMajorVersion();
687 Table->MinorVersion = CurrentNode->getMinorVersion();
688 auto &IDChildren = CurrentNode->getIDChildren();
689 auto &StringChildren = CurrentNode->getStringChildren();
690 Table->NumberOfNameEntries = StringChildren.size();
691 Table->NumberOfIDEntries = IDChildren.size();
692 CurrentOffset += sizeof(coff_resource_dir_table);
693 CurrentRelativeOffset += sizeof(coff_resource_dir_table);
695 // Write the directory entries immediately following each directory table.
696 for (auto const &Child : StringChildren) {
697 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +
698 CurrentOffset);
699 Entry->Identifier.setNameOffset(
700 StringTableOffsets[Child.second->getStringIndex()]);
701 if (Child.second->checkIsDataNode()) {
702 Entry->Offset.DataEntryOffset = NextLevelOffset;
703 NextLevelOffset += sizeof(coff_resource_data_entry);
704 DataEntriesTreeOrder.push_back(Child.second.get());
705 } else {
706 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);
707 NextLevelOffset += sizeof(coff_resource_dir_table) +
708 (Child.second->getStringChildren().size() +
709 Child.second->getIDChildren().size()) *
710 sizeof(coff_resource_dir_entry);
711 Queue.push(Child.second.get());
713 CurrentOffset += sizeof(coff_resource_dir_entry);
714 CurrentRelativeOffset += sizeof(coff_resource_dir_entry);
716 for (auto const &Child : IDChildren) {
717 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +
718 CurrentOffset);
719 Entry->Identifier.ID = Child.first;
720 if (Child.second->checkIsDataNode()) {
721 Entry->Offset.DataEntryOffset = NextLevelOffset;
722 NextLevelOffset += sizeof(coff_resource_data_entry);
723 DataEntriesTreeOrder.push_back(Child.second.get());
724 } else {
725 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);
726 NextLevelOffset += sizeof(coff_resource_dir_table) +
727 (Child.second->getStringChildren().size() +
728 Child.second->getIDChildren().size()) *
729 sizeof(coff_resource_dir_entry);
730 Queue.push(Child.second.get());
732 CurrentOffset += sizeof(coff_resource_dir_entry);
733 CurrentRelativeOffset += sizeof(coff_resource_dir_entry);
737 RelocationAddresses.resize(Data.size());
738 // Now write all the resource data entries.
739 for (auto DataNodes : DataEntriesTreeOrder) {
740 auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart +
741 CurrentOffset);
742 RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset;
743 Entry->DataRVA = 0; // Set to zero because it is a relocation.
744 Entry->DataSize = Data[DataNodes->getDataIndex()].size();
745 Entry->Codepage = 0;
746 Entry->Reserved = 0;
747 CurrentOffset += sizeof(coff_resource_data_entry);
748 CurrentRelativeOffset += sizeof(coff_resource_data_entry);
752 void WindowsResourceCOFFWriter::writeDirectoryStringTable() {
753 // Now write the directory string table for .rsrc$01
754 uint32_t TotalStringTableSize = 0;
755 for (auto &String : StringTable) {
756 uint16_t Length = String.size();
757 support::endian::write16le(BufferStart + CurrentOffset, Length);
758 CurrentOffset += sizeof(uint16_t);
759 auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset);
760 llvm::copy(String, Start);
761 CurrentOffset += Length * sizeof(UTF16);
762 TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t);
764 CurrentOffset +=
765 alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize;
768 void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {
770 // Now write the relocations for .rsrc$01
771 // Five symbols already in table before we start, @feat.00 and 2 for each
772 // .rsrc section.
773 uint32_t NextSymbolIndex = 5;
774 for (unsigned i = 0; i < Data.size(); i++) {
775 auto *Reloc =
776 reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset);
777 Reloc->VirtualAddress = RelocationAddresses[i];
778 Reloc->SymbolTableIndex = NextSymbolIndex++;
779 switch (MachineType) {
780 case COFF::IMAGE_FILE_MACHINE_ARMNT:
781 Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB;
782 break;
783 case COFF::IMAGE_FILE_MACHINE_AMD64:
784 Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB;
785 break;
786 case COFF::IMAGE_FILE_MACHINE_I386:
787 Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB;
788 break;
789 case COFF::IMAGE_FILE_MACHINE_ARM64:
790 Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB;
791 break;
792 default:
793 llvm_unreachable("unknown machine type");
795 CurrentOffset += sizeof(coff_relocation);
799 Expected<std::unique_ptr<MemoryBuffer>>
800 writeWindowsResourceCOFF(COFF::MachineTypes MachineType,
801 const WindowsResourceParser &Parser,
802 uint32_t TimeDateStamp) {
803 Error E = Error::success();
804 WindowsResourceCOFFWriter Writer(MachineType, Parser, E);
805 if (E)
806 return std::move(E);
807 return Writer.write(TimeDateStamp);
810 } // namespace object
811 } // namespace llvm