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

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