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(bool MinGW
)
141 : Root(false), MinGW(MinGW
) {}
143 void printResourceTypeName(uint16_t TypeID
, raw_ostream
&OS
) {
145 case 1: OS
<< "CURSOR (ID 1)"; break;
146 case 2: OS
<< "BITMAP (ID 2)"; break;
147 case 3: OS
<< "ICON (ID 3)"; break;
148 case 4: OS
<< "MENU (ID 4)"; break;
149 case 5: OS
<< "DIALOG (ID 5)"; break;
150 case 6: OS
<< "STRINGTABLE (ID 6)"; break;
151 case 7: OS
<< "FONTDIR (ID 7)"; break;
152 case 8: OS
<< "FONT (ID 8)"; break;
153 case 9: OS
<< "ACCELERATOR (ID 9)"; break;
154 case 10: OS
<< "RCDATA (ID 10)"; break;
155 case 11: OS
<< "MESSAGETABLE (ID 11)"; break;
156 case 12: OS
<< "GROUP_CURSOR (ID 12)"; break;
157 case 14: OS
<< "GROUP_ICON (ID 14)"; break;
158 case 16: OS
<< "VERSIONINFO (ID 16)"; break;
159 case 17: OS
<< "DLGINCLUDE (ID 17)"; break;
160 case 19: OS
<< "PLUGPLAY (ID 19)"; break;
161 case 20: OS
<< "VXD (ID 20)"; break;
162 case 21: OS
<< "ANICURSOR (ID 21)"; break;
163 case 22: OS
<< "ANIICON (ID 22)"; break;
164 case 23: OS
<< "HTML (ID 23)"; break;
165 case 24: OS
<< "MANIFEST (ID 24)"; break;
166 default: OS
<< "ID " << TypeID
; break;
170 static bool convertUTF16LEToUTF8String(ArrayRef
<UTF16
> Src
, std::string
&Out
) {
171 if (!sys::IsBigEndianHost
)
172 return convertUTF16ToUTF8String(Src
, Out
);
174 std::vector
<UTF16
> EndianCorrectedSrc
;
175 EndianCorrectedSrc
.resize(Src
.size() + 1);
176 llvm::copy(Src
, EndianCorrectedSrc
.begin() + 1);
177 EndianCorrectedSrc
[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED
;
178 return convertUTF16ToUTF8String(makeArrayRef(EndianCorrectedSrc
), Out
);
181 static std::string
makeDuplicateResourceError(
182 const ResourceEntryRef
&Entry
, StringRef File1
, StringRef File2
) {
184 raw_string_ostream
OS(Ret
);
186 OS
<< "duplicate resource:";
189 if (Entry
.checkTypeString()) {
191 if (!convertUTF16LEToUTF8String(Entry
.getTypeString(), UTF8
))
192 UTF8
= "(failed conversion from UTF16)";
193 OS
<< '\"' << UTF8
<< '\"';
195 printResourceTypeName(Entry
.getTypeID(), OS
);
198 if (Entry
.checkNameString()) {
200 if (!convertUTF16LEToUTF8String(Entry
.getNameString(), UTF8
))
201 UTF8
= "(failed conversion from UTF16)";
202 OS
<< '\"' << UTF8
<< '\"';
204 OS
<< "ID " << Entry
.getNameID();
207 OS
<< "/language " << Entry
.getLanguage() << ", in " << File1
<< " and in "
213 static void printStringOrID(const WindowsResourceParser::StringOrID
&S
,
214 raw_string_ostream
&OS
, bool IsType
, bool IsID
) {
217 if (!convertUTF16LEToUTF8String(S
.String
, UTF8
))
218 UTF8
= "(failed conversion from UTF16)";
219 OS
<< '\"' << UTF8
<< '\"';
221 printResourceTypeName(S
.ID
, OS
);
228 static std::string
makeDuplicateResourceError(
229 const std::vector
<WindowsResourceParser::StringOrID
> &Context
,
230 StringRef File1
, StringRef File2
) {
232 raw_string_ostream
OS(Ret
);
234 OS
<< "duplicate resource:";
236 if (Context
.size() >= 1) {
238 printStringOrID(Context
[0], OS
, /* IsType */ true, /* IsID */ true);
241 if (Context
.size() >= 2) {
243 printStringOrID(Context
[1], OS
, /* IsType */ false, /* IsID */ true);
246 if (Context
.size() >= 3) {
248 printStringOrID(Context
[2], OS
, /* IsType */ false, /* IsID */ false);
250 OS
<< ", in " << File1
<< " and in " << File2
;
255 // MinGW specific. Remove default manifests (with language zero) if there are
256 // other manifests present, and report an error if there are more than one
257 // manifest with a non-zero language code.
258 // GCC has the concept of a default manifest resource object, which gets
259 // linked in implicitly if present. This default manifest has got language
260 // id zero, and should be dropped silently if there's another manifest present.
261 // If the user resources surprisignly had a manifest with language id zero,
262 // we should also ignore the duplicate default manifest.
263 void WindowsResourceParser::cleanUpManifests(
264 std::vector
<std::string
> &Duplicates
) {
265 auto TypeIt
= Root
.IDChildren
.find(/* RT_MANIFEST */ 24);
266 if (TypeIt
== Root
.IDChildren
.end())
269 TreeNode
*TypeNode
= TypeIt
->second
.get();
271 TypeNode
->IDChildren
.find(/* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1);
272 if (NameIt
== TypeNode
->IDChildren
.end())
275 TreeNode
*NameNode
= NameIt
->second
.get();
276 if (NameNode
->IDChildren
.size() <= 1)
277 return; // None or one manifest present, all good.
279 // If we have more than one manifest, drop the language zero one if present,
281 auto LangZeroIt
= NameNode
->IDChildren
.find(0);
282 if (LangZeroIt
!= NameNode
->IDChildren
.end() &&
283 LangZeroIt
->second
->IsDataNode
) {
284 uint32_t RemovedIndex
= LangZeroIt
->second
->DataIndex
;
285 NameNode
->IDChildren
.erase(LangZeroIt
);
286 Data
.erase(Data
.begin() + RemovedIndex
);
287 Root
.shiftDataIndexDown(RemovedIndex
);
289 // If we're now down to one manifest, all is good.
290 if (NameNode
->IDChildren
.size() <= 1)
294 // More than one non-language-zero manifest
295 auto FirstIt
= NameNode
->IDChildren
.begin();
296 uint32_t FirstLang
= FirstIt
->first
;
297 TreeNode
*FirstNode
= FirstIt
->second
.get();
298 auto LastIt
= NameNode
->IDChildren
.rbegin();
299 uint32_t LastLang
= LastIt
->first
;
300 TreeNode
*LastNode
= LastIt
->second
.get();
301 Duplicates
.push_back(
302 ("duplicate non-default manifests with languages " + Twine(FirstLang
) +
303 " in " + InputFilenames
[FirstNode
->Origin
] + " and " + Twine(LastLang
) +
304 " in " + InputFilenames
[LastNode
->Origin
])
308 // Ignore duplicates of manifests with language zero (the default manifest),
309 // in case the user has provided a manifest with that language id. See
310 // the function comment above for context. Only returns true if MinGW is set
312 bool WindowsResourceParser::shouldIgnoreDuplicate(
313 const ResourceEntryRef
&Entry
) const {
314 return MinGW
&& !Entry
.checkTypeString() &&
315 Entry
.getTypeID() == /* RT_MANIFEST */ 24 &&
316 !Entry
.checkNameString() &&
317 Entry
.getNameID() == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&
318 Entry
.getLanguage() == 0;
321 bool WindowsResourceParser::shouldIgnoreDuplicate(
322 const std::vector
<StringOrID
> &Context
) const {
323 return MinGW
&& Context
.size() == 3 && !Context
[0].IsString
&&
324 Context
[0].ID
== /* RT_MANIFEST */ 24 && !Context
[1].IsString
&&
325 Context
[1].ID
== /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&
326 !Context
[2].IsString
&& Context
[2].ID
== 0;
329 Error
WindowsResourceParser::parse(WindowsResource
*WR
,
330 std::vector
<std::string
> &Duplicates
) {
331 auto EntryOrErr
= WR
->getHeadEntry();
333 auto E
= EntryOrErr
.takeError();
334 if (E
.isA
<EmptyResError
>()) {
335 // Check if the .res file contains no entries. In this case we don't have
336 // to throw an error but can rather just return without parsing anything.
337 // This applies for files which have a valid PE header magic and the
338 // mandatory empty null resource entry. Files which do not fit this
339 // criteria would have already been filtered out by
340 // WindowsResource::createWindowsResource().
341 consumeError(std::move(E
));
342 return Error::success();
347 ResourceEntryRef Entry
= EntryOrErr
.get();
348 uint32_t Origin
= InputFilenames
.size();
349 InputFilenames
.push_back(WR
->getFileName());
354 bool IsNewNode
= Root
.addEntry(Entry
, Origin
, Data
, StringTable
, Node
);
356 if (!shouldIgnoreDuplicate(Entry
))
357 Duplicates
.push_back(makeDuplicateResourceError(
358 Entry
, InputFilenames
[Node
->Origin
], WR
->getFileName()));
361 RETURN_IF_ERROR(Entry
.moveNext(End
));
364 return Error::success();
367 Error
WindowsResourceParser::parse(ResourceSectionRef
&RSR
, StringRef Filename
,
368 std::vector
<std::string
> &Duplicates
) {
369 UNWRAP_REF_OR_RETURN(BaseTable
, RSR
.getBaseTable());
370 uint32_t Origin
= InputFilenames
.size();
371 InputFilenames
.push_back(Filename
);
372 std::vector
<StringOrID
> Context
;
373 return addChildren(Root
, RSR
, BaseTable
, Origin
, Context
, Duplicates
);
376 void WindowsResourceParser::printTree(raw_ostream
&OS
) const {
377 ScopedPrinter
Writer(OS
);
378 Root
.print(Writer
, "Resource Tree");
381 bool WindowsResourceParser::TreeNode::addEntry(
382 const ResourceEntryRef
&Entry
, uint32_t Origin
,
383 std::vector
<std::vector
<uint8_t>> &Data
,
384 std::vector
<std::vector
<UTF16
>> &StringTable
, TreeNode
*&Result
) {
385 TreeNode
&TypeNode
= addTypeNode(Entry
, StringTable
);
386 TreeNode
&NameNode
= TypeNode
.addNameNode(Entry
, StringTable
);
387 return NameNode
.addLanguageNode(Entry
, Origin
, Data
, Result
);
390 Error
WindowsResourceParser::addChildren(TreeNode
&Node
,
391 ResourceSectionRef
&RSR
,
392 const coff_resource_dir_table
&Table
,
394 std::vector
<StringOrID
> &Context
,
395 std::vector
<std::string
> &Duplicates
) {
397 for (int i
= 0; i
< Table
.NumberOfNameEntries
+ Table
.NumberOfIDEntries
;
399 UNWRAP_REF_OR_RETURN(Entry
, RSR
.getTableEntry(Table
, i
));
402 if (Entry
.Offset
.isSubDir()) {
404 // Create a new subdirectory and recurse
405 if (i
< Table
.NumberOfNameEntries
) {
406 UNWRAP_OR_RETURN(NameString
, RSR
.getEntryNameString(Entry
));
407 Child
= &Node
.addNameChild(NameString
, StringTable
);
408 Context
.push_back(StringOrID(NameString
));
410 Child
= &Node
.addIDChild(Entry
.Identifier
.ID
);
411 Context
.push_back(StringOrID(Entry
.Identifier
.ID
));
414 UNWRAP_REF_OR_RETURN(NextTable
, RSR
.getEntrySubDir(Entry
));
416 addChildren(*Child
, RSR
, NextTable
, Origin
, Context
, Duplicates
);
423 // Data leaves are supposed to have a numeric ID as identifier (language).
424 if (Table
.NumberOfNameEntries
> 0)
425 return createStringError(object_error::parse_failed
,
426 "unexpected string key for data object");
428 // Try adding a data leaf
429 UNWRAP_REF_OR_RETURN(DataEntry
, RSR
.getEntryData(Entry
));
431 Context
.push_back(StringOrID(Entry
.Identifier
.ID
));
432 bool Added
= Node
.addDataChild(Entry
.Identifier
.ID
, Table
.MajorVersion
,
433 Table
.MinorVersion
, Table
.Characteristics
,
434 Origin
, Data
.size(), Child
);
436 UNWRAP_OR_RETURN(Contents
, RSR
.getContents(DataEntry
));
437 Data
.push_back(ArrayRef
<uint8_t>(
438 reinterpret_cast<const uint8_t *>(Contents
.data()),
441 if (!shouldIgnoreDuplicate(Context
))
442 Duplicates
.push_back(makeDuplicateResourceError(
443 Context
, InputFilenames
[Child
->Origin
], InputFilenames
.back()));
449 return Error::success();
452 WindowsResourceParser::TreeNode::TreeNode(uint32_t StringIndex
)
453 : StringIndex(StringIndex
) {}
455 WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion
,
456 uint16_t MinorVersion
,
457 uint32_t Characteristics
,
458 uint32_t Origin
, uint32_t DataIndex
)
459 : IsDataNode(true), DataIndex(DataIndex
), MajorVersion(MajorVersion
),
460 MinorVersion(MinorVersion
), Characteristics(Characteristics
),
463 std::unique_ptr
<WindowsResourceParser::TreeNode
>
464 WindowsResourceParser::TreeNode::createStringNode(uint32_t Index
) {
465 return std::unique_ptr
<TreeNode
>(new TreeNode(Index
));
468 std::unique_ptr
<WindowsResourceParser::TreeNode
>
469 WindowsResourceParser::TreeNode::createIDNode() {
470 return std::unique_ptr
<TreeNode
>(new TreeNode(0));
473 std::unique_ptr
<WindowsResourceParser::TreeNode
>
474 WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion
,
475 uint16_t MinorVersion
,
476 uint32_t Characteristics
,
478 uint32_t DataIndex
) {
479 return std::unique_ptr
<TreeNode
>(new TreeNode(
480 MajorVersion
, MinorVersion
, Characteristics
, Origin
, DataIndex
));
483 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addTypeNode(
484 const ResourceEntryRef
&Entry
,
485 std::vector
<std::vector
<UTF16
>> &StringTable
) {
486 if (Entry
.checkTypeString())
487 return addNameChild(Entry
.getTypeString(), StringTable
);
489 return addIDChild(Entry
.getTypeID());
492 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addNameNode(
493 const ResourceEntryRef
&Entry
,
494 std::vector
<std::vector
<UTF16
>> &StringTable
) {
495 if (Entry
.checkNameString())
496 return addNameChild(Entry
.getNameString(), StringTable
);
498 return addIDChild(Entry
.getNameID());
501 bool WindowsResourceParser::TreeNode::addLanguageNode(
502 const ResourceEntryRef
&Entry
, uint32_t Origin
,
503 std::vector
<std::vector
<uint8_t>> &Data
, TreeNode
*&Result
) {
504 bool Added
= addDataChild(Entry
.getLanguage(), Entry
.getMajorVersion(),
505 Entry
.getMinorVersion(), Entry
.getCharacteristics(),
506 Origin
, Data
.size(), Result
);
508 Data
.push_back(Entry
.getData());
512 bool WindowsResourceParser::TreeNode::addDataChild(
513 uint32_t ID
, uint16_t MajorVersion
, uint16_t MinorVersion
,
514 uint32_t Characteristics
, uint32_t Origin
, uint32_t DataIndex
,
516 auto NewChild
= createDataNode(MajorVersion
, MinorVersion
, Characteristics
,
518 auto ElementInserted
= IDChildren
.emplace(ID
, std::move(NewChild
));
519 Result
= ElementInserted
.first
->second
.get();
520 return ElementInserted
.second
;
523 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addIDChild(
525 auto Child
= IDChildren
.find(ID
);
526 if (Child
== IDChildren
.end()) {
527 auto NewChild
= createIDNode();
528 WindowsResourceParser::TreeNode
&Node
= *NewChild
;
529 IDChildren
.emplace(ID
, std::move(NewChild
));
532 return *(Child
->second
);
535 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addNameChild(
536 ArrayRef
<UTF16
> NameRef
, std::vector
<std::vector
<UTF16
>> &StringTable
) {
537 std::string NameString
;
538 convertUTF16LEToUTF8String(NameRef
, NameString
);
540 auto Child
= StringChildren
.find(NameString
);
541 if (Child
== StringChildren
.end()) {
542 auto NewChild
= createStringNode(StringTable
.size());
543 StringTable
.push_back(NameRef
);
544 WindowsResourceParser::TreeNode
&Node
= *NewChild
;
545 StringChildren
.emplace(NameString
, std::move(NewChild
));
548 return *(Child
->second
);
551 void WindowsResourceParser::TreeNode::print(ScopedPrinter
&Writer
,
552 StringRef Name
) const {
553 ListScope
NodeScope(Writer
, Name
);
554 for (auto const &Child
: StringChildren
) {
555 Child
.second
->print(Writer
, Child
.first
);
557 for (auto const &Child
: IDChildren
) {
558 Child
.second
->print(Writer
, to_string(Child
.first
));
562 // This function returns the size of the entire resource tree, including
563 // directory tables, directory entries, and data entries. It does not include
564 // the directory strings or the relocations of the .rsrc section.
565 uint32_t WindowsResourceParser::TreeNode::getTreeSize() const {
566 uint32_t Size
= (IDChildren
.size() + StringChildren
.size()) *
567 sizeof(coff_resource_dir_entry
);
569 // Reached a node pointing to a data entry.
571 Size
+= sizeof(coff_resource_data_entry
);
575 // If the node does not point to data, it must have a directory table pointing
577 Size
+= sizeof(coff_resource_dir_table
);
579 for (auto const &Child
: StringChildren
) {
580 Size
+= Child
.second
->getTreeSize();
582 for (auto const &Child
: IDChildren
) {
583 Size
+= Child
.second
->getTreeSize();
588 // Shift DataIndex of all data children with an Index greater or equal to the
589 // given one, to fill a gap from removing an entry from the Data vector.
590 void WindowsResourceParser::TreeNode::shiftDataIndexDown(uint32_t Index
) {
591 if (IsDataNode
&& DataIndex
>= Index
) {
594 for (auto &Child
: IDChildren
)
595 Child
.second
->shiftDataIndexDown(Index
);
596 for (auto &Child
: StringChildren
)
597 Child
.second
->shiftDataIndexDown(Index
);
601 class WindowsResourceCOFFWriter
{
603 WindowsResourceCOFFWriter(COFF::MachineTypes MachineType
,
604 const WindowsResourceParser
&Parser
, Error
&E
);
605 std::unique_ptr
<MemoryBuffer
> write(uint32_t TimeDateStamp
);
608 void performFileLayout();
609 void performSectionOneLayout();
610 void performSectionTwoLayout();
611 void writeCOFFHeader(uint32_t TimeDateStamp
);
612 void writeFirstSectionHeader();
613 void writeSecondSectionHeader();
614 void writeFirstSection();
615 void writeSecondSection();
616 void writeSymbolTable();
617 void writeStringTable();
618 void writeDirectoryTree();
619 void writeDirectoryStringTable();
620 void writeFirstSectionRelocations();
621 std::unique_ptr
<WritableMemoryBuffer
> OutputBuffer
;
623 uint64_t CurrentOffset
= 0;
624 COFF::MachineTypes MachineType
;
625 const WindowsResourceParser::TreeNode
&Resources
;
626 const ArrayRef
<std::vector
<uint8_t>> Data
;
628 uint32_t SymbolTableOffset
;
629 uint32_t SectionOneSize
;
630 uint32_t SectionOneOffset
;
631 uint32_t SectionOneRelocations
;
632 uint32_t SectionTwoSize
;
633 uint32_t SectionTwoOffset
;
634 const ArrayRef
<std::vector
<UTF16
>> StringTable
;
635 std::vector
<uint32_t> StringTableOffsets
;
636 std::vector
<uint32_t> DataOffsets
;
637 std::vector
<uint32_t> RelocationAddresses
;
640 WindowsResourceCOFFWriter::WindowsResourceCOFFWriter(
641 COFF::MachineTypes MachineType
, const WindowsResourceParser
&Parser
,
643 : MachineType(MachineType
), Resources(Parser
.getTree()),
644 Data(Parser
.getData()), StringTable(Parser
.getStringTable()) {
647 OutputBuffer
= WritableMemoryBuffer::getNewMemBuffer(
648 FileSize
, "internal .obj file created from .res files");
651 void WindowsResourceCOFFWriter::performFileLayout() {
652 // Add size of COFF header.
653 FileSize
= COFF::Header16Size
;
655 // one .rsrc section header for directory tree, another for resource data.
656 FileSize
+= 2 * COFF::SectionSize
;
658 performSectionOneLayout();
659 performSectionTwoLayout();
661 // We have reached the address of the symbol table.
662 SymbolTableOffset
= FileSize
;
664 FileSize
+= COFF::Symbol16Size
; // size of the @feat.00 symbol.
665 FileSize
+= 4 * COFF::Symbol16Size
; // symbol + aux for each section.
666 FileSize
+= Data
.size() * COFF::Symbol16Size
; // 1 symbol per resource.
667 FileSize
+= 4; // four null bytes for the string table.
670 void WindowsResourceCOFFWriter::performSectionOneLayout() {
671 SectionOneOffset
= FileSize
;
673 SectionOneSize
= Resources
.getTreeSize();
674 uint32_t CurrentStringOffset
= SectionOneSize
;
675 uint32_t TotalStringTableSize
= 0;
676 for (auto const &String
: StringTable
) {
677 StringTableOffsets
.push_back(CurrentStringOffset
);
678 uint32_t StringSize
= String
.size() * sizeof(UTF16
) + sizeof(uint16_t);
679 CurrentStringOffset
+= StringSize
;
680 TotalStringTableSize
+= StringSize
;
682 SectionOneSize
+= alignTo(TotalStringTableSize
, sizeof(uint32_t));
684 // account for the relocations of section one.
685 SectionOneRelocations
= FileSize
+ SectionOneSize
;
686 FileSize
+= SectionOneSize
;
688 Data
.size() * COFF::RelocationSize
; // one relocation for each resource.
689 FileSize
= alignTo(FileSize
, SECTION_ALIGNMENT
);
692 void WindowsResourceCOFFWriter::performSectionTwoLayout() {
693 // add size of .rsrc$2 section, which contains all resource data on 8-byte
695 SectionTwoOffset
= FileSize
;
697 for (auto const &Entry
: Data
) {
698 DataOffsets
.push_back(SectionTwoSize
);
699 SectionTwoSize
+= alignTo(Entry
.size(), sizeof(uint64_t));
701 FileSize
+= SectionTwoSize
;
702 FileSize
= alignTo(FileSize
, SECTION_ALIGNMENT
);
705 std::unique_ptr
<MemoryBuffer
>
706 WindowsResourceCOFFWriter::write(uint32_t TimeDateStamp
) {
707 BufferStart
= OutputBuffer
->getBufferStart();
709 writeCOFFHeader(TimeDateStamp
);
710 writeFirstSectionHeader();
711 writeSecondSectionHeader();
713 writeSecondSection();
717 return std::move(OutputBuffer
);
720 // According to COFF specification, if the Src has a size equal to Dest,
721 // it's okay to *not* copy the trailing zero.
722 static void coffnamecpy(char (&Dest
)[COFF::NameSize
], StringRef Src
) {
723 assert(Src
.size() <= COFF::NameSize
&&
724 "Src is not larger than COFF::NameSize");
725 strncpy(Dest
, Src
.data(), (size_t)COFF::NameSize
);
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 (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 Reloc
->Type
= COFF::IMAGE_REL_ARM64_ADDR32NB
;
995 llvm_unreachable("unknown machine type");
997 CurrentOffset
+= sizeof(coff_relocation
);
1001 Expected
<std::unique_ptr
<MemoryBuffer
>>
1002 writeWindowsResourceCOFF(COFF::MachineTypes MachineType
,
1003 const WindowsResourceParser
&Parser
,
1004 uint32_t TimeDateStamp
) {
1005 Error E
= Error::success();
1006 WindowsResourceCOFFWriter
Writer(MachineType
, Parser
, E
);
1008 return std::move(E
);
1009 return Writer
.write(TimeDateStamp
);
1012 } // namespace object