mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 12:12:47 +01:00
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:
parent
69a17befcb
commit
6bcda35ff2
@ -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;
|
||||
|
82
include/llvm/Object/WindowsResource.h
Normal file
82
include/llvm/Object/WindowsResource.h
Normal 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
|
@ -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>
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
92
lib/Object/WindowsResource.cpp
Normal file
92
lib/Object/WindowsResource.cpp
Normal 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
|
BIN
test/tools/llvm-cvtres/Inputs/cursor_small.bmp
Normal file
BIN
test/tools/llvm-cvtres/Inputs/cursor_small.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 822 B |
BIN
test/tools/llvm-cvtres/Inputs/okay_small.bmp
Normal file
BIN
test/tools/llvm-cvtres/Inputs/okay_small.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 822 B |
44
test/tools/llvm-cvtres/Inputs/test_resource.rc
Normal file
44
test/tools/llvm-cvtres/Inputs/test_resource.rc
Normal 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
|
||||
}
|
BIN
test/tools/llvm-cvtres/Inputs/test_resource.res
Normal file
BIN
test/tools/llvm-cvtres/Inputs/test_resource.res
Normal file
Binary file not shown.
7
test/tools/llvm-cvtres/resource.test
Normal file
7
test/tools/llvm-cvtres/resource.test
Normal 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
|
@ -1,4 +1,5 @@
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
Object
|
||||
Option
|
||||
Support
|
||||
)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user