diff --git a/llvm/include/llvm/Object/Binary.h b/llvm/include/llvm/Object/Binary.h --- a/llvm/include/llvm/Object/Binary.h +++ b/llvm/include/llvm/Object/Binary.h @@ -228,7 +228,8 @@ return Bin.get(); } -Expected> createBinary(StringRef Path); +Expected> createBinary(StringRef Path, + LLVMContext *Context = nullptr); } // end namespace object diff --git a/llvm/include/llvm/Object/MachOUniversal.h b/llvm/include/llvm/Object/MachOUniversal.h --- a/llvm/include/llvm/Object/MachOUniversal.h +++ b/llvm/include/llvm/Object/MachOUniversal.h @@ -22,8 +22,11 @@ namespace llvm { class StringRef; +class Module; +class LLVMContext; namespace object { +class IRObjectFile; class MachOUniversalBinary : public Binary { virtual void anchor(); @@ -101,6 +104,8 @@ } Expected> getAsObjectFile() const; + Expected> + getAsIRObject(LLVMContext &Ctx) const; Expected> getAsArchive() const; }; @@ -154,6 +159,9 @@ Expected> getMachOObjectForArch(StringRef ArchName) const; + Expected> + getIRObjectForArch(StringRef ArchName, LLVMContext &Ctx) const; + Expected> getArchiveForArch(StringRef ArchName) const; }; diff --git a/llvm/include/llvm/Object/MachOUniversalWriter.h b/llvm/include/llvm/Object/MachOUniversalWriter.h --- a/llvm/include/llvm/Object/MachOUniversalWriter.h +++ b/llvm/include/llvm/Object/MachOUniversalWriter.h @@ -19,7 +19,10 @@ #include "llvm/Object/MachO.h" namespace llvm { +class LLVMContext; + namespace object { +class IRObjectFile; class Slice { const Binary *B; @@ -32,12 +35,18 @@ // file size can be calculated before creating the output buffer. uint32_t P2Alignment; + Slice(const IRObjectFile *IRO, uint32_t CPUType, uint32_t CPUSubType, + std::string ArchName, uint32_t Align); + public: explicit Slice(const MachOObjectFile &O); Slice(const MachOObjectFile &O, uint32_t Align); - static Expected create(const Archive *A); + static Expected create(const Archive *A, + LLVMContext *LLVMCtx = nullptr); + + static Expected create(const IRObjectFile *IRO, uint32_t Align); void setP2Alignment(uint32_t Align) { P2Alignment = Align; } diff --git a/llvm/lib/Object/Binary.cpp b/llvm/lib/Object/Binary.cpp --- a/llvm/lib/Object/Binary.cpp +++ b/llvm/lib/Object/Binary.cpp @@ -93,7 +93,8 @@ llvm_unreachable("Unexpected Binary File Type"); } -Expected> object::createBinary(StringRef Path) { +Expected> object::createBinary(StringRef Path, + LLVMContext *Context) { ErrorOr> FileOrErr = MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, /*RequiresNullTerminator=*/false); @@ -102,7 +103,7 @@ std::unique_ptr &Buffer = FileOrErr.get(); Expected> BinOrErr = - createBinary(Buffer->getMemBufferRef()); + createBinary(Buffer->getMemBufferRef(), Context); if (!BinOrErr) return BinOrErr.takeError(); std::unique_ptr &Bin = BinOrErr.get(); diff --git a/llvm/lib/Object/MachOUniversal.cpp b/llvm/lib/Object/MachOUniversal.cpp --- a/llvm/lib/Object/MachOUniversal.cpp +++ b/llvm/lib/Object/MachOUniversal.cpp @@ -12,6 +12,7 @@ #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/Archive.h" +#include "llvm/Object/IRObjectFile.h" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Casting.h" @@ -80,6 +81,25 @@ return ObjectFile::createMachOObjectFile(ObjBuffer, cputype, Index); } +Expected> +MachOUniversalBinary::ObjectForArch::getAsIRObject(LLVMContext &Ctx) const { + if (!Parent) + report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsIRObject() " + "called when Parent is a nullptr"); + + StringRef ParentData = Parent->getData(); + StringRef ObjectData; + if (Parent->getMagic() == MachO::FAT_MAGIC) { + ObjectData = ParentData.substr(Header.offset, Header.size); + } else { // Parent->getMagic() == MachO::FAT_MAGIC_64 + ObjectData = ParentData.substr(Header64.offset, Header64.size); + } + StringRef ObjectName = Parent->getFileName(); + MemoryBufferRef ObjBuffer(ObjectData, ObjectName); + + return IRObjectFile::create(ObjBuffer, Ctx); +} + Expected> MachOUniversalBinary::ObjectForArch::getAsArchive() const { if (!Parent) @@ -234,6 +254,15 @@ return O->getAsObjectFile(); } +Expected> +MachOUniversalBinary::getIRObjectForArch(StringRef ArchName, + LLVMContext &Ctx) const { + Expected O = getObjectForArch(ArchName); + if (!O) + return O.takeError(); + return O->getAsIRObject(Ctx); +} + Expected> MachOUniversalBinary::getArchiveForArch(StringRef ArchName) const { Expected O = getObjectForArch(ArchName); diff --git a/llvm/lib/Object/MachOUniversalWriter.cpp b/llvm/lib/Object/MachOUniversalWriter.cpp --- a/llvm/lib/Object/MachOUniversalWriter.cpp +++ b/llvm/lib/Object/MachOUniversalWriter.cpp @@ -12,9 +12,11 @@ //===----------------------------------------------------------------------===// #include "llvm/Object/MachOUniversalWriter.h" +#include "llvm/ADT/Triple.h" #include "llvm/Object/Archive.h" #include "llvm/Object/Binary.h" #include "llvm/Object/Error.h" +#include "llvm/Object/IRObjectFile.h" #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Support/FileOutputBuffer.h" @@ -79,13 +81,36 @@ ArchName(std::string(O.getArchTriple().getArchName())), P2Alignment(Align) {} +Slice::Slice(const IRObjectFile *IRO, uint32_t CPUType, uint32_t CPUSubType, + std::string ArchName, uint32_t Align) + : B(IRO), CPUType(CPUType), CPUSubType(CPUSubType), + ArchName(std::move(ArchName)), P2Alignment(Align) {} + Slice::Slice(const MachOObjectFile &O) : Slice(O, calculateAlignment(O)) {} -Expected Slice::create(const Archive *A) { +using MachoCPUTy = std::pair; + +static Expected getMachoCPUFromTriple(Triple TT) { + auto CPU = std::make_pair(MachO::getCPUType(TT), MachO::getCPUSubType(TT)); + if (!CPU.first) { + return CPU.first.takeError(); + } + if (!CPU.second) { + return CPU.second.takeError(); + } + return std::make_pair(*CPU.first, *CPU.second); +} + +static Expected getMachoCPUFromTriple(StringRef TT) { + return getMachoCPUFromTriple(Triple{TT}); +} + +Expected Slice::create(const Archive *A, LLVMContext *LLVMCtx) { Error Err = Error::success(); - std::unique_ptr FO = nullptr; + std::unique_ptr MFO = nullptr; + std::unique_ptr IRFO = nullptr; for (const Archive::Child &Child : A->children(Err)) { - Expected> ChildOrErr = Child.getAsBinary(); + Expected> ChildOrErr = Child.getAsBinary(LLVMCtx); if (!ChildOrErr) return createFileError(A->getFileName(), ChildOrErr.takeError()); Binary *Bin = ChildOrErr.get().get(); @@ -95,36 +120,79 @@ " is a fat file (not allowed in an archive)") .str() .c_str()); - if (!Bin->isMachO()) - return createStringError( - std::errc::invalid_argument, - ("archive member " + Bin->getFileName() + - " is not a MachO file (not allowed in an archive)") - .str() - .c_str()); - MachOObjectFile *O = cast(Bin); - if (FO && std::tie(FO->getHeader().cputype, FO->getHeader().cpusubtype) != - std::tie(O->getHeader().cputype, O->getHeader().cpusubtype)) { - return createStringError( - std::errc::invalid_argument, - ("archive member " + O->getFileName() + " cputype (" + - Twine(O->getHeader().cputype) + ") and cpusubtype(" + - Twine(O->getHeader().cpusubtype) + - ") does not match previous archive members cputype (" + - Twine(FO->getHeader().cputype) + ") and cpusubtype(" + - Twine(FO->getHeader().cpusubtype) + ") (all members must match) " + - FO->getFileName()) - .str() - .c_str()); - } - if (!FO) { - ChildOrErr.get().release(); - FO.reset(O); - } + if (Bin->isMachO()) { + MachOObjectFile *O = cast(Bin); + if (IRFO) { + return createStringError( + std::errc::invalid_argument, + "archive member %s is a MachO, while previous archive member " + "%s was an IR LLVM object", + O->getFileName().str().c_str(), IRFO->getFileName().str().c_str()); + } + if (MFO && + std::tie(MFO->getHeader().cputype, MFO->getHeader().cpusubtype) != + std::tie(O->getHeader().cputype, O->getHeader().cpusubtype)) { + return createStringError( + std::errc::invalid_argument, + ("archive member " + O->getFileName() + " cputype (" + + Twine(O->getHeader().cputype) + ") and cpusubtype(" + + Twine(O->getHeader().cpusubtype) + + ") does not match previous archive members cputype (" + + Twine(MFO->getHeader().cputype) + ") and cpusubtype(" + + Twine(MFO->getHeader().cpusubtype) + + ") (all members must match) " + MFO->getFileName()) + .str() + .c_str()); + } + if (!MFO) { + ChildOrErr.get().release(); + MFO.reset(O); + } + } else if (Bin->isIR()) { + IRObjectFile *O = cast(Bin); + if (MFO) { + return createStringError(std::errc::invalid_argument, + "archive member '%s' is an LLVM IR object, " + "while previous archive member " + "'%s' was a MachO", + O->getFileName().str().c_str(), + MFO->getFileName().str().c_str()); + } + if (IRFO) { + Expected CPUO = getMachoCPUFromTriple(O->getTargetTriple()); + Expected CPUFO = + getMachoCPUFromTriple(IRFO->getTargetTriple()); + if (!CPUO) + return CPUO.takeError(); + if (!CPUFO) + return CPUFO.takeError(); + if (*CPUO != *CPUFO) { + return createStringError( + std::errc::invalid_argument, + ("archive member " + O->getFileName() + " cputype (" + + Twine(CPUO->first) + ") and cpusubtype(" + Twine(CPUO->second) + + ") does not match previous archive members cputype (" + + Twine(CPUFO->first) + ") and cpusubtype(" + + Twine(CPUFO->second) + ") (all members must match) " + + IRFO->getFileName()) + .str() + .c_str()); + } + } else { + ChildOrErr.get().release(); + IRFO.reset(O); + } + } else + return createStringError(std::errc::invalid_argument, + ("archive member " + Bin->getFileName() + + " is neither a MachO file or an LLVM IR file " + "(not allowed in an archive)") + .str() + .c_str()); } if (Err) return createFileError(A->getFileName(), std::move(Err)); - if (!FO) + if (!MFO && !IRFO) return createStringError( std::errc::invalid_argument, ("empty archive with no architecture specification: " + @@ -132,9 +200,32 @@ .str() .c_str()); - Slice ArchiveSlice = Slice(*(FO.get()), FO->is64Bit() ? 3 : 2); + if (MFO) { + Slice ArchiveSlice(*(MFO.get()), MFO->is64Bit() ? 3 : 2); + ArchiveSlice.B = A; + return ArchiveSlice; + } + + // For IR objects + Expected ArchiveSliceOrErr = Slice::create(IRFO.get(), 0); + if (!ArchiveSliceOrErr) + return createFileError(A->getFileName(), ArchiveSliceOrErr.takeError()); + auto &ArchiveSlice = ArchiveSliceOrErr.get(); ArchiveSlice.B = A; - return ArchiveSlice; + return Slice{std::move(ArchiveSlice)}; +} + +Expected Slice::create(const IRObjectFile *IRO, uint32_t Align) { + Expected CPUOrErr = getMachoCPUFromTriple(IRO->getTargetTriple()); + if (!CPUOrErr) + return CPUOrErr.takeError(); + unsigned CPUType, CPUSubType; + std::tie(CPUType, CPUSubType) = CPUOrErr.get(); + // We don't directly use the architecture name of the target triple T, as, + // for instance, thumb is treated as ARM by the MachOUniversal object. + std::string ArchName( + MachOObjectFile::getArchTriple(CPUType, CPUSubType).getArchName()); + return Slice{IRO, CPUType, CPUSubType, std::move(ArchName), Align}; } static Expected> diff --git a/llvm/test/tools/llvm-lipo/Inputs/arm64-ios.ll b/llvm/test/tools/llvm-lipo/Inputs/arm64-ios.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/Inputs/arm64-ios.ll @@ -0,0 +1 @@ +target triple = "arm64-apple-ios8.0.0" diff --git a/llvm/test/tools/llvm-lipo/Inputs/armv7-ios.ll b/llvm/test/tools/llvm-lipo/Inputs/armv7-ios.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/Inputs/armv7-ios.ll @@ -0,0 +1 @@ +target triple = "thumbv7-apple-ios8.0.0" diff --git a/llvm/test/tools/llvm-lipo/Inputs/x64-osx.ll b/llvm/test/tools/llvm-lipo/Inputs/x64-osx.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/Inputs/x64-osx.ll @@ -0,0 +1 @@ +target triple = "x86_64-apple-macosx10.15.0" diff --git a/llvm/test/tools/llvm-lipo/archs-ir-binary.test b/llvm/test/tools/llvm-lipo/archs-ir-binary.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/archs-ir-binary.test @@ -0,0 +1,4 @@ +# RUN: llvm-as %p/Inputs/armv7-ios.ll -o %t-armv7.o +# RUN: llvm-lipo -archs %t-armv7.o | FileCheck %s + +# CHECK: armv7 diff --git a/llvm/test/tools/llvm-lipo/create-arch-ir.test b/llvm/test/tools/llvm-lipo/create-arch-ir.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/create-arch-ir.test @@ -0,0 +1,9 @@ +# RUN: llvm-as %p/Inputs/armv7-ios.ll -o %t-armv7.o +# RUN: llvm-as %p/Inputs/arm64-ios.ll -o %t-arm64.o + +# RUN: llvm-lipo %t-armv7.o %t-arm64.o -create -output %t-universal.o +# RUN: llvm-lipo -arch armv7 %t-armv7.o -arch arm64 %t-arm64.o -create -output %t-universal-1.o +# RUN: cmp %t-universal.o %t-universal-1.o +# +# RUN: not llvm-lipo -arch armv7 %t-arm64.o -create -output /dev/null 2>&1 | FileCheck --check-prefix=ARCH_NOT_MATCHED %s +# ARCH_NOT_MATCHED: error: specified architecture: armv7 for file: {{.*}} does not match the file's architecture (arm64) diff --git a/llvm/test/tools/llvm-lipo/create-archive-input.test b/llvm/test/tools/llvm-lipo/create-archive-input.test --- a/llvm/test/tools/llvm-lipo/create-archive-input.test +++ b/llvm/test/tools/llvm-lipo/create-archive-input.test @@ -1,6 +1,8 @@ # RUN: yaml2obj %p/Inputs/i386-slice.yaml -o %t-i386.o # RUN: yaml2obj %p/Inputs/x86_64-slice.yaml -o %t-x86_64.o # RUN: yaml2obj %p/Inputs/i386-x86_64-universal.yaml -o %t-universal.o +# RUN: llvm-as %p/Inputs/armv7-ios.ll -o %t-ir-armv7.o +# RUN: llvm-as %p/Inputs/x64-osx.ll -o %t-ir-x86_64.o # RUN: llvm-ar cr %t.empty.a # RUN: not llvm-lipo %t.empty.a -create -output /dev/null 2>&1 | FileCheck --check-prefix=EMPTY-ARCHIVE %s @@ -18,9 +20,25 @@ # RUN: cmp %t-extracted-i386-lib.a %t-i386-lib.a # RUN: llvm-lipo %t-i386-x86_64-universal.o -thin x86_64 -output %t-extracted-x86_64.o # RUN: cmp %t-extracted-x86_64.o %t-x86_64.o -# + +# RUN: llvm-ar cr %t-ir-armv7-lib.a %t-ir-armv7.o +# RUN: llvm-lipo %t-ir-armv7-lib.a %t-ir-x86_64.o -create -output %t-ir-armv7-x86_64-universal.o +# RUN: llvm-lipo %t-ir-armv7-x86_64-universal.o -thin armv7 -output %t-ir-extracted-armv7-lib.a +# RUN: cmp %t-ir-extracted-armv7-lib.a %t-ir-armv7-lib.a +# RUN: llvm-lipo %t-ir-armv7-x86_64-universal.o -thin x86_64 -output %t-ir-extracted-x86_64.o +# RUN: cmp %t-ir-extracted-x86_64.o %t-ir-x86_64.o + +# RUN: llvm-ar cr %t.different_types0.a %t-i386.o %t-ir-x86_64.o +# RUN: not llvm-lipo -create %t.different_types0.a -output /dev/null 2>&1 | FileCheck --check-prefix=ARCHIVE-WITH-MACHO-AND-IR %s +# RUN: llvm-ar cr %t.different_types1.a %t-ir-x86_64.o %t-i386.o +# RUN: not llvm-lipo -create %t.different_types1.a -output /dev/null 2>&1 | FileCheck --check-prefix=ARCHIVE-WITH-IR-AND-MACHO %s +# RUN: llvm-ar cr %t.different_architectures_ir.a %t-ir-x86_64.o %t-ir-armv7.o +# RUN: not llvm-lipo -create %t.different_architectures_ir.a -output /dev/null 2>&1 | FileCheck --check-prefix=ARCHIVE-WITH-DIFFERENT-ARCHS %s + # EMPTY-ARCHIVE: empty archive # ARCHIVE-WITH-DIFFERENT-ARCHS: all members must match +# ARCHIVE-WITH-MACHO-AND-IR: is an LLVM IR object, while previous archive member {{.*}} was a MachO +# ARCHIVE-WITH-IR-AND-MACHO: is a MachO, while previous archive member {{.*}} was an IR LLVM object # ARCHIVE-WITH-FAT-BINARY: fat file (not allowed in an archive) # # INFO-i386-x86_64: i386 x86_64 diff --git a/llvm/test/tools/llvm-lipo/info-universal-binary-ir.test b/llvm/test/tools/llvm-lipo/info-universal-binary-ir.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/info-universal-binary-ir.test @@ -0,0 +1,16 @@ +# RUN: llvm-as %p/Inputs/armv7-ios.ll -o %t-armv7.o +# RUN: llvm-as %p/Inputs/arm64-ios.ll -o %t-arm64.o +# RUN: llvm-as %p/Inputs/x64-osx.ll -o %t-x64.o + +# RUN: llvm-lipo %t-arm64.o %t-x64.o -create -output %t-universal0.o +# RUN: llvm-lipo -info %t-universal0.o | FileCheck %s +# +# CHECK: Architectures in the fat file: {{.*}} are: x86_64 arm64 + +# Test that, despites the "thumbv7" triple present in armv7-ios.ll, +# architecture is still reported as armv7 +# RUN: llvm-lipo %t-armv7.o -create -output %t-universal1.o +# RUN: llvm-lipo -info %t-universal1.o | FileCheck --check-prefix=ARMV7 %s +# +# ARMV7: armv7 +# ARMV7-NOT: thumbv7 diff --git a/llvm/test/tools/llvm-lipo/thin-universal-binary-ir.test b/llvm/test/tools/llvm-lipo/thin-universal-binary-ir.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-lipo/thin-universal-binary-ir.test @@ -0,0 +1,11 @@ +# RUN: llvm-as %p/Inputs/armv7-ios.ll -o %t-armv7.o +# RUN: llvm-as %p/Inputs/arm64-ios.ll -o %t-arm64.o + +# RUN: llvm-lipo %t-armv7.o %t-arm64.o -create -output %t-universal.o +# RUN: llvm-lipo %t-universal.o -thin arm64 -output %t-arm64.out +# RUN: opt -S %t-arm64.out | FileCheck --check-prefix=ARM64 %s +# RUN: llvm-lipo %t-universal.o -thin armv7 -output %t-armv7.out +# RUN: opt -S %t-armv7.out | FileCheck --check-prefix=ARMV7 %s + +# ARM64: arm64-apple-ios8.0.0 +# ARMV7: thumbv7-apple-ios8.0.0 diff --git a/llvm/tools/llvm-lipo/CMakeLists.txt b/llvm/tools/llvm-lipo/CMakeLists.txt --- a/llvm/tools/llvm-lipo/CMakeLists.txt +++ b/llvm/tools/llvm-lipo/CMakeLists.txt @@ -4,6 +4,8 @@ Option Support TextAPI + Core + BinaryFormat ) set(LLVM_TARGET_DEFINITIONS LipoOpts.td) diff --git a/llvm/tools/llvm-lipo/LLVMBuild.txt b/llvm/tools/llvm-lipo/LLVMBuild.txt --- a/llvm/tools/llvm-lipo/LLVMBuild.txt +++ b/llvm/tools/llvm-lipo/LLVMBuild.txt @@ -17,4 +17,4 @@ type = Tool name = llvm-lipo parent = Tools -required_libraries = Object Option Support +required_libraries = Object Option Support BinaryFormat 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 @@ -12,7 +12,11 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" #include "llvm/Object/Binary.h" +#include "llvm/Object/IRObjectFile.h" #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/MachOUniversalWriter.h" @@ -30,6 +34,7 @@ using namespace llvm::object; static const StringRef ToolName = "llvm-lipo"; +static LLVMContext LLVMCtx; LLVM_ATTRIBUTE_NORETURN static void reportError(Twine Message) { WithColor::error(errs(), ToolName) << Message << "\n"; @@ -113,13 +118,20 @@ LipoAction ActionToPerform; }; -static Slice archiveSlice(const Archive *A, StringRef File) { - Expected ArchiveOrSlice = Slice::create(A); +static Slice createSliceFromArchive(const Archive *A) { + Expected ArchiveOrSlice = Slice::create(A, &LLVMCtx); if (!ArchiveOrSlice) - reportError(File, ArchiveOrSlice.takeError()); + reportError(A->getFileName(), ArchiveOrSlice.takeError()); return *ArchiveOrSlice; } +static Slice createSliceFromIR(const IRObjectFile *IRO, unsigned Align) { + Expected IROrErr = Slice::create(IRO, Align); + if (!IROrErr) + reportError(IRO->getFileName(), IROrErr.takeError()); + return *IROrErr; +} + } // end namespace static void validateArchitectureName(StringRef ArchitectureName) { @@ -307,15 +319,20 @@ readInputBinaries(ArrayRef InputFiles) { SmallVector, 1> InputBinaries; for (const InputFile &IF : InputFiles) { - Expected> BinaryOrErr = createBinary(IF.FileName); + Expected> BinaryOrErr = + createBinary(IF.FileName, &LLVMCtx); if (!BinaryOrErr) reportError(IF.FileName, BinaryOrErr.takeError()); const Binary *B = BinaryOrErr->getBinary(); - if (!B->isArchive() && !B->isMachO() && !B->isMachOUniversalBinary()) + if (!B->isArchive() && !B->isMachO() && !B->isMachOUniversalBinary() && + !B->isIR()) reportError("File " + IF.FileName + " has unsupported binary format"); - if (IF.ArchType && (B->isMachO() || B->isArchive())) { - const auto S = B->isMachO() ? Slice(*cast(B)) - : archiveSlice(cast(B), IF.FileName); + if (IF.ArchType && (B->isMachO() || B->isArchive() || B->isIR())) { + const auto S = B->isMachO() + ? Slice(*cast(B)) + : B->isArchive() + ? createSliceFromArchive(cast(B)) + : createSliceFromIR(cast(B), 0); const auto SpecifiedCPUType = MachO::getCPUTypeFromArchitecture( MachO::getArchitectureFromName( Triple(*IF.ArchType).getArchName())) @@ -367,27 +384,55 @@ // Prints trailing space for compatibility with cctools lipo. if (auto UO = dyn_cast(Binary)) { for (const auto &O : UO->objects()) { + // Order here is important, because both MachOObjectFile and + // IRObjectFile can be created with a binary that has embedded bitcode. Expected> MachOObjOrError = O.getAsObjectFile(); if (MachOObjOrError) { OS << Slice(*(MachOObjOrError->get())).getArchString() << " "; continue; } + Expected> IROrError = + O.getAsIRObject(LLVMCtx); + if (IROrError) { + consumeError(MachOObjOrError.takeError()); + Expected SliceOrErr = + Slice::create(IROrError->get(), O.getAlign()); + if (!SliceOrErr) { + reportError(Binary->getFileName(), SliceOrErr.takeError()); + continue; + } + OS << SliceOrErr.get().getArchString() << " "; + continue; + } Expected> ArchiveOrError = O.getAsArchive(); if (ArchiveOrError) { consumeError(MachOObjOrError.takeError()); - OS << archiveSlice(ArchiveOrError->get(), Binary->getFileName()) - .getArchString() + consumeError(IROrError.takeError()); + OS << createSliceFromArchive(ArchiveOrError->get()).getArchString() << " "; continue; } consumeError(ArchiveOrError.takeError()); reportError(Binary->getFileName(), MachOObjOrError.takeError()); + reportError(Binary->getFileName(), IROrError.takeError()); } OS << "\n"; return; } - OS << Slice(*cast(Binary)).getArchString() << " \n"; + + if (const auto *MachO = dyn_cast(Binary)) { + OS << Slice(*MachO).getArchString() << " \n"; + return; + } + + // This should be always the case, as this is tested in readInputBinaries + const auto *IR = cast(Binary); + Expected SliceOrErr = createSliceFromIR(IR, 0); + if (!SliceOrErr) + reportError(IR->getFileName(), SliceOrErr.takeError()); + + OS << SliceOrErr->getArchString() << " \n"; } LLVM_ATTRIBUTE_NORETURN @@ -437,13 +482,23 @@ auto *UO = cast(InputBinaries.front().getBinary()); Expected> Obj = UO->getMachOObjectForArch(ArchType); + Expected> IRObj = + UO->getIRObjectForArch(ArchType, LLVMCtx); Expected> Ar = UO->getArchiveForArch(ArchType); - if (!Obj && !Ar) + if (!Obj && !IRObj && !Ar) reportError("fat input file " + UO->getFileName() + " does not contain the specified architecture " + ArchType + " to thin it to"); - Binary *B = Obj ? static_cast(Obj->get()) - : static_cast(Ar->get()); + Binary *B; + // Order here is important, because both Obj and IRObj will be valid with a + // binary that has embedded bitcode. + if (Obj) + B = Obj->get(); + else if (IRObj) + B = IRObj->get(); + else + B = Ar->get(); + Expected> OutFileOrError = FileOutputBuffer::create(OutputFileName, B->getMemoryBufferRef().getBufferSize(), @@ -498,26 +553,48 @@ // Updates vector ExtractedObjects with the MachOObjectFiles extracted from // Universal Binary files to transfer ownership. -static SmallVector buildSlices( - ArrayRef> InputBinaries, - const StringMap &Alignments, - SmallVectorImpl> &ExtractedObjects) { +static SmallVector +buildSlices(ArrayRef> InputBinaries, + const StringMap &Alignments, + SmallVectorImpl> &ExtractedObjects) { SmallVector Slices; for (auto &IB : InputBinaries) { const Binary *InputBinary = IB.getBinary(); if (auto UO = dyn_cast(InputBinary)) { for (const auto &O : UO->objects()) { + // Order here is important, because both MachOObjectFile and + // IRObjectFile can be created with a binary that has embedded bitcode. Expected> BinaryOrError = O.getAsObjectFile(); - if (!BinaryOrError) - reportError(InputBinary->getFileName(), BinaryOrError.takeError()); - ExtractedObjects.push_back(std::move(BinaryOrError.get())); - Slices.emplace_back(*(ExtractedObjects.back().get()), O.getAlign()); + if (BinaryOrError) { + Slices.emplace_back(*(BinaryOrError.get()), O.getAlign()); + ExtractedObjects.push_back(std::move(BinaryOrError.get())); + continue; + } + Expected> IROrError = + O.getAsIRObject(LLVMCtx); + if (IROrError) { + consumeError(BinaryOrError.takeError()); + Slice S = createSliceFromIR( + static_cast(IROrError.get().get()), O.getAlign()); + ExtractedObjects.emplace_back(std::move(IROrError.get())); + Slices.emplace_back(std::move(S)); + continue; + } + reportError(InputBinary->getFileName(), BinaryOrError.takeError()); } } else if (auto O = dyn_cast(InputBinary)) { Slices.emplace_back(*O); } else if (auto A = dyn_cast(InputBinary)) { - Slices.push_back(archiveSlice(A, InputBinary->getFileName())); + Slices.push_back(createSliceFromArchive(A)); + } else if (const auto *IRO = dyn_cast(InputBinary)) { + // Original Apple's lipo set the alignment to 0 + Expected SliceOrErr = Slice::create(IRO, 0); + if (!SliceOrErr) { + reportError(InputBinary->getFileName(), SliceOrErr.takeError()); + continue; + } + Slices.emplace_back(std::move(SliceOrErr.get())); } else { llvm_unreachable("Unexpected binary format"); } @@ -533,7 +610,7 @@ assert(InputBinaries.size() >= 1 && "Incorrect number of input binaries"); assert(!OutputFileName.empty() && "Create expects a single output file"); - SmallVector, 1> ExtractedObjects; + SmallVector, 1> ExtractedObjects; SmallVector Slices = buildSlices(InputBinaries, Alignments, ExtractedObjects); checkArchDuplicates(Slices); @@ -561,7 +638,7 @@ " must be a fat file when the -extract option is specified"); } - SmallVector, 2> ExtractedObjects; + SmallVector, 2> ExtractedObjects; SmallVector Slices = buildSlices(InputBinaries, Alignments, ExtractedObjects); erase_if(Slices, [ArchType](const Slice &S) { @@ -623,7 +700,7 @@ StringMap ReplacementSlices = buildReplacementSlices(ReplacementBinaries, Alignments); - SmallVector, 2> ExtractedObjects; + SmallVector, 2> ExtractedObjects; SmallVector Slices = buildSlices(InputBinaries, Alignments, ExtractedObjects);