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"
20 #include <system_error>
23 using namespace object
;
28 #define RETURN_IF_ERROR(X) \
32 const uint32_t MIN_HEADER_SIZE
= 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t);
34 // COFF files seem to be inconsistent with alignment between sections, just use
35 // 8-byte because it makes everyone happy.
36 const uint32_t SECTION_ALIGNMENT
= sizeof(uint64_t);
38 uint32_t WindowsResourceParser::TreeNode::StringCount
= 0;
39 uint32_t WindowsResourceParser::TreeNode::DataCount
= 0;
41 WindowsResource::WindowsResource(MemoryBufferRef Source
)
42 : Binary(Binary::ID_WinRes
, Source
) {
43 size_t LeadingSize
= WIN_RES_MAGIC_SIZE
+ WIN_RES_NULL_ENTRY_SIZE
;
44 BBS
= BinaryByteStream(Data
.getBuffer().drop_front(LeadingSize
),
48 Expected
<std::unique_ptr
<WindowsResource
>>
49 WindowsResource::createWindowsResource(MemoryBufferRef Source
) {
50 if (Source
.getBufferSize() < WIN_RES_MAGIC_SIZE
+ WIN_RES_NULL_ENTRY_SIZE
)
51 return make_error
<GenericBinaryError
>(
52 "File too small to be a resource file",
53 object_error::invalid_file_type
);
54 std::unique_ptr
<WindowsResource
> Ret(new WindowsResource(Source
));
55 return std::move(Ret
);
58 Expected
<ResourceEntryRef
> WindowsResource::getHeadEntry() {
59 if (BBS
.getLength() < sizeof(WinResHeaderPrefix
) + sizeof(WinResHeaderSuffix
))
60 return make_error
<EmptyResError
>(".res contains no entries",
61 object_error::unexpected_eof
);
62 return ResourceEntryRef::create(BinaryStreamRef(BBS
), this);
65 ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref
,
66 const WindowsResource
*Owner
)
69 Expected
<ResourceEntryRef
>
70 ResourceEntryRef::create(BinaryStreamRef BSR
, const WindowsResource
*Owner
) {
71 auto Ref
= ResourceEntryRef(BSR
, Owner
);
72 if (auto E
= Ref
.loadNext())
77 Error
ResourceEntryRef::moveNext(bool &End
) {
78 // Reached end of all the entries.
79 if (Reader
.bytesRemaining() == 0) {
81 return Error::success();
83 RETURN_IF_ERROR(loadNext());
85 return Error::success();
88 static Error
readStringOrId(BinaryStreamReader
&Reader
, uint16_t &ID
,
89 ArrayRef
<UTF16
> &Str
, bool &IsString
) {
91 RETURN_IF_ERROR(Reader
.readInteger(IDFlag
));
92 IsString
= IDFlag
!= 0xffff;
97 sizeof(uint16_t)); // Re-read the bytes which we used to check the flag.
98 RETURN_IF_ERROR(Reader
.readWideString(Str
));
100 RETURN_IF_ERROR(Reader
.readInteger(ID
));
102 return Error::success();
105 Error
ResourceEntryRef::loadNext() {
106 const WinResHeaderPrefix
*Prefix
;
107 RETURN_IF_ERROR(Reader
.readObject(Prefix
));
109 if (Prefix
->HeaderSize
< MIN_HEADER_SIZE
)
110 return make_error
<GenericBinaryError
>("Header size is too small.",
111 object_error::parse_failed
);
113 RETURN_IF_ERROR(readStringOrId(Reader
, TypeID
, Type
, IsStringType
));
115 RETURN_IF_ERROR(readStringOrId(Reader
, NameID
, Name
, IsStringName
));
117 RETURN_IF_ERROR(Reader
.padToAlignment(WIN_RES_HEADER_ALIGNMENT
));
119 RETURN_IF_ERROR(Reader
.readObject(Suffix
));
121 RETURN_IF_ERROR(Reader
.readArray(Data
, Prefix
->DataSize
));
123 RETURN_IF_ERROR(Reader
.padToAlignment(WIN_RES_DATA_ALIGNMENT
));
125 return Error::success();
128 WindowsResourceParser::WindowsResourceParser() : Root(false) {}
130 Error
WindowsResourceParser::parse(WindowsResource
*WR
) {
131 auto EntryOrErr
= WR
->getHeadEntry();
133 auto E
= EntryOrErr
.takeError();
134 if (E
.isA
<EmptyResError
>()) {
135 // Check if the .res file contains no entries. In this case we don't have
136 // to throw an error but can rather just return without parsing anything.
137 // This applies for files which have a valid PE header magic and the
138 // mandatory empty null resource entry. Files which do not fit this
139 // criteria would have already been filtered out by
140 // WindowsResource::createWindowsResource().
141 consumeError(std::move(E
));
142 return Error::success();
147 ResourceEntryRef Entry
= EntryOrErr
.get();
150 Data
.push_back(Entry
.getData());
152 bool IsNewTypeString
= false;
153 bool IsNewNameString
= false;
155 Root
.addEntry(Entry
, IsNewTypeString
, IsNewNameString
);
158 StringTable
.push_back(Entry
.getTypeString());
161 StringTable
.push_back(Entry
.getNameString());
163 RETURN_IF_ERROR(Entry
.moveNext(End
));
166 return Error::success();
169 void WindowsResourceParser::printTree(raw_ostream
&OS
) const {
170 ScopedPrinter
Writer(OS
);
171 Root
.print(Writer
, "Resource Tree");
174 void WindowsResourceParser::TreeNode::addEntry(const ResourceEntryRef
&Entry
,
175 bool &IsNewTypeString
,
176 bool &IsNewNameString
) {
177 TreeNode
&TypeNode
= addTypeNode(Entry
, IsNewTypeString
);
178 TreeNode
&NameNode
= TypeNode
.addNameNode(Entry
, IsNewNameString
);
179 NameNode
.addLanguageNode(Entry
);
182 WindowsResourceParser::TreeNode::TreeNode(bool IsStringNode
) {
184 StringIndex
= StringCount
++;
187 WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion
,
188 uint16_t MinorVersion
,
189 uint32_t Characteristics
)
190 : IsDataNode(true), MajorVersion(MajorVersion
), MinorVersion(MinorVersion
),
191 Characteristics(Characteristics
) {
192 DataIndex
= DataCount
++;
195 std::unique_ptr
<WindowsResourceParser::TreeNode
>
196 WindowsResourceParser::TreeNode::createStringNode() {
197 return std::unique_ptr
<TreeNode
>(new TreeNode(true));
200 std::unique_ptr
<WindowsResourceParser::TreeNode
>
201 WindowsResourceParser::TreeNode::createIDNode() {
202 return std::unique_ptr
<TreeNode
>(new TreeNode(false));
205 std::unique_ptr
<WindowsResourceParser::TreeNode
>
206 WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion
,
207 uint16_t MinorVersion
,
208 uint32_t Characteristics
) {
209 return std::unique_ptr
<TreeNode
>(
210 new TreeNode(MajorVersion
, MinorVersion
, Characteristics
));
213 WindowsResourceParser::TreeNode
&
214 WindowsResourceParser::TreeNode::addTypeNode(const ResourceEntryRef
&Entry
,
215 bool &IsNewTypeString
) {
216 if (Entry
.checkTypeString())
217 return addChild(Entry
.getTypeString(), IsNewTypeString
);
219 return addChild(Entry
.getTypeID());
222 WindowsResourceParser::TreeNode
&
223 WindowsResourceParser::TreeNode::addNameNode(const ResourceEntryRef
&Entry
,
224 bool &IsNewNameString
) {
225 if (Entry
.checkNameString())
226 return addChild(Entry
.getNameString(), IsNewNameString
);
228 return addChild(Entry
.getNameID());
231 WindowsResourceParser::TreeNode
&
232 WindowsResourceParser::TreeNode::addLanguageNode(
233 const ResourceEntryRef
&Entry
) {
234 return addChild(Entry
.getLanguage(), true, Entry
.getMajorVersion(),
235 Entry
.getMinorVersion(), Entry
.getCharacteristics());
238 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addChild(
239 uint32_t ID
, bool IsDataNode
, uint16_t MajorVersion
, uint16_t MinorVersion
,
240 uint32_t Characteristics
) {
241 auto Child
= IDChildren
.find(ID
);
242 if (Child
== IDChildren
.end()) {
244 IsDataNode
? createDataNode(MajorVersion
, MinorVersion
, Characteristics
)
246 WindowsResourceParser::TreeNode
&Node
= *NewChild
;
247 IDChildren
.emplace(ID
, std::move(NewChild
));
250 return *(Child
->second
);
253 WindowsResourceParser::TreeNode
&
254 WindowsResourceParser::TreeNode::addChild(ArrayRef
<UTF16
> NameRef
,
256 std::string NameString
;
257 ArrayRef
<UTF16
> CorrectedName
;
258 std::vector
<UTF16
> EndianCorrectedName
;
259 if (sys::IsBigEndianHost
) {
260 EndianCorrectedName
.resize(NameRef
.size() + 1);
261 llvm::copy(NameRef
, EndianCorrectedName
.begin() + 1);
262 EndianCorrectedName
[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED
;
263 CorrectedName
= makeArrayRef(EndianCorrectedName
);
265 CorrectedName
= NameRef
;
266 convertUTF16ToUTF8String(CorrectedName
, NameString
);
268 auto Child
= StringChildren
.find(NameString
);
269 if (Child
== StringChildren
.end()) {
270 auto NewChild
= createStringNode();
272 WindowsResourceParser::TreeNode
&Node
= *NewChild
;
273 StringChildren
.emplace(NameString
, std::move(NewChild
));
276 return *(Child
->second
);
279 void WindowsResourceParser::TreeNode::print(ScopedPrinter
&Writer
,
280 StringRef Name
) const {
281 ListScope
NodeScope(Writer
, Name
);
282 for (auto const &Child
: StringChildren
) {
283 Child
.second
->print(Writer
, Child
.first
);
285 for (auto const &Child
: IDChildren
) {
286 Child
.second
->print(Writer
, to_string(Child
.first
));
290 // This function returns the size of the entire resource tree, including
291 // directory tables, directory entries, and data entries. It does not include
292 // the directory strings or the relocations of the .rsrc section.
293 uint32_t WindowsResourceParser::TreeNode::getTreeSize() const {
294 uint32_t Size
= (IDChildren
.size() + StringChildren
.size()) *
295 sizeof(coff_resource_dir_entry
);
297 // Reached a node pointing to a data entry.
299 Size
+= sizeof(coff_resource_data_entry
);
303 // If the node does not point to data, it must have a directory table pointing
305 Size
+= sizeof(coff_resource_dir_table
);
307 for (auto const &Child
: StringChildren
) {
308 Size
+= Child
.second
->getTreeSize();
310 for (auto const &Child
: IDChildren
) {
311 Size
+= Child
.second
->getTreeSize();
316 class WindowsResourceCOFFWriter
{
318 WindowsResourceCOFFWriter(COFF::MachineTypes MachineType
,
319 const WindowsResourceParser
&Parser
, Error
&E
);
320 std::unique_ptr
<MemoryBuffer
> write();
323 void performFileLayout();
324 void performSectionOneLayout();
325 void performSectionTwoLayout();
326 void writeCOFFHeader();
327 void writeFirstSectionHeader();
328 void writeSecondSectionHeader();
329 void writeFirstSection();
330 void writeSecondSection();
331 void writeSymbolTable();
332 void writeStringTable();
333 void writeDirectoryTree();
334 void writeDirectoryStringTable();
335 void writeFirstSectionRelocations();
336 std::unique_ptr
<WritableMemoryBuffer
> OutputBuffer
;
338 uint64_t CurrentOffset
= 0;
339 COFF::MachineTypes MachineType
;
340 const WindowsResourceParser::TreeNode
&Resources
;
341 const ArrayRef
<std::vector
<uint8_t>> Data
;
343 uint32_t SymbolTableOffset
;
344 uint32_t SectionOneSize
;
345 uint32_t SectionOneOffset
;
346 uint32_t SectionOneRelocations
;
347 uint32_t SectionTwoSize
;
348 uint32_t SectionTwoOffset
;
349 const ArrayRef
<std::vector
<UTF16
>> StringTable
;
350 std::vector
<uint32_t> StringTableOffsets
;
351 std::vector
<uint32_t> DataOffsets
;
352 std::vector
<uint32_t> RelocationAddresses
;
355 WindowsResourceCOFFWriter::WindowsResourceCOFFWriter(
356 COFF::MachineTypes MachineType
, const WindowsResourceParser
&Parser
,
358 : MachineType(MachineType
), Resources(Parser
.getTree()),
359 Data(Parser
.getData()), StringTable(Parser
.getStringTable()) {
362 OutputBuffer
= WritableMemoryBuffer::getNewMemBuffer(FileSize
);
365 void WindowsResourceCOFFWriter::performFileLayout() {
366 // Add size of COFF header.
367 FileSize
= COFF::Header16Size
;
369 // one .rsrc section header for directory tree, another for resource data.
370 FileSize
+= 2 * COFF::SectionSize
;
372 performSectionOneLayout();
373 performSectionTwoLayout();
375 // We have reached the address of the symbol table.
376 SymbolTableOffset
= FileSize
;
378 FileSize
+= COFF::Symbol16Size
; // size of the @feat.00 symbol.
379 FileSize
+= 4 * COFF::Symbol16Size
; // symbol + aux for each section.
380 FileSize
+= Data
.size() * COFF::Symbol16Size
; // 1 symbol per resource.
381 FileSize
+= 4; // four null bytes for the string table.
384 void WindowsResourceCOFFWriter::performSectionOneLayout() {
385 SectionOneOffset
= FileSize
;
387 SectionOneSize
= Resources
.getTreeSize();
388 uint32_t CurrentStringOffset
= SectionOneSize
;
389 uint32_t TotalStringTableSize
= 0;
390 for (auto const &String
: StringTable
) {
391 StringTableOffsets
.push_back(CurrentStringOffset
);
392 uint32_t StringSize
= String
.size() * sizeof(UTF16
) + sizeof(uint16_t);
393 CurrentStringOffset
+= StringSize
;
394 TotalStringTableSize
+= StringSize
;
396 SectionOneSize
+= alignTo(TotalStringTableSize
, sizeof(uint32_t));
398 // account for the relocations of section one.
399 SectionOneRelocations
= FileSize
+ SectionOneSize
;
400 FileSize
+= SectionOneSize
;
402 Data
.size() * COFF::RelocationSize
; // one relocation for each resource.
403 FileSize
= alignTo(FileSize
, SECTION_ALIGNMENT
);
406 void WindowsResourceCOFFWriter::performSectionTwoLayout() {
407 // add size of .rsrc$2 section, which contains all resource data on 8-byte
409 SectionTwoOffset
= FileSize
;
411 for (auto const &Entry
: Data
) {
412 DataOffsets
.push_back(SectionTwoSize
);
413 SectionTwoSize
+= alignTo(Entry
.size(), sizeof(uint64_t));
415 FileSize
+= SectionTwoSize
;
416 FileSize
= alignTo(FileSize
, SECTION_ALIGNMENT
);
419 static std::time_t getTime() {
420 std::time_t Now
= time(nullptr);
421 if (Now
< 0 || !isUInt
<32>(Now
))
426 std::unique_ptr
<MemoryBuffer
> WindowsResourceCOFFWriter::write() {
427 BufferStart
= OutputBuffer
->getBufferStart();
430 writeFirstSectionHeader();
431 writeSecondSectionHeader();
433 writeSecondSection();
437 return std::move(OutputBuffer
);
440 void WindowsResourceCOFFWriter::writeCOFFHeader() {
441 // Write the COFF header.
442 auto *Header
= reinterpret_cast<coff_file_header
*>(BufferStart
);
443 Header
->Machine
= MachineType
;
444 Header
->NumberOfSections
= 2;
445 Header
->TimeDateStamp
= getTime();
446 Header
->PointerToSymbolTable
= SymbolTableOffset
;
447 // One symbol for every resource plus 2 for each section and @feat.00
448 Header
->NumberOfSymbols
= Data
.size() + 5;
449 Header
->SizeOfOptionalHeader
= 0;
450 Header
->Characteristics
= COFF::IMAGE_FILE_32BIT_MACHINE
;
453 void WindowsResourceCOFFWriter::writeFirstSectionHeader() {
454 // Write the first section header.
455 CurrentOffset
+= sizeof(coff_file_header
);
456 auto *SectionOneHeader
=
457 reinterpret_cast<coff_section
*>(BufferStart
+ CurrentOffset
);
458 strncpy(SectionOneHeader
->Name
, ".rsrc$01", (size_t)COFF::NameSize
);
459 SectionOneHeader
->VirtualSize
= 0;
460 SectionOneHeader
->VirtualAddress
= 0;
461 SectionOneHeader
->SizeOfRawData
= SectionOneSize
;
462 SectionOneHeader
->PointerToRawData
= SectionOneOffset
;
463 SectionOneHeader
->PointerToRelocations
= SectionOneRelocations
;
464 SectionOneHeader
->PointerToLinenumbers
= 0;
465 SectionOneHeader
->NumberOfRelocations
= Data
.size();
466 SectionOneHeader
->NumberOfLinenumbers
= 0;
467 SectionOneHeader
->Characteristics
+= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
;
468 SectionOneHeader
->Characteristics
+= COFF::IMAGE_SCN_MEM_READ
;
471 void WindowsResourceCOFFWriter::writeSecondSectionHeader() {
472 // Write the second section header.
473 CurrentOffset
+= sizeof(coff_section
);
474 auto *SectionTwoHeader
=
475 reinterpret_cast<coff_section
*>(BufferStart
+ CurrentOffset
);
476 strncpy(SectionTwoHeader
->Name
, ".rsrc$02", (size_t)COFF::NameSize
);
477 SectionTwoHeader
->VirtualSize
= 0;
478 SectionTwoHeader
->VirtualAddress
= 0;
479 SectionTwoHeader
->SizeOfRawData
= SectionTwoSize
;
480 SectionTwoHeader
->PointerToRawData
= SectionTwoOffset
;
481 SectionTwoHeader
->PointerToRelocations
= 0;
482 SectionTwoHeader
->PointerToLinenumbers
= 0;
483 SectionTwoHeader
->NumberOfRelocations
= 0;
484 SectionTwoHeader
->NumberOfLinenumbers
= 0;
485 SectionTwoHeader
->Characteristics
= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
;
486 SectionTwoHeader
->Characteristics
+= COFF::IMAGE_SCN_MEM_READ
;
489 void WindowsResourceCOFFWriter::writeFirstSection() {
490 // Write section one.
491 CurrentOffset
+= sizeof(coff_section
);
493 writeDirectoryTree();
494 writeDirectoryStringTable();
495 writeFirstSectionRelocations();
497 CurrentOffset
= alignTo(CurrentOffset
, SECTION_ALIGNMENT
);
500 void WindowsResourceCOFFWriter::writeSecondSection() {
501 // Now write the .rsrc$02 section.
502 for (auto const &RawDataEntry
: Data
) {
503 llvm::copy(RawDataEntry
, BufferStart
+ CurrentOffset
);
504 CurrentOffset
+= alignTo(RawDataEntry
.size(), sizeof(uint64_t));
507 CurrentOffset
= alignTo(CurrentOffset
, SECTION_ALIGNMENT
);
510 void WindowsResourceCOFFWriter::writeSymbolTable() {
511 // Now write the symbol table.
512 // First, the feat symbol.
513 auto *Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
514 strncpy(Symbol
->Name
.ShortName
, "@feat.00", (size_t)COFF::NameSize
);
515 Symbol
->Value
= 0x11;
516 Symbol
->SectionNumber
= 0xffff;
517 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
518 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
519 Symbol
->NumberOfAuxSymbols
= 0;
520 CurrentOffset
+= sizeof(coff_symbol16
);
522 // Now write the .rsrc1 symbol + aux.
523 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
524 strncpy(Symbol
->Name
.ShortName
, ".rsrc$01", (size_t)COFF::NameSize
);
526 Symbol
->SectionNumber
= 1;
527 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
528 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
529 Symbol
->NumberOfAuxSymbols
= 1;
530 CurrentOffset
+= sizeof(coff_symbol16
);
531 auto *Aux
= reinterpret_cast<coff_aux_section_definition
*>(BufferStart
+
533 Aux
->Length
= SectionOneSize
;
534 Aux
->NumberOfRelocations
= Data
.size();
535 Aux
->NumberOfLinenumbers
= 0;
537 Aux
->NumberLowPart
= 0;
539 CurrentOffset
+= sizeof(coff_aux_section_definition
);
541 // Now write the .rsrc2 symbol + aux.
542 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
543 strncpy(Symbol
->Name
.ShortName
, ".rsrc$02", (size_t)COFF::NameSize
);
545 Symbol
->SectionNumber
= 2;
546 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
547 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
548 Symbol
->NumberOfAuxSymbols
= 1;
549 CurrentOffset
+= sizeof(coff_symbol16
);
550 Aux
= reinterpret_cast<coff_aux_section_definition
*>(BufferStart
+
552 Aux
->Length
= SectionTwoSize
;
553 Aux
->NumberOfRelocations
= 0;
554 Aux
->NumberOfLinenumbers
= 0;
556 Aux
->NumberLowPart
= 0;
558 CurrentOffset
+= sizeof(coff_aux_section_definition
);
560 // Now write a symbol for each relocation.
561 for (unsigned i
= 0; i
< Data
.size(); i
++) {
562 auto RelocationName
= formatv("$R{0:X-6}", i
& 0xffffff).sstr
<COFF::NameSize
>();
563 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
564 memcpy(Symbol
->Name
.ShortName
, RelocationName
.data(), (size_t) COFF::NameSize
);
565 Symbol
->Value
= DataOffsets
[i
];
566 Symbol
->SectionNumber
= 2;
567 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
568 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
569 Symbol
->NumberOfAuxSymbols
= 0;
570 CurrentOffset
+= sizeof(coff_symbol16
);
574 void WindowsResourceCOFFWriter::writeStringTable() {
575 // Just 4 null bytes for the string table.
576 auto COFFStringTable
= reinterpret_cast<void *>(BufferStart
+ CurrentOffset
);
577 memset(COFFStringTable
, 0, 4);
580 void WindowsResourceCOFFWriter::writeDirectoryTree() {
581 // Traverse parsed resource tree breadth-first and write the corresponding
583 std::queue
<const WindowsResourceParser::TreeNode
*> Queue
;
584 Queue
.push(&Resources
);
585 uint32_t NextLevelOffset
=
586 sizeof(coff_resource_dir_table
) + (Resources
.getStringChildren().size() +
587 Resources
.getIDChildren().size()) *
588 sizeof(coff_resource_dir_entry
);
589 std::vector
<const WindowsResourceParser::TreeNode
*> DataEntriesTreeOrder
;
590 uint32_t CurrentRelativeOffset
= 0;
592 while (!Queue
.empty()) {
593 auto CurrentNode
= Queue
.front();
595 auto *Table
= reinterpret_cast<coff_resource_dir_table
*>(BufferStart
+
597 Table
->Characteristics
= CurrentNode
->getCharacteristics();
598 Table
->TimeDateStamp
= 0;
599 Table
->MajorVersion
= CurrentNode
->getMajorVersion();
600 Table
->MinorVersion
= CurrentNode
->getMinorVersion();
601 auto &IDChildren
= CurrentNode
->getIDChildren();
602 auto &StringChildren
= CurrentNode
->getStringChildren();
603 Table
->NumberOfNameEntries
= StringChildren
.size();
604 Table
->NumberOfIDEntries
= IDChildren
.size();
605 CurrentOffset
+= sizeof(coff_resource_dir_table
);
606 CurrentRelativeOffset
+= sizeof(coff_resource_dir_table
);
608 // Write the directory entries immediately following each directory table.
609 for (auto const &Child
: StringChildren
) {
610 auto *Entry
= reinterpret_cast<coff_resource_dir_entry
*>(BufferStart
+
612 Entry
->Identifier
.setNameOffset(
613 StringTableOffsets
[Child
.second
->getStringIndex()]);
614 if (Child
.second
->checkIsDataNode()) {
615 Entry
->Offset
.DataEntryOffset
= NextLevelOffset
;
616 NextLevelOffset
+= sizeof(coff_resource_data_entry
);
617 DataEntriesTreeOrder
.push_back(Child
.second
.get());
619 Entry
->Offset
.SubdirOffset
= NextLevelOffset
+ (1 << 31);
620 NextLevelOffset
+= sizeof(coff_resource_dir_table
) +
621 (Child
.second
->getStringChildren().size() +
622 Child
.second
->getIDChildren().size()) *
623 sizeof(coff_resource_dir_entry
);
624 Queue
.push(Child
.second
.get());
626 CurrentOffset
+= sizeof(coff_resource_dir_entry
);
627 CurrentRelativeOffset
+= sizeof(coff_resource_dir_entry
);
629 for (auto const &Child
: IDChildren
) {
630 auto *Entry
= reinterpret_cast<coff_resource_dir_entry
*>(BufferStart
+
632 Entry
->Identifier
.ID
= Child
.first
;
633 if (Child
.second
->checkIsDataNode()) {
634 Entry
->Offset
.DataEntryOffset
= NextLevelOffset
;
635 NextLevelOffset
+= sizeof(coff_resource_data_entry
);
636 DataEntriesTreeOrder
.push_back(Child
.second
.get());
638 Entry
->Offset
.SubdirOffset
= NextLevelOffset
+ (1 << 31);
639 NextLevelOffset
+= sizeof(coff_resource_dir_table
) +
640 (Child
.second
->getStringChildren().size() +
641 Child
.second
->getIDChildren().size()) *
642 sizeof(coff_resource_dir_entry
);
643 Queue
.push(Child
.second
.get());
645 CurrentOffset
+= sizeof(coff_resource_dir_entry
);
646 CurrentRelativeOffset
+= sizeof(coff_resource_dir_entry
);
650 RelocationAddresses
.resize(Data
.size());
651 // Now write all the resource data entries.
652 for (auto DataNodes
: DataEntriesTreeOrder
) {
653 auto *Entry
= reinterpret_cast<coff_resource_data_entry
*>(BufferStart
+
655 RelocationAddresses
[DataNodes
->getDataIndex()] = CurrentRelativeOffset
;
656 Entry
->DataRVA
= 0; // Set to zero because it is a relocation.
657 Entry
->DataSize
= Data
[DataNodes
->getDataIndex()].size();
660 CurrentOffset
+= sizeof(coff_resource_data_entry
);
661 CurrentRelativeOffset
+= sizeof(coff_resource_data_entry
);
665 void WindowsResourceCOFFWriter::writeDirectoryStringTable() {
666 // Now write the directory string table for .rsrc$01
667 uint32_t TotalStringTableSize
= 0;
668 for (auto &String
: StringTable
) {
669 uint16_t Length
= String
.size();
670 support::endian::write16le(BufferStart
+ CurrentOffset
, Length
);
671 CurrentOffset
+= sizeof(uint16_t);
672 auto *Start
= reinterpret_cast<UTF16
*>(BufferStart
+ CurrentOffset
);
673 llvm::copy(String
, Start
);
674 CurrentOffset
+= Length
* sizeof(UTF16
);
675 TotalStringTableSize
+= Length
* sizeof(UTF16
) + sizeof(uint16_t);
678 alignTo(TotalStringTableSize
, sizeof(uint32_t)) - TotalStringTableSize
;
681 void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {
683 // Now write the relocations for .rsrc$01
684 // Five symbols already in table before we start, @feat.00 and 2 for each
686 uint32_t NextSymbolIndex
= 5;
687 for (unsigned i
= 0; i
< Data
.size(); i
++) {
689 reinterpret_cast<coff_relocation
*>(BufferStart
+ CurrentOffset
);
690 Reloc
->VirtualAddress
= RelocationAddresses
[i
];
691 Reloc
->SymbolTableIndex
= NextSymbolIndex
++;
692 switch (MachineType
) {
693 case COFF::IMAGE_FILE_MACHINE_ARMNT
:
694 Reloc
->Type
= COFF::IMAGE_REL_ARM_ADDR32NB
;
696 case COFF::IMAGE_FILE_MACHINE_AMD64
:
697 Reloc
->Type
= COFF::IMAGE_REL_AMD64_ADDR32NB
;
699 case COFF::IMAGE_FILE_MACHINE_I386
:
700 Reloc
->Type
= COFF::IMAGE_REL_I386_DIR32NB
;
702 case COFF::IMAGE_FILE_MACHINE_ARM64
:
703 Reloc
->Type
= COFF::IMAGE_REL_ARM64_ADDR32NB
;
706 llvm_unreachable("unknown machine type");
708 CurrentOffset
+= sizeof(coff_relocation
);
712 Expected
<std::unique_ptr
<MemoryBuffer
>>
713 writeWindowsResourceCOFF(COFF::MachineTypes MachineType
,
714 const WindowsResourceParser
&Parser
) {
715 Error E
= Error::success();
716 WindowsResourceCOFFWriter
Writer(MachineType
, Parser
, E
);
719 return Writer
.write();
722 } // namespace object