WindowsResource.cpp revision 360784
1318663Sdim//===-- WindowsResource.cpp -------------------------------------*- C++ -*-===// 2318663Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6318663Sdim// 7318663Sdim//===----------------------------------------------------------------------===// 8318663Sdim// 9318663Sdim// This file implements the .res file class. 10318663Sdim// 11318663Sdim//===----------------------------------------------------------------------===// 12318663Sdim 13318663Sdim#include "llvm/Object/WindowsResource.h" 14319799Sdim#include "llvm/Object/COFF.h" 15319799Sdim#include "llvm/Support/FileOutputBuffer.h" 16327952Sdim#include "llvm/Support/FormatVariadic.h" 17319799Sdim#include "llvm/Support/MathExtras.h" 18353358Sdim#include "llvm/Support/ScopedPrinter.h" 19319799Sdim#include <ctime> 20319799Sdim#include <queue> 21318663Sdim#include <system_error> 22318663Sdim 23320397Sdimusing namespace llvm; 24320397Sdimusing namespace object; 25320397Sdim 26318663Sdimnamespace llvm { 27318663Sdimnamespace object { 28318663Sdim 29319479Sdim#define RETURN_IF_ERROR(X) \ 30319479Sdim if (auto EC = X) \ 31319479Sdim return EC; 32319479Sdim 33360784Sdim#define UNWRAP_REF_OR_RETURN(Name, Expr) \ 34360784Sdim auto Name##OrErr = Expr; \ 35360784Sdim if (!Name##OrErr) \ 36360784Sdim return Name##OrErr.takeError(); \ 37360784Sdim const auto &Name = *Name##OrErr; 38360784Sdim 39360784Sdim#define UNWRAP_OR_RETURN(Name, Expr) \ 40360784Sdim auto Name##OrErr = Expr; \ 41360784Sdim if (!Name##OrErr) \ 42360784Sdim return Name##OrErr.takeError(); \ 43360784Sdim auto Name = *Name##OrErr; 44360784Sdim 45319479Sdimconst uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t); 46319479Sdim 47320041Sdim// COFF files seem to be inconsistent with alignment between sections, just use 48320041Sdim// 8-byte because it makes everyone happy. 49320041Sdimconst uint32_t SECTION_ALIGNMENT = sizeof(uint64_t); 50320041Sdim 51318663SdimWindowsResource::WindowsResource(MemoryBufferRef Source) 52318663Sdim : Binary(Binary::ID_WinRes, Source) { 53320397Sdim size_t LeadingSize = WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE; 54318663Sdim BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize), 55318663Sdim support::little); 56318663Sdim} 57318663Sdim 58353358Sdim// static 59318663SdimExpected<std::unique_ptr<WindowsResource>> 60318663SdimWindowsResource::createWindowsResource(MemoryBufferRef Source) { 61320397Sdim if (Source.getBufferSize() < WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE) 62318663Sdim return make_error<GenericBinaryError>( 63353358Sdim Source.getBufferIdentifier() + ": too small to be a resource file", 64318663Sdim object_error::invalid_file_type); 65318663Sdim std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source)); 66318663Sdim return std::move(Ret); 67318663Sdim} 68318663Sdim 69318663SdimExpected<ResourceEntryRef> WindowsResource::getHeadEntry() { 70327952Sdim if (BBS.getLength() < sizeof(WinResHeaderPrefix) + sizeof(WinResHeaderSuffix)) 71353358Sdim return make_error<EmptyResError>(getFileName() + " contains no entries", 72327952Sdim object_error::unexpected_eof); 73327952Sdim return ResourceEntryRef::create(BinaryStreamRef(BBS), this); 74318663Sdim} 75318663Sdim 76318663SdimResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref, 77327952Sdim const WindowsResource *Owner) 78353358Sdim : Reader(Ref), Owner(Owner) {} 79327952Sdim 80327952SdimExpected<ResourceEntryRef> 81327952SdimResourceEntryRef::create(BinaryStreamRef BSR, const WindowsResource *Owner) { 82327952Sdim auto Ref = ResourceEntryRef(BSR, Owner); 83327952Sdim if (auto E = Ref.loadNext()) 84327952Sdim return std::move(E); 85327952Sdim return Ref; 86318663Sdim} 87318663Sdim 88318663SdimError ResourceEntryRef::moveNext(bool &End) { 89318663Sdim // Reached end of all the entries. 90318663Sdim if (Reader.bytesRemaining() == 0) { 91318663Sdim End = true; 92318663Sdim return Error::success(); 93318663Sdim } 94318663Sdim RETURN_IF_ERROR(loadNext()); 95318663Sdim 96318663Sdim return Error::success(); 97318663Sdim} 98318663Sdim 99319479Sdimstatic Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID, 100319479Sdim ArrayRef<UTF16> &Str, bool &IsString) { 101319479Sdim uint16_t IDFlag; 102319479Sdim RETURN_IF_ERROR(Reader.readInteger(IDFlag)); 103319479Sdim IsString = IDFlag != 0xffff; 104319479Sdim 105319479Sdim if (IsString) { 106319479Sdim Reader.setOffset( 107319479Sdim Reader.getOffset() - 108319479Sdim sizeof(uint16_t)); // Re-read the bytes which we used to check the flag. 109319479Sdim RETURN_IF_ERROR(Reader.readWideString(Str)); 110319479Sdim } else 111319479Sdim RETURN_IF_ERROR(Reader.readInteger(ID)); 112319479Sdim 113319479Sdim return Error::success(); 114319479Sdim} 115319479Sdim 116318663SdimError ResourceEntryRef::loadNext() { 117320397Sdim const WinResHeaderPrefix *Prefix; 118320397Sdim RETURN_IF_ERROR(Reader.readObject(Prefix)); 119319479Sdim 120320397Sdim if (Prefix->HeaderSize < MIN_HEADER_SIZE) 121353358Sdim return make_error<GenericBinaryError>(Owner->getFileName() + 122353358Sdim ": header size too small", 123319479Sdim object_error::parse_failed); 124319479Sdim 125319479Sdim RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType)); 126319479Sdim 127319479Sdim RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName)); 128319479Sdim 129320397Sdim RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_HEADER_ALIGNMENT)); 130319479Sdim 131319479Sdim RETURN_IF_ERROR(Reader.readObject(Suffix)); 132319479Sdim 133320397Sdim RETURN_IF_ERROR(Reader.readArray(Data, Prefix->DataSize)); 134319479Sdim 135320397Sdim RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_DATA_ALIGNMENT)); 136319479Sdim 137318663Sdim return Error::success(); 138318663Sdim} 139318663Sdim 140360784SdimWindowsResourceParser::WindowsResourceParser(bool MinGW) 141360784Sdim : Root(false), MinGW(MinGW) {} 142319479Sdim 143353358Sdimvoid printResourceTypeName(uint16_t TypeID, raw_ostream &OS) { 144353358Sdim switch (TypeID) { 145353358Sdim case 1: OS << "CURSOR (ID 1)"; break; 146353358Sdim case 2: OS << "BITMAP (ID 2)"; break; 147353358Sdim case 3: OS << "ICON (ID 3)"; break; 148353358Sdim case 4: OS << "MENU (ID 4)"; break; 149353358Sdim case 5: OS << "DIALOG (ID 5)"; break; 150353358Sdim case 6: OS << "STRINGTABLE (ID 6)"; break; 151353358Sdim case 7: OS << "FONTDIR (ID 7)"; break; 152353358Sdim case 8: OS << "FONT (ID 8)"; break; 153353358Sdim case 9: OS << "ACCELERATOR (ID 9)"; break; 154353358Sdim case 10: OS << "RCDATA (ID 10)"; break; 155353358Sdim case 11: OS << "MESSAGETABLE (ID 11)"; break; 156353358Sdim case 12: OS << "GROUP_CURSOR (ID 12)"; break; 157353358Sdim case 14: OS << "GROUP_ICON (ID 14)"; break; 158353358Sdim case 16: OS << "VERSIONINFO (ID 16)"; break; 159353358Sdim case 17: OS << "DLGINCLUDE (ID 17)"; break; 160353358Sdim case 19: OS << "PLUGPLAY (ID 19)"; break; 161353358Sdim case 20: OS << "VXD (ID 20)"; break; 162353358Sdim case 21: OS << "ANICURSOR (ID 21)"; break; 163353358Sdim case 22: OS << "ANIICON (ID 22)"; break; 164353358Sdim case 23: OS << "HTML (ID 23)"; break; 165353358Sdim case 24: OS << "MANIFEST (ID 24)"; break; 166353358Sdim default: OS << "ID " << TypeID; break; 167353358Sdim } 168353358Sdim} 169353358Sdim 170353358Sdimstatic bool convertUTF16LEToUTF8String(ArrayRef<UTF16> Src, std::string &Out) { 171353358Sdim if (!sys::IsBigEndianHost) 172353358Sdim return convertUTF16ToUTF8String(Src, Out); 173353358Sdim 174353358Sdim std::vector<UTF16> EndianCorrectedSrc; 175353358Sdim EndianCorrectedSrc.resize(Src.size() + 1); 176353358Sdim llvm::copy(Src, EndianCorrectedSrc.begin() + 1); 177353358Sdim EndianCorrectedSrc[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED; 178353358Sdim return convertUTF16ToUTF8String(makeArrayRef(EndianCorrectedSrc), Out); 179353358Sdim} 180353358Sdim 181353358Sdimstatic std::string makeDuplicateResourceError( 182353358Sdim const ResourceEntryRef &Entry, StringRef File1, StringRef File2) { 183353358Sdim std::string Ret; 184353358Sdim raw_string_ostream OS(Ret); 185353358Sdim 186353358Sdim OS << "duplicate resource:"; 187353358Sdim 188353358Sdim OS << " type "; 189353358Sdim if (Entry.checkTypeString()) { 190353358Sdim std::string UTF8; 191353358Sdim if (!convertUTF16LEToUTF8String(Entry.getTypeString(), UTF8)) 192353358Sdim UTF8 = "(failed conversion from UTF16)"; 193353358Sdim OS << '\"' << UTF8 << '\"'; 194353358Sdim } else 195353358Sdim printResourceTypeName(Entry.getTypeID(), OS); 196353358Sdim 197353358Sdim OS << "/name "; 198353358Sdim if (Entry.checkNameString()) { 199353358Sdim std::string UTF8; 200353358Sdim if (!convertUTF16LEToUTF8String(Entry.getNameString(), UTF8)) 201353358Sdim UTF8 = "(failed conversion from UTF16)"; 202353358Sdim OS << '\"' << UTF8 << '\"'; 203353358Sdim } else { 204353358Sdim OS << "ID " << Entry.getNameID(); 205353358Sdim } 206353358Sdim 207353358Sdim OS << "/language " << Entry.getLanguage() << ", in " << File1 << " and in " 208353358Sdim << File2; 209353358Sdim 210353358Sdim return OS.str(); 211353358Sdim} 212353358Sdim 213360784Sdimstatic void printStringOrID(const WindowsResourceParser::StringOrID &S, 214360784Sdim raw_string_ostream &OS, bool IsType, bool IsID) { 215360784Sdim if (S.IsString) { 216360784Sdim std::string UTF8; 217360784Sdim if (!convertUTF16LEToUTF8String(S.String, UTF8)) 218360784Sdim UTF8 = "(failed conversion from UTF16)"; 219360784Sdim OS << '\"' << UTF8 << '\"'; 220360784Sdim } else if (IsType) 221360784Sdim printResourceTypeName(S.ID, OS); 222360784Sdim else if (IsID) 223360784Sdim OS << "ID " << S.ID; 224360784Sdim else 225360784Sdim OS << S.ID; 226360784Sdim} 227360784Sdim 228360784Sdimstatic std::string makeDuplicateResourceError( 229360784Sdim const std::vector<WindowsResourceParser::StringOrID> &Context, 230360784Sdim StringRef File1, StringRef File2) { 231360784Sdim std::string Ret; 232360784Sdim raw_string_ostream OS(Ret); 233360784Sdim 234360784Sdim OS << "duplicate resource:"; 235360784Sdim 236360784Sdim if (Context.size() >= 1) { 237360784Sdim OS << " type "; 238360784Sdim printStringOrID(Context[0], OS, /* IsType */ true, /* IsID */ true); 239360784Sdim } 240360784Sdim 241360784Sdim if (Context.size() >= 2) { 242360784Sdim OS << "/name "; 243360784Sdim printStringOrID(Context[1], OS, /* IsType */ false, /* IsID */ true); 244360784Sdim } 245360784Sdim 246360784Sdim if (Context.size() >= 3) { 247360784Sdim OS << "/language "; 248360784Sdim printStringOrID(Context[2], OS, /* IsType */ false, /* IsID */ false); 249360784Sdim } 250360784Sdim OS << ", in " << File1 << " and in " << File2; 251360784Sdim 252360784Sdim return OS.str(); 253360784Sdim} 254360784Sdim 255360784Sdim// MinGW specific. Remove default manifests (with language zero) if there are 256360784Sdim// other manifests present, and report an error if there are more than one 257360784Sdim// manifest with a non-zero language code. 258360784Sdim// GCC has the concept of a default manifest resource object, which gets 259360784Sdim// linked in implicitly if present. This default manifest has got language 260360784Sdim// id zero, and should be dropped silently if there's another manifest present. 261360784Sdim// If the user resources surprisignly had a manifest with language id zero, 262360784Sdim// we should also ignore the duplicate default manifest. 263360784Sdimvoid WindowsResourceParser::cleanUpManifests( 264360784Sdim std::vector<std::string> &Duplicates) { 265360784Sdim auto TypeIt = Root.IDChildren.find(/* RT_MANIFEST */ 24); 266360784Sdim if (TypeIt == Root.IDChildren.end()) 267360784Sdim return; 268360784Sdim 269360784Sdim TreeNode *TypeNode = TypeIt->second.get(); 270360784Sdim auto NameIt = 271360784Sdim TypeNode->IDChildren.find(/* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1); 272360784Sdim if (NameIt == TypeNode->IDChildren.end()) 273360784Sdim return; 274360784Sdim 275360784Sdim TreeNode *NameNode = NameIt->second.get(); 276360784Sdim if (NameNode->IDChildren.size() <= 1) 277360784Sdim return; // None or one manifest present, all good. 278360784Sdim 279360784Sdim // If we have more than one manifest, drop the language zero one if present, 280360784Sdim // and check again. 281360784Sdim auto LangZeroIt = NameNode->IDChildren.find(0); 282360784Sdim if (LangZeroIt != NameNode->IDChildren.end() && 283360784Sdim LangZeroIt->second->IsDataNode) { 284360784Sdim uint32_t RemovedIndex = LangZeroIt->second->DataIndex; 285360784Sdim NameNode->IDChildren.erase(LangZeroIt); 286360784Sdim Data.erase(Data.begin() + RemovedIndex); 287360784Sdim Root.shiftDataIndexDown(RemovedIndex); 288360784Sdim 289360784Sdim // If we're now down to one manifest, all is good. 290360784Sdim if (NameNode->IDChildren.size() <= 1) 291360784Sdim return; 292360784Sdim } 293360784Sdim 294360784Sdim // More than one non-language-zero manifest 295360784Sdim auto FirstIt = NameNode->IDChildren.begin(); 296360784Sdim uint32_t FirstLang = FirstIt->first; 297360784Sdim TreeNode *FirstNode = FirstIt->second.get(); 298360784Sdim auto LastIt = NameNode->IDChildren.rbegin(); 299360784Sdim uint32_t LastLang = LastIt->first; 300360784Sdim TreeNode *LastNode = LastIt->second.get(); 301360784Sdim Duplicates.push_back( 302360784Sdim ("duplicate non-default manifests with languages " + Twine(FirstLang) + 303360784Sdim " in " + InputFilenames[FirstNode->Origin] + " and " + Twine(LastLang) + 304360784Sdim " in " + InputFilenames[LastNode->Origin]) 305360784Sdim .str()); 306360784Sdim} 307360784Sdim 308360784Sdim// Ignore duplicates of manifests with language zero (the default manifest), 309360784Sdim// in case the user has provided a manifest with that language id. See 310360784Sdim// the function comment above for context. Only returns true if MinGW is set 311360784Sdim// to true. 312360784Sdimbool WindowsResourceParser::shouldIgnoreDuplicate( 313360784Sdim const ResourceEntryRef &Entry) const { 314360784Sdim return MinGW && !Entry.checkTypeString() && 315360784Sdim Entry.getTypeID() == /* RT_MANIFEST */ 24 && 316360784Sdim !Entry.checkNameString() && 317360784Sdim Entry.getNameID() == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 && 318360784Sdim Entry.getLanguage() == 0; 319360784Sdim} 320360784Sdim 321360784Sdimbool WindowsResourceParser::shouldIgnoreDuplicate( 322360784Sdim const std::vector<StringOrID> &Context) const { 323360784Sdim return MinGW && Context.size() == 3 && !Context[0].IsString && 324360784Sdim Context[0].ID == /* RT_MANIFEST */ 24 && !Context[1].IsString && 325360784Sdim Context[1].ID == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 && 326360784Sdim !Context[2].IsString && Context[2].ID == 0; 327360784Sdim} 328360784Sdim 329353358SdimError WindowsResourceParser::parse(WindowsResource *WR, 330353358Sdim std::vector<std::string> &Duplicates) { 331319479Sdim auto EntryOrErr = WR->getHeadEntry(); 332327952Sdim if (!EntryOrErr) { 333327952Sdim auto E = EntryOrErr.takeError(); 334327952Sdim if (E.isA<EmptyResError>()) { 335327952Sdim // Check if the .res file contains no entries. In this case we don't have 336327952Sdim // to throw an error but can rather just return without parsing anything. 337327952Sdim // This applies for files which have a valid PE header magic and the 338327952Sdim // mandatory empty null resource entry. Files which do not fit this 339327952Sdim // criteria would have already been filtered out by 340327952Sdim // WindowsResource::createWindowsResource(). 341327952Sdim consumeError(std::move(E)); 342327952Sdim return Error::success(); 343327952Sdim } 344327952Sdim return E; 345327952Sdim } 346319479Sdim 347319479Sdim ResourceEntryRef Entry = EntryOrErr.get(); 348360784Sdim uint32_t Origin = InputFilenames.size(); 349360784Sdim InputFilenames.push_back(WR->getFileName()); 350319479Sdim bool End = false; 351319479Sdim while (!End) { 352319799Sdim 353360784Sdim TreeNode *Node; 354360784Sdim bool IsNewNode = Root.addEntry(Entry, Origin, Data, StringTable, Node); 355353358Sdim if (!IsNewNode) { 356360784Sdim if (!shouldIgnoreDuplicate(Entry)) 357360784Sdim Duplicates.push_back(makeDuplicateResourceError( 358360784Sdim Entry, InputFilenames[Node->Origin], WR->getFileName())); 359353358Sdim } 360320041Sdim 361319479Sdim RETURN_IF_ERROR(Entry.moveNext(End)); 362319479Sdim } 363319479Sdim 364319479Sdim return Error::success(); 365319479Sdim} 366319479Sdim 367360784SdimError WindowsResourceParser::parse(ResourceSectionRef &RSR, StringRef Filename, 368360784Sdim std::vector<std::string> &Duplicates) { 369360784Sdim UNWRAP_REF_OR_RETURN(BaseTable, RSR.getBaseTable()); 370360784Sdim uint32_t Origin = InputFilenames.size(); 371360784Sdim InputFilenames.push_back(Filename); 372360784Sdim std::vector<StringOrID> Context; 373360784Sdim return addChildren(Root, RSR, BaseTable, Origin, Context, Duplicates); 374360784Sdim} 375360784Sdim 376320041Sdimvoid WindowsResourceParser::printTree(raw_ostream &OS) const { 377320041Sdim ScopedPrinter Writer(OS); 378319479Sdim Root.print(Writer, "Resource Tree"); 379319479Sdim} 380319479Sdim 381360784Sdimbool WindowsResourceParser::TreeNode::addEntry( 382360784Sdim const ResourceEntryRef &Entry, uint32_t Origin, 383360784Sdim std::vector<std::vector<uint8_t>> &Data, 384360784Sdim std::vector<std::vector<UTF16>> &StringTable, TreeNode *&Result) { 385360784Sdim TreeNode &TypeNode = addTypeNode(Entry, StringTable); 386360784Sdim TreeNode &NameNode = TypeNode.addNameNode(Entry, StringTable); 387360784Sdim return NameNode.addLanguageNode(Entry, Origin, Data, Result); 388319479Sdim} 389319479Sdim 390360784SdimError WindowsResourceParser::addChildren(TreeNode &Node, 391360784Sdim ResourceSectionRef &RSR, 392360784Sdim const coff_resource_dir_table &Table, 393360784Sdim uint32_t Origin, 394360784Sdim std::vector<StringOrID> &Context, 395360784Sdim std::vector<std::string> &Duplicates) { 396360784Sdim 397360784Sdim for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries; 398360784Sdim i++) { 399360784Sdim UNWRAP_REF_OR_RETURN(Entry, RSR.getTableEntry(Table, i)); 400360784Sdim TreeNode *Child; 401360784Sdim 402360784Sdim if (Entry.Offset.isSubDir()) { 403360784Sdim 404360784Sdim // Create a new subdirectory and recurse 405360784Sdim if (i < Table.NumberOfNameEntries) { 406360784Sdim UNWRAP_OR_RETURN(NameString, RSR.getEntryNameString(Entry)); 407360784Sdim Child = &Node.addNameChild(NameString, StringTable); 408360784Sdim Context.push_back(StringOrID(NameString)); 409360784Sdim } else { 410360784Sdim Child = &Node.addIDChild(Entry.Identifier.ID); 411360784Sdim Context.push_back(StringOrID(Entry.Identifier.ID)); 412360784Sdim } 413360784Sdim 414360784Sdim UNWRAP_REF_OR_RETURN(NextTable, RSR.getEntrySubDir(Entry)); 415360784Sdim Error E = 416360784Sdim addChildren(*Child, RSR, NextTable, Origin, Context, Duplicates); 417360784Sdim if (E) 418360784Sdim return E; 419360784Sdim Context.pop_back(); 420360784Sdim 421360784Sdim } else { 422360784Sdim 423360784Sdim // Data leaves are supposed to have a numeric ID as identifier (language). 424360784Sdim if (Table.NumberOfNameEntries > 0) 425360784Sdim return createStringError(object_error::parse_failed, 426360784Sdim "unexpected string key for data object"); 427360784Sdim 428360784Sdim // Try adding a data leaf 429360784Sdim UNWRAP_REF_OR_RETURN(DataEntry, RSR.getEntryData(Entry)); 430360784Sdim TreeNode *Child; 431360784Sdim Context.push_back(StringOrID(Entry.Identifier.ID)); 432360784Sdim bool Added = Node.addDataChild(Entry.Identifier.ID, Table.MajorVersion, 433360784Sdim Table.MinorVersion, Table.Characteristics, 434360784Sdim Origin, Data.size(), Child); 435360784Sdim if (Added) { 436360784Sdim UNWRAP_OR_RETURN(Contents, RSR.getContents(DataEntry)); 437360784Sdim Data.push_back(ArrayRef<uint8_t>( 438360784Sdim reinterpret_cast<const uint8_t *>(Contents.data()), 439360784Sdim Contents.size())); 440360784Sdim } else { 441360784Sdim if (!shouldIgnoreDuplicate(Context)) 442360784Sdim Duplicates.push_back(makeDuplicateResourceError( 443360784Sdim Context, InputFilenames[Child->Origin], InputFilenames.back())); 444360784Sdim } 445360784Sdim Context.pop_back(); 446360784Sdim 447360784Sdim } 448360784Sdim } 449360784Sdim return Error::success(); 450319799Sdim} 451319479Sdim 452360784SdimWindowsResourceParser::TreeNode::TreeNode(uint32_t StringIndex) 453360784Sdim : StringIndex(StringIndex) {} 454360784Sdim 455319799SdimWindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion, 456319799Sdim uint16_t MinorVersion, 457353358Sdim uint32_t Characteristics, 458360784Sdim uint32_t Origin, uint32_t DataIndex) 459360784Sdim : IsDataNode(true), DataIndex(DataIndex), MajorVersion(MajorVersion), 460360784Sdim MinorVersion(MinorVersion), Characteristics(Characteristics), 461360784Sdim Origin(Origin) {} 462319799Sdim 463319799Sdimstd::unique_ptr<WindowsResourceParser::TreeNode> 464360784SdimWindowsResourceParser::TreeNode::createStringNode(uint32_t Index) { 465360784Sdim return std::unique_ptr<TreeNode>(new TreeNode(Index)); 466319799Sdim} 467319799Sdim 468319799Sdimstd::unique_ptr<WindowsResourceParser::TreeNode> 469319799SdimWindowsResourceParser::TreeNode::createIDNode() { 470360784Sdim return std::unique_ptr<TreeNode>(new TreeNode(0)); 471319799Sdim} 472319799Sdim 473319799Sdimstd::unique_ptr<WindowsResourceParser::TreeNode> 474319799SdimWindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion, 475319799Sdim uint16_t MinorVersion, 476353358Sdim uint32_t Characteristics, 477360784Sdim uint32_t Origin, 478360784Sdim uint32_t DataIndex) { 479360784Sdim return std::unique_ptr<TreeNode>(new TreeNode( 480360784Sdim MajorVersion, MinorVersion, Characteristics, Origin, DataIndex)); 481319799Sdim} 482319799Sdim 483360784SdimWindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addTypeNode( 484360784Sdim const ResourceEntryRef &Entry, 485360784Sdim std::vector<std::vector<UTF16>> &StringTable) { 486319479Sdim if (Entry.checkTypeString()) 487360784Sdim return addNameChild(Entry.getTypeString(), StringTable); 488319479Sdim else 489353358Sdim return addIDChild(Entry.getTypeID()); 490319479Sdim} 491319479Sdim 492360784SdimWindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameNode( 493360784Sdim const ResourceEntryRef &Entry, 494360784Sdim std::vector<std::vector<UTF16>> &StringTable) { 495319479Sdim if (Entry.checkNameString()) 496360784Sdim return addNameChild(Entry.getNameString(), StringTable); 497319479Sdim else 498353358Sdim return addIDChild(Entry.getNameID()); 499319479Sdim} 500319479Sdim 501353358Sdimbool WindowsResourceParser::TreeNode::addLanguageNode( 502360784Sdim const ResourceEntryRef &Entry, uint32_t Origin, 503360784Sdim std::vector<std::vector<uint8_t>> &Data, TreeNode *&Result) { 504360784Sdim bool Added = addDataChild(Entry.getLanguage(), Entry.getMajorVersion(), 505360784Sdim Entry.getMinorVersion(), Entry.getCharacteristics(), 506360784Sdim Origin, Data.size(), Result); 507360784Sdim if (Added) 508360784Sdim Data.push_back(Entry.getData()); 509360784Sdim return Added; 510319479Sdim} 511319479Sdim 512353358Sdimbool WindowsResourceParser::TreeNode::addDataChild( 513353358Sdim uint32_t ID, uint16_t MajorVersion, uint16_t MinorVersion, 514360784Sdim uint32_t Characteristics, uint32_t Origin, uint32_t DataIndex, 515360784Sdim TreeNode *&Result) { 516360784Sdim auto NewChild = createDataNode(MajorVersion, MinorVersion, Characteristics, 517360784Sdim Origin, DataIndex); 518353358Sdim auto ElementInserted = IDChildren.emplace(ID, std::move(NewChild)); 519353358Sdim Result = ElementInserted.first->second.get(); 520353358Sdim return ElementInserted.second; 521353358Sdim} 522353358Sdim 523353358SdimWindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addIDChild( 524353358Sdim uint32_t ID) { 525319479Sdim auto Child = IDChildren.find(ID); 526319479Sdim if (Child == IDChildren.end()) { 527353358Sdim auto NewChild = createIDNode(); 528319479Sdim WindowsResourceParser::TreeNode &Node = *NewChild; 529319479Sdim IDChildren.emplace(ID, std::move(NewChild)); 530319479Sdim return Node; 531319479Sdim } else 532319479Sdim return *(Child->second); 533319479Sdim} 534319479Sdim 535360784SdimWindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameChild( 536360784Sdim ArrayRef<UTF16> NameRef, std::vector<std::vector<UTF16>> &StringTable) { 537319479Sdim std::string NameString; 538353358Sdim convertUTF16LEToUTF8String(NameRef, NameString); 539319479Sdim 540319479Sdim auto Child = StringChildren.find(NameString); 541319479Sdim if (Child == StringChildren.end()) { 542360784Sdim auto NewChild = createStringNode(StringTable.size()); 543360784Sdim StringTable.push_back(NameRef); 544319479Sdim WindowsResourceParser::TreeNode &Node = *NewChild; 545319479Sdim StringChildren.emplace(NameString, std::move(NewChild)); 546319479Sdim return Node; 547319479Sdim } else 548319479Sdim return *(Child->second); 549319479Sdim} 550319479Sdim 551319479Sdimvoid WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer, 552319479Sdim StringRef Name) const { 553319479Sdim ListScope NodeScope(Writer, Name); 554319479Sdim for (auto const &Child : StringChildren) { 555319479Sdim Child.second->print(Writer, Child.first); 556319479Sdim } 557319479Sdim for (auto const &Child : IDChildren) { 558319479Sdim Child.second->print(Writer, to_string(Child.first)); 559319479Sdim } 560319479Sdim} 561319479Sdim 562319799Sdim// This function returns the size of the entire resource tree, including 563319799Sdim// directory tables, directory entries, and data entries. It does not include 564319799Sdim// the directory strings or the relocations of the .rsrc section. 565319799Sdimuint32_t WindowsResourceParser::TreeNode::getTreeSize() const { 566319799Sdim uint32_t Size = (IDChildren.size() + StringChildren.size()) * 567320397Sdim sizeof(coff_resource_dir_entry); 568319799Sdim 569319799Sdim // Reached a node pointing to a data entry. 570319799Sdim if (IsDataNode) { 571320397Sdim Size += sizeof(coff_resource_data_entry); 572319799Sdim return Size; 573319799Sdim } 574319799Sdim 575319799Sdim // If the node does not point to data, it must have a directory table pointing 576319799Sdim // to other nodes. 577320397Sdim Size += sizeof(coff_resource_dir_table); 578319799Sdim 579319799Sdim for (auto const &Child : StringChildren) { 580319799Sdim Size += Child.second->getTreeSize(); 581319799Sdim } 582319799Sdim for (auto const &Child : IDChildren) { 583319799Sdim Size += Child.second->getTreeSize(); 584319799Sdim } 585319799Sdim return Size; 586319799Sdim} 587319799Sdim 588360784Sdim// Shift DataIndex of all data children with an Index greater or equal to the 589360784Sdim// given one, to fill a gap from removing an entry from the Data vector. 590360784Sdimvoid WindowsResourceParser::TreeNode::shiftDataIndexDown(uint32_t Index) { 591360784Sdim if (IsDataNode && DataIndex >= Index) { 592360784Sdim DataIndex--; 593360784Sdim } else { 594360784Sdim for (auto &Child : IDChildren) 595360784Sdim Child.second->shiftDataIndexDown(Index); 596360784Sdim for (auto &Child : StringChildren) 597360784Sdim Child.second->shiftDataIndexDown(Index); 598360784Sdim } 599360784Sdim} 600360784Sdim 601319799Sdimclass WindowsResourceCOFFWriter { 602319799Sdimpublic: 603320397Sdim WindowsResourceCOFFWriter(COFF::MachineTypes MachineType, 604319799Sdim const WindowsResourceParser &Parser, Error &E); 605353358Sdim std::unique_ptr<MemoryBuffer> write(uint32_t TimeDateStamp); 606319799Sdim 607319799Sdimprivate: 608319799Sdim void performFileLayout(); 609319799Sdim void performSectionOneLayout(); 610319799Sdim void performSectionTwoLayout(); 611353358Sdim void writeCOFFHeader(uint32_t TimeDateStamp); 612319799Sdim void writeFirstSectionHeader(); 613319799Sdim void writeSecondSectionHeader(); 614319799Sdim void writeFirstSection(); 615319799Sdim void writeSecondSection(); 616319799Sdim void writeSymbolTable(); 617319799Sdim void writeStringTable(); 618319799Sdim void writeDirectoryTree(); 619319799Sdim void writeDirectoryStringTable(); 620319799Sdim void writeFirstSectionRelocations(); 621341825Sdim std::unique_ptr<WritableMemoryBuffer> OutputBuffer; 622320397Sdim char *BufferStart; 623320041Sdim uint64_t CurrentOffset = 0; 624320397Sdim COFF::MachineTypes MachineType; 625319799Sdim const WindowsResourceParser::TreeNode &Resources; 626319799Sdim const ArrayRef<std::vector<uint8_t>> Data; 627319799Sdim uint64_t FileSize; 628319799Sdim uint32_t SymbolTableOffset; 629319799Sdim uint32_t SectionOneSize; 630319799Sdim uint32_t SectionOneOffset; 631319799Sdim uint32_t SectionOneRelocations; 632319799Sdim uint32_t SectionTwoSize; 633319799Sdim uint32_t SectionTwoOffset; 634319799Sdim const ArrayRef<std::vector<UTF16>> StringTable; 635319799Sdim std::vector<uint32_t> StringTableOffsets; 636319799Sdim std::vector<uint32_t> DataOffsets; 637319799Sdim std::vector<uint32_t> RelocationAddresses; 638319799Sdim}; 639319799Sdim 640319799SdimWindowsResourceCOFFWriter::WindowsResourceCOFFWriter( 641320397Sdim COFF::MachineTypes MachineType, const WindowsResourceParser &Parser, 642320397Sdim Error &E) 643319799Sdim : MachineType(MachineType), Resources(Parser.getTree()), 644319799Sdim Data(Parser.getData()), StringTable(Parser.getStringTable()) { 645319799Sdim performFileLayout(); 646319799Sdim 647353358Sdim OutputBuffer = WritableMemoryBuffer::getNewMemBuffer( 648353358Sdim FileSize, "internal .obj file created from .res files"); 649319799Sdim} 650319799Sdim 651319799Sdimvoid WindowsResourceCOFFWriter::performFileLayout() { 652319799Sdim // Add size of COFF header. 653320397Sdim FileSize = COFF::Header16Size; 654319799Sdim 655319799Sdim // one .rsrc section header for directory tree, another for resource data. 656320397Sdim FileSize += 2 * COFF::SectionSize; 657319799Sdim 658319799Sdim performSectionOneLayout(); 659319799Sdim performSectionTwoLayout(); 660319799Sdim 661319799Sdim // We have reached the address of the symbol table. 662319799Sdim SymbolTableOffset = FileSize; 663319799Sdim 664320397Sdim FileSize += COFF::Symbol16Size; // size of the @feat.00 symbol. 665320397Sdim FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section. 666320397Sdim FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource. 667319799Sdim FileSize += 4; // four null bytes for the string table. 668319799Sdim} 669319799Sdim 670319799Sdimvoid WindowsResourceCOFFWriter::performSectionOneLayout() { 671319799Sdim SectionOneOffset = FileSize; 672319799Sdim 673319799Sdim SectionOneSize = Resources.getTreeSize(); 674319799Sdim uint32_t CurrentStringOffset = SectionOneSize; 675319799Sdim uint32_t TotalStringTableSize = 0; 676319799Sdim for (auto const &String : StringTable) { 677319799Sdim StringTableOffsets.push_back(CurrentStringOffset); 678319799Sdim uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t); 679319799Sdim CurrentStringOffset += StringSize; 680319799Sdim TotalStringTableSize += StringSize; 681319799Sdim } 682319799Sdim SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t)); 683319799Sdim 684319799Sdim // account for the relocations of section one. 685319799Sdim SectionOneRelocations = FileSize + SectionOneSize; 686319799Sdim FileSize += SectionOneSize; 687320397Sdim FileSize += 688320397Sdim Data.size() * COFF::RelocationSize; // one relocation for each resource. 689320041Sdim FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 690319799Sdim} 691319799Sdim 692319799Sdimvoid WindowsResourceCOFFWriter::performSectionTwoLayout() { 693319799Sdim // add size of .rsrc$2 section, which contains all resource data on 8-byte 694319799Sdim // alignment. 695319799Sdim SectionTwoOffset = FileSize; 696319799Sdim SectionTwoSize = 0; 697319799Sdim for (auto const &Entry : Data) { 698319799Sdim DataOffsets.push_back(SectionTwoSize); 699320397Sdim SectionTwoSize += alignTo(Entry.size(), sizeof(uint64_t)); 700319799Sdim } 701319799Sdim FileSize += SectionTwoSize; 702320041Sdim FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 703319799Sdim} 704319799Sdim 705353358Sdimstd::unique_ptr<MemoryBuffer> 706353358SdimWindowsResourceCOFFWriter::write(uint32_t TimeDateStamp) { 707341825Sdim BufferStart = OutputBuffer->getBufferStart(); 708319799Sdim 709353358Sdim writeCOFFHeader(TimeDateStamp); 710319799Sdim writeFirstSectionHeader(); 711319799Sdim writeSecondSectionHeader(); 712319799Sdim writeFirstSection(); 713319799Sdim writeSecondSection(); 714319799Sdim writeSymbolTable(); 715319799Sdim writeStringTable(); 716319799Sdim 717320397Sdim return std::move(OutputBuffer); 718319799Sdim} 719319799Sdim 720360784Sdim// According to COFF specification, if the Src has a size equal to Dest, 721360784Sdim// it's okay to *not* copy the trailing zero. 722360784Sdimstatic void coffnamecpy(char (&Dest)[COFF::NameSize], StringRef Src) { 723360784Sdim assert(Src.size() <= COFF::NameSize && 724360784Sdim "Src is not larger than COFF::NameSize"); 725360784Sdim strncpy(Dest, Src.data(), (size_t)COFF::NameSize); 726360784Sdim} 727360784Sdim 728353358Sdimvoid WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp) { 729319799Sdim // Write the COFF header. 730320397Sdim auto *Header = reinterpret_cast<coff_file_header *>(BufferStart); 731327952Sdim Header->Machine = MachineType; 732319799Sdim Header->NumberOfSections = 2; 733353358Sdim Header->TimeDateStamp = TimeDateStamp; 734319799Sdim Header->PointerToSymbolTable = SymbolTableOffset; 735353358Sdim // One symbol for every resource plus 2 for each section and 1 for @feat.00 736319799Sdim Header->NumberOfSymbols = Data.size() + 5; 737319799Sdim Header->SizeOfOptionalHeader = 0; 738353358Sdim // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it. 739320397Sdim Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE; 740319799Sdim} 741319799Sdim 742319799Sdimvoid WindowsResourceCOFFWriter::writeFirstSectionHeader() { 743319799Sdim // Write the first section header. 744320397Sdim CurrentOffset += sizeof(coff_file_header); 745320397Sdim auto *SectionOneHeader = 746320397Sdim reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 747360784Sdim coffnamecpy(SectionOneHeader->Name, ".rsrc$01"); 748319799Sdim SectionOneHeader->VirtualSize = 0; 749319799Sdim SectionOneHeader->VirtualAddress = 0; 750319799Sdim SectionOneHeader->SizeOfRawData = SectionOneSize; 751319799Sdim SectionOneHeader->PointerToRawData = SectionOneOffset; 752319799Sdim SectionOneHeader->PointerToRelocations = SectionOneRelocations; 753319799Sdim SectionOneHeader->PointerToLinenumbers = 0; 754319799Sdim SectionOneHeader->NumberOfRelocations = Data.size(); 755319799Sdim SectionOneHeader->NumberOfLinenumbers = 0; 756320397Sdim SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 757320397Sdim SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 758319799Sdim} 759319799Sdim 760319799Sdimvoid WindowsResourceCOFFWriter::writeSecondSectionHeader() { 761319799Sdim // Write the second section header. 762320397Sdim CurrentOffset += sizeof(coff_section); 763320397Sdim auto *SectionTwoHeader = 764320397Sdim reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 765360784Sdim coffnamecpy(SectionTwoHeader->Name, ".rsrc$02"); 766319799Sdim SectionTwoHeader->VirtualSize = 0; 767319799Sdim SectionTwoHeader->VirtualAddress = 0; 768319799Sdim SectionTwoHeader->SizeOfRawData = SectionTwoSize; 769319799Sdim SectionTwoHeader->PointerToRawData = SectionTwoOffset; 770319799Sdim SectionTwoHeader->PointerToRelocations = 0; 771319799Sdim SectionTwoHeader->PointerToLinenumbers = 0; 772319799Sdim SectionTwoHeader->NumberOfRelocations = 0; 773319799Sdim SectionTwoHeader->NumberOfLinenumbers = 0; 774320397Sdim SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 775320397Sdim SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 776319799Sdim} 777319799Sdim 778319799Sdimvoid WindowsResourceCOFFWriter::writeFirstSection() { 779319799Sdim // Write section one. 780320397Sdim CurrentOffset += sizeof(coff_section); 781319799Sdim 782319799Sdim writeDirectoryTree(); 783319799Sdim writeDirectoryStringTable(); 784319799Sdim writeFirstSectionRelocations(); 785320041Sdim 786320041Sdim CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 787319799Sdim} 788319799Sdim 789319799Sdimvoid WindowsResourceCOFFWriter::writeSecondSection() { 790319799Sdim // Now write the .rsrc$02 section. 791319799Sdim for (auto const &RawDataEntry : Data) { 792344779Sdim llvm::copy(RawDataEntry, BufferStart + CurrentOffset); 793320041Sdim CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t)); 794319799Sdim } 795320041Sdim 796320041Sdim CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 797319799Sdim} 798319799Sdim 799319799Sdimvoid WindowsResourceCOFFWriter::writeSymbolTable() { 800319799Sdim // Now write the symbol table. 801319799Sdim // First, the feat symbol. 802320397Sdim auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 803360784Sdim coffnamecpy(Symbol->Name.ShortName, "@feat.00"); 804319799Sdim Symbol->Value = 0x11; 805319799Sdim Symbol->SectionNumber = 0xffff; 806320397Sdim Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 807320397Sdim Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 808319799Sdim Symbol->NumberOfAuxSymbols = 0; 809320397Sdim CurrentOffset += sizeof(coff_symbol16); 810319799Sdim 811319799Sdim // Now write the .rsrc1 symbol + aux. 812320397Sdim Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 813360784Sdim coffnamecpy(Symbol->Name.ShortName, ".rsrc$01"); 814319799Sdim Symbol->Value = 0; 815319799Sdim Symbol->SectionNumber = 1; 816320397Sdim Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 817320397Sdim Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 818319799Sdim Symbol->NumberOfAuxSymbols = 1; 819320397Sdim CurrentOffset += sizeof(coff_symbol16); 820320397Sdim auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 821320397Sdim CurrentOffset); 822319799Sdim Aux->Length = SectionOneSize; 823319799Sdim Aux->NumberOfRelocations = Data.size(); 824319799Sdim Aux->NumberOfLinenumbers = 0; 825319799Sdim Aux->CheckSum = 0; 826319799Sdim Aux->NumberLowPart = 0; 827319799Sdim Aux->Selection = 0; 828320397Sdim CurrentOffset += sizeof(coff_aux_section_definition); 829319799Sdim 830319799Sdim // Now write the .rsrc2 symbol + aux. 831320397Sdim Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 832360784Sdim coffnamecpy(Symbol->Name.ShortName, ".rsrc$02"); 833319799Sdim Symbol->Value = 0; 834319799Sdim Symbol->SectionNumber = 2; 835320397Sdim Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 836320397Sdim Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 837319799Sdim Symbol->NumberOfAuxSymbols = 1; 838320397Sdim CurrentOffset += sizeof(coff_symbol16); 839320397Sdim Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 840320397Sdim CurrentOffset); 841319799Sdim Aux->Length = SectionTwoSize; 842319799Sdim Aux->NumberOfRelocations = 0; 843319799Sdim Aux->NumberOfLinenumbers = 0; 844319799Sdim Aux->CheckSum = 0; 845319799Sdim Aux->NumberLowPart = 0; 846319799Sdim Aux->Selection = 0; 847320397Sdim CurrentOffset += sizeof(coff_aux_section_definition); 848319799Sdim 849319799Sdim // Now write a symbol for each relocation. 850319799Sdim for (unsigned i = 0; i < Data.size(); i++) { 851327952Sdim auto RelocationName = formatv("$R{0:X-6}", i & 0xffffff).sstr<COFF::NameSize>(); 852320397Sdim Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 853360784Sdim coffnamecpy(Symbol->Name.ShortName, RelocationName); 854319799Sdim Symbol->Value = DataOffsets[i]; 855320572Sdim Symbol->SectionNumber = 2; 856320397Sdim Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 857320397Sdim Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 858319799Sdim Symbol->NumberOfAuxSymbols = 0; 859320397Sdim CurrentOffset += sizeof(coff_symbol16); 860319799Sdim } 861319799Sdim} 862319799Sdim 863319799Sdimvoid WindowsResourceCOFFWriter::writeStringTable() { 864319799Sdim // Just 4 null bytes for the string table. 865320041Sdim auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset); 866320041Sdim memset(COFFStringTable, 0, 4); 867319799Sdim} 868319799Sdim 869319799Sdimvoid WindowsResourceCOFFWriter::writeDirectoryTree() { 870319799Sdim // Traverse parsed resource tree breadth-first and write the corresponding 871319799Sdim // COFF objects. 872319799Sdim std::queue<const WindowsResourceParser::TreeNode *> Queue; 873319799Sdim Queue.push(&Resources); 874320397Sdim uint32_t NextLevelOffset = 875320397Sdim sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() + 876320397Sdim Resources.getIDChildren().size()) * 877320397Sdim sizeof(coff_resource_dir_entry); 878319799Sdim std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder; 879319799Sdim uint32_t CurrentRelativeOffset = 0; 880319799Sdim 881319799Sdim while (!Queue.empty()) { 882319799Sdim auto CurrentNode = Queue.front(); 883319799Sdim Queue.pop(); 884320397Sdim auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart + 885320397Sdim CurrentOffset); 886319799Sdim Table->Characteristics = CurrentNode->getCharacteristics(); 887319799Sdim Table->TimeDateStamp = 0; 888319799Sdim Table->MajorVersion = CurrentNode->getMajorVersion(); 889319799Sdim Table->MinorVersion = CurrentNode->getMinorVersion(); 890319799Sdim auto &IDChildren = CurrentNode->getIDChildren(); 891319799Sdim auto &StringChildren = CurrentNode->getStringChildren(); 892319799Sdim Table->NumberOfNameEntries = StringChildren.size(); 893319799Sdim Table->NumberOfIDEntries = IDChildren.size(); 894320397Sdim CurrentOffset += sizeof(coff_resource_dir_table); 895320397Sdim CurrentRelativeOffset += sizeof(coff_resource_dir_table); 896319799Sdim 897319799Sdim // Write the directory entries immediately following each directory table. 898319799Sdim for (auto const &Child : StringChildren) { 899320397Sdim auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 900320397Sdim CurrentOffset); 901320970Sdim Entry->Identifier.setNameOffset( 902320970Sdim StringTableOffsets[Child.second->getStringIndex()]); 903319799Sdim if (Child.second->checkIsDataNode()) { 904319799Sdim Entry->Offset.DataEntryOffset = NextLevelOffset; 905320397Sdim NextLevelOffset += sizeof(coff_resource_data_entry); 906319799Sdim DataEntriesTreeOrder.push_back(Child.second.get()); 907319799Sdim } else { 908319799Sdim Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 909320397Sdim NextLevelOffset += sizeof(coff_resource_dir_table) + 910319799Sdim (Child.second->getStringChildren().size() + 911319799Sdim Child.second->getIDChildren().size()) * 912320397Sdim sizeof(coff_resource_dir_entry); 913319799Sdim Queue.push(Child.second.get()); 914319799Sdim } 915320397Sdim CurrentOffset += sizeof(coff_resource_dir_entry); 916320397Sdim CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 917319799Sdim } 918319799Sdim for (auto const &Child : IDChildren) { 919320397Sdim auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 920320397Sdim CurrentOffset); 921319799Sdim Entry->Identifier.ID = Child.first; 922319799Sdim if (Child.second->checkIsDataNode()) { 923319799Sdim Entry->Offset.DataEntryOffset = NextLevelOffset; 924320397Sdim NextLevelOffset += sizeof(coff_resource_data_entry); 925319799Sdim DataEntriesTreeOrder.push_back(Child.second.get()); 926319799Sdim } else { 927319799Sdim Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 928320397Sdim NextLevelOffset += sizeof(coff_resource_dir_table) + 929319799Sdim (Child.second->getStringChildren().size() + 930319799Sdim Child.second->getIDChildren().size()) * 931320397Sdim sizeof(coff_resource_dir_entry); 932319799Sdim Queue.push(Child.second.get()); 933319799Sdim } 934320397Sdim CurrentOffset += sizeof(coff_resource_dir_entry); 935320397Sdim CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 936319799Sdim } 937319799Sdim } 938319799Sdim 939319799Sdim RelocationAddresses.resize(Data.size()); 940319799Sdim // Now write all the resource data entries. 941319799Sdim for (auto DataNodes : DataEntriesTreeOrder) { 942320397Sdim auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart + 943320397Sdim CurrentOffset); 944319799Sdim RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset; 945319799Sdim Entry->DataRVA = 0; // Set to zero because it is a relocation. 946319799Sdim Entry->DataSize = Data[DataNodes->getDataIndex()].size(); 947319799Sdim Entry->Codepage = 0; 948319799Sdim Entry->Reserved = 0; 949320397Sdim CurrentOffset += sizeof(coff_resource_data_entry); 950320397Sdim CurrentRelativeOffset += sizeof(coff_resource_data_entry); 951319799Sdim } 952319799Sdim} 953319799Sdim 954319799Sdimvoid WindowsResourceCOFFWriter::writeDirectoryStringTable() { 955319799Sdim // Now write the directory string table for .rsrc$01 956319799Sdim uint32_t TotalStringTableSize = 0; 957320041Sdim for (auto &String : StringTable) { 958319799Sdim uint16_t Length = String.size(); 959320041Sdim support::endian::write16le(BufferStart + CurrentOffset, Length); 960320041Sdim CurrentOffset += sizeof(uint16_t); 961320041Sdim auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset); 962344779Sdim llvm::copy(String, Start); 963320041Sdim CurrentOffset += Length * sizeof(UTF16); 964319799Sdim TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t); 965319799Sdim } 966320041Sdim CurrentOffset += 967319799Sdim alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize; 968319799Sdim} 969319799Sdim 970319799Sdimvoid WindowsResourceCOFFWriter::writeFirstSectionRelocations() { 971319799Sdim 972319799Sdim // Now write the relocations for .rsrc$01 973319799Sdim // Five symbols already in table before we start, @feat.00 and 2 for each 974319799Sdim // .rsrc section. 975319799Sdim uint32_t NextSymbolIndex = 5; 976319799Sdim for (unsigned i = 0; i < Data.size(); i++) { 977320397Sdim auto *Reloc = 978320397Sdim reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset); 979319799Sdim Reloc->VirtualAddress = RelocationAddresses[i]; 980319799Sdim Reloc->SymbolTableIndex = NextSymbolIndex++; 981319799Sdim switch (MachineType) { 982320397Sdim case COFF::IMAGE_FILE_MACHINE_ARMNT: 983320397Sdim Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB; 984319799Sdim break; 985320397Sdim case COFF::IMAGE_FILE_MACHINE_AMD64: 986320397Sdim Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB; 987319799Sdim break; 988320397Sdim case COFF::IMAGE_FILE_MACHINE_I386: 989320397Sdim Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB; 990319799Sdim break; 991327952Sdim case COFF::IMAGE_FILE_MACHINE_ARM64: 992327952Sdim Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB; 993327952Sdim break; 994319799Sdim default: 995327952Sdim llvm_unreachable("unknown machine type"); 996319799Sdim } 997320397Sdim CurrentOffset += sizeof(coff_relocation); 998319799Sdim } 999319799Sdim} 1000319799Sdim 1001320397SdimExpected<std::unique_ptr<MemoryBuffer>> 1002320397SdimwriteWindowsResourceCOFF(COFF::MachineTypes MachineType, 1003353358Sdim const WindowsResourceParser &Parser, 1004353358Sdim uint32_t TimeDateStamp) { 1005319799Sdim Error E = Error::success(); 1006320397Sdim WindowsResourceCOFFWriter Writer(MachineType, Parser, E); 1007319799Sdim if (E) 1008320397Sdim return std::move(E); 1009353358Sdim return Writer.write(TimeDateStamp); 1010319799Sdim} 1011319799Sdim 1012318663Sdim} // namespace object 1013318663Sdim} // namespace llvm 1014