1 //===-- WindowsResource.cpp -------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
21 #include <system_error>
24 using namespace object
;
29 #define RETURN_IF_ERROR(X) \
33 #define UNWRAP_REF_OR_RETURN(Name, Expr) \
34 auto Name##OrErr = Expr; \
36 return Name##OrErr.takeError(); \
37 const auto &Name = *Name##OrErr;
39 #define UNWRAP_OR_RETURN(Name, Expr) \
40 auto Name##OrErr = Expr; \
42 return Name##OrErr.takeError(); \
43 auto Name = *Name##OrErr;
45 const uint32_t MIN_HEADER_SIZE
= 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t);
47 // COFF files seem to be inconsistent with alignment between sections, just use
48 // 8-byte because it makes everyone happy.
49 const uint32_t SECTION_ALIGNMENT
= sizeof(uint64_t);
51 WindowsResource::WindowsResource(MemoryBufferRef Source
)
52 : Binary(Binary::ID_WinRes
, Source
) {
53 size_t LeadingSize
= WIN_RES_MAGIC_SIZE
+ WIN_RES_NULL_ENTRY_SIZE
;
54 BBS
= BinaryByteStream(Data
.getBuffer().drop_front(LeadingSize
),
59 Expected
<std::unique_ptr
<WindowsResource
>>
60 WindowsResource::createWindowsResource(MemoryBufferRef Source
) {
61 if (Source
.getBufferSize() < WIN_RES_MAGIC_SIZE
+ WIN_RES_NULL_ENTRY_SIZE
)
62 return make_error
<GenericBinaryError
>(
63 Source
.getBufferIdentifier() + ": too small to be a resource file",
64 object_error::invalid_file_type
);
65 std::unique_ptr
<WindowsResource
> Ret(new WindowsResource(Source
));
66 return std::move(Ret
);
69 Expected
<ResourceEntryRef
> WindowsResource::getHeadEntry() {
70 if (BBS
.getLength() < sizeof(WinResHeaderPrefix
) + sizeof(WinResHeaderSuffix
))
71 return make_error
<EmptyResError
>(getFileName() + " contains no entries",
72 object_error::unexpected_eof
);
73 return ResourceEntryRef::create(BinaryStreamRef(BBS
), this);
76 ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref
,
77 const WindowsResource
*Owner
)
78 : Reader(Ref
), Owner(Owner
) {}
80 Expected
<ResourceEntryRef
>
81 ResourceEntryRef::create(BinaryStreamRef BSR
, const WindowsResource
*Owner
) {
82 auto Ref
= ResourceEntryRef(BSR
, Owner
);
83 if (auto E
= Ref
.loadNext())
88 Error
ResourceEntryRef::moveNext(bool &End
) {
89 // Reached end of all the entries.
90 if (Reader
.bytesRemaining() == 0) {
92 return Error::success();
94 RETURN_IF_ERROR(loadNext());
96 return Error::success();
99 static Error
readStringOrId(BinaryStreamReader
&Reader
, uint16_t &ID
,
100 ArrayRef
<UTF16
> &Str
, bool &IsString
) {
102 RETURN_IF_ERROR(Reader
.readInteger(IDFlag
));
103 IsString
= IDFlag
!= 0xffff;
108 sizeof(uint16_t)); // Re-read the bytes which we used to check the flag.
109 RETURN_IF_ERROR(Reader
.readWideString(Str
));
111 RETURN_IF_ERROR(Reader
.readInteger(ID
));
113 return Error::success();
116 Error
ResourceEntryRef::loadNext() {
117 const WinResHeaderPrefix
*Prefix
;
118 RETURN_IF_ERROR(Reader
.readObject(Prefix
));
120 if (Prefix
->HeaderSize
< MIN_HEADER_SIZE
)
121 return make_error
<GenericBinaryError
>(Owner
->getFileName() +
122 ": header size too small",
123 object_error::parse_failed
);
125 RETURN_IF_ERROR(readStringOrId(Reader
, TypeID
, Type
, IsStringType
));
127 RETURN_IF_ERROR(readStringOrId(Reader
, NameID
, Name
, IsStringName
));
129 RETURN_IF_ERROR(Reader
.padToAlignment(WIN_RES_HEADER_ALIGNMENT
));
131 RETURN_IF_ERROR(Reader
.readObject(Suffix
));
133 RETURN_IF_ERROR(Reader
.readArray(Data
, Prefix
->DataSize
));
135 RETURN_IF_ERROR(Reader
.padToAlignment(WIN_RES_DATA_ALIGNMENT
));
137 return Error::success();
140 WindowsResourceParser::WindowsResourceParser() : Root(false) {}
142 void printResourceTypeName(uint16_t TypeID
, raw_ostream
&OS
) {
144 case 1: OS
<< "CURSOR (ID 1)"; break;
145 case 2: OS
<< "BITMAP (ID 2)"; break;
146 case 3: OS
<< "ICON (ID 3)"; break;
147 case 4: OS
<< "MENU (ID 4)"; break;
148 case 5: OS
<< "DIALOG (ID 5)"; break;
149 case 6: OS
<< "STRINGTABLE (ID 6)"; break;
150 case 7: OS
<< "FONTDIR (ID 7)"; break;
151 case 8: OS
<< "FONT (ID 8)"; break;
152 case 9: OS
<< "ACCELERATOR (ID 9)"; break;
153 case 10: OS
<< "RCDATA (ID 10)"; break;
154 case 11: OS
<< "MESSAGETABLE (ID 11)"; break;
155 case 12: OS
<< "GROUP_CURSOR (ID 12)"; break;
156 case 14: OS
<< "GROUP_ICON (ID 14)"; break;
157 case 16: OS
<< "VERSIONINFO (ID 16)"; break;
158 case 17: OS
<< "DLGINCLUDE (ID 17)"; break;
159 case 19: OS
<< "PLUGPLAY (ID 19)"; break;
160 case 20: OS
<< "VXD (ID 20)"; break;
161 case 21: OS
<< "ANICURSOR (ID 21)"; break;
162 case 22: OS
<< "ANIICON (ID 22)"; break;
163 case 23: OS
<< "HTML (ID 23)"; break;
164 case 24: OS
<< "MANIFEST (ID 24)"; break;
165 default: OS
<< "ID " << TypeID
; break;
169 static bool convertUTF16LEToUTF8String(ArrayRef
<UTF16
> Src
, std::string
&Out
) {
170 if (!sys::IsBigEndianHost
)
171 return convertUTF16ToUTF8String(Src
, Out
);
173 std::vector
<UTF16
> EndianCorrectedSrc
;
174 EndianCorrectedSrc
.resize(Src
.size() + 1);
175 llvm::copy(Src
, EndianCorrectedSrc
.begin() + 1);
176 EndianCorrectedSrc
[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED
;
177 return convertUTF16ToUTF8String(makeArrayRef(EndianCorrectedSrc
), Out
);
180 static std::string
makeDuplicateResourceError(
181 const ResourceEntryRef
&Entry
, StringRef File1
, StringRef File2
) {
183 raw_string_ostream
OS(Ret
);
185 OS
<< "duplicate resource:";
188 if (Entry
.checkTypeString()) {
190 if (!convertUTF16LEToUTF8String(Entry
.getTypeString(), UTF8
))
191 UTF8
= "(failed conversion from UTF16)";
192 OS
<< '\"' << UTF8
<< '\"';
194 printResourceTypeName(Entry
.getTypeID(), OS
);
197 if (Entry
.checkNameString()) {
199 if (!convertUTF16LEToUTF8String(Entry
.getNameString(), UTF8
))
200 UTF8
= "(failed conversion from UTF16)";
201 OS
<< '\"' << UTF8
<< '\"';
203 OS
<< "ID " << Entry
.getNameID();
206 OS
<< "/language " << Entry
.getLanguage() << ", in " << File1
<< " and in "
212 static void printStringOrID(const WindowsResourceParser::StringOrID
&S
,
213 raw_string_ostream
&OS
, bool IsType
, bool IsID
) {
216 if (!convertUTF16LEToUTF8String(S
.String
, UTF8
))
217 UTF8
= "(failed conversion from UTF16)";
218 OS
<< '\"' << UTF8
<< '\"';
220 printResourceTypeName(S
.ID
, OS
);
227 static std::string
makeDuplicateResourceError(
228 const std::vector
<WindowsResourceParser::StringOrID
> &Context
,
229 StringRef File1
, StringRef File2
) {
231 raw_string_ostream
OS(Ret
);
233 OS
<< "duplicate resource:";
235 if (Context
.size() >= 1) {
237 printStringOrID(Context
[0], OS
, /* IsType */ true, /* IsID */ true);
240 if (Context
.size() >= 2) {
242 printStringOrID(Context
[1], OS
, /* IsType */ false, /* IsID */ true);
245 if (Context
.size() >= 3) {
247 printStringOrID(Context
[2], OS
, /* IsType */ false, /* IsID */ false);
249 OS
<< ", in " << File1
<< " and in " << File2
;
254 Error
WindowsResourceParser::parse(WindowsResource
*WR
,
255 std::vector
<std::string
> &Duplicates
) {
256 auto EntryOrErr
= WR
->getHeadEntry();
258 auto E
= EntryOrErr
.takeError();
259 if (E
.isA
<EmptyResError
>()) {
260 // Check if the .res file contains no entries. In this case we don't have
261 // to throw an error but can rather just return without parsing anything.
262 // This applies for files which have a valid PE header magic and the
263 // mandatory empty null resource entry. Files which do not fit this
264 // criteria would have already been filtered out by
265 // WindowsResource::createWindowsResource().
266 consumeError(std::move(E
));
267 return Error::success();
272 ResourceEntryRef Entry
= EntryOrErr
.get();
273 uint32_t Origin
= InputFilenames
.size();
274 InputFilenames
.push_back(WR
->getFileName());
279 bool IsNewNode
= Root
.addEntry(Entry
, Origin
, Data
, StringTable
, Node
);
281 Duplicates
.push_back(makeDuplicateResourceError(
282 Entry
, InputFilenames
[Node
->Origin
], WR
->getFileName()));
285 RETURN_IF_ERROR(Entry
.moveNext(End
));
288 return Error::success();
291 Error
WindowsResourceParser::parse(ResourceSectionRef
&RSR
, StringRef Filename
,
292 std::vector
<std::string
> &Duplicates
) {
293 UNWRAP_REF_OR_RETURN(BaseTable
, RSR
.getBaseTable());
294 uint32_t Origin
= InputFilenames
.size();
295 InputFilenames
.push_back(Filename
);
296 std::vector
<StringOrID
> Context
;
297 return addChildren(Root
, RSR
, BaseTable
, Origin
, Context
, Duplicates
);
300 void WindowsResourceParser::printTree(raw_ostream
&OS
) const {
301 ScopedPrinter
Writer(OS
);
302 Root
.print(Writer
, "Resource Tree");
305 bool WindowsResourceParser::TreeNode::addEntry(
306 const ResourceEntryRef
&Entry
, uint32_t Origin
,
307 std::vector
<std::vector
<uint8_t>> &Data
,
308 std::vector
<std::vector
<UTF16
>> &StringTable
, TreeNode
*&Result
) {
309 TreeNode
&TypeNode
= addTypeNode(Entry
, StringTable
);
310 TreeNode
&NameNode
= TypeNode
.addNameNode(Entry
, StringTable
);
311 return NameNode
.addLanguageNode(Entry
, Origin
, Data
, Result
);
314 Error
WindowsResourceParser::addChildren(TreeNode
&Node
,
315 ResourceSectionRef
&RSR
,
316 const coff_resource_dir_table
&Table
,
318 std::vector
<StringOrID
> &Context
,
319 std::vector
<std::string
> &Duplicates
) {
321 for (int i
= 0; i
< Table
.NumberOfNameEntries
+ Table
.NumberOfIDEntries
;
323 UNWRAP_REF_OR_RETURN(Entry
, RSR
.getTableEntry(Table
, i
));
326 if (Entry
.Offset
.isSubDir()) {
328 // Create a new subdirectory and recurse
329 if (i
< Table
.NumberOfNameEntries
) {
330 UNWRAP_OR_RETURN(NameString
, RSR
.getEntryNameString(Entry
));
331 Child
= &Node
.addNameChild(NameString
, StringTable
);
332 Context
.push_back(StringOrID(NameString
));
334 Child
= &Node
.addIDChild(Entry
.Identifier
.ID
);
335 Context
.push_back(StringOrID(Entry
.Identifier
.ID
));
338 UNWRAP_REF_OR_RETURN(NextTable
, RSR
.getEntrySubDir(Entry
));
340 addChildren(*Child
, RSR
, NextTable
, Origin
, Context
, Duplicates
);
347 // Data leaves are supposed to have a numeric ID as identifier (language).
348 if (Table
.NumberOfNameEntries
> 0)
349 return createStringError(object_error::parse_failed
,
350 "unexpected string key for data object");
352 // Try adding a data leaf
353 UNWRAP_REF_OR_RETURN(DataEntry
, RSR
.getEntryData(Entry
));
355 Context
.push_back(StringOrID(Entry
.Identifier
.ID
));
356 bool Added
= Node
.addDataChild(Entry
.Identifier
.ID
, Table
.MajorVersion
,
357 Table
.MinorVersion
, Table
.Characteristics
,
358 Origin
, Data
.size(), Child
);
360 UNWRAP_OR_RETURN(Contents
, RSR
.getContents(DataEntry
));
361 Data
.push_back(ArrayRef
<uint8_t>(
362 reinterpret_cast<const uint8_t *>(Contents
.data()),
365 Duplicates
.push_back(makeDuplicateResourceError(
366 Context
, InputFilenames
[Child
->Origin
], InputFilenames
.back()));
372 return Error::success();
375 WindowsResourceParser::TreeNode::TreeNode(uint32_t StringIndex
)
376 : StringIndex(StringIndex
) {}
378 WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion
,
379 uint16_t MinorVersion
,
380 uint32_t Characteristics
,
381 uint32_t Origin
, uint32_t DataIndex
)
382 : IsDataNode(true), DataIndex(DataIndex
), MajorVersion(MajorVersion
),
383 MinorVersion(MinorVersion
), Characteristics(Characteristics
),
386 std::unique_ptr
<WindowsResourceParser::TreeNode
>
387 WindowsResourceParser::TreeNode::createStringNode(uint32_t Index
) {
388 return std::unique_ptr
<TreeNode
>(new TreeNode(Index
));
391 std::unique_ptr
<WindowsResourceParser::TreeNode
>
392 WindowsResourceParser::TreeNode::createIDNode() {
393 return std::unique_ptr
<TreeNode
>(new TreeNode(0));
396 std::unique_ptr
<WindowsResourceParser::TreeNode
>
397 WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion
,
398 uint16_t MinorVersion
,
399 uint32_t Characteristics
,
401 uint32_t DataIndex
) {
402 return std::unique_ptr
<TreeNode
>(new TreeNode(
403 MajorVersion
, MinorVersion
, Characteristics
, Origin
, DataIndex
));
406 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addTypeNode(
407 const ResourceEntryRef
&Entry
,
408 std::vector
<std::vector
<UTF16
>> &StringTable
) {
409 if (Entry
.checkTypeString())
410 return addNameChild(Entry
.getTypeString(), StringTable
);
412 return addIDChild(Entry
.getTypeID());
415 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addNameNode(
416 const ResourceEntryRef
&Entry
,
417 std::vector
<std::vector
<UTF16
>> &StringTable
) {
418 if (Entry
.checkNameString())
419 return addNameChild(Entry
.getNameString(), StringTable
);
421 return addIDChild(Entry
.getNameID());
424 bool WindowsResourceParser::TreeNode::addLanguageNode(
425 const ResourceEntryRef
&Entry
, uint32_t Origin
,
426 std::vector
<std::vector
<uint8_t>> &Data
, TreeNode
*&Result
) {
427 bool Added
= addDataChild(Entry
.getLanguage(), Entry
.getMajorVersion(),
428 Entry
.getMinorVersion(), Entry
.getCharacteristics(),
429 Origin
, Data
.size(), Result
);
431 Data
.push_back(Entry
.getData());
435 bool WindowsResourceParser::TreeNode::addDataChild(
436 uint32_t ID
, uint16_t MajorVersion
, uint16_t MinorVersion
,
437 uint32_t Characteristics
, uint32_t Origin
, uint32_t DataIndex
,
439 auto NewChild
= createDataNode(MajorVersion
, MinorVersion
, Characteristics
,
441 auto ElementInserted
= IDChildren
.emplace(ID
, std::move(NewChild
));
442 Result
= ElementInserted
.first
->second
.get();
443 return ElementInserted
.second
;
446 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addIDChild(
448 auto Child
= IDChildren
.find(ID
);
449 if (Child
== IDChildren
.end()) {
450 auto NewChild
= createIDNode();
451 WindowsResourceParser::TreeNode
&Node
= *NewChild
;
452 IDChildren
.emplace(ID
, std::move(NewChild
));
455 return *(Child
->second
);
458 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addNameChild(
459 ArrayRef
<UTF16
> NameRef
, std::vector
<std::vector
<UTF16
>> &StringTable
) {
460 std::string NameString
;
461 convertUTF16LEToUTF8String(NameRef
, NameString
);
463 auto Child
= StringChildren
.find(NameString
);
464 if (Child
== StringChildren
.end()) {
465 auto NewChild
= createStringNode(StringTable
.size());
466 StringTable
.push_back(NameRef
);
467 WindowsResourceParser::TreeNode
&Node
= *NewChild
;
468 StringChildren
.emplace(NameString
, std::move(NewChild
));
471 return *(Child
->second
);
474 void WindowsResourceParser::TreeNode::print(ScopedPrinter
&Writer
,
475 StringRef Name
) const {
476 ListScope
NodeScope(Writer
, Name
);
477 for (auto const &Child
: StringChildren
) {
478 Child
.second
->print(Writer
, Child
.first
);
480 for (auto const &Child
: IDChildren
) {
481 Child
.second
->print(Writer
, to_string(Child
.first
));
485 // This function returns the size of the entire resource tree, including
486 // directory tables, directory entries, and data entries. It does not include
487 // the directory strings or the relocations of the .rsrc section.
488 uint32_t WindowsResourceParser::TreeNode::getTreeSize() const {
489 uint32_t Size
= (IDChildren
.size() + StringChildren
.size()) *
490 sizeof(coff_resource_dir_entry
);
492 // Reached a node pointing to a data entry.
494 Size
+= sizeof(coff_resource_data_entry
);
498 // If the node does not point to data, it must have a directory table pointing
500 Size
+= sizeof(coff_resource_dir_table
);
502 for (auto const &Child
: StringChildren
) {
503 Size
+= Child
.second
->getTreeSize();
505 for (auto const &Child
: IDChildren
) {
506 Size
+= Child
.second
->getTreeSize();
511 class WindowsResourceCOFFWriter
{
513 WindowsResourceCOFFWriter(COFF::MachineTypes MachineType
,
514 const WindowsResourceParser
&Parser
, Error
&E
);
515 std::unique_ptr
<MemoryBuffer
> write(uint32_t TimeDateStamp
);
518 void performFileLayout();
519 void performSectionOneLayout();
520 void performSectionTwoLayout();
521 void writeCOFFHeader(uint32_t TimeDateStamp
);
522 void writeFirstSectionHeader();
523 void writeSecondSectionHeader();
524 void writeFirstSection();
525 void writeSecondSection();
526 void writeSymbolTable();
527 void writeStringTable();
528 void writeDirectoryTree();
529 void writeDirectoryStringTable();
530 void writeFirstSectionRelocations();
531 std::unique_ptr
<WritableMemoryBuffer
> OutputBuffer
;
533 uint64_t CurrentOffset
= 0;
534 COFF::MachineTypes MachineType
;
535 const WindowsResourceParser::TreeNode
&Resources
;
536 const ArrayRef
<std::vector
<uint8_t>> Data
;
538 uint32_t SymbolTableOffset
;
539 uint32_t SectionOneSize
;
540 uint32_t SectionOneOffset
;
541 uint32_t SectionOneRelocations
;
542 uint32_t SectionTwoSize
;
543 uint32_t SectionTwoOffset
;
544 const ArrayRef
<std::vector
<UTF16
>> StringTable
;
545 std::vector
<uint32_t> StringTableOffsets
;
546 std::vector
<uint32_t> DataOffsets
;
547 std::vector
<uint32_t> RelocationAddresses
;
550 WindowsResourceCOFFWriter::WindowsResourceCOFFWriter(
551 COFF::MachineTypes MachineType
, const WindowsResourceParser
&Parser
,
553 : MachineType(MachineType
), Resources(Parser
.getTree()),
554 Data(Parser
.getData()), StringTable(Parser
.getStringTable()) {
557 OutputBuffer
= WritableMemoryBuffer::getNewMemBuffer(
558 FileSize
, "internal .obj file created from .res files");
561 void WindowsResourceCOFFWriter::performFileLayout() {
562 // Add size of COFF header.
563 FileSize
= COFF::Header16Size
;
565 // one .rsrc section header for directory tree, another for resource data.
566 FileSize
+= 2 * COFF::SectionSize
;
568 performSectionOneLayout();
569 performSectionTwoLayout();
571 // We have reached the address of the symbol table.
572 SymbolTableOffset
= FileSize
;
574 FileSize
+= COFF::Symbol16Size
; // size of the @feat.00 symbol.
575 FileSize
+= 4 * COFF::Symbol16Size
; // symbol + aux for each section.
576 FileSize
+= Data
.size() * COFF::Symbol16Size
; // 1 symbol per resource.
577 FileSize
+= 4; // four null bytes for the string table.
580 void WindowsResourceCOFFWriter::performSectionOneLayout() {
581 SectionOneOffset
= FileSize
;
583 SectionOneSize
= Resources
.getTreeSize();
584 uint32_t CurrentStringOffset
= SectionOneSize
;
585 uint32_t TotalStringTableSize
= 0;
586 for (auto const &String
: StringTable
) {
587 StringTableOffsets
.push_back(CurrentStringOffset
);
588 uint32_t StringSize
= String
.size() * sizeof(UTF16
) + sizeof(uint16_t);
589 CurrentStringOffset
+= StringSize
;
590 TotalStringTableSize
+= StringSize
;
592 SectionOneSize
+= alignTo(TotalStringTableSize
, sizeof(uint32_t));
594 // account for the relocations of section one.
595 SectionOneRelocations
= FileSize
+ SectionOneSize
;
596 FileSize
+= SectionOneSize
;
598 Data
.size() * COFF::RelocationSize
; // one relocation for each resource.
599 FileSize
= alignTo(FileSize
, SECTION_ALIGNMENT
);
602 void WindowsResourceCOFFWriter::performSectionTwoLayout() {
603 // add size of .rsrc$2 section, which contains all resource data on 8-byte
605 SectionTwoOffset
= FileSize
;
607 for (auto const &Entry
: Data
) {
608 DataOffsets
.push_back(SectionTwoSize
);
609 SectionTwoSize
+= alignTo(Entry
.size(), sizeof(uint64_t));
611 FileSize
+= SectionTwoSize
;
612 FileSize
= alignTo(FileSize
, SECTION_ALIGNMENT
);
615 std::unique_ptr
<MemoryBuffer
>
616 WindowsResourceCOFFWriter::write(uint32_t TimeDateStamp
) {
617 BufferStart
= OutputBuffer
->getBufferStart();
619 writeCOFFHeader(TimeDateStamp
);
620 writeFirstSectionHeader();
621 writeSecondSectionHeader();
623 writeSecondSection();
627 return std::move(OutputBuffer
);
630 // According to COFF specification, if the Src has a size equal to Dest,
631 // it's okay to *not* copy the trailing zero.
632 static void coffnamecpy(char (&Dest
)[COFF::NameSize
], StringRef Src
) {
633 assert(Src
.size() <= COFF::NameSize
&&
634 "Src is not larger than COFF::NameSize");
635 strncpy(Dest
, Src
.data(), (size_t)COFF::NameSize
);
638 void WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp
) {
639 // Write the COFF header.
640 auto *Header
= reinterpret_cast<coff_file_header
*>(BufferStart
);
641 Header
->Machine
= MachineType
;
642 Header
->NumberOfSections
= 2;
643 Header
->TimeDateStamp
= TimeDateStamp
;
644 Header
->PointerToSymbolTable
= SymbolTableOffset
;
645 // One symbol for every resource plus 2 for each section and 1 for @feat.00
646 Header
->NumberOfSymbols
= Data
.size() + 5;
647 Header
->SizeOfOptionalHeader
= 0;
648 // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it.
649 Header
->Characteristics
= COFF::IMAGE_FILE_32BIT_MACHINE
;
652 void WindowsResourceCOFFWriter::writeFirstSectionHeader() {
653 // Write the first section header.
654 CurrentOffset
+= sizeof(coff_file_header
);
655 auto *SectionOneHeader
=
656 reinterpret_cast<coff_section
*>(BufferStart
+ CurrentOffset
);
657 coffnamecpy(SectionOneHeader
->Name
, ".rsrc$01");
658 SectionOneHeader
->VirtualSize
= 0;
659 SectionOneHeader
->VirtualAddress
= 0;
660 SectionOneHeader
->SizeOfRawData
= SectionOneSize
;
661 SectionOneHeader
->PointerToRawData
= SectionOneOffset
;
662 SectionOneHeader
->PointerToRelocations
= SectionOneRelocations
;
663 SectionOneHeader
->PointerToLinenumbers
= 0;
664 SectionOneHeader
->NumberOfRelocations
= Data
.size();
665 SectionOneHeader
->NumberOfLinenumbers
= 0;
666 SectionOneHeader
->Characteristics
+= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
;
667 SectionOneHeader
->Characteristics
+= COFF::IMAGE_SCN_MEM_READ
;
670 void WindowsResourceCOFFWriter::writeSecondSectionHeader() {
671 // Write the second section header.
672 CurrentOffset
+= sizeof(coff_section
);
673 auto *SectionTwoHeader
=
674 reinterpret_cast<coff_section
*>(BufferStart
+ CurrentOffset
);
675 coffnamecpy(SectionTwoHeader
->Name
, ".rsrc$02");
676 SectionTwoHeader
->VirtualSize
= 0;
677 SectionTwoHeader
->VirtualAddress
= 0;
678 SectionTwoHeader
->SizeOfRawData
= SectionTwoSize
;
679 SectionTwoHeader
->PointerToRawData
= SectionTwoOffset
;
680 SectionTwoHeader
->PointerToRelocations
= 0;
681 SectionTwoHeader
->PointerToLinenumbers
= 0;
682 SectionTwoHeader
->NumberOfRelocations
= 0;
683 SectionTwoHeader
->NumberOfLinenumbers
= 0;
684 SectionTwoHeader
->Characteristics
= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
;
685 SectionTwoHeader
->Characteristics
+= COFF::IMAGE_SCN_MEM_READ
;
688 void WindowsResourceCOFFWriter::writeFirstSection() {
689 // Write section one.
690 CurrentOffset
+= sizeof(coff_section
);
692 writeDirectoryTree();
693 writeDirectoryStringTable();
694 writeFirstSectionRelocations();
696 CurrentOffset
= alignTo(CurrentOffset
, SECTION_ALIGNMENT
);
699 void WindowsResourceCOFFWriter::writeSecondSection() {
700 // Now write the .rsrc$02 section.
701 for (auto const &RawDataEntry
: Data
) {
702 llvm::copy(RawDataEntry
, BufferStart
+ CurrentOffset
);
703 CurrentOffset
+= alignTo(RawDataEntry
.size(), sizeof(uint64_t));
706 CurrentOffset
= alignTo(CurrentOffset
, SECTION_ALIGNMENT
);
709 void WindowsResourceCOFFWriter::writeSymbolTable() {
710 // Now write the symbol table.
711 // First, the feat symbol.
712 auto *Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
713 coffnamecpy(Symbol
->Name
.ShortName
, "@feat.00");
714 Symbol
->Value
= 0x11;
715 Symbol
->SectionNumber
= 0xffff;
716 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
717 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
718 Symbol
->NumberOfAuxSymbols
= 0;
719 CurrentOffset
+= sizeof(coff_symbol16
);
721 // Now write the .rsrc1 symbol + aux.
722 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
723 coffnamecpy(Symbol
->Name
.ShortName
, ".rsrc$01");
725 Symbol
->SectionNumber
= 1;
726 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
727 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
728 Symbol
->NumberOfAuxSymbols
= 1;
729 CurrentOffset
+= sizeof(coff_symbol16
);
730 auto *Aux
= reinterpret_cast<coff_aux_section_definition
*>(BufferStart
+
732 Aux
->Length
= SectionOneSize
;
733 Aux
->NumberOfRelocations
= Data
.size();
734 Aux
->NumberOfLinenumbers
= 0;
736 Aux
->NumberLowPart
= 0;
738 CurrentOffset
+= sizeof(coff_aux_section_definition
);
740 // Now write the .rsrc2 symbol + aux.
741 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
742 coffnamecpy(Symbol
->Name
.ShortName
, ".rsrc$02");
744 Symbol
->SectionNumber
= 2;
745 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
746 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
747 Symbol
->NumberOfAuxSymbols
= 1;
748 CurrentOffset
+= sizeof(coff_symbol16
);
749 Aux
= reinterpret_cast<coff_aux_section_definition
*>(BufferStart
+
751 Aux
->Length
= SectionTwoSize
;
752 Aux
->NumberOfRelocations
= 0;
753 Aux
->NumberOfLinenumbers
= 0;
755 Aux
->NumberLowPart
= 0;
757 CurrentOffset
+= sizeof(coff_aux_section_definition
);
759 // Now write a symbol for each relocation.
760 for (unsigned i
= 0; i
< Data
.size(); i
++) {
761 auto RelocationName
= formatv("$R{0:X-6}", i
& 0xffffff).sstr
<COFF::NameSize
>();
762 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
763 coffnamecpy(Symbol
->Name
.ShortName
, RelocationName
);
764 Symbol
->Value
= DataOffsets
[i
];
765 Symbol
->SectionNumber
= 2;
766 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
767 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
768 Symbol
->NumberOfAuxSymbols
= 0;
769 CurrentOffset
+= sizeof(coff_symbol16
);
773 void WindowsResourceCOFFWriter::writeStringTable() {
774 // Just 4 null bytes for the string table.
775 auto COFFStringTable
= reinterpret_cast<void *>(BufferStart
+ CurrentOffset
);
776 memset(COFFStringTable
, 0, 4);
779 void WindowsResourceCOFFWriter::writeDirectoryTree() {
780 // Traverse parsed resource tree breadth-first and write the corresponding
782 std::queue
<const WindowsResourceParser::TreeNode
*> Queue
;
783 Queue
.push(&Resources
);
784 uint32_t NextLevelOffset
=
785 sizeof(coff_resource_dir_table
) + (Resources
.getStringChildren().size() +
786 Resources
.getIDChildren().size()) *
787 sizeof(coff_resource_dir_entry
);
788 std::vector
<const WindowsResourceParser::TreeNode
*> DataEntriesTreeOrder
;
789 uint32_t CurrentRelativeOffset
= 0;
791 while (!Queue
.empty()) {
792 auto CurrentNode
= Queue
.front();
794 auto *Table
= reinterpret_cast<coff_resource_dir_table
*>(BufferStart
+
796 Table
->Characteristics
= CurrentNode
->getCharacteristics();
797 Table
->TimeDateStamp
= 0;
798 Table
->MajorVersion
= CurrentNode
->getMajorVersion();
799 Table
->MinorVersion
= CurrentNode
->getMinorVersion();
800 auto &IDChildren
= CurrentNode
->getIDChildren();
801 auto &StringChildren
= CurrentNode
->getStringChildren();
802 Table
->NumberOfNameEntries
= StringChildren
.size();
803 Table
->NumberOfIDEntries
= IDChildren
.size();
804 CurrentOffset
+= sizeof(coff_resource_dir_table
);
805 CurrentRelativeOffset
+= sizeof(coff_resource_dir_table
);
807 // Write the directory entries immediately following each directory table.
808 for (auto const &Child
: StringChildren
) {
809 auto *Entry
= reinterpret_cast<coff_resource_dir_entry
*>(BufferStart
+
811 Entry
->Identifier
.setNameOffset(
812 StringTableOffsets
[Child
.second
->getStringIndex()]);
813 if (Child
.second
->checkIsDataNode()) {
814 Entry
->Offset
.DataEntryOffset
= NextLevelOffset
;
815 NextLevelOffset
+= sizeof(coff_resource_data_entry
);
816 DataEntriesTreeOrder
.push_back(Child
.second
.get());
818 Entry
->Offset
.SubdirOffset
= NextLevelOffset
+ (1 << 31);
819 NextLevelOffset
+= sizeof(coff_resource_dir_table
) +
820 (Child
.second
->getStringChildren().size() +
821 Child
.second
->getIDChildren().size()) *
822 sizeof(coff_resource_dir_entry
);
823 Queue
.push(Child
.second
.get());
825 CurrentOffset
+= sizeof(coff_resource_dir_entry
);
826 CurrentRelativeOffset
+= sizeof(coff_resource_dir_entry
);
828 for (auto const &Child
: IDChildren
) {
829 auto *Entry
= reinterpret_cast<coff_resource_dir_entry
*>(BufferStart
+
831 Entry
->Identifier
.ID
= Child
.first
;
832 if (Child
.second
->checkIsDataNode()) {
833 Entry
->Offset
.DataEntryOffset
= NextLevelOffset
;
834 NextLevelOffset
+= sizeof(coff_resource_data_entry
);
835 DataEntriesTreeOrder
.push_back(Child
.second
.get());
837 Entry
->Offset
.SubdirOffset
= NextLevelOffset
+ (1 << 31);
838 NextLevelOffset
+= sizeof(coff_resource_dir_table
) +
839 (Child
.second
->getStringChildren().size() +
840 Child
.second
->getIDChildren().size()) *
841 sizeof(coff_resource_dir_entry
);
842 Queue
.push(Child
.second
.get());
844 CurrentOffset
+= sizeof(coff_resource_dir_entry
);
845 CurrentRelativeOffset
+= sizeof(coff_resource_dir_entry
);
849 RelocationAddresses
.resize(Data
.size());
850 // Now write all the resource data entries.
851 for (auto DataNodes
: DataEntriesTreeOrder
) {
852 auto *Entry
= reinterpret_cast<coff_resource_data_entry
*>(BufferStart
+
854 RelocationAddresses
[DataNodes
->getDataIndex()] = CurrentRelativeOffset
;
855 Entry
->DataRVA
= 0; // Set to zero because it is a relocation.
856 Entry
->DataSize
= Data
[DataNodes
->getDataIndex()].size();
859 CurrentOffset
+= sizeof(coff_resource_data_entry
);
860 CurrentRelativeOffset
+= sizeof(coff_resource_data_entry
);
864 void WindowsResourceCOFFWriter::writeDirectoryStringTable() {
865 // Now write the directory string table for .rsrc$01
866 uint32_t TotalStringTableSize
= 0;
867 for (auto &String
: StringTable
) {
868 uint16_t Length
= String
.size();
869 support::endian::write16le(BufferStart
+ CurrentOffset
, Length
);
870 CurrentOffset
+= sizeof(uint16_t);
871 auto *Start
= reinterpret_cast<UTF16
*>(BufferStart
+ CurrentOffset
);
872 llvm::copy(String
, Start
);
873 CurrentOffset
+= Length
* sizeof(UTF16
);
874 TotalStringTableSize
+= Length
* sizeof(UTF16
) + sizeof(uint16_t);
877 alignTo(TotalStringTableSize
, sizeof(uint32_t)) - TotalStringTableSize
;
880 void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {
882 // Now write the relocations for .rsrc$01
883 // Five symbols already in table before we start, @feat.00 and 2 for each
885 uint32_t NextSymbolIndex
= 5;
886 for (unsigned i
= 0; i
< Data
.size(); i
++) {
888 reinterpret_cast<coff_relocation
*>(BufferStart
+ CurrentOffset
);
889 Reloc
->VirtualAddress
= RelocationAddresses
[i
];
890 Reloc
->SymbolTableIndex
= NextSymbolIndex
++;
891 switch (MachineType
) {
892 case COFF::IMAGE_FILE_MACHINE_ARMNT
:
893 Reloc
->Type
= COFF::IMAGE_REL_ARM_ADDR32NB
;
895 case COFF::IMAGE_FILE_MACHINE_AMD64
:
896 Reloc
->Type
= COFF::IMAGE_REL_AMD64_ADDR32NB
;
898 case COFF::IMAGE_FILE_MACHINE_I386
:
899 Reloc
->Type
= COFF::IMAGE_REL_I386_DIR32NB
;
901 case COFF::IMAGE_FILE_MACHINE_ARM64
:
902 Reloc
->Type
= COFF::IMAGE_REL_ARM64_ADDR32NB
;
905 llvm_unreachable("unknown machine type");
907 CurrentOffset
+= sizeof(coff_relocation
);
911 Expected
<std::unique_ptr
<MemoryBuffer
>>
912 writeWindowsResourceCOFF(COFF::MachineTypes MachineType
,
913 const WindowsResourceParser
&Parser
,
914 uint32_t TimeDateStamp
) {
915 Error E
= Error::success();
916 WindowsResourceCOFFWriter
Writer(MachineType
, Parser
, E
);
919 return Writer
.write(TimeDateStamp
);
922 } // namespace object