Add functionality to cvtres to parse all entries in res file.

Summary: Added the new modules in the Object/ folder.  Updated the
llvm-cvtres interface as well, and added additional tests.

Subscribers: llvm-commits, mgorny

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

llvm-svn: 303480
This commit is contained in:
Eric Beckmann 2017-05-20 01:49:19 +00:00
parent 4f3e3811d1
commit a6bdf751a2
15 changed files with 339 additions and 5 deletions

View File

@ -57,6 +57,8 @@ protected:
ID_MachO64L, // MachO 64-bit, little endian
ID_MachO64B, // MachO 64-bit, big endian
ID_WinRes, // Windows resource (.res) file.
ID_Wasm,
ID_EndObjects
@ -132,6 +134,8 @@ public:
TypeID == ID_MachO32B || TypeID == ID_MachO64B);
}
bool isWinRes() const { return TypeID == ID_WinRes; }
Triple::ObjectFormatType getTripleObjectFormat() const {
if (isCOFF())
return Triple::COFF;

View File

@ -0,0 +1,82 @@
//===-- WindowsResource.h ---------------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
//
// This file declares the .res file class. .res files are intermediate
// products of the typical resource-compilation process on Windows. This
// process is as follows:
//
// .rc file(s) ---(rc.exe)---> .res file(s) ---(cvtres.exe)---> COFF file
//
// .rc files are human-readable scripts that list all resources a program uses.
//
// They are compiled into .res files, which are a list of the resources in
// binary form.
//
// Finally the data stored in the .res is compiled into a COFF file, where it
// is organized in a directory tree structure for optimized access by the
// program during runtime.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648007(v=vs.85).aspx
//
//===---------------------------------------------------------------------===//
#ifndef LLVM_INCLUDE_LLVM_OBJECT_RESFILE_H
#define LLVM_INCLUDE_LLVM_OBJECT_RESFILE_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Object/Binary.h"
#include "llvm/Support/BinaryByteStream.h"
#include "llvm/Support/BinaryStreamReader.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
namespace llvm {
namespace object {
class WindowsResource;
class ResourceEntryRef {
public:
Error moveNext(bool &End);
private:
friend class WindowsResource;
ResourceEntryRef(BinaryStreamRef Ref, const WindowsResource *Owner,
Error &Err);
Error loadNext();
BinaryStreamReader Reader;
BinaryStreamRef HeaderBytes;
BinaryStreamRef DataBytes;
const WindowsResource *OwningRes = nullptr;
};
class WindowsResource : public Binary {
public:
~WindowsResource() override;
Expected<ResourceEntryRef> getHeadEntry();
static bool classof(const Binary *V) { return V->isWinRes(); }
static Expected<std::unique_ptr<WindowsResource>>
createWindowsResource(MemoryBufferRef Source);
private:
friend class ResourceEntryRef;
WindowsResource(MemoryBufferRef Source);
BinaryByteStream BBS;
};
} // namespace object
} // namespace llvm
#endif

View File

@ -16,7 +16,6 @@
#include "llvm/Support/BinaryStreamRef.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/type_traits.h"
#include <string>

View File

@ -261,7 +261,7 @@ struct file_magic {
coff_object, ///< COFF object file
coff_import_library, ///< COFF import library
pecoff_executable, ///< PECOFF executable file
windows_resource, ///< Windows compiled resource file (.rc)
windows_resource, ///< Windows compiled resource file (.res)
wasm_object ///< WebAssembly Object file
};

View File

@ -17,6 +17,7 @@
#include "llvm/Object/Error.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/WindowsResource.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
@ -71,9 +72,10 @@ Expected<std::unique_ptr<Binary>> object::createBinary(MemoryBufferRef Buffer,
return ObjectFile::createSymbolicFile(Buffer, Type, Context);
case sys::fs::file_magic::macho_universal_binary:
return MachOUniversalBinary::create(Buffer);
case sys::fs::file_magic::windows_resource:
return WindowsResource::createWindowsResource(Buffer);
case sys::fs::file_magic::unknown:
case sys::fs::file_magic::coff_cl_gl_object:
case sys::fs::file_magic::windows_resource:
// Unrecognized object file format.
return errorCodeToError(object_error::invalid_file_type);
}

View File

@ -18,6 +18,7 @@ add_llvm_library(LLVMObject
SymbolicFile.cpp
SymbolSize.cpp
WasmObjectFile.cpp
WindowsResource.cpp
ADDITIONAL_HEADER_DIRS
${LLVM_MAIN_INCLUDE_DIR}/llvm/Object

View File

@ -0,0 +1,92 @@
//===-- WindowsResource.cpp -------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the .res file class.
//
//===----------------------------------------------------------------------===//
#include "llvm/Object/WindowsResource.h"
#include "llvm/Object/Error.h"
#include <system_error>
namespace llvm {
namespace object {
static const char ResourceMagic[] = {
'\0', '\0', '\0', '\0', '\x20', '\0', '\0', '\0',
'\xff', '\xff', '\0', '\0', '\xff', '\xff', '\0', '\0'};
static const char NullEntry[16] = {'\0'};
#define RETURN_IF_ERROR(X) \
if (auto EC = X) \
return EC;
WindowsResource::WindowsResource(MemoryBufferRef Source)
: Binary(Binary::ID_WinRes, Source) {
size_t LeadingSize = sizeof(ResourceMagic) + sizeof(NullEntry);
BBS = BinaryByteStream(Data.getBuffer().drop_front(LeadingSize),
support::little);
}
WindowsResource::~WindowsResource() = default;
Expected<std::unique_ptr<WindowsResource>>
WindowsResource::createWindowsResource(MemoryBufferRef Source) {
if (Source.getBufferSize() < sizeof(ResourceMagic) + sizeof(NullEntry))
return make_error<GenericBinaryError>(
"File too small to be a resource file",
object_error::invalid_file_type);
std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source));
return std::move(Ret);
}
Expected<ResourceEntryRef> WindowsResource::getHeadEntry() {
Error Err = Error::success();
auto Ref = ResourceEntryRef(BinaryStreamRef(BBS), this, Err);
if (Err)
return std::move(Err);
return Ref;
}
ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref,
const WindowsResource *Owner, Error &Err)
: Reader(Ref), OwningRes(Owner) {
if (loadNext())
Err = make_error<GenericBinaryError>("Could not read first entry.",
object_error::unexpected_eof);
}
Error ResourceEntryRef::moveNext(bool &End) {
// Reached end of all the entries.
if (Reader.bytesRemaining() == 0) {
End = true;
return Error::success();
}
RETURN_IF_ERROR(loadNext());
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));
RETURN_IF_ERROR(Reader.padToAlignment(sizeof(uint32_t)));
return Error::success();
}
} // namespace object
} // namespace llvm

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

View File

@ -0,0 +1,44 @@
#include "windows.h"
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
myaccelerators ACCELERATORS
{
"^C", 999, VIRTKEY, ALT
"D", 1100, VIRTKEY, CONTROL, SHIFT
"^R", 444, ASCII, NOINVERT
}
cursor BITMAP "cursor_small.bmp"
okay BITMAP "okay_small.bmp"
14432 MENU
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
{
MENUITEM "yu", 100
MENUITEM "shala", 101
MENUITEM "kaoya", 102
}
testdialog DIALOG 10, 10, 200, 300
STYLE WS_POPUP | WS_BORDER
CAPTION "Test"
{
CTEXT "Continue:", 1, 10, 10, 230, 14
PUSHBUTTON "&OK", 2, 66, 134, 161, 13
}
12 ACCELERATORS
{
"X", 164, VIRTKEY, ALT
"H", 5678, VIRTKEY, CONTROL, SHIFT
"^R", 444, ASCII, NOINVERT
}
"eat" MENU
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS
{
MENUITEM "fish", 100
MENUITEM "salad", 101
MENUITEM "duck", 102
}

Binary file not shown.

View File

@ -0,0 +1,7 @@
// The input was generated with the following command, using the original Windows
// rc.exe:
// > rc /fo test_resource.res /nologo test_resource.rc
RUN: llvm-cvtres %p/Inputs/test_resource.res | FileCheck %s
CHECK: Number of resources: 7

View File

@ -1,4 +1,5 @@
set(LLVM_LINK_COMPONENTS
Object
Option
Support
)

View File

@ -14,17 +14,23 @@
#include "llvm-cvtres.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/WindowsResource.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/BinaryStreamError.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace object;
namespace {
@ -61,6 +67,28 @@ public:
static ExitOnError ExitOnErr;
}
LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg) {
errs() << Msg;
exit(1);
}
static void reportError(StringRef Input, std::error_code EC) {
reportError(Twine(Input) + ": " + EC.message() + ".\n");
}
void error(std::error_code EC) {
if (!EC)
return;
reportError(EC.message() + ".\n");
}
void error(Error EC) {
if (!EC)
return;
handleAllErrors(std::move(EC),
[&](const ErrorInfoBase &EI) { reportError(EI.message()); });
}
int main(int argc_, const char *argv_[]) {
sys::PrintStackTraceOnErrorSignal(argv_[0]);
PrettyStackTraceProgram X(argc_, argv_);
@ -76,11 +104,79 @@ int main(int argc_, const char *argv_[]) {
CvtResOptTable T;
unsigned MAI, MAC;
ArrayRef<const char *> ArgsArr = makeArrayRef(argv_, argc_);
ArrayRef<const char *> ArgsArr = makeArrayRef(argv_ + 1, argc_);
opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
if (InputArgs.hasArg(OPT_HELP))
if (InputArgs.hasArg(OPT_HELP)) {
T.PrintHelp(outs(), "cvtres", "Resource Converter", false);
return 0;
}
machine Machine;
if (InputArgs.hasArg(OPT_MACHINE)) {
std::string MachineString = InputArgs.getLastArgValue(OPT_MACHINE).upper();
Machine = StringSwitch<machine>(MachineString)
.Case("ARM", machine::ARM)
.Case("X64", machine::X64)
.Case("X86", machine::X86)
.Default(machine::UNKNOWN);
if (Machine == machine::UNKNOWN)
reportError("Unsupported machine architecture");
} else {
outs() << "Machine architecture not specified; assumed X64.\n";
Machine = machine::X64;
}
std::vector<std::string> InputFiles = InputArgs.getAllArgValues(OPT_INPUT);
if (InputFiles.size() == 0) {
reportError("No input file specified");
}
SmallString<128> OutputFile;
if (InputArgs.hasArg(OPT_OUT)) {
OutputFile = InputArgs.getLastArgValue(OPT_OUT);
} else {
OutputFile = StringRef(InputFiles[0]);
llvm::sys::path::replace_extension(OutputFile, ".obj");
}
for (const auto &File : InputFiles) {
Expected<object::OwningBinary<object::Binary>> BinaryOrErr =
object::createBinary(File);
if (!BinaryOrErr)
reportError(File, errorToErrorCode(BinaryOrErr.takeError()));
Binary &Binary = *BinaryOrErr.get().getBinary();
WindowsResource *RF = dyn_cast<WindowsResource>(&Binary);
if (!RF)
reportError(File + ": unrecognized file format.\n");
int EntryNumber = 0;
Expected<ResourceEntryRef> EntryOrErr = RF->getHeadEntry();
if (!EntryOrErr)
error(EntryOrErr.takeError());
ResourceEntryRef Entry = EntryOrErr.get();
bool End = false;
while (!End) {
error(Entry.moveNext(End));
EntryNumber++;
}
outs() << "Number of resources: " << EntryNumber << "\n";
}
outs() << "Machine: ";
switch (Machine) {
case machine::ARM:
outs() << "ARM\n";
break;
case machine::X86:
outs() << "X86\n";
break;
default:
outs() << "X64\n";
}
return 0;
}

View File

@ -10,4 +10,10 @@
#ifndef LLVM_TOOLS_LLVMCVTRES_LLVMCVTRES_H
#define LLVM_TOOLS_LLVMCVTRES_LLVMCVTRES_H
#include <system_error>
void error(std::error_code EC);
enum class machine { UNKNOWN = 0, ARM, X64, X86 };
#endif