Index: llvm/include/llvm/Object/COFF.h =================================================================== --- llvm/include/llvm/Object/COFF.h +++ llvm/include/llvm/Object/COFF.h @@ -646,6 +646,13 @@ } Offset; }; +struct coff_resource_data_entry { + support::ulittle32_t DataRVA; + support::ulittle32_t DataSize; + support::ulittle32_t Codepage; + support::ulittle32_t Reserved; +}; + struct coff_resource_dir_table { support::ulittle32_t Characteristics; support::ulittle32_t TimeDateStamp; Index: llvm/include/llvm/Object/WindowsResource.h =================================================================== --- llvm/include/llvm/Object/WindowsResource.h +++ llvm/include/llvm/Object/WindowsResource.h @@ -44,10 +44,19 @@ #include namespace llvm { + +class FileOutputBuffer; + namespace object { +namespace { +template using Table = std::vector>; +} + class WindowsResource; +enum class machine { UNKNOWN = 0, ARM, X64, X86 }; + class ResourceEntryRef { public: Error moveNext(bool &End); @@ -58,6 +67,14 @@ ArrayRef getNameString() const { return Name; } uint16_t getNameID() const { return NameID; } uint16_t getLanguage() const { return Suffix->Language; } + uint16_t getMajorVersion() const { + return (maskLeadingOnes(16) & Suffix->Version) >> 16; + } + uint16_t getMinorVersion() const { + return maskTrailingOnes(16) & Suffix->Version; + } + uint32_t getCharacteristics() const { return Suffix->Characteristics; } + ArrayRef getData() const { return Data; } private: friend class WindowsResource; @@ -106,34 +123,71 @@ class WindowsResourceParser { public: + class TreeNode; WindowsResourceParser(); Error parse(WindowsResource *WR); void printTree() const; -private: + const TreeNode &getTree() const { return Root; } + + const Table &getData() const { return Data; } + + const Table &getStringTable() const { return StringTable; } + class TreeNode { public: - TreeNode() = default; - explicit TreeNode(ArrayRef Ref); + template + using Children = std::map>; + + explicit TreeNode(bool IsStringNode); + TreeNode(bool IsDataNode, uint16_t MajorVersion, uint16_t MinorVersion, + uint32_t Characteristics); void addEntry(const ResourceEntryRef &Entry); void print(ScopedPrinter &Writer, StringRef Name) const; + uint32_t getTreeSize() const; + uint32_t getStringIndex() const { return StringIndex; } + uint32_t getDataIndex() const { return DataIndex; } + uint16_t getMajorVersion() const { return MajorVersion; } + uint16_t getMinorVersion() const { return MinorVersion; } + uint32_t getCharacteristics() const { return Characteristics; } + bool checkIsDataNode() const { return IsDataNode; } + const Children &getIDChildren() const { return IDChildren; } + const Children &getStringChildren() const { + return StringChildren; + } private: + static uint32_t StringCount; + static uint32_t DataCount; + TreeNode &addTypeNode(const ResourceEntryRef &Entry); TreeNode &addNameNode(const ResourceEntryRef &Entry); TreeNode &addLanguageNode(const ResourceEntryRef &Entry); - TreeNode &addChild(uint32_t ID); + TreeNode &addChild(uint32_t ID, bool IsDataNode = false, + uint16_t MajorVersion = 0, uint16_t MinorVersion = 0, + uint32_t Characteristics = 0); TreeNode &addChild(ArrayRef NameRef); - std::vector Name; - std::map> IDChildren; - std::map> StringChildren; + bool IsDataNode; + uint32_t StringIndex; + uint32_t DataIndex; + Children IDChildren; + Children StringChildren; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t Characteristics; }; +private: TreeNode Root; + Table Data; + Table StringTable; }; +Error writeWindowsResourceCOFF(StringRef OutputFile, machine MachineType, + const WindowsResourceParser &Parser); + } // namespace object } // namespace llvm Index: llvm/lib/Object/WindowsResource.cpp =================================================================== --- llvm/lib/Object/WindowsResource.cpp +++ llvm/lib/Object/WindowsResource.cpp @@ -13,6 +13,11 @@ #include "llvm/Object/WindowsResource.h" #include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MathExtras.h" +#include +#include #include #include @@ -29,6 +34,9 @@ static const size_t NullEntrySize = 16; +uint32_t WindowsResourceParser::TreeNode::StringCount = 0; +uint32_t WindowsResourceParser::TreeNode::DataCount = 0; + WindowsResource::WindowsResource(MemoryBufferRef Source) : Binary(Binary::ID_WinRes, Source) { size_t LeadingSize = ResourceMagicSize + NullEntrySize; @@ -115,7 +123,7 @@ return Error::success(); } -WindowsResourceParser::WindowsResourceParser() {} +WindowsResourceParser::WindowsResourceParser() : Root(false) {} Error WindowsResourceParser::parse(WindowsResource *WR) { auto EntryOrErr = WR->getHeadEntry(); @@ -127,6 +135,14 @@ while (!End) { + Data.emplace_back(Entry.getData()); + + if (Entry.checkTypeString()) + StringTable.emplace_back(Entry.getTypeString()); + + if (Entry.checkNameString()) + StringTable.emplace_back(Entry.getNameString()); + Root.addEntry(Entry); RETURN_IF_ERROR(Entry.moveNext(End)); @@ -146,8 +162,21 @@ NameNode.addLanguageNode(Entry); } -WindowsResourceParser::TreeNode::TreeNode(ArrayRef NameRef) - : Name(NameRef) {} +WindowsResourceParser::TreeNode::TreeNode(bool IsStringNode) + : IsDataNode(false), MajorVersion(0), MinorVersion(0), Characteristics(0) { + if (IsStringNode) + StringIndex = StringCount++; +} + +WindowsResourceParser::TreeNode::TreeNode(bool IsDataNode, + uint16_t MajorVersion, + uint16_t MinorVersion, + uint32_t Characteristics) + : IsDataNode(IsDataNode), MajorVersion(MajorVersion), + MinorVersion(MinorVersion), Characteristics(Characteristics) { + if (IsDataNode) + DataIndex = DataCount++; +} WindowsResourceParser::TreeNode & WindowsResourceParser::TreeNode::addTypeNode(const ResourceEntryRef &Entry) { @@ -168,14 +197,17 @@ WindowsResourceParser::TreeNode & WindowsResourceParser::TreeNode::addLanguageNode( const ResourceEntryRef &Entry) { - return addChild(Entry.getLanguage()); + return addChild(Entry.getLanguage(), true, Entry.getMajorVersion(), + Entry.getMinorVersion(), Entry.getCharacteristics()); } -WindowsResourceParser::TreeNode & -WindowsResourceParser::TreeNode::addChild(uint32_t ID) { +WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addChild( + uint32_t ID, bool IsDataNode, uint16_t MajorVersion, uint16_t MinorVersion, + uint32_t Characteristics) { auto Child = IDChildren.find(ID); if (Child == IDChildren.end()) { - auto NewChild = llvm::make_unique(ID); + auto NewChild = llvm::make_unique( + IsDataNode, MajorVersion, MinorVersion, Characteristics); WindowsResourceParser::TreeNode &Node = *NewChild; IDChildren.emplace(ID, std::move(NewChild)); return Node; @@ -199,7 +231,7 @@ auto Child = StringChildren.find(NameString); if (Child == StringChildren.end()) { - auto NewChild = llvm::make_unique(NameRef); + auto NewChild = llvm::make_unique(true); WindowsResourceParser::TreeNode &Node = *NewChild; StringChildren.emplace(NameString, std::move(NewChild)); return Node; @@ -218,5 +250,456 @@ } } +// This function returns the size of the entire resource tree, including +// directory tables, directory entries, and data entries. It does not include +// the directory strings or the relocations of the .rsrc section. +uint32_t WindowsResourceParser::TreeNode::getTreeSize() const { + uint32_t Size = (IDChildren.size() + StringChildren.size()) * + sizeof(llvm::object::coff_resource_dir_entry); + + // Reached a node pointing to a data entry. + if (IsDataNode) { + Size += sizeof(llvm::object::coff_resource_data_entry); + return Size; + } + + // If the node does not point to data, it must have a directory table pointing + // to other nodes. + Size += sizeof(llvm::object::coff_resource_dir_table); + + for (auto const &Child : StringChildren) { + Size += Child.second->getTreeSize(); + } + for (auto const &Child : IDChildren) { + Size += Child.second->getTreeSize(); + } + return Size; +} + +class WindowsResourceCOFFWriter { +public: + WindowsResourceCOFFWriter(StringRef OutputFile, machine MachineType, + const WindowsResourceParser &Parser, Error &E); + + Error write(); + +private: + void performFileLayout(); + void performSectionOneLayout(); + void performSectionTwoLayout(); + void writeCOFFHeader(); + void writeFirstSectionHeader(); + void writeSecondSectionHeader(); + void writeFirstSection(); + void writeSecondSection(); + void writeSymbolTable(); + void writeStringTable(); + void writeDirectoryTree(); + void writeDirectoryStringTable(); + void writeFirstSectionRelocations(); + std::unique_ptr Buffer; + uint8_t *Current; + machine MachineType; + const WindowsResourceParser::TreeNode &Resources; + const Table &Data; + uint64_t FileSize; + uint32_t SymbolTableOffset; + uint32_t SectionOneSize; + uint32_t SectionOneOffset; + uint32_t SectionOneRelocations; + uint32_t SectionTwoSize; + uint32_t SectionTwoOffset; + const Table &StringTable; + std::vector StringTableOffsets; + std::vector DataOffsets; + std::vector RelocationAddresses; +}; + +WindowsResourceCOFFWriter::WindowsResourceCOFFWriter( + StringRef OutputFile, machine MachineType, + const WindowsResourceParser &Parser, Error &E) + : MachineType(MachineType), Resources(Parser.getTree()), + Data(Parser.getData()), StringTable(Parser.getStringTable()) { + performFileLayout(); + + ErrorOr> BufferOrErr = + FileOutputBuffer::create(OutputFile, FileSize); + if (!BufferOrErr) { + E = errorCodeToError(BufferOrErr.getError()); + return; + } + + Buffer = std::move(*BufferOrErr); +} + +void WindowsResourceCOFFWriter::performFileLayout() { + // Add size of COFF header. + FileSize = llvm::COFF::Header16Size; + + // one .rsrc section header for directory tree, another for resource data. + FileSize += 2 * llvm::COFF::SectionSize; + + performSectionOneLayout(); + performSectionTwoLayout(); + + SymbolTableOffset = + FileSize; // We have reached the address of the symbol table. + + FileSize += llvm::COFF::Symbol16Size; // size of the @feat.00 symbol. + FileSize += 4 * llvm::COFF::Symbol16Size; // symbol + aux for each section. + FileSize += + Data.size() * llvm::COFF::Symbol16Size; // one symbol for each resource. + FileSize += 4; // four null bytes for the string table. +} + +void WindowsResourceCOFFWriter::performSectionOneLayout() { + SectionOneOffset = FileSize; + + SectionOneSize = Resources.getTreeSize(); + uint32_t CurrentStringOffset = SectionOneSize; + uint32_t TotalStringTableSize = 0; + for (auto const &String : StringTable) { + StringTableOffsets.push_back(CurrentStringOffset); + uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t); + CurrentStringOffset += StringSize; + TotalStringTableSize += StringSize; + } + SectionOneSize += alignTo(TotalStringTableSize, sizeof(uint32_t)); + + // account for the relocations of section one. + SectionOneRelocations = FileSize + SectionOneSize; + FileSize += SectionOneSize; + FileSize += Data.size() * + llvm::COFF::RelocationSize; // one relocation for each resource. +} + +void WindowsResourceCOFFWriter::performSectionTwoLayout() { + // add size of .rsrc$2 section, which contains all resource data on 8-byte + // alignment. + SectionTwoOffset = FileSize; + SectionTwoSize = 0; + for (auto const &Entry : Data) { + DataOffsets.push_back(SectionTwoSize); + SectionTwoSize += llvm::alignTo(Entry.size(), sizeof(uint64_t)); + } + FileSize += SectionTwoSize; +} + +static std::time_t getTime() { + std::time_t Now = time(nullptr); + if (Now < 0 || !isUInt<32>(Now)) + return UINT32_MAX; + return Now; +} + +Error WindowsResourceCOFFWriter::write() { + Current = Buffer->getBufferStart(); + + writeCOFFHeader(); + writeFirstSectionHeader(); + writeSecondSectionHeader(); + writeFirstSection(); + writeSecondSection(); + writeSymbolTable(); + writeStringTable(); + + if (auto EC = Buffer->commit()) { + return errorCodeToError(EC); + } + + return Error::success(); +} + +void WindowsResourceCOFFWriter::writeCOFFHeader() { + // Write the COFF header. + auto *Header = reinterpret_cast(Current); + switch (MachineType) { + case machine::ARM: + Header->Machine = llvm::COFF::IMAGE_FILE_MACHINE_ARMNT; + break; + case machine::X64: + Header->Machine = llvm::COFF::IMAGE_FILE_MACHINE_AMD64; + break; + case machine::X86: + Header->Machine = llvm::COFF::IMAGE_FILE_MACHINE_I386; + break; + default: + Header->Machine = llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; + } + Header->NumberOfSections = 2; + Header->TimeDateStamp = getTime(); + Header->PointerToSymbolTable = SymbolTableOffset; + // One symbol for every resource plus 2 for each section and @feat.00 + Header->NumberOfSymbols = Data.size() + 5; + Header->SizeOfOptionalHeader = 0; + Header->Characteristics = llvm::COFF::IMAGE_FILE_32BIT_MACHINE; +} + +void WindowsResourceCOFFWriter::writeFirstSectionHeader() { + // Write the first section header. + Current += sizeof(llvm::object::coff_file_header); + auto *SectionOneHeader = + reinterpret_cast(Current); + strncpy(SectionOneHeader->Name, ".rsrc$01", (size_t)llvm::COFF::NameSize); + SectionOneHeader->VirtualSize = 0; + SectionOneHeader->VirtualAddress = 0; + SectionOneHeader->SizeOfRawData = SectionOneSize; + SectionOneHeader->PointerToRawData = SectionOneOffset; + SectionOneHeader->PointerToRelocations = SectionOneRelocations; + SectionOneHeader->PointerToLinenumbers = 0; + SectionOneHeader->NumberOfRelocations = Data.size(); + SectionOneHeader->NumberOfLinenumbers = 0; + SectionOneHeader->Characteristics = llvm::COFF::IMAGE_SCN_ALIGN_1BYTES; + SectionOneHeader->Characteristics += + llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; + SectionOneHeader->Characteristics += llvm::COFF::IMAGE_SCN_MEM_DISCARDABLE; + SectionOneHeader->Characteristics += llvm::COFF::IMAGE_SCN_MEM_READ; +} + +void WindowsResourceCOFFWriter::writeSecondSectionHeader() { + // Write the second section header. + Current += sizeof(llvm::object::coff_section); + auto *SectionTwoHeader = + reinterpret_cast(Current); + strncpy(SectionTwoHeader->Name, ".rsrc$02", (size_t)llvm::COFF::NameSize); + SectionTwoHeader->VirtualSize = 0; + SectionTwoHeader->VirtualAddress = 0; + SectionTwoHeader->SizeOfRawData = SectionTwoSize; + SectionTwoHeader->PointerToRawData = SectionTwoOffset; + SectionTwoHeader->PointerToRelocations = 0; + SectionTwoHeader->PointerToLinenumbers = 0; + SectionTwoHeader->NumberOfRelocations = 0; + SectionTwoHeader->NumberOfLinenumbers = 0; + SectionTwoHeader->Characteristics = + llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; + SectionTwoHeader->Characteristics += llvm::COFF::IMAGE_SCN_MEM_READ; +} + +void WindowsResourceCOFFWriter::writeFirstSection() { + // Write section one. + Current += sizeof(llvm::object::coff_section); + + writeDirectoryTree(); + writeDirectoryStringTable(); + writeFirstSectionRelocations(); +} + +void WindowsResourceCOFFWriter::writeSecondSection() { + // Now write the .rsrc$02 section. + for (auto const &RawDataEntry : Data) { + std::copy(RawDataEntry.begin(), RawDataEntry.end(), Current); + Current += alignTo(RawDataEntry.size(), sizeof(uint64_t)); + } +} + +void WindowsResourceCOFFWriter::writeSymbolTable() { + // Now write the symbol table. + // First, the feat symbol. + auto *Symbol = reinterpret_cast(Current); + strncpy(Symbol->Name.ShortName, "@feat.00", (size_t)llvm::COFF::NameSize); + Symbol->Value = 0x11; + Symbol->SectionNumber = 0xffff; + Symbol->Type = llvm::COFF::IMAGE_SYM_DTYPE_NULL; + Symbol->StorageClass = llvm::COFF::IMAGE_SYM_CLASS_STATIC; + Symbol->NumberOfAuxSymbols = 0; + Current += sizeof(llvm::object::coff_symbol16); + + // Now write the .rsrc1 symbol + aux. + Symbol = reinterpret_cast(Current); + strncpy(Symbol->Name.ShortName, ".rsrc$01", (size_t)llvm::COFF::NameSize); + Symbol->Value = 0; + Symbol->SectionNumber = 1; + Symbol->Type = llvm::COFF::IMAGE_SYM_DTYPE_NULL; + Symbol->StorageClass = llvm::COFF::IMAGE_SYM_CLASS_STATIC; + Symbol->NumberOfAuxSymbols = 1; + Current += sizeof(llvm::object::coff_symbol16); + auto *Aux = + reinterpret_cast(Current); + Aux->Length = SectionOneSize; + Aux->NumberOfRelocations = Data.size(); + Aux->NumberOfLinenumbers = 0; + Aux->CheckSum = 0; + Aux->NumberLowPart = 0; + Aux->Selection = 0; + Current += sizeof(llvm::object::coff_aux_section_definition); + + // Now write the .rsrc2 symbol + aux. + Symbol = reinterpret_cast(Current); + strncpy(Symbol->Name.ShortName, ".rsrc$02", (size_t)llvm::COFF::NameSize); + Symbol->Value = 0; + Symbol->SectionNumber = 2; + Symbol->Type = llvm::COFF::IMAGE_SYM_DTYPE_NULL; + Symbol->StorageClass = llvm::COFF::IMAGE_SYM_CLASS_STATIC; + Symbol->NumberOfAuxSymbols = 1; + Current += sizeof(llvm::object::coff_symbol16); + Aux = reinterpret_cast(Current); + Aux->Length = SectionTwoSize; + Aux->NumberOfRelocations = 0; + Aux->NumberOfLinenumbers = 0; + Aux->CheckSum = 0; + Aux->NumberLowPart = 0; + Aux->Selection = 0; + Current += sizeof(llvm::object::coff_aux_section_definition); + + // Now write a symbol for each relocation. + for (unsigned i = 0; i < Data.size(); i++) { + char RelocationName[9]; + sprintf(RelocationName, "$R%06d", DataOffsets[i]); + Symbol = reinterpret_cast(Current); + strncpy(Symbol->Name.ShortName, RelocationName, + (size_t)llvm::COFF::NameSize); + Symbol->Value = DataOffsets[i]; + Symbol->SectionNumber = 1; + Symbol->Type = llvm::COFF::IMAGE_SYM_DTYPE_NULL; + Symbol->StorageClass = llvm::COFF::IMAGE_SYM_CLASS_STATIC; + Symbol->NumberOfAuxSymbols = 0; + Current += sizeof(llvm::object::coff_symbol16); + } +} + +void WindowsResourceCOFFWriter::writeStringTable() { + // Just 4 null bytes for the string table. + auto COFFStringTable = reinterpret_cast(Current); + *COFFStringTable = 0; +} + +void WindowsResourceCOFFWriter::writeDirectoryTree() { + // Traverse parsed resource tree breadth-first and write the corresponding + // COFF objects. + std::queue Queue; + Queue.push(&Resources); + uint32_t NextLevelOffset = sizeof(llvm::object::coff_resource_dir_table) + + (Resources.getStringChildren().size() + + Resources.getIDChildren().size()) * + sizeof(llvm::object::coff_resource_dir_entry); + std::vector DataEntriesTreeOrder; + uint32_t CurrentRelativeOffset = 0; + + while (!Queue.empty()) { + auto CurrentNode = Queue.front(); + Queue.pop(); + auto *Table = + reinterpret_cast(Current); + Table->Characteristics = CurrentNode->getCharacteristics(); + Table->TimeDateStamp = 0; + Table->MajorVersion = CurrentNode->getMajorVersion(); + Table->MinorVersion = CurrentNode->getMinorVersion(); + auto &IDChildren = CurrentNode->getIDChildren(); + auto &StringChildren = CurrentNode->getStringChildren(); + Table->NumberOfNameEntries = StringChildren.size(); + Table->NumberOfIDEntries = IDChildren.size(); + Current += sizeof(llvm::object::coff_resource_dir_table); + CurrentRelativeOffset += sizeof(llvm::object::coff_resource_dir_table); + + // Write the directory entries immediately following each directory table. + for (auto const &Child : StringChildren) { + auto *Entry = + reinterpret_cast(Current); + Entry->Identifier.NameOffset = + StringTableOffsets[Child.second->getStringIndex()]; + if (Child.second->checkIsDataNode()) { + Entry->Offset.DataEntryOffset = NextLevelOffset; + NextLevelOffset += sizeof(llvm::object::coff_resource_data_entry); + DataEntriesTreeOrder.push_back(Child.second.get()); + } else { + Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); + NextLevelOffset += sizeof(llvm::object::coff_resource_dir_table) + + (Child.second->getStringChildren().size() + + Child.second->getIDChildren().size()) * + sizeof(llvm::object::coff_resource_dir_entry); + Queue.push(Child.second.get()); + } + Current += sizeof(llvm::object::coff_resource_dir_entry); + CurrentRelativeOffset += sizeof(llvm::object::coff_resource_dir_entry); + } + for (auto const &Child : IDChildren) { + auto *Entry = + reinterpret_cast(Current); + Entry->Identifier.ID = Child.first; + if (Child.second->checkIsDataNode()) { + Entry->Offset.DataEntryOffset = NextLevelOffset; + NextLevelOffset += sizeof(llvm::object::coff_resource_data_entry); + DataEntriesTreeOrder.push_back(Child.second.get()); + } else { + Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); + NextLevelOffset += sizeof(llvm::object::coff_resource_dir_table) + + (Child.second->getStringChildren().size() + + Child.second->getIDChildren().size()) * + sizeof(llvm::object::coff_resource_dir_entry); + Queue.push(Child.second.get()); + } + Current += sizeof(llvm::object::coff_resource_dir_entry); + CurrentRelativeOffset += sizeof(llvm::object::coff_resource_dir_entry); + } + } + + RelocationAddresses.resize(Data.size()); + // Now write all the resource data entries. + for (auto DataNodes : DataEntriesTreeOrder) { + auto *Entry = + reinterpret_cast(Current); + RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset; + Entry->DataRVA = 0; // Set to zero because it is a relocation. + Entry->DataSize = Data[DataNodes->getDataIndex()].size(); + Entry->Codepage = 0; + Entry->Reserved = 0; + Current += sizeof(llvm::object::coff_resource_data_entry); + CurrentRelativeOffset += sizeof(llvm::object::coff_resource_data_entry); + } +} + +void WindowsResourceCOFFWriter::writeDirectoryStringTable() { + // Now write the directory string table for .rsrc$01 + uint32_t TotalStringTableSize = 0; + for (auto String : StringTable) { + auto *LengthField = reinterpret_cast(Current); + uint16_t Length = String.size(); + *LengthField = Length; + Current += sizeof(uint16_t); + auto *Start = reinterpret_cast(Current); + std::copy(String.begin(), String.end(), Start); + Current += Length * sizeof(UTF16); + TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t); + } + Current += + alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize; +} + +void WindowsResourceCOFFWriter::writeFirstSectionRelocations() { + + // Now write the relocations for .rsrc$01 + // Five symbols already in table before we start, @feat.00 and 2 for each + // .rsrc section. + uint32_t NextSymbolIndex = 5; + for (unsigned i = 0; i < Data.size(); i++) { + auto *Reloc = reinterpret_cast(Current); + Reloc->VirtualAddress = RelocationAddresses[i]; + Reloc->SymbolTableIndex = NextSymbolIndex++; + switch (MachineType) { + case machine::ARM: + Reloc->Type = llvm::COFF::IMAGE_REL_ARM_ADDR32NB; + break; + case machine::X64: + Reloc->Type = llvm::COFF::IMAGE_REL_AMD64_ADDR32NB; + break; + case machine::X86: + Reloc->Type = llvm::COFF::IMAGE_REL_I386_DIR32NB; + break; + default: + Reloc->Type = 0; + } + Current += sizeof(llvm::object::coff_relocation); + } +} + +Error writeWindowsResourceCOFF(StringRef OutputFile, machine MachineType, + const WindowsResourceParser &Parser) { + Error E = Error::success(); + WindowsResourceCOFFWriter Writer(OutputFile, MachineType, Parser, E); + if (E) + return E; + return Writer.write(); +} + } // namespace object } // namespace llvm Index: llvm/test/tools/llvm-cvtres/object.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-cvtres/object.test @@ -0,0 +1,229 @@ +// Check COFF emission of cvtres +// The input was generated with the following command, using the original Windows +// rc.exe: +// > rc /fo test_resource.res /nologo test_resource.rc +// The object file we are comparing against was generated with this command using +// the original cvtres. +// > cvtres /machine:X86 /readonly /nologo /out:test_resource.o test_resource.res + +RUN: llvm-cvtres /out:%t %p/Inputs/test_resource.res +RUN: llvm-readobj -coff-resources -section-data %t | FileCheck %s + +CHECK: Resources [ +CHECK-NEXT: String Name Entries: 1 +CHECK-NEXT: ID Entries: 4 +CHECK-NEXT: Type: STRINGARRAY [ +CHECK-NEXT: String Name Entries: 1 +CHECK-NEXT: ID Entries: 0 +CHECK-NEXT: Name: MYRESOURCE [ +CHECK-NEXT: String Name Entries: 0 +CHECK-NEXT: ID Entries: 1 +CHECK-NEXT: Language: (ID 1033) [ +CHECK-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +CHECK-NEXT: Major Version: 0 +CHECK-NEXT: Minor Version: 0 +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: Type: kRT_BITMAP (ID 2) [ +CHECK-NEXT: String Name Entries: 2 +CHECK-NEXT: ID Entries: 0 +CHECK-NEXT: Name: CURSOR [ +CHECK-NEXT: String Name Entries: 0 +CHECK-NEXT: ID Entries: 1 +CHECK-NEXT: Language: (ID 1033) [ +CHECK-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +CHECK-NEXT: Major Version: 0 +CHECK-NEXT: Minor Version: 0 +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: Name: OKAY [ +CHECK-NEXT: String Name Entries: 0 +CHECK-NEXT: ID Entries: 1 +CHECK-NEXT: Language: (ID 1033) [ +CHECK-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +CHECK-NEXT: Major Version: 0 +CHECK-NEXT: Minor Version: 0 +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: Type: kRT_MENU (ID 4) [ +CHECK-NEXT: String Name Entries: 1 +CHECK-NEXT: ID Entries: 1 +CHECK-NEXT: Name: "EAT" [ +CHECK-NEXT: String Name Entries: 0 +CHECK-NEXT: ID Entries: 1 +CHECK-NEXT: Language: (ID 3081) [ +CHECK-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +CHECK-NEXT: Major Version: 0 +CHECK-NEXT: Minor Version: 0 +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: Name: (ID 14432) [ +CHECK-NEXT: String Name Entries: 0 +CHECK-NEXT: ID Entries: 1 +CHECK-NEXT: Language: (ID 2052) [ +CHECK-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +CHECK-NEXT: Major Version: 0 +CHECK-NEXT: Minor Version: 0 +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: Type: kRT_DIALOG (ID 5) [ +CHECK-NEXT: String Name Entries: 1 +CHECK-NEXT: ID Entries: 0 +CHECK-NEXT: Name: TESTDIALOG [ +CHECK-NEXT: String Name Entries: 0 +CHECK-NEXT: ID Entries: 1 +CHECK-NEXT: Language: (ID 1033) [ +CHECK-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +CHECK-NEXT: Major Version: 0 +CHECK-NEXT: Minor Version: 0 +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: Type: kRT_ACCELERATOR (ID 9) [ +CHECK-NEXT: String Name Entries: 1 +CHECK-NEXT: ID Entries: 1 +CHECK-NEXT: Name: MYACCELERATORS [ +CHECK-NEXT: String Name Entries: 0 +CHECK-NEXT: ID Entries: 1 +CHECK-NEXT: Language: (ID 1033) [ +CHECK-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +CHECK-NEXT: Major Version: 0 +CHECK-NEXT: Minor Version: 0 +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: Name: (ID 12) [ +CHECK-NEXT: String Name Entries: 0 +CHECK-NEXT: ID Entries: 1 +CHECK-NEXT: Language: (ID 1033) [ +CHECK-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +CHECK-NEXT: Major Version: 0 +CHECK-NEXT: Minor Version: 0 +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-DAG: .rsrc$02 Data ( +CHECK-NEXT: 0000: 11000300 E7030000 0D004400 4C040000 |..........D.L...| +CHECK-NEXT: 0010: 82001200 BC010000 28000000 10000000 |........(.......| +CHECK-NEXT: 0020: 10000000 01001800 00000000 00030000 |................| +CHECK-NEXT: 0030: C40E0000 C40E0000 00000000 00000000 |................| +CHECK-NEXT: 0040: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0050: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0060: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0070: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0080: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0090: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 00A0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 00B0: FFFFFFFF FF7F7F7F 7C7C7C78 78787575 |........|||xxxuu| +CHECK-NEXT: 00C0: 75FFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |u...............| +CHECK-NEXT: 00D0: FFFFFFFF FFFFFFFF FFFFFFFF 979797FF |................| +CHECK-NEXT: 00E0: FFFFFFFF FF838383 AAAAAADB DBDB7979 |..............yy| +CHECK-NEXT: 00F0: 79757575 FFFFFFFF FFFFFFFF FFFFFFFF |yuuu............| +CHECK-NEXT: 0100: FFFFFFFF FFFFFFFF FFFFFFFF 9C9C9C98 |................| +CHECK-NEXT: 0110: 9898FFFF FF888888 DBDBDBB7 B7B77D7D |..............}}| +CHECK-NEXT: 0120: 7DFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |}...............| +CHECK-NEXT: 0130: FFFFFFFF FFFFFFFF FFFFFFFF A0A0A09C |................| +CHECK-NEXT: 0140: 9C9C9393 93ADADAD F2F2F284 84848181 |................| +CHECK-NEXT: 0150: 81FFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0160: FFFFFFFF FFFFFFFF FFFFFFFF A4A4A4D7 |................| +CHECK-NEXT: 0170: D7D79D9D 9DD0D0D0 EEEEEE91 91918D8D |................| +CHECK-NEXT: 0180: 8DFFFFFF FFFFFF81 81817E7E 7EFFFFFF |..........~~~...| +CHECK-NEXT: 0190: FFFFFFFF FFFFFFFF FFFFFFFF A9A9A9F2 |................| +CHECK-NEXT: 01A0: F2F2E5E5 E5E2E2E2 95959591 91918D8D |................| +CHECK-NEXT: 01B0: 8D898989 868686FF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 01C0: FFFFFFFF FFFFFFFF FFFFFFFF ADADADF2 |................| +CHECK-NEXT: 01D0: F2F2E1E1 E1DFDFDF E7E7E7E4 E4E4BBBB |................| +CHECK-NEXT: 01E0: BB8E8E8E FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 01F0: FFFFFFFF FFFFFFFF FFFFFFFF B5B5B5F2 |................| +CHECK-NEXT: 0200: F2F2E8E8 E8E7E7E7 EAEAEAC6 C6C69E9E |................| +CHECK-NEXT: 0210: 9EFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0220: FFFFFFFF FFFFFFFF FFFFFFFF B9B9B9F4 |................| +CHECK-NEXT: 0230: F4F4ECEC ECEDEDED CBCBCBA7 A7A7FFFF |................| +CHECK-NEXT: 0240: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0250: FFFFFFFF FFFFFFFF FFFFFFFF BDBDBDF7 |................| +CHECK-NEXT: 0260: F7F7EFEF EFD0D0D0 AFAFAFFF FFFFFFFF |................| +CHECK-NEXT: 0270: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0280: FFFFFFFF FFFFFFFF FFFFFFFF C1C1C1F7 |................| +CHECK-NEXT: 0290: F7F7D5D5 D5B6B6B6 FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 02A0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 02B0: FFFFFFFF FFFFFFFF FFFFFFFF C4C4C4D9 |................| +CHECK-NEXT: 02C0: D9D9BEBE BEFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 02D0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 02E0: FFFFFFFF FFFFFFFF FFFFFFFF C8C8C8C5 |................| +CHECK-NEXT: 02F0: C5C5FFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0300: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0310: FFFFFFFF FFFFFFFF FFFFFFFF CBCBCBFF |................| +CHECK-NEXT: 0320: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0330: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0340: 28000000 10000000 10000000 01001800 |(...............| +CHECK-NEXT: 0350: 00000000 00030000 C40E0000 C40E0000 |................| +CHECK-NEXT: 0360: 00000000 00000000 FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0370: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0380: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0390: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 03A0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 03B0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 03C0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 03D0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 03E0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 03F0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0400: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0410: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0420: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0430: FFFFFFFF A0E3A901 B31801B3 1801B318 |................| +CHECK-NEXT: 0440: 01B31801 B31801B3 1861D06F FFFFFFFF |.........a.o....| +CHECK-NEXT: 0450: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0460: FFFFFFFF 01B31800 D7331CDB 49DBF9E2 |.........3..I...| +CHECK-NEXT: 0470: 9BEFAF00 D73300D7 3301B318 FFFFFFFF |.....3..3.......| +CHECK-NEXT: 0480: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0490: FFFFFFFF 01B31800 DE55F6FE F9DBFAE7 |.........U......| +CHECK-NEXT: 04A0: FEFFFE86 EFAE00DE 5501B318 FFFFFFFF |........U.......| +CHECK-NEXT: 04B0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 04C0: FFFFFFFF 01B31800 E676DBFB EC00E676 |.........v.....v| +CHECK-NEXT: 04D0: 57EFA5FB FFFD55EE A401B318 FFFFFFFF |W.....U.........| +CHECK-NEXT: 04E0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 04F0: FFFFFFFF 01B31800 ED9800ED 9800ED98 |................| +CHECK-NEXT: 0500: 00ED9887 F7CFFEFF FF01B318 FFFFFFFF |................| +CHECK-NEXT: 0510: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0520: FFFFFFFF 01B31800 F4BA00F4 BA00F4BA |................| +CHECK-NEXT: 0530: 00F4BA00 F4BA9CFB E401B318 FFFFFFFF |................| +CHECK-NEXT: 0540: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0550: FFFFFFFF 01B31800 FBDB00FB DB00FBDB |................| +CHECK-NEXT: 0560: 00FBDB00 FBDB00FB DB01B318 FFFFFFFF |................| +CHECK-NEXT: 0570: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0580: FFFFFFFF 9FE2A801 B31801B3 1801B318 |................| +CHECK-NEXT: 0590: 01B31801 B31801B3 1861D06F FFFFFFFF |.........a.o....| +CHECK-NEXT: 05A0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 05B0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 05C0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 05D0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 05E0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 05F0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0600: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0610: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0620: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0630: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0640: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0650: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0660: FFFFFFFF FFFFFFFF 00000000 00006400 |..............d.| +CHECK-NEXT: 0670: 79007500 00000000 65007300 68006100 |y.u.....e.s.h.a.| +CHECK-NEXT: 0680: 6C006100 00008000 66006B00 61006F00 |l.a.....f.k.a.o.| +CHECK-NEXT: 0690: 79006100 00000000 0000C080 00000000 |y.a.............| +CHECK-NEXT: 06A0: 02000A00 0A00C800 2C010000 00005400 |........,.....T.| +CHECK-NEXT: 06B0: 65007300 74000000 01000250 00000000 |e.s.t......P....| +CHECK-NEXT: 06C0: 0A000A00 E6000E00 0100FFFF 82004300 |..............C.| +CHECK-NEXT: 06D0: 6F006E00 74006900 6E007500 65003A00 |o.n.t.i.n.u.e.:.| +CHECK-NEXT: 06E0: 00000000 00000150 00000000 42008600 |.......P....B...| +CHECK-NEXT: 06F0: A1000D00 0200FFFF 80002600 4F004B00 |..........&.O.K.| +CHECK-NEXT: 0700: 00000000 00000000 11005800 A4000000 |..........X.....| +CHECK-NEXT: 0710: 0D004800 2E160000 82001200 BC010000 |..H.............| +CHECK-NEXT: 0720: 00000000 00006400 66006900 73006800 |......d.f.i.s.h.| +CHECK-NEXT: 0730: 00000000 65007300 61006C00 61006400 |....e.s.a.l.a.d.| +CHECK-NEXT: 0740: 00008000 66006400 75006300 6B000000 |....f.d.u.c.k...| +CHECK-NEXT: 0750: 74686973 20697320 61207573 65722064 |this is a user d| +CHECK-NEXT: 0760: 6566696E 65642072 65736F75 72636500 |efined resource.| +CHECK-NEXT: 0770: 69742063 6F6E7461 696E7320 6D616E79 |it contains many| +CHECK-NEXT: 0780: 20737472 696E6773 00000000 00000000 | strings........| +CHECK-NEXT: ) Index: llvm/test/tools/llvm-cvtres/parse.test =================================================================== --- llvm/test/tools/llvm-cvtres/parse.test +++ llvm/test/tools/llvm-cvtres/parse.test @@ -2,7 +2,7 @@ // rc.exe: // > rc /fo test_resource.res /nologo test_resource.rc -RUN: llvm-cvtres %p/Inputs/test_resource.res | FileCheck %s +RUN: llvm-cvtres /verbose %p/Inputs/test_resource.res | FileCheck %s CHECK: Number of resources: 8 CHECK-NEXT: Resource Tree [ Index: llvm/tools/llvm-cvtres/llvm-cvtres.h =================================================================== --- llvm/tools/llvm-cvtres/llvm-cvtres.h +++ llvm/tools/llvm-cvtres/llvm-cvtres.h @@ -14,6 +14,4 @@ void error(std::error_code EC); -enum class machine { UNKNOWN = 0, ARM, X64, X86 }; - #endif Index: llvm/tools/llvm-cvtres/llvm-cvtres.cpp =================================================================== --- llvm/tools/llvm-cvtres/llvm-cvtres.cpp +++ llvm/tools/llvm-cvtres/llvm-cvtres.cpp @@ -112,6 +112,11 @@ return 0; } + bool Verbose = false; + + if (InputArgs.hasArg(OPT_VERBOSE)) + Verbose = true; + machine Machine; if (InputArgs.hasArg(OPT_MACHINE)) { @@ -124,7 +129,8 @@ if (Machine == machine::UNKNOWN) reportError("Unsupported machine architecture"); } else { - outs() << "Machine architecture not specified; assumed X64.\n"; + if (Verbose) + outs() << "Machine architecture not specified; assumed X64.\n"; Machine = machine::X64; } @@ -139,20 +145,22 @@ if (InputArgs.hasArg(OPT_OUT)) { OutputFile = InputArgs.getLastArgValue(OPT_OUT); } else { - OutputFile = StringRef(InputFiles[0]); + OutputFile = llvm::sys::path::filename(StringRef(InputFiles[0])); llvm::sys::path::replace_extension(OutputFile, ".obj"); } - outs() << "Machine: "; - switch (Machine) { - case machine::ARM: - outs() << "ARM\n"; - break; - case machine::X86: - outs() << "X86\n"; - break; - default: - outs() << "X64\n"; + if (Verbose) { + outs() << "Machine: "; + switch (Machine) { + case machine::ARM: + outs() << "ARM\n"; + break; + case machine::X86: + outs() << "X86\n"; + break; + default: + outs() << "X64\n"; + } } WindowsResourceParser Parser; @@ -179,12 +187,16 @@ error(Entry.moveNext(End)); EntryNumber++; } - outs() << "Number of resources: " << EntryNumber << "\n"; + if (Verbose) + outs() << "Number of resources: " << EntryNumber << "\n"; error(Parser.parse(RF)); } - Parser.printTree(); + if (Verbose) + Parser.printTree(); + + error(llvm::object::writeWindowsResourceCOFF(OutputFile, Machine, Parser)); return 0; }