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/Object/WindowsMachineFlag.h"
16 #include "llvm/Support/FormatVariadic.h"
17 #include "llvm/Support/MathExtras.h"
18 #include "llvm/Support/ScopedPrinter.h"
23 using namespace object
;
28 #define RETURN_IF_ERROR(X) \
32 #define UNWRAP_REF_OR_RETURN(Name, Expr) \
33 auto Name##OrErr = Expr; \
35 return Name##OrErr.takeError(); \
36 const auto &Name = *Name##OrErr;
38 #define UNWRAP_OR_RETURN(Name, Expr) \
39 auto Name##OrErr = Expr; \
41 return Name##OrErr.takeError(); \
42 auto Name = *Name##OrErr;
44 const uint32_t MIN_HEADER_SIZE
= 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t);
46 // COFF files seem to be inconsistent with alignment between sections, just use
47 // 8-byte because it makes everyone happy.
48 const uint32_t SECTION_ALIGNMENT
= sizeof(uint64_t);
50 WindowsResource::WindowsResource(MemoryBufferRef Source
)
51 : Binary(Binary::ID_WinRes
, Source
) {
52 size_t LeadingSize
= WIN_RES_MAGIC_SIZE
+ WIN_RES_NULL_ENTRY_SIZE
;
53 BBS
= BinaryByteStream(Data
.getBuffer().drop_front(LeadingSize
),
54 llvm::endianness::little
);
58 Expected
<std::unique_ptr
<WindowsResource
>>
59 WindowsResource::createWindowsResource(MemoryBufferRef Source
) {
60 if (Source
.getBufferSize() < WIN_RES_MAGIC_SIZE
+ WIN_RES_NULL_ENTRY_SIZE
)
61 return make_error
<GenericBinaryError
>(
62 Source
.getBufferIdentifier() + ": too small to be a resource file",
63 object_error::invalid_file_type
);
64 std::unique_ptr
<WindowsResource
> Ret(new WindowsResource(Source
));
65 return std::move(Ret
);
68 Expected
<ResourceEntryRef
> WindowsResource::getHeadEntry() {
69 if (BBS
.getLength() < sizeof(WinResHeaderPrefix
) + sizeof(WinResHeaderSuffix
))
70 return make_error
<EmptyResError
>(getFileName() + " contains no entries",
71 object_error::unexpected_eof
);
72 return ResourceEntryRef::create(BinaryStreamRef(BBS
), this);
75 ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref
,
76 const WindowsResource
*Owner
)
77 : Reader(Ref
), Owner(Owner
) {}
79 Expected
<ResourceEntryRef
>
80 ResourceEntryRef::create(BinaryStreamRef BSR
, const WindowsResource
*Owner
) {
81 auto Ref
= ResourceEntryRef(BSR
, Owner
);
82 if (auto E
= Ref
.loadNext())
87 Error
ResourceEntryRef::moveNext(bool &End
) {
88 // Reached end of all the entries.
89 if (Reader
.bytesRemaining() == 0) {
91 return Error::success();
93 RETURN_IF_ERROR(loadNext());
95 return Error::success();
98 static Error
readStringOrId(BinaryStreamReader
&Reader
, uint16_t &ID
,
99 ArrayRef
<UTF16
> &Str
, bool &IsString
) {
101 RETURN_IF_ERROR(Reader
.readInteger(IDFlag
));
102 IsString
= IDFlag
!= 0xffff;
107 sizeof(uint16_t)); // Re-read the bytes which we used to check the flag.
108 RETURN_IF_ERROR(Reader
.readWideString(Str
));
110 RETURN_IF_ERROR(Reader
.readInteger(ID
));
112 return Error::success();
115 Error
ResourceEntryRef::loadNext() {
116 const WinResHeaderPrefix
*Prefix
;
117 RETURN_IF_ERROR(Reader
.readObject(Prefix
));
119 if (Prefix
->HeaderSize
< MIN_HEADER_SIZE
)
120 return make_error
<GenericBinaryError
>(Owner
->getFileName() +
121 ": header size too small",
122 object_error::parse_failed
);
124 RETURN_IF_ERROR(readStringOrId(Reader
, TypeID
, Type
, IsStringType
));
126 RETURN_IF_ERROR(readStringOrId(Reader
, NameID
, Name
, IsStringName
));
128 RETURN_IF_ERROR(Reader
.padToAlignment(WIN_RES_HEADER_ALIGNMENT
));
130 RETURN_IF_ERROR(Reader
.readObject(Suffix
));
132 RETURN_IF_ERROR(Reader
.readArray(Data
, Prefix
->DataSize
));
134 RETURN_IF_ERROR(Reader
.padToAlignment(WIN_RES_DATA_ALIGNMENT
));
136 return Error::success();
139 WindowsResourceParser::WindowsResourceParser(bool MinGW
)
140 : Root(false), MinGW(MinGW
) {}
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(ArrayRef(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 // MinGW specific. Remove default manifests (with language zero) if there are
255 // other manifests present, and report an error if there are more than one
256 // manifest with a non-zero language code.
257 // GCC has the concept of a default manifest resource object, which gets
258 // linked in implicitly if present. This default manifest has got language
259 // id zero, and should be dropped silently if there's another manifest present.
260 // If the user resources surprisignly had a manifest with language id zero,
261 // we should also ignore the duplicate default manifest.
262 void WindowsResourceParser::cleanUpManifests(
263 std::vector
<std::string
> &Duplicates
) {
264 auto TypeIt
= Root
.IDChildren
.find(/* RT_MANIFEST */ 24);
265 if (TypeIt
== Root
.IDChildren
.end())
268 TreeNode
*TypeNode
= TypeIt
->second
.get();
270 TypeNode
->IDChildren
.find(/* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1);
271 if (NameIt
== TypeNode
->IDChildren
.end())
274 TreeNode
*NameNode
= NameIt
->second
.get();
275 if (NameNode
->IDChildren
.size() <= 1)
276 return; // None or one manifest present, all good.
278 // If we have more than one manifest, drop the language zero one if present,
280 auto LangZeroIt
= NameNode
->IDChildren
.find(0);
281 if (LangZeroIt
!= NameNode
->IDChildren
.end() &&
282 LangZeroIt
->second
->IsDataNode
) {
283 uint32_t RemovedIndex
= LangZeroIt
->second
->DataIndex
;
284 NameNode
->IDChildren
.erase(LangZeroIt
);
285 Data
.erase(Data
.begin() + RemovedIndex
);
286 Root
.shiftDataIndexDown(RemovedIndex
);
288 // If we're now down to one manifest, all is good.
289 if (NameNode
->IDChildren
.size() <= 1)
293 // More than one non-language-zero manifest
294 auto FirstIt
= NameNode
->IDChildren
.begin();
295 uint32_t FirstLang
= FirstIt
->first
;
296 TreeNode
*FirstNode
= FirstIt
->second
.get();
297 auto LastIt
= NameNode
->IDChildren
.rbegin();
298 uint32_t LastLang
= LastIt
->first
;
299 TreeNode
*LastNode
= LastIt
->second
.get();
300 Duplicates
.push_back(
301 ("duplicate non-default manifests with languages " + Twine(FirstLang
) +
302 " in " + InputFilenames
[FirstNode
->Origin
] + " and " + Twine(LastLang
) +
303 " in " + InputFilenames
[LastNode
->Origin
])
307 // Ignore duplicates of manifests with language zero (the default manifest),
308 // in case the user has provided a manifest with that language id. See
309 // the function comment above for context. Only returns true if MinGW is set
311 bool WindowsResourceParser::shouldIgnoreDuplicate(
312 const ResourceEntryRef
&Entry
) const {
313 return MinGW
&& !Entry
.checkTypeString() &&
314 Entry
.getTypeID() == /* RT_MANIFEST */ 24 &&
315 !Entry
.checkNameString() &&
316 Entry
.getNameID() == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&
317 Entry
.getLanguage() == 0;
320 bool WindowsResourceParser::shouldIgnoreDuplicate(
321 const std::vector
<StringOrID
> &Context
) const {
322 return MinGW
&& Context
.size() == 3 && !Context
[0].IsString
&&
323 Context
[0].ID
== /* RT_MANIFEST */ 24 && !Context
[1].IsString
&&
324 Context
[1].ID
== /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&
325 !Context
[2].IsString
&& Context
[2].ID
== 0;
328 Error
WindowsResourceParser::parse(WindowsResource
*WR
,
329 std::vector
<std::string
> &Duplicates
) {
330 auto EntryOrErr
= WR
->getHeadEntry();
332 auto E
= EntryOrErr
.takeError();
333 if (E
.isA
<EmptyResError
>()) {
334 // Check if the .res file contains no entries. In this case we don't have
335 // to throw an error but can rather just return without parsing anything.
336 // This applies for files which have a valid PE header magic and the
337 // mandatory empty null resource entry. Files which do not fit this
338 // criteria would have already been filtered out by
339 // WindowsResource::createWindowsResource().
340 consumeError(std::move(E
));
341 return Error::success();
346 ResourceEntryRef Entry
= EntryOrErr
.get();
347 uint32_t Origin
= InputFilenames
.size();
348 InputFilenames
.push_back(std::string(WR
->getFileName()));
353 bool IsNewNode
= Root
.addEntry(Entry
, Origin
, Data
, StringTable
, Node
);
355 if (!shouldIgnoreDuplicate(Entry
))
356 Duplicates
.push_back(makeDuplicateResourceError(
357 Entry
, InputFilenames
[Node
->Origin
], WR
->getFileName()));
360 RETURN_IF_ERROR(Entry
.moveNext(End
));
363 return Error::success();
366 Error
WindowsResourceParser::parse(ResourceSectionRef
&RSR
, StringRef Filename
,
367 std::vector
<std::string
> &Duplicates
) {
368 UNWRAP_REF_OR_RETURN(BaseTable
, RSR
.getBaseTable());
369 uint32_t Origin
= InputFilenames
.size();
370 InputFilenames
.push_back(std::string(Filename
));
371 std::vector
<StringOrID
> Context
;
372 return addChildren(Root
, RSR
, BaseTable
, Origin
, Context
, Duplicates
);
375 void WindowsResourceParser::printTree(raw_ostream
&OS
) const {
376 ScopedPrinter
Writer(OS
);
377 Root
.print(Writer
, "Resource Tree");
380 bool WindowsResourceParser::TreeNode::addEntry(
381 const ResourceEntryRef
&Entry
, uint32_t Origin
,
382 std::vector
<std::vector
<uint8_t>> &Data
,
383 std::vector
<std::vector
<UTF16
>> &StringTable
, TreeNode
*&Result
) {
384 TreeNode
&TypeNode
= addTypeNode(Entry
, StringTable
);
385 TreeNode
&NameNode
= TypeNode
.addNameNode(Entry
, StringTable
);
386 return NameNode
.addLanguageNode(Entry
, Origin
, Data
, Result
);
389 Error
WindowsResourceParser::addChildren(TreeNode
&Node
,
390 ResourceSectionRef
&RSR
,
391 const coff_resource_dir_table
&Table
,
393 std::vector
<StringOrID
> &Context
,
394 std::vector
<std::string
> &Duplicates
) {
396 for (int i
= 0; i
< Table
.NumberOfNameEntries
+ Table
.NumberOfIDEntries
;
398 UNWRAP_REF_OR_RETURN(Entry
, RSR
.getTableEntry(Table
, i
));
401 if (Entry
.Offset
.isSubDir()) {
403 // Create a new subdirectory and recurse
404 if (i
< Table
.NumberOfNameEntries
) {
405 UNWRAP_OR_RETURN(NameString
, RSR
.getEntryNameString(Entry
));
406 Child
= &Node
.addNameChild(NameString
, StringTable
);
407 Context
.push_back(StringOrID(NameString
));
409 Child
= &Node
.addIDChild(Entry
.Identifier
.ID
);
410 Context
.push_back(StringOrID(Entry
.Identifier
.ID
));
413 UNWRAP_REF_OR_RETURN(NextTable
, RSR
.getEntrySubDir(Entry
));
415 addChildren(*Child
, RSR
, NextTable
, Origin
, Context
, Duplicates
);
422 // Data leaves are supposed to have a numeric ID as identifier (language).
423 if (Table
.NumberOfNameEntries
> 0)
424 return createStringError(object_error::parse_failed
,
425 "unexpected string key for data object");
427 // Try adding a data leaf
428 UNWRAP_REF_OR_RETURN(DataEntry
, RSR
.getEntryData(Entry
));
430 Context
.push_back(StringOrID(Entry
.Identifier
.ID
));
431 bool Added
= Node
.addDataChild(Entry
.Identifier
.ID
, Table
.MajorVersion
,
432 Table
.MinorVersion
, Table
.Characteristics
,
433 Origin
, Data
.size(), Child
);
435 UNWRAP_OR_RETURN(Contents
, RSR
.getContents(DataEntry
));
436 Data
.push_back(ArrayRef
<uint8_t>(
437 reinterpret_cast<const uint8_t *>(Contents
.data()),
440 if (!shouldIgnoreDuplicate(Context
))
441 Duplicates
.push_back(makeDuplicateResourceError(
442 Context
, InputFilenames
[Child
->Origin
], InputFilenames
.back()));
448 return Error::success();
451 WindowsResourceParser::TreeNode::TreeNode(uint32_t StringIndex
)
452 : StringIndex(StringIndex
) {}
454 WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion
,
455 uint16_t MinorVersion
,
456 uint32_t Characteristics
,
457 uint32_t Origin
, uint32_t DataIndex
)
458 : IsDataNode(true), DataIndex(DataIndex
), MajorVersion(MajorVersion
),
459 MinorVersion(MinorVersion
), Characteristics(Characteristics
),
462 std::unique_ptr
<WindowsResourceParser::TreeNode
>
463 WindowsResourceParser::TreeNode::createStringNode(uint32_t Index
) {
464 return std::unique_ptr
<TreeNode
>(new TreeNode(Index
));
467 std::unique_ptr
<WindowsResourceParser::TreeNode
>
468 WindowsResourceParser::TreeNode::createIDNode() {
469 return std::unique_ptr
<TreeNode
>(new TreeNode(0));
472 std::unique_ptr
<WindowsResourceParser::TreeNode
>
473 WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion
,
474 uint16_t MinorVersion
,
475 uint32_t Characteristics
,
477 uint32_t DataIndex
) {
478 return std::unique_ptr
<TreeNode
>(new TreeNode(
479 MajorVersion
, MinorVersion
, Characteristics
, Origin
, DataIndex
));
482 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addTypeNode(
483 const ResourceEntryRef
&Entry
,
484 std::vector
<std::vector
<UTF16
>> &StringTable
) {
485 if (Entry
.checkTypeString())
486 return addNameChild(Entry
.getTypeString(), StringTable
);
488 return addIDChild(Entry
.getTypeID());
491 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addNameNode(
492 const ResourceEntryRef
&Entry
,
493 std::vector
<std::vector
<UTF16
>> &StringTable
) {
494 if (Entry
.checkNameString())
495 return addNameChild(Entry
.getNameString(), StringTable
);
497 return addIDChild(Entry
.getNameID());
500 bool WindowsResourceParser::TreeNode::addLanguageNode(
501 const ResourceEntryRef
&Entry
, uint32_t Origin
,
502 std::vector
<std::vector
<uint8_t>> &Data
, TreeNode
*&Result
) {
503 bool Added
= addDataChild(Entry
.getLanguage(), Entry
.getMajorVersion(),
504 Entry
.getMinorVersion(), Entry
.getCharacteristics(),
505 Origin
, Data
.size(), Result
);
507 Data
.push_back(Entry
.getData());
511 bool WindowsResourceParser::TreeNode::addDataChild(
512 uint32_t ID
, uint16_t MajorVersion
, uint16_t MinorVersion
,
513 uint32_t Characteristics
, uint32_t Origin
, uint32_t DataIndex
,
515 auto NewChild
= createDataNode(MajorVersion
, MinorVersion
, Characteristics
,
517 auto ElementInserted
= IDChildren
.emplace(ID
, std::move(NewChild
));
518 Result
= ElementInserted
.first
->second
.get();
519 return ElementInserted
.second
;
522 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addIDChild(
524 auto Child
= IDChildren
.find(ID
);
525 if (Child
== IDChildren
.end()) {
526 auto NewChild
= createIDNode();
527 WindowsResourceParser::TreeNode
&Node
= *NewChild
;
528 IDChildren
.emplace(ID
, std::move(NewChild
));
531 return *(Child
->second
);
534 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addNameChild(
535 ArrayRef
<UTF16
> NameRef
, std::vector
<std::vector
<UTF16
>> &StringTable
) {
536 std::string NameString
;
537 convertUTF16LEToUTF8String(NameRef
, NameString
);
539 auto Child
= StringChildren
.find(NameString
);
540 if (Child
== StringChildren
.end()) {
541 auto NewChild
= createStringNode(StringTable
.size());
542 StringTable
.push_back(NameRef
);
543 WindowsResourceParser::TreeNode
&Node
= *NewChild
;
544 StringChildren
.emplace(NameString
, std::move(NewChild
));
547 return *(Child
->second
);
550 void WindowsResourceParser::TreeNode::print(ScopedPrinter
&Writer
,
551 StringRef Name
) const {
552 ListScope
NodeScope(Writer
, Name
);
553 for (auto const &Child
: StringChildren
) {
554 Child
.second
->print(Writer
, Child
.first
);
556 for (auto const &Child
: IDChildren
) {
557 Child
.second
->print(Writer
, to_string(Child
.first
));
561 // This function returns the size of the entire resource tree, including
562 // directory tables, directory entries, and data entries. It does not include
563 // the directory strings or the relocations of the .rsrc section.
564 uint32_t WindowsResourceParser::TreeNode::getTreeSize() const {
565 uint32_t Size
= (IDChildren
.size() + StringChildren
.size()) *
566 sizeof(coff_resource_dir_entry
);
568 // Reached a node pointing to a data entry.
570 Size
+= sizeof(coff_resource_data_entry
);
574 // If the node does not point to data, it must have a directory table pointing
576 Size
+= sizeof(coff_resource_dir_table
);
578 for (auto const &Child
: StringChildren
) {
579 Size
+= Child
.second
->getTreeSize();
581 for (auto const &Child
: IDChildren
) {
582 Size
+= Child
.second
->getTreeSize();
587 // Shift DataIndex of all data children with an Index greater or equal to the
588 // given one, to fill a gap from removing an entry from the Data vector.
589 void WindowsResourceParser::TreeNode::shiftDataIndexDown(uint32_t Index
) {
590 if (IsDataNode
&& DataIndex
>= Index
) {
593 for (auto &Child
: IDChildren
)
594 Child
.second
->shiftDataIndexDown(Index
);
595 for (auto &Child
: StringChildren
)
596 Child
.second
->shiftDataIndexDown(Index
);
600 class WindowsResourceCOFFWriter
{
602 WindowsResourceCOFFWriter(COFF::MachineTypes MachineType
,
603 const WindowsResourceParser
&Parser
, Error
&E
);
604 std::unique_ptr
<MemoryBuffer
> write(uint32_t TimeDateStamp
);
607 void performFileLayout();
608 void performSectionOneLayout();
609 void performSectionTwoLayout();
610 void writeCOFFHeader(uint32_t TimeDateStamp
);
611 void writeFirstSectionHeader();
612 void writeSecondSectionHeader();
613 void writeFirstSection();
614 void writeSecondSection();
615 void writeSymbolTable();
616 void writeStringTable();
617 void writeDirectoryTree();
618 void writeDirectoryStringTable();
619 void writeFirstSectionRelocations();
620 std::unique_ptr
<WritableMemoryBuffer
> OutputBuffer
;
622 uint64_t CurrentOffset
= 0;
623 COFF::MachineTypes MachineType
;
624 const WindowsResourceParser::TreeNode
&Resources
;
625 const ArrayRef
<std::vector
<uint8_t>> Data
;
627 uint32_t SymbolTableOffset
;
628 uint32_t SectionOneSize
;
629 uint32_t SectionOneOffset
;
630 uint32_t SectionOneRelocations
;
631 uint32_t SectionTwoSize
;
632 uint32_t SectionTwoOffset
;
633 const ArrayRef
<std::vector
<UTF16
>> StringTable
;
634 std::vector
<uint32_t> StringTableOffsets
;
635 std::vector
<uint32_t> DataOffsets
;
636 std::vector
<uint32_t> RelocationAddresses
;
639 WindowsResourceCOFFWriter::WindowsResourceCOFFWriter(
640 COFF::MachineTypes MachineType
, const WindowsResourceParser
&Parser
,
642 : MachineType(MachineType
), Resources(Parser
.getTree()),
643 Data(Parser
.getData()), StringTable(Parser
.getStringTable()) {
646 OutputBuffer
= WritableMemoryBuffer::getNewMemBuffer(
647 FileSize
, "internal .obj file created from .res files");
650 void WindowsResourceCOFFWriter::performFileLayout() {
651 // Add size of COFF header.
652 FileSize
= COFF::Header16Size
;
654 // one .rsrc section header for directory tree, another for resource data.
655 FileSize
+= 2 * COFF::SectionSize
;
657 performSectionOneLayout();
658 performSectionTwoLayout();
660 // We have reached the address of the symbol table.
661 SymbolTableOffset
= FileSize
;
663 FileSize
+= COFF::Symbol16Size
; // size of the @feat.00 symbol.
664 FileSize
+= 4 * COFF::Symbol16Size
; // symbol + aux for each section.
665 FileSize
+= Data
.size() * COFF::Symbol16Size
; // 1 symbol per resource.
666 FileSize
+= 4; // four null bytes for the string table.
669 void WindowsResourceCOFFWriter::performSectionOneLayout() {
670 SectionOneOffset
= FileSize
;
672 SectionOneSize
= Resources
.getTreeSize();
673 uint32_t CurrentStringOffset
= SectionOneSize
;
674 uint32_t TotalStringTableSize
= 0;
675 for (auto const &String
: StringTable
) {
676 StringTableOffsets
.push_back(CurrentStringOffset
);
677 uint32_t StringSize
= String
.size() * sizeof(UTF16
) + sizeof(uint16_t);
678 CurrentStringOffset
+= StringSize
;
679 TotalStringTableSize
+= StringSize
;
681 SectionOneSize
+= alignTo(TotalStringTableSize
, sizeof(uint32_t));
683 // account for the relocations of section one.
684 SectionOneRelocations
= FileSize
+ SectionOneSize
;
685 FileSize
+= SectionOneSize
;
687 Data
.size() * COFF::RelocationSize
; // one relocation for each resource.
688 FileSize
= alignTo(FileSize
, SECTION_ALIGNMENT
);
691 void WindowsResourceCOFFWriter::performSectionTwoLayout() {
692 // add size of .rsrc$2 section, which contains all resource data on 8-byte
694 SectionTwoOffset
= FileSize
;
696 for (auto const &Entry
: Data
) {
697 DataOffsets
.push_back(SectionTwoSize
);
698 SectionTwoSize
+= alignTo(Entry
.size(), sizeof(uint64_t));
700 FileSize
+= SectionTwoSize
;
701 FileSize
= alignTo(FileSize
, SECTION_ALIGNMENT
);
704 std::unique_ptr
<MemoryBuffer
>
705 WindowsResourceCOFFWriter::write(uint32_t TimeDateStamp
) {
706 BufferStart
= OutputBuffer
->getBufferStart();
708 writeCOFFHeader(TimeDateStamp
);
709 writeFirstSectionHeader();
710 writeSecondSectionHeader();
712 writeSecondSection();
716 return std::move(OutputBuffer
);
719 // According to COFF specification, if the Src has a size equal to Dest,
720 // it's okay to *not* copy the trailing zero.
721 static void coffnamecpy(char (&Dest
)[COFF::NameSize
], StringRef Src
) {
722 assert(Src
.size() <= COFF::NameSize
&&
723 "Src is larger than COFF::NameSize");
724 assert((Src
.size() == COFF::NameSize
|| Dest
[Src
.size()] == '\0') &&
725 "Dest not zeroed upon initialization");
726 memcpy(Dest
, Src
.data(), Src
.size());
729 void WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp
) {
730 // Write the COFF header.
731 auto *Header
= reinterpret_cast<coff_file_header
*>(BufferStart
);
732 Header
->Machine
= MachineType
;
733 Header
->NumberOfSections
= 2;
734 Header
->TimeDateStamp
= TimeDateStamp
;
735 Header
->PointerToSymbolTable
= SymbolTableOffset
;
736 // One symbol for every resource plus 2 for each section and 1 for @feat.00
737 Header
->NumberOfSymbols
= Data
.size() + 5;
738 Header
->SizeOfOptionalHeader
= 0;
739 // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it.
740 Header
->Characteristics
= COFF::IMAGE_FILE_32BIT_MACHINE
;
743 void WindowsResourceCOFFWriter::writeFirstSectionHeader() {
744 // Write the first section header.
745 CurrentOffset
+= sizeof(coff_file_header
);
746 auto *SectionOneHeader
=
747 reinterpret_cast<coff_section
*>(BufferStart
+ CurrentOffset
);
748 coffnamecpy(SectionOneHeader
->Name
, ".rsrc$01");
749 SectionOneHeader
->VirtualSize
= 0;
750 SectionOneHeader
->VirtualAddress
= 0;
751 SectionOneHeader
->SizeOfRawData
= SectionOneSize
;
752 SectionOneHeader
->PointerToRawData
= SectionOneOffset
;
753 SectionOneHeader
->PointerToRelocations
= SectionOneRelocations
;
754 SectionOneHeader
->PointerToLinenumbers
= 0;
755 SectionOneHeader
->NumberOfRelocations
= Data
.size();
756 SectionOneHeader
->NumberOfLinenumbers
= 0;
757 SectionOneHeader
->Characteristics
+= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
;
758 SectionOneHeader
->Characteristics
+= COFF::IMAGE_SCN_MEM_READ
;
761 void WindowsResourceCOFFWriter::writeSecondSectionHeader() {
762 // Write the second section header.
763 CurrentOffset
+= sizeof(coff_section
);
764 auto *SectionTwoHeader
=
765 reinterpret_cast<coff_section
*>(BufferStart
+ CurrentOffset
);
766 coffnamecpy(SectionTwoHeader
->Name
, ".rsrc$02");
767 SectionTwoHeader
->VirtualSize
= 0;
768 SectionTwoHeader
->VirtualAddress
= 0;
769 SectionTwoHeader
->SizeOfRawData
= SectionTwoSize
;
770 SectionTwoHeader
->PointerToRawData
= SectionTwoOffset
;
771 SectionTwoHeader
->PointerToRelocations
= 0;
772 SectionTwoHeader
->PointerToLinenumbers
= 0;
773 SectionTwoHeader
->NumberOfRelocations
= 0;
774 SectionTwoHeader
->NumberOfLinenumbers
= 0;
775 SectionTwoHeader
->Characteristics
= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
;
776 SectionTwoHeader
->Characteristics
+= COFF::IMAGE_SCN_MEM_READ
;
779 void WindowsResourceCOFFWriter::writeFirstSection() {
780 // Write section one.
781 CurrentOffset
+= sizeof(coff_section
);
783 writeDirectoryTree();
784 writeDirectoryStringTable();
785 writeFirstSectionRelocations();
787 CurrentOffset
= alignTo(CurrentOffset
, SECTION_ALIGNMENT
);
790 void WindowsResourceCOFFWriter::writeSecondSection() {
791 // Now write the .rsrc$02 section.
792 for (auto const &RawDataEntry
: Data
) {
793 llvm::copy(RawDataEntry
, BufferStart
+ CurrentOffset
);
794 CurrentOffset
+= alignTo(RawDataEntry
.size(), sizeof(uint64_t));
797 CurrentOffset
= alignTo(CurrentOffset
, SECTION_ALIGNMENT
);
800 void WindowsResourceCOFFWriter::writeSymbolTable() {
801 // Now write the symbol table.
802 // First, the feat symbol.
803 auto *Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
804 coffnamecpy(Symbol
->Name
.ShortName
, "@feat.00");
805 Symbol
->Value
= 0x11;
806 Symbol
->SectionNumber
= 0xffff;
807 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
808 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
809 Symbol
->NumberOfAuxSymbols
= 0;
810 CurrentOffset
+= sizeof(coff_symbol16
);
812 // Now write the .rsrc1 symbol + aux.
813 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
814 coffnamecpy(Symbol
->Name
.ShortName
, ".rsrc$01");
816 Symbol
->SectionNumber
= 1;
817 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
818 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
819 Symbol
->NumberOfAuxSymbols
= 1;
820 CurrentOffset
+= sizeof(coff_symbol16
);
821 auto *Aux
= reinterpret_cast<coff_aux_section_definition
*>(BufferStart
+
823 Aux
->Length
= SectionOneSize
;
824 Aux
->NumberOfRelocations
= Data
.size();
825 Aux
->NumberOfLinenumbers
= 0;
827 Aux
->NumberLowPart
= 0;
829 CurrentOffset
+= sizeof(coff_aux_section_definition
);
831 // Now write the .rsrc2 symbol + aux.
832 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
833 coffnamecpy(Symbol
->Name
.ShortName
, ".rsrc$02");
835 Symbol
->SectionNumber
= 2;
836 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
837 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
838 Symbol
->NumberOfAuxSymbols
= 1;
839 CurrentOffset
+= sizeof(coff_symbol16
);
840 Aux
= reinterpret_cast<coff_aux_section_definition
*>(BufferStart
+
842 Aux
->Length
= SectionTwoSize
;
843 Aux
->NumberOfRelocations
= 0;
844 Aux
->NumberOfLinenumbers
= 0;
846 Aux
->NumberLowPart
= 0;
848 CurrentOffset
+= sizeof(coff_aux_section_definition
);
850 // Now write a symbol for each relocation.
851 for (unsigned i
= 0; i
< Data
.size(); i
++) {
852 auto RelocationName
= formatv("$R{0:X-6}", i
& 0xffffff).sstr
<COFF::NameSize
>();
853 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
854 coffnamecpy(Symbol
->Name
.ShortName
, RelocationName
);
855 Symbol
->Value
= DataOffsets
[i
];
856 Symbol
->SectionNumber
= 2;
857 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
858 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
859 Symbol
->NumberOfAuxSymbols
= 0;
860 CurrentOffset
+= sizeof(coff_symbol16
);
864 void WindowsResourceCOFFWriter::writeStringTable() {
865 // Just 4 null bytes for the string table.
866 auto COFFStringTable
= reinterpret_cast<void *>(BufferStart
+ CurrentOffset
);
867 memset(COFFStringTable
, 0, 4);
870 void WindowsResourceCOFFWriter::writeDirectoryTree() {
871 // Traverse parsed resource tree breadth-first and write the corresponding
873 std::queue
<const WindowsResourceParser::TreeNode
*> Queue
;
874 Queue
.push(&Resources
);
875 uint32_t NextLevelOffset
=
876 sizeof(coff_resource_dir_table
) + (Resources
.getStringChildren().size() +
877 Resources
.getIDChildren().size()) *
878 sizeof(coff_resource_dir_entry
);
879 std::vector
<const WindowsResourceParser::TreeNode
*> DataEntriesTreeOrder
;
880 uint32_t CurrentRelativeOffset
= 0;
882 while (!Queue
.empty()) {
883 auto CurrentNode
= Queue
.front();
885 auto *Table
= reinterpret_cast<coff_resource_dir_table
*>(BufferStart
+
887 Table
->Characteristics
= CurrentNode
->getCharacteristics();
888 Table
->TimeDateStamp
= 0;
889 Table
->MajorVersion
= CurrentNode
->getMajorVersion();
890 Table
->MinorVersion
= CurrentNode
->getMinorVersion();
891 auto &IDChildren
= CurrentNode
->getIDChildren();
892 auto &StringChildren
= CurrentNode
->getStringChildren();
893 Table
->NumberOfNameEntries
= StringChildren
.size();
894 Table
->NumberOfIDEntries
= IDChildren
.size();
895 CurrentOffset
+= sizeof(coff_resource_dir_table
);
896 CurrentRelativeOffset
+= sizeof(coff_resource_dir_table
);
898 // Write the directory entries immediately following each directory table.
899 for (auto const &Child
: StringChildren
) {
900 auto *Entry
= reinterpret_cast<coff_resource_dir_entry
*>(BufferStart
+
902 Entry
->Identifier
.setNameOffset(
903 StringTableOffsets
[Child
.second
->getStringIndex()]);
904 if (Child
.second
->checkIsDataNode()) {
905 Entry
->Offset
.DataEntryOffset
= NextLevelOffset
;
906 NextLevelOffset
+= sizeof(coff_resource_data_entry
);
907 DataEntriesTreeOrder
.push_back(Child
.second
.get());
909 Entry
->Offset
.SubdirOffset
= NextLevelOffset
+ (1 << 31);
910 NextLevelOffset
+= sizeof(coff_resource_dir_table
) +
911 (Child
.second
->getStringChildren().size() +
912 Child
.second
->getIDChildren().size()) *
913 sizeof(coff_resource_dir_entry
);
914 Queue
.push(Child
.second
.get());
916 CurrentOffset
+= sizeof(coff_resource_dir_entry
);
917 CurrentRelativeOffset
+= sizeof(coff_resource_dir_entry
);
919 for (auto const &Child
: IDChildren
) {
920 auto *Entry
= reinterpret_cast<coff_resource_dir_entry
*>(BufferStart
+
922 Entry
->Identifier
.ID
= Child
.first
;
923 if (Child
.second
->checkIsDataNode()) {
924 Entry
->Offset
.DataEntryOffset
= NextLevelOffset
;
925 NextLevelOffset
+= sizeof(coff_resource_data_entry
);
926 DataEntriesTreeOrder
.push_back(Child
.second
.get());
928 Entry
->Offset
.SubdirOffset
= NextLevelOffset
+ (1 << 31);
929 NextLevelOffset
+= sizeof(coff_resource_dir_table
) +
930 (Child
.second
->getStringChildren().size() +
931 Child
.second
->getIDChildren().size()) *
932 sizeof(coff_resource_dir_entry
);
933 Queue
.push(Child
.second
.get());
935 CurrentOffset
+= sizeof(coff_resource_dir_entry
);
936 CurrentRelativeOffset
+= sizeof(coff_resource_dir_entry
);
940 RelocationAddresses
.resize(Data
.size());
941 // Now write all the resource data entries.
942 for (const auto *DataNodes
: DataEntriesTreeOrder
) {
943 auto *Entry
= reinterpret_cast<coff_resource_data_entry
*>(BufferStart
+
945 RelocationAddresses
[DataNodes
->getDataIndex()] = CurrentRelativeOffset
;
946 Entry
->DataRVA
= 0; // Set to zero because it is a relocation.
947 Entry
->DataSize
= Data
[DataNodes
->getDataIndex()].size();
950 CurrentOffset
+= sizeof(coff_resource_data_entry
);
951 CurrentRelativeOffset
+= sizeof(coff_resource_data_entry
);
955 void WindowsResourceCOFFWriter::writeDirectoryStringTable() {
956 // Now write the directory string table for .rsrc$01
957 uint32_t TotalStringTableSize
= 0;
958 for (auto &String
: StringTable
) {
959 uint16_t Length
= String
.size();
960 support::endian::write16le(BufferStart
+ CurrentOffset
, Length
);
961 CurrentOffset
+= sizeof(uint16_t);
962 auto *Start
= reinterpret_cast<UTF16
*>(BufferStart
+ CurrentOffset
);
963 llvm::copy(String
, Start
);
964 CurrentOffset
+= Length
* sizeof(UTF16
);
965 TotalStringTableSize
+= Length
* sizeof(UTF16
) + sizeof(uint16_t);
968 alignTo(TotalStringTableSize
, sizeof(uint32_t)) - TotalStringTableSize
;
971 void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {
973 // Now write the relocations for .rsrc$01
974 // Five symbols already in table before we start, @feat.00 and 2 for each
976 uint32_t NextSymbolIndex
= 5;
977 for (unsigned i
= 0; i
< Data
.size(); i
++) {
979 reinterpret_cast<coff_relocation
*>(BufferStart
+ CurrentOffset
);
980 Reloc
->VirtualAddress
= RelocationAddresses
[i
];
981 Reloc
->SymbolTableIndex
= NextSymbolIndex
++;
982 switch (getMachineArchType(MachineType
)) {
984 Reloc
->Type
= COFF::IMAGE_REL_ARM_ADDR32NB
;
987 Reloc
->Type
= COFF::IMAGE_REL_AMD64_ADDR32NB
;
990 Reloc
->Type
= COFF::IMAGE_REL_I386_DIR32NB
;
992 case Triple::aarch64
:
993 Reloc
->Type
= COFF::IMAGE_REL_ARM64_ADDR32NB
;
996 llvm_unreachable("unknown machine type");
998 CurrentOffset
+= sizeof(coff_relocation
);
1002 Expected
<std::unique_ptr
<MemoryBuffer
>>
1003 writeWindowsResourceCOFF(COFF::MachineTypes MachineType
,
1004 const WindowsResourceParser
&Parser
,
1005 uint32_t TimeDateStamp
) {
1006 Error E
= Error::success();
1007 WindowsResourceCOFFWriter
Writer(MachineType
, Parser
, E
);
1010 return Writer
.write(TimeDateStamp
);
1013 } // namespace object