Adding parsing ability for .res file.

Subscribers: llvm-commits

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

llvm-svn: 304225
diff --git a/llvm/lib/Object/WindowsResource.cpp b/llvm/lib/Object/WindowsResource.cpp
index b525634..9dea553 100644
--- a/llvm/lib/Object/WindowsResource.cpp
+++ b/llvm/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 @@
                          support::little);
 }
 
-WindowsResource::~WindowsResource() = default;
-
 Expected<std::unique_ptr<WindowsResource>>
 WindowsResource::createWindowsResource(MemoryBufferRef Source) {
   if (Source.getBufferSize() < ResourceMagicSize + NullEntrySize)
@@ -72,19 +72,152 @@
   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/llvm/lib/Support/BinaryStreamReader.cpp b/llvm/lib/Support/BinaryStreamReader.cpp
index 8622329..bfb658c 100644
--- a/llvm/lib/Support/BinaryStreamReader.cpp
+++ b/llvm/lib/Support/BinaryStreamReader.cpp
@@ -69,6 +69,26 @@
   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))