Adding parsing ability for .res file.

Subscribers: llvm-commits

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

llvm-svn: 304225
This commit is contained in:
Eric Beckmann 2017-05-30 18:19:06 +00:00
parent 4cec434a1b
commit 72fb6a87fb
8 changed files with 307 additions and 28 deletions

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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))

View File

@ -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",
}

View File

@ -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: ]

View File

@ -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;
}