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,15 @@ #include namespace llvm { + +class FileOutputBuffer; + namespace object { class WindowsResource; +enum class machine { UNKNOWN = 0, ARM, X64, X86 }; + class ResourceEntryRef { public: Error moveNext(bool &End); @@ -58,7 +63,13 @@ 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; } + void copyData(std::vector &Buffer); + void copyTypeString(std::vector &String); + void copyNameString(std::vector &String); + private: friend class WindowsResource; @@ -106,19 +117,34 @@ class WindowsResourceParser { public: + class TreeNode; + WindowsResourceParser(); Error parse(WindowsResource *WR); void printTree() const; -private: + const TreeNode &getTree() const { return Root; } + + const std::vector> &getData() const { return Data; } + + const std::vector> &getStringTable() const { return StringTable; } + class TreeNode { public: - TreeNode() = default; - explicit TreeNode(ArrayRef Ref); + TreeNode(); 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 std::map> &getIDChildren() const { return IDChildren; } + const std::map> &getStringChildren() const { return StringChildren; } private: TreeNode &addTypeNode(const ResourceEntryRef &Entry); @@ -126,12 +152,52 @@ TreeNode &addLanguageNode(const ResourceEntryRef &Entry); TreeNode &addChild(uint32_t ID); TreeNode &addChild(ArrayRef NameRef); - std::vector Name; + void setMajorVersion(uint16_t Version) { MajorVersion = Version; } + void setMinorVersion(uint16_t Version) { MinorVersion = Version; } + void setCharacteristics(uint32_t C) { Characteristics = C; } + void setToDataNode() { IsDataNode = true; DataIndex = DataCount++; } + void setToStringNode() { StringIndex = StringCount++; } + bool IsDataNode; + uint32_t StringIndex; + uint32_t DataIndex; std::map> IDChildren; std::map> StringChildren; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t Characteristics; + static uint32_t StringCount; + static uint32_t DataCount; }; +private: TreeNode Root; + std::vector> Data; + std::vector> StringTable; + +}; + +void writeWindowsResourceCOFF(StringRef OutputFile, machine MachineType, const WindowsResourceParser &Parser); + +class WindowsResourceCOFFWriter { +public: + WindowsResourceCOFFWriter(StringRef OutputFile, machine MachineType, const WindowsResourceParser &Parser); + + void write(); + +private: + std::unique_ptr Buffer; + machine MachineType; + const WindowsResourceParser::TreeNode &Resources; + const std::vector> &Data; + uint32_t SymbolTableOffset; + uint32_t SectionOneSize; + uint32_t SectionOneOffset; + uint32_t SectionOneRelocations; + uint32_t SectionTwoSize; + uint32_t SectionTwoOffset; + const std::vector> &StringTable; + std::vector StringTableOffsets; + std::vector DataOffsets; }; } // namespace object 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; @@ -73,6 +81,21 @@ return Error::success(); } +void ResourceEntryRef::copyData(std::vector &Buffer) { + Buffer.resize(Data.size()); + std::copy(Data.begin(), Data.end(), Buffer.begin()); +} + +void ResourceEntryRef::copyTypeString(std::vector &String) { + String.resize(Type.size()); + std::copy(Type.begin(), Type.end(), String.begin()); +} + +void ResourceEntryRef::copyNameString(std::vector &String) { + String.resize(Name.size()); + std::copy(Name.begin(), Name.end(), String.begin()); +} + static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID, ArrayRef &Str, bool &IsString) { uint16_t IDFlag; @@ -127,6 +150,19 @@ while (!End) { + Data.emplace_back(); + Entry.copyData(Data.back()); + + if (Entry.checkTypeString()) { + StringTable.emplace_back(); + Entry.copyTypeString(StringTable.back()); + } + + if (Entry.checkNameString()) { + StringTable.emplace_back(); + Entry.copyNameString(StringTable.back()); + } + Root.addEntry(Entry); RETURN_IF_ERROR(Entry.moveNext(End)); @@ -146,8 +182,8 @@ NameNode.addLanguageNode(Entry); } -WindowsResourceParser::TreeNode::TreeNode(ArrayRef NameRef) - : Name(NameRef) {} +WindowsResourceParser::TreeNode::TreeNode() + : IsDataNode(false), MajorVersion(0), MinorVersion(0) {} WindowsResourceParser::TreeNode & WindowsResourceParser::TreeNode::addTypeNode(const ResourceEntryRef &Entry) { @@ -168,14 +204,19 @@ WindowsResourceParser::TreeNode & WindowsResourceParser::TreeNode::addLanguageNode( const ResourceEntryRef &Entry) { - return addChild(Entry.getLanguage()); + auto &Node = addChild(Entry.getLanguage()); + Node.setMajorVersion(Entry.getMajorVersion()); + Node.setMinorVersion(Entry.getMinorVersion()); + Node.setCharacteristics(Entry.getCharacteristics()); + Node.setToDataNode(); + return Node; } WindowsResourceParser::TreeNode & WindowsResourceParser::TreeNode::addChild(uint32_t ID) { auto Child = IDChildren.find(ID); if (Child == IDChildren.end()) { - auto NewChild = llvm::make_unique(ID); + auto NewChild = llvm::make_unique(); WindowsResourceParser::TreeNode &Node = *NewChild; IDChildren.emplace(ID, std::move(NewChild)); return Node; @@ -199,9 +240,10 @@ auto Child = StringChildren.find(NameString); if (Child == StringChildren.end()) { - auto NewChild = llvm::make_unique(NameRef); + auto NewChild = llvm::make_unique(); WindowsResourceParser::TreeNode &Node = *NewChild; StringChildren.emplace(NameString, std::move(NewChild)); + Node.setToStringNode(); return Node; } else return *(Child->second); @@ -218,5 +260,331 @@ } } + +// 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; +} + +void writeWindowsResourceCOFF(StringRef OutputFile, machine MachineType, const WindowsResourceParser &Parser) { + WindowsResourceCOFFWriter(OutputFile, MachineType, Parser).write(); +} + +WindowsResourceCOFFWriter::WindowsResourceCOFFWriter(StringRef OutputFile, machine MachineType, const WindowsResourceParser &Parser) + : MachineType(MachineType), Resources(Parser.getTree()), Data(Parser.getData()), StringTable(Parser.getStringTable()) { + uint64_t FileSize = llvm::COFF::Header16Size; + + //one .rsrc section for directory tree, another for resource data. + FileSize += 2*llvm::COFF::SectionSize; + + 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 += Data.size() * llvm::COFF::RelocationSize; // one relocation for each resource. + + //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; + + 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. + + ErrorOr> BufferOrErr = FileOutputBuffer::create(OutputFile, FileSize); + if (!BufferOrErr) { + errs() << BufferOrErr.getError().message(); + exit(1); + } + + Buffer = std::move(*BufferOrErr); +} + +static std::time_t getTime() { + std::time_t Now = time(nullptr); + if (Now < 0 || !isUInt<32>(Now)) + return UINT32_MAX; + return Now; +} + +void WindowsResourceCOFFWriter::write() { + uint8_t *Buf = Buffer->getBufferStart(); + + // Write the COFF header. + auto *Header = reinterpret_cast(Buf); + 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; + + // Write the first section header. + Buf += sizeof(llvm::object::coff_file_header); + auto *SectionOneHeader = reinterpret_cast(Buf); + 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; + + // Write the section section header. + Buf += sizeof(llvm::object::coff_section); + auto *SectionTwoHeader = reinterpret_cast(Buf); + 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; + + // Write section one. + // First, traverse directory tree breadth-first and write contents. + Buf += sizeof(llvm::object::coff_section); + 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 CurrentSectionOffset = 0; + while (!Queue.empty()) { + auto Current = Queue.front(); + Queue.pop(); + auto *Table = reinterpret_cast(Buf); + Table->Characteristics = Current->getCharacteristics(); + Table->TimeDateStamp = 0; + Table->MajorVersion = Current->getMajorVersion(); + Table->MinorVersion = Current->getMinorVersion(); + auto& IDChildren = Current->getIDChildren(); + auto& StringChildren = Current->getStringChildren(); + Table->NumberOfNameEntries = StringChildren.size(); + Table->NumberOfIDEntries = IDChildren.size(); + Buf += sizeof(llvm::object::coff_resource_dir_table); + CurrentSectionOffset += 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(Buf); + 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()); + } + Buf += sizeof(llvm::object::coff_resource_dir_entry); + CurrentSectionOffset += sizeof(llvm::object::coff_resource_dir_entry); + } + for (auto const& Child: IDChildren) { + auto *Entry = reinterpret_cast(Buf); + 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()); + } + Buf += sizeof(llvm::object::coff_resource_dir_entry); + CurrentSectionOffset += sizeof(llvm::object::coff_resource_dir_entry); + } + } + + // Now write all the resource data entries. + std::vector RelocationAddresses(Data.size()); + for (auto DataNodes : DataEntriesTreeOrder) { + auto *Entry = reinterpret_cast(Buf); + RelocationAddresses[DataNodes->getDataIndex()] = CurrentSectionOffset; + Entry->DataRVA = 0; // Set to zero because it is a relocation. + Entry->DataSize = Data[DataNodes->getDataIndex()].size(); + Entry->Codepage = 0; + Entry->Reserved = 0; + Buf += sizeof(llvm::object::coff_resource_data_entry); + CurrentSectionOffset += sizeof(llvm::object::coff_resource_data_entry); + } + + // Now write the directory string table for .rsrc$01 + uint32_t TotalStringTableSize = 0; + for (auto String : StringTable) { + auto *LengthField = reinterpret_cast(Buf); + uint16_t Length = String.size(); + *LengthField = Length; + Buf += sizeof(uint16_t); + auto *Start = reinterpret_cast(Buf); + std::copy(String.begin(), String.end(), Start); + Buf += Length*sizeof(UTF16); + TotalStringTableSize += Length*sizeof(UTF16) + sizeof(uint16_t); + } + Buf += alignTo(TotalStringTableSize, sizeof(uint32_t)) - TotalStringTableSize; + + // 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(Buf); + 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; + } + Buf += sizeof(llvm::object::coff_relocation); + } + + // Now write the .rsrc$02 section. + for (auto const& RawDataEntry : Data) { + std::copy(RawDataEntry.begin(), RawDataEntry.end(), Buf); + Buf += alignTo(RawDataEntry.size(), sizeof(uint64_t)); + } + + // Now write the symbol table. + // First, the feat symbol. + auto *Symbol = reinterpret_cast(Buf); + 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; + Buf += sizeof(llvm::object::coff_symbol16); + + // Now write the .rsrc1 symbol + aux. + Symbol = reinterpret_cast(Buf); + 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; + Buf += sizeof(llvm::object::coff_symbol16); + auto *Aux = reinterpret_cast(Buf); + Aux->Length = SectionOneSize; + Aux->NumberOfRelocations = Data.size(); + Aux->NumberOfLinenumbers = 0; + Aux->CheckSum = 0; + Aux->NumberLowPart = 0; + Aux->Selection = 0; + Buf += sizeof(llvm::object::coff_aux_section_definition); + + // Now write the .rsrc2 symbol + aux. + Symbol = reinterpret_cast(Buf); + 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; + Buf += sizeof(llvm::object::coff_symbol16); + Aux = reinterpret_cast(Buf); + Aux->Length = SectionTwoSize; + Aux->NumberOfRelocations = 0; + Aux->NumberOfLinenumbers = 0; + Aux->CheckSum = 0; + Aux->NumberLowPart = 0; + Aux->Selection = 0; + Buf += 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(Buf); + 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; + Buf += sizeof(llvm::object::coff_symbol16); + } + + // Just 4 null bytes for the string table. + auto COFFStringTable = reinterpret_cast(Buf); + *COFFStringTable = 0; + + if (auto EC = Buffer->commit()) { + errs() << "Failed to write the output file"; + exit(1); + } +} + } // 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: 00000000 00000000 00000000 02000000 |................| +CHECK-NEXT: 0010: 26020000 E0000080 34020000 F8000080 |&.......4.......| +CHECK-NEXT: 0020: 00000000 00000000 00000000 01000100 |................| +CHECK-NEXT: 0030: 54020000 10010080 60380000 28010080 |T.......`8..(...| +CHECK-NEXT: 0040: 00000000 00000000 00000000 01000000 |................| +CHECK-NEXT: 0050: 3E020000 40010080 00000000 00000000 |>...@...........| +CHECK-NEXT: 0060: 00000000 01000100 08020000 58010080 |............X...| +CHECK-NEXT: 0070: 0C000000 70010080 00000000 00000000 |....p...........| +CHECK-NEXT: 0080: 00000000 00000100 09040000 88010000 |................| +CHECK-NEXT: 0090: 00000000 00000000 00000000 00000100 |................| +CHECK-NEXT: 00A0: 09040000 98010000 00000000 00000000 |................| +CHECK-NEXT: 00B0: 00000000 00000100 09040000 A8010000 |................| +CHECK-NEXT: 00C0: 00000000 00000000 00000000 00000100 |................| +CHECK-NEXT: 00D0: 090C0000 B8010000 00000000 00000000 |................| +CHECK-NEXT: 00E0: 00000000 00000100 04080000 C8010000 |................| +CHECK-NEXT: 00F0: 00000000 00000000 00000000 00000100 |................| +CHECK-NEXT: 0100: 09040000 D8010000 00000000 00000000 |................| +CHECK-NEXT: 0110: 00000000 00000100 09040000 E8010000 |................| +CHECK-NEXT: 0120: 00000000 00000000 00000000 00000100 |................| +CHECK-NEXT: 0130: 09040000 F8010000 00000000 39000000 |............9...| +CHECK-NEXT: 0140: 00000000 00000000 00000000 28030000 |............(...| +CHECK-NEXT: 0150: 00000000 00000000 00000000 28030000 |............(...| +CHECK-NEXT: 0160: 00000000 00000000 00000000 30000000 |............0...| +CHECK-NEXT: 0170: 00000000 00000000 00000000 2E000000 |................| +CHECK-NEXT: 0180: 00000000 00000000 00000000 6C000000 |............l...| +CHECK-NEXT: 0190: 00000000 00000000 00000000 18000000 |................| +CHECK-NEXT: 01A0: 00000000 00000000 00000000 18000000 |................| +CHECK-NEXT: 01B0: 00000000 00000000 0E004D00 59004100 |..........M.Y.A.| +CHECK-NEXT: 01C0: 43004300 45004C00 45005200 41005400 |C.C.E.L.E.R.A.T.| +CHECK-NEXT: 01D0: 4F005200 53000600 43005500 52005300 |O.R.S...C.U.R.S.| +CHECK-NEXT: 01E0: 4F005200 04004F00 4B004100 59000A00 |O.R...O.K.A.Y...| +CHECK-NEXT: 01F0: 54004500 53005400 44004900 41004C00 |T.E.S.T.D.I.A.L.| +CHECK-NEXT: 0200: 4F004700 05002200 45004100 54002200 |O.G...".E.A.T.".| +CHECK-NEXT: 0210: 0B005300 54005200 49004E00 47004100 |..S.T.R.I.N.G.A.| +CHECK-NEXT: 0220: 52005200 41005900 0A004D00 59005200 |R.R.A.Y...M.Y.R.| +CHECK-NEXT: 0230: 45005300 4F005500 52004300 45000000 |E.S.O.U.R.C.E...| +CHECK-NEXT: 0240: E8010000 05000000 03009801 00000600 |................| +CHECK-NEXT: 0250: 00000300 A8010000 07000000 0300C801 |................| +CHECK-NEXT: 0260: 00000800 00000300 D8010000 09000000 |................| +CHECK-NEXT: 0270: 0300F801 00000A00 00000300 B8010000 |................| +CHECK-NEXT: 0280: 0B000000 03008801 00000C00 00000300 |................| +CHECK-NEXT: 0290: 11000300 E7030000 0D004400 4C040000 |..........D.L...| +CHECK-NEXT: 02A0: 82001200 BC010000 28000000 10000000 |........(.......| +CHECK-NEXT: 02B0: 10000000 01001800 00000000 00030000 |................| +CHECK-NEXT: 02C0: C40E0000 C40E0000 00000000 00000000 |................| +CHECK-NEXT: 02D0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 02E0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 02F0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0300: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0310: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0320: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0330: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0340: FFFFFFFF FF7F7F7F 7C7C7C78 78787575 |........|||xxxuu| +CHECK-NEXT: 0350: 75FFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |u...............| +CHECK-NEXT: 0360: FFFFFFFF FFFFFFFF FFFFFFFF 979797FF |................| +CHECK-NEXT: 0370: FFFFFFFF FF838383 AAAAAADB DBDB7979 |..............yy| +CHECK-NEXT: 0380: 79757575 FFFFFFFF FFFFFFFF FFFFFFFF |yuuu............| +CHECK-NEXT: 0390: FFFFFFFF FFFFFFFF FFFFFFFF 9C9C9C98 |................| +CHECK-NEXT: 03A0: 9898FFFF FF888888 DBDBDBB7 B7B77D7D |..............}}| +CHECK-NEXT: 03B0: 7DFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |}...............| +CHECK-NEXT: 03C0: FFFFFFFF FFFFFFFF FFFFFFFF A0A0A09C |................| +CHECK-NEXT: 03D0: 9C9C9393 93ADADAD F2F2F284 84848181 |................| +CHECK-NEXT: 03E0: 81FFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 03F0: FFFFFFFF FFFFFFFF FFFFFFFF A4A4A4D7 |................| +CHECK-NEXT: 0400: D7D79D9D 9DD0D0D0 EEEEEE91 91918D8D |................| +CHECK-NEXT: 0410: 8DFFFFFF FFFFFF81 81817E7E 7EFFFFFF |..........~~~...| +CHECK-NEXT: 0420: FFFFFFFF FFFFFFFF FFFFFFFF A9A9A9F2 |................| +CHECK-NEXT: 0430: F2F2E5E5 E5E2E2E2 95959591 91918D8D |................| +CHECK-NEXT: 0440: 8D898989 868686FF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0450: FFFFFFFF FFFFFFFF FFFFFFFF ADADADF2 |................| +CHECK-NEXT: 0460: F2F2E1E1 E1DFDFDF E7E7E7E4 E4E4BBBB |................| +CHECK-NEXT: 0470: BB8E8E8E FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0480: FFFFFFFF FFFFFFFF FFFFFFFF B5B5B5F2 |................| +CHECK-NEXT: 0490: F2F2E8E8 E8E7E7E7 EAEAEAC6 C6C69E9E |................| +CHECK-NEXT: 04A0: 9EFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 04B0: FFFFFFFF FFFFFFFF FFFFFFFF B9B9B9F4 |................| +CHECK-NEXT: 04C0: F4F4ECEC ECEDEDED CBCBCBA7 A7A7FFFF |................| +CHECK-NEXT: 04D0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 04E0: FFFFFFFF FFFFFFFF FFFFFFFF BDBDBDF7 |................| +CHECK-NEXT: 04F0: F7F7EFEF EFD0D0D0 AFAFAFFF FFFFFFFF |................| +CHECK-NEXT: 0500: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0510: FFFFFFFF FFFFFFFF FFFFFFFF C1C1C1F7 |................| +CHECK-NEXT: 0520: F7F7D5D5 D5B6B6B6 FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0530: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0540: FFFFFFFF FFFFFFFF FFFFFFFF C4C4C4D9 |................| +CHECK-NEXT: 0550: D9D9BEBE BEFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0560: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0570: FFFFFFFF FFFFFFFF FFFFFFFF C8C8C8C5 |................| +CHECK-NEXT: 0580: C5C5FFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0590: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 05A0: FFFFFFFF FFFFFFFF FFFFFFFF CBCBCBFF |................| +CHECK-NEXT: 05B0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 05C0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 05D0: 28000000 10000000 10000000 01001800 |(...............| +CHECK-NEXT: 05E0: 00000000 00030000 C40E0000 C40E0000 |................| +CHECK-NEXT: 05F0: 00000000 00000000 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 FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0670: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0680: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0690: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 06A0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 06B0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 06C0: FFFFFFFF A0E3A901 B31801B3 1801B318 |................| +CHECK-NEXT: 06D0: 01B31801 B31801B3 1861D06F FFFFFFFF |.........a.o....| +CHECK-NEXT: 06E0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 06F0: FFFFFFFF 01B31800 D7331CDB 49DBF9E2 |.........3..I...| +CHECK-NEXT: 0700: 9BEFAF00 D73300D7 3301B318 FFFFFFFF |.....3..3.......| +CHECK-NEXT: 0710: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0720: FFFFFFFF 01B31800 DE55F6FE F9DBFAE7 |.........U......| +CHECK-NEXT: 0730: FEFFFE86 EFAE00DE 5501B318 FFFFFFFF |........U.......| +CHECK-NEXT: 0740: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0750: FFFFFFFF 01B31800 E676DBFB EC00E676 |.........v.....v| +CHECK-NEXT: 0760: 57EFA5FB FFFD55EE A401B318 FFFFFFFF |W.....U.........| +CHECK-NEXT: 0770: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +CHECK-NEXT: 0780: FFFFFFFF 01B31800 ED9800ED 9800ED98 |................| +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,10 @@ return 0; } + bool Verbose = false; + + if (InputArgs.hasArg(OPT_VERBOSE)) Verbose = true; + machine Machine; if (InputArgs.hasArg(OPT_MACHINE)) { @@ -124,6 +128,7 @@ if (Machine == machine::UNKNOWN) reportError("Unsupported machine architecture"); } else { + if (Verbose) outs() << "Machine architecture not specified; assumed X64.\n"; Machine = machine::X64; } @@ -139,10 +144,11 @@ 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"); } + if (Verbose) { outs() << "Machine: "; switch (Machine) { case machine::ARM: @@ -154,6 +160,7 @@ default: outs() << "X64\n"; } +} WindowsResourceParser Parser; @@ -179,12 +186,16 @@ error(Entry.moveNext(End)); EntryNumber++; } + if (Verbose) outs() << "Number of resources: " << EntryNumber << "\n"; error(Parser.parse(RF)); } + if (Verbose) Parser.printTree(); + llvm::object::writeWindowsResourceCOFF(OutputFile, Machine, Parser); + return 0; }