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 // According to COFF specification, if the Src has a size equal to Dest,
519 // it's okay to *not* copy the trailing zero.
520 static void coffnamecpy(char (&Dest
)[COFF::NameSize
], StringRef Src
) {
521 assert(Src
.size() <= COFF::NameSize
&&
522 "Src is not larger than COFF::NameSize");
523 strncpy(Dest
, Src
.data(), (size_t)COFF::NameSize
);
526 void WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp
) {
527 // Write the COFF header.
528 auto *Header
= reinterpret_cast<coff_file_header
*>(BufferStart
);
529 Header
->Machine
= MachineType
;
530 Header
->NumberOfSections
= 2;
531 Header
->TimeDateStamp
= TimeDateStamp
;
532 Header
->PointerToSymbolTable
= SymbolTableOffset
;
533 // One symbol for every resource plus 2 for each section and 1 for @feat.00
534 Header
->NumberOfSymbols
= Data
.size() + 5;
535 Header
->SizeOfOptionalHeader
= 0;
536 // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it.
537 Header
->Characteristics
= COFF::IMAGE_FILE_32BIT_MACHINE
;
540 void WindowsResourceCOFFWriter::writeFirstSectionHeader() {
541 // Write the first section header.
542 CurrentOffset
+= sizeof(coff_file_header
);
543 auto *SectionOneHeader
=
544 reinterpret_cast<coff_section
*>(BufferStart
+ CurrentOffset
);
545 coffnamecpy(SectionOneHeader
->Name
, ".rsrc$01");
546 SectionOneHeader
->VirtualSize
= 0;
547 SectionOneHeader
->VirtualAddress
= 0;
548 SectionOneHeader
->SizeOfRawData
= SectionOneSize
;
549 SectionOneHeader
->PointerToRawData
= SectionOneOffset
;
550 SectionOneHeader
->PointerToRelocations
= SectionOneRelocations
;
551 SectionOneHeader
->PointerToLinenumbers
= 0;
552 SectionOneHeader
->NumberOfRelocations
= Data
.size();
553 SectionOneHeader
->NumberOfLinenumbers
= 0;
554 SectionOneHeader
->Characteristics
+= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
;
555 SectionOneHeader
->Characteristics
+= COFF::IMAGE_SCN_MEM_READ
;
558 void WindowsResourceCOFFWriter::writeSecondSectionHeader() {
559 // Write the second section header.
560 CurrentOffset
+= sizeof(coff_section
);
561 auto *SectionTwoHeader
=
562 reinterpret_cast<coff_section
*>(BufferStart
+ CurrentOffset
);
563 coffnamecpy(SectionTwoHeader
->Name
, ".rsrc$02");
564 SectionTwoHeader
->VirtualSize
= 0;
565 SectionTwoHeader
->VirtualAddress
= 0;
566 SectionTwoHeader
->SizeOfRawData
= SectionTwoSize
;
567 SectionTwoHeader
->PointerToRawData
= SectionTwoOffset
;
568 SectionTwoHeader
->PointerToRelocations
= 0;
569 SectionTwoHeader
->PointerToLinenumbers
= 0;
570 SectionTwoHeader
->NumberOfRelocations
= 0;
571 SectionTwoHeader
->NumberOfLinenumbers
= 0;
572 SectionTwoHeader
->Characteristics
= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
;
573 SectionTwoHeader
->Characteristics
+= COFF::IMAGE_SCN_MEM_READ
;
576 void WindowsResourceCOFFWriter::writeFirstSection() {
577 // Write section one.
578 CurrentOffset
+= sizeof(coff_section
);
580 writeDirectoryTree();
581 writeDirectoryStringTable();
582 writeFirstSectionRelocations();
584 CurrentOffset
= alignTo(CurrentOffset
, SECTION_ALIGNMENT
);
587 void WindowsResourceCOFFWriter::writeSecondSection() {
588 // Now write the .rsrc$02 section.
589 for (auto const &RawDataEntry
: Data
) {
590 llvm::copy(RawDataEntry
, BufferStart
+ CurrentOffset
);
591 CurrentOffset
+= alignTo(RawDataEntry
.size(), sizeof(uint64_t));
594 CurrentOffset
= alignTo(CurrentOffset
, SECTION_ALIGNMENT
);
597 void WindowsResourceCOFFWriter::writeSymbolTable() {
598 // Now write the symbol table.
599 // First, the feat symbol.
600 auto *Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
601 coffnamecpy(Symbol
->Name
.ShortName
, "@feat.00");
602 Symbol
->Value
= 0x11;
603 Symbol
->SectionNumber
= 0xffff;
604 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
605 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
606 Symbol
->NumberOfAuxSymbols
= 0;
607 CurrentOffset
+= sizeof(coff_symbol16
);
609 // Now write the .rsrc1 symbol + aux.
610 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
611 coffnamecpy(Symbol
->Name
.ShortName
, ".rsrc$01");
613 Symbol
->SectionNumber
= 1;
614 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
615 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
616 Symbol
->NumberOfAuxSymbols
= 1;
617 CurrentOffset
+= sizeof(coff_symbol16
);
618 auto *Aux
= reinterpret_cast<coff_aux_section_definition
*>(BufferStart
+
620 Aux
->Length
= SectionOneSize
;
621 Aux
->NumberOfRelocations
= Data
.size();
622 Aux
->NumberOfLinenumbers
= 0;
624 Aux
->NumberLowPart
= 0;
626 CurrentOffset
+= sizeof(coff_aux_section_definition
);
628 // Now write the .rsrc2 symbol + aux.
629 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
630 coffnamecpy(Symbol
->Name
.ShortName
, ".rsrc$02");
632 Symbol
->SectionNumber
= 2;
633 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
634 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
635 Symbol
->NumberOfAuxSymbols
= 1;
636 CurrentOffset
+= sizeof(coff_symbol16
);
637 Aux
= reinterpret_cast<coff_aux_section_definition
*>(BufferStart
+
639 Aux
->Length
= SectionTwoSize
;
640 Aux
->NumberOfRelocations
= 0;
641 Aux
->NumberOfLinenumbers
= 0;
643 Aux
->NumberLowPart
= 0;
645 CurrentOffset
+= sizeof(coff_aux_section_definition
);
647 // Now write a symbol for each relocation.
648 for (unsigned i
= 0; i
< Data
.size(); i
++) {
649 auto RelocationName
= formatv("$R{0:X-6}", i
& 0xffffff).sstr
<COFF::NameSize
>();
650 Symbol
= reinterpret_cast<coff_symbol16
*>(BufferStart
+ CurrentOffset
);
651 coffnamecpy(Symbol
->Name
.ShortName
, RelocationName
);
652 Symbol
->Value
= DataOffsets
[i
];
653 Symbol
->SectionNumber
= 2;
654 Symbol
->Type
= COFF::IMAGE_SYM_DTYPE_NULL
;
655 Symbol
->StorageClass
= COFF::IMAGE_SYM_CLASS_STATIC
;
656 Symbol
->NumberOfAuxSymbols
= 0;
657 CurrentOffset
+= sizeof(coff_symbol16
);
661 void WindowsResourceCOFFWriter::writeStringTable() {
662 // Just 4 null bytes for the string table.
663 auto COFFStringTable
= reinterpret_cast<void *>(BufferStart
+ CurrentOffset
);
664 memset(COFFStringTable
, 0, 4);
667 void WindowsResourceCOFFWriter::writeDirectoryTree() {
668 // Traverse parsed resource tree breadth-first and write the corresponding
670 std::queue
<const WindowsResourceParser::TreeNode
*> Queue
;
671 Queue
.push(&Resources
);
672 uint32_t NextLevelOffset
=
673 sizeof(coff_resource_dir_table
) + (Resources
.getStringChildren().size() +
674 Resources
.getIDChildren().size()) *
675 sizeof(coff_resource_dir_entry
);
676 std::vector
<const WindowsResourceParser::TreeNode
*> DataEntriesTreeOrder
;
677 uint32_t CurrentRelativeOffset
= 0;
679 while (!Queue
.empty()) {
680 auto CurrentNode
= Queue
.front();
682 auto *Table
= reinterpret_cast<coff_resource_dir_table
*>(BufferStart
+
684 Table
->Characteristics
= CurrentNode
->getCharacteristics();
685 Table
->TimeDateStamp
= 0;
686 Table
->MajorVersion
= CurrentNode
->getMajorVersion();
687 Table
->MinorVersion
= CurrentNode
->getMinorVersion();
688 auto &IDChildren
= CurrentNode
->getIDChildren();
689 auto &StringChildren
= CurrentNode
->getStringChildren();
690 Table
->NumberOfNameEntries
= StringChildren
.size();
691 Table
->NumberOfIDEntries
= IDChildren
.size();
692 CurrentOffset
+= sizeof(coff_resource_dir_table
);
693 CurrentRelativeOffset
+= sizeof(coff_resource_dir_table
);
695 // Write the directory entries immediately following each directory table.
696 for (auto const &Child
: StringChildren
) {
697 auto *Entry
= reinterpret_cast<coff_resource_dir_entry
*>(BufferStart
+
699 Entry
->Identifier
.setNameOffset(
700 StringTableOffsets
[Child
.second
->getStringIndex()]);
701 if (Child
.second
->checkIsDataNode()) {
702 Entry
->Offset
.DataEntryOffset
= NextLevelOffset
;
703 NextLevelOffset
+= sizeof(coff_resource_data_entry
);
704 DataEntriesTreeOrder
.push_back(Child
.second
.get());
706 Entry
->Offset
.SubdirOffset
= NextLevelOffset
+ (1 << 31);
707 NextLevelOffset
+= sizeof(coff_resource_dir_table
) +
708 (Child
.second
->getStringChildren().size() +
709 Child
.second
->getIDChildren().size()) *
710 sizeof(coff_resource_dir_entry
);
711 Queue
.push(Child
.second
.get());
713 CurrentOffset
+= sizeof(coff_resource_dir_entry
);
714 CurrentRelativeOffset
+= sizeof(coff_resource_dir_entry
);
716 for (auto const &Child
: IDChildren
) {
717 auto *Entry
= reinterpret_cast<coff_resource_dir_entry
*>(BufferStart
+
719 Entry
->Identifier
.ID
= Child
.first
;
720 if (Child
.second
->checkIsDataNode()) {
721 Entry
->Offset
.DataEntryOffset
= NextLevelOffset
;
722 NextLevelOffset
+= sizeof(coff_resource_data_entry
);
723 DataEntriesTreeOrder
.push_back(Child
.second
.get());
725 Entry
->Offset
.SubdirOffset
= NextLevelOffset
+ (1 << 31);
726 NextLevelOffset
+= sizeof(coff_resource_dir_table
) +
727 (Child
.second
->getStringChildren().size() +
728 Child
.second
->getIDChildren().size()) *
729 sizeof(coff_resource_dir_entry
);
730 Queue
.push(Child
.second
.get());
732 CurrentOffset
+= sizeof(coff_resource_dir_entry
);
733 CurrentRelativeOffset
+= sizeof(coff_resource_dir_entry
);
737 RelocationAddresses
.resize(Data
.size());
738 // Now write all the resource data entries.
739 for (auto DataNodes
: DataEntriesTreeOrder
) {
740 auto *Entry
= reinterpret_cast<coff_resource_data_entry
*>(BufferStart
+
742 RelocationAddresses
[DataNodes
->getDataIndex()] = CurrentRelativeOffset
;
743 Entry
->DataRVA
= 0; // Set to zero because it is a relocation.
744 Entry
->DataSize
= Data
[DataNodes
->getDataIndex()].size();
747 CurrentOffset
+= sizeof(coff_resource_data_entry
);
748 CurrentRelativeOffset
+= sizeof(coff_resource_data_entry
);
752 void WindowsResourceCOFFWriter::writeDirectoryStringTable() {
753 // Now write the directory string table for .rsrc$01
754 uint32_t TotalStringTableSize
= 0;
755 for (auto &String
: StringTable
) {
756 uint16_t Length
= String
.size();
757 support::endian::write16le(BufferStart
+ CurrentOffset
, Length
);
758 CurrentOffset
+= sizeof(uint16_t);
759 auto *Start
= reinterpret_cast<UTF16
*>(BufferStart
+ CurrentOffset
);
760 llvm::copy(String
, Start
);
761 CurrentOffset
+= Length
* sizeof(UTF16
);
762 TotalStringTableSize
+= Length
* sizeof(UTF16
) + sizeof(uint16_t);
765 alignTo(TotalStringTableSize
, sizeof(uint32_t)) - TotalStringTableSize
;
768 void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {
770 // Now write the relocations for .rsrc$01
771 // Five symbols already in table before we start, @feat.00 and 2 for each
773 uint32_t NextSymbolIndex
= 5;
774 for (unsigned i
= 0; i
< Data
.size(); i
++) {
776 reinterpret_cast<coff_relocation
*>(BufferStart
+ CurrentOffset
);
777 Reloc
->VirtualAddress
= RelocationAddresses
[i
];
778 Reloc
->SymbolTableIndex
= NextSymbolIndex
++;
779 switch (MachineType
) {
780 case COFF::IMAGE_FILE_MACHINE_ARMNT
:
781 Reloc
->Type
= COFF::IMAGE_REL_ARM_ADDR32NB
;
783 case COFF::IMAGE_FILE_MACHINE_AMD64
:
784 Reloc
->Type
= COFF::IMAGE_REL_AMD64_ADDR32NB
;
786 case COFF::IMAGE_FILE_MACHINE_I386
:
787 Reloc
->Type
= COFF::IMAGE_REL_I386_DIR32NB
;
789 case COFF::IMAGE_FILE_MACHINE_ARM64
:
790 Reloc
->Type
= COFF::IMAGE_REL_ARM64_ADDR32NB
;
793 llvm_unreachable("unknown machine type");
795 CurrentOffset
+= sizeof(coff_relocation
);
799 Expected
<std::unique_ptr
<MemoryBuffer
>>
800 writeWindowsResourceCOFF(COFF::MachineTypes MachineType
,
801 const WindowsResourceParser
&Parser
,
802 uint32_t TimeDateStamp
) {
803 Error E
= Error::success();
804 WindowsResourceCOFFWriter
Writer(MachineType
, Parser
, E
);
807 return Writer
.write(TimeDateStamp
);
810 } // namespace object