diff --git a/llvm/test/tools/llvm-lipo/archs-macho-binary-unknown.test b/llvm/test/tools/llvm-lipo/archs-macho-binary-unknown.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/archs-macho-binary-unknown.test @@ -0,0 +1,15 @@ +# RUN: yaml2obj %s > %t + +# Tests that the output for an unknown architecture is the same as to check cctools lipo +# RUN: llvm-lipo %t -archs | FileCheck %s +# CHECK: unknown(151,3) +--- !mach-o +FileHeader: + magic: 0xFEEDFACE + cputype: 0x00000097 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 0 + sizeofcmds: 0 + flags: 0x00002000 +... diff --git a/llvm/test/tools/llvm-lipo/archs-macho-binary.test b/llvm/test/tools/llvm-lipo/archs-macho-binary.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/archs-macho-binary.test @@ -0,0 +1,22 @@ +# RUN: yaml2obj %s > %t + +# RUN: llvm-lipo %t -archs | FileCheck --check-prefix=ARCHS %s +# RUN: llvm-lipo %t --archs | FileCheck --check-prefix=ARCHS %s +# ARCHS: i386 + +# RUN: not llvm-lipo %t %t -archs 2>&1 | FileCheck --check-prefix=MULTIPLE_INPUT_OBJ %s +# MULTIPLE_INPUT_OBJ: archs expects a single input file + +# RUN: not llvm-lipo %t -archs -verify_arch i386 2>&1 | FileCheck --check-prefix=MULTIPLE_FLAGS %s +# MULTIPLE_FLAGS: only one of the following actions can be specified: -archs -verify_arch + +--- !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/archs-universal-binary-unknown.test b/llvm/test/tools/llvm-lipo/archs-universal-binary-unknown.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/archs-universal-binary-unknown.test @@ -0,0 +1,42 @@ +# RUN: yaml2obj %s > %t + +# Tests that the output for an unknown architecture is the same as to check cctools lipo +# RUN: llvm-lipo %t -archs 2>&1 | FileCheck %s +# CHECK: i386 unknown(16777367,3) + +--- !fat-mach-o +FatHeader: + magic: 0xCAFEBABE + nfat_arch: 2 +FatArchs: + - cputype: 0x00000007 + cpusubtype: 0x00000003 + offset: 0x0000000000001000 + size: 28 + align: 12 + - cputype: 0x01000097 + 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: 0x01000097 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 0 + sizeofcmds: 0 + flags: 0x00002000 + reserved: 0x00000000 +... diff --git a/llvm/test/tools/llvm-lipo/archs-universal-binary.test b/llvm/test/tools/llvm-lipo/archs-universal-binary.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/archs-universal-binary.test @@ -0,0 +1,41 @@ +# RUN: yaml2obj %s > %t + +# RUN: llvm-lipo %t -archs 2>&1 | FileCheck %s +# CHECK: i386 x86_64 + +--- !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/LipoOpts.td b/llvm/tools/llvm-lipo/LipoOpts.td --- a/llvm/tools/llvm-lipo/LipoOpts.td +++ b/llvm/tools/llvm-lipo/LipoOpts.td @@ -6,5 +6,14 @@ 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">; +def action_group : OptionGroup<"action group">; + +def verify_arch + : Option<["-", "--"], "verify_arch", KIND_REMAINING_ARGS>, + Group, + HelpText< + "Verify that the specified arch_types are present in the input file">; + +def archs : Option<["-", "--"], "archs", KIND_FLAG>, + Group, + HelpText<"Display the arch_types present in the input file">; diff --git a/llvm/tools/llvm-lipo/llvm-lipo.cpp b/llvm/tools/llvm-lipo/llvm-lipo.cpp --- a/llvm/tools/llvm-lipo/llvm-lipo.cpp +++ b/llvm/tools/llvm-lipo/llvm-lipo.cpp @@ -53,6 +53,7 @@ #undef OPTION }; +const char *const *LIPO_nullptr = nullptr; #define PREFIX(NAME, VALUE) const char *const LIPO_##NAME[] = VALUE; #include "LipoOpts.inc" #undef PREFIX @@ -73,9 +74,15 @@ LipoOptTable() : OptTable(LipoInfoTable) {} }; +enum class LipoAction { + PrintArchs, + VerifyArch, +}; + struct Config { SmallVector InputFiles; SmallVector VerifyArchList; + LipoAction ActionToPerform; }; } // end namespace @@ -113,6 +120,18 @@ if (C.InputFiles.empty()) reportError("at least one input file should be specified"); + SmallVector ActionArgs(InputArgs.filtered(LIPO_action_group)); + if (ActionArgs.empty()) + reportError("at least one action should be specified"); + if (ActionArgs.size() > 1) { + std::string Buf; + raw_string_ostream OS(Buf); + OS << "only one of the following actions can be specified:"; + for (auto Arg : ActionArgs) + OS << " " << Arg->getSpelling(); + reportError(OS.str()); + } + if (InputArgs.hasArg(LIPO_verify_arch)) { for (auto A : InputArgs.getAllArgValues(LIPO_verify_arch)) C.VerifyArchList.push_back(A); @@ -121,8 +140,18 @@ "verify_arch requires at least one architecture to be specified"); if (C.InputFiles.size() > 1) reportError("verify_arch expects a single input file"); + C.ActionToPerform = LipoAction::VerifyArch; + return C; + } + + if (InputArgs.hasArg(LIPO_archs)) { + if (C.InputFiles.size() > 1) + reportError("archs expects a single input file"); + C.ActionToPerform = LipoAction::PrintArchs; + return C; } - return C; + + llvm_unreachable("llvm-lipo action unspecified"); } static SmallVector, 1> @@ -144,8 +173,6 @@ 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"); @@ -174,12 +201,47 @@ exit(EXIT_SUCCESS); } +static void printArchFromCPUType(uint32_t CPUType, uint32_t CPUSubType) { + // Prints trailing space and unknown in this format for compatibility with + // cctools lipo + Triple::ArchType Arch = MachOObjectFile::getArch(CPUType); + if (Arch == Triple::ArchType::UnknownArch) + outs() << "unknown(" << CPUType << "," + << (CPUSubType & ~MachO::CPU_SUBTYPE_MASK) << ") "; + else + outs() << Triple::getArchTypeName(Arch).str() << " "; +} + +LLVM_ATTRIBUTE_NORETURN +static void printArchs(ArrayRef> InputBinaries) { + assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); + + if (auto UO = + dyn_cast(InputBinaries.front().getBinary())) + for (const MachOUniversalBinary::ObjectForArch &Obj : UO->objects()) + printArchFromCPUType(Obj.getCPUType(), Obj.getCPUSubType()); + else if (auto O = + dyn_cast(InputBinaries.front().getBinary())) + printArchFromCPUType(O->getHeader().cputype, O->getHeader().cpusubtype); + else + llvm_unreachable("Unexpected binary format"); + + outs() << "\n"; + 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()) + switch (C.ActionToPerform) { + case LipoAction::VerifyArch: verifyArch(InputBinaries, C.VerifyArchList); + break; + case LipoAction::PrintArchs: + printArchs(InputBinaries); + break; + } return EXIT_SUCCESS; }