]> granicus.if.org Git - llvm/commitdiff
Add functionality to cvtres to parse all entries in res file.
authorEric Beckmann <ecbeckmann@google.com>
Sat, 20 May 2017 01:49:19 +0000 (01:49 +0000)
committerEric Beckmann <ecbeckmann@google.com>
Sat, 20 May 2017 01:49:19 +0000 (01:49 +0000)
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

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@303480 91177308-0d34-0410-b5e6-96231b3b80d8

15 files changed:
include/llvm/Object/Binary.h
include/llvm/Object/WindowsResource.h [new file with mode: 0644]
include/llvm/Support/BinaryStreamReader.h
include/llvm/Support/FileSystem.h
lib/Object/Binary.cpp
lib/Object/CMakeLists.txt
lib/Object/WindowsResource.cpp [new file with mode: 0644]
test/tools/llvm-cvtres/Inputs/cursor_small.bmp [new file with mode: 0644]
test/tools/llvm-cvtres/Inputs/okay_small.bmp [new file with mode: 0644]
test/tools/llvm-cvtres/Inputs/test_resource.rc [new file with mode: 0644]
test/tools/llvm-cvtres/Inputs/test_resource.res [new file with mode: 0644]
test/tools/llvm-cvtres/resource.test [new file with mode: 0644]
tools/llvm-cvtres/CMakeLists.txt
tools/llvm-cvtres/llvm-cvtres.cpp
tools/llvm-cvtres/llvm-cvtres.h

index f42048e48ee3a50167ede42925802c9b867b5049..cf5d93ee9ed7e3c4d20aa809704c6799db7eeffd 100644 (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;
diff --git a/include/llvm/Object/WindowsResource.h b/include/llvm/Object/WindowsResource.h
new file mode 100644 (file)
index 0000000..f94ad09
--- /dev/null
@@ -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
index 75e96a999a11f47c62f29dc66a290b5993360a4f..56375f41d2c0d611d5a2987b6965faf7b0224095 100644 (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>
index e3c5de7fbe642c2ffb29613aff99e0f56827718d..7caefb5359b87c8e4e681142f8795e754005b30e 100644 (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
   };
 
index 2b44c4a82d2ca81f118ee49c968add8c0318f5b8..116af3c917be5d072fd5a337320c8029dd222d44 100644 (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);
   }
index 08365e71c2f6a1fc3fca7c406bb3bfe8b95da9f4..fa0335890285843a0eeac92382b07f1c433d8e8e 100644 (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
diff --git a/lib/Object/WindowsResource.cpp b/lib/Object/WindowsResource.cpp
new file mode 100644 (file)
index 0000000..29a02b8
--- /dev/null
@@ -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
diff --git a/test/tools/llvm-cvtres/Inputs/cursor_small.bmp b/test/tools/llvm-cvtres/Inputs/cursor_small.bmp
new file mode 100644 (file)
index 0000000..ce51326
Binary files /dev/null and b/test/tools/llvm-cvtres/Inputs/cursor_small.bmp differ
diff --git a/test/tools/llvm-cvtres/Inputs/okay_small.bmp b/test/tools/llvm-cvtres/Inputs/okay_small.bmp
new file mode 100644 (file)
index 0000000..e4005bf
Binary files /dev/null and b/test/tools/llvm-cvtres/Inputs/okay_small.bmp differ
diff --git a/test/tools/llvm-cvtres/Inputs/test_resource.rc b/test/tools/llvm-cvtres/Inputs/test_resource.rc
new file mode 100644 (file)
index 0000000..fd61652
--- /dev/null
@@ -0,0 +1,44 @@
+#include "windows.h"\r
+\r
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US\r
+\r
+myaccelerators ACCELERATORS\r
+{\r
+       "^C", 999, VIRTKEY, ALT\r
+       "D", 1100, VIRTKEY, CONTROL, SHIFT\r
+       "^R", 444, ASCII, NOINVERT\r
+}\r
+\r
+cursor BITMAP "cursor_small.bmp"\r
+okay BITMAP "okay_small.bmp"\r
+\r
+14432 MENU\r
+LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED\r
+{\r
+       MENUITEM "yu", 100\r
+       MENUITEM "shala", 101\r
+       MENUITEM "kaoya", 102\r
+}\r
+\r
+testdialog DIALOG 10, 10, 200, 300\r
+STYLE WS_POPUP | WS_BORDER\r
+CAPTION "Test"\r
+{\r
+       CTEXT "Continue:", 1, 10, 10, 230, 14\r
+       PUSHBUTTON "&OK", 2, 66, 134, 161, 13\r
+}\r
+\r
+12 ACCELERATORS\r
+{\r
+       "X", 164, VIRTKEY, ALT\r
+       "H", 5678, VIRTKEY, CONTROL, SHIFT\r
+       "^R", 444, ASCII, NOINVERT\r
+}\r
+\r
+"eat" MENU\r
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS\r
+{\r
+       MENUITEM "fish", 100\r
+       MENUITEM "salad", 101\r
+       MENUITEM "duck", 102\r
+}\r
diff --git a/test/tools/llvm-cvtres/Inputs/test_resource.res b/test/tools/llvm-cvtres/Inputs/test_resource.res
new file mode 100644 (file)
index 0000000..c577ecc
Binary files /dev/null and b/test/tools/llvm-cvtres/Inputs/test_resource.res differ
diff --git a/test/tools/llvm-cvtres/resource.test b/test/tools/llvm-cvtres/resource.test
new file mode 100644 (file)
index 0000000..1697034
--- /dev/null
@@ -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 52edccac816550a08b6ac65eaed59a57b013a597..e912030e205ee060d7a250547ffa3f56c05dcdce 100644 (file)
@@ -1,4 +1,5 @@
 set(LLVM_LINK_COMPONENTS
+  Object
   Option
   Support
   )
index f03e0b772e1b74c71f3a8ba7c86487449fdad0d0..96f7437ab5f6cdb81f9f145b69e8d98ac39277ea 100644 (file)
 
 #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;
 }
index eeaba196903658c961daf26e0de89e331eb4f56b..2e45b66461f02ebc5f582bfa64797ca1c5ec2f34 100644 (file)
 #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