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 const uint32_t MIN_HEADER_SIZE
= 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t);
35 // COFF files seem to be inconsistent with alignment between sections, just use
36 // 8-byte because it makes everyone happy.
37 const uint32_t SECTION_ALIGNMENT
= sizeof(uint64_t);
39 uint32_t WindowsResourceParser::TreeNode::StringCount
= 0;
40 uint32_t WindowsResourceParser::TreeNode::DataCount
= 0;
42 WindowsResource::WindowsResource(MemoryBufferRef Source
)
43 : Binary(Binary::ID_WinRes
, Source
) {
44 size_t LeadingSize
= WIN_RES_MAGIC_SIZE
+ WIN_RES_NULL_ENTRY_SIZE
;
45 BBS
= BinaryByteStream(Data
.getBuffer().drop_front(LeadingSize
),
50 Expected
<std::unique_ptr
<WindowsResource
>>
51 WindowsResource::createWindowsResource(MemoryBufferRef Source
) {
52 if (Source
.getBufferSize() < WIN_RES_MAGIC_SIZE
+ WIN_RES_NULL_ENTRY_SIZE
)
53 return make_error
<GenericBinaryError
>(
54 Source
.getBufferIdentifier() + ": too small to be a resource file",
55 object_error::invalid_file_type
);
56 std::unique_ptr
<WindowsResource
> Ret(new WindowsResource(Source
));
57 return std::move(Ret
);
60 Expected
<ResourceEntryRef
> WindowsResource::getHeadEntry() {
61 if (BBS
.getLength() < sizeof(WinResHeaderPrefix
) + sizeof(WinResHeaderSuffix
))
62 return make_error
<EmptyResError
>(getFileName() + " contains no entries",
63 object_error::unexpected_eof
);
64 return ResourceEntryRef::create(BinaryStreamRef(BBS
), this);
67 ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref
,
68 const WindowsResource
*Owner
)
69 : Reader(Ref
), Owner(Owner
) {}
71 Expected
<ResourceEntryRef
>
72 ResourceEntryRef::create(BinaryStreamRef BSR
, const WindowsResource
*Owner
) {
73 auto Ref
= ResourceEntryRef(BSR
, Owner
);
74 if (auto E
= Ref
.loadNext())
79 Error
ResourceEntryRef::moveNext(bool &End
) {
80 // Reached end of all the entries.
81 if (Reader
.bytesRemaining() == 0) {
83 return Error::success();
85 RETURN_IF_ERROR(loadNext());
87 return Error::success();
90 static Error
readStringOrId(BinaryStreamReader
&Reader
, uint16_t &ID
,
91 ArrayRef
<UTF16
> &Str
, bool &IsString
) {
93 RETURN_IF_ERROR(Reader
.readInteger(IDFlag
));
94 IsString
= IDFlag
!= 0xffff;
99 sizeof(uint16_t)); // Re-read the bytes which we used to check the flag.
100 RETURN_IF_ERROR(Reader
.readWideString(Str
));
102 RETURN_IF_ERROR(Reader
.readInteger(ID
));
104 return Error::success();
107 Error
ResourceEntryRef::loadNext() {
108 const WinResHeaderPrefix
*Prefix
;
109 RETURN_IF_ERROR(Reader
.readObject(Prefix
));
111 if (Prefix
->HeaderSize
< MIN_HEADER_SIZE
)
112 return make_error
<GenericBinaryError
>(Owner
->getFileName() +
113 ": header size too small",
114 object_error::parse_failed
);
116 RETURN_IF_ERROR(readStringOrId(Reader
, TypeID
, Type
, IsStringType
));
118 RETURN_IF_ERROR(readStringOrId(Reader
, NameID
, Name
, IsStringName
));
120 RETURN_IF_ERROR(Reader
.padToAlignment(WIN_RES_HEADER_ALIGNMENT
));
122 RETURN_IF_ERROR(Reader
.readObject(Suffix
));
124 RETURN_IF_ERROR(Reader
.readArray(Data
, Prefix
->DataSize
));
126 RETURN_IF_ERROR(Reader
.padToAlignment(WIN_RES_DATA_ALIGNMENT
));
128 return Error::success();
131 WindowsResourceParser::WindowsResourceParser() : Root(false) {}
133 void printResourceTypeName(uint16_t TypeID
, raw_ostream
&OS
) {
135 case 1: OS
<< "CURSOR (ID 1)"; break;
136 case 2: OS
<< "BITMAP (ID 2)"; break;
137 case 3: OS
<< "ICON (ID 3)"; break;
138 case 4: OS
<< "MENU (ID 4)"; break;
139 case 5: OS
<< "DIALOG (ID 5)"; break;
140 case 6: OS
<< "STRINGTABLE (ID 6)"; break;
141 case 7: OS
<< "FONTDIR (ID 7)"; break;
142 case 8: OS
<< "FONT (ID 8)"; break;
143 case 9: OS
<< "ACCELERATOR (ID 9)"; break;
144 case 10: OS
<< "RCDATA (ID 10)"; break;
145 case 11: OS
<< "MESSAGETABLE (ID 11)"; break;
146 case 12: OS
<< "GROUP_CURSOR (ID 12)"; break;
147 case 14: OS
<< "GROUP_ICON (ID 14)"; break;
148 case 16: OS
<< "VERSIONINFO (ID 16)"; break;
149 case 17: OS
<< "DLGINCLUDE (ID 17)"; break;
150 case 19: OS
<< "PLUGPLAY (ID 19)"; break;
151 case 20: OS
<< "VXD (ID 20)"; break;
152 case 21: OS
<< "ANICURSOR (ID 21)"; break;
153 case 22: OS
<< "ANIICON (ID 22)"; break;
154 case 23: OS
<< "HTML (ID 23)"; break;
155 case 24: OS
<< "MANIFEST (ID 24)"; break;
156 default: OS
<< "ID " << TypeID
; break;
160 static bool convertUTF16LEToUTF8String(ArrayRef
<UTF16
> Src
, std::string
&Out
) {
161 if (!sys::IsBigEndianHost
)
162 return convertUTF16ToUTF8String(Src
, Out
);
164 std::vector
<UTF16
> EndianCorrectedSrc
;
165 EndianCorrectedSrc
.resize(Src
.size() + 1);
166 llvm::copy(Src
, EndianCorrectedSrc
.begin() + 1);
167 EndianCorrectedSrc
[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED
;
168 return convertUTF16ToUTF8String(makeArrayRef(EndianCorrectedSrc
), Out
);
171 static std::string
makeDuplicateResourceError(
172 const ResourceEntryRef
&Entry
, StringRef File1
, StringRef File2
) {
174 raw_string_ostream
OS(Ret
);
176 OS
<< "duplicate resource:";
179 if (Entry
.checkTypeString()) {
181 if (!convertUTF16LEToUTF8String(Entry
.getTypeString(), UTF8
))
182 UTF8
= "(failed conversion from UTF16)";
183 OS
<< '\"' << UTF8
<< '\"';
185 printResourceTypeName(Entry
.getTypeID(), OS
);
188 if (Entry
.checkNameString()) {
190 if (!convertUTF16LEToUTF8String(Entry
.getNameString(), UTF8
))
191 UTF8
= "(failed conversion from UTF16)";
192 OS
<< '\"' << UTF8
<< '\"';
194 OS
<< "ID " << Entry
.getNameID();
197 OS
<< "/language " << Entry
.getLanguage() << ", in " << File1
<< " and in "
203 Error
WindowsResourceParser::parse(WindowsResource
*WR
,
204 std::vector
<std::string
> &Duplicates
) {
205 auto EntryOrErr
= WR
->getHeadEntry();
207 auto E
= EntryOrErr
.takeError();
208 if (E
.isA
<EmptyResError
>()) {
209 // Check if the .res file contains no entries. In this case we don't have
210 // to throw an error but can rather just return without parsing anything.
211 // This applies for files which have a valid PE header magic and the
212 // mandatory empty null resource entry. Files which do not fit this
213 // criteria would have already been filtered out by
214 // WindowsResource::createWindowsResource().
215 consumeError(std::move(E
));
216 return Error::success();
221 ResourceEntryRef Entry
= EntryOrErr
.get();
224 Data
.push_back(Entry
.getData());
226 bool IsNewTypeString
= false;
227 bool IsNewNameString
= false;
230 bool IsNewNode
= Root
.addEntry(Entry
, InputFilenames
.size(),
231 IsNewTypeString
, IsNewNameString
, Node
);
232 InputFilenames
.push_back(WR
->getFileName());
234 Duplicates
.push_back(makeDuplicateResourceError(
235 Entry
, InputFilenames
[Node
->Origin
], WR
->getFileName()));
239 StringTable
.push_back(Entry
.getTypeString());
242 StringTable
.push_back(Entry
.getNameString());
244 RETURN_IF_ERROR(Entry
.moveNext(End
));
247 return Error::success();
250 void WindowsResourceParser::printTree(raw_ostream
&OS
) const {
251 ScopedPrinter
Writer(OS
);
252 Root
.print(Writer
, "Resource Tree");
255 bool WindowsResourceParser::TreeNode::addEntry(const ResourceEntryRef
&Entry
,
257 bool &IsNewTypeString
,
258 bool &IsNewNameString
,
260 TreeNode
&TypeNode
= addTypeNode(Entry
, IsNewTypeString
);
261 TreeNode
&NameNode
= TypeNode
.addNameNode(Entry
, IsNewNameString
);
262 return NameNode
.addLanguageNode(Entry
, Origin
, Result
);
265 WindowsResourceParser::TreeNode::TreeNode(bool IsStringNode
) {
267 StringIndex
= StringCount
++;
270 WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion
,
271 uint16_t MinorVersion
,
272 uint32_t Characteristics
,
274 : IsDataNode(true), MajorVersion(MajorVersion
), MinorVersion(MinorVersion
),
275 Characteristics(Characteristics
), Origin(Origin
) {
276 DataIndex
= DataCount
++;
279 std::unique_ptr
<WindowsResourceParser::TreeNode
>
280 WindowsResourceParser::TreeNode::createStringNode() {
281 return std::unique_ptr
<TreeNode
>(new TreeNode(true));
284 std::unique_ptr
<WindowsResourceParser::TreeNode
>
285 WindowsResourceParser::TreeNode::createIDNode() {
286 return std::unique_ptr
<TreeNode
>(new TreeNode(false));
289 std::unique_ptr
<WindowsResourceParser::TreeNode
>
290 WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion
,
291 uint16_t MinorVersion
,
292 uint32_t Characteristics
,
294 return std::unique_ptr
<TreeNode
>(
295 new TreeNode(MajorVersion
, MinorVersion
, Characteristics
, Origin
));
298 WindowsResourceParser::TreeNode
&
299 WindowsResourceParser::TreeNode::addTypeNode(const ResourceEntryRef
&Entry
,
300 bool &IsNewTypeString
) {
301 if (Entry
.checkTypeString())
302 return addNameChild(Entry
.getTypeString(), IsNewTypeString
);
304 return addIDChild(Entry
.getTypeID());
307 WindowsResourceParser::TreeNode
&
308 WindowsResourceParser::TreeNode::addNameNode(const ResourceEntryRef
&Entry
,
309 bool &IsNewNameString
) {
310 if (Entry
.checkNameString())
311 return addNameChild(Entry
.getNameString(), IsNewNameString
);
313 return addIDChild(Entry
.getNameID());
316 bool WindowsResourceParser::TreeNode::addLanguageNode(
317 const ResourceEntryRef
&Entry
, uint32_t Origin
, TreeNode
*&Result
) {
318 return addDataChild(Entry
.getLanguage(), Entry
.getMajorVersion(),
319 Entry
.getMinorVersion(), Entry
.getCharacteristics(),
323 bool WindowsResourceParser::TreeNode::addDataChild(
324 uint32_t ID
, uint16_t MajorVersion
, uint16_t MinorVersion
,
325 uint32_t Characteristics
, uint32_t Origin
, TreeNode
*&Result
) {
327 createDataNode(MajorVersion
, MinorVersion
, Characteristics
, Origin
);
328 auto ElementInserted
= IDChildren
.emplace(ID
, std::move(NewChild
));
329 Result
= ElementInserted
.first
->second
.get();
330 return ElementInserted
.second
;
333 WindowsResourceParser::TreeNode
&WindowsResourceParser::TreeNode::addIDChild(
335 auto Child
= IDChildren
.find(ID
);
336 if (Child
== IDChildren
.end()) {
337 auto NewChild
= createIDNode();
338 WindowsResourceParser::TreeNode
&Node
= *NewChild
;
339 IDChildren
.emplace(ID
, std::move(NewChild
));
342 return *(Child
->second
);
345 WindowsResourceParser::TreeNode
&
346 WindowsResourceParser::TreeNode::addNameChild(ArrayRef
<UTF16
> NameRef
,
348 std::string NameString
;
349 convertUTF16LEToUTF8String(NameRef
, NameString
);
351 auto Child
= StringChildren
.find(NameString
);
352 if (Child
== StringChildren
.end()) {
353 auto NewChild
= createStringNode();
355 WindowsResourceParser::TreeNode
&Node
= *NewChild
;
356 StringChildren
.emplace(NameString
, std::move(NewChild
));
359 return *(Child
->second
);
362 void WindowsResourceParser::TreeNode::print(ScopedPrinter
&Writer
,
363 StringRef Name
) const {
364 ListScope
NodeScope(Writer
, Name
);
365 for (auto const &Child
: StringChildren
) {
366 Child
.second
->print(Writer
, Child
.first
);
368 for (auto const &Child
: IDChildren
) {
369 Child
.second
->print(Writer
, to_string(Child
.first
));
373 // This function returns the size of the entire resource tree, including
374 // directory tables, directory entries, and data entries. It does not include
375 // the directory strings or the relocations of the .rsrc section.
376 uint32_t WindowsResourceParser::TreeNode::getTreeSize() const {
377 uint32_t Size
= (IDChildren
.size() + StringChildren
.size()) *
378 sizeof(coff_resource_dir_entry
);
380 // Reached a node pointing to a data entry.
382 Size
+= sizeof(coff_resource_data_entry
);
386 // If the node does not point to data, it must have a directory table pointing
388 Size
+= sizeof(coff_resource_dir_table
);
390 for (auto const &Child
: StringChildren
) {
391 Size
+= Child
.second
->getTreeSize();
393 for (auto const &Child
: IDChildren
) {
394 Size
+= Child
.second
->getTreeSize();
399 class WindowsResourceCOFFWriter
{
401 WindowsResourceCOFFWriter(COFF::MachineTypes MachineType
,
402 const WindowsResourceParser
&Parser
, Error
&E
);
403 std::unique_ptr
<MemoryBuffer
> write(uint32_t TimeDateStamp
);
406 void performFileLayout();
407 void performSectionOneLayout();
408 void performSectionTwoLayout();
409 void writeCOFFHeader(uint32_t TimeDateStamp
);
410 void writeFirstSectionHeader();
411 void writeSecondSectionHeader();
412 void writeFirstSection();
413 void writeSecondSection();
414 void writeSymbolTable();
415 void writeStringTable();
416 void writeDirectoryTree();
417 void writeDirectoryStringTable();
418 void writeFirstSectionRelocations();
419 std::unique_ptr
<WritableMemoryBuffer
> OutputBuffer
;
421 uint64_t CurrentOffset
= 0;
422 COFF::MachineTypes MachineType
;
423 const WindowsResourceParser::TreeNode
&Resources
;
424 const ArrayRef
<std::vector
<uint8_t>> Data
;
426 uint32_t SymbolTableOffset
;
427 uint32_t SectionOneSize
;
428 uint32_t SectionOneOffset
;
429 uint32_t SectionOneRelocations
;
430 uint32_t SectionTwoSize
;
431 uint32_t SectionTwoOffset
;
432 const ArrayRef
<std::vector
<UTF16
>> StringTable
;
433 std::vector
<uint32_t> StringTableOffsets
;
434 std::vector
<uint32_t> DataOffsets
;
435 std::vector
<uint32_t> RelocationAddresses
;
438 WindowsResourceCOFFWriter::WindowsResourceCOFFWriter(
439 COFF::MachineTypes MachineType
, const WindowsResourceParser
&Parser
,
441 : MachineType(MachineType
), Resources(Parser
.getTree()),
442 Data(Parser
.getData()), StringTable(Parser
.getStringTable()) {
445 OutputBuffer
= WritableMemoryBuffer::getNewMemBuffer(
446 FileSize
, "internal .obj file created from .res files");
449 void WindowsResourceCOFFWriter::performFileLayout() {
450 // Add size of COFF header.
451 FileSize
= COFF::Header16Size
;
453 // one .rsrc section header for directory tree, another for resource data.
454 FileSize
+= 2 * COFF::SectionSize
;
456 performSectionOneLayout();
457 performSectionTwoLayout();
459 // We have reached the address of the symbol table.
460 SymbolTableOffset
= FileSize
;
462 FileSize
+= COFF::Symbol16Size
; // size of the @feat.00 symbol.
463 FileSize
+= 4 * COFF::Symbol16Size
; // symbol + aux for each section.
464 FileSize
+= Data
.size() * COFF::Symbol16Size
; // 1 symbol per resource.
465 FileSize
+= 4; // four null bytes for the string table.
468 void WindowsResourceCOFFWriter::performSectionOneLayout() {
469 SectionOneOffset
= FileSize
;
471 SectionOneSize
= Resources
.getTreeSize();
472 uint32_t CurrentStringOffset
= SectionOneSize
;
473 uint32_t TotalStringTableSize
= 0;
474 for (auto const &String
: StringTable
) {
475 StringTableOffsets
.push_back(CurrentStringOffset
);
476 uint32_t StringSize
= String
.size() * sizeof(UTF16
) + sizeof(uint16_t);
477 CurrentStringOffset
+= StringSize
;
478 TotalStringTableSize
+= StringSize
;
480 SectionOneSize
+= alignTo(TotalStringTableSize
, sizeof(uint32_t));
482 // account for the relocations of section one.
483 SectionOneRelocations
= FileSize
+ SectionOneSize
;
484 FileSize
+= SectionOneSize
;
486 Data
.size() * COFF::RelocationSize
; // one relocation for each resource.
487 FileSize
= alignTo(FileSize
, SECTION_ALIGNMENT
);
490 void WindowsResourceCOFFWriter::performSectionTwoLayout() {
491 // add size of .rsrc$2 section, which contains all resource data on 8-byte
493 SectionTwoOffset
= FileSize
;
495 for (auto const &Entry
: Data
) {
496 DataOffsets
.push_back(SectionTwoSize
);
497 SectionTwoSize
+= alignTo(Entry
.size(), sizeof(uint64_t));
499 FileSize
+= SectionTwoSize
;
500 FileSize
= alignTo(FileSize
, SECTION_ALIGNMENT
);
503 std::unique_ptr
<MemoryBuffer
>
504 WindowsResourceCOFFWriter::write(uint32_t TimeDateStamp
) {
505 BufferStart
= OutputBuffer
->getBufferStart();
507 writeCOFFHeader(TimeDateStamp
);
508 writeFirstSectionHeader();
509 writeSecondSectionHeader();
511 writeSecondSection();
515 return std::move(OutputBuffer
);
518 void WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp
) {
519 // Write the COFF header.
520 auto *Header
= reinterpret_cast<coff_file_header
*>(BufferStart
);
521 Header
->Machine
= MachineType
;
522 Header
->NumberOfSections
= 2;
523 Header
->TimeDateStamp
= TimeDateStamp
;
524 Header
->PointerToSymbolTable
= SymbolTableOffset
;
525 // One symbol for every resource plus 2 for each section and 1 for @feat.00
526 Header
->NumberOfSymbols
= Data
.size() + 5;
527 Header
->SizeOfOptionalHeader
= 0;
528 // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it.
529 Header
->Characteristics
= COFF::IMAGE_FILE_32BIT_MACHINE
;
532 void WindowsResourceCOFFWriter::writeFirstSectionHeader() {
533 // Write the first section header.
534 CurrentOffset
+= sizeof(coff_file_header
);
535 auto *SectionOneHeader
=
536 reinterpret_cast<coff_section
*>(BufferStart
+ CurrentOffset
);
537 strncpy(SectionOneHeader
->Name
, ".rsrc$01", (size_t)COFF::NameSize
);
538 SectionOneHeader
->VirtualSize
= 0;
539 SectionOneHeader
->VirtualAddress
= 0;
540 SectionOneHeader
->SizeOfRawData
= SectionOneSize
;
541 SectionOneHeader
->PointerToRawData
= SectionOneOffset
;
542 SectionOneHeader
->PointerToRelocations
= SectionOneRelocations
;
543 SectionOneHeader
->PointerToLinenumbers
= 0;
544 SectionOneHeader
->NumberOfRelocations
= Data
.size();
545 SectionOneHeader
->NumberOfLinenumbers
= 0;
546 SectionOneHeader
->Characteristics
+= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
;
547 SectionOneHeader
->Characteristics
+= COFF::IMAGE_SCN_MEM_READ
;
550 void WindowsResourceCOFFWriter::writeSecondSectionHeader() {
551 // Write the second section header.
552 CurrentOffset
+= sizeof(coff_section
);
553 auto *SectionTwoHeader
=
554 reinterpret_cast<coff_section
*>(BufferStart
+ CurrentOffset
);
555 strncpy(SectionTwoHeader
->Name
, ".rsrc$02", (size_t)COFF::NameSize
);
556 SectionTwoHeader
->VirtualSize
= 0;
557 SectionTwoHeader
->VirtualAddress
= 0;
558 SectionTwoHeader
->SizeOfRawData
= SectionTwoSize
;
559 SectionTwoHeader
->PointerToRawData
= SectionTwoOffset
;
560 SectionTwoHeader
->PointerToRelocations
= 0;
561 SectionTwoHeader
->PointerToLinenumbers
= 0;
562 SectionTwoHeader
->NumberOfRelocations
= 0;
563 SectionTwoHeader
->NumberOfLinenumbers
= 0;
564 SectionTwoHeader
->Characteristics
= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
;
565 SectionTwoHeader
->Characteristics
+= COFF::IMAGE_SCN_MEM_READ
;
568 void WindowsResourceCOFFWriter::writeFirstSection() {
569 // Write section one.
570 CurrentOffset
+= sizeof(coff_section
);
572 writeDirectoryTree();
573 writeDirectoryStringTable();
574 writeFirstSectionRelocations();
576 CurrentOffset
= alignTo(CurrentOffset
, SECTION_ALIGNMENT
);
579 void WindowsResourceCOFFWriter::writeSecondSection() {
580 // Now write the .rsrc$02 section.
581 for (auto const &RawDataEntry
: Data
) {
582 llvm::copy(RawDataEntry
, BufferStart
+ CurrentOffset
);
583 CurrentOffset
+= alignTo(RawDataEntry
.size(), sizeof(uint64_t));
586 CurrentOffset
= alignTo(CurrentOffset
, SECTION_ALIGNMENT
);
589 void WindowsResourceCOFFWriter::writeSymbolTable() {
590 // Now write the symbol table.
591 // First, the feat symbol.
592 auto *Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
593 strncpy(Symbol
->Name
.ShortName
, "@feat.00", (size_t)COFF::NameSize
);
594 Symbol
->Value
= 0x11;
595 Symbol
->SectionNumber
= 0xffff;
596 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
597 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
598 Symbol
->NumberOfAuxSymbols
= 0;
599 CurrentOffset
+= sizeof(coff_symbol16
);
601 // Now write the .rsrc1 symbol + aux.
602 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
603 strncpy(Symbol
->Name
.ShortName
, ".rsrc$01", (size_t)COFF::NameSize
);
605 Symbol
->SectionNumber
= 1;
606 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
607 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
608 Symbol
->NumberOfAuxSymbols
= 1;
609 CurrentOffset
+= sizeof(coff_symbol16
);
610 auto *Aux
= reinterpret_cast<coff_aux_section_definition
*>(BufferStart
+
612 Aux
->Length
= SectionOneSize
;
613 Aux
->NumberOfRelocations
= Data
.size();
614 Aux
->NumberOfLinenumbers
= 0;
616 Aux
->NumberLowPart
= 0;
618 CurrentOffset
+= sizeof(coff_aux_section_definition
);
620 // Now write the .rsrc2 symbol + aux.
621 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
622 strncpy(Symbol
->Name
.ShortName
, ".rsrc$02", (size_t)COFF::NameSize
);
624 Symbol
->SectionNumber
= 2;
625 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
626 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
627 Symbol
->NumberOfAuxSymbols
= 1;
628 CurrentOffset
+= sizeof(coff_symbol16
);
629 Aux
= reinterpret_cast<coff_aux_section_definition
*>(BufferStart
+
631 Aux
->Length
= SectionTwoSize
;
632 Aux
->NumberOfRelocations
= 0;
633 Aux
->NumberOfLinenumbers
= 0;
635 Aux
->NumberLowPart
= 0;
637 CurrentOffset
+= sizeof(coff_aux_section_definition
);
639 // Now write a symbol for each relocation.
640 for (unsigned i
= 0; i
< Data
.size(); i
++) {
641 auto RelocationName
= formatv("$R{0:X-6}", i
& 0xffffff).sstr
<COFF::NameSize
>();
642 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
643 memcpy(Symbol
->Name
.ShortName
, RelocationName
.data(), (size_t) COFF::NameSize
);
644 Symbol
->Value
= DataOffsets
[i
];
645 Symbol
->SectionNumber
= 2;
646 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
647 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
648 Symbol
->NumberOfAuxSymbols
= 0;
649 CurrentOffset
+= sizeof(coff_symbol16
);
653 void WindowsResourceCOFFWriter::writeStringTable() {
654 // Just 4 null bytes for the string table.
655 auto COFFStringTable
= reinterpret_cast<void *>(BufferStart
+ CurrentOffset
);
656 memset(COFFStringTable
, 0, 4);
659 void WindowsResourceCOFFWriter::writeDirectoryTree() {
660 // Traverse parsed resource tree breadth-first and write the corresponding
662 std::queue
<const WindowsResourceParser::TreeNode
*> Queue
;
663 Queue
.push(&Resources
);
664 uint32_t NextLevelOffset
=
665 sizeof(coff_resource_dir_table
) + (Resources
.getStringChildren().size() +
666 Resources
.getIDChildren().size()) *
667 sizeof(coff_resource_dir_entry
);
668 std::vector
<const WindowsResourceParser::TreeNode
*> DataEntriesTreeOrder
;
669 uint32_t CurrentRelativeOffset
= 0;
671 while (!Queue
.empty()) {
672 auto CurrentNode
= Queue
.front();
674 auto *Table
= reinterpret_cast<coff_resource_dir_table
*>(BufferStart
+
676 Table
->Characteristics
= CurrentNode
->getCharacteristics();
677 Table
->TimeDateStamp
= 0;
678 Table
->MajorVersion
= CurrentNode
->getMajorVersion();
679 Table
->MinorVersion
= CurrentNode
->getMinorVersion();
680 auto &IDChildren
= CurrentNode
->getIDChildren();
681 auto &StringChildren
= CurrentNode
->getStringChildren();
682 Table
->NumberOfNameEntries
= StringChildren
.size();
683 Table
->NumberOfIDEntries
= IDChildren
.size();
684 CurrentOffset
+= sizeof(coff_resource_dir_table
);
685 CurrentRelativeOffset
+= sizeof(coff_resource_dir_table
);
687 // Write the directory entries immediately following each directory table.
688 for (auto const &Child
: StringChildren
) {
689 auto *Entry
= reinterpret_cast<coff_resource_dir_entry
*>(BufferStart
+
691 Entry
->Identifier
.setNameOffset(
692 StringTableOffsets
[Child
.second
->getStringIndex()]);
693 if (Child
.second
->checkIsDataNode()) {
694 Entry
->Offset
.DataEntryOffset
= NextLevelOffset
;
695 NextLevelOffset
+= sizeof(coff_resource_data_entry
);
696 DataEntriesTreeOrder
.push_back(Child
.second
.get());
698 Entry
->Offset
.SubdirOffset
= NextLevelOffset
+ (1 << 31);
699 NextLevelOffset
+= sizeof(coff_resource_dir_table
) +
700 (Child
.second
->getStringChildren().size() +
701 Child
.second
->getIDChildren().size()) *
702 sizeof(coff_resource_dir_entry
);
703 Queue
.push(Child
.second
.get());
705 CurrentOffset
+= sizeof(coff_resource_dir_entry
);
706 CurrentRelativeOffset
+= sizeof(coff_resource_dir_entry
);
708 for (auto const &Child
: IDChildren
) {
709 auto *Entry
= reinterpret_cast<coff_resource_dir_entry
*>(BufferStart
+
711 Entry
->Identifier
.ID
= Child
.first
;
712 if (Child
.second
->checkIsDataNode()) {
713 Entry
->Offset
.DataEntryOffset
= NextLevelOffset
;
714 NextLevelOffset
+= sizeof(coff_resource_data_entry
);
715 DataEntriesTreeOrder
.push_back(Child
.second
.get());
717 Entry
->Offset
.SubdirOffset
= NextLevelOffset
+ (1 << 31);
718 NextLevelOffset
+= sizeof(coff_resource_dir_table
) +
719 (Child
.second
->getStringChildren().size() +
720 Child
.second
->getIDChildren().size()) *
721 sizeof(coff_resource_dir_entry
);
722 Queue
.push(Child
.second
.get());
724 CurrentOffset
+= sizeof(coff_resource_dir_entry
);
725 CurrentRelativeOffset
+= sizeof(coff_resource_dir_entry
);
729 RelocationAddresses
.resize(Data
.size());
730 // Now write all the resource data entries.
731 for (auto DataNodes
: DataEntriesTreeOrder
) {
732 auto *Entry
= reinterpret_cast<coff_resource_data_entry
*>(BufferStart
+
734 RelocationAddresses
[DataNodes
->getDataIndex()] = CurrentRelativeOffset
;
735 Entry
->DataRVA
= 0; // Set to zero because it is a relocation.
736 Entry
->DataSize
= Data
[DataNodes
->getDataIndex()].size();
739 CurrentOffset
+= sizeof(coff_resource_data_entry
);
740 CurrentRelativeOffset
+= sizeof(coff_resource_data_entry
);
744 void WindowsResourceCOFFWriter::writeDirectoryStringTable() {
745 // Now write the directory string table for .rsrc$01
746 uint32_t TotalStringTableSize
= 0;
747 for (auto &String
: StringTable
) {
748 uint16_t Length
= String
.size();
749 support::endian::write16le(BufferStart
+ CurrentOffset
, Length
);
750 CurrentOffset
+= sizeof(uint16_t);
751 auto *Start
= reinterpret_cast<UTF16
*>(BufferStart
+ CurrentOffset
);
752 llvm::copy(String
, Start
);
753 CurrentOffset
+= Length
* sizeof(UTF16
);
754 TotalStringTableSize
+= Length
* sizeof(UTF16
) + sizeof(uint16_t);
757 alignTo(TotalStringTableSize
, sizeof(uint32_t)) - TotalStringTableSize
;
760 void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {
762 // Now write the relocations for .rsrc$01
763 // Five symbols already in table before we start, @feat.00 and 2 for each
765 uint32_t NextSymbolIndex
= 5;
766 for (unsigned i
= 0; i
< Data
.size(); i
++) {
768 reinterpret_cast<coff_relocation
*>(BufferStart
+ CurrentOffset
);
769 Reloc
->VirtualAddress
= RelocationAddresses
[i
];
770 Reloc
->SymbolTableIndex
= NextSymbolIndex
++;
771 switch (MachineType
) {
772 case COFF::IMAGE_FILE_MACHINE_ARMNT
:
773 Reloc
->Type
= COFF::IMAGE_REL_ARM_ADDR32NB
;
775 case COFF::IMAGE_FILE_MACHINE_AMD64
:
776 Reloc
->Type
= COFF::IMAGE_REL_AMD64_ADDR32NB
;
778 case COFF::IMAGE_FILE_MACHINE_I386
:
779 Reloc
->Type
= COFF::IMAGE_REL_I386_DIR32NB
;
781 case COFF::IMAGE_FILE_MACHINE_ARM64
:
782 Reloc
->Type
= COFF::IMAGE_REL_ARM64_ADDR32NB
;
785 llvm_unreachable("unknown machine type");
787 CurrentOffset
+= sizeof(coff_relocation
);
791 Expected
<std::unique_ptr
<MemoryBuffer
>>
792 writeWindowsResourceCOFF(COFF::MachineTypes MachineType
,
793 const WindowsResourceParser
&Parser
,
794 uint32_t TimeDateStamp
) {
795 Error E
= Error::success();
796 WindowsResourceCOFFWriter
Writer(MachineType
, Parser
, E
);
799 return Writer
.write(TimeDateStamp
);
802 } // namespace object