[tools] Introduce llvm-lipo
authorAlexander Shaposhnikov <shal1t712@gmail.com>
Tue, 28 May 2019 23:22:12 +0000 (23:22 +0000)
committerAlexander Shaposhnikov <shal1t712@gmail.com>
Tue, 28 May 2019 23:22:12 +0000 (23:22 +0000)
This diff starts the implementation of llvm-lipo
which is supposed to be a drop-in replacement for the well-known tool lipo.

Test plan: make check-all

Differential revision: https://reviews.llvm.org/D61927

llvm-svn: 361896

llvm/test/CMakeLists.txt
llvm/test/tools/llvm-lipo/help-message.test [new file with mode: 0644]
llvm/test/tools/llvm-lipo/verify-arch-macho-binary.test [new file with mode: 0644]
llvm/test/tools/llvm-lipo/verify-arch-universal-binary.test [new file with mode: 0644]
llvm/tools/llvm-lipo/CMakeLists.txt [new file with mode: 0644]
llvm/tools/llvm-lipo/LLVMBuild.txt [new file with mode: 0644]
llvm/tools/llvm-lipo/LipoOpts.td [new file with mode: 0644]
llvm/tools/llvm-lipo/llvm-lipo.cpp [new file with mode: 0644]

index f596602..03d154e 100644 (file)
@@ -72,6 +72,7 @@ set(LLVM_TEST_DEPENDS
           llvm-jitlink
           llvm-lib
           llvm-link
+          llvm-lipo
           llvm-lto2
           llvm-mc
           llvm-mca
diff --git a/llvm/test/tools/llvm-lipo/help-message.test b/llvm/test/tools/llvm-lipo/help-message.test
new file mode 100644 (file)
index 0000000..8a9d48b
--- /dev/null
@@ -0,0 +1,13 @@
+# RUN: llvm-lipo -h | FileCheck --check-prefix=LIPO-USAGE %s
+# RUN: llvm-lipo --help | FileCheck --check-prefix=LIPO-USAGE %s
+
+# RUN: llvm-lipo -version | FileCheck --check-prefix=LIPO-VERSION %s
+# RUN: llvm-lipo --version | FileCheck --check-prefix=LIPO-VERSION %s
+
+# RUN: not llvm-lipo 2>&1 | FileCheck --check-prefix=LIPO-USAGE %s
+# RUN: not llvm-lipo -abcabc 2>&1 | FileCheck --check-prefix=LIPO-UNKNOWN-ARG %s
+# RUN: not llvm-lipo --abcabc 2>&1 | FileCheck --check-prefix=LIPO-UNKNOWN-ARG %s
+
+# LIPO-USAGE:    USAGE: llvm-lipo
+# LIPO-UNKNOWN-ARG:    unknown argument '{{-+}}abcabc'
+# LIPO-VERSION: {{ version }}
diff --git a/llvm/test/tools/llvm-lipo/verify-arch-macho-binary.test b/llvm/test/tools/llvm-lipo/verify-arch-macho-binary.test
new file mode 100644 (file)
index 0000000..1c0a7b8
--- /dev/null
@@ -0,0 +1,31 @@
+# RUN: yaml2obj %s > %t
+
+# RUN: llvm-lipo %t -verify_arch i386
+# RUN: llvm-lipo %t --verify_arch i386
+
+# RUN: not llvm-lipo %t -verify_arch aarch64
+# RUN: not llvm-lipo %t -verify_arch aarch64 i386 
+
+# INVALID_ARCH: Invalid architecture: aarch101
+# RUN: not llvm-lipo %t -verify_arch aarch101 2>&1 | FileCheck --check-prefix=INVALID_ARCH %s
+
+# INVALID_OBJ: The file was not recognized as a valid object file
+# RUN: touch %t.empty
+# RUN: not llvm-lipo %t.empty -verify_arch aarch101 2>&1 | FileCheck --check-prefix=INVALID_OBJ %s
+
+# NO_INPUT_OBJ: at least one input file should be specified
+# RUN: not llvm-lipo -verify_arch i386 2>&1 | FileCheck --check-prefix=NO_INPUT_OBJ %s
+
+# MULTIPLE_INPUT_OBJ: verify_arch expects a single input file
+# RUN: not llvm-lipo %t %t -verify_arch i386 2>&1 | FileCheck --check-prefix=MULTIPLE_INPUT_OBJ %s
+
+--- !mach-o
+FileHeader:      
+  magic:           0xFEEDFACE
+  cputype:         0x00000007
+  cpusubtype:      0x00000003
+  filetype:        0x00000001
+  ncmds:           0
+  sizeofcmds:      0
+  flags:           0x00002000
+...
diff --git a/llvm/test/tools/llvm-lipo/verify-arch-universal-binary.test b/llvm/test/tools/llvm-lipo/verify-arch-universal-binary.test
new file mode 100644 (file)
index 0000000..128813b
--- /dev/null
@@ -0,0 +1,44 @@
+# RUN: yaml2obj %s > %t
+
+# RUN: llvm-lipo %t -verify_arch i386
+# RUN: llvm-lipo %t -verify_arch i386 x86_64
+
+# RUN: not llvm-lipo %t -verify_arch aarch64
+# RUN: not llvm-lipo %t -verify_arch aarch64 i386 
+
+--- !fat-mach-o
+FatHeader:       
+  magic:           0xCAFEBABE
+  nfat_arch:       2
+FatArchs:        
+  - cputype:         0x00000007
+    cpusubtype:      0x00000003
+    offset:          0x0000000000001000
+    size:            28
+    align:           12
+  - cputype:         0x01000007
+    cpusubtype:      0x00000003
+    offset:          0x0000000000002000
+    size:            32
+    align:           12
+Slices:          
+  - !mach-o
+    FileHeader:      
+      magic:           0xFEEDFACE
+      cputype:         0x00000007
+      cpusubtype:      0x00000003
+      filetype:        0x00000001
+      ncmds:           0
+      sizeofcmds:      0
+      flags:           0x00002000
+  - !mach-o
+    FileHeader:      
+      magic:           0xFEEDFACF
+      cputype:         0x01000007
+      cpusubtype:      0x00000003
+      filetype:        0x00000001
+      ncmds:           0
+      sizeofcmds:      0
+      flags:           0x00002000
+      reserved:        0x00000000
+...
diff --git a/llvm/tools/llvm-lipo/CMakeLists.txt b/llvm/tools/llvm-lipo/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b51f792
--- /dev/null
@@ -0,0 +1,16 @@
+set(LLVM_LINK_COMPONENTS
+  ${LLVM_TARGETS_TO_BUILD}
+  Object
+  Option
+  Support
+)
+
+set(LLVM_TARGET_DEFINITIONS LipoOpts.td)
+tablegen(LLVM LipoOpts.inc -gen-opt-parser-defs)
+add_public_tablegen_target(LipoOptsTableGen)
+
+add_llvm_tool(llvm-lipo
+  llvm-lipo.cpp
+  DEPENDS
+  LipoOptsTableGen
+)
diff --git a/llvm/tools/llvm-lipo/LLVMBuild.txt b/llvm/tools/llvm-lipo/LLVMBuild.txt
new file mode 100644 (file)
index 0000000..fa807ad
--- /dev/null
@@ -0,0 +1,20 @@
+;===- ./tools/llvm-lipo/LLVMBuild.txt --------------------------*- Conf -*--===;
+;
+; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+; See https://llvm.org/LICENSE.txt for license information.
+; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+;
+;===------------------------------------------------------------------------===;
+;
+; This is an LLVMBuild description file for the components in this subdirectory.
+;
+; For more information on the LLVMBuild system, please see:
+;
+;   http://llvm.org/docs/LLVMBuild.html
+;
+;===------------------------------------------------------------------------===;
+[component_0]
+type = Tool
+name = llvm-lipo
+parent = Tools
+required_libraries = Object Option Support
diff --git a/llvm/tools/llvm-lipo/LipoOpts.td b/llvm/tools/llvm-lipo/LipoOpts.td
new file mode 100644 (file)
index 0000000..0ff667a
--- /dev/null
@@ -0,0 +1,10 @@
+include "llvm/Option/OptParser.td"
+
+def help : Flag<["-", "--"], "help">;
+def h : Flag<["-"], "h">, Alias<help>;
+
+def version : Flag<["-", "--"], "version">,
+              HelpText<"Print the version and exit.">;
+
+def verify_arch : Option<["-", "--"], "verify_arch", KIND_REMAINING_ARGS>,
+                  HelpText<"Verify that the specified arch_types are present in the input file">;
diff --git a/llvm/tools/llvm-lipo/llvm-lipo.cpp b/llvm/tools/llvm-lipo/llvm-lipo.cpp
new file mode 100644 (file)
index 0000000..a38d3ae
--- /dev/null
@@ -0,0 +1,185 @@
+//===-- llvm-lipo.cpp - a tool for manipulating universal binaries --------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// A utility for creating / splitting / inspecting universal binaries.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/Triple.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/WithColor.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+static const StringRef ToolName = "llvm-lipo";
+
+LLVM_ATTRIBUTE_NORETURN static void reportError(Twine Message) {
+  WithColor::error(errs(), ToolName) << Message << "\n";
+  errs().flush();
+  exit(EXIT_FAILURE);
+}
+
+LLVM_ATTRIBUTE_NORETURN static void reportError(StringRef File, Error E) {
+  assert(E);
+  std::string Buf;
+  raw_string_ostream OS(Buf);
+  logAllUnhandledErrors(std::move(E), OS);
+  OS.flush();
+  WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf;
+  exit(EXIT_FAILURE);
+}
+
+namespace {
+enum LipoID {
+  LIPO_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
+               HELPTEXT, METAVAR, VALUES)                                      \
+  LIPO_##ID,
+#include "LipoOpts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const LIPO_##NAME[] = VALUE;
+#include "LipoOpts.inc"
+#undef PREFIX
+
+static const opt::OptTable::Info LipoInfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
+               HELPTEXT, METAVAR, VALUES)                                      \
+  {LIPO_##PREFIX, NAME,      HELPTEXT,                                         \
+   METAVAR,       LIPO_##ID, opt::Option::KIND##Class,                         \
+   PARAM,         FLAGS,     LIPO_##GROUP,                                     \
+   LIPO_##ALIAS,  ALIASARGS, VALUES},
+#include "LipoOpts.inc"
+#undef OPTION
+};
+
+class LipoOptTable : public opt::OptTable {
+public:
+  LipoOptTable() : OptTable(LipoInfoTable) {}
+};
+
+struct Config {
+  SmallVector<std::string, 1> InputFiles;
+  SmallVector<std::string, 1> VerifyArchList;
+};
+
+} // end namespace
+
+static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
+  Config C;
+  LipoOptTable T;
+  unsigned MissingArgumentIndex, MissingArgumentCount;
+  llvm::opt::InputArgList InputArgs =
+      T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
+
+  if (InputArgs.size() == 0) {
+    // PrintHelp does not accept Twine.
+    T.PrintHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
+    exit(EXIT_FAILURE);
+  }
+
+  if (InputArgs.hasArg(LIPO_help)) {
+    // PrintHelp does not accept Twine.
+    T.PrintHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
+    exit(EXIT_SUCCESS);
+  }
+
+  if (InputArgs.hasArg(LIPO_version)) {
+    outs() << ToolName + "\n";
+    cl::PrintVersionMessage();
+    exit(EXIT_SUCCESS);
+  }
+
+  for (auto Arg : InputArgs.filtered(LIPO_UNKNOWN))
+    reportError("unknown argument '" + Arg->getAsString(InputArgs) + "'");
+
+  for (auto Arg : InputArgs.filtered(LIPO_INPUT))
+    C.InputFiles.push_back(Arg->getValue());
+  if (C.InputFiles.empty())
+    reportError("at least one input file should be specified");
+
+  if (InputArgs.hasArg(LIPO_verify_arch)) {
+    for (auto A : InputArgs.getAllArgValues(LIPO_verify_arch))
+      C.VerifyArchList.push_back(A);
+    if (C.VerifyArchList.empty())
+      reportError(
+          "verify_arch requires at least one architecture to be specified");
+    if (C.InputFiles.size() > 1)
+      reportError("verify_arch expects a single input file");
+  }
+  return C;
+}
+
+static SmallVector<OwningBinary<Binary>, 1>
+readInputBinaries(ArrayRef<std::string> InputFiles) {
+  SmallVector<OwningBinary<Binary>, 1> InputBinaries;
+  for (StringRef InputFile : InputFiles) {
+    Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr =
+        createBinary(InputFile);
+    if (!BinaryOrErr)
+      reportError(InputFile, BinaryOrErr.takeError());
+    if (!isa<MachOObjectFile>(BinaryOrErr->getBinary()) &&
+        !isa<MachOUniversalBinary>(BinaryOrErr->getBinary()))
+      reportError("File " + InputFile + " has unsupported binary format");
+    InputBinaries.push_back(std::move(*BinaryOrErr));
+  }
+  return InputBinaries;
+}
+
+LLVM_ATTRIBUTE_NORETURN
+static void verifyArch(ArrayRef<OwningBinary<Binary>> InputBinaries,
+                       ArrayRef<std::string> VerifyArchList) {
+  assert(!InputBinaries.empty() &&
+         "The list of input binaries should be non-empty");
+  assert(!VerifyArchList.empty() &&
+         "The list of architectures should be non-empty");
+  assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
+
+  for (StringRef Arch : VerifyArchList)
+    if (Triple(Arch).getArch() == Triple::ArchType::UnknownArch)
+      reportError("Invalid architecture: " + Arch);
+
+  if (auto UO =
+          dyn_cast<MachOUniversalBinary>(InputBinaries.front().getBinary())) {
+    for (StringRef Arch : VerifyArchList) {
+      Expected<std::unique_ptr<MachOObjectFile>> Obj =
+          UO->getObjectForArch(Arch);
+      if (!Obj)
+        exit(EXIT_FAILURE);
+    }
+  } else if (auto O =
+                 dyn_cast<MachOObjectFile>(InputBinaries.front().getBinary())) {
+    const Triple::ArchType ObjectArch = O->getArch();
+    for (StringRef Arch : VerifyArchList)
+      if (ObjectArch != Triple(Arch).getArch())
+        exit(EXIT_FAILURE);
+  } else {
+    llvm_unreachable("Unexpected binary format");
+  }
+  exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv) {
+  InitLLVM X(argc, argv);
+  Config C = parseLipoOptions(makeArrayRef(argv + 1, argc));
+  SmallVector<OwningBinary<Binary>, 1> InputBinaries =
+      readInputBinaries(C.InputFiles);
+  if (!C.VerifyArchList.empty())
+    verifyArch(InputBinaries, C.VerifyArchList);
+  return EXIT_SUCCESS;
+}