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 @@ -16,6 +16,7 @@ #include "llvm/Object/Archive.h" #include "llvm/Object/Binary.h" +#include "llvm/Object/IRObjectFile.h" #include "llvm/Object/MachO.h" namespace llvm { @@ -32,6 +33,9 @@ // 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); @@ -39,6 +43,27 @@ static Expected create(const Archive *A); + static Expected create(const IRObjectFile *IRO, uint32_t Align) { + Triple T(IRO->getTargetTriple()); + + auto CPUTypeOrErr = MachO::getCPUType(T); + if (!CPUTypeOrErr) { + return CPUTypeOrErr.takeError(); + } + const uint32_t CPUType = CPUTypeOrErr.get(); + + auto CPUSubTypeOrErr = MachO::getCPUSubType(T); + if (!CPUSubTypeOrErr) { + return CPUSubTypeOrErr.takeError(); + } + const uint32_t 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, CPUType, CPUSubType, std::move(ArchName), Align}; + } + void setP2Alignment(uint32_t Align) { P2Alignment = Align; } const Binary *getBinary() const { return B; } 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::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); 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 @@ -79,6 +79,11 @@ 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) { 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,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/MachOUniversalWriter.h" @@ -26,10 +28,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"; @@ -307,11 +313,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)) @@ -367,15 +375,30 @@ // 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::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()); + consumeError(IROrError.takeError()); OS << archiveSlice(ArchiveOrError->get(), Binary->getFileName()) .getArchString() << " "; @@ -383,6 +406,7 @@ } consumeError(ArchiveOrError.takeError()); reportError(Binary->getFileName(), MachOObjOrError.takeError()); + reportError(Binary->getFileName(), IROrError.takeError()); } OS << "\n"; return; @@ -437,13 +461,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 +532,52 @@ // 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()); + auto SliceOrErr = Slice::create( + 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.push_back(archiveSlice(A, InputBinary->getFileName())); + } else if (auto IRO = dyn_cast(InputBinary)) { + // Original Apple's lipo set the alignment to 0 + auto 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 +593,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 +621,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 +683,7 @@ StringMap ReplacementSlices = buildReplacementSlices(ReplacementBinaries, Alignments); - SmallVector, 2> ExtractedObjects; + SmallVector, 2> ExtractedObjects; SmallVector Slices = buildSlices(InputBinaries, Alignments, ExtractedObjects);