Skip to content

Commit b116747

Browse files
authored
[Object] Remove restriction universal archives having both IR and native (#67505)
Mach-O archives seems to be able to contain both IR objects and native objects mixed together. Apple tooling seems to deal with them correctly. The current implementation was adding an additional restriction of all the objects in the archive being either IR objects or native objects. The changes in this commit remove that restriction and allow mixing both IR and native objects, while still checking that the CPU restrictions still apply (all objects in a slice need to be the same CPU type/subtype). A test that was testing for the previous behaviour had been modified to test that mixed archives are allowed and that they create the expected results. Additionally, locally I checked the results of Apple's `libtool` against `llvm-libtool-darwin` with this code, and the resulting libraries are almost identical with expected differences in the GUID and code signatures load commands, and some minor differences in the rest of the binary.
1 parent e897014 commit b116747

File tree

2 files changed

+56
-50
lines changed

2 files changed

+56
-50
lines changed

llvm/lib/Object/MachOUniversalWriter.cpp

Lines changed: 40 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ Slice::Slice(const IRObjectFile &IRO, uint32_t CPUType, uint32_t CPUSubType,
100100

101101
Slice::Slice(const MachOObjectFile &O) : Slice(O, calculateAlignment(O)) {}
102102

103-
using MachoCPUTy = std::pair<unsigned, unsigned>;
103+
using MachoCPUTy = std::pair<uint32_t, uint32_t>;
104104

105105
static Expected<MachoCPUTy> getMachoCPUFromTriple(Triple TT) {
106106
auto CPU = std::make_pair(MachO::getCPUType(TT), MachO::getCPUSubType(TT));
@@ -117,10 +117,15 @@ static Expected<MachoCPUTy> getMachoCPUFromTriple(StringRef TT) {
117117
return getMachoCPUFromTriple(Triple{TT});
118118
}
119119

120+
static MachoCPUTy getMachoCPUFromObjectFile(const MachOObjectFile &O) {
121+
return std::make_pair(O.getHeader().cputype, O.getHeader().cpusubtype);
122+
}
123+
120124
Expected<Slice> Slice::create(const Archive &A, LLVMContext *LLVMCtx) {
121125
Error Err = Error::success();
122126
std::unique_ptr<MachOObjectFile> MFO = nullptr;
123127
std::unique_ptr<IRObjectFile> IRFO = nullptr;
128+
std::optional<MachoCPUTy> CPU = std::nullopt;
124129
for (const Archive::Child &Child : A.children(Err)) {
125130
Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(LLVMCtx);
126131
if (!ChildOrErr)
@@ -134,65 +139,56 @@ Expected<Slice> Slice::create(const Archive &A, LLVMContext *LLVMCtx) {
134139
.c_str());
135140
if (Bin->isMachO()) {
136141
MachOObjectFile *O = cast<MachOObjectFile>(Bin);
137-
if (IRFO) {
138-
return createStringError(
139-
std::errc::invalid_argument,
140-
"archive member %s is a MachO, while previous archive member "
141-
"%s was an IR LLVM object",
142-
O->getFileName().str().c_str(), IRFO->getFileName().str().c_str());
143-
}
144-
if (MFO &&
145-
std::tie(MFO->getHeader().cputype, MFO->getHeader().cpusubtype) !=
146-
std::tie(O->getHeader().cputype, O->getHeader().cpusubtype)) {
142+
MachoCPUTy ObjectCPU = getMachoCPUFromObjectFile(*O);
143+
144+
if (CPU && CPU != ObjectCPU) {
145+
// If CPU != nullptr, one of MFO, IRFO will be != nullptr.
146+
StringRef PreviousName = MFO ? MFO->getFileName() : IRFO->getFileName();
147147
return createStringError(
148148
std::errc::invalid_argument,
149149
("archive member " + O->getFileName() + " cputype (" +
150-
Twine(O->getHeader().cputype) + ") and cpusubtype(" +
151-
Twine(O->getHeader().cpusubtype) +
150+
Twine(ObjectCPU.first) + ") and cpusubtype(" +
151+
Twine(ObjectCPU.second) +
152152
") does not match previous archive members cputype (" +
153-
Twine(MFO->getHeader().cputype) + ") and cpusubtype(" +
154-
Twine(MFO->getHeader().cpusubtype) +
155-
") (all members must match) " + MFO->getFileName())
153+
Twine(CPU->first) + ") and cpusubtype(" + Twine(CPU->second) +
154+
") (all members must match) " + PreviousName)
156155
.str()
157156
.c_str());
158157
}
159158
if (!MFO) {
160159
ChildOrErr.get().release();
161160
MFO.reset(O);
161+
if (!CPU)
162+
CPU.emplace(ObjectCPU);
162163
}
163164
} else if (Bin->isIR()) {
164165
IRObjectFile *O = cast<IRObjectFile>(Bin);
165-
if (MFO) {
166-
return createStringError(std::errc::invalid_argument,
167-
"archive member '%s' is an LLVM IR object, "
168-
"while previous archive member "
169-
"'%s' was a MachO",
170-
O->getFileName().str().c_str(),
171-
MFO->getFileName().str().c_str());
166+
Expected<MachoCPUTy> ObjectCPU =
167+
getMachoCPUFromTriple(O->getTargetTriple());
168+
if (!ObjectCPU)
169+
return ObjectCPU.takeError();
170+
171+
if (CPU && CPU != *ObjectCPU) {
172+
// If CPU != nullptr, one of MFO, IRFO will be != nullptr.
173+
StringRef PreviousName =
174+
IRFO ? IRFO->getFileName() : MFO->getFileName();
175+
return createStringError(
176+
std::errc::invalid_argument,
177+
("archive member " + O->getFileName() + " cputype (" +
178+
Twine(ObjectCPU->first) + ") and cpusubtype(" +
179+
Twine(ObjectCPU->second) +
180+
") does not match previous archive members cputype (" +
181+
Twine(CPU->first) + ") and cpusubtype(" + Twine(CPU->second) +
182+
") (all members must match) " + PreviousName)
183+
.str()
184+
.c_str());
172185
}
173-
if (IRFO) {
174-
Expected<MachoCPUTy> CPUO = getMachoCPUFromTriple(O->getTargetTriple());
175-
Expected<MachoCPUTy> CPUFO =
176-
getMachoCPUFromTriple(IRFO->getTargetTriple());
177-
if (!CPUO)
178-
return CPUO.takeError();
179-
if (!CPUFO)
180-
return CPUFO.takeError();
181-
if (*CPUO != *CPUFO) {
182-
return createStringError(
183-
std::errc::invalid_argument,
184-
("archive member " + O->getFileName() + " cputype (" +
185-
Twine(CPUO->first) + ") and cpusubtype(" + Twine(CPUO->second) +
186-
") does not match previous archive members cputype (" +
187-
Twine(CPUFO->first) + ") and cpusubtype(" +
188-
Twine(CPUFO->second) + ") (all members must match) " +
189-
IRFO->getFileName())
190-
.str()
191-
.c_str());
192-
}
193-
} else {
186+
187+
if (!IRFO) {
194188
ChildOrErr.get().release();
195189
IRFO.reset(O);
190+
if (!CPU)
191+
CPU.emplace(*ObjectCPU);
196192
}
197193
} else
198194
return createStringError(std::errc::invalid_argument,

llvm/test/tools/llvm-lipo/create-archive-input.test

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,27 @@
2828
# RUN: llvm-lipo %t-ir-armv7-x86_64-universal.o -thin x86_64 -output %t-ir-extracted-x86_64.o
2929
# RUN: cmp %t-ir-extracted-x86_64.o %t-ir-x86_64.o
3030

31-
# RUN: llvm-ar cr %t.different_types0.a %t-i386.o %t-ir-x86_64.o
32-
# RUN: not llvm-lipo -create %t.different_types0.a -output /dev/null 2>&1 | FileCheck --check-prefix=ARCHIVE-WITH-MACHO-AND-IR %s
33-
# RUN: llvm-ar cr %t.different_types1.a %t-ir-x86_64.o %t-i386.o
34-
# RUN: not llvm-lipo -create %t.different_types1.a -output /dev/null 2>&1 | FileCheck --check-prefix=ARCHIVE-WITH-IR-AND-MACHO %s
31+
# RUN: llvm-ar cr %t.different_types0-i386.a %t-i386.o
32+
# RUN: llvm-ar cr %t.different_types0-x86_64.a %t-x86_64.o %t-ir-x86_64.o
33+
# RUN: llvm-lipo -create %t.different_types0-i386.a %t.different_types0-x86_64.a -output %t.different_types0-universal.a
34+
# RUN: llvm-lipo %t.different_types0-universal.a -thin i386 -output %t.different_types0-extracted-i386.a
35+
# RUN: llvm-lipo %t.different_types0-universal.a -thin x86_64 -output %t.different_types0-extracted-x86_64.a
36+
# RUN: cmp %t.different_types0-extracted-i386.a %t.different_types0-i386.a
37+
# RUN: cmp %t.different_types0-extracted-x86_64.a %t.different_types0-x86_64.a
38+
39+
# RUN: llvm-ar cr %t.different_types1-i386.a %t-i386.o
40+
# RUN: llvm-ar cr %t.different_types1-x86_64.a %t-ir-x86_64.o %t-x86_64.o
41+
# RUN: llvm-lipo -create %t.different_types1-x86_64.a %t.different_types1-i386.a -output %t.different_types1-universal.a
42+
# RUN: llvm-lipo %t.different_types1-universal.a -thin i386 -output %t.different_types1-extracted-i386.a
43+
# RUN: llvm-lipo %t.different_types1-universal.a -thin x86_64 -output %t.different_types1-extracted-x86_64.a
44+
# RUN: cmp %t.different_types1-extracted-i386.a %t.different_types1-i386.a
45+
# RUN: cmp %t.different_types1-extracted-x86_64.a %t.different_types1-x86_64.a
46+
3547
# RUN: llvm-ar cr %t.different_architectures_ir.a %t-ir-x86_64.o %t-ir-armv7.o
3648
# RUN: not llvm-lipo -create %t.different_architectures_ir.a -output /dev/null 2>&1 | FileCheck --check-prefix=ARCHIVE-WITH-DIFFERENT-ARCHS %s
3749

3850
# EMPTY-ARCHIVE: empty archive
3951
# ARCHIVE-WITH-DIFFERENT-ARCHS: all members must match
40-
# ARCHIVE-WITH-MACHO-AND-IR: is an LLVM IR object, while previous archive member {{.*}} was a MachO
41-
# ARCHIVE-WITH-IR-AND-MACHO: is a MachO, while previous archive member {{.*}} was an IR LLVM object
4252
# ARCHIVE-WITH-FAT-BINARY: fat file (not allowed in an archive)
4353
#
4454
# INFO-i386-x86_64: i386 x86_64

0 commit comments

Comments
 (0)