diff options
author | Eric Beckmann <ecbeckmann@google.com> | 2017-05-30 18:19:06 +0000 |
---|---|---|
committer | Eric Beckmann <ecbeckmann@google.com> | 2017-05-30 18:19:06 +0000 |
commit | 1f0488cec7a7f4c35ca9338bb663fb04d4225a0d (patch) | |
tree | a5bcec3f75f130a3e3875a41b93fd61909d32d10 | |
parent | 7aecbf8e991a5a8916bf6639feff659b08308dd6 (diff) |
Adding parsing ability for .res file.
Subscribers: llvm-commits
Differential Revision: https://reviews.llvm.org/D33566
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@304225 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/llvm/Object/WindowsResource.h | 66 | ||||
-rw-r--r-- | include/llvm/Support/BinaryStreamReader.h | 8 | ||||
-rw-r--r-- | lib/Object/WindowsResource.cpp | 157 | ||||
-rw-r--r-- | lib/Support/BinaryStreamReader.cpp | 20 | ||||
-rw-r--r-- | test/tools/llvm-cvtres/Inputs/test_resource.rc | 6 | ||||
-rw-r--r-- | test/tools/llvm-cvtres/Inputs/test_resource.res | bin | 2200 -> 2332 bytes | |||
-rw-r--r-- | test/tools/llvm-cvtres/resource.test | 46 | ||||
-rw-r--r-- | tools/llvm-cvtres/llvm-cvtres.cpp | 32 |
8 files changed, 307 insertions, 28 deletions
diff --git a/include/llvm/Object/WindowsResource.h b/include/llvm/Object/WindowsResource.h index f94ad09ce0c..6867798db09 100644 --- a/include/llvm/Object/WindowsResource.h +++ b/include/llvm/Object/WindowsResource.h @@ -30,11 +30,18 @@ #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 <map> namespace llvm { namespace object { @@ -44,23 +51,44 @@ class WindowsResource; class ResourceEntryRef { public: Error moveNext(bool &End); + bool checkTypeString() const { return IsStringType; } + ArrayRef<UTF16> getTypeString() const { return Type; } + uint16_t getTypeID() const { return TypeID; } + bool checkNameString() const { return IsStringName; } + ArrayRef<UTF16> getNameString() const { return Name; } + uint16_t getNameID() const { return NameID; } + uint16_t getLanguage() const { return Suffix->Language; } private: friend class WindowsResource; ResourceEntryRef(BinaryStreamRef Ref, const WindowsResource *Owner, Error &Err); + Error loadNext(); + struct HeaderSuffix { + support::ulittle32_t DataVersion; + support::ulittle16_t MemoryFlags; + support::ulittle16_t Language; + support::ulittle32_t Version; + support::ulittle32_t Characteristics; + }; + BinaryStreamReader Reader; - BinaryStreamRef HeaderBytes; - BinaryStreamRef DataBytes; + bool IsStringType; + ArrayRef<UTF16> Type; + uint16_t TypeID; + bool IsStringName; + ArrayRef<UTF16> Name; + uint16_t NameID; + const HeaderSuffix *Suffix = nullptr; + ArrayRef<uint8_t> Data; const WindowsResource *OwningRes = nullptr; }; class WindowsResource : public Binary { public: - ~WindowsResource() override; Expected<ResourceEntryRef> getHeadEntry(); static bool classof(const Binary *V) { return V->isWinRes(); } @@ -76,6 +104,38 @@ private: BinaryByteStream BBS; }; +class WindowsResourceParser { +public: + WindowsResourceParser(); + + Error parse(WindowsResource *WR); + + void printTree() const; + +private: + class TreeNode { + public: + TreeNode() = default; + explicit TreeNode(uint32_t ID); + explicit TreeNode(ArrayRef<UTF16> Ref); + void addEntry(const ResourceEntryRef &Entry); + void print(ScopedPrinter &Writer, StringRef Name) const; + + private: + TreeNode &addTypeNode(const ResourceEntryRef &Entry); + TreeNode &addNameNode(const ResourceEntryRef &Entry); + TreeNode &addLanguageNode(const ResourceEntryRef &Entry); + TreeNode &addChild(uint32_t ID); + TreeNode &addChild(ArrayRef<UTF16> NameRef); + uint16_t ID; + std::vector<UTF16> Name; + std::map<uint32_t, std::unique_ptr<TreeNode>> IDChildren; + std::map<std::string, std::unique_ptr<TreeNode>> StringChildren; + }; + + TreeNode Root; +}; + } // namespace object } // namespace llvm diff --git a/include/llvm/Support/BinaryStreamReader.h b/include/llvm/Support/BinaryStreamReader.h index 56375f41d2c..29e8a2ab08a 100644 --- a/include/llvm/Support/BinaryStreamReader.h +++ b/include/llvm/Support/BinaryStreamReader.h @@ -14,6 +14,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Support/BinaryStreamArray.h" #include "llvm/Support/BinaryStreamRef.h" +#include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/type_traits.h" @@ -104,6 +105,13 @@ public: /// returns an appropriate error code. Error readCString(StringRef &Dest); + /// Similar to readCString, however read a null-terminated UTF16 string + /// instead. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + Error readWideString(ArrayRef<UTF16> &Dest); + /// Read a \p Length byte string into \p Dest. Whether a copy occurs depends /// on the implementation of the underlying stream. Updates the stream's /// offset to point after the newly read data. diff --git a/lib/Object/WindowsResource.cpp b/lib/Object/WindowsResource.cpp index b5256346909..9dea5539858 100644 --- a/lib/Object/WindowsResource.cpp +++ b/lib/Object/WindowsResource.cpp @@ -12,20 +12,22 @@ //===----------------------------------------------------------------------===// #include "llvm/Object/WindowsResource.h" -#include "llvm/Object/Error.h" +#include "llvm/Support/COFF.h" #include <system_error> namespace llvm { namespace object { +#define RETURN_IF_ERROR(X) \ + if (auto EC = X) \ + return std::move(EC); + +const uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t); + static const size_t ResourceMagicSize = 16; 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 +35,6 @@ WindowsResource::WindowsResource(MemoryBufferRef Source) support::little); } -WindowsResource::~WindowsResource() = default; - Expected<std::unique_ptr<WindowsResource>> WindowsResource::createWindowsResource(MemoryBufferRef Source) { if (Source.getBufferSize() < ResourceMagicSize + NullEntrySize) @@ -72,19 +72,152 @@ Error ResourceEntryRef::moveNext(bool &End) { return Error::success(); } +static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID, + ArrayRef<UTF16> &Str, bool &IsString) { + uint16_t IDFlag; + RETURN_IF_ERROR(Reader.readInteger(IDFlag)); + IsString = IDFlag != 0xffff; + + if (IsString) { + Reader.setOffset( + Reader.getOffset() - + sizeof(uint16_t)); // Re-read the bytes which we used to check the flag. + RETURN_IF_ERROR(Reader.readWideString(Str)); + } else + RETURN_IF_ERROR(Reader.readInteger(ID)); + + return Error::success(); +} + Error ResourceEntryRef::loadNext() { uint32_t DataSize; RETURN_IF_ERROR(Reader.readInteger(DataSize)); uint32_t HeaderSize; RETURN_IF_ERROR(Reader.readInteger(HeaderSize)); - // 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(DataBytes, DataSize)); + + if (HeaderSize < MIN_HEADER_SIZE) + return make_error<GenericBinaryError>("Header size is too small.", + object_error::parse_failed); + + RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType)); + + RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName)); + + RETURN_IF_ERROR(Reader.padToAlignment(sizeof(uint32_t))); + + RETURN_IF_ERROR(Reader.readObject(Suffix)); + + RETURN_IF_ERROR(Reader.readArray(Data, 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) { + + Root.addEntry(Entry); + + RETURN_IF_ERROR(Entry.moveNext(End)); + } + + return Error::success(); +} + +void WindowsResourceParser::printTree() const { + ScopedPrinter Writer(outs()); + Root.print(Writer, "Resource Tree"); +} + +void WindowsResourceParser::TreeNode::addEntry(const ResourceEntryRef &Entry) { + TreeNode &TypeNode = addTypeNode(Entry); + TreeNode &NameNode = TypeNode.addNameNode(Entry); + NameNode.addLanguageNode(Entry); +} + +WindowsResourceParser::TreeNode::TreeNode(uint32_t ID) : ID(ID) {} + +WindowsResourceParser::TreeNode::TreeNode(ArrayRef<UTF16> NameRef) + : Name(NameRef) {} + +WindowsResourceParser::TreeNode & +WindowsResourceParser::TreeNode::addTypeNode(const ResourceEntryRef &Entry) { + if (Entry.checkTypeString()) + return addChild(Entry.getTypeString()); + else + return addChild(Entry.getTypeID()); +} + +WindowsResourceParser::TreeNode & +WindowsResourceParser::TreeNode::addNameNode(const ResourceEntryRef &Entry) { + if (Entry.checkNameString()) + return addChild(Entry.getNameString()); + else + return addChild(Entry.getNameID()); +} + +WindowsResourceParser::TreeNode & +WindowsResourceParser::TreeNode::addLanguageNode( + const ResourceEntryRef &Entry) { + return addChild(Entry.getLanguage()); +} + +WindowsResourceParser::TreeNode & +WindowsResourceParser::TreeNode::addChild(uint32_t ID) { + auto Child = IDChildren.find(ID); + if (Child == IDChildren.end()) { + auto NewChild = llvm::make_unique<WindowsResourceParser::TreeNode>(ID); + WindowsResourceParser::TreeNode &Node = *NewChild; + IDChildren.emplace(ID, std::move(NewChild)); + return Node; + } else + return *(Child->second); +} + +WindowsResourceParser::TreeNode & +WindowsResourceParser::TreeNode::addChild(ArrayRef<UTF16> NameRef) { + std::string NameString; + ArrayRef<UTF16> CorrectedName; + if (llvm::sys::IsBigEndianHost) { + std::vector<UTF16> 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; + llvm::convertUTF16ToUTF8String(CorrectedName, NameString); + + auto Child = StringChildren.find(NameString); + if (Child == StringChildren.end()) { + auto NewChild = llvm::make_unique<WindowsResourceParser::TreeNode>(NameRef); + WindowsResourceParser::TreeNode &Node = *NewChild; + StringChildren.emplace(NameString, std::move(NewChild)); + return Node; + } else + return *(Child->second); +} + +void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer, + StringRef Name) const { + 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)); + } +} + } // namespace object } // namespace llvm diff --git a/lib/Support/BinaryStreamReader.cpp b/lib/Support/BinaryStreamReader.cpp index 86223297116..bfb658cfa0b 100644 --- a/lib/Support/BinaryStreamReader.cpp +++ b/lib/Support/BinaryStreamReader.cpp @@ -69,6 +69,26 @@ Error BinaryStreamReader::readCString(StringRef &Dest) { return Error::success(); } +Error BinaryStreamReader::readWideString(ArrayRef<UTF16> &Dest) { + uint32_t Length = 0; + uint32_t OriginalOffset = getOffset(); + const UTF16 *C; + while (true) { + if (auto EC = readObject(C)) + return EC; + if (*C == 0x0000) + break; + ++Length; + } + uint32_t NewOffset = getOffset(); + setOffset(OriginalOffset); + + if (auto EC = readArray(Dest, Length)) + return EC; + setOffset(NewOffset); + return Error::success(); +} + Error BinaryStreamReader::readFixedString(StringRef &Dest, uint32_t Length) { ArrayRef<uint8_t> Bytes; if (auto EC = readBytes(Bytes, Length)) diff --git a/test/tools/llvm-cvtres/Inputs/test_resource.rc b/test/tools/llvm-cvtres/Inputs/test_resource.rc index fd616520dbe..5ca097baa0f 100644 --- a/test/tools/llvm-cvtres/Inputs/test_resource.rc +++ b/test/tools/llvm-cvtres/Inputs/test_resource.rc @@ -42,3 +42,9 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS MENUITEM "salad", 101
MENUITEM "duck", 102
}
+
+
+myresource stringarray {
+ "this is a user defined resource\0",
+ "it contains many strings\0",
+}
\ No newline at end of file diff --git a/test/tools/llvm-cvtres/Inputs/test_resource.res b/test/tools/llvm-cvtres/Inputs/test_resource.res Binary files differindex c577ecc3d63..d422bb4904d 100644 --- a/test/tools/llvm-cvtres/Inputs/test_resource.res +++ b/test/tools/llvm-cvtres/Inputs/test_resource.res diff --git a/test/tools/llvm-cvtres/resource.test b/test/tools/llvm-cvtres/resource.test index 16970343c60..b9be74bf671 100644 --- a/test/tools/llvm-cvtres/resource.test +++ b/test/tools/llvm-cvtres/resource.test @@ -4,4 +4,48 @@ RUN: llvm-cvtres %p/Inputs/test_resource.res | FileCheck %s -CHECK: Number of resources: 7 +CHECK: Number of resources: 8 +CHECK-NEXT: Resource Tree [ +CHECK-NEXT: STRINGARRAY [ +CHECK-NEXT: MYRESOURCE [ +CHECK-NEXT: 1033 [ +CHECK-NEXT: ] +CHECK-NEXT: ] +CHECK-NEXT: ] +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: ] diff --git a/tools/llvm-cvtres/llvm-cvtres.cpp b/tools/llvm-cvtres/llvm-cvtres.cpp index 96f7437ab5f..95a6623b44e 100644 --- a/tools/llvm-cvtres/llvm-cvtres.cpp +++ b/tools/llvm-cvtres/llvm-cvtres.cpp @@ -131,7 +131,7 @@ int main(int argc_, const char *argv_[]) { std::vector<std::string> 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 @@ int main(int argc_, const char *argv_[]) { 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<object::OwningBinary<object::Binary>> BinaryOrErr = object::createBinary(File); @@ -166,17 +180,11 @@ int main(int argc_, const char *argv_[]) { 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; } |