1*0b57cec5SDimitry Andric //===-- WindowsResource.cpp -------------------------------------*- C++ -*-===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // This file implements the .res file class. 10*0b57cec5SDimitry Andric // 11*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 12*0b57cec5SDimitry Andric 13*0b57cec5SDimitry Andric #include "llvm/Object/WindowsResource.h" 14*0b57cec5SDimitry Andric #include "llvm/Object/COFF.h" 15*0b57cec5SDimitry Andric #include "llvm/Support/FileOutputBuffer.h" 16*0b57cec5SDimitry Andric #include "llvm/Support/FormatVariadic.h" 17*0b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h" 18*0b57cec5SDimitry Andric #include "llvm/Support/ScopedPrinter.h" 19*0b57cec5SDimitry Andric #include <ctime> 20*0b57cec5SDimitry Andric #include <queue> 21*0b57cec5SDimitry Andric #include <system_error> 22*0b57cec5SDimitry Andric 23*0b57cec5SDimitry Andric using namespace llvm; 24*0b57cec5SDimitry Andric using namespace object; 25*0b57cec5SDimitry Andric 26*0b57cec5SDimitry Andric namespace llvm { 27*0b57cec5SDimitry Andric namespace object { 28*0b57cec5SDimitry Andric 29*0b57cec5SDimitry Andric #define RETURN_IF_ERROR(X) \ 30*0b57cec5SDimitry Andric if (auto EC = X) \ 31*0b57cec5SDimitry Andric return EC; 32*0b57cec5SDimitry Andric 33*0b57cec5SDimitry Andric const uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t); 34*0b57cec5SDimitry Andric 35*0b57cec5SDimitry Andric // COFF files seem to be inconsistent with alignment between sections, just use 36*0b57cec5SDimitry Andric // 8-byte because it makes everyone happy. 37*0b57cec5SDimitry Andric const uint32_t SECTION_ALIGNMENT = sizeof(uint64_t); 38*0b57cec5SDimitry Andric 39*0b57cec5SDimitry Andric uint32_t WindowsResourceParser::TreeNode::StringCount = 0; 40*0b57cec5SDimitry Andric uint32_t WindowsResourceParser::TreeNode::DataCount = 0; 41*0b57cec5SDimitry Andric 42*0b57cec5SDimitry Andric WindowsResource::WindowsResource(MemoryBufferRef Source) 43*0b57cec5SDimitry Andric : Binary(Binary::ID_WinRes, Source) { 44*0b57cec5SDimitry Andric size_t LeadingSize = WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE; 45*0b57cec5SDimitry Andric BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize), 46*0b57cec5SDimitry Andric support::little); 47*0b57cec5SDimitry Andric } 48*0b57cec5SDimitry Andric 49*0b57cec5SDimitry Andric // static 50*0b57cec5SDimitry Andric Expected<std::unique_ptr<WindowsResource>> 51*0b57cec5SDimitry Andric WindowsResource::createWindowsResource(MemoryBufferRef Source) { 52*0b57cec5SDimitry Andric if (Source.getBufferSize() < WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE) 53*0b57cec5SDimitry Andric return make_error<GenericBinaryError>( 54*0b57cec5SDimitry Andric Source.getBufferIdentifier() + ": too small to be a resource file", 55*0b57cec5SDimitry Andric object_error::invalid_file_type); 56*0b57cec5SDimitry Andric std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source)); 57*0b57cec5SDimitry Andric return std::move(Ret); 58*0b57cec5SDimitry Andric } 59*0b57cec5SDimitry Andric 60*0b57cec5SDimitry Andric Expected<ResourceEntryRef> WindowsResource::getHeadEntry() { 61*0b57cec5SDimitry Andric if (BBS.getLength() < sizeof(WinResHeaderPrefix) + sizeof(WinResHeaderSuffix)) 62*0b57cec5SDimitry Andric return make_error<EmptyResError>(getFileName() + " contains no entries", 63*0b57cec5SDimitry Andric object_error::unexpected_eof); 64*0b57cec5SDimitry Andric return ResourceEntryRef::create(BinaryStreamRef(BBS), this); 65*0b57cec5SDimitry Andric } 66*0b57cec5SDimitry Andric 67*0b57cec5SDimitry Andric ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref, 68*0b57cec5SDimitry Andric const WindowsResource *Owner) 69*0b57cec5SDimitry Andric : Reader(Ref), Owner(Owner) {} 70*0b57cec5SDimitry Andric 71*0b57cec5SDimitry Andric Expected<ResourceEntryRef> 72*0b57cec5SDimitry Andric ResourceEntryRef::create(BinaryStreamRef BSR, const WindowsResource *Owner) { 73*0b57cec5SDimitry Andric auto Ref = ResourceEntryRef(BSR, Owner); 74*0b57cec5SDimitry Andric if (auto E = Ref.loadNext()) 75*0b57cec5SDimitry Andric return std::move(E); 76*0b57cec5SDimitry Andric return Ref; 77*0b57cec5SDimitry Andric } 78*0b57cec5SDimitry Andric 79*0b57cec5SDimitry Andric Error ResourceEntryRef::moveNext(bool &End) { 80*0b57cec5SDimitry Andric // Reached end of all the entries. 81*0b57cec5SDimitry Andric if (Reader.bytesRemaining() == 0) { 82*0b57cec5SDimitry Andric End = true; 83*0b57cec5SDimitry Andric return Error::success(); 84*0b57cec5SDimitry Andric } 85*0b57cec5SDimitry Andric RETURN_IF_ERROR(loadNext()); 86*0b57cec5SDimitry Andric 87*0b57cec5SDimitry Andric return Error::success(); 88*0b57cec5SDimitry Andric } 89*0b57cec5SDimitry Andric 90*0b57cec5SDimitry Andric static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID, 91*0b57cec5SDimitry Andric ArrayRef<UTF16> &Str, bool &IsString) { 92*0b57cec5SDimitry Andric uint16_t IDFlag; 93*0b57cec5SDimitry Andric RETURN_IF_ERROR(Reader.readInteger(IDFlag)); 94*0b57cec5SDimitry Andric IsString = IDFlag != 0xffff; 95*0b57cec5SDimitry Andric 96*0b57cec5SDimitry Andric if (IsString) { 97*0b57cec5SDimitry Andric Reader.setOffset( 98*0b57cec5SDimitry Andric Reader.getOffset() - 99*0b57cec5SDimitry Andric sizeof(uint16_t)); // Re-read the bytes which we used to check the flag. 100*0b57cec5SDimitry Andric RETURN_IF_ERROR(Reader.readWideString(Str)); 101*0b57cec5SDimitry Andric } else 102*0b57cec5SDimitry Andric RETURN_IF_ERROR(Reader.readInteger(ID)); 103*0b57cec5SDimitry Andric 104*0b57cec5SDimitry Andric return Error::success(); 105*0b57cec5SDimitry Andric } 106*0b57cec5SDimitry Andric 107*0b57cec5SDimitry Andric Error ResourceEntryRef::loadNext() { 108*0b57cec5SDimitry Andric const WinResHeaderPrefix *Prefix; 109*0b57cec5SDimitry Andric RETURN_IF_ERROR(Reader.readObject(Prefix)); 110*0b57cec5SDimitry Andric 111*0b57cec5SDimitry Andric if (Prefix->HeaderSize < MIN_HEADER_SIZE) 112*0b57cec5SDimitry Andric return make_error<GenericBinaryError>(Owner->getFileName() + 113*0b57cec5SDimitry Andric ": header size too small", 114*0b57cec5SDimitry Andric object_error::parse_failed); 115*0b57cec5SDimitry Andric 116*0b57cec5SDimitry Andric RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType)); 117*0b57cec5SDimitry Andric 118*0b57cec5SDimitry Andric RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName)); 119*0b57cec5SDimitry Andric 120*0b57cec5SDimitry Andric RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_HEADER_ALIGNMENT)); 121*0b57cec5SDimitry Andric 122*0b57cec5SDimitry Andric RETURN_IF_ERROR(Reader.readObject(Suffix)); 123*0b57cec5SDimitry Andric 124*0b57cec5SDimitry Andric RETURN_IF_ERROR(Reader.readArray(Data, Prefix->DataSize)); 125*0b57cec5SDimitry Andric 126*0b57cec5SDimitry Andric RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_DATA_ALIGNMENT)); 127*0b57cec5SDimitry Andric 128*0b57cec5SDimitry Andric return Error::success(); 129*0b57cec5SDimitry Andric } 130*0b57cec5SDimitry Andric 131*0b57cec5SDimitry Andric WindowsResourceParser::WindowsResourceParser() : Root(false) {} 132*0b57cec5SDimitry Andric 133*0b57cec5SDimitry Andric void printResourceTypeName(uint16_t TypeID, raw_ostream &OS) { 134*0b57cec5SDimitry Andric switch (TypeID) { 135*0b57cec5SDimitry Andric case 1: OS << "CURSOR (ID 1)"; break; 136*0b57cec5SDimitry Andric case 2: OS << "BITMAP (ID 2)"; break; 137*0b57cec5SDimitry Andric case 3: OS << "ICON (ID 3)"; break; 138*0b57cec5SDimitry Andric case 4: OS << "MENU (ID 4)"; break; 139*0b57cec5SDimitry Andric case 5: OS << "DIALOG (ID 5)"; break; 140*0b57cec5SDimitry Andric case 6: OS << "STRINGTABLE (ID 6)"; break; 141*0b57cec5SDimitry Andric case 7: OS << "FONTDIR (ID 7)"; break; 142*0b57cec5SDimitry Andric case 8: OS << "FONT (ID 8)"; break; 143*0b57cec5SDimitry Andric case 9: OS << "ACCELERATOR (ID 9)"; break; 144*0b57cec5SDimitry Andric case 10: OS << "RCDATA (ID 10)"; break; 145*0b57cec5SDimitry Andric case 11: OS << "MESSAGETABLE (ID 11)"; break; 146*0b57cec5SDimitry Andric case 12: OS << "GROUP_CURSOR (ID 12)"; break; 147*0b57cec5SDimitry Andric case 14: OS << "GROUP_ICON (ID 14)"; break; 148*0b57cec5SDimitry Andric case 16: OS << "VERSIONINFO (ID 16)"; break; 149*0b57cec5SDimitry Andric case 17: OS << "DLGINCLUDE (ID 17)"; break; 150*0b57cec5SDimitry Andric case 19: OS << "PLUGPLAY (ID 19)"; break; 151*0b57cec5SDimitry Andric case 20: OS << "VXD (ID 20)"; break; 152*0b57cec5SDimitry Andric case 21: OS << "ANICURSOR (ID 21)"; break; 153*0b57cec5SDimitry Andric case 22: OS << "ANIICON (ID 22)"; break; 154*0b57cec5SDimitry Andric case 23: OS << "HTML (ID 23)"; break; 155*0b57cec5SDimitry Andric case 24: OS << "MANIFEST (ID 24)"; break; 156*0b57cec5SDimitry Andric default: OS << "ID " << TypeID; break; 157*0b57cec5SDimitry Andric } 158*0b57cec5SDimitry Andric } 159*0b57cec5SDimitry Andric 160*0b57cec5SDimitry Andric static bool convertUTF16LEToUTF8String(ArrayRef<UTF16> Src, std::string &Out) { 161*0b57cec5SDimitry Andric if (!sys::IsBigEndianHost) 162*0b57cec5SDimitry Andric return convertUTF16ToUTF8String(Src, Out); 163*0b57cec5SDimitry Andric 164*0b57cec5SDimitry Andric std::vector<UTF16> EndianCorrectedSrc; 165*0b57cec5SDimitry Andric EndianCorrectedSrc.resize(Src.size() + 1); 166*0b57cec5SDimitry Andric llvm::copy(Src, EndianCorrectedSrc.begin() + 1); 167*0b57cec5SDimitry Andric EndianCorrectedSrc[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED; 168*0b57cec5SDimitry Andric return convertUTF16ToUTF8String(makeArrayRef(EndianCorrectedSrc), Out); 169*0b57cec5SDimitry Andric } 170*0b57cec5SDimitry Andric 171*0b57cec5SDimitry Andric static std::string makeDuplicateResourceError( 172*0b57cec5SDimitry Andric const ResourceEntryRef &Entry, StringRef File1, StringRef File2) { 173*0b57cec5SDimitry Andric std::string Ret; 174*0b57cec5SDimitry Andric raw_string_ostream OS(Ret); 175*0b57cec5SDimitry Andric 176*0b57cec5SDimitry Andric OS << "duplicate resource:"; 177*0b57cec5SDimitry Andric 178*0b57cec5SDimitry Andric OS << " type "; 179*0b57cec5SDimitry Andric if (Entry.checkTypeString()) { 180*0b57cec5SDimitry Andric std::string UTF8; 181*0b57cec5SDimitry Andric if (!convertUTF16LEToUTF8String(Entry.getTypeString(), UTF8)) 182*0b57cec5SDimitry Andric UTF8 = "(failed conversion from UTF16)"; 183*0b57cec5SDimitry Andric OS << '\"' << UTF8 << '\"'; 184*0b57cec5SDimitry Andric } else 185*0b57cec5SDimitry Andric printResourceTypeName(Entry.getTypeID(), OS); 186*0b57cec5SDimitry Andric 187*0b57cec5SDimitry Andric OS << "/name "; 188*0b57cec5SDimitry Andric if (Entry.checkNameString()) { 189*0b57cec5SDimitry Andric std::string UTF8; 190*0b57cec5SDimitry Andric if (!convertUTF16LEToUTF8String(Entry.getNameString(), UTF8)) 191*0b57cec5SDimitry Andric UTF8 = "(failed conversion from UTF16)"; 192*0b57cec5SDimitry Andric OS << '\"' << UTF8 << '\"'; 193*0b57cec5SDimitry Andric } else { 194*0b57cec5SDimitry Andric OS << "ID " << Entry.getNameID(); 195*0b57cec5SDimitry Andric } 196*0b57cec5SDimitry Andric 197*0b57cec5SDimitry Andric OS << "/language " << Entry.getLanguage() << ", in " << File1 << " and in " 198*0b57cec5SDimitry Andric << File2; 199*0b57cec5SDimitry Andric 200*0b57cec5SDimitry Andric return OS.str(); 201*0b57cec5SDimitry Andric } 202*0b57cec5SDimitry Andric 203*0b57cec5SDimitry Andric Error WindowsResourceParser::parse(WindowsResource *WR, 204*0b57cec5SDimitry Andric std::vector<std::string> &Duplicates) { 205*0b57cec5SDimitry Andric auto EntryOrErr = WR->getHeadEntry(); 206*0b57cec5SDimitry Andric if (!EntryOrErr) { 207*0b57cec5SDimitry Andric auto E = EntryOrErr.takeError(); 208*0b57cec5SDimitry Andric if (E.isA<EmptyResError>()) { 209*0b57cec5SDimitry Andric // Check if the .res file contains no entries. In this case we don't have 210*0b57cec5SDimitry Andric // to throw an error but can rather just return without parsing anything. 211*0b57cec5SDimitry Andric // This applies for files which have a valid PE header magic and the 212*0b57cec5SDimitry Andric // mandatory empty null resource entry. Files which do not fit this 213*0b57cec5SDimitry Andric // criteria would have already been filtered out by 214*0b57cec5SDimitry Andric // WindowsResource::createWindowsResource(). 215*0b57cec5SDimitry Andric consumeError(std::move(E)); 216*0b57cec5SDimitry Andric return Error::success(); 217*0b57cec5SDimitry Andric } 218*0b57cec5SDimitry Andric return E; 219*0b57cec5SDimitry Andric } 220*0b57cec5SDimitry Andric 221*0b57cec5SDimitry Andric ResourceEntryRef Entry = EntryOrErr.get(); 222*0b57cec5SDimitry Andric bool End = false; 223*0b57cec5SDimitry Andric while (!End) { 224*0b57cec5SDimitry Andric Data.push_back(Entry.getData()); 225*0b57cec5SDimitry Andric 226*0b57cec5SDimitry Andric bool IsNewTypeString = false; 227*0b57cec5SDimitry Andric bool IsNewNameString = false; 228*0b57cec5SDimitry Andric 229*0b57cec5SDimitry Andric TreeNode* Node; 230*0b57cec5SDimitry Andric bool IsNewNode = Root.addEntry(Entry, InputFilenames.size(), 231*0b57cec5SDimitry Andric IsNewTypeString, IsNewNameString, Node); 232*0b57cec5SDimitry Andric InputFilenames.push_back(WR->getFileName()); 233*0b57cec5SDimitry Andric if (!IsNewNode) { 234*0b57cec5SDimitry Andric Duplicates.push_back(makeDuplicateResourceError( 235*0b57cec5SDimitry Andric Entry, InputFilenames[Node->Origin], WR->getFileName())); 236*0b57cec5SDimitry Andric } 237*0b57cec5SDimitry Andric 238*0b57cec5SDimitry Andric if (IsNewTypeString) 239*0b57cec5SDimitry Andric StringTable.push_back(Entry.getTypeString()); 240*0b57cec5SDimitry Andric 241*0b57cec5SDimitry Andric if (IsNewNameString) 242*0b57cec5SDimitry Andric StringTable.push_back(Entry.getNameString()); 243*0b57cec5SDimitry Andric 244*0b57cec5SDimitry Andric RETURN_IF_ERROR(Entry.moveNext(End)); 245*0b57cec5SDimitry Andric } 246*0b57cec5SDimitry Andric 247*0b57cec5SDimitry Andric return Error::success(); 248*0b57cec5SDimitry Andric } 249*0b57cec5SDimitry Andric 250*0b57cec5SDimitry Andric void WindowsResourceParser::printTree(raw_ostream &OS) const { 251*0b57cec5SDimitry Andric ScopedPrinter Writer(OS); 252*0b57cec5SDimitry Andric Root.print(Writer, "Resource Tree"); 253*0b57cec5SDimitry Andric } 254*0b57cec5SDimitry Andric 255*0b57cec5SDimitry Andric bool WindowsResourceParser::TreeNode::addEntry(const ResourceEntryRef &Entry, 256*0b57cec5SDimitry Andric uint32_t Origin, 257*0b57cec5SDimitry Andric bool &IsNewTypeString, 258*0b57cec5SDimitry Andric bool &IsNewNameString, 259*0b57cec5SDimitry Andric TreeNode *&Result) { 260*0b57cec5SDimitry Andric TreeNode &TypeNode = addTypeNode(Entry, IsNewTypeString); 261*0b57cec5SDimitry Andric TreeNode &NameNode = TypeNode.addNameNode(Entry, IsNewNameString); 262*0b57cec5SDimitry Andric return NameNode.addLanguageNode(Entry, Origin, Result); 263*0b57cec5SDimitry Andric } 264*0b57cec5SDimitry Andric 265*0b57cec5SDimitry Andric WindowsResourceParser::TreeNode::TreeNode(bool IsStringNode) { 266*0b57cec5SDimitry Andric if (IsStringNode) 267*0b57cec5SDimitry Andric StringIndex = StringCount++; 268*0b57cec5SDimitry Andric } 269*0b57cec5SDimitry Andric 270*0b57cec5SDimitry Andric WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion, 271*0b57cec5SDimitry Andric uint16_t MinorVersion, 272*0b57cec5SDimitry Andric uint32_t Characteristics, 273*0b57cec5SDimitry Andric uint32_t Origin) 274*0b57cec5SDimitry Andric : IsDataNode(true), MajorVersion(MajorVersion), MinorVersion(MinorVersion), 275*0b57cec5SDimitry Andric Characteristics(Characteristics), Origin(Origin) { 276*0b57cec5SDimitry Andric DataIndex = DataCount++; 277*0b57cec5SDimitry Andric } 278*0b57cec5SDimitry Andric 279*0b57cec5SDimitry Andric std::unique_ptr<WindowsResourceParser::TreeNode> 280*0b57cec5SDimitry Andric WindowsResourceParser::TreeNode::createStringNode() { 281*0b57cec5SDimitry Andric return std::unique_ptr<TreeNode>(new TreeNode(true)); 282*0b57cec5SDimitry Andric } 283*0b57cec5SDimitry Andric 284*0b57cec5SDimitry Andric std::unique_ptr<WindowsResourceParser::TreeNode> 285*0b57cec5SDimitry Andric WindowsResourceParser::TreeNode::createIDNode() { 286*0b57cec5SDimitry Andric return std::unique_ptr<TreeNode>(new TreeNode(false)); 287*0b57cec5SDimitry Andric } 288*0b57cec5SDimitry Andric 289*0b57cec5SDimitry Andric std::unique_ptr<WindowsResourceParser::TreeNode> 290*0b57cec5SDimitry Andric WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion, 291*0b57cec5SDimitry Andric uint16_t MinorVersion, 292*0b57cec5SDimitry Andric uint32_t Characteristics, 293*0b57cec5SDimitry Andric uint32_t Origin) { 294*0b57cec5SDimitry Andric return std::unique_ptr<TreeNode>( 295*0b57cec5SDimitry Andric new TreeNode(MajorVersion, MinorVersion, Characteristics, Origin)); 296*0b57cec5SDimitry Andric } 297*0b57cec5SDimitry Andric 298*0b57cec5SDimitry Andric WindowsResourceParser::TreeNode & 299*0b57cec5SDimitry Andric WindowsResourceParser::TreeNode::addTypeNode(const ResourceEntryRef &Entry, 300*0b57cec5SDimitry Andric bool &IsNewTypeString) { 301*0b57cec5SDimitry Andric if (Entry.checkTypeString()) 302*0b57cec5SDimitry Andric return addNameChild(Entry.getTypeString(), IsNewTypeString); 303*0b57cec5SDimitry Andric else 304*0b57cec5SDimitry Andric return addIDChild(Entry.getTypeID()); 305*0b57cec5SDimitry Andric } 306*0b57cec5SDimitry Andric 307*0b57cec5SDimitry Andric WindowsResourceParser::TreeNode & 308*0b57cec5SDimitry Andric WindowsResourceParser::TreeNode::addNameNode(const ResourceEntryRef &Entry, 309*0b57cec5SDimitry Andric bool &IsNewNameString) { 310*0b57cec5SDimitry Andric if (Entry.checkNameString()) 311*0b57cec5SDimitry Andric return addNameChild(Entry.getNameString(), IsNewNameString); 312*0b57cec5SDimitry Andric else 313*0b57cec5SDimitry Andric return addIDChild(Entry.getNameID()); 314*0b57cec5SDimitry Andric } 315*0b57cec5SDimitry Andric 316*0b57cec5SDimitry Andric bool WindowsResourceParser::TreeNode::addLanguageNode( 317*0b57cec5SDimitry Andric const ResourceEntryRef &Entry, uint32_t Origin, TreeNode *&Result) { 318*0b57cec5SDimitry Andric return addDataChild(Entry.getLanguage(), Entry.getMajorVersion(), 319*0b57cec5SDimitry Andric Entry.getMinorVersion(), Entry.getCharacteristics(), 320*0b57cec5SDimitry Andric Origin, Result); 321*0b57cec5SDimitry Andric } 322*0b57cec5SDimitry Andric 323*0b57cec5SDimitry Andric bool WindowsResourceParser::TreeNode::addDataChild( 324*0b57cec5SDimitry Andric uint32_t ID, uint16_t MajorVersion, uint16_t MinorVersion, 325*0b57cec5SDimitry Andric uint32_t Characteristics, uint32_t Origin, TreeNode *&Result) { 326*0b57cec5SDimitry Andric auto NewChild = 327*0b57cec5SDimitry Andric createDataNode(MajorVersion, MinorVersion, Characteristics, Origin); 328*0b57cec5SDimitry Andric auto ElementInserted = IDChildren.emplace(ID, std::move(NewChild)); 329*0b57cec5SDimitry Andric Result = ElementInserted.first->second.get(); 330*0b57cec5SDimitry Andric return ElementInserted.second; 331*0b57cec5SDimitry Andric } 332*0b57cec5SDimitry Andric 333*0b57cec5SDimitry Andric WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addIDChild( 334*0b57cec5SDimitry Andric uint32_t ID) { 335*0b57cec5SDimitry Andric auto Child = IDChildren.find(ID); 336*0b57cec5SDimitry Andric if (Child == IDChildren.end()) { 337*0b57cec5SDimitry Andric auto NewChild = createIDNode(); 338*0b57cec5SDimitry Andric WindowsResourceParser::TreeNode &Node = *NewChild; 339*0b57cec5SDimitry Andric IDChildren.emplace(ID, std::move(NewChild)); 340*0b57cec5SDimitry Andric return Node; 341*0b57cec5SDimitry Andric } else 342*0b57cec5SDimitry Andric return *(Child->second); 343*0b57cec5SDimitry Andric } 344*0b57cec5SDimitry Andric 345*0b57cec5SDimitry Andric WindowsResourceParser::TreeNode & 346*0b57cec5SDimitry Andric WindowsResourceParser::TreeNode::addNameChild(ArrayRef<UTF16> NameRef, 347*0b57cec5SDimitry Andric bool &IsNewString) { 348*0b57cec5SDimitry Andric std::string NameString; 349*0b57cec5SDimitry Andric convertUTF16LEToUTF8String(NameRef, NameString); 350*0b57cec5SDimitry Andric 351*0b57cec5SDimitry Andric auto Child = StringChildren.find(NameString); 352*0b57cec5SDimitry Andric if (Child == StringChildren.end()) { 353*0b57cec5SDimitry Andric auto NewChild = createStringNode(); 354*0b57cec5SDimitry Andric IsNewString = true; 355*0b57cec5SDimitry Andric WindowsResourceParser::TreeNode &Node = *NewChild; 356*0b57cec5SDimitry Andric StringChildren.emplace(NameString, std::move(NewChild)); 357*0b57cec5SDimitry Andric return Node; 358*0b57cec5SDimitry Andric } else 359*0b57cec5SDimitry Andric return *(Child->second); 360*0b57cec5SDimitry Andric } 361*0b57cec5SDimitry Andric 362*0b57cec5SDimitry Andric void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer, 363*0b57cec5SDimitry Andric StringRef Name) const { 364*0b57cec5SDimitry Andric ListScope NodeScope(Writer, Name); 365*0b57cec5SDimitry Andric for (auto const &Child : StringChildren) { 366*0b57cec5SDimitry Andric Child.second->print(Writer, Child.first); 367*0b57cec5SDimitry Andric } 368*0b57cec5SDimitry Andric for (auto const &Child : IDChildren) { 369*0b57cec5SDimitry Andric Child.second->print(Writer, to_string(Child.first)); 370*0b57cec5SDimitry Andric } 371*0b57cec5SDimitry Andric } 372*0b57cec5SDimitry Andric 373*0b57cec5SDimitry Andric // This function returns the size of the entire resource tree, including 374*0b57cec5SDimitry Andric // directory tables, directory entries, and data entries. It does not include 375*0b57cec5SDimitry Andric // the directory strings or the relocations of the .rsrc section. 376*0b57cec5SDimitry Andric uint32_t WindowsResourceParser::TreeNode::getTreeSize() const { 377*0b57cec5SDimitry Andric uint32_t Size = (IDChildren.size() + StringChildren.size()) * 378*0b57cec5SDimitry Andric sizeof(coff_resource_dir_entry); 379*0b57cec5SDimitry Andric 380*0b57cec5SDimitry Andric // Reached a node pointing to a data entry. 381*0b57cec5SDimitry Andric if (IsDataNode) { 382*0b57cec5SDimitry Andric Size += sizeof(coff_resource_data_entry); 383*0b57cec5SDimitry Andric return Size; 384*0b57cec5SDimitry Andric } 385*0b57cec5SDimitry Andric 386*0b57cec5SDimitry Andric // If the node does not point to data, it must have a directory table pointing 387*0b57cec5SDimitry Andric // to other nodes. 388*0b57cec5SDimitry Andric Size += sizeof(coff_resource_dir_table); 389*0b57cec5SDimitry Andric 390*0b57cec5SDimitry Andric for (auto const &Child : StringChildren) { 391*0b57cec5SDimitry Andric Size += Child.second->getTreeSize(); 392*0b57cec5SDimitry Andric } 393*0b57cec5SDimitry Andric for (auto const &Child : IDChildren) { 394*0b57cec5SDimitry Andric Size += Child.second->getTreeSize(); 395*0b57cec5SDimitry Andric } 396*0b57cec5SDimitry Andric return Size; 397*0b57cec5SDimitry Andric } 398*0b57cec5SDimitry Andric 399*0b57cec5SDimitry Andric class WindowsResourceCOFFWriter { 400*0b57cec5SDimitry Andric public: 401*0b57cec5SDimitry Andric WindowsResourceCOFFWriter(COFF::MachineTypes MachineType, 402*0b57cec5SDimitry Andric const WindowsResourceParser &Parser, Error &E); 403*0b57cec5SDimitry Andric std::unique_ptr<MemoryBuffer> write(uint32_t TimeDateStamp); 404*0b57cec5SDimitry Andric 405*0b57cec5SDimitry Andric private: 406*0b57cec5SDimitry Andric void performFileLayout(); 407*0b57cec5SDimitry Andric void performSectionOneLayout(); 408*0b57cec5SDimitry Andric void performSectionTwoLayout(); 409*0b57cec5SDimitry Andric void writeCOFFHeader(uint32_t TimeDateStamp); 410*0b57cec5SDimitry Andric void writeFirstSectionHeader(); 411*0b57cec5SDimitry Andric void writeSecondSectionHeader(); 412*0b57cec5SDimitry Andric void writeFirstSection(); 413*0b57cec5SDimitry Andric void writeSecondSection(); 414*0b57cec5SDimitry Andric void writeSymbolTable(); 415*0b57cec5SDimitry Andric void writeStringTable(); 416*0b57cec5SDimitry Andric void writeDirectoryTree(); 417*0b57cec5SDimitry Andric void writeDirectoryStringTable(); 418*0b57cec5SDimitry Andric void writeFirstSectionRelocations(); 419*0b57cec5SDimitry Andric std::unique_ptr<WritableMemoryBuffer> OutputBuffer; 420*0b57cec5SDimitry Andric char *BufferStart; 421*0b57cec5SDimitry Andric uint64_t CurrentOffset = 0; 422*0b57cec5SDimitry Andric COFF::MachineTypes MachineType; 423*0b57cec5SDimitry Andric const WindowsResourceParser::TreeNode &Resources; 424*0b57cec5SDimitry Andric const ArrayRef<std::vector<uint8_t>> Data; 425*0b57cec5SDimitry Andric uint64_t FileSize; 426*0b57cec5SDimitry Andric uint32_t SymbolTableOffset; 427*0b57cec5SDimitry Andric uint32_t SectionOneSize; 428*0b57cec5SDimitry Andric uint32_t SectionOneOffset; 429*0b57cec5SDimitry Andric uint32_t SectionOneRelocations; 430*0b57cec5SDimitry Andric uint32_t SectionTwoSize; 431*0b57cec5SDimitry Andric uint32_t SectionTwoOffset; 432*0b57cec5SDimitry Andric const ArrayRef<std::vector<UTF16>> StringTable; 433*0b57cec5SDimitry Andric std::vector<uint32_t> StringTableOffsets; 434*0b57cec5SDimitry Andric std::vector<uint32_t> DataOffsets; 435*0b57cec5SDimitry Andric std::vector<uint32_t> RelocationAddresses; 436*0b57cec5SDimitry Andric }; 437*0b57cec5SDimitry Andric 438*0b57cec5SDimitry Andric WindowsResourceCOFFWriter::WindowsResourceCOFFWriter( 439*0b57cec5SDimitry Andric COFF::MachineTypes MachineType, const WindowsResourceParser &Parser, 440*0b57cec5SDimitry Andric Error &E) 441*0b57cec5SDimitry Andric : MachineType(MachineType), Resources(Parser.getTree()), 442*0b57cec5SDimitry Andric Data(Parser.getData()), StringTable(Parser.getStringTable()) { 443*0b57cec5SDimitry Andric performFileLayout(); 444*0b57cec5SDimitry Andric 445*0b57cec5SDimitry Andric OutputBuffer = WritableMemoryBuffer::getNewMemBuffer( 446*0b57cec5SDimitry Andric FileSize, "internal .obj file created from .res files"); 447*0b57cec5SDimitry Andric } 448*0b57cec5SDimitry Andric 449*0b57cec5SDimitry Andric void WindowsResourceCOFFWriter::performFileLayout() { 450*0b57cec5SDimitry Andric // Add size of COFF header. 451*0b57cec5SDimitry Andric FileSize = COFF::Header16Size; 452*0b57cec5SDimitry Andric 453*0b57cec5SDimitry Andric // one .rsrc section header for directory tree, another for resource data. 454*0b57cec5SDimitry Andric FileSize += 2 * COFF::SectionSize; 455*0b57cec5SDimitry Andric 456*0b57cec5SDimitry Andric performSectionOneLayout(); 457*0b57cec5SDimitry Andric performSectionTwoLayout(); 458*0b57cec5SDimitry Andric 459*0b57cec5SDimitry Andric // We have reached the address of the symbol table. 460*0b57cec5SDimitry Andric SymbolTableOffset = FileSize; 461*0b57cec5SDimitry Andric 462*0b57cec5SDimitry Andric FileSize += COFF::Symbol16Size; // size of the @feat.00 symbol. 463*0b57cec5SDimitry Andric FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section. 464*0b57cec5SDimitry Andric FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource. 465*0b57cec5SDimitry Andric FileSize += 4; // four null bytes for the string table. 466*0b57cec5SDimitry Andric } 467*0b57cec5SDimitry Andric 468*0b57cec5SDimitry Andric void WindowsResourceCOFFWriter::performSectionOneLayout() { 469*0b57cec5SDimitry Andric SectionOneOffset = FileSize; 470*0b57cec5SDimitry Andric 471*0b57cec5SDimitry Andric SectionOneSize = Resources.getTreeSize(); 472*0b57cec5SDimitry Andric uint32_t CurrentStringOffset = SectionOneSize; 473*0b57cec5SDimitry Andric uint32_t TotalStringTableSize = 0; 474*0b57cec5SDimitry Andric for (auto const &String : StringTable) { 475*0b57cec5SDimitry Andric StringTableOffsets.push_back(CurrentStringOffset); 476*0b57cec5SDimitry Andric uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t); 477*0b57cec5SDimitry Andric CurrentStringOffset += StringSize; 478*0b57cec5SDimitry Andric TotalStringTableSize += StringSize; 479*0b57cec5SDimitry Andric } 480*0b57cec5SDimitry Andric SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t)); 481*0b57cec5SDimitry Andric 482*0b57cec5SDimitry Andric // account for the relocations of section one. 483*0b57cec5SDimitry Andric SectionOneRelocations = FileSize + SectionOneSize; 484*0b57cec5SDimitry Andric FileSize += SectionOneSize; 485*0b57cec5SDimitry Andric FileSize += 486*0b57cec5SDimitry Andric Data.size() * COFF::RelocationSize; // one relocation for each resource. 487*0b57cec5SDimitry Andric FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 488*0b57cec5SDimitry Andric } 489*0b57cec5SDimitry Andric 490*0b57cec5SDimitry Andric void WindowsResourceCOFFWriter::performSectionTwoLayout() { 491*0b57cec5SDimitry Andric // add size of .rsrc$2 section, which contains all resource data on 8-byte 492*0b57cec5SDimitry Andric // alignment. 493*0b57cec5SDimitry Andric SectionTwoOffset = FileSize; 494*0b57cec5SDimitry Andric SectionTwoSize = 0; 495*0b57cec5SDimitry Andric for (auto const &Entry : Data) { 496*0b57cec5SDimitry Andric DataOffsets.push_back(SectionTwoSize); 497*0b57cec5SDimitry Andric SectionTwoSize += alignTo(Entry.size(), sizeof(uint64_t)); 498*0b57cec5SDimitry Andric } 499*0b57cec5SDimitry Andric FileSize += SectionTwoSize; 500*0b57cec5SDimitry Andric FileSize = alignTo(FileSize, SECTION_ALIGNMENT); 501*0b57cec5SDimitry Andric } 502*0b57cec5SDimitry Andric 503*0b57cec5SDimitry Andric std::unique_ptr<MemoryBuffer> 504*0b57cec5SDimitry Andric WindowsResourceCOFFWriter::write(uint32_t TimeDateStamp) { 505*0b57cec5SDimitry Andric BufferStart = OutputBuffer->getBufferStart(); 506*0b57cec5SDimitry Andric 507*0b57cec5SDimitry Andric writeCOFFHeader(TimeDateStamp); 508*0b57cec5SDimitry Andric writeFirstSectionHeader(); 509*0b57cec5SDimitry Andric writeSecondSectionHeader(); 510*0b57cec5SDimitry Andric writeFirstSection(); 511*0b57cec5SDimitry Andric writeSecondSection(); 512*0b57cec5SDimitry Andric writeSymbolTable(); 513*0b57cec5SDimitry Andric writeStringTable(); 514*0b57cec5SDimitry Andric 515*0b57cec5SDimitry Andric return std::move(OutputBuffer); 516*0b57cec5SDimitry Andric } 517*0b57cec5SDimitry Andric 518*0b57cec5SDimitry Andric void WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp) { 519*0b57cec5SDimitry Andric // Write the COFF header. 520*0b57cec5SDimitry Andric auto *Header = reinterpret_cast<coff_file_header *>(BufferStart); 521*0b57cec5SDimitry Andric Header->Machine = MachineType; 522*0b57cec5SDimitry Andric Header->NumberOfSections = 2; 523*0b57cec5SDimitry Andric Header->TimeDateStamp = TimeDateStamp; 524*0b57cec5SDimitry Andric Header->PointerToSymbolTable = SymbolTableOffset; 525*0b57cec5SDimitry Andric // One symbol for every resource plus 2 for each section and 1 for @feat.00 526*0b57cec5SDimitry Andric Header->NumberOfSymbols = Data.size() + 5; 527*0b57cec5SDimitry Andric Header->SizeOfOptionalHeader = 0; 528*0b57cec5SDimitry Andric // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it. 529*0b57cec5SDimitry Andric Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE; 530*0b57cec5SDimitry Andric } 531*0b57cec5SDimitry Andric 532*0b57cec5SDimitry Andric void WindowsResourceCOFFWriter::writeFirstSectionHeader() { 533*0b57cec5SDimitry Andric // Write the first section header. 534*0b57cec5SDimitry Andric CurrentOffset += sizeof(coff_file_header); 535*0b57cec5SDimitry Andric auto *SectionOneHeader = 536*0b57cec5SDimitry Andric reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 537*0b57cec5SDimitry Andric strncpy(SectionOneHeader->Name, ".rsrc$01", (size_t)COFF::NameSize); 538*0b57cec5SDimitry Andric SectionOneHeader->VirtualSize = 0; 539*0b57cec5SDimitry Andric SectionOneHeader->VirtualAddress = 0; 540*0b57cec5SDimitry Andric SectionOneHeader->SizeOfRawData = SectionOneSize; 541*0b57cec5SDimitry Andric SectionOneHeader->PointerToRawData = SectionOneOffset; 542*0b57cec5SDimitry Andric SectionOneHeader->PointerToRelocations = SectionOneRelocations; 543*0b57cec5SDimitry Andric SectionOneHeader->PointerToLinenumbers = 0; 544*0b57cec5SDimitry Andric SectionOneHeader->NumberOfRelocations = Data.size(); 545*0b57cec5SDimitry Andric SectionOneHeader->NumberOfLinenumbers = 0; 546*0b57cec5SDimitry Andric SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 547*0b57cec5SDimitry Andric SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 548*0b57cec5SDimitry Andric } 549*0b57cec5SDimitry Andric 550*0b57cec5SDimitry Andric void WindowsResourceCOFFWriter::writeSecondSectionHeader() { 551*0b57cec5SDimitry Andric // Write the second section header. 552*0b57cec5SDimitry Andric CurrentOffset += sizeof(coff_section); 553*0b57cec5SDimitry Andric auto *SectionTwoHeader = 554*0b57cec5SDimitry Andric reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); 555*0b57cec5SDimitry Andric strncpy(SectionTwoHeader->Name, ".rsrc$02", (size_t)COFF::NameSize); 556*0b57cec5SDimitry Andric SectionTwoHeader->VirtualSize = 0; 557*0b57cec5SDimitry Andric SectionTwoHeader->VirtualAddress = 0; 558*0b57cec5SDimitry Andric SectionTwoHeader->SizeOfRawData = SectionTwoSize; 559*0b57cec5SDimitry Andric SectionTwoHeader->PointerToRawData = SectionTwoOffset; 560*0b57cec5SDimitry Andric SectionTwoHeader->PointerToRelocations = 0; 561*0b57cec5SDimitry Andric SectionTwoHeader->PointerToLinenumbers = 0; 562*0b57cec5SDimitry Andric SectionTwoHeader->NumberOfRelocations = 0; 563*0b57cec5SDimitry Andric SectionTwoHeader->NumberOfLinenumbers = 0; 564*0b57cec5SDimitry Andric SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 565*0b57cec5SDimitry Andric SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; 566*0b57cec5SDimitry Andric } 567*0b57cec5SDimitry Andric 568*0b57cec5SDimitry Andric void WindowsResourceCOFFWriter::writeFirstSection() { 569*0b57cec5SDimitry Andric // Write section one. 570*0b57cec5SDimitry Andric CurrentOffset += sizeof(coff_section); 571*0b57cec5SDimitry Andric 572*0b57cec5SDimitry Andric writeDirectoryTree(); 573*0b57cec5SDimitry Andric writeDirectoryStringTable(); 574*0b57cec5SDimitry Andric writeFirstSectionRelocations(); 575*0b57cec5SDimitry Andric 576*0b57cec5SDimitry Andric CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 577*0b57cec5SDimitry Andric } 578*0b57cec5SDimitry Andric 579*0b57cec5SDimitry Andric void WindowsResourceCOFFWriter::writeSecondSection() { 580*0b57cec5SDimitry Andric // Now write the .rsrc$02 section. 581*0b57cec5SDimitry Andric for (auto const &RawDataEntry : Data) { 582*0b57cec5SDimitry Andric llvm::copy(RawDataEntry, BufferStart + CurrentOffset); 583*0b57cec5SDimitry Andric CurrentOffset += alignTo(RawDataEntry.size(), sizeof(uint64_t)); 584*0b57cec5SDimitry Andric } 585*0b57cec5SDimitry Andric 586*0b57cec5SDimitry Andric CurrentOffset = alignTo(CurrentOffset, SECTION_ALIGNMENT); 587*0b57cec5SDimitry Andric } 588*0b57cec5SDimitry Andric 589*0b57cec5SDimitry Andric void WindowsResourceCOFFWriter::writeSymbolTable() { 590*0b57cec5SDimitry Andric // Now write the symbol table. 591*0b57cec5SDimitry Andric // First, the feat symbol. 592*0b57cec5SDimitry Andric auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 593*0b57cec5SDimitry Andric strncpy(Symbol->Name.ShortName, "@feat.00", (size_t)COFF::NameSize); 594*0b57cec5SDimitry Andric Symbol->Value = 0x11; 595*0b57cec5SDimitry Andric Symbol->SectionNumber = 0xffff; 596*0b57cec5SDimitry Andric Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 597*0b57cec5SDimitry Andric Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 598*0b57cec5SDimitry Andric Symbol->NumberOfAuxSymbols = 0; 599*0b57cec5SDimitry Andric CurrentOffset += sizeof(coff_symbol16); 600*0b57cec5SDimitry Andric 601*0b57cec5SDimitry Andric // Now write the .rsrc1 symbol + aux. 602*0b57cec5SDimitry Andric Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 603*0b57cec5SDimitry Andric strncpy(Symbol->Name.ShortName, ".rsrc$01", (size_t)COFF::NameSize); 604*0b57cec5SDimitry Andric Symbol->Value = 0; 605*0b57cec5SDimitry Andric Symbol->SectionNumber = 1; 606*0b57cec5SDimitry Andric Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 607*0b57cec5SDimitry Andric Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 608*0b57cec5SDimitry Andric Symbol->NumberOfAuxSymbols = 1; 609*0b57cec5SDimitry Andric CurrentOffset += sizeof(coff_symbol16); 610*0b57cec5SDimitry Andric auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 611*0b57cec5SDimitry Andric CurrentOffset); 612*0b57cec5SDimitry Andric Aux->Length = SectionOneSize; 613*0b57cec5SDimitry Andric Aux->NumberOfRelocations = Data.size(); 614*0b57cec5SDimitry Andric Aux->NumberOfLinenumbers = 0; 615*0b57cec5SDimitry Andric Aux->CheckSum = 0; 616*0b57cec5SDimitry Andric Aux->NumberLowPart = 0; 617*0b57cec5SDimitry Andric Aux->Selection = 0; 618*0b57cec5SDimitry Andric CurrentOffset += sizeof(coff_aux_section_definition); 619*0b57cec5SDimitry Andric 620*0b57cec5SDimitry Andric // Now write the .rsrc2 symbol + aux. 621*0b57cec5SDimitry Andric Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 622*0b57cec5SDimitry Andric strncpy(Symbol->Name.ShortName, ".rsrc$02", (size_t)COFF::NameSize); 623*0b57cec5SDimitry Andric Symbol->Value = 0; 624*0b57cec5SDimitry Andric Symbol->SectionNumber = 2; 625*0b57cec5SDimitry Andric Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 626*0b57cec5SDimitry Andric Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 627*0b57cec5SDimitry Andric Symbol->NumberOfAuxSymbols = 1; 628*0b57cec5SDimitry Andric CurrentOffset += sizeof(coff_symbol16); 629*0b57cec5SDimitry Andric Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + 630*0b57cec5SDimitry Andric CurrentOffset); 631*0b57cec5SDimitry Andric Aux->Length = SectionTwoSize; 632*0b57cec5SDimitry Andric Aux->NumberOfRelocations = 0; 633*0b57cec5SDimitry Andric Aux->NumberOfLinenumbers = 0; 634*0b57cec5SDimitry Andric Aux->CheckSum = 0; 635*0b57cec5SDimitry Andric Aux->NumberLowPart = 0; 636*0b57cec5SDimitry Andric Aux->Selection = 0; 637*0b57cec5SDimitry Andric CurrentOffset += sizeof(coff_aux_section_definition); 638*0b57cec5SDimitry Andric 639*0b57cec5SDimitry Andric // Now write a symbol for each relocation. 640*0b57cec5SDimitry Andric for (unsigned i = 0; i < Data.size(); i++) { 641*0b57cec5SDimitry Andric auto RelocationName = formatv("$R{0:X-6}", i & 0xffffff).sstr<COFF::NameSize>(); 642*0b57cec5SDimitry Andric Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); 643*0b57cec5SDimitry Andric memcpy(Symbol->Name.ShortName, RelocationName.data(), (size_t) COFF::NameSize); 644*0b57cec5SDimitry Andric Symbol->Value = DataOffsets[i]; 645*0b57cec5SDimitry Andric Symbol->SectionNumber = 2; 646*0b57cec5SDimitry Andric Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; 647*0b57cec5SDimitry Andric Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; 648*0b57cec5SDimitry Andric Symbol->NumberOfAuxSymbols = 0; 649*0b57cec5SDimitry Andric CurrentOffset += sizeof(coff_symbol16); 650*0b57cec5SDimitry Andric } 651*0b57cec5SDimitry Andric } 652*0b57cec5SDimitry Andric 653*0b57cec5SDimitry Andric void WindowsResourceCOFFWriter::writeStringTable() { 654*0b57cec5SDimitry Andric // Just 4 null bytes for the string table. 655*0b57cec5SDimitry Andric auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset); 656*0b57cec5SDimitry Andric memset(COFFStringTable, 0, 4); 657*0b57cec5SDimitry Andric } 658*0b57cec5SDimitry Andric 659*0b57cec5SDimitry Andric void WindowsResourceCOFFWriter::writeDirectoryTree() { 660*0b57cec5SDimitry Andric // Traverse parsed resource tree breadth-first and write the corresponding 661*0b57cec5SDimitry Andric // COFF objects. 662*0b57cec5SDimitry Andric std::queue<const WindowsResourceParser::TreeNode *> Queue; 663*0b57cec5SDimitry Andric Queue.push(&Resources); 664*0b57cec5SDimitry Andric uint32_t NextLevelOffset = 665*0b57cec5SDimitry Andric sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() + 666*0b57cec5SDimitry Andric Resources.getIDChildren().size()) * 667*0b57cec5SDimitry Andric sizeof(coff_resource_dir_entry); 668*0b57cec5SDimitry Andric std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder; 669*0b57cec5SDimitry Andric uint32_t CurrentRelativeOffset = 0; 670*0b57cec5SDimitry Andric 671*0b57cec5SDimitry Andric while (!Queue.empty()) { 672*0b57cec5SDimitry Andric auto CurrentNode = Queue.front(); 673*0b57cec5SDimitry Andric Queue.pop(); 674*0b57cec5SDimitry Andric auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart + 675*0b57cec5SDimitry Andric CurrentOffset); 676*0b57cec5SDimitry Andric Table->Characteristics = CurrentNode->getCharacteristics(); 677*0b57cec5SDimitry Andric Table->TimeDateStamp = 0; 678*0b57cec5SDimitry Andric Table->MajorVersion = CurrentNode->getMajorVersion(); 679*0b57cec5SDimitry Andric Table->MinorVersion = CurrentNode->getMinorVersion(); 680*0b57cec5SDimitry Andric auto &IDChildren = CurrentNode->getIDChildren(); 681*0b57cec5SDimitry Andric auto &StringChildren = CurrentNode->getStringChildren(); 682*0b57cec5SDimitry Andric Table->NumberOfNameEntries = StringChildren.size(); 683*0b57cec5SDimitry Andric Table->NumberOfIDEntries = IDChildren.size(); 684*0b57cec5SDimitry Andric CurrentOffset += sizeof(coff_resource_dir_table); 685*0b57cec5SDimitry Andric CurrentRelativeOffset += sizeof(coff_resource_dir_table); 686*0b57cec5SDimitry Andric 687*0b57cec5SDimitry Andric // Write the directory entries immediately following each directory table. 688*0b57cec5SDimitry Andric for (auto const &Child : StringChildren) { 689*0b57cec5SDimitry Andric auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 690*0b57cec5SDimitry Andric CurrentOffset); 691*0b57cec5SDimitry Andric Entry->Identifier.setNameOffset( 692*0b57cec5SDimitry Andric StringTableOffsets[Child.second->getStringIndex()]); 693*0b57cec5SDimitry Andric if (Child.second->checkIsDataNode()) { 694*0b57cec5SDimitry Andric Entry->Offset.DataEntryOffset = NextLevelOffset; 695*0b57cec5SDimitry Andric NextLevelOffset += sizeof(coff_resource_data_entry); 696*0b57cec5SDimitry Andric DataEntriesTreeOrder.push_back(Child.second.get()); 697*0b57cec5SDimitry Andric } else { 698*0b57cec5SDimitry Andric Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 699*0b57cec5SDimitry Andric NextLevelOffset += sizeof(coff_resource_dir_table) + 700*0b57cec5SDimitry Andric (Child.second->getStringChildren().size() + 701*0b57cec5SDimitry Andric Child.second->getIDChildren().size()) * 702*0b57cec5SDimitry Andric sizeof(coff_resource_dir_entry); 703*0b57cec5SDimitry Andric Queue.push(Child.second.get()); 704*0b57cec5SDimitry Andric } 705*0b57cec5SDimitry Andric CurrentOffset += sizeof(coff_resource_dir_entry); 706*0b57cec5SDimitry Andric CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 707*0b57cec5SDimitry Andric } 708*0b57cec5SDimitry Andric for (auto const &Child : IDChildren) { 709*0b57cec5SDimitry Andric auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + 710*0b57cec5SDimitry Andric CurrentOffset); 711*0b57cec5SDimitry Andric Entry->Identifier.ID = Child.first; 712*0b57cec5SDimitry Andric if (Child.second->checkIsDataNode()) { 713*0b57cec5SDimitry Andric Entry->Offset.DataEntryOffset = NextLevelOffset; 714*0b57cec5SDimitry Andric NextLevelOffset += sizeof(coff_resource_data_entry); 715*0b57cec5SDimitry Andric DataEntriesTreeOrder.push_back(Child.second.get()); 716*0b57cec5SDimitry Andric } else { 717*0b57cec5SDimitry Andric Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); 718*0b57cec5SDimitry Andric NextLevelOffset += sizeof(coff_resource_dir_table) + 719*0b57cec5SDimitry Andric (Child.second->getStringChildren().size() + 720*0b57cec5SDimitry Andric Child.second->getIDChildren().size()) * 721*0b57cec5SDimitry Andric sizeof(coff_resource_dir_entry); 722*0b57cec5SDimitry Andric Queue.push(Child.second.get()); 723*0b57cec5SDimitry Andric } 724*0b57cec5SDimitry Andric CurrentOffset += sizeof(coff_resource_dir_entry); 725*0b57cec5SDimitry Andric CurrentRelativeOffset += sizeof(coff_resource_dir_entry); 726*0b57cec5SDimitry Andric } 727*0b57cec5SDimitry Andric } 728*0b57cec5SDimitry Andric 729*0b57cec5SDimitry Andric RelocationAddresses.resize(Data.size()); 730*0b57cec5SDimitry Andric // Now write all the resource data entries. 731*0b57cec5SDimitry Andric for (auto DataNodes : DataEntriesTreeOrder) { 732*0b57cec5SDimitry Andric auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart + 733*0b57cec5SDimitry Andric CurrentOffset); 734*0b57cec5SDimitry Andric RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset; 735*0b57cec5SDimitry Andric Entry->DataRVA = 0; // Set to zero because it is a relocation. 736*0b57cec5SDimitry Andric Entry->DataSize = Data[DataNodes->getDataIndex()].size(); 737*0b57cec5SDimitry Andric Entry->Codepage = 0; 738*0b57cec5SDimitry Andric Entry->Reserved = 0; 739*0b57cec5SDimitry Andric CurrentOffset += sizeof(coff_resource_data_entry); 740*0b57cec5SDimitry Andric CurrentRelativeOffset += sizeof(coff_resource_data_entry); 741*0b57cec5SDimitry Andric } 742*0b57cec5SDimitry Andric } 743*0b57cec5SDimitry Andric 744*0b57cec5SDimitry Andric void WindowsResourceCOFFWriter::writeDirectoryStringTable() { 745*0b57cec5SDimitry Andric // Now write the directory string table for .rsrc$01 746*0b57cec5SDimitry Andric uint32_t TotalStringTableSize = 0; 747*0b57cec5SDimitry Andric for (auto &String : StringTable) { 748*0b57cec5SDimitry Andric uint16_t Length = String.size(); 749*0b57cec5SDimitry Andric support::endian::write16le(BufferStart + CurrentOffset, Length); 750*0b57cec5SDimitry Andric CurrentOffset += sizeof(uint16_t); 751*0b57cec5SDimitry Andric auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset); 752*0b57cec5SDimitry Andric llvm::copy(String, Start); 753*0b57cec5SDimitry Andric CurrentOffset += Length * sizeof(UTF16); 754*0b57cec5SDimitry Andric TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t); 755*0b57cec5SDimitry Andric } 756*0b57cec5SDimitry Andric CurrentOffset += 757*0b57cec5SDimitry Andric alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize; 758*0b57cec5SDimitry Andric } 759*0b57cec5SDimitry Andric 760*0b57cec5SDimitry Andric void WindowsResourceCOFFWriter::writeFirstSectionRelocations() { 761*0b57cec5SDimitry Andric 762*0b57cec5SDimitry Andric // Now write the relocations for .rsrc$01 763*0b57cec5SDimitry Andric // Five symbols already in table before we start, @feat.00 and 2 for each 764*0b57cec5SDimitry Andric // .rsrc section. 765*0b57cec5SDimitry Andric uint32_t NextSymbolIndex = 5; 766*0b57cec5SDimitry Andric for (unsigned i = 0; i < Data.size(); i++) { 767*0b57cec5SDimitry Andric auto *Reloc = 768*0b57cec5SDimitry Andric reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset); 769*0b57cec5SDimitry Andric Reloc->VirtualAddress = RelocationAddresses[i]; 770*0b57cec5SDimitry Andric Reloc->SymbolTableIndex = NextSymbolIndex++; 771*0b57cec5SDimitry Andric switch (MachineType) { 772*0b57cec5SDimitry Andric case COFF::IMAGE_FILE_MACHINE_ARMNT: 773*0b57cec5SDimitry Andric Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB; 774*0b57cec5SDimitry Andric break; 775*0b57cec5SDimitry Andric case COFF::IMAGE_FILE_MACHINE_AMD64: 776*0b57cec5SDimitry Andric Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB; 777*0b57cec5SDimitry Andric break; 778*0b57cec5SDimitry Andric case COFF::IMAGE_FILE_MACHINE_I386: 779*0b57cec5SDimitry Andric Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB; 780*0b57cec5SDimitry Andric break; 781*0b57cec5SDimitry Andric case COFF::IMAGE_FILE_MACHINE_ARM64: 782*0b57cec5SDimitry Andric Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB; 783*0b57cec5SDimitry Andric break; 784*0b57cec5SDimitry Andric default: 785*0b57cec5SDimitry Andric llvm_unreachable("unknown machine type"); 786*0b57cec5SDimitry Andric } 787*0b57cec5SDimitry Andric CurrentOffset += sizeof(coff_relocation); 788*0b57cec5SDimitry Andric } 789*0b57cec5SDimitry Andric } 790*0b57cec5SDimitry Andric 791*0b57cec5SDimitry Andric Expected<std::unique_ptr<MemoryBuffer>> 792*0b57cec5SDimitry Andric writeWindowsResourceCOFF(COFF::MachineTypes MachineType, 793*0b57cec5SDimitry Andric const WindowsResourceParser &Parser, 794*0b57cec5SDimitry Andric uint32_t TimeDateStamp) { 795*0b57cec5SDimitry Andric Error E = Error::success(); 796*0b57cec5SDimitry Andric WindowsResourceCOFFWriter Writer(MachineType, Parser, E); 797*0b57cec5SDimitry Andric if (E) 798*0b57cec5SDimitry Andric return std::move(E); 799*0b57cec5SDimitry Andric return Writer.write(TimeDateStamp); 800*0b57cec5SDimitry Andric } 801*0b57cec5SDimitry Andric 802*0b57cec5SDimitry Andric } // namespace object 803*0b57cec5SDimitry Andric } // namespace llvm 804