[llvm] r304225 - Adding parsing ability for .res file.

Eric Beckmann via llvm-commits llvm-commits at lists.llvm.org
Tue May 30 11:19:07 PDT 2017


Author: ecbeckmann
Date: Tue May 30 13:19:06 2017
New Revision: 304225

URL: http://llvm.org/viewvc/llvm-project?rev=304225&view=rev
Log:
Adding parsing ability for .res file.

Subscribers: llvm-commits

Differential Revision: https://reviews.llvm.org/D33566

Modified:
    llvm/trunk/include/llvm/Object/WindowsResource.h
    llvm/trunk/include/llvm/Support/BinaryStreamReader.h
    llvm/trunk/lib/Object/WindowsResource.cpp
    llvm/trunk/lib/Support/BinaryStreamReader.cpp
    llvm/trunk/test/tools/llvm-cvtres/Inputs/test_resource.rc
    llvm/trunk/test/tools/llvm-cvtres/Inputs/test_resource.res
    llvm/trunk/test/tools/llvm-cvtres/resource.test
    llvm/trunk/tools/llvm-cvtres/llvm-cvtres.cpp

Modified: llvm/trunk/include/llvm/Object/WindowsResource.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Object/WindowsResource.h?rev=304225&r1=304224&r2=304225&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Object/WindowsResource.h (original)
+++ llvm/trunk/include/llvm/Object/WindowsResource.h Tue May 30 13:19:06 2017
@@ -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
 

Modified: llvm/trunk/include/llvm/Support/BinaryStreamReader.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/BinaryStreamReader.h?rev=304225&r1=304224&r2=304225&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/BinaryStreamReader.h (original)
+++ llvm/trunk/include/llvm/Support/BinaryStreamReader.h Tue May 30 13:19:06 2017
@@ -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.

Modified: llvm/trunk/lib/Object/WindowsResource.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Object/WindowsResource.cpp?rev=304225&r1=304224&r2=304225&view=diff
==============================================================================
--- llvm/trunk/lib/Object/WindowsResource.cpp (original)
+++ llvm/trunk/lib/Object/WindowsResource.cpp Tue May 30 13:19:06 2017
@@ -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(MemoryB
                          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 &E
   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

Modified: llvm/trunk/lib/Support/BinaryStreamReader.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/BinaryStreamReader.cpp?rev=304225&r1=304224&r2=304225&view=diff
==============================================================================
--- llvm/trunk/lib/Support/BinaryStreamReader.cpp (original)
+++ llvm/trunk/lib/Support/BinaryStreamReader.cpp Tue May 30 13:19:06 2017
@@ -69,6 +69,26 @@ Error BinaryStreamReader::readCString(St
   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))

Modified: llvm/trunk/test/tools/llvm-cvtres/Inputs/test_resource.rc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-cvtres/Inputs/test_resource.rc?rev=304225&r1=304224&r2=304225&view=diff
==============================================================================
--- llvm/trunk/test/tools/llvm-cvtres/Inputs/test_resource.rc (original)
+++ llvm/trunk/test/tools/llvm-cvtres/Inputs/test_resource.rc Tue May 30 13:19:06 2017
@@ -42,3 +42,9 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_A
 	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

Modified: llvm/trunk/test/tools/llvm-cvtres/Inputs/test_resource.res
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-cvtres/Inputs/test_resource.res?rev=304225&r1=304224&r2=304225&view=diff
==============================================================================
Binary files - no diff available.

Modified: llvm/trunk/test/tools/llvm-cvtres/resource.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-cvtres/resource.test?rev=304225&r1=304224&r2=304225&view=diff
==============================================================================
--- llvm/trunk/test/tools/llvm-cvtres/resource.test (original)
+++ llvm/trunk/test/tools/llvm-cvtres/resource.test Tue May 30 13:19:06 2017
@@ -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: ]

Modified: llvm/trunk/tools/llvm-cvtres/llvm-cvtres.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cvtres/llvm-cvtres.cpp?rev=304225&r1=304224&r2=304225&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-cvtres/llvm-cvtres.cpp (original)
+++ llvm/trunk/tools/llvm-cvtres/llvm-cvtres.cpp Tue May 30 13:19:06 2017
@@ -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;
 }




More information about the llvm-commits mailing list