mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 04:02:41 +01:00
[llvm-lipo] Add support for bitcode files
A Mach-O universal binary may contain bitcode as a slice. This diff adds proper handling of such binaries to llvm-lipo. Test plan: make check-all Differential revision: https://reviews.llvm.org/D85740
This commit is contained in:
parent
c6e0a6e7fb
commit
add1ee3a12
@ -228,7 +228,8 @@ template <typename T> const T* OwningBinary<T>::getBinary() const {
|
||||
return Bin.get();
|
||||
}
|
||||
|
||||
Expected<OwningBinary<Binary>> createBinary(StringRef Path);
|
||||
Expected<OwningBinary<Binary>> createBinary(StringRef Path,
|
||||
LLVMContext *Context = nullptr);
|
||||
|
||||
} // end namespace object
|
||||
|
||||
|
@ -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 @@ public:
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<MachOObjectFile>> getAsObjectFile() const;
|
||||
Expected<std::unique_ptr<IRObjectFile>>
|
||||
getAsIRObject(LLVMContext &Ctx) const;
|
||||
|
||||
Expected<std::unique_ptr<Archive>> getAsArchive() const;
|
||||
};
|
||||
@ -154,6 +159,9 @@ public:
|
||||
Expected<std::unique_ptr<MachOObjectFile>>
|
||||
getMachOObjectForArch(StringRef ArchName) const;
|
||||
|
||||
Expected<std::unique_ptr<IRObjectFile>>
|
||||
getIRObjectForArch(StringRef ArchName, LLVMContext &Ctx) const;
|
||||
|
||||
Expected<std::unique_ptr<Archive>>
|
||||
getArchiveForArch(StringRef ArchName) const;
|
||||
};
|
||||
|
@ -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 @@ class Slice {
|
||||
// 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<Slice> create(const Archive *A);
|
||||
static Expected<Slice> create(const Archive *A,
|
||||
LLVMContext *LLVMCtx = nullptr);
|
||||
|
||||
static Expected<Slice> create(const IRObjectFile *IRO, uint32_t Align);
|
||||
|
||||
void setP2Alignment(uint32_t Align) { P2Alignment = Align; }
|
||||
|
||||
|
@ -93,7 +93,8 @@ Expected<std::unique_ptr<Binary>> object::createBinary(MemoryBufferRef Buffer,
|
||||
llvm_unreachable("Unexpected Binary File Type");
|
||||
}
|
||||
|
||||
Expected<OwningBinary<Binary>> object::createBinary(StringRef Path) {
|
||||
Expected<OwningBinary<Binary>> object::createBinary(StringRef Path,
|
||||
LLVMContext *Context) {
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
|
||||
MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1,
|
||||
/*RequiresNullTerminator=*/false);
|
||||
@ -102,7 +103,7 @@ Expected<OwningBinary<Binary>> object::createBinary(StringRef Path) {
|
||||
std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get();
|
||||
|
||||
Expected<std::unique_ptr<Binary>> BinOrErr =
|
||||
createBinary(Buffer->getMemBufferRef());
|
||||
createBinary(Buffer->getMemBufferRef(), Context);
|
||||
if (!BinOrErr)
|
||||
return BinOrErr.takeError();
|
||||
std::unique_ptr<Binary> &Bin = BinOrErr.get();
|
||||
|
@ -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 @@ MachOUniversalBinary::ObjectForArch::getAsObjectFile() const {
|
||||
return ObjectFile::createMachOObjectFile(ObjBuffer, cputype, Index);
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<IRObjectFile>>
|
||||
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<std::unique_ptr<Archive>>
|
||||
MachOUniversalBinary::ObjectForArch::getAsArchive() const {
|
||||
if (!Parent)
|
||||
@ -234,6 +254,15 @@ MachOUniversalBinary::getMachOObjectForArch(StringRef ArchName) const {
|
||||
return O->getAsObjectFile();
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<IRObjectFile>>
|
||||
MachOUniversalBinary::getIRObjectForArch(StringRef ArchName,
|
||||
LLVMContext &Ctx) const {
|
||||
Expected<ObjectForArch> O = getObjectForArch(ArchName);
|
||||
if (!O)
|
||||
return O.takeError();
|
||||
return O->getAsIRObject(Ctx);
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<Archive>>
|
||||
MachOUniversalBinary::getArchiveForArch(StringRef ArchName) const {
|
||||
Expected<ObjectForArch> O = getObjectForArch(ArchName);
|
||||
|
@ -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 @@ Slice::Slice(const MachOObjectFile &O, uint32_t Align)
|
||||
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> Slice::create(const Archive *A) {
|
||||
using MachoCPUTy = std::pair<unsigned, unsigned>;
|
||||
|
||||
static Expected<MachoCPUTy> 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<MachoCPUTy> getMachoCPUFromTriple(StringRef TT) {
|
||||
return getMachoCPUFromTriple(Triple{TT});
|
||||
}
|
||||
|
||||
Expected<Slice> Slice::create(const Archive *A, LLVMContext *LLVMCtx) {
|
||||
Error Err = Error::success();
|
||||
std::unique_ptr<MachOObjectFile> FO = nullptr;
|
||||
std::unique_ptr<MachOObjectFile> MFO = nullptr;
|
||||
std::unique_ptr<IRObjectFile> IRFO = nullptr;
|
||||
for (const Archive::Child &Child : A->children(Err)) {
|
||||
Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
|
||||
Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(LLVMCtx);
|
||||
if (!ChildOrErr)
|
||||
return createFileError(A->getFileName(), ChildOrErr.takeError());
|
||||
Binary *Bin = ChildOrErr.get().get();
|
||||
@ -95,36 +120,79 @@ Expected<Slice> Slice::create(const Archive *A) {
|
||||
" 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<MachOObjectFile>(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<MachOObjectFile>(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<IRObjectFile>(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<MachoCPUTy> CPUO = getMachoCPUFromTriple(O->getTargetTriple());
|
||||
Expected<MachoCPUTy> 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 @@ Expected<Slice> Slice::create(const Archive *A) {
|
||||
.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<Slice> 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> Slice::create(const IRObjectFile *IRO, uint32_t Align) {
|
||||
Expected<MachoCPUTy> 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<SmallVector<MachO::fat_arch, 2>>
|
||||
|
1
test/tools/llvm-lipo/Inputs/arm64-ios.ll
Normal file
1
test/tools/llvm-lipo/Inputs/arm64-ios.ll
Normal file
@ -0,0 +1 @@
|
||||
target triple = "arm64-apple-ios8.0.0"
|
1
test/tools/llvm-lipo/Inputs/armv7-ios.ll
Normal file
1
test/tools/llvm-lipo/Inputs/armv7-ios.ll
Normal file
@ -0,0 +1 @@
|
||||
target triple = "thumbv7-apple-ios8.0.0"
|
1
test/tools/llvm-lipo/Inputs/x64-osx.ll
Normal file
1
test/tools/llvm-lipo/Inputs/x64-osx.ll
Normal file
@ -0,0 +1 @@
|
||||
target triple = "x86_64-apple-macosx10.15.0"
|
4
test/tools/llvm-lipo/archs-ir-binary.test
Normal file
4
test/tools/llvm-lipo/archs-ir-binary.test
Normal file
@ -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
|
9
test/tools/llvm-lipo/create-arch-ir.test
Normal file
9
test/tools/llvm-lipo/create-arch-ir.test
Normal file
@ -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)
|
@ -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
|
||||
|
16
test/tools/llvm-lipo/info-universal-binary-ir.test
Normal file
16
test/tools/llvm-lipo/info-universal-binary-ir.test
Normal file
@ -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
|
11
test/tools/llvm-lipo/thin-universal-binary-ir.test
Normal file
11
test/tools/llvm-lipo/thin-universal-binary-ir.test
Normal file
@ -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
|
@ -4,6 +4,8 @@ set(LLVM_LINK_COMPONENTS
|
||||
Option
|
||||
Support
|
||||
TextAPI
|
||||
Core
|
||||
BinaryFormat
|
||||
)
|
||||
|
||||
set(LLVM_TARGET_DEFINITIONS LipoOpts.td)
|
||||
|
@ -17,4 +17,4 @@
|
||||
type = Tool
|
||||
name = llvm-lipo
|
||||
parent = Tools
|
||||
required_libraries = Object Option Support
|
||||
required_libraries = Object Option Support BinaryFormat
|
||||
|
@ -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;
|
||||
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 @@ struct Config {
|
||||
LipoAction ActionToPerform;
|
||||
};
|
||||
|
||||
static Slice archiveSlice(const Archive *A, StringRef File) {
|
||||
Expected<Slice> ArchiveOrSlice = Slice::create(A);
|
||||
static Slice createSliceFromArchive(const Archive *A) {
|
||||
Expected<Slice> 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<Slice> 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 @@ static SmallVector<OwningBinary<Binary>, 1>
|
||||
readInputBinaries(ArrayRef<InputFile> InputFiles) {
|
||||
SmallVector<OwningBinary<Binary>, 1> InputBinaries;
|
||||
for (const InputFile &IF : InputFiles) {
|
||||
Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(IF.FileName);
|
||||
Expected<OwningBinary<Binary>> 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<MachOObjectFile>(B))
|
||||
: archiveSlice(cast<Archive>(B), IF.FileName);
|
||||
if (IF.ArchType && (B->isMachO() || B->isArchive() || B->isIR())) {
|
||||
const auto S = B->isMachO()
|
||||
? Slice(*cast<MachOObjectFile>(B))
|
||||
: B->isArchive()
|
||||
? createSliceFromArchive(cast<Archive>(B))
|
||||
: createSliceFromIR(cast<IRObjectFile>(B), 0);
|
||||
const auto SpecifiedCPUType = MachO::getCPUTypeFromArchitecture(
|
||||
MachO::getArchitectureFromName(
|
||||
Triple(*IF.ArchType).getArchName()))
|
||||
@ -367,27 +384,55 @@ static void printBinaryArchs(const Binary *Binary, raw_ostream &OS) {
|
||||
// Prints trailing space for compatibility with cctools lipo.
|
||||
if (auto UO = dyn_cast<MachOUniversalBinary>(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<std::unique_ptr<MachOObjectFile>> MachOObjOrError =
|
||||
O.getAsObjectFile();
|
||||
if (MachOObjOrError) {
|
||||
OS << Slice(*(MachOObjOrError->get())).getArchString() << " ";
|
||||
continue;
|
||||
}
|
||||
Expected<std::unique_ptr<IRObjectFile>> IROrError =
|
||||
O.getAsIRObject(LLVMCtx);
|
||||
if (IROrError) {
|
||||
consumeError(MachOObjOrError.takeError());
|
||||
Expected<Slice> SliceOrErr =
|
||||
Slice::create(IROrError->get(), O.getAlign());
|
||||
if (!SliceOrErr) {
|
||||
reportError(Binary->getFileName(), SliceOrErr.takeError());
|
||||
continue;
|
||||
}
|
||||
OS << SliceOrErr.get().getArchString() << " ";
|
||||
continue;
|
||||
}
|
||||
Expected<std::unique_ptr<Archive>> 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<MachOObjectFile>(Binary)).getArchString() << " \n";
|
||||
|
||||
if (const auto *MachO = dyn_cast<MachOObjectFile>(Binary)) {
|
||||
OS << Slice(*MachO).getArchString() << " \n";
|
||||
return;
|
||||
}
|
||||
|
||||
// This should be always the case, as this is tested in readInputBinaries
|
||||
const auto *IR = cast<IRObjectFile>(Binary);
|
||||
Expected<Slice> SliceOrErr = createSliceFromIR(IR, 0);
|
||||
if (!SliceOrErr)
|
||||
reportError(IR->getFileName(), SliceOrErr.takeError());
|
||||
|
||||
OS << SliceOrErr->getArchString() << " \n";
|
||||
}
|
||||
|
||||
LLVM_ATTRIBUTE_NORETURN
|
||||
@ -437,13 +482,23 @@ static void thinSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,
|
||||
auto *UO = cast<MachOUniversalBinary>(InputBinaries.front().getBinary());
|
||||
Expected<std::unique_ptr<MachOObjectFile>> Obj =
|
||||
UO->getMachOObjectForArch(ArchType);
|
||||
Expected<std::unique_ptr<IRObjectFile>> IRObj =
|
||||
UO->getIRObjectForArch(ArchType, LLVMCtx);
|
||||
Expected<std::unique_ptr<Archive>> 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<Binary *>(Obj->get())
|
||||
: static_cast<Binary *>(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<std::unique_ptr<FileOutputBuffer>> OutFileOrError =
|
||||
FileOutputBuffer::create(OutputFileName,
|
||||
B->getMemoryBufferRef().getBufferSize(),
|
||||
@ -498,26 +553,48 @@ static void checkUnusedAlignments(ArrayRef<Slice> Slices,
|
||||
|
||||
// Updates vector ExtractedObjects with the MachOObjectFiles extracted from
|
||||
// Universal Binary files to transfer ownership.
|
||||
static SmallVector<Slice, 2> buildSlices(
|
||||
ArrayRef<OwningBinary<Binary>> InputBinaries,
|
||||
const StringMap<const uint32_t> &Alignments,
|
||||
SmallVectorImpl<std::unique_ptr<MachOObjectFile>> &ExtractedObjects) {
|
||||
static SmallVector<Slice, 2>
|
||||
buildSlices(ArrayRef<OwningBinary<Binary>> InputBinaries,
|
||||
const StringMap<const uint32_t> &Alignments,
|
||||
SmallVectorImpl<std::unique_ptr<SymbolicFile>> &ExtractedObjects) {
|
||||
SmallVector<Slice, 2> Slices;
|
||||
for (auto &IB : InputBinaries) {
|
||||
const Binary *InputBinary = IB.getBinary();
|
||||
if (auto UO = dyn_cast<MachOUniversalBinary>(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<std::unique_ptr<MachOObjectFile>> 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<std::unique_ptr<IRObjectFile>> IROrError =
|
||||
O.getAsIRObject(LLVMCtx);
|
||||
if (IROrError) {
|
||||
consumeError(BinaryOrError.takeError());
|
||||
Slice S = createSliceFromIR(
|
||||
static_cast<IRObjectFile *>(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<MachOObjectFile>(InputBinary)) {
|
||||
Slices.emplace_back(*O);
|
||||
} else if (auto A = dyn_cast<Archive>(InputBinary)) {
|
||||
Slices.push_back(archiveSlice(A, InputBinary->getFileName()));
|
||||
Slices.push_back(createSliceFromArchive(A));
|
||||
} else if (const auto *IRO = dyn_cast<IRObjectFile>(InputBinary)) {
|
||||
// Original Apple's lipo set the alignment to 0
|
||||
Expected<Slice> 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 @@ static void createUniversalBinary(ArrayRef<OwningBinary<Binary>> InputBinaries,
|
||||
assert(InputBinaries.size() >= 1 && "Incorrect number of input binaries");
|
||||
assert(!OutputFileName.empty() && "Create expects a single output file");
|
||||
|
||||
SmallVector<std::unique_ptr<MachOObjectFile>, 1> ExtractedObjects;
|
||||
SmallVector<std::unique_ptr<SymbolicFile>, 1> ExtractedObjects;
|
||||
SmallVector<Slice, 1> Slices =
|
||||
buildSlices(InputBinaries, Alignments, ExtractedObjects);
|
||||
checkArchDuplicates(Slices);
|
||||
@ -561,7 +638,7 @@ static void extractSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,
|
||||
" must be a fat file when the -extract option is specified");
|
||||
}
|
||||
|
||||
SmallVector<std::unique_ptr<MachOObjectFile>, 2> ExtractedObjects;
|
||||
SmallVector<std::unique_ptr<SymbolicFile>, 2> ExtractedObjects;
|
||||
SmallVector<Slice, 2> Slices =
|
||||
buildSlices(InputBinaries, Alignments, ExtractedObjects);
|
||||
erase_if(Slices, [ArchType](const Slice &S) {
|
||||
@ -623,7 +700,7 @@ static void replaceSlices(ArrayRef<OwningBinary<Binary>> InputBinaries,
|
||||
|
||||
StringMap<Slice> ReplacementSlices =
|
||||
buildReplacementSlices(ReplacementBinaries, Alignments);
|
||||
SmallVector<std::unique_ptr<MachOObjectFile>, 2> ExtractedObjects;
|
||||
SmallVector<std::unique_ptr<SymbolicFile>, 2> ExtractedObjects;
|
||||
SmallVector<Slice, 2> Slices =
|
||||
buildSlices(InputBinaries, Alignments, ExtractedObjects);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user