Skip to content

Commit c6f7ac0

Browse files
aguinetAlexander Shaposhnikov
authored andcommitted
[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
1 parent b1e856d commit c6f7ac0

17 files changed

+343
-64
lines changed

llvm/include/llvm/Object/Binary.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,8 @@ template <typename T> const T* OwningBinary<T>::getBinary() const {
228228
return Bin.get();
229229
}
230230

231-
Expected<OwningBinary<Binary>> createBinary(StringRef Path);
231+
Expected<OwningBinary<Binary>> createBinary(StringRef Path,
232+
LLVMContext *Context = nullptr);
232233

233234
} // end namespace object
234235

llvm/include/llvm/Object/MachOUniversal.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@
2222

2323
namespace llvm {
2424
class StringRef;
25+
class Module;
26+
class LLVMContext;
2527

2628
namespace object {
29+
class IRObjectFile;
2730

2831
class MachOUniversalBinary : public Binary {
2932
virtual void anchor();
@@ -101,6 +104,8 @@ class MachOUniversalBinary : public Binary {
101104
}
102105

103106
Expected<std::unique_ptr<MachOObjectFile>> getAsObjectFile() const;
107+
Expected<std::unique_ptr<IRObjectFile>>
108+
getAsIRObject(LLVMContext &Ctx) const;
104109

105110
Expected<std::unique_ptr<Archive>> getAsArchive() const;
106111
};
@@ -154,6 +159,9 @@ class MachOUniversalBinary : public Binary {
154159
Expected<std::unique_ptr<MachOObjectFile>>
155160
getMachOObjectForArch(StringRef ArchName) const;
156161

162+
Expected<std::unique_ptr<IRObjectFile>>
163+
getIRObjectForArch(StringRef ArchName, LLVMContext &Ctx) const;
164+
157165
Expected<std::unique_ptr<Archive>>
158166
getArchiveForArch(StringRef ArchName) const;
159167
};

llvm/include/llvm/Object/MachOUniversalWriter.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
#include "llvm/Object/MachO.h"
2020

2121
namespace llvm {
22+
class LLVMContext;
23+
2224
namespace object {
25+
class IRObjectFile;
2326

2427
class Slice {
2528
const Binary *B;
@@ -32,12 +35,18 @@ class Slice {
3235
// file size can be calculated before creating the output buffer.
3336
uint32_t P2Alignment;
3437

38+
Slice(const IRObjectFile *IRO, uint32_t CPUType, uint32_t CPUSubType,
39+
std::string ArchName, uint32_t Align);
40+
3541
public:
3642
explicit Slice(const MachOObjectFile &O);
3743

3844
Slice(const MachOObjectFile &O, uint32_t Align);
3945

40-
static Expected<Slice> create(const Archive *A);
46+
static Expected<Slice> create(const Archive *A,
47+
LLVMContext *LLVMCtx = nullptr);
48+
49+
static Expected<Slice> create(const IRObjectFile *IRO, uint32_t Align);
4150

4251
void setP2Alignment(uint32_t Align) { P2Alignment = Align; }
4352

llvm/lib/Object/Binary.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ Expected<std::unique_ptr<Binary>> object::createBinary(MemoryBufferRef Buffer,
9393
llvm_unreachable("Unexpected Binary File Type");
9494
}
9595

96-
Expected<OwningBinary<Binary>> object::createBinary(StringRef Path) {
96+
Expected<OwningBinary<Binary>> object::createBinary(StringRef Path,
97+
LLVMContext *Context) {
9798
ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
9899
MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1,
99100
/*RequiresNullTerminator=*/false);
@@ -102,7 +103,7 @@ Expected<OwningBinary<Binary>> object::createBinary(StringRef Path) {
102103
std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get();
103104

104105
Expected<std::unique_ptr<Binary>> BinOrErr =
105-
createBinary(Buffer->getMemBufferRef());
106+
createBinary(Buffer->getMemBufferRef(), Context);
106107
if (!BinOrErr)
107108
return BinOrErr.takeError();
108109
std::unique_ptr<Binary> &Bin = BinOrErr.get();

llvm/lib/Object/MachOUniversal.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "llvm/Object/MachOUniversal.h"
1414
#include "llvm/Object/Archive.h"
15+
#include "llvm/Object/IRObjectFile.h"
1516
#include "llvm/Object/MachO.h"
1617
#include "llvm/Object/ObjectFile.h"
1718
#include "llvm/Support/Casting.h"
@@ -80,6 +81,25 @@ MachOUniversalBinary::ObjectForArch::getAsObjectFile() const {
8081
return ObjectFile::createMachOObjectFile(ObjBuffer, cputype, Index);
8182
}
8283

84+
Expected<std::unique_ptr<IRObjectFile>>
85+
MachOUniversalBinary::ObjectForArch::getAsIRObject(LLVMContext &Ctx) const {
86+
if (!Parent)
87+
report_fatal_error("MachOUniversalBinary::ObjectForArch::getAsIRObject() "
88+
"called when Parent is a nullptr");
89+
90+
StringRef ParentData = Parent->getData();
91+
StringRef ObjectData;
92+
if (Parent->getMagic() == MachO::FAT_MAGIC) {
93+
ObjectData = ParentData.substr(Header.offset, Header.size);
94+
} else { // Parent->getMagic() == MachO::FAT_MAGIC_64
95+
ObjectData = ParentData.substr(Header64.offset, Header64.size);
96+
}
97+
StringRef ObjectName = Parent->getFileName();
98+
MemoryBufferRef ObjBuffer(ObjectData, ObjectName);
99+
100+
return IRObjectFile::create(ObjBuffer, Ctx);
101+
}
102+
83103
Expected<std::unique_ptr<Archive>>
84104
MachOUniversalBinary::ObjectForArch::getAsArchive() const {
85105
if (!Parent)
@@ -234,6 +254,15 @@ MachOUniversalBinary::getMachOObjectForArch(StringRef ArchName) const {
234254
return O->getAsObjectFile();
235255
}
236256

257+
Expected<std::unique_ptr<IRObjectFile>>
258+
MachOUniversalBinary::getIRObjectForArch(StringRef ArchName,
259+
LLVMContext &Ctx) const {
260+
Expected<ObjectForArch> O = getObjectForArch(ArchName);
261+
if (!O)
262+
return O.takeError();
263+
return O->getAsIRObject(Ctx);
264+
}
265+
237266
Expected<std::unique_ptr<Archive>>
238267
MachOUniversalBinary::getArchiveForArch(StringRef ArchName) const {
239268
Expected<ObjectForArch> O = getObjectForArch(ArchName);

llvm/lib/Object/MachOUniversalWriter.cpp

Lines changed: 123 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "llvm/Object/MachOUniversalWriter.h"
15+
#include "llvm/ADT/Triple.h"
1516
#include "llvm/Object/Archive.h"
1617
#include "llvm/Object/Binary.h"
1718
#include "llvm/Object/Error.h"
19+
#include "llvm/Object/IRObjectFile.h"
1820
#include "llvm/Object/MachO.h"
1921
#include "llvm/Object/MachOUniversal.h"
2022
#include "llvm/Support/FileOutputBuffer.h"
@@ -79,13 +81,36 @@ Slice::Slice(const MachOObjectFile &O, uint32_t Align)
7981
ArchName(std::string(O.getArchTriple().getArchName())),
8082
P2Alignment(Align) {}
8183

84+
Slice::Slice(const IRObjectFile *IRO, uint32_t CPUType, uint32_t CPUSubType,
85+
std::string ArchName, uint32_t Align)
86+
: B(IRO), CPUType(CPUType), CPUSubType(CPUSubType),
87+
ArchName(std::move(ArchName)), P2Alignment(Align) {}
88+
8289
Slice::Slice(const MachOObjectFile &O) : Slice(O, calculateAlignment(O)) {}
8390

84-
Expected<Slice> Slice::create(const Archive *A) {
91+
using MachoCPUTy = std::pair<unsigned, unsigned>;
92+
93+
static Expected<MachoCPUTy> getMachoCPUFromTriple(Triple TT) {
94+
auto CPU = std::make_pair(MachO::getCPUType(TT), MachO::getCPUSubType(TT));
95+
if (!CPU.first) {
96+
return CPU.first.takeError();
97+
}
98+
if (!CPU.second) {
99+
return CPU.second.takeError();
100+
}
101+
return std::make_pair(*CPU.first, *CPU.second);
102+
}
103+
104+
static Expected<MachoCPUTy> getMachoCPUFromTriple(StringRef TT) {
105+
return getMachoCPUFromTriple(Triple{TT});
106+
}
107+
108+
Expected<Slice> Slice::create(const Archive *A, LLVMContext *LLVMCtx) {
85109
Error Err = Error::success();
86-
std::unique_ptr<MachOObjectFile> FO = nullptr;
110+
std::unique_ptr<MachOObjectFile> MFO = nullptr;
111+
std::unique_ptr<IRObjectFile> IRFO = nullptr;
87112
for (const Archive::Child &Child : A->children(Err)) {
88-
Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
113+
Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(LLVMCtx);
89114
if (!ChildOrErr)
90115
return createFileError(A->getFileName(), ChildOrErr.takeError());
91116
Binary *Bin = ChildOrErr.get().get();
@@ -95,46 +120,112 @@ Expected<Slice> Slice::create(const Archive *A) {
95120
" is a fat file (not allowed in an archive)")
96121
.str()
97122
.c_str());
98-
if (!Bin->isMachO())
99-
return createStringError(
100-
std::errc::invalid_argument,
101-
("archive member " + Bin->getFileName() +
102-
" is not a MachO file (not allowed in an archive)")
103-
.str()
104-
.c_str());
105-
MachOObjectFile *O = cast<MachOObjectFile>(Bin);
106-
if (FO && std::tie(FO->getHeader().cputype, FO->getHeader().cpusubtype) !=
107-
std::tie(O->getHeader().cputype, O->getHeader().cpusubtype)) {
108-
return createStringError(
109-
std::errc::invalid_argument,
110-
("archive member " + O->getFileName() + " cputype (" +
111-
Twine(O->getHeader().cputype) + ") and cpusubtype(" +
112-
Twine(O->getHeader().cpusubtype) +
113-
") does not match previous archive members cputype (" +
114-
Twine(FO->getHeader().cputype) + ") and cpusubtype(" +
115-
Twine(FO->getHeader().cpusubtype) + ") (all members must match) " +
116-
FO->getFileName())
117-
.str()
118-
.c_str());
119-
}
120-
if (!FO) {
121-
ChildOrErr.get().release();
122-
FO.reset(O);
123-
}
123+
if (Bin->isMachO()) {
124+
MachOObjectFile *O = cast<MachOObjectFile>(Bin);
125+
if (IRFO) {
126+
return createStringError(
127+
std::errc::invalid_argument,
128+
"archive member %s is a MachO, while previous archive member "
129+
"%s was an IR LLVM object",
130+
O->getFileName().str().c_str(), IRFO->getFileName().str().c_str());
131+
}
132+
if (MFO &&
133+
std::tie(MFO->getHeader().cputype, MFO->getHeader().cpusubtype) !=
134+
std::tie(O->getHeader().cputype, O->getHeader().cpusubtype)) {
135+
return createStringError(
136+
std::errc::invalid_argument,
137+
("archive member " + O->getFileName() + " cputype (" +
138+
Twine(O->getHeader().cputype) + ") and cpusubtype(" +
139+
Twine(O->getHeader().cpusubtype) +
140+
") does not match previous archive members cputype (" +
141+
Twine(MFO->getHeader().cputype) + ") and cpusubtype(" +
142+
Twine(MFO->getHeader().cpusubtype) +
143+
") (all members must match) " + MFO->getFileName())
144+
.str()
145+
.c_str());
146+
}
147+
if (!MFO) {
148+
ChildOrErr.get().release();
149+
MFO.reset(O);
150+
}
151+
} else if (Bin->isIR()) {
152+
IRObjectFile *O = cast<IRObjectFile>(Bin);
153+
if (MFO) {
154+
return createStringError(std::errc::invalid_argument,
155+
"archive member '%s' is an LLVM IR object, "
156+
"while previous archive member "
157+
"'%s' was a MachO",
158+
O->getFileName().str().c_str(),
159+
MFO->getFileName().str().c_str());
160+
}
161+
if (IRFO) {
162+
Expected<MachoCPUTy> CPUO = getMachoCPUFromTriple(O->getTargetTriple());
163+
Expected<MachoCPUTy> CPUFO =
164+
getMachoCPUFromTriple(IRFO->getTargetTriple());
165+
if (!CPUO)
166+
return CPUO.takeError();
167+
if (!CPUFO)
168+
return CPUFO.takeError();
169+
if (*CPUO != *CPUFO) {
170+
return createStringError(
171+
std::errc::invalid_argument,
172+
("archive member " + O->getFileName() + " cputype (" +
173+
Twine(CPUO->first) + ") and cpusubtype(" + Twine(CPUO->second) +
174+
") does not match previous archive members cputype (" +
175+
Twine(CPUFO->first) + ") and cpusubtype(" +
176+
Twine(CPUFO->second) + ") (all members must match) " +
177+
IRFO->getFileName())
178+
.str()
179+
.c_str());
180+
}
181+
} else {
182+
ChildOrErr.get().release();
183+
IRFO.reset(O);
184+
}
185+
} else
186+
return createStringError(std::errc::invalid_argument,
187+
("archive member " + Bin->getFileName() +
188+
" is neither a MachO file or an LLVM IR file "
189+
"(not allowed in an archive)")
190+
.str()
191+
.c_str());
124192
}
125193
if (Err)
126194
return createFileError(A->getFileName(), std::move(Err));
127-
if (!FO)
195+
if (!MFO && !IRFO)
128196
return createStringError(
129197
std::errc::invalid_argument,
130198
("empty archive with no architecture specification: " +
131199
A->getFileName() + " (can't determine architecture for it)")
132200
.str()
133201
.c_str());
134202

135-
Slice ArchiveSlice = Slice(*(FO.get()), FO->is64Bit() ? 3 : 2);
203+
if (MFO) {
204+
Slice ArchiveSlice(*(MFO.get()), MFO->is64Bit() ? 3 : 2);
205+
ArchiveSlice.B = A;
206+
return ArchiveSlice;
207+
}
208+
209+
// For IR objects
210+
Expected<Slice> ArchiveSliceOrErr = Slice::create(IRFO.get(), 0);
211+
if (!ArchiveSliceOrErr)
212+
return createFileError(A->getFileName(), ArchiveSliceOrErr.takeError());
213+
auto &ArchiveSlice = ArchiveSliceOrErr.get();
136214
ArchiveSlice.B = A;
137-
return ArchiveSlice;
215+
return Slice{std::move(ArchiveSlice)};
216+
}
217+
218+
Expected<Slice> Slice::create(const IRObjectFile *IRO, uint32_t Align) {
219+
Expected<MachoCPUTy> CPUOrErr = getMachoCPUFromTriple(IRO->getTargetTriple());
220+
if (!CPUOrErr)
221+
return CPUOrErr.takeError();
222+
unsigned CPUType, CPUSubType;
223+
std::tie(CPUType, CPUSubType) = CPUOrErr.get();
224+
// We don't directly use the architecture name of the target triple T, as,
225+
// for instance, thumb is treated as ARM by the MachOUniversal object.
226+
std::string ArchName(
227+
MachOObjectFile::getArchTriple(CPUType, CPUSubType).getArchName());
228+
return Slice{IRO, CPUType, CPUSubType, std::move(ArchName), Align};
138229
}
139230

140231
static Expected<SmallVector<MachO::fat_arch, 2>>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
target triple = "arm64-apple-ios8.0.0"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
target triple = "thumbv7-apple-ios8.0.0"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
target triple = "x86_64-apple-macosx10.15.0"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# RUN: llvm-as %p/Inputs/armv7-ios.ll -o %t-armv7.o
2+
# RUN: llvm-lipo -archs %t-armv7.o | FileCheck %s
3+
4+
# CHECK: armv7

0 commit comments

Comments
 (0)