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/FormatVariadic.h"
16 #include "llvm/Support/MathExtras.h"
17 #include "llvm/Support/ScopedPrinter.h"
22 using namespace object
;
27 #define RETURN_IF_ERROR(X) \
31 #define UNWRAP_REF_OR_RETURN(Name, Expr) \
32 auto Name##OrErr = Expr; \
34 return Name##OrErr.takeError(); \
35 const auto &Name = *Name##OrErr;
37 #define UNWRAP_OR_RETURN(Name, Expr) \
38 auto Name##OrErr = Expr; \
40 return Name##OrErr.takeError(); \
41 auto Name = *Name##OrErr;
43 const uint32_t MIN_HEADER_SIZE
= 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t);
45 // COFF files seem to be inconsistent with alignment between sections, just use
46 // 8-byte because it makes everyone happy.
47 const uint32_t SECTION_ALIGNMENT
= sizeof(uint64_t);
49 WindowsResource::WindowsResource(MemoryBufferRef Source
)
50 : Binary(Binary::ID_WinRes
, Source
) {
51 size_t LeadingSize
= WIN_RES_MAGIC_SIZE
+ WIN_RES_NULL_ENTRY_SIZE
;
52 BBS
= BinaryByteStream(Data
.getBuffer().drop_front(LeadingSize
),
53 llvm::endianness::little
);
57 Expected
<std::unique_ptr
<WindowsResource
>>
58 WindowsResource::createWindowsResource(MemoryBufferRef Source
) {
59 if (Source
.getBufferSize() < WIN_RES_MAGIC_SIZE
+ WIN_RES_NULL_ENTRY_SIZE
)
60 return make_error
<GenericBinaryError
>(
61 Source
.getBufferIdentifier() + ": too small to be a resource file",
62 object_error::invalid_file_type
);
63 std::unique_ptr
<WindowsResource
> Ret(new WindowsResource(Source
));
64 return std::move(Ret
);
67 Expected
<ResourceEntryRef
> WindowsResource::getHeadEntry() {
68 if (BBS
.getLength() < sizeof(WinResHeaderPrefix
) + sizeof(WinResHeaderSuffix
))
69 return make_error
<EmptyResError
>(getFileName() + " contains no entries",
70 object_error::unexpected_eof
);
71 return ResourceEntryRef::create(BinaryStreamRef(BBS
), this);
74 ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref
,
75 const WindowsResource
*Owner
)
76 : Reader(Ref
), Owner(Owner
) {}
78 Expected
<ResourceEntryRef
>
79 ResourceEntryRef::create(BinaryStreamRef BSR
, const WindowsResource
*Owner
) {
80 auto Ref
= ResourceEntryRef(BSR
, Owner
);
81 if (auto E
= Ref
.loadNext())
86 Error
ResourceEntryRef::moveNext(bool &End
) {
87 // Reached end of all the entries.
88 if (Reader
.bytesRemaining() == 0) {
90 return Error::success();
92 RETURN_IF_ERROR(loadNext());
94 return Error::success();
97 static Error
readStringOrId(BinaryStreamReader
&Reader
, uint16_t &ID
,
98 ArrayRef
<UTF16
> &Str
, bool &IsString
) {
100 RETURN_IF_ERROR(Reader
.readInteger(IDFlag
));
101 IsString
= IDFlag
!= 0xffff;
106 sizeof(uint16_t)); // Re-read the bytes which we used to check the flag.
107 RETURN_IF_ERROR(Reader
.readWideString(Str
));
109 RETURN_IF_ERROR(Reader
.readInteger(ID
));
111 return Error::success();
114 Error
ResourceEntryRef::loadNext() {
115 const WinResHeaderPrefix
*Prefix
;
116 RETURN_IF_ERROR(Reader
.readObject(Prefix
));
118 if (Prefix
->HeaderSize
< MIN_HEADER_SIZE
)
119 return make_error
<GenericBinaryError
>(Owner
->getFileName() +
120 ": header size too small",
121 object_error::parse_failed
);
123 RETURN_IF_ERROR(readStringOrId(Reader
, TypeID
, Type
, IsStringType
));
125 RETURN_IF_ERROR(readStringOrId(Reader
, NameID
, Name
, IsStringName
));
127 RETURN_IF_ERROR(Reader
.padToAlignment(WIN_RES_HEADER_ALIGNMENT
));
129 RETURN_IF_ERROR(Reader
.readObject(Suffix
));
131 RETURN_IF_ERROR(Reader
.readArray(Data
, Prefix
->DataSize
));
133 RETURN_IF_ERROR(Reader
.padToAlignment(WIN_RES_DATA_ALIGNMENT
));
135 return Error::success();
138 WindowsResourceParser::WindowsResourceParser(bool MinGW
)
139 : Root(false), MinGW(MinGW
) {}
141 void printResourceTypeName(uint16_t TypeID
, raw_ostream
&OS
) {
143 case 1: OS
<< "CURSOR (ID 1)"; break;
144 case 2: OS
<< "BITMAP (ID 2)"; break;
145 case 3: OS
<< "ICON (ID 3)"; break;
146 case 4: OS
<< "MENU (ID 4)"; break;
147 case 5: OS
<< "DIALOG (ID 5)"; break;
148 case 6: OS
<< "STRINGTABLE (ID 6)"; break;
149 case 7: OS
<< "FONTDIR (ID 7)"; break;
150 case 8: OS
<< "FONT (ID 8)"; break;
151 case 9: OS
<< "ACCELERATOR (ID 9)"; break;
152 case 10: OS
<< "RCDATA (ID 10)"; break;
153 case 11: OS
<< "MESSAGETABLE (ID 11)"; break;
154 case 12: OS
<< "GROUP_CURSOR (ID 12)"; break;
155 case 14: OS
<< "GROUP_ICON (ID 14)"; break;
156 case 16: OS
<< "VERSIONINFO (ID 16)"; break;
157 case 17: OS
<< "DLGINCLUDE (ID 17)"; break;
158 case 19: OS
<< "PLUGPLAY (ID 19)"; break;
159 case 20: OS
<< "VXD (ID 20)"; break;
160 case 21: OS
<< "ANICURSOR (ID 21)"; break;
161 case 22: OS
<< "ANIICON (ID 22)"; break;
162 case 23: OS
<< "HTML (ID 23)"; break;
163 case 24: OS
<< "MANIFEST (ID 24)"; break;
164 default: OS
<< "ID " << TypeID
; break;
168 static bool convertUTF16LEToUTF8String(ArrayRef
<UTF16
> Src
, std::string
&Out
) {
169 if (!sys::IsBigEndianHost
)
170 return convertUTF16ToUTF8String(Src
, Out
);
172 std::vector
<UTF16
> EndianCorrectedSrc
;
173 EndianCorrectedSrc
.resize(Src
.size() + 1);
174 llvm::copy(Src
, EndianCorrectedSrc
.begin() + 1);
175 EndianCorrectedSrc
[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED
;
176 return convertUTF16ToUTF8String(ArrayRef(EndianCorrectedSrc
), Out
);
179 static std::string
makeDuplicateResourceError(
180 const ResourceEntryRef
&Entry
, StringRef File1
, StringRef File2
) {
182 raw_string_ostream
OS(Ret
);
184 OS
<< "duplicate resource:";
187 if (Entry
.checkTypeString()) {
189 if (!convertUTF16LEToUTF8String(Entry
.getTypeString(), UTF8
))
190 UTF8
= "(failed conversion from UTF16)";
191 OS
<< '\"' << UTF8
<< '\"';
193 printResourceTypeName(Entry
.getTypeID(), OS
);
196 if (Entry
.checkNameString()) {
198 if (!convertUTF16LEToUTF8String(Entry
.getNameString(), UTF8
))
199 UTF8
= "(failed conversion from UTF16)";
200 OS
<< '\"' << UTF8
<< '\"';
202 OS
<< "ID " << Entry
.getNameID();
205 OS
<< "/language " << Entry
.getLanguage() << ", in " << File1
<< " and in "
211 static void printStringOrID(const WindowsResourceParser::StringOrID
&S
,
212 raw_string_ostream
&OS
, bool IsType
, bool IsID
) {
215 if (!convertUTF16LEToUTF8String(S
.String
, UTF8
))
216 UTF8
= "(failed conversion from UTF16)";
217 OS
<< '\"' << UTF8
<< '\"';
219 printResourceTypeName(S
.ID
, OS
);
226 static std::string
makeDuplicateResourceError(
227 const std::vector
<WindowsResourceParser::StringOrID
> &Context
,
228 StringRef File1
, StringRef File2
) {
230 raw_string_ostream
OS(Ret
);
232 OS
<< "duplicate resource:";
234 if (Context
.size() >= 1) {
236 printStringOrID(Context
[0], OS
, /* IsType */ true, /* IsID */ true);
239 if (Context
.size() >= 2) {
241 printStringOrID(Context
[1], OS
, /* IsType */ false, /* IsID */ true);
244 if (Context
.size() >= 3) {
246 printStringOrID(Context
[2], OS
, /* IsType */ false, /* IsID */ false);
248 OS
<< ", in " << File1
<< " and in " << File2
;
253 // MinGW specific. Remove default manifests (with language zero) if there are
254 // other manifests present, and report an error if there are more than one
255 // manifest with a non-zero language code.
256 // GCC has the concept of a default manifest resource object, which gets
257 // linked in implicitly if present. This default manifest has got language
258 // id zero, and should be dropped silently if there's another manifest present.
259 // If the user resources surprisignly had a manifest with language id zero,
260 // we should also ignore the duplicate default manifest.
261 void WindowsResourceParser::cleanUpManifests(
262 std::vector
<std::string
> &Duplicates
) {
263 auto TypeIt
= Root
.IDChildren
.find(/* RT_MANIFEST */ 24);
264 if (TypeIt
== Root
.IDChildren
.end())
267 TreeNode
*TypeNode
= TypeIt
->second
.get();
269 TypeNode
->IDChildren
.find(/* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1);
270 if (NameIt
== TypeNode
->IDChildren
.end())
273 TreeNode
*NameNode
= NameIt
->second
.get();
274 if (NameNode
->IDChildren
.size() <= 1)
275 return; // None or one manifest present, all good.
277 // If we have more than one manifest, drop the language zero one if present,
279 auto LangZeroIt
= NameNode
->IDChildren
.find(0);
280 if (LangZeroIt
!= NameNode
->IDChildren
.end() &&
281 LangZeroIt
->second
->IsDataNode
) {
282 uint32_t RemovedIndex
= LangZeroIt
->second
->DataIndex
;
283 NameNode
->IDChildren
.erase(LangZeroIt
);
284 Data
.erase(Data
.begin() + RemovedIndex
);
285 Root
.shiftDataIndexDown(RemovedIndex
);
287 // If we're now down to one manifest, all is good.
288 if (NameNode
->IDChildren
.size() <= 1)
292 // More than one non-language-zero manifest
293 auto FirstIt
= NameNode
->IDChildren
.begin();
294 uint32_t FirstLang
= FirstIt
->first
;
295 TreeNode
*FirstNode
= FirstIt
->second
.get();
296 auto LastIt
= NameNode
->IDChildren
.rbegin();
297 uint32_t LastLang
= LastIt
->first
;
298 TreeNode
*LastNode
= LastIt
->second
.get();
299 Duplicates
.push_back(
300 ("duplicate non-default manifests with languages " + Twine(FirstLang
) +
301 " in " + InputFilenames
[FirstNode
->Origin
] + " and " + Twine(LastLang
) +
302 " in " + InputFilenames
[LastNode
->Origin
])
306 // Ignore duplicates of manifests with language zero (the default manifest),
307 // in case the user has provided a manifest with that language id. See
308 // the function comment above for context. Only returns true if MinGW is set
310 bool WindowsResourceParser::shouldIgnoreDuplicate(
311 const ResourceEntryRef
&Entry
) const {
312 return MinGW
&& !Entry
.checkTypeString() &&
313 Entry
.getTypeID() == /* RT_MANIFEST */ 24 &&
314 !Entry
.checkNameString() &&
315 Entry
.getNameID() == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&
316 Entry
.getLanguage() == 0;
319 bool WindowsResourceParser::shouldIgnoreDuplicate(
320 const std::vector
<StringOrID
> &Context
) const {
321 return MinGW
&& Context
.size() == 3 && !Context
[0].IsString
&&
322 Context
[0].ID
== /* RT_MANIFEST */ 24 && !Context
[1].IsString
&&
323 Context
[1].ID
== /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&
324 !Context
[2].IsString
&& Context
[2].ID
== 0;
327 Error
WindowsResourceParser::parse(WindowsResource
*WR
,
328 std::vector
<std::string
> &Duplicates
) {
329 auto EntryOrErr
= WR
->getHeadEntry();
331 auto E
= EntryOrErr
.takeError();
332 if (E
.isA
<EmptyResError
>()) {
333 // Check if the .res file contains no entries. In this case we don't have
334 // to throw an error but can rather just return without parsing anything.
335 // This applies for files which have a valid PE header magic and the
336 // mandatory empty null resource entry. Files which do not fit this
337 // criteria would have already been filtered out by
338 // WindowsResource::createWindowsResource().
339 consumeError(std::move(E
));
340 return Error::success();
345 ResourceEntryRef Entry
= EntryOrErr
.get();
346 uint32_t Origin
= InputFilenames
.size();
347 InputFilenames
.push_back(std::string(WR
->getFileName()));
352 bool IsNewNode
= Root
.addEntry(Entry
, Origin
, Data
, StringTable
, Node
);
354 if (!shouldIgnoreDuplicate(Entry
))
355 Duplicates
.push_back(makeDuplicateResourceError(
356 Entry
, InputFilenames
[Node
->Origin
], WR
->getFileName()));
359 RETURN_IF_ERROR(Entry
.moveNext(End
));
362 return Error::success();
365 Error
WindowsResourceParser::parse(ResourceSectionRef
&RSR
, StringRef Filename
,
366 std::vector
<std::string
> &Duplicates
) {
367 UNWRAP_REF_OR_RETURN(BaseTable
, RSR
.getBaseTable());
368 uint32_t Origin
= InputFilenames
.size();
369 InputFilenames
.push_back(std::string(Filename
));
370 std::vector
<StringOrID
> Context
;
371 return addChildren(Root
, RSR
, BaseTable
, Origin
, Context
, Duplicates
);
374 void WindowsResourceParser::printTree(raw_ostream
&OS
) const {
375 ScopedPrinter
Writer(OS
);
376 Root
.print(Writer
, "Resource Tree");
379 bool WindowsResourceParser::TreeNode::addEntry(
380 const ResourceEntryRef
&Entry
, uint32_t Origin
,
381 std::vector
<std::vector
<uint8_t>> &Data
,
382 std::vector
<std::vector
<UTF16
>> &StringTable
, TreeNode
*&Result
) {
383 TreeNode
&TypeNode
= addTypeNode(Entry
, StringTable
);
384 TreeNode
&NameNode
= TypeNode
.addNameNode(Entry
, StringTable
);
385 return NameNode
.addLanguageNode(Entry
, Origin
, Data
, Result
);
388 Error
WindowsResourceParser::addChildren(TreeNode
&Node
,
389 ResourceSectionRef
&RSR
,
390 const coff_resource_dir_table
&Table
,
392 std::vector
<StringOrID
> &Context
,
393 std::vector
<std::string
> &Duplicates
) {
395 for (int i
= 0; i
< Table
.NumberOfNameEntries
+ Table
.NumberOfIDEntries
;
397 UNWRAP_REF_OR_RETURN(Entry
, RSR
.getTableEntry(Table
, i
));
400 if (Entry
.Offset
.isSubDir()) {
402 // Create a new subdirectory and recurse
403 if (i
< Table
.NumberOfNameEntries
) {
404 UNWRAP_OR_RETURN(NameString
, RSR
.getEntryNameString(Entry
));
405 Child
= &Node
.addNameChild(NameString
, StringTable
);
406 Context
.push_back(StringOrID(NameString
));
408 Child
= &Node
.addIDChild(Entry
.Identifier
.ID
);
409 Context
.push_back(StringOrID(Entry
.Identifier
.ID
));
412 UNWRAP_REF_OR_RETURN(NextTable
, RSR
.getEntrySubDir(Entry
));
414 addChildren(*Child
, RSR
, NextTable
, Origin
, Context
, Duplicates
);
421 // Data leaves are supposed to have a numeric ID as identifier (language).
422 if (Table
.NumberOfNameEntries
> 0)
423 return createStringError(object_error::parse_failed
,
424 "unexpected string key for data object");
426 // Try adding a data leaf
427 UNWRAP_REF_OR_RETURN(DataEntry
, RSR
.getEntryData(Entry
));
429 Context
.push_back(StringOrID(Entry
.Identifier
.ID
));
430 bool Added
= Node
.addDataChild(Entry
.Identifier
.ID
, Table
.MajorVersion
,
431 Table
.MinorVersion
, Table
.Characteristics
,
432 Origin
, Data
.size(), Child
);
434 UNWRAP_OR_RETURN(Contents
, RSR
.getContents(DataEntry
));
435 Data
.push_back(ArrayRef
<uint8_t>(
436 reinterpret_cast<const uint8_t *>(Contents
.data()),
439 if (!shouldIgnoreDuplicate(Context
))
440 Duplicates
.push_back(makeDuplicateResourceError(
441 Context
, InputFilenames
[Child
->Origin
], InputFilenames
.back()));
447 return Error::success();
450 WindowsResourceParser::TreeNode::TreeNode(uint32_t StringIndex
)
451 : StringIndex(StringIndex
) {}
453 WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion
,
454 uint16_t MinorVersion
,
455 uint32_t Characteristics
,
456 uint32_t Origin
, uint32_t DataIndex
)
457 : IsDataNode(true), DataIndex(DataIndex
), MajorVersion(MajorVersion
),
458 MinorVersion(MinorVersion
), Characteristics(Characteristics
),
461 std::unique_ptr
<WindowsResourceParser::TreeNode
>
462 WindowsResourceParser::TreeNode::createStringNode(uint32_t Index
) {
463 return std::unique_ptr
<TreeNode
>(new TreeNode(Index
));
466 std::unique_ptr
<WindowsResourceParser::TreeNode
>
467 WindowsResourceParser::TreeNode::createIDNode() {
468 return std::unique_ptr
<TreeNode
>(new TreeNode(0));
471 std::unique_ptr
<WindowsResourceParser::TreeNode
>
472 WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion
,
473 uint16_t MinorVersion
,
474 uint32_t Characteristics
,
476 uint32_t DataIndex
) {
477 return std::unique_ptr
<TreeNode
>(new TreeNode(
478 MajorVersion
, MinorVersion
, Characteristics
, Origin
, DataIndex
));
481 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addTypeNode(
482 const ResourceEntryRef
&Entry
,
483 std::vector
<std::vector
<UTF16
>> &StringTable
) {
484 if (Entry
.checkTypeString())
485 return addNameChild(Entry
.getTypeString(), StringTable
);
487 return addIDChild(Entry
.getTypeID());
490 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addNameNode(
491 const ResourceEntryRef
&Entry
,
492 std::vector
<std::vector
<UTF16
>> &StringTable
) {
493 if (Entry
.checkNameString())
494 return addNameChild(Entry
.getNameString(), StringTable
);
496 return addIDChild(Entry
.getNameID());
499 bool WindowsResourceParser::TreeNode::addLanguageNode(
500 const ResourceEntryRef
&Entry
, uint32_t Origin
,
501 std::vector
<std::vector
<uint8_t>> &Data
, TreeNode
*&Result
) {
502 bool Added
= addDataChild(Entry
.getLanguage(), Entry
.getMajorVersion(),
503 Entry
.getMinorVersion(), Entry
.getCharacteristics(),
504 Origin
, Data
.size(), Result
);
506 Data
.push_back(Entry
.getData());
510 bool WindowsResourceParser::TreeNode::addDataChild(
511 uint32_t ID
, uint16_t MajorVersion
, uint16_t MinorVersion
,
512 uint32_t Characteristics
, uint32_t Origin
, uint32_t DataIndex
,
514 auto NewChild
= createDataNode(MajorVersion
, MinorVersion
, Characteristics
,
516 auto ElementInserted
= IDChildren
.emplace(ID
, std::move(NewChild
));
517 Result
= ElementInserted
.first
->second
.get();
518 return ElementInserted
.second
;
521 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addIDChild(
523 auto Child
= IDChildren
.find(ID
);
524 if (Child
== IDChildren
.end()) {
525 auto NewChild
= createIDNode();
526 WindowsResourceParser::TreeNode
&Node
= *NewChild
;
527 IDChildren
.emplace(ID
, std::move(NewChild
));
530 return *(Child
->second
);
533 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addNameChild(
534 ArrayRef
<UTF16
> NameRef
, std::vector
<std::vector
<UTF16
>> &StringTable
) {
535 std::string NameString
;
536 convertUTF16LEToUTF8String(NameRef
, NameString
);
538 auto Child
= StringChildren
.find(NameString
);
539 if (Child
== StringChildren
.end()) {
540 auto NewChild
= createStringNode(StringTable
.size());
541 StringTable
.push_back(NameRef
);
542 WindowsResourceParser::TreeNode
&Node
= *NewChild
;
543 StringChildren
.emplace(NameString
, std::move(NewChild
));
546 return *(Child
->second
);
549 void WindowsResourceParser::TreeNode::print(ScopedPrinter
&Writer
,
550 StringRef Name
) const {
551 ListScope
NodeScope(Writer
, Name
);
552 for (auto const &Child
: StringChildren
) {
553 Child
.second
->print(Writer
, Child
.first
);
555 for (auto const &Child
: IDChildren
) {
556 Child
.second
->print(Writer
, to_string(Child
.first
));
560 // This function returns the size of the entire resource tree, including
561 // directory tables, directory entries, and data entries. It does not include
562 // the directory strings or the relocations of the .rsrc section.
563 uint32_t WindowsResourceParser::TreeNode::getTreeSize() const {
564 uint32_t Size
= (IDChildren
.size() + StringChildren
.size()) *
565 sizeof(coff_resource_dir_entry
);
567 // Reached a node pointing to a data entry.
569 Size
+= sizeof(coff_resource_data_entry
);
573 // If the node does not point to data, it must have a directory table pointing
575 Size
+= sizeof(coff_resource_dir_table
);
577 for (auto const &Child
: StringChildren
) {
578 Size
+= Child
.second
->getTreeSize();
580 for (auto const &Child
: IDChildren
) {
581 Size
+= Child
.second
->getTreeSize();
586 // Shift DataIndex of all data children with an Index greater or equal to the
587 // given one, to fill a gap from removing an entry from the Data vector.
588 void WindowsResourceParser::TreeNode::shiftDataIndexDown(uint32_t Index
) {
589 if (IsDataNode
&& DataIndex
>= Index
) {
592 for (auto &Child
: IDChildren
)
593 Child
.second
->shiftDataIndexDown(Index
);
594 for (auto &Child
: StringChildren
)
595 Child
.second
->shiftDataIndexDown(Index
);
599 class WindowsResourceCOFFWriter
{
601 WindowsResourceCOFFWriter(COFF::MachineTypes MachineType
,
602 const WindowsResourceParser
&Parser
, Error
&E
);
603 std::unique_ptr
<MemoryBuffer
> write(uint32_t TimeDateStamp
);
606 void performFileLayout();
607 void performSectionOneLayout();
608 void performSectionTwoLayout();
609 void writeCOFFHeader(uint32_t TimeDateStamp
);
610 void writeFirstSectionHeader();
611 void writeSecondSectionHeader();
612 void writeFirstSection();
613 void writeSecondSection();
614 void writeSymbolTable();
615 void writeStringTable();
616 void writeDirectoryTree();
617 void writeDirectoryStringTable();
618 void writeFirstSectionRelocations();
619 std::unique_ptr
<WritableMemoryBuffer
> OutputBuffer
;
621 uint64_t CurrentOffset
= 0;
622 COFF::MachineTypes MachineType
;
623 const WindowsResourceParser::TreeNode
&Resources
;
624 const ArrayRef
<std::vector
<uint8_t>> Data
;
626 uint32_t SymbolTableOffset
;
627 uint32_t SectionOneSize
;
628 uint32_t SectionOneOffset
;
629 uint32_t SectionOneRelocations
;
630 uint32_t SectionTwoSize
;
631 uint32_t SectionTwoOffset
;
632 const ArrayRef
<std::vector
<UTF16
>> StringTable
;
633 std::vector
<uint32_t> StringTableOffsets
;
634 std::vector
<uint32_t> DataOffsets
;
635 std::vector
<uint32_t> RelocationAddresses
;
638 WindowsResourceCOFFWriter::WindowsResourceCOFFWriter(
639 COFF::MachineTypes MachineType
, const WindowsResourceParser
&Parser
,
641 : MachineType(MachineType
), Resources(Parser
.getTree()),
642 Data(Parser
.getData()), StringTable(Parser
.getStringTable()) {
645 OutputBuffer
= WritableMemoryBuffer::getNewMemBuffer(
646 FileSize
, "internal .obj file created from .res files");
649 void WindowsResourceCOFFWriter::performFileLayout() {
650 // Add size of COFF header.
651 FileSize
= COFF::Header16Size
;
653 // one .rsrc section header for directory tree, another for resource data.
654 FileSize
+= 2 * COFF::SectionSize
;
656 performSectionOneLayout();
657 performSectionTwoLayout();
659 // We have reached the address of the symbol table.
660 SymbolTableOffset
= FileSize
;
662 FileSize
+= COFF::Symbol16Size
; // size of the @feat.00 symbol.
663 FileSize
+= 4 * COFF::Symbol16Size
; // symbol + aux for each section.
664 FileSize
+= Data
.size() * COFF::Symbol16Size
; // 1 symbol per resource.
665 FileSize
+= 4; // four null bytes for the string table.
668 void WindowsResourceCOFFWriter::performSectionOneLayout() {
669 SectionOneOffset
= FileSize
;
671 SectionOneSize
= Resources
.getTreeSize();
672 uint32_t CurrentStringOffset
= SectionOneSize
;
673 uint32_t TotalStringTableSize
= 0;
674 for (auto const &String
: StringTable
) {
675 StringTableOffsets
.push_back(CurrentStringOffset
);
676 uint32_t StringSize
= String
.size() * sizeof(UTF16
) + sizeof(uint16_t);
677 CurrentStringOffset
+= StringSize
;
678 TotalStringTableSize
+= StringSize
;
680 SectionOneSize
+= alignTo(TotalStringTableSize
, sizeof(uint32_t));
682 // account for the relocations of section one.
683 SectionOneRelocations
= FileSize
+ SectionOneSize
;
684 FileSize
+= SectionOneSize
;
686 Data
.size() * COFF::RelocationSize
; // one relocation for each resource.
687 FileSize
= alignTo(FileSize
, SECTION_ALIGNMENT
);
690 void WindowsResourceCOFFWriter::performSectionTwoLayout() {
691 // add size of .rsrc$2 section, which contains all resource data on 8-byte
693 SectionTwoOffset
= FileSize
;
695 for (auto const &Entry
: Data
) {
696 DataOffsets
.push_back(SectionTwoSize
);
697 SectionTwoSize
+= alignTo(Entry
.size(), sizeof(uint64_t));
699 FileSize
+= SectionTwoSize
;
700 FileSize
= alignTo(FileSize
, SECTION_ALIGNMENT
);
703 std::unique_ptr
<MemoryBuffer
>
704 WindowsResourceCOFFWriter::write(uint32_t TimeDateStamp
) {
705 BufferStart
= OutputBuffer
->getBufferStart();
707 writeCOFFHeader(TimeDateStamp
);
708 writeFirstSectionHeader();
709 writeSecondSectionHeader();
711 writeSecondSection();
715 return std::move(OutputBuffer
);
718 // According to COFF specification, if the Src has a size equal to Dest,
719 // it's okay to *not* copy the trailing zero.
720 static void coffnamecpy(char (&Dest
)[COFF::NameSize
], StringRef Src
) {
721 assert(Src
.size() <= COFF::NameSize
&&
722 "Src is larger than COFF::NameSize");
723 assert((Src
.size() == COFF::NameSize
|| Dest
[Src
.size()] == '\0') &&
724 "Dest not zeroed upon initialization");
725 memcpy(Dest
, Src
.data(), Src
.size());
728 void WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp
) {
729 // Write the COFF header.
730 auto *Header
= reinterpret_cast<coff_file_header
*>(BufferStart
);
731 Header
->Machine
= MachineType
;
732 Header
->NumberOfSections
= 2;
733 Header
->TimeDateStamp
= TimeDateStamp
;
734 Header
->PointerToSymbolTable
= SymbolTableOffset
;
735 // One symbol for every resource plus 2 for each section and 1 for @feat.00
736 Header
->NumberOfSymbols
= Data
.size() + 5;
737 Header
->SizeOfOptionalHeader
= 0;
738 // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it.
739 Header
->Characteristics
= COFF::IMAGE_FILE_32BIT_MACHINE
;
742 void WindowsResourceCOFFWriter::writeFirstSectionHeader() {
743 // Write the first section header.
744 CurrentOffset
+= sizeof(coff_file_header
);
745 auto *SectionOneHeader
=
746 reinterpret_cast<coff_section
*>(BufferStart
+ CurrentOffset
);
747 coffnamecpy(SectionOneHeader
->Name
, ".rsrc$01");
748 SectionOneHeader
->VirtualSize
= 0;
749 SectionOneHeader
->VirtualAddress
= 0;
750 SectionOneHeader
->SizeOfRawData
= SectionOneSize
;
751 SectionOneHeader
->PointerToRawData
= SectionOneOffset
;
752 SectionOneHeader
->PointerToRelocations
= SectionOneRelocations
;
753 SectionOneHeader
->PointerToLinenumbers
= 0;
754 SectionOneHeader
->NumberOfRelocations
= Data
.size();
755 SectionOneHeader
->NumberOfLinenumbers
= 0;
756 SectionOneHeader
->Characteristics
+= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
;
757 SectionOneHeader
->Characteristics
+= COFF::IMAGE_SCN_MEM_READ
;
760 void WindowsResourceCOFFWriter::writeSecondSectionHeader() {
761 // Write the second section header.
762 CurrentOffset
+= sizeof(coff_section
);
763 auto *SectionTwoHeader
=
764 reinterpret_cast<coff_section
*>(BufferStart
+ CurrentOffset
);
765 coffnamecpy(SectionTwoHeader
->Name
, ".rsrc$02");
766 SectionTwoHeader
->VirtualSize
= 0;
767 SectionTwoHeader
->VirtualAddress
= 0;
768 SectionTwoHeader
->SizeOfRawData
= SectionTwoSize
;
769 SectionTwoHeader
->PointerToRawData
= SectionTwoOffset
;
770 SectionTwoHeader
->PointerToRelocations
= 0;
771 SectionTwoHeader
->PointerToLinenumbers
= 0;
772 SectionTwoHeader
->NumberOfRelocations
= 0;
773 SectionTwoHeader
->NumberOfLinenumbers
= 0;
774 SectionTwoHeader
->Characteristics
= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
;
775 SectionTwoHeader
->Characteristics
+= COFF::IMAGE_SCN_MEM_READ
;
778 void WindowsResourceCOFFWriter::writeFirstSection() {
779 // Write section one.
780 CurrentOffset
+= sizeof(coff_section
);
782 writeDirectoryTree();
783 writeDirectoryStringTable();
784 writeFirstSectionRelocations();
786 CurrentOffset
= alignTo(CurrentOffset
, SECTION_ALIGNMENT
);
789 void WindowsResourceCOFFWriter::writeSecondSection() {
790 // Now write the .rsrc$02 section.
791 for (auto const &RawDataEntry
: Data
) {
792 llvm::copy(RawDataEntry
, BufferStart
+ CurrentOffset
);
793 CurrentOffset
+= alignTo(RawDataEntry
.size(), sizeof(uint64_t));
796 CurrentOffset
= alignTo(CurrentOffset
, SECTION_ALIGNMENT
);
799 void WindowsResourceCOFFWriter::writeSymbolTable() {
800 // Now write the symbol table.
801 // First, the feat symbol.
802 auto *Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
803 coffnamecpy(Symbol
->Name
.ShortName
, "@feat.00");
804 Symbol
->Value
= 0x11;
805 Symbol
->SectionNumber
= 0xffff;
806 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
807 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
808 Symbol
->NumberOfAuxSymbols
= 0;
809 CurrentOffset
+= sizeof(coff_symbol16
);
811 // Now write the .rsrc1 symbol + aux.
812 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
813 coffnamecpy(Symbol
->Name
.ShortName
, ".rsrc$01");
815 Symbol
->SectionNumber
= 1;
816 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
817 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
818 Symbol
->NumberOfAuxSymbols
= 1;
819 CurrentOffset
+= sizeof(coff_symbol16
);
820 auto *Aux
= reinterpret_cast<coff_aux_section_definition
*>(BufferStart
+
822 Aux
->Length
= SectionOneSize
;
823 Aux
->NumberOfRelocations
= Data
.size();
824 Aux
->NumberOfLinenumbers
= 0;
826 Aux
->NumberLowPart
= 0;
828 CurrentOffset
+= sizeof(coff_aux_section_definition
);
830 // Now write the .rsrc2 symbol + aux.
831 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
832 coffnamecpy(Symbol
->Name
.ShortName
, ".rsrc$02");
834 Symbol
->SectionNumber
= 2;
835 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
836 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
837 Symbol
->NumberOfAuxSymbols
= 1;
838 CurrentOffset
+= sizeof(coff_symbol16
);
839 Aux
= reinterpret_cast<coff_aux_section_definition
*>(BufferStart
+
841 Aux
->Length
= SectionTwoSize
;
842 Aux
->NumberOfRelocations
= 0;
843 Aux
->NumberOfLinenumbers
= 0;
845 Aux
->NumberLowPart
= 0;
847 CurrentOffset
+= sizeof(coff_aux_section_definition
);
849 // Now write a symbol for each relocation.
850 for (unsigned i
= 0; i
< Data
.size(); i
++) {
851 auto RelocationName
= formatv("$R{0:X-6}", i
& 0xffffff).sstr
<COFF::NameSize
>();
852 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
853 coffnamecpy(Symbol
->Name
.ShortName
, RelocationName
);
854 Symbol
->Value
= DataOffsets
[i
];
855 Symbol
->SectionNumber
= 2;
856 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
857 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
858 Symbol
->NumberOfAuxSymbols
= 0;
859 CurrentOffset
+= sizeof(coff_symbol16
);
863 void WindowsResourceCOFFWriter::writeStringTable() {
864 // Just 4 null bytes for the string table.
865 auto COFFStringTable
= reinterpret_cast<void *>(BufferStart
+ CurrentOffset
);
866 memset(COFFStringTable
, 0, 4);
869 void WindowsResourceCOFFWriter::writeDirectoryTree() {
870 // Traverse parsed resource tree breadth-first and write the corresponding
872 std::queue
<const WindowsResourceParser::TreeNode
*> Queue
;
873 Queue
.push(&Resources
);
874 uint32_t NextLevelOffset
=
875 sizeof(coff_resource_dir_table
) + (Resources
.getStringChildren().size() +
876 Resources
.getIDChildren().size()) *
877 sizeof(coff_resource_dir_entry
);
878 std::vector
<const WindowsResourceParser::TreeNode
*> DataEntriesTreeOrder
;
879 uint32_t CurrentRelativeOffset
= 0;
881 while (!Queue
.empty()) {
882 auto CurrentNode
= Queue
.front();
884 auto *Table
= reinterpret_cast<coff_resource_dir_table
*>(BufferStart
+
886 Table
->Characteristics
= CurrentNode
->getCharacteristics();
887 Table
->TimeDateStamp
= 0;
888 Table
->MajorVersion
= CurrentNode
->getMajorVersion();
889 Table
->MinorVersion
= CurrentNode
->getMinorVersion();
890 auto &IDChildren
= CurrentNode
->getIDChildren();
891 auto &StringChildren
= CurrentNode
->getStringChildren();
892 Table
->NumberOfNameEntries
= StringChildren
.size();
893 Table
->NumberOfIDEntries
= IDChildren
.size();
894 CurrentOffset
+= sizeof(coff_resource_dir_table
);
895 CurrentRelativeOffset
+= sizeof(coff_resource_dir_table
);
897 // Write the directory entries immediately following each directory table.
898 for (auto const &Child
: StringChildren
) {
899 auto *Entry
= reinterpret_cast<coff_resource_dir_entry
*>(BufferStart
+
901 Entry
->Identifier
.setNameOffset(
902 StringTableOffsets
[Child
.second
->getStringIndex()]);
903 if (Child
.second
->checkIsDataNode()) {
904 Entry
->Offset
.DataEntryOffset
= NextLevelOffset
;
905 NextLevelOffset
+= sizeof(coff_resource_data_entry
);
906 DataEntriesTreeOrder
.push_back(Child
.second
.get());
908 Entry
->Offset
.SubdirOffset
= NextLevelOffset
+ (1 << 31);
909 NextLevelOffset
+= sizeof(coff_resource_dir_table
) +
910 (Child
.second
->getStringChildren().size() +
911 Child
.second
->getIDChildren().size()) *
912 sizeof(coff_resource_dir_entry
);
913 Queue
.push(Child
.second
.get());
915 CurrentOffset
+= sizeof(coff_resource_dir_entry
);
916 CurrentRelativeOffset
+= sizeof(coff_resource_dir_entry
);
918 for (auto const &Child
: IDChildren
) {
919 auto *Entry
= reinterpret_cast<coff_resource_dir_entry
*>(BufferStart
+
921 Entry
->Identifier
.ID
= Child
.first
;
922 if (Child
.second
->checkIsDataNode()) {
923 Entry
->Offset
.DataEntryOffset
= NextLevelOffset
;
924 NextLevelOffset
+= sizeof(coff_resource_data_entry
);
925 DataEntriesTreeOrder
.push_back(Child
.second
.get());
927 Entry
->Offset
.SubdirOffset
= NextLevelOffset
+ (1 << 31);
928 NextLevelOffset
+= sizeof(coff_resource_dir_table
) +
929 (Child
.second
->getStringChildren().size() +
930 Child
.second
->getIDChildren().size()) *
931 sizeof(coff_resource_dir_entry
);
932 Queue
.push(Child
.second
.get());
934 CurrentOffset
+= sizeof(coff_resource_dir_entry
);
935 CurrentRelativeOffset
+= sizeof(coff_resource_dir_entry
);
939 RelocationAddresses
.resize(Data
.size());
940 // Now write all the resource data entries.
941 for (const auto *DataNodes
: DataEntriesTreeOrder
) {
942 auto *Entry
= reinterpret_cast<coff_resource_data_entry
*>(BufferStart
+
944 RelocationAddresses
[DataNodes
->getDataIndex()] = CurrentRelativeOffset
;
945 Entry
->DataRVA
= 0; // Set to zero because it is a relocation.
946 Entry
->DataSize
= Data
[DataNodes
->getDataIndex()].size();
949 CurrentOffset
+= sizeof(coff_resource_data_entry
);
950 CurrentRelativeOffset
+= sizeof(coff_resource_data_entry
);
954 void WindowsResourceCOFFWriter::writeDirectoryStringTable() {
955 // Now write the directory string table for .rsrc$01
956 uint32_t TotalStringTableSize
= 0;
957 for (auto &String
: StringTable
) {
958 uint16_t Length
= String
.size();
959 support::endian::write16le(BufferStart
+ CurrentOffset
, Length
);
960 CurrentOffset
+= sizeof(uint16_t);
961 auto *Start
= reinterpret_cast<UTF16
*>(BufferStart
+ CurrentOffset
);
962 llvm::copy(String
, Start
);
963 CurrentOffset
+= Length
* sizeof(UTF16
);
964 TotalStringTableSize
+= Length
* sizeof(UTF16
) + sizeof(uint16_t);
967 alignTo(TotalStringTableSize
, sizeof(uint32_t)) - TotalStringTableSize
;
970 void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {
972 // Now write the relocations for .rsrc$01
973 // Five symbols already in table before we start, @feat.00 and 2 for each
975 uint32_t NextSymbolIndex
= 5;
976 for (unsigned i
= 0; i
< Data
.size(); i
++) {
978 reinterpret_cast<coff_relocation
*>(BufferStart
+ CurrentOffset
);
979 Reloc
->VirtualAddress
= RelocationAddresses
[i
];
980 Reloc
->SymbolTableIndex
= NextSymbolIndex
++;
981 switch (MachineType
) {
982 case COFF::IMAGE_FILE_MACHINE_ARMNT
:
983 Reloc
->Type
= COFF::IMAGE_REL_ARM_ADDR32NB
;
985 case COFF::IMAGE_FILE_MACHINE_AMD64
:
986 Reloc
->Type
= COFF::IMAGE_REL_AMD64_ADDR32NB
;
988 case COFF::IMAGE_FILE_MACHINE_I386
:
989 Reloc
->Type
= COFF::IMAGE_REL_I386_DIR32NB
;
991 case COFF::IMAGE_FILE_MACHINE_ARM64
:
992 case COFF::IMAGE_FILE_MACHINE_ARM64EC
:
993 case COFF::IMAGE_FILE_MACHINE_ARM64X
:
994 Reloc
->Type
= COFF::IMAGE_REL_ARM64_ADDR32NB
;
997 llvm_unreachable("unknown machine type");
999 CurrentOffset
+= sizeof(coff_relocation
);
1003 Expected
<std::unique_ptr
<MemoryBuffer
>>
1004 writeWindowsResourceCOFF(COFF::MachineTypes MachineType
,
1005 const WindowsResourceParser
&Parser
,
1006 uint32_t TimeDateStamp
) {
1007 Error E
= Error::success();
1008 WindowsResourceCOFFWriter
Writer(MachineType
, Parser
, E
);
1010 return std::move(E
);
1011 return Writer
.write(TimeDateStamp
);
1014 } // namespace object