Index: llvm/trunk/include/llvm/Object/Binary.h =================================================================== --- llvm/trunk/include/llvm/Object/Binary.h +++ llvm/trunk/include/llvm/Object/Binary.h @@ -57,6 +57,8 @@ 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 @@ TypeID == ID_MachO32B || TypeID == ID_MachO64B); } + bool isWinRes() const { return TypeID == ID_WinRes; } + Triple::ObjectFormatType getTripleObjectFormat() const { if (isCOFF()) return Triple::COFF; Index: llvm/trunk/include/llvm/Object/WindowsResource.h =================================================================== --- llvm/trunk/include/llvm/Object/WindowsResource.h +++ llvm/trunk/include/llvm/Object/WindowsResource.h @@ -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 getHeadEntry(); + + static bool classof(const Binary *V) { return V->isWinRes(); } + + static Expected> + createWindowsResource(MemoryBufferRef Source); + +private: + friend class ResourceEntryRef; + + WindowsResource(MemoryBufferRef Source); + + BinaryByteStream BBS; +}; + +} // namespace object +} // namespace llvm + +#endif Index: llvm/trunk/include/llvm/Support/BinaryStreamReader.h =================================================================== --- llvm/trunk/include/llvm/Support/BinaryStreamReader.h +++ llvm/trunk/include/llvm/Support/BinaryStreamReader.h @@ -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 Index: llvm/trunk/include/llvm/Support/FileSystem.h =================================================================== --- llvm/trunk/include/llvm/Support/FileSystem.h +++ llvm/trunk/include/llvm/Support/FileSystem.h @@ -261,7 +261,7 @@ 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 }; Index: llvm/trunk/lib/Object/Binary.cpp =================================================================== --- llvm/trunk/lib/Object/Binary.cpp +++ llvm/trunk/lib/Object/Binary.cpp @@ -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 @@ 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); } Index: llvm/trunk/lib/Object/CMakeLists.txt =================================================================== --- llvm/trunk/lib/Object/CMakeLists.txt +++ llvm/trunk/lib/Object/CMakeLists.txt @@ -18,6 +18,7 @@ SymbolicFile.cpp SymbolSize.cpp WasmObjectFile.cpp + WindowsResource.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/Object Index: llvm/trunk/lib/Object/WindowsResource.cpp =================================================================== --- llvm/trunk/lib/Object/WindowsResource.cpp +++ llvm/trunk/lib/Object/WindowsResource.cpp @@ -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 + +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> +WindowsResource::createWindowsResource(MemoryBufferRef Source) { + if (Source.getBufferSize() < sizeof(ResourceMagic) + sizeof(NullEntry)) + return make_error( + "File too small to be a resource file", + object_error::invalid_file_type); + std::unique_ptr Ret(new WindowsResource(Source)); + return std::move(Ret); +} + +Expected 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("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 Index: llvm/trunk/test/tools/llvm-cvtres/Inputs/test_resource.rc =================================================================== --- llvm/trunk/test/tools/llvm-cvtres/Inputs/test_resource.rc +++ llvm/trunk/test/tools/llvm-cvtres/Inputs/test_resource.rc @@ -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 +} Index: llvm/trunk/test/tools/llvm-cvtres/resource.test =================================================================== --- llvm/trunk/test/tools/llvm-cvtres/resource.test +++ llvm/trunk/test/tools/llvm-cvtres/resource.test @@ -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 Index: llvm/trunk/tools/llvm-cvtres/CMakeLists.txt =================================================================== --- llvm/trunk/tools/llvm-cvtres/CMakeLists.txt +++ llvm/trunk/tools/llvm-cvtres/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + Object Option Support ) Index: llvm/trunk/tools/llvm-cvtres/llvm-cvtres.h =================================================================== --- llvm/trunk/tools/llvm-cvtres/llvm-cvtres.h +++ llvm/trunk/tools/llvm-cvtres/llvm-cvtres.h @@ -10,4 +10,10 @@ #ifndef LLVM_TOOLS_LLVMCVTRES_LLVMCVTRES_H #define LLVM_TOOLS_LLVMCVTRES_LLVMCVTRES_H +#include + +void error(std::error_code EC); + +enum class machine { UNKNOWN = 0, ARM, X64, X86 }; + #endif Index: llvm/trunk/tools/llvm-cvtres/llvm-cvtres.cpp =================================================================== --- llvm/trunk/tools/llvm-cvtres/llvm-cvtres.cpp +++ llvm/trunk/tools/llvm-cvtres/llvm-cvtres.cpp @@ -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 @@ 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 @@ CvtResOptTable T; unsigned MAI, MAC; - ArrayRef ArgsArr = makeArrayRef(argv_, argc_); + ArrayRef 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(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 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> BinaryOrErr = + object::createBinary(File); + if (!BinaryOrErr) + reportError(File, errorToErrorCode(BinaryOrErr.takeError())); + + Binary &Binary = *BinaryOrErr.get().getBinary(); + + WindowsResource *RF = dyn_cast(&Binary); + if (!RF) + reportError(File + ": unrecognized file format.\n"); + + int EntryNumber = 0; + Expected 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; }