Index: llvm/include/llvm/Object/Binary.h =================================================================== --- llvm/include/llvm/Object/Binary.h +++ 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 Index: llvm/include/llvm/Object/MachOUniversal.h =================================================================== --- llvm/include/llvm/Object/MachOUniversal.h +++ 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; }; Index: llvm/lib/Object/Binary.cpp =================================================================== --- llvm/lib/Object/Binary.cpp +++ 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(); Index: llvm/lib/Object/MachOUniversal.cpp =================================================================== --- llvm/lib/Object/MachOUniversal.cpp +++ 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::getAsLLVMModule() " + "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); Index: llvm/tools/llvm-lipo/CMakeLists.txt =================================================================== --- llvm/tools/llvm-lipo/CMakeLists.txt +++ llvm/tools/llvm-lipo/CMakeLists.txt @@ -4,6 +4,8 @@ Option Support TextAPI + Core + BinaryFormat ) set(LLVM_TARGET_DEFINITIONS LipoOpts.td) Index: llvm/tools/llvm-lipo/LLVMBuild.txt =================================================================== --- llvm/tools/llvm-lipo/LLVMBuild.txt +++ 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 Index: llvm/tools/llvm-lipo/llvm-lipo.cpp =================================================================== --- llvm/tools/llvm-lipo/llvm-lipo.cpp +++ llvm/tools/llvm-lipo/llvm-lipo.cpp @@ -12,7 +12,9 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" +#include "llvm/BinaryFormat/MachO.h" #include "llvm/Object/Binary.h" +#include "llvm/Object/IRObjectFile.h" #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" @@ -25,10 +27,14 @@ #include "llvm/Support/WithColor.h" #include "llvm/TextAPI/MachO/Architecture.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" + using namespace llvm; 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"; @@ -165,6 +171,11 @@ // file size can be calculated before creating the output buffer. uint32_t P2Alignment; + explicit Slice(const IRObjectFile *IRO, uint32_t Align, uint32_t CPUType, + uint32_t CPUSubType, std::string ArchName) + : B(IRO), CPUType(CPUType), CPUSubType(CPUSubType), + ArchName(std::move(ArchName)), P2Alignment(Align) {} + public: Slice(const MachOObjectFile *O, uint32_t Align) : B(O), CPUType(O->getHeader().cputype), @@ -220,6 +231,33 @@ P2Alignment = FO->is64Bit() ? 3 : 2; } + Slice(Slice const &) = default; + Slice(Slice &&) = default; + Slice &operator=(Slice const &) = default; + Slice &operator=(Slice &&) = default; + + static Expected createIRObjectSlice(const IRObjectFile *IRO, + uint32_t Align) { + Triple T(IRO->getTargetTriple()); + + auto CPUTypeOrErr = MachO::getCPUType(T); + if (!CPUTypeOrErr) { + return CPUTypeOrErr.takeError(); + } + const auto CPUType = CPUTypeOrErr.get(); + + auto CPUSubTypeOrErr = MachO::getCPUSubType(T); + if (!CPUSubTypeOrErr) { + return CPUSubTypeOrErr.takeError(); + } + const auto CPUSubType = CPUSubTypeOrErr.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, Align, CPUType, CPUSubType, std::move(ArchName)}; + } + void setP2Alignment(uint32_t Align) { P2Alignment = Align; } const Binary *getBinary() const { return B; } @@ -443,11 +481,13 @@ 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)) @@ -503,20 +543,37 @@ // 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()); + auto SliceOrErr = + Slice::createIRObjectSlice(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()); + consumeError(IROrError.takeError()); OS << Slice(ArchiveOrError->get()).getArchString() << " "; continue; } consumeError(ArchiveOrError.takeError()); reportError(Binary->getFileName(), MachOObjOrError.takeError()); + reportError(Binary->getFileName(), IROrError.takeError()); } OS << "\n"; return; @@ -571,13 +628,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(), @@ -632,26 +699,54 @@ // 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) { + ExtractedObjects.push_back(std::move(BinaryOrError.get())); + Slices.emplace_back( + static_cast(ExtractedObjects.back().get()), + O.getAlign()); + continue; + } + Expected> IROrError = + O.getAsIRObject(LLVMCtx); + if (IROrError) { + consumeError(BinaryOrError.takeError()); + auto SliceOrErr = Slice::createIRObjectSlice( + static_cast(IROrError.get().get()), O.getAlign()); + if (!SliceOrErr) { + reportError(InputBinary->getFileName(), SliceOrErr.takeError()); + continue; + } + ExtractedObjects.emplace_back(std::move(IROrError.get())); + Slices.emplace_back(std::move(SliceOrErr.get())); + 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.emplace_back(A); + } else if (auto IRO = dyn_cast(InputBinary)) { + // Original Apple's lipo set the alignment to 0 + auto SliceOrErr = Slice::createIRObjectSlice(IRO, 0); + if (!SliceOrErr) { + reportError(InputBinary->getFileName(), SliceOrErr.takeError()); + continue; + } + Slices.emplace_back(std::move(SliceOrErr.get())); } else { llvm_unreachable("Unexpected binary format"); } @@ -740,7 +835,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); @@ -765,7 +860,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) { @@ -824,7 +919,7 @@ StringMap ReplacementSlices = buildReplacementSlices(ReplacementBinaries, Alignments); - SmallVector, 2> ExtractedObjects; + SmallVector, 2> ExtractedObjects; SmallVector Slices = buildSlices(InputBinaries, Alignments, ExtractedObjects);