aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Beckmann <ecbeckmann@google.com>2017-05-30 18:19:06 +0000
committerEric Beckmann <ecbeckmann@google.com>2017-05-30 18:19:06 +0000
commit1f0488cec7a7f4c35ca9338bb663fb04d4225a0d (patch)
treea5bcec3f75f130a3e3875a41b93fd61909d32d10
parent7aecbf8e991a5a8916bf6639feff659b08308dd6 (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.h66
-rw-r--r--include/llvm/Support/BinaryStreamReader.h8
-rw-r--r--lib/Object/WindowsResource.cpp157
-rw-r--r--lib/Support/BinaryStreamReader.cpp20
-rw-r--r--test/tools/llvm-cvtres/Inputs/test_resource.rc6
-rw-r--r--test/tools/llvm-cvtres/Inputs/test_resource.resbin2200 -> 2332 bytes
-rw-r--r--test/tools/llvm-cvtres/resource.test46
-rw-r--r--tools/llvm-cvtres/llvm-cvtres.cpp32
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
index c577ecc3d63..d422bb4904d 100644
--- a/test/tools/llvm-cvtres/Inputs/test_resource.res
+++ b/test/tools/llvm-cvtres/Inputs/test_resource.res
Binary files differ
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;
}