diff --git a/llvm/test/tools/llvm-lipo/Inputs/i386-slice.yaml b/llvm/test/tools/llvm-lipo/Inputs/i386-slice.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/Inputs/i386-slice.yaml @@ -0,0 +1,88 @@ +--- !mach-o +FileHeader: + magic: 0xFEEDFACE + cputype: 0x00000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 4 + sizeofcmds: 312 + flags: 0x00002000 +LoadCommands: + - cmd: LC_SEGMENT + cmdsize: 192 + segname: '' + vmaddr: 0 + vmsize: 72 + fileoff: 340 + filesize: 72 + maxprot: 7 + initprot: 7 + nsects: 2 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000000 + size: 18 + offset: 0x00000154 + align: 4 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - sectname: __eh_frame + segname: __TEXT + addr: 0x0000000000000014 + size: 52 + offset: 0x00000168 + align: 2 + reloff: 0x00000000 + nreloc: 0 + flags: 0x6800000B + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - cmd: LC_VERSION_MIN_MACOSX + cmdsize: 16 + version: 656384 + sdk: 0 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 412 + nsyms: 1 + stroff: 424 + strsize: 8 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 1 + iundefsym: 1 + nundefsym: 0 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 0 + nindirectsyms: 0 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 +LinkEditData: + NameList: + - n_strx: 1 + n_type: 0x0F + n_sect: 1 + n_desc: 0 + n_value: 0 + StringTable: + - '' + - _main + - '' +... diff --git a/llvm/test/tools/llvm-lipo/Inputs/i386-x86_64-universal.yaml b/llvm/test/tools/llvm-lipo/Inputs/i386-x86_64-universal.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/Inputs/i386-x86_64-universal.yaml @@ -0,0 +1,192 @@ +--- !fat-mach-o +FatHeader: + magic: 0xCAFEBABE + nfat_arch: 2 +FatArchs: + - cputype: 0x00000007 + cpusubtype: 0x00000003 + offset: 0x0000000000001000 + size: 432 + align: 12 + - cputype: 0x01000007 + cpusubtype: 0x00000003 + offset: 0x0000000000002000 + size: 488 + align: 12 +Slices: + - !mach-o + FileHeader: + magic: 0xFEEDFACE + cputype: 0x00000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 4 + sizeofcmds: 312 + flags: 0x00002000 + LoadCommands: + - cmd: LC_SEGMENT + cmdsize: 192 + segname: '' + vmaddr: 0 + vmsize: 72 + fileoff: 340 + filesize: 72 + maxprot: 7 + initprot: 7 + nsects: 2 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000000 + size: 18 + offset: 0x00000154 + align: 4 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - sectname: __eh_frame + segname: __TEXT + addr: 0x0000000000000014 + size: 52 + offset: 0x00000168 + align: 2 + reloff: 0x00000000 + nreloc: 0 + flags: 0x6800000B + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - cmd: LC_VERSION_MIN_MACOSX + cmdsize: 16 + version: 656384 + sdk: 0 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 412 + nsyms: 1 + stroff: 424 + strsize: 8 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 1 + iundefsym: 1 + nundefsym: 0 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 0 + nindirectsyms: 0 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 + LinkEditData: + NameList: + - n_strx: 1 + n_type: 0x0F + n_sect: 1 + n_desc: 0 + n_value: 0 + StringTable: + - '' + - _main + - '' + - !mach-o + FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 4 + sizeofcmds: 352 + flags: 0x00002000 + reserved: 0x00000000 + LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 232 + segname: '' + vmaddr: 0 + vmsize: 80 + fileoff: 384 + filesize: 80 + maxprot: 7 + initprot: 7 + nsects: 2 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000000 + size: 15 + offset: 0x00000180 + align: 4 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - sectname: __eh_frame + segname: __TEXT + addr: 0x0000000000000010 + size: 64 + offset: 0x00000190 + align: 3 + reloff: 0x00000000 + nreloc: 0 + flags: 0x6800000B + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - cmd: LC_VERSION_MIN_MACOSX + cmdsize: 16 + version: 656384 + sdk: 0 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 464 + nsyms: 1 + stroff: 480 + strsize: 8 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 1 + iundefsym: 1 + nundefsym: 0 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 0 + nindirectsyms: 0 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 + LinkEditData: + NameList: + - n_strx: 1 + n_type: 0x0F + n_sect: 1 + n_desc: 0 + n_value: 0 + StringTable: + - '' + - _main + - '' +... diff --git a/llvm/test/tools/llvm-lipo/help-error-messages.test b/llvm/test/tools/llvm-lipo/help-error-messages.test --- a/llvm/test/tools/llvm-lipo/help-error-messages.test +++ b/llvm/test/tools/llvm-lipo/help-error-messages.test @@ -9,7 +9,10 @@ # RUN: not llvm-lipo --abcabc 2>&1 | FileCheck --check-prefix=LIPO-UNKNOWN-ARG %s # 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 +# RUN: not llvm-lipo %t -archs -thin i386 2>&1 | FileCheck --check-prefix=MULTIPLE_FLAGS %s +# RUN: not llvm-lipo %t -thin i386 -verify_arch i386 2>&1 | FileCheck --check-prefix=MULTIPLE_FLAGS %s +# RUN: not llvm-lipo %t -archs -thin i386 -verify_arch i386 2>&1 | FileCheck --check-prefix=MULTIPLE_FLAGS %s +# MULTIPLE_FLAGS: only one of the following actions can be specified: # LIPO-USAGE: USAGE: llvm-lipo # LIPO-UNKNOWN-ARG: unknown argument '{{-+}}abcabc' diff --git a/llvm/test/tools/llvm-lipo/thin-executable-universal-binary.test b/llvm/test/tools/llvm-lipo/thin-executable-universal-binary.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/thin-executable-universal-binary.test @@ -0,0 +1,50 @@ +# Executable testing is not supported on Windows, since all files are considered executable +# UNSUPPORTED: windows +# RUN: yaml2obj %s > %t-universal.o + +# RUN: chmod -x %t-universal.o +# RUN: llvm-lipo %t-universal.o -thin i386 -output %t32.o +# RUN: test ! -x %t32.o + +# RUN: chmod +x %t-universal.o +# RUN: llvm-lipo %t-universal.o -thin i386 -output %t32-ex.o +# RUN: test -x %t32-ex.o + + + +--- !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/test/tools/llvm-lipo/thin-macho-binary.test b/llvm/test/tools/llvm-lipo/thin-macho-binary.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/thin-macho-binary.test @@ -0,0 +1,21 @@ +# RUN: yaml2obj %s > %t + +# RUN: not llvm-lipo %t -thin i386 2>&1 | FileCheck --check-prefix=NO_OUTPUT %s +# NO_OUTPUT: error: thin expects a single output file + +# RUN: not llvm-lipo %t %t -thin i386 2>&1 | FileCheck --check-prefix=MULTIPLE_INPUT_OBJ %s +# MULTIPLE_INPUT_OBJ: thin expects a single input file + +# RUN: not llvm-lipo %t -thin i386 -output %t.out 2>&1 | FileCheck --check-prefix=INPUT_FILE_THIN %s +# INPUT_FILE_THIN: must be a fat file when the -thin option is specified + +--- !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/thin-universal-binary.test b/llvm/test/tools/llvm-lipo/thin-universal-binary.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/thin-universal-binary.test @@ -0,0 +1,49 @@ +# RUN: yaml2obj %s > %t + +# RUN: not llvm-lipo %t -thin arc -output %t.out 2>&1 | FileCheck --check-prefix=ARCH_NOT_IN_FILE %s +# ARCH_NOT_IN_FILE: does not contain the specified architecture arc to thin it to + +# RUN: not llvm-lipo %t -thin aarch101 -output %t.out 2>&1 | FileCheck --check-prefix=INVALID_ARCH %s +# INVALID_ARCH: Invalid architecture: aarch101 + +# RUN: yaml2obj %p/Inputs/i386-x86_64-universal.yaml > %t-universal.o +# RUN: llvm-lipo %t-universal.o -thin i386 -output %t32.o +# RUN: yaml2obj %p/Inputs/i386-slice.yaml > %t-basic32.o +# RUN: cmp %t32.o %t-basic32.o + +--- !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 @@ -17,3 +17,12 @@ def archs : Option<["-", "--"], "archs", KIND_FLAG>, Group, HelpText<"Display the arch_types present in the input file">; + +def thin : Option<["-", "--"], "thin", KIND_SEPARATE>, + Group, + HelpText<"Create a thin output file of specified arch_type from the " + "fat input file. Requires -output option">; + +def output : Option<["-", "--"], "output", KIND_SEPARATE>, + HelpText<"Create output file with specified name">; +def o : JoinedOrSeparate<["-"], "o">, Alias; 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 @@ -19,6 +19,7 @@ #include "llvm/Option/ArgList.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" +#include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/WithColor.h" @@ -78,16 +79,24 @@ enum class LipoAction { PrintArchs, VerifyArch, + ThinArch, }; struct Config { SmallVector InputFiles; SmallVector VerifyArchList; + std::string ThinArchType; + std::string OutputFile; LipoAction ActionToPerform; }; } // end namespace +static void validateArchitectureName(StringRef ArchitectureName) { + if (Triple(ArchitectureName).getArch() == Triple::ArchType::UnknownArch) + reportError("Invalid architecture: " + ArchitectureName); +} + static Config parseLipoOptions(ArrayRef ArgsArr) { Config C; LipoOptTable T; @@ -95,6 +104,11 @@ llvm::opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); + if (MissingArgumentCount) + reportError("missing argument to " + + StringRef(InputArgs.getArgString(MissingArgumentIndex)) + + " option"); + if (InputArgs.size() == 0) { // PrintHelp does not accept Twine. T.PrintHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo"); @@ -121,6 +135,9 @@ if (C.InputFiles.empty()) reportError("at least one input file should be specified"); + if (InputArgs.hasArg(LIPO_output)) + C.OutputFile = InputArgs.getLastArgValue(LIPO_output); + SmallVector ActionArgs(InputArgs.filtered(LIPO_action_group)); if (ActionArgs.empty()) reportError("at least one action should be specified"); @@ -151,6 +168,17 @@ C.ActionToPerform = LipoAction::PrintArchs; return C; + 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.ActionToPerform = LipoAction::ThinArch; + return C; + default: reportError("llvm-lipo action unspecified"); } @@ -164,8 +192,12 @@ createBinary(InputFile); if (!BinaryOrErr) reportError(InputFile, BinaryOrErr.takeError()); - if (!isa(BinaryOrErr->getBinary()) && - !isa(BinaryOrErr->getBinary())) + // TODO: Add compatibility for archive files + if (BinaryOrErr->getBinary()->isArchive()) + reportError("File " + InputFile + + " is an archive file and is not yet supported."); + if (!BinaryOrErr->getBinary()->isMachO() && + !BinaryOrErr->getBinary()->isMachOUniversalBinary()) reportError("File " + InputFile + " has unsupported binary format"); InputBinaries.push_back(std::move(*BinaryOrErr)); } @@ -180,8 +212,7 @@ assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); for (StringRef Arch : VerifyArchList) - if (Triple(Arch).getArch() == Triple::ArchType::UnknownArch) - reportError("Invalid architecture: " + Arch); + validateArchitectureName(Arch); if (auto UO = dyn_cast(InputBinaries.front().getBinary())) { @@ -238,6 +269,44 @@ exit(EXIT_SUCCESS); } +LLVM_ATTRIBUTE_NORETURN +static void extractSlice(ArrayRef> InputBinaries, + StringRef ThinArchType, StringRef OutputFileName) { + assert(!ThinArchType.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 -thin option is specified"); + exit(EXIT_FAILURE); + } + + auto *UO = cast(InputBinaries.front().getBinary()); + Expected> Obj = + UO->getObjectForArch(ThinArchType); + if (!Obj) + reportError("fat input file " + UO->getFileName() + + " does not contain the specified architecture " + ThinArchType + + " to thin it to"); + + Expected> OutFileOrError = + FileOutputBuffer::create(OutputFileName, + Obj.get()->getMemoryBufferRef().getBufferSize(), + sys::fs::can_execute(UO->getFileName()) + ? FileOutputBuffer::F_executable + : 0); + if (!OutFileOrError) + reportError(OutputFileName, OutFileOrError.takeError()); + std::copy(Obj.get()->getMemoryBufferRef().getBufferStart(), + Obj.get()->getMemoryBufferRef().getBufferEnd(), + OutFileOrError.get()->getBufferStart()); + if (Error E = OutFileOrError.get()->commit()) + reportError(OutputFileName, std::move(E)); + exit(EXIT_SUCCESS); +} + int main(int argc, char **argv) { InitLLVM X(argc, argv); Config C = parseLipoOptions(makeArrayRef(argv + 1, argc)); @@ -251,6 +320,9 @@ case LipoAction::PrintArchs: printArchs(InputBinaries); break; + case LipoAction::ThinArch: + extractSlice(InputBinaries, C.ThinArchType, C.OutputFile); + break; } return EXIT_SUCCESS; }