1015 lines · cpp
1//===-- WindowsResource.cpp -------------------------------------*- C++ -*-===//2//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-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the .res file class.10//11//===----------------------------------------------------------------------===//12 13#include "llvm/Object/WindowsResource.h"14#include "llvm/Object/COFF.h"15#include "llvm/Object/WindowsMachineFlag.h"16#include "llvm/Support/FormatVariadic.h"17#include "llvm/Support/MathExtras.h"18#include "llvm/Support/ScopedPrinter.h"19#include <ctime>20#include <queue>21 22using namespace llvm;23using namespace object;24 25namespace llvm {26namespace object {27 28#define RETURN_IF_ERROR(X) \29 if (auto EC = X) \30 return EC;31 32#define UNWRAP_REF_OR_RETURN(Name, Expr) \33 auto Name##OrErr = Expr; \34 if (!Name##OrErr) \35 return Name##OrErr.takeError(); \36 const auto &Name = *Name##OrErr;37 38#define UNWRAP_OR_RETURN(Name, Expr) \39 auto Name##OrErr = Expr; \40 if (!Name##OrErr) \41 return Name##OrErr.takeError(); \42 auto Name = *Name##OrErr;43 44const uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t);45 46// COFF files seem to be inconsistent with alignment between sections, just use47// 8-byte because it makes everyone happy.48const uint32_t SECTION_ALIGNMENT = sizeof(uint64_t);49 50WindowsResource::WindowsResource(MemoryBufferRef Source)51 : Binary(Binary::ID_WinRes, Source) {52 size_t LeadingSize = WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE;53 BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize),54 llvm::endianness::little);55}56 57// static58Expected<std::unique_ptr<WindowsResource>>59WindowsResource::createWindowsResource(MemoryBufferRef Source) {60 if (Source.getBufferSize() < WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE)61 return make_error<GenericBinaryError>(62 Source.getBufferIdentifier() + ": too small to be a resource file",63 object_error::invalid_file_type);64 std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source));65 return std::move(Ret);66}67 68Expected<ResourceEntryRef> WindowsResource::getHeadEntry() {69 if (BBS.getLength() < sizeof(WinResHeaderPrefix) + sizeof(WinResHeaderSuffix))70 return make_error<EmptyResError>(getFileName() + " contains no entries",71 object_error::unexpected_eof);72 return ResourceEntryRef::create(BinaryStreamRef(BBS), this);73}74 75ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref,76 const WindowsResource *Owner)77 : Reader(Ref), Owner(Owner) {}78 79Expected<ResourceEntryRef>80ResourceEntryRef::create(BinaryStreamRef BSR, const WindowsResource *Owner) {81 auto Ref = ResourceEntryRef(BSR, Owner);82 if (auto E = Ref.loadNext())83 return E;84 return Ref;85}86 87Error ResourceEntryRef::moveNext(bool &End) {88 // Reached end of all the entries.89 if (Reader.bytesRemaining() == 0) {90 End = true;91 return Error::success();92 }93 RETURN_IF_ERROR(loadNext());94 95 return Error::success();96}97 98static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID,99 ArrayRef<UTF16> &Str, bool &IsString) {100 uint16_t IDFlag;101 RETURN_IF_ERROR(Reader.readInteger(IDFlag));102 IsString = IDFlag != 0xffff;103 104 if (IsString) {105 Reader.setOffset(106 Reader.getOffset() -107 sizeof(uint16_t)); // Re-read the bytes which we used to check the flag.108 RETURN_IF_ERROR(Reader.readWideString(Str));109 } else110 RETURN_IF_ERROR(Reader.readInteger(ID));111 112 return Error::success();113}114 115Error ResourceEntryRef::loadNext() {116 const WinResHeaderPrefix *Prefix;117 RETURN_IF_ERROR(Reader.readObject(Prefix));118 119 if (Prefix->HeaderSize < MIN_HEADER_SIZE)120 return make_error<GenericBinaryError>(Owner->getFileName() +121 ": header size too small",122 object_error::parse_failed);123 124 RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType));125 126 RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName));127 128 RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_HEADER_ALIGNMENT));129 130 RETURN_IF_ERROR(Reader.readObject(Suffix));131 132 RETURN_IF_ERROR(Reader.readArray(Data, Prefix->DataSize));133 134 RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_DATA_ALIGNMENT));135 136 return Error::success();137}138 139WindowsResourceParser::WindowsResourceParser(bool MinGW)140 : Root(false), MinGW(MinGW) {}141 142void printResourceTypeName(uint16_t TypeID, raw_ostream &OS) {143 switch (TypeID) {144 case 1: OS << "CURSOR (ID 1)"; break;145 case 2: OS << "BITMAP (ID 2)"; break;146 case 3: OS << "ICON (ID 3)"; break;147 case 4: OS << "MENU (ID 4)"; break;148 case 5: OS << "DIALOG (ID 5)"; break;149 case 6: OS << "STRINGTABLE (ID 6)"; break;150 case 7: OS << "FONTDIR (ID 7)"; break;151 case 8: OS << "FONT (ID 8)"; break;152 case 9: OS << "ACCELERATOR (ID 9)"; break;153 case 10: OS << "RCDATA (ID 10)"; break;154 case 11: OS << "MESSAGETABLE (ID 11)"; break;155 case 12: OS << "GROUP_CURSOR (ID 12)"; break;156 case 14: OS << "GROUP_ICON (ID 14)"; break;157 case 16: OS << "VERSIONINFO (ID 16)"; break;158 case 17: OS << "DLGINCLUDE (ID 17)"; break;159 case 19: OS << "PLUGPLAY (ID 19)"; break;160 case 20: OS << "VXD (ID 20)"; break;161 case 21: OS << "ANICURSOR (ID 21)"; break;162 case 22: OS << "ANIICON (ID 22)"; break;163 case 23: OS << "HTML (ID 23)"; break;164 case 24: OS << "MANIFEST (ID 24)"; break;165 default: OS << "ID " << TypeID; break;166 }167}168 169static bool convertUTF16LEToUTF8String(ArrayRef<UTF16> Src, std::string &Out) {170 if (!sys::IsBigEndianHost)171 return convertUTF16ToUTF8String(Src, Out);172 173 std::vector<UTF16> EndianCorrectedSrc;174 EndianCorrectedSrc.resize(Src.size() + 1);175 llvm::copy(Src, EndianCorrectedSrc.begin() + 1);176 EndianCorrectedSrc[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED;177 return convertUTF16ToUTF8String(ArrayRef(EndianCorrectedSrc), Out);178}179 180static std::string makeDuplicateResourceError(181 const ResourceEntryRef &Entry, StringRef File1, StringRef File2) {182 std::string Ret;183 raw_string_ostream OS(Ret);184 185 OS << "duplicate resource:";186 187 OS << " type ";188 if (Entry.checkTypeString()) {189 std::string UTF8;190 if (!convertUTF16LEToUTF8String(Entry.getTypeString(), UTF8))191 UTF8 = "(failed conversion from UTF16)";192 OS << '\"' << UTF8 << '\"';193 } else194 printResourceTypeName(Entry.getTypeID(), OS);195 196 OS << "/name ";197 if (Entry.checkNameString()) {198 std::string UTF8;199 if (!convertUTF16LEToUTF8String(Entry.getNameString(), UTF8))200 UTF8 = "(failed conversion from UTF16)";201 OS << '\"' << UTF8 << '\"';202 } else {203 OS << "ID " << Entry.getNameID();204 }205 206 OS << "/language " << Entry.getLanguage() << ", in " << File1 << " and in "207 << File2;208 209 return OS.str();210}211 212static void printStringOrID(const WindowsResourceParser::StringOrID &S,213 raw_string_ostream &OS, bool IsType, bool IsID) {214 if (S.IsString) {215 std::string UTF8;216 if (!convertUTF16LEToUTF8String(S.String, UTF8))217 UTF8 = "(failed conversion from UTF16)";218 OS << '\"' << UTF8 << '\"';219 } else if (IsType)220 printResourceTypeName(S.ID, OS);221 else if (IsID)222 OS << "ID " << S.ID;223 else224 OS << S.ID;225}226 227static std::string makeDuplicateResourceError(228 const std::vector<WindowsResourceParser::StringOrID> &Context,229 StringRef File1, StringRef File2) {230 std::string Ret;231 raw_string_ostream OS(Ret);232 233 OS << "duplicate resource:";234 235 if (Context.size() >= 1) {236 OS << " type ";237 printStringOrID(Context[0], OS, /* IsType */ true, /* IsID */ true);238 }239 240 if (Context.size() >= 2) {241 OS << "/name ";242 printStringOrID(Context[1], OS, /* IsType */ false, /* IsID */ true);243 }244 245 if (Context.size() >= 3) {246 OS << "/language ";247 printStringOrID(Context[2], OS, /* IsType */ false, /* IsID */ false);248 }249 OS << ", in " << File1 << " and in " << File2;250 251 return OS.str();252}253 254// MinGW specific. Remove default manifests (with language zero) if there are255// other manifests present, and report an error if there are more than one256// manifest with a non-zero language code.257// GCC has the concept of a default manifest resource object, which gets258// linked in implicitly if present. This default manifest has got language259// id zero, and should be dropped silently if there's another manifest present.260// If the user resources surprisignly had a manifest with language id zero,261// we should also ignore the duplicate default manifest.262void WindowsResourceParser::cleanUpManifests(263 std::vector<std::string> &Duplicates) {264 auto TypeIt = Root.IDChildren.find(/* RT_MANIFEST */ 24);265 if (TypeIt == Root.IDChildren.end())266 return;267 268 TreeNode *TypeNode = TypeIt->second.get();269 auto NameIt =270 TypeNode->IDChildren.find(/* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1);271 if (NameIt == TypeNode->IDChildren.end())272 return;273 274 TreeNode *NameNode = NameIt->second.get();275 if (NameNode->IDChildren.size() <= 1)276 return; // None or one manifest present, all good.277 278 // If we have more than one manifest, drop the language zero one if present,279 // and check again.280 auto LangZeroIt = NameNode->IDChildren.find(0);281 if (LangZeroIt != NameNode->IDChildren.end() &&282 LangZeroIt->second->IsDataNode) {283 uint32_t RemovedIndex = LangZeroIt->second->DataIndex;284 NameNode->IDChildren.erase(LangZeroIt);285 Data.erase(Data.begin() + RemovedIndex);286 Root.shiftDataIndexDown(RemovedIndex);287 288 // If we're now down to one manifest, all is good.289 if (NameNode->IDChildren.size() <= 1)290 return;291 }292 293 // More than one non-language-zero manifest294 auto FirstIt = NameNode->IDChildren.begin();295 uint32_t FirstLang = FirstIt->first;296 TreeNode *FirstNode = FirstIt->second.get();297 auto LastIt = NameNode->IDChildren.rbegin();298 uint32_t LastLang = LastIt->first;299 TreeNode *LastNode = LastIt->second.get();300 Duplicates.push_back(301 ("duplicate non-default manifests with languages " + Twine(FirstLang) +302 " in " + InputFilenames[FirstNode->Origin] + " and " + Twine(LastLang) +303 " in " + InputFilenames[LastNode->Origin])304 .str());305}306 307// Ignore duplicates of manifests with language zero (the default manifest),308// in case the user has provided a manifest with that language id. See309// the function comment above for context. Only returns true if MinGW is set310// to true.311bool WindowsResourceParser::shouldIgnoreDuplicate(312 const ResourceEntryRef &Entry) const {313 return MinGW && !Entry.checkTypeString() &&314 Entry.getTypeID() == /* RT_MANIFEST */ 24 &&315 !Entry.checkNameString() &&316 Entry.getNameID() == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&317 Entry.getLanguage() == 0;318}319 320bool WindowsResourceParser::shouldIgnoreDuplicate(321 const std::vector<StringOrID> &Context) const {322 return MinGW && Context.size() == 3 && !Context[0].IsString &&323 Context[0].ID == /* RT_MANIFEST */ 24 && !Context[1].IsString &&324 Context[1].ID == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&325 !Context[2].IsString && Context[2].ID == 0;326}327 328Error WindowsResourceParser::parse(WindowsResource *WR,329 std::vector<std::string> &Duplicates) {330 auto EntryOrErr = WR->getHeadEntry();331 if (!EntryOrErr) {332 auto E = EntryOrErr.takeError();333 if (E.isA<EmptyResError>()) {334 // Check if the .res file contains no entries. In this case we don't have335 // to throw an error but can rather just return without parsing anything.336 // This applies for files which have a valid PE header magic and the337 // mandatory empty null resource entry. Files which do not fit this338 // criteria would have already been filtered out by339 // WindowsResource::createWindowsResource().340 consumeError(std::move(E));341 return Error::success();342 }343 return E;344 }345 346 ResourceEntryRef Entry = EntryOrErr.get();347 uint32_t Origin = InputFilenames.size();348 InputFilenames.push_back(std::string(WR->getFileName()));349 bool End = false;350 while (!End) {351 352 TreeNode *Node;353 bool IsNewNode = Root.addEntry(Entry, Origin, Data, StringTable, Node);354 if (!IsNewNode) {355 if (!shouldIgnoreDuplicate(Entry))356 Duplicates.push_back(makeDuplicateResourceError(357 Entry, InputFilenames[Node->Origin], WR->getFileName()));358 }359 360 RETURN_IF_ERROR(Entry.moveNext(End));361 }362 363 return Error::success();364}365 366Error WindowsResourceParser::parse(ResourceSectionRef &RSR, StringRef Filename,367 std::vector<std::string> &Duplicates) {368 UNWRAP_REF_OR_RETURN(BaseTable, RSR.getBaseTable());369 uint32_t Origin = InputFilenames.size();370 InputFilenames.push_back(std::string(Filename));371 std::vector<StringOrID> Context;372 return addChildren(Root, RSR, BaseTable, Origin, Context, Duplicates);373}374 375void WindowsResourceParser::printTree(raw_ostream &OS) const {376 ScopedPrinter Writer(OS);377 Root.print(Writer, "Resource Tree");378}379 380bool WindowsResourceParser::TreeNode::addEntry(381 const ResourceEntryRef &Entry, uint32_t Origin,382 std::vector<std::vector<uint8_t>> &Data,383 std::vector<std::vector<UTF16>> &StringTable, TreeNode *&Result) {384 TreeNode &TypeNode = addTypeNode(Entry, StringTable);385 TreeNode &NameNode = TypeNode.addNameNode(Entry, StringTable);386 return NameNode.addLanguageNode(Entry, Origin, Data, Result);387}388 389Error WindowsResourceParser::addChildren(TreeNode &Node,390 ResourceSectionRef &RSR,391 const coff_resource_dir_table &Table,392 uint32_t Origin,393 std::vector<StringOrID> &Context,394 std::vector<std::string> &Duplicates) {395 396 for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries;397 i++) {398 UNWRAP_REF_OR_RETURN(Entry, RSR.getTableEntry(Table, i));399 TreeNode *Child;400 401 if (Entry.Offset.isSubDir()) {402 403 // Create a new subdirectory and recurse404 if (i < Table.NumberOfNameEntries) {405 UNWRAP_OR_RETURN(NameString, RSR.getEntryNameString(Entry));406 Child = &Node.addNameChild(NameString, StringTable);407 Context.push_back(StringOrID(NameString));408 } else {409 Child = &Node.addIDChild(Entry.Identifier.ID);410 Context.push_back(StringOrID(Entry.Identifier.ID));411 }412 413 UNWRAP_REF_OR_RETURN(NextTable, RSR.getEntrySubDir(Entry));414 Error E =415 addChildren(*Child, RSR, NextTable, Origin, Context, Duplicates);416 if (E)417 return E;418 Context.pop_back();419 420 } else {421 422 // Data leaves are supposed to have a numeric ID as identifier (language).423 if (Table.NumberOfNameEntries > 0)424 return createStringError(object_error::parse_failed,425 "unexpected string key for data object");426 427 // Try adding a data leaf428 UNWRAP_REF_OR_RETURN(DataEntry, RSR.getEntryData(Entry));429 TreeNode *Child;430 Context.push_back(StringOrID(Entry.Identifier.ID));431 bool Added = Node.addDataChild(Entry.Identifier.ID, Table.MajorVersion,432 Table.MinorVersion, Table.Characteristics,433 Origin, Data.size(), Child);434 if (Added) {435 UNWRAP_OR_RETURN(Contents, RSR.getContents(DataEntry));436 Data.push_back(ArrayRef<uint8_t>(437 reinterpret_cast<const uint8_t *>(Contents.data()),438 Contents.size()));439 } else {440 if (!shouldIgnoreDuplicate(Context))441 Duplicates.push_back(makeDuplicateResourceError(442 Context, InputFilenames[Child->Origin], InputFilenames.back()));443 }444 Context.pop_back();445 446 }447 }448 return Error::success();449}450 451WindowsResourceParser::TreeNode::TreeNode(uint32_t StringIndex)452 : StringIndex(StringIndex) {}453 454WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion,455 uint16_t MinorVersion,456 uint32_t Characteristics,457 uint32_t Origin, uint32_t DataIndex)458 : IsDataNode(true), DataIndex(DataIndex), MajorVersion(MajorVersion),459 MinorVersion(MinorVersion), Characteristics(Characteristics),460 Origin(Origin) {}461 462std::unique_ptr<WindowsResourceParser::TreeNode>463WindowsResourceParser::TreeNode::createStringNode(uint32_t Index) {464 return std::unique_ptr<TreeNode>(new TreeNode(Index));465}466 467std::unique_ptr<WindowsResourceParser::TreeNode>468WindowsResourceParser::TreeNode::createIDNode() {469 return std::unique_ptr<TreeNode>(new TreeNode(0));470}471 472std::unique_ptr<WindowsResourceParser::TreeNode>473WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion,474 uint16_t MinorVersion,475 uint32_t Characteristics,476 uint32_t Origin,477 uint32_t DataIndex) {478 return std::unique_ptr<TreeNode>(new TreeNode(479 MajorVersion, MinorVersion, Characteristics, Origin, DataIndex));480}481 482WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addTypeNode(483 const ResourceEntryRef &Entry,484 std::vector<std::vector<UTF16>> &StringTable) {485 if (Entry.checkTypeString())486 return addNameChild(Entry.getTypeString(), StringTable);487 else488 return addIDChild(Entry.getTypeID());489}490 491WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameNode(492 const ResourceEntryRef &Entry,493 std::vector<std::vector<UTF16>> &StringTable) {494 if (Entry.checkNameString())495 return addNameChild(Entry.getNameString(), StringTable);496 else497 return addIDChild(Entry.getNameID());498}499 500bool WindowsResourceParser::TreeNode::addLanguageNode(501 const ResourceEntryRef &Entry, uint32_t Origin,502 std::vector<std::vector<uint8_t>> &Data, TreeNode *&Result) {503 bool Added = addDataChild(Entry.getLanguage(), Entry.getMajorVersion(),504 Entry.getMinorVersion(), Entry.getCharacteristics(),505 Origin, Data.size(), Result);506 if (Added)507 Data.push_back(Entry.getData());508 return Added;509}510 511bool WindowsResourceParser::TreeNode::addDataChild(512 uint32_t ID, uint16_t MajorVersion, uint16_t MinorVersion,513 uint32_t Characteristics, uint32_t Origin, uint32_t DataIndex,514 TreeNode *&Result) {515 auto NewChild = createDataNode(MajorVersion, MinorVersion, Characteristics,516 Origin, DataIndex);517 auto ElementInserted = IDChildren.emplace(ID, std::move(NewChild));518 Result = ElementInserted.first->second.get();519 return ElementInserted.second;520}521 522WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addIDChild(523 uint32_t ID) {524 auto Child = IDChildren.find(ID);525 if (Child == IDChildren.end()) {526 auto NewChild = createIDNode();527 WindowsResourceParser::TreeNode &Node = *NewChild;528 IDChildren.emplace(ID, std::move(NewChild));529 return Node;530 } else531 return *(Child->second);532}533 534WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameChild(535 ArrayRef<UTF16> NameRef, std::vector<std::vector<UTF16>> &StringTable) {536 std::string NameString;537 convertUTF16LEToUTF8String(NameRef, NameString);538 539 auto Child = StringChildren.find(NameString);540 if (Child == StringChildren.end()) {541 auto NewChild = createStringNode(StringTable.size());542 StringTable.push_back(NameRef);543 WindowsResourceParser::TreeNode &Node = *NewChild;544 StringChildren.emplace(NameString, std::move(NewChild));545 return Node;546 } else547 return *(Child->second);548}549 550void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer,551 StringRef Name) const {552 ListScope NodeScope(Writer, Name);553 for (auto const &Child : StringChildren) {554 Child.second->print(Writer, Child.first);555 }556 for (auto const &Child : IDChildren) {557 Child.second->print(Writer, to_string(Child.first));558 }559}560 561// This function returns the size of the entire resource tree, including562// directory tables, directory entries, and data entries. It does not include563// the directory strings or the relocations of the .rsrc section.564uint32_t WindowsResourceParser::TreeNode::getTreeSize() const {565 uint32_t Size = (IDChildren.size() + StringChildren.size()) *566 sizeof(coff_resource_dir_entry);567 568 // Reached a node pointing to a data entry.569 if (IsDataNode) {570 Size += sizeof(coff_resource_data_entry);571 return Size;572 }573 574 // If the node does not point to data, it must have a directory table pointing575 // to other nodes.576 Size += sizeof(coff_resource_dir_table);577 578 for (auto const &Child : StringChildren) {579 Size += Child.second->getTreeSize();580 }581 for (auto const &Child : IDChildren) {582 Size += Child.second->getTreeSize();583 }584 return Size;585}586 587// Shift DataIndex of all data children with an Index greater or equal to the588// given one, to fill a gap from removing an entry from the Data vector.589void WindowsResourceParser::TreeNode::shiftDataIndexDown(uint32_t Index) {590 if (IsDataNode && DataIndex >= Index) {591 DataIndex--;592 } else {593 for (auto &Child : IDChildren)594 Child.second->shiftDataIndexDown(Index);595 for (auto &Child : StringChildren)596 Child.second->shiftDataIndexDown(Index);597 }598}599 600class WindowsResourceCOFFWriter {601public:602 WindowsResourceCOFFWriter(COFF::MachineTypes MachineType,603 const WindowsResourceParser &Parser, Error &E);604 std::unique_ptr<MemoryBuffer> write(uint32_t TimeDateStamp);605 606private:607 void performFileLayout();608 void performSectionOneLayout();609 void performSectionTwoLayout();610 void writeCOFFHeader(uint32_t TimeDateStamp);611 void writeFirstSectionHeader();612 void writeSecondSectionHeader();613 void writeFirstSection();614 void writeSecondSection();615 void writeSymbolTable();616 void writeStringTable();617 void writeDirectoryTree();618 void writeDirectoryStringTable();619 void writeFirstSectionRelocations();620 std::unique_ptr<WritableMemoryBuffer> OutputBuffer;621 char *BufferStart;622 uint64_t CurrentOffset = 0;623 COFF::MachineTypes MachineType;624 const WindowsResourceParser::TreeNode &Resources;625 const ArrayRef<std::vector<uint8_t>> Data;626 uint64_t FileSize;627 uint32_t SymbolTableOffset;628 uint32_t SectionOneSize;629 uint32_t SectionOneOffset;630 uint32_t SectionOneRelocations;631 uint32_t SectionTwoSize;632 uint32_t SectionTwoOffset;633 const ArrayRef<std::vector<UTF16>> StringTable;634 std::vector<uint32_t> StringTableOffsets;635 std::vector<uint32_t> DataOffsets;636 std::vector<uint32_t> RelocationAddresses;637};638 639WindowsResourceCOFFWriter::WindowsResourceCOFFWriter(640 COFF::MachineTypes MachineType, const WindowsResourceParser &Parser,641 Error &E)642 : MachineType(MachineType), Resources(Parser.getTree()),643 Data(Parser.getData()), StringTable(Parser.getStringTable()) {644 performFileLayout();645 646 OutputBuffer = WritableMemoryBuffer::getNewMemBuffer(647 FileSize, "internal .obj file created from .res files");648}649 650void WindowsResourceCOFFWriter::performFileLayout() {651 // Add size of COFF header.652 FileSize = COFF::Header16Size;653 654 // one .rsrc section header for directory tree, another for resource data.655 FileSize += 2 * COFF::SectionSize;656 657 performSectionOneLayout();658 performSectionTwoLayout();659 660 // We have reached the address of the symbol table.661 SymbolTableOffset = FileSize;662 663 FileSize += COFF::Symbol16Size; // size of the @feat.00 symbol.664 FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section.665 FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource.666 FileSize += 4; // four null bytes for the string table.667}668 669void WindowsResourceCOFFWriter::performSectionOneLayout() {670 SectionOneOffset = FileSize;671 672 SectionOneSize = Resources.getTreeSize();673 uint32_t CurrentStringOffset = SectionOneSize;674 uint32_t TotalStringTableSize = 0;675 for (auto const &String : StringTable) {676 StringTableOffsets.push_back(CurrentStringOffset);677 uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t);678 CurrentStringOffset += StringSize;679 TotalStringTableSize += StringSize;680 }681 SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t));682 683 // account for the relocations of section one.684 SectionOneRelocations = FileSize + SectionOneSize;685 FileSize += SectionOneSize;686 FileSize +=687 Data.size() * COFF::RelocationSize; // one relocation for each resource.688 FileSize = alignTo(FileSize, SECTION_ALIGNMENT);689}690 691void WindowsResourceCOFFWriter::performSectionTwoLayout() {692 // add size of .rsrc$2 section, which contains all resource data on 8-byte693 // alignment.694 SectionTwoOffset = FileSize;695 SectionTwoSize = 0;696 for (auto const &Entry : Data) {697 DataOffsets.push_back(SectionTwoSize);698 SectionTwoSize += alignTo(Entry.size(), sizeof(uint64_t));699 }700 FileSize += SectionTwoSize;701 FileSize = alignTo(FileSize, SECTION_ALIGNMENT);702}703 704std::unique_ptr<MemoryBuffer>705WindowsResourceCOFFWriter::write(uint32_t TimeDateStamp) {706 BufferStart = OutputBuffer->getBufferStart();707 708 writeCOFFHeader(TimeDateStamp);709 writeFirstSectionHeader();710 writeSecondSectionHeader();711 writeFirstSection();712 writeSecondSection();713 writeSymbolTable();714 writeStringTable();715 716 return std::move(OutputBuffer);717}718 719// According to COFF specification, if the Src has a size equal to Dest,720// it's okay to *not* copy the trailing zero.721static void coffnamecpy(char (&Dest)[COFF::NameSize], StringRef Src) {722 assert(Src.size() <= COFF::NameSize &&723 "Src is larger than COFF::NameSize");724 assert((Src.size() == COFF::NameSize || Dest[Src.size()] == '\0') &&725 "Dest not zeroed upon initialization");726 memcpy(Dest, Src.data(), Src.size());727}728 729void WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp) {730 // Write the COFF header.731 auto *Header = reinterpret_cast<coff_file_header *>(BufferStart);732 Header->Machine = MachineType;733 Header->NumberOfSections = 2;734 Header->TimeDateStamp = TimeDateStamp;735 Header->PointerToSymbolTable = SymbolTableOffset;736 // One symbol for every resource plus 2 for each section and 1 for @feat.00737 Header->NumberOfSymbols = Data.size() + 5;738 Header->SizeOfOptionalHeader = 0;739 // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it.740 Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE;741}742 743void WindowsResourceCOFFWriter::writeFirstSectionHeader() {744 // Write the first section header.745 CurrentOffset += sizeof(coff_file_header);746 auto *SectionOneHeader =747 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);748 coffnamecpy(SectionOneHeader->Name, ".rsrc$01");749 SectionOneHeader->VirtualSize = 0;750 SectionOneHeader->VirtualAddress = 0;751 SectionOneHeader->SizeOfRawData = SectionOneSize;752 SectionOneHeader->PointerToRawData = SectionOneOffset;753 SectionOneHeader->PointerToRelocations = SectionOneRelocations;754 SectionOneHeader->PointerToLinenumbers = 0;755 SectionOneHeader->NumberOfRelocations = Data.size();756 SectionOneHeader->NumberOfLinenumbers = 0;757 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;758 SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;759}760 761void WindowsResourceCOFFWriter::writeSecondSectionHeader() {762 // Write the second section header.763 CurrentOffset += sizeof(coff_section);764 auto *SectionTwoHeader =765 reinterpret_cast<coff_section *>(BufferStart + CurrentOffset);766 coffnamecpy(SectionTwoHeader->Name, ".rsrc$02");767 SectionTwoHeader->VirtualSize = 0;768 SectionTwoHeader->VirtualAddress = 0;769 SectionTwoHeader->SizeOfRawData = SectionTwoSize;770 SectionTwoHeader->PointerToRawData = SectionTwoOffset;771 SectionTwoHeader->PointerToRelocations = 0;772 SectionTwoHeader->PointerToLinenumbers = 0;773 SectionTwoHeader->NumberOfRelocations = 0;774 SectionTwoHeader->NumberOfLinenumbers = 0;775 SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;776 SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ;777}778 779void WindowsResourceCOFFWriter::writeFirstSection() {780 // Write section one.781 CurrentOffset += sizeof(coff_section);782 783 writeDirectoryTree();784 writeDirectoryStringTable();785 writeFirstSectionRelocations();786 787 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);788}789 790void WindowsResourceCOFFWriter::writeSecondSection() {791 // Now write the .rsrc$02 section.792 for (auto const &RawDataEntry : Data) {793 llvm::copy(RawDataEntry, BufferStart + CurrentOffset);794 CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t));795 }796 797 CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT);798}799 800void WindowsResourceCOFFWriter::writeSymbolTable() {801 // Now write the symbol table.802 // First, the feat symbol.803 auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);804 coffnamecpy(Symbol->Name.ShortName, "@feat.00");805 Symbol->Value = 0x11;806 Symbol->SectionNumber = 0xffff;807 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;808 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;809 Symbol->NumberOfAuxSymbols = 0;810 CurrentOffset += sizeof(coff_symbol16);811 812 // Now write the .rsrc1 symbol + aux.813 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);814 coffnamecpy(Symbol->Name.ShortName, ".rsrc$01");815 Symbol->Value = 0;816 Symbol->SectionNumber = 1;817 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;818 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;819 Symbol->NumberOfAuxSymbols = 1;820 CurrentOffset += sizeof(coff_symbol16);821 auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +822 CurrentOffset);823 Aux->Length = SectionOneSize;824 Aux->NumberOfRelocations = Data.size();825 Aux->NumberOfLinenumbers = 0;826 Aux->CheckSum = 0;827 Aux->NumberLowPart = 0;828 Aux->Selection = 0;829 CurrentOffset += sizeof(coff_aux_section_definition);830 831 // Now write the .rsrc2 symbol + aux.832 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);833 coffnamecpy(Symbol->Name.ShortName, ".rsrc$02");834 Symbol->Value = 0;835 Symbol->SectionNumber = 2;836 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;837 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;838 Symbol->NumberOfAuxSymbols = 1;839 CurrentOffset += sizeof(coff_symbol16);840 Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart +841 CurrentOffset);842 Aux->Length = SectionTwoSize;843 Aux->NumberOfRelocations = 0;844 Aux->NumberOfLinenumbers = 0;845 Aux->CheckSum = 0;846 Aux->NumberLowPart = 0;847 Aux->Selection = 0;848 CurrentOffset += sizeof(coff_aux_section_definition);849 850 // Now write a symbol for each relocation.851 for (unsigned i = 0; i < Data.size(); i++) {852 auto RelocationName = formatv("$R{0:X-6}", i & 0xffffff).sstr<COFF::NameSize>();853 Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset);854 coffnamecpy(Symbol->Name.ShortName, RelocationName);855 Symbol->Value = DataOffsets[i];856 Symbol->SectionNumber = 2;857 Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL;858 Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;859 Symbol->NumberOfAuxSymbols = 0;860 CurrentOffset += sizeof(coff_symbol16);861 }862}863 864void WindowsResourceCOFFWriter::writeStringTable() {865 // Just 4 null bytes for the string table.866 auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset);867 memset(COFFStringTable, 0, 4);868}869 870void WindowsResourceCOFFWriter::writeDirectoryTree() {871 // Traverse parsed resource tree breadth-first and write the corresponding872 // COFF objects.873 std::queue<const WindowsResourceParser::TreeNode *> Queue;874 Queue.push(&Resources);875 uint32_t NextLevelOffset =876 sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() +877 Resources.getIDChildren().size()) *878 sizeof(coff_resource_dir_entry);879 std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder;880 uint32_t CurrentRelativeOffset = 0;881 882 while (!Queue.empty()) {883 auto CurrentNode = Queue.front();884 Queue.pop();885 auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart +886 CurrentOffset);887 Table->Characteristics = CurrentNode->getCharacteristics();888 Table->TimeDateStamp = 0;889 Table->MajorVersion = CurrentNode->getMajorVersion();890 Table->MinorVersion = CurrentNode->getMinorVersion();891 auto &IDChildren = CurrentNode->getIDChildren();892 auto &StringChildren = CurrentNode->getStringChildren();893 Table->NumberOfNameEntries = StringChildren.size();894 Table->NumberOfIDEntries = IDChildren.size();895 CurrentOffset += sizeof(coff_resource_dir_table);896 CurrentRelativeOffset += sizeof(coff_resource_dir_table);897 898 // Write the directory entries immediately following each directory table.899 for (auto const &Child : StringChildren) {900 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +901 CurrentOffset);902 Entry->Identifier.setNameOffset(903 StringTableOffsets[Child.second->getStringIndex()]);904 if (Child.second->checkIsDataNode()) {905 Entry->Offset.DataEntryOffset = NextLevelOffset;906 NextLevelOffset += sizeof(coff_resource_data_entry);907 DataEntriesTreeOrder.push_back(Child.second.get());908 } else {909 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);910 NextLevelOffset += sizeof(coff_resource_dir_table) +911 (Child.second->getStringChildren().size() +912 Child.second->getIDChildren().size()) *913 sizeof(coff_resource_dir_entry);914 Queue.push(Child.second.get());915 }916 CurrentOffset += sizeof(coff_resource_dir_entry);917 CurrentRelativeOffset += sizeof(coff_resource_dir_entry);918 }919 for (auto const &Child : IDChildren) {920 auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart +921 CurrentOffset);922 Entry->Identifier.ID = Child.first;923 if (Child.second->checkIsDataNode()) {924 Entry->Offset.DataEntryOffset = NextLevelOffset;925 NextLevelOffset += sizeof(coff_resource_data_entry);926 DataEntriesTreeOrder.push_back(Child.second.get());927 } else {928 Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31);929 NextLevelOffset += sizeof(coff_resource_dir_table) +930 (Child.second->getStringChildren().size() +931 Child.second->getIDChildren().size()) *932 sizeof(coff_resource_dir_entry);933 Queue.push(Child.second.get());934 }935 CurrentOffset += sizeof(coff_resource_dir_entry);936 CurrentRelativeOffset += sizeof(coff_resource_dir_entry);937 }938 }939 940 RelocationAddresses.resize(Data.size());941 // Now write all the resource data entries.942 for (const auto *DataNodes : DataEntriesTreeOrder) {943 auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart +944 CurrentOffset);945 RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset;946 Entry->DataRVA = 0; // Set to zero because it is a relocation.947 Entry->DataSize = Data[DataNodes->getDataIndex()].size();948 Entry->Codepage = 0;949 Entry->Reserved = 0;950 CurrentOffset += sizeof(coff_resource_data_entry);951 CurrentRelativeOffset += sizeof(coff_resource_data_entry);952 }953}954 955void WindowsResourceCOFFWriter::writeDirectoryStringTable() {956 // Now write the directory string table for .rsrc$01957 uint32_t TotalStringTableSize = 0;958 for (auto &String : StringTable) {959 uint16_t Length = String.size();960 support::endian::write16le(BufferStart + CurrentOffset, Length);961 CurrentOffset += sizeof(uint16_t);962 auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset);963 llvm::copy(String, Start);964 CurrentOffset += Length * sizeof(UTF16);965 TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t);966 }967 CurrentOffset +=968 alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize;969}970 971void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {972 973 // Now write the relocations for .rsrc$01974 // Five symbols already in table before we start, @feat.00 and 2 for each975 // .rsrc section.976 uint32_t NextSymbolIndex = 5;977 for (unsigned i = 0; i < Data.size(); i++) {978 auto *Reloc =979 reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset);980 Reloc->VirtualAddress = RelocationAddresses[i];981 Reloc->SymbolTableIndex = NextSymbolIndex++;982 switch (getMachineArchType(MachineType)) {983 case Triple::thumb:984 Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB;985 break;986 case Triple::x86_64:987 Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB;988 break;989 case Triple::x86:990 Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB;991 break;992 case Triple::aarch64:993 Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB;994 break;995 default:996 llvm_unreachable("unknown machine type");997 }998 CurrentOffset += sizeof(coff_relocation);999 }1000}1001 1002Expected<std::unique_ptr<MemoryBuffer>>1003writeWindowsResourceCOFF(COFF::MachineTypes MachineType,1004 const WindowsResourceParser &Parser,1005 uint32_t TimeDateStamp) {1006 Error E = Error::success();1007 WindowsResourceCOFFWriter Writer(MachineType, Parser, E);1008 if (E)1009 return E;1010 return Writer.write(TimeDateStamp);1011}1012 1013} // namespace object1014} // namespace llvm1015