Index: llvm/include/llvm/Object/Binary.h =================================================================== --- llvm/include/llvm/Object/Binary.h +++ llvm/include/llvm/Object/Binary.h @@ -57,6 +57,8 @@ ID_MachO64L, // MachO 64-bit, little endian ID_MachO64B, // MachO 64-bit, big endian + ID_RES, + ID_Wasm, ID_EndObjects @@ -132,6 +134,10 @@ TypeID == ID_MachO32B || TypeID == ID_MachO64B); } + bool isRes() const { + return TypeID == ID_RES; + } + Triple::ObjectFormatType getTripleObjectFormat() const { if (isCOFF()) return Triple::COFF; Index: llvm/include/llvm/Object/ResFile.h =================================================================== --- /dev/null +++ llvm/include/llvm/Object/ResFile.h @@ -0,0 +1,88 @@ +//===-- ResFile.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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_INCLUDE_LLVM_OBJECT_RESFILE_H +#define LLVM_INCLUDE_LLVM_OBJECT_RESFILE_H + +#include "llvm/Object/Binary.h" +#include "llvm/Object/Error.h" +#include "llvm/Support/Endian.h" + +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'}; + +class ResFile; + +class ResEntryRef { +public: + ResEntryRef() = default; + ResEntryRef(const uint8_t* EntryP, const ResFile *Owner); + + bool moveNext(); + +private: + const uint8_t* EntryP; + const ResFile *OwningRes = nullptr; + +}; + +class ResFile : public Binary { +public: + ResFile(MemoryBufferRef Source, Error &Err); + ~ResFile() override; + + ResEntryRef HeadEntry(); + + static inline bool classof(const Binary *v) { + return v->isRes(); + } + + static Expected> createResFile(MemoryBufferRef Source); + +private: + Binary* Bin; + + friend class ResEntryRef; +}; + +inline ResEntryRef::ResEntryRef(const uint8_t* EntryP, const ResFile* Owner) + : EntryP(EntryP), OwningRes(Owner) {} + +inline bool ResEntryRef::moveNext() { + auto DataSizeP = reinterpret_cast(EntryP); + auto HeaderSizeP = reinterpret_cast(EntryP) + 1; + uint32_t Offset = *DataSizeP + *HeaderSizeP; + outs() << "DataSize " << *DataSizeP << "\n"; + //outs () << "HeaderSize " << *HeaderSizeP << "\n"; + //outs() << "Offset " << Offset << "\n"; + const uint8_t* P = EntryP; + //align to DWORD + if (Offset % 4 != 0) + Offset += 4 - (Offset % 4); + P += Offset; + outs() << "Result " << P - reinterpret_cast(OwningRes->Data.getBufferStart()) << "\n\n"; + // reached end of entries. + if (P < reinterpret_cast(OwningRes->Data.getBufferStart()) || P > reinterpret_cast(OwningRes->Data.getBufferEnd()) - 4) + return false; + EntryP = P; + return true; +} + +} +} + +#endif \ No newline at end of file Index: llvm/include/llvm/Support/FileSystem.h =================================================================== --- llvm/include/llvm/Support/FileSystem.h +++ llvm/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/lib/Object/Binary.cpp =================================================================== --- llvm/lib/Object/Binary.cpp +++ llvm/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/ResFile.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 ResFile::createResFile(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/lib/Object/CMakeLists.txt =================================================================== --- llvm/lib/Object/CMakeLists.txt +++ llvm/lib/Object/CMakeLists.txt @@ -15,6 +15,7 @@ Object.cpp ObjectFile.cpp RecordStreamer.cpp + ResFile.cpp SymbolicFile.cpp SymbolSize.cpp WasmObjectFile.cpp Index: llvm/lib/Object/ResFile.cpp =================================================================== --- /dev/null +++ llvm/lib/Object/ResFile.cpp @@ -0,0 +1,41 @@ +//===-- ResFile.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/ResFile.h" +#include "llvm/Support/COFF.h" +#include + +namespace llvm { +namespace object { + + +ResFile::ResFile(MemoryBufferRef Source, Error &Err) + : Binary(Binary::ID_RES, Source) {} + +ResFile::~ResFile() = default; + +Expected> ResFile::createResFile(MemoryBufferRef Source) { + Error Err = Error::success(); + std::unique_ptr Ret(new ResFile(Source, Err)); + if (Err) + return std::move(Err); + return std::move(Ret); + +} + +ResEntryRef ResFile::HeadEntry() { + return ResEntryRef(reinterpret_cast(Data.getBufferStart()) + sizeof(ResourceMagic) + sizeof(NullEntry), this); +} + +} +} Index: llvm/test/tools/llvm-cvtres/Inputs/test_resource.rc =================================================================== --- /dev/null +++ llvm/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/test/tools/llvm-cvtres/resource.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-cvtres/resource.test @@ -0,0 +1,4 @@ +RUN: llvm-cvtres %p/Inputs/test_resource.res \ +RUN: | FileCheck %s -check-prefix TEST_RES + +TEST_RES: Number of resources: 7 Index: llvm/tools/llvm-cvtres/CMakeLists.txt =================================================================== --- llvm/tools/llvm-cvtres/CMakeLists.txt +++ llvm/tools/llvm-cvtres/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + Object Option Support ) @@ -9,5 +10,7 @@ add_public_tablegen_target(CvtResTableGen) add_llvm_tool(llvm-cvtres + Error.cpp llvm-cvtres.cpp + ResCOFFWriter.h ) Index: llvm/tools/llvm-cvtres/Error.h =================================================================== --- /dev/null +++ llvm/tools/llvm-cvtres/Error.h @@ -0,0 +1,37 @@ +//===- Error.h - system_error extensions for llvm-cvtres --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This declares a new error_category for the llvm-cvtres tool. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_CVTRES_ERROR_H +#define LLVM_TOOLS_LLVM_CVTRES_ERROR_H + +#include + +namespace llvm { +const std::error_category &cvtres_category(); + +enum class cvtres_error { + success = 0, + unrecognized_file_format +}; + +inline std::error_code make_error_code(cvtres_error e) { + return std::error_code(static_cast(e), cvtres_category()); +} + +} + +namespace std { +template <> struct is_error_code_enum : std::true_type {}; +} + +#endif \ No newline at end of file Index: llvm/tools/llvm-cvtres/Error.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-cvtres/Error.cpp @@ -0,0 +1,46 @@ +//===- Error.cpp - system_error extensions for llvm-cvtres ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines a new error_category for the llvm-cvtres tool. +// +//===----------------------------------------------------------------------===// + +#include "Error.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; + +namespace { +class _cvtres_error_category : public std::error_category { +public: + const char* name() const noexcept override; + std::string message(int ev) const override; +}; +} + +const char *_cvtres_error_category::name() const noexcept { + return "llvm.cvtres"; +} + +std::string _cvtres_error_category::message(int EV) const { + switch (static_cast(EV)) { + case cvtres_error::success: return "Success"; + case cvtres_error::unrecognized_file_format: + return "Unrecognized file type."; + } + llvm_unreachable("An enumerator of cvtres_error does not have a message " + "defined."); +} + +namespace llvm { +const std::error_category &cvtres_category() { + static _cvtres_error_category o; + return o; +} +} // namespace llvm Index: llvm/tools/llvm-cvtres/ResCOFFWriter.h =================================================================== --- /dev/null +++ llvm/tools/llvm-cvtres/ResCOFFWriter.h @@ -0,0 +1,23 @@ +//===- ResCOFFWriter.h ---------------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_CVTRES_RESCOFFWRITER_H +#define LLVM_TOOLS_LLVM_CVTRES_RESCOFFWRITER_H + +namespace llvm { + +enum class machine { + ARM, + X64, + X86 +}; + +} + +#endif \ No newline at end of file Index: llvm/tools/llvm-cvtres/llvm-cvtres.cpp =================================================================== --- llvm/tools/llvm-cvtres/llvm-cvtres.cpp +++ llvm/tools/llvm-cvtres/llvm-cvtres.cpp @@ -12,19 +12,28 @@ // //===----------------------------------------------------------------------===// +#include + #include "llvm-cvtres.h" +#include "Error.h" +#include "ResCOFFWriter.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ResFile.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.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/StringSaver.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; +using namespace object; namespace { @@ -61,6 +70,16 @@ static ExitOnError ExitOnErr; } +LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg) { + errs() << "llvm-cvtres error: " << Msg << ".\n"; + errs().flush(); + exit(1); +} + +static void reportError(StringRef Input, std::error_code EC) { + reportError(Twine(Input) + ": " + EC.message()); +} + int main(int argc_, const char *argv_[]) { sys::PrintStackTraceOnErrorSignal(argv_[0]); PrettyStackTraceProgram X(argc_, argv_); @@ -76,11 +95,91 @@ CvtResOptTable T; unsigned MAI, MAC; - ArrayRef ArgsArr = makeArrayRef(argv_, argc_); + ArrayRef ArgsArr = makeArrayRef(argv_ + 1, argc_); opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); + // llvm::outs() << "MAI " << MAI << "\n"; + // llvm::outs() << "MAC " << MAC << "\n"; + // for (auto Arg : InputArgs) { + // Arg->print(llvm::outs()); + // } - if (InputArgs.hasArg(OPT_HELP)) + if (InputArgs.hasArg(OPT_HELP)) { T.PrintHelp(outs(), "cvtres", "Resource Converter", false); - + return 0; + } + + if (InputArgs.hasArg(OPT_INVALID)) + outs() << "invalid\n"; + + if (InputArgs.hasArg(OPT_UNKNOWN)) + outs() << "invalid\n"; + + llvm::machine Machine; + + if (InputArgs.hasArg(OPT_MACHINE)) { + std::locale Loc; + std::string MachineString = InputArgs.getLastArgValue(OPT_MACHINE).str(); + for (unsigned int i = 0; i < MachineString.length(); i++) { + MachineString[i] = std::toupper(MachineString[i], Loc); + } + if(MachineString == "ARM") + Machine = machine::ARM; + else if(MachineString == "X64") + Machine = machine::X64; + else if(MachineString == "X86") + Machine = machine::X86; + else + reportError("Unsupported machine architecture"); + } else { + outs() << "\nMachine architecture not specified; assumed X64.\n"; + Machine = machine::X64; + } + + for (auto *Arg : InputArgs.filtered(OPT_INVALID)) + outs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n"; + + std::vector InputFiles = InputArgs.getAllArgValues(OPT_INPUT); + + if (InputFiles.size() == 0) { + reportError("No input file specified"); + } + + for (auto Entry : InputFiles) { + outs() << Entry << "\n"; + } + + 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"); + } + + outs() << "Output " << OutputFile << "\n"; + + for (auto File : InputFiles) { + Expected> BinaryOrErr = object::createBinary(File); + if (!BinaryOrErr) { + outs() << "badbin\n"; + reportError(File, errorToErrorCode(BinaryOrErr.takeError())); + } + + Binary& Binary = *BinaryOrErr.get().getBinary(); + + ResFile *RF = dyn_cast(&Binary); + if (!RF) { + outs() << "badfile\n"; + reportError(File, cvtres_error::unrecognized_file_format); + } + + int EntryNumber = 0; + ResEntryRef Entry = RF->HeadEntry(); + do { + EntryNumber++; + }while (Entry.moveNext()); + outs() << "Number of resources: " << EntryNumber << "\n"; + } return 0; }