Index: llvm/include/llvm/Object/WindowsResource.h =================================================================== --- llvm/include/llvm/Object/WindowsResource.h +++ llvm/include/llvm/Object/WindowsResource.h @@ -30,37 +30,59 @@ #define LLVM_INCLUDE_LLVM_OBJECT_RESFILE_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Object/Binary.h" +#include "llvm/Object/Error.h" #include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/COFF.h" +#include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" +#include "llvm/Support/ScopedPrinter.h" + +#include namespace llvm { namespace object { +namespace { + +#define RETURN_IF_ERROR(X) \ + if (auto EC = X) \ + return std::move(EC); +} + class WindowsResource; class ResourceEntryRef { public: Error moveNext(bool &End); + Error getType(uint16_t &Type) const; + Error checkNameString(bool &isString) const; + Error getNameString(ArrayRef &Name) const; + Error getNameID(uint16_t &Name) const; + Error getLanguage(uint16_t &Language) const; private: friend class WindowsResource; ResourceEntryRef(BinaryStreamRef Ref, const WindowsResource *Owner, Error &Err); + Error loadNext(); BinaryStreamReader Reader; + uint32_t HeaderSize; // Size of the header not including the size fields + // themselves. BinaryStreamRef HeaderBytes; + uint32_t DataSize; BinaryStreamRef DataBytes; const WindowsResource *OwningRes = nullptr; }; class WindowsResource : public Binary { public: - ~WindowsResource() override; Expected getHeadEntry(); static bool classof(const Binary *V) { return V->isWinRes(); } @@ -76,6 +98,93 @@ BinaryByteStream BBS; }; +class WindowsResourceParser { +public: + WindowsResourceParser(); + + Error parse(WindowsResource *WR); + + void printTree(); + +private: + class TreeNode { + public: + TreeNode() : ID(-1), Name() {} + explicit TreeNode(uint32_t ID) : ID(ID), Name() {} + explicit TreeNode(ArrayRef NameRef) : ID(-1), Name() { + Name.resize(NameRef.size()); + std::copy(NameRef.begin(), NameRef.end(), Name.begin()); + } + Expected addNameNode(const ResourceEntryRef &Entry) { + bool IsStringName; + RETURN_IF_ERROR(Entry.checkNameString(IsStringName)); + if (IsStringName) { + ArrayRef EntryNameString; + RETURN_IF_ERROR(Entry.getNameString(EntryNameString)); + return addChild(EntryNameString); + } else { + uint16_t NameID; + RETURN_IF_ERROR(Entry.getNameID(NameID)); + return addChild(NameID); + } + } + Expected addChild(uint32_t ID) { + auto Child = IDChildren.find(ID); + if (Child == IDChildren.end()) { + auto NewChild = llvm::make_unique(ID); + TreeNode &Node = *NewChild; + IDChildren.insert(std::pair>( + ID, std::move(NewChild))); + return Node; + } else + return *(Child->second); + } + Expected addChild(ArrayRef NameRef) { + std::string NameString; + ArrayRef CorrectedName; + if (llvm::sys::IsBigEndianHost) { + std::vector EndianCorrectedName; + EndianCorrectedName.resize(NameRef.size() + 1); + std::copy(NameRef.begin(), NameRef.end(), + EndianCorrectedName.begin() + 1); + EndianCorrectedName[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED; + CorrectedName = makeArrayRef(EndianCorrectedName); + } else + CorrectedName = NameRef; + if (!llvm::convertUTF16ToUTF8String(CorrectedName, NameString)) + return make_error( + "Could not convert UTF16 to UTF8.", object_error::parse_failed); + + auto Child = StringChildren.find(NameString); + if (Child == StringChildren.end()) { + auto NewChild = llvm::make_unique(NameRef); + TreeNode &Node = *NewChild; + StringChildren.insert(std::pair>( + NameString, std::move(NewChild))); + return Node; + } else + return *(Child->second); + } + void print(ScopedPrinter &Writer, std::string Name) { + ListScope NodeScope(Writer, Name); + for (auto const &Child : StringChildren) { + Child.second->print(Writer, Child.first); + } + for (auto const &Child : IDChildren) { + Child.second->print(Writer, std::to_string(Child.first)); + } + } + + private: + uint16_t ID; + std::vector Name; + std::map> IDChildren; + std::map> StringChildren; + }; + + TreeNode Root; +}; + } // namespace object } // namespace llvm Index: llvm/lib/Object/WindowsResource.cpp =================================================================== --- llvm/lib/Object/WindowsResource.cpp +++ llvm/lib/Object/WindowsResource.cpp @@ -12,7 +12,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Object/WindowsResource.h" -#include "llvm/Object/Error.h" +#include "llvm/Support/COFF.h" #include namespace llvm { @@ -22,10 +22,6 @@ static const size_t NullEntrySize = 16; -#define RETURN_IF_ERROR(X) \ - if (auto EC = X) \ - return EC; - WindowsResource::WindowsResource(MemoryBufferRef Source) : Binary(Binary::ID_WinRes, Source) { size_t LeadingSize = ResourceMagicSize + NullEntrySize; @@ -33,8 +29,6 @@ support::little); } -WindowsResource::~WindowsResource() = default; - Expected> WindowsResource::createWindowsResource(MemoryBufferRef Source) { if (Source.getBufferSize() < ResourceMagicSize + NullEntrySize) @@ -72,19 +66,123 @@ return Error::success(); } +Error ResourceEntryRef::getType(uint16_t &TypeID) const { + BinaryStreamReader TypeReader(HeaderBytes); + RETURN_IF_ERROR(TypeReader.skip(sizeof(uint16_t))); + RETURN_IF_ERROR(TypeReader.readInteger(TypeID)); + return Error::success(); +} + +Error ResourceEntryRef::checkNameString(bool &isString) const { + BinaryStreamReader NameChecker(HeaderBytes); + RETURN_IF_ERROR(NameChecker.skip(4)); + uint16_t IDFlag; + RETURN_IF_ERROR(NameChecker.readInteger(IDFlag)); + if (IDFlag == 0xffff) + isString = false; + else + isString = true; + return Error::success(); +} + +Error ResourceEntryRef::getNameString(ArrayRef &Name) const { + bool IsString; + RETURN_IF_ERROR(checkNameString(IsString)); + if (!IsString) + return make_error( + "Incorrectly attempted to parse string.", object_error::parse_failed); + + uint32_t NameSize = HeaderSize + - sizeof(uint32_t) /*Type ID field*/ + - sizeof(uint32_t) /*DataVersion field*/ + - sizeof(uint16_t) /*MemoryFlags*/ + - sizeof(uint16_t) /*LanguageID*/ + - sizeof(uint32_t) /*Version*/ + - sizeof(uint32_t) /*Characteristics*/; + NameSize /= 2; // divide by 2 because we're reading UTF16s, not bytes. + + BinaryStreamReader Reader = BinaryStreamReader(HeaderBytes); + RETURN_IF_ERROR(Reader.skip(sizeof(uint32_t))); + RETURN_IF_ERROR(Reader.readArray(Name, NameSize)); + Name = Name.take_while([](const UTF16 &c) { return c != '\0'; }); + return Error::success(); +} + +Error ResourceEntryRef::getNameID(uint16_t &Name) const { + BinaryStreamReader NameReader(HeaderBytes); + bool IsString; + + RETURN_IF_ERROR(checkNameString(IsString)); + if (IsString) + return make_error("Incorrectly attempted to parse ID.", + object_error::parse_failed); + + RETURN_IF_ERROR(NameReader.skip(sizeof(uint32_t) + sizeof(uint16_t))); + RETURN_IF_ERROR(NameReader.readInteger(Name)); + return Error::success(); +} + +Error ResourceEntryRef::getLanguage(uint16_t &Language) const { + BinaryStreamReader LanguageReader(HeaderBytes); + uint32_t RelativeLanguageOffset = + HeaderSize - sizeof(uint32_t) /*Characteristics*/ - + sizeof(uint32_t) /*Version*/ - sizeof(uint16_t) /*Language ID*/; + RETURN_IF_ERROR(LanguageReader.skip(RelativeLanguageOffset)); + RETURN_IF_ERROR(LanguageReader.readInteger(Language)); + return Error::success(); +} + Error ResourceEntryRef::loadNext() { - uint32_t DataSize; RETURN_IF_ERROR(Reader.readInteger(DataSize)); - uint32_t HeaderSize; RETURN_IF_ERROR(Reader.readInteger(HeaderSize)); + HeaderSize -= 2 * sizeof(uint32_t); // Already read the two integers. // The data and header size ints are themselves part of the header, so we must // subtract them from the size. - RETURN_IF_ERROR( - Reader.readStreamRef(HeaderBytes, HeaderSize - 2 * sizeof(uint32_t))); + RETURN_IF_ERROR(Reader.readStreamRef(HeaderBytes, HeaderSize)); RETURN_IF_ERROR(Reader.readStreamRef(DataBytes, DataSize)); RETURN_IF_ERROR(Reader.padToAlignment(sizeof(uint32_t))); return Error::success(); } +WindowsResourceParser::WindowsResourceParser() {} + +Error WindowsResourceParser::parse(WindowsResource *WR) { + auto EntryOrErr = WR->getHeadEntry(); + if (!EntryOrErr) + return EntryOrErr.takeError(); + + ResourceEntryRef Entry = EntryOrErr.get(); + bool End = false; + + while (!End) { + uint16_t Type; + RETURN_IF_ERROR(Entry.getType(Type)); + Expected TypeNodeOrErr = Root.addChild(Type); + if (!TypeNodeOrErr) + return TypeNodeOrErr.takeError(); + TreeNode &TypeNode = TypeNodeOrErr.get(); + + Expected NameNodeOrErr = TypeNode.addNameNode(Entry); + if (!NameNodeOrErr) + return NameNodeOrErr.takeError(); + TreeNode &NameNode = NameNodeOrErr.get(); + + uint16_t Language; + RETURN_IF_ERROR(Entry.getLanguage(Language)); + Expected LanguageNodeOrErr = NameNode.addChild(Language); + if (!LanguageNodeOrErr) + return LanguageNodeOrErr.takeError(); + + RETURN_IF_ERROR(Entry.moveNext(End)); + } + + return Error::success(); +} + +void WindowsResourceParser::printTree() { + ScopedPrinter Writer(outs()); + Root.print(Writer, "Resource Tree"); +} + } // namespace object } // namespace llvm Index: llvm/test/tools/llvm-cvtres/resource.test =================================================================== --- llvm/test/tools/llvm-cvtres/resource.test +++ llvm/test/tools/llvm-cvtres/resource.test @@ -4,4 +4,42 @@ RUN: llvm-cvtres %p/Inputs/test_resource.res | FileCheck %s -CHECK: Number of resources: 7 +CHECK: Number of resources: 7 +CHECK-NEXT: Resource Tree [ +CHECK-NEXT: 2 [ +CHECK-NEXT: CURSOR [ +CHECK-NEXT: 1033 [ +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: OKAY [ +CHECK-NEXT: 1033 [ +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: 4 [ +CHECK-NEXT: "EAT" [ +CHECK-NEXT: 3081 [ +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: 14432 [ +CHECK-NEXT: 2052 [ +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: 5 [ +CHECK-NEXT: TESTDIALOG [ +CHECK-NEXT: 1033 [ +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: 9 [ +CHECK-NEXT: MYACCELERATORS [ +CHECK-NEXT: 1033 [ +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: 12 [ +CHECK-NEXT: 1033 [ +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: ] Index: llvm/tools/llvm-cvtres/llvm-cvtres.cpp =================================================================== --- llvm/tools/llvm-cvtres/llvm-cvtres.cpp +++ llvm/tools/llvm-cvtres/llvm-cvtres.cpp @@ -131,7 +131,7 @@ std::vector InputFiles = InputArgs.getAllArgValues(OPT_INPUT); if (InputFiles.size() == 0) { - reportError("No input file specified"); + reportError("No input file specified.\n"); } SmallString<128> OutputFile; @@ -143,6 +143,20 @@ 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"; + } + + WindowsResourceParser Parser; + for (const auto &File : InputFiles) { Expected> BinaryOrErr = object::createBinary(File); @@ -166,17 +180,11 @@ EntryNumber++; } outs() << "Number of resources: " << EntryNumber << "\n"; + + error(Parser.parse(RF)); } - outs() << "Machine: "; - switch (Machine) { - case machine::ARM: - outs() << "ARM\n"; - break; - case machine::X86: - outs() << "X86\n"; - break; - default: - outs() << "X64\n"; - } + + Parser.printTree(); + return 0; }