diff --git a/llvm/test/tools/llvm-lipo/extract.test b/llvm/test/tools/llvm-lipo/extract.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/extract.test @@ -0,0 +1,14 @@ +# RUN: yaml2obj %p/Inputs/i386-x86_64-universal.yaml > %t-universal.o + +# RUN: not llvm-lipo %t-universal.o -extract arm64_32 -output /dev/null 2>&1 | FileCheck --check-prefix=ARCH_NOT_IN_FILE %s +# ARCH_NOT_IN_FILE: does not contain the specified architecture arm64_32 + +# RUN: yaml2obj %p/Inputs/i386-slice.yaml > %t-i386.o +# RUN: not llvm-lipo %t-i386.o -extract arm64_32 -output /dev/null 2>&1 | FileCheck --check-prefix=INPUT_NOT_A_FAT_FILE %s +# INPUT_NOT_A_FAT_FILE: must be a fat file when the -extract option is specified + +# RUN: llvm-lipo -create %t-i386.o -output %t-i386-universal.o +# RUN: llvm-lipo -extract i386 %t-universal.o -output %t-i386-universal-extracted.o +# RUN: cmp %t-i386-universal.o %t-i386-universal-extracted.o +# RUN: llvm-lipo -thin i386 %t-i386-universal-extracted.o -output %t-i386-thinned.o +# RUN: cmp %t-i386.o %t-i386-thinned.o 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 @@ -39,6 +39,11 @@ HelpText<"Create a thin output file of specified arch_type from the " "fat input file. Requires -output option">; +def extract : Option<["-", "--"], "extract", KIND_SEPARATE>, + Group, + HelpText<"Create a universal output file containing only the specified " + "arch_type from the fat input file. Requires -output option">; + def create : Option<["-", "--"], "create", KIND_FLAG>, Group, HelpText<"Create a universal binary output file from the input " 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 @@ -83,6 +83,7 @@ PrintInfo, VerifyArch, ThinArch, + ExtractArch, CreateUniversal, ReplaceArch, }; @@ -97,7 +98,7 @@ SmallVector VerifyArchList; SmallVector ReplacementFiles; StringMap SegmentAlignments; - std::string ThinArchType; + std::string ArchType; std::string OutputFile; LipoAction ActionToPerform; }; @@ -391,14 +392,23 @@ case LIPO_thin: if (C.InputFiles.size() > 1) reportError("thin expects a single input file"); - C.ThinArchType = ActionArgs[0]->getValue(); - validateArchitectureName(C.ThinArchType); if (C.OutputFile.empty()) reportError("thin expects a single output file"); - + C.ArchType = ActionArgs[0]->getValue(); + validateArchitectureName(C.ArchType); C.ActionToPerform = LipoAction::ThinArch; return C; + case LIPO_extract: + if (C.InputFiles.size() > 1) + reportError("extract expects a single input file"); + if (C.OutputFile.empty()) + reportError("extract expects a single output file"); + C.ArchType = ActionArgs[0]->getValue(); + validateArchitectureName(C.ArchType); + C.ActionToPerform = LipoAction::ExtractArch; + return C; + case LIPO_create: if (C.OutputFile.empty()) reportError("create expects a single output file to be specified"); @@ -544,9 +554,9 @@ } LLVM_ATTRIBUTE_NORETURN -static void extractSlice(ArrayRef> InputBinaries, - StringRef ThinArchType, StringRef OutputFileName) { - assert(!ThinArchType.empty() && "The architecture type should be non-empty"); +static void thinSlice(ArrayRef> InputBinaries, + StringRef ArchType, StringRef OutputFileName) { + assert(!ArchType.empty() && "The architecture type should be non-empty"); assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); assert(!OutputFileName.empty() && "Thin expects a single output file"); @@ -559,11 +569,11 @@ auto *UO = cast(InputBinaries.front().getBinary()); Expected> Obj = - UO->getMachOObjectForArch(ThinArchType); - Expected> Ar = UO->getArchiveForArch(ThinArchType); + UO->getMachOObjectForArch(ArchType); + Expected> Ar = UO->getArchiveForArch(ArchType); if (!Obj && !Ar) reportError("fat input file " + UO->getFileName() + - " does not contain the specified architecture " + ThinArchType + + " does not contain the specified architecture " + ArchType + " to thin it to"); Binary *B = Obj ? static_cast(Obj->get()) : static_cast(Ar->get()); @@ -739,6 +749,37 @@ exit(EXIT_SUCCESS); } +LLVM_ATTRIBUTE_NORETURN +static void extractSlice(ArrayRef> InputBinaries, + const StringMap &Alignments, + StringRef ArchType, StringRef OutputFileName) { + assert(!ArchType.empty() && + "The architecture type should be non-empty"); + assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); + assert(!OutputFileName.empty() && "Thin expects a single output file"); + + if (InputBinaries.front().getBinary()->isMachO()) { + reportError("input file " + + InputBinaries.front().getBinary()->getFileName() + + " must be a fat file when the -extract option is specified"); + exit(EXIT_FAILURE); + } + + SmallVector, 2> ExtractedObjects; + SmallVector Slices = + buildSlices(InputBinaries, Alignments, ExtractedObjects); + erase_if(Slices, [ArchType](const Slice &S) { + return ArchType != S.getArchString(); + }); + + if (Slices.empty()) + reportError( + "fat input file " + InputBinaries.front().getBinary()->getFileName() + + " does not contain the specified architecture " + ArchType); + createUniversalBinary(Slices, OutputFileName); + exit(EXIT_SUCCESS); +} + static StringMap buildReplacementSlices(ArrayRef> ReplacementBinaries, const StringMap &Alignments) { @@ -823,7 +864,10 @@ printInfo(InputBinaries); break; case LipoAction::ThinArch: - extractSlice(InputBinaries, C.ThinArchType, C.OutputFile); + thinSlice(InputBinaries, C.ArchType, C.OutputFile); + break; + case LipoAction::ExtractArch: + extractSlice(InputBinaries, C.SegmentAlignments, C.ArchType, C.OutputFile); break; case LipoAction::CreateUniversal: createUniversalBinary(InputBinaries, C.SegmentAlignments, C.OutputFile);