Index: test/tools/llvm-lipo/help-message.test =================================================================== --- test/tools/llvm-lipo/help-message.test +++ 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 }} Index: test/tools/llvm-lipo/verify-arch-macho-binary.test =================================================================== --- test/tools/llvm-lipo/verify-arch-macho-binary.test +++ 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 +... Index: test/tools/llvm-lipo/verify-arch-universal-binary.test =================================================================== --- test/tools/llvm-lipo/verify-arch-universal-binary.test +++ 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 +... Index: tools/llvm-lipo/CMakeLists.txt =================================================================== --- tools/llvm-lipo/CMakeLists.txt +++ 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 +) Index: tools/llvm-lipo/LLVMBuild.txt =================================================================== --- tools/llvm-lipo/LLVMBuild.txt +++ 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 Index: tools/llvm-lipo/LipoOpts.td =================================================================== --- tools/llvm-lipo/LipoOpts.td +++ tools/llvm-lipo/LipoOpts.td @@ -0,0 +1,10 @@ +include "llvm/Option/OptParser.td" + +def help : Flag<["-", "--"], "help">; +def h : Flag<["-"], "h">, Alias; + +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">; Index: tools/llvm-lipo/llvm-lipo.cpp =================================================================== --- tools/llvm-lipo/llvm-lipo.cpp +++ 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 InputFiles; + SmallVector VerifyArchList; +}; + +} // end namespace + +static Config parseLipoOptions(ArrayRef 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, 1> +readInputBinaries(ArrayRef InputFiles) { + SmallVector, 1> InputBinaries; + for (StringRef InputFile : InputFiles) { + Expected> BinaryOrErr = + createBinary(InputFile); + if (!BinaryOrErr) + reportError(InputFile, BinaryOrErr.takeError()); + if (!isa(BinaryOrErr->getBinary()) && + !isa(BinaryOrErr->getBinary())) + reportError("File " + InputFile + " has unsupported binary format"); + InputBinaries.push_back(std::move(*BinaryOrErr)); + } + return InputBinaries; +} + +LLVM_ATTRIBUTE_NORETURN +static void verifyArch(ArrayRef> InputBinaries, + ArrayRef 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(InputBinaries.front().getBinary())) { + for (StringRef Arch : VerifyArchList) { + Expected> Obj = + UO->getObjectForArch(Arch); + if (!Obj) + exit(EXIT_FAILURE); + } + } else if (auto O = + dyn_cast(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, 1> InputBinaries = + readInputBinaries(C.InputFiles); + if (!C.VerifyArchList.empty()) + verifyArch(InputBinaries, C.VerifyArchList); + return EXIT_SUCCESS; +}