Skip to content

Commit

Permalink
[tools] Introduce llvm-lipo
Browse files Browse the repository at this point in the history
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
  • Loading branch information
alexander-shaposhnikov committed May 28, 2019
1 parent d103bc3 commit 88aed8d
Show file tree
Hide file tree
Showing 8 changed files with 320 additions and 0 deletions.
1 change: 1 addition & 0 deletions llvm/test/CMakeLists.txt
Expand Up @@ -72,6 +72,7 @@ set(LLVM_TEST_DEPENDS
llvm-jitlink
llvm-lib
llvm-link
llvm-lipo
llvm-lto2
llvm-mc
llvm-mca
Expand Down
13 changes: 13 additions & 0 deletions llvm/test/tools/llvm-lipo/help-message.test
@@ -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 }}
31 changes: 31 additions & 0 deletions llvm/test/tools/llvm-lipo/verify-arch-macho-binary.test
@@ -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
...
44 changes: 44 additions & 0 deletions llvm/test/tools/llvm-lipo/verify-arch-universal-binary.test
@@ -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
...
16 changes: 16 additions & 0 deletions llvm/tools/llvm-lipo/CMakeLists.txt
@@ -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
)
20 changes: 20 additions & 0 deletions llvm/tools/llvm-lipo/LLVMBuild.txt
@@ -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
10 changes: 10 additions & 0 deletions llvm/tools/llvm-lipo/LipoOpts.td
@@ -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">;
185 changes: 185 additions & 0 deletions llvm/tools/llvm-lipo/llvm-lipo.cpp
@@ -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;
}

0 comments on commit 88aed8d

Please sign in to comment.