Skip to content

Commit efb4651

Browse files
authored
[lipo] Support creating Universal 64 bit Mach-O files. (#67737)
Xcode `lipo` seems to support a non-documented `-fat64` option that creates Universal Mach-O archives using 64 bit versions of the `fat_arch` header, which allows offsets larger than 32 bits to be specified. Modify `llvm-lipo` to support the same flag, and use the value of the flag to use either 32 bits or 64 bits Mach-O headers. The Mach-O universal writer allows specifying a new option to write these 64 bits headers. The default is still using 32 bits. `dsymutil` implemented support for a similar flag in https://reviews.llvm.org/D146879.
1 parent b28d83e commit efb4651

File tree

5 files changed

+98
-30
lines changed

5 files changed

+98
-30
lines changed

llvm/include/llvm/Object/MachOUniversalWriter.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,14 @@ class Slice {
9797
}
9898
};
9999

100-
Error writeUniversalBinary(ArrayRef<Slice> Slices, StringRef OutputFileName);
100+
enum class FatHeaderType { FatHeader, Fat64Header };
101101

102-
Error writeUniversalBinaryToStream(ArrayRef<Slice> Slices, raw_ostream &Out);
102+
Error writeUniversalBinary(ArrayRef<Slice> Slices, StringRef OutputFileName,
103+
FatHeaderType FatHeader = FatHeaderType::FatHeader);
104+
105+
Error writeUniversalBinaryToStream(
106+
ArrayRef<Slice> Slices, raw_ostream &Out,
107+
FatHeaderType FatHeader = FatHeaderType::FatHeader);
103108

104109
} // end namespace object
105110

llvm/lib/Object/MachOUniversalWriter.cpp

Lines changed: 65 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -240,25 +240,48 @@ Expected<Slice> Slice::create(const IRObjectFile &IRO, uint32_t Align) {
240240
return Slice{IRO, CPUType, CPUSubType, std::move(ArchName), Align};
241241
}
242242

243-
static Expected<SmallVector<MachO::fat_arch, 2>>
243+
template <typename FatArchTy> struct FatArchTraits {
244+
static const uint64_t OffsetLimit;
245+
static const std::string StructName;
246+
static const uint8_t BitCount;
247+
};
248+
249+
template <> struct FatArchTraits<MachO::fat_arch> {
250+
static const uint64_t OffsetLimit = UINT32_MAX;
251+
static const std::string StructName;
252+
static const uint8_t BitCount = 32;
253+
};
254+
const std::string FatArchTraits<MachO::fat_arch>::StructName = "fat_arch";
255+
256+
template <> struct FatArchTraits<MachO::fat_arch_64> {
257+
static const uint64_t OffsetLimit = UINT64_MAX;
258+
static const std::string StructName;
259+
static const uint8_t BitCount = 64;
260+
};
261+
const std::string FatArchTraits<MachO::fat_arch_64>::StructName = "fat_arch_64";
262+
263+
template <typename FatArchTy>
264+
static Expected<SmallVector<FatArchTy, 2>>
244265
buildFatArchList(ArrayRef<Slice> Slices) {
245-
SmallVector<MachO::fat_arch, 2> FatArchList;
266+
SmallVector<FatArchTy, 2> FatArchList;
246267
uint64_t Offset =
247-
sizeof(MachO::fat_header) + Slices.size() * sizeof(MachO::fat_arch);
268+
sizeof(MachO::fat_header) + Slices.size() * sizeof(FatArchTy);
248269

249270
for (const auto &S : Slices) {
250271
Offset = alignTo(Offset, 1ull << S.getP2Alignment());
251-
if (Offset > UINT32_MAX)
272+
if (Offset > FatArchTraits<FatArchTy>::OffsetLimit)
252273
return createStringError(
253274
std::errc::invalid_argument,
254-
("fat file too large to be created because the offset "
255-
"field in struct fat_arch is only 32-bits and the offset " +
275+
("fat file too large to be created because the offset field in the "
276+
"struct " +
277+
Twine(FatArchTraits<FatArchTy>::StructName) + " is only " +
278+
Twine(FatArchTraits<FatArchTy>::BitCount) + "-bits and the offset " +
256279
Twine(Offset) + " for " + S.getBinary()->getFileName() +
257280
" for architecture " + S.getArchString() + "exceeds that.")
258281
.str()
259282
.c_str());
260283

261-
MachO::fat_arch FatArch;
284+
FatArchTy FatArch;
262285
FatArch.cputype = S.getCPUType();
263286
FatArch.cpusubtype = S.getCPUSubType();
264287
FatArch.offset = Offset;
@@ -270,35 +293,33 @@ buildFatArchList(ArrayRef<Slice> Slices) {
270293
return FatArchList;
271294
}
272295

273-
Error object::writeUniversalBinaryToStream(ArrayRef<Slice> Slices,
274-
raw_ostream &Out) {
275-
MachO::fat_header FatHeader;
276-
FatHeader.magic = MachO::FAT_MAGIC;
277-
FatHeader.nfat_arch = Slices.size();
278-
279-
Expected<SmallVector<MachO::fat_arch, 2>> FatArchListOrErr =
280-
buildFatArchList(Slices);
296+
template <typename FatArchTy>
297+
static Error writeUniversalArchsToStream(MachO::fat_header FatHeader,
298+
ArrayRef<Slice> Slices,
299+
raw_ostream &Out) {
300+
Expected<SmallVector<FatArchTy, 2>> FatArchListOrErr =
301+
buildFatArchList<FatArchTy>(Slices);
281302
if (!FatArchListOrErr)
282303
return FatArchListOrErr.takeError();
283-
SmallVector<MachO::fat_arch, 2> FatArchList = *FatArchListOrErr;
304+
SmallVector<FatArchTy, 2> FatArchList = *FatArchListOrErr;
284305

285306
if (sys::IsLittleEndianHost)
286307
MachO::swapStruct(FatHeader);
287308
Out.write(reinterpret_cast<const char *>(&FatHeader),
288309
sizeof(MachO::fat_header));
289310

290311
if (sys::IsLittleEndianHost)
291-
for (MachO::fat_arch &FA : FatArchList)
312+
for (FatArchTy &FA : FatArchList)
292313
MachO::swapStruct(FA);
293314
Out.write(reinterpret_cast<const char *>(FatArchList.data()),
294-
sizeof(MachO::fat_arch) * FatArchList.size());
315+
sizeof(FatArchTy) * FatArchList.size());
295316

296317
if (sys::IsLittleEndianHost)
297-
for (MachO::fat_arch &FA : FatArchList)
318+
for (FatArchTy &FA : FatArchList)
298319
MachO::swapStruct(FA);
299320

300321
size_t Offset =
301-
sizeof(MachO::fat_header) + sizeof(MachO::fat_arch) * FatArchList.size();
322+
sizeof(MachO::fat_header) + sizeof(FatArchTy) * FatArchList.size();
302323
for (size_t Index = 0, Size = Slices.size(); Index < Size; ++Index) {
303324
MemoryBufferRef BufferRef = Slices[Index].getBinary()->getMemoryBufferRef();
304325
assert((Offset <= FatArchList[Index].offset) && "Incorrect slice offset");
@@ -311,8 +332,30 @@ Error object::writeUniversalBinaryToStream(ArrayRef<Slice> Slices,
311332
return Error::success();
312333
}
313334

335+
Error object::writeUniversalBinaryToStream(ArrayRef<Slice> Slices,
336+
raw_ostream &Out,
337+
FatHeaderType HeaderType) {
338+
MachO::fat_header FatHeader;
339+
FatHeader.nfat_arch = Slices.size();
340+
341+
switch (HeaderType) {
342+
case FatHeaderType::Fat64Header:
343+
FatHeader.magic = MachO::FAT_MAGIC_64;
344+
return writeUniversalArchsToStream<MachO::fat_arch_64>(FatHeader, Slices,
345+
Out);
346+
break;
347+
case FatHeaderType::FatHeader:
348+
FatHeader.magic = MachO::FAT_MAGIC;
349+
return writeUniversalArchsToStream<MachO::fat_arch>(FatHeader, Slices, Out);
350+
break;
351+
default:
352+
llvm_unreachable("Invalid fat header type");
353+
}
354+
}
355+
314356
Error object::writeUniversalBinary(ArrayRef<Slice> Slices,
315-
StringRef OutputFileName) {
357+
StringRef OutputFileName,
358+
FatHeaderType HeaderType) {
316359
const bool IsExecutable = any_of(Slices, [](Slice S) {
317360
return sys::fs::can_execute(S.getBinary()->getFileName());
318361
});
@@ -324,7 +367,7 @@ Error object::writeUniversalBinary(ArrayRef<Slice> Slices,
324367
if (!Temp)
325368
return Temp.takeError();
326369
raw_fd_ostream Out(Temp->FD, false);
327-
if (Error E = writeUniversalBinaryToStream(Slices, Out)) {
370+
if (Error E = writeUniversalBinaryToStream(Slices, Out, HeaderType)) {
328371
if (Error DiscardError = Temp->discard())
329372
return joinErrors(std::move(E), std::move(DiscardError));
330373
return E;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# RUN: yaml2obj %p/Inputs/i386-slice.yaml -o %t-i386.o
2+
# RUN: yaml2obj %p/Inputs/x86_64-slice.yaml -o %t-x86_64.o
3+
4+
# RUN: llvm-lipo %t-i386.o %t-x86_64.o -create -output %t-universal-32.o
5+
# RUN: llvm-objdump -m --universal-headers %t-universal-32.o | FileCheck %s -check-prefixes=FAT32
6+
7+
# RUN: llvm-lipo %t-i386.o %t-x86_64.o -create -fat64 -output %t-universal-64.o
8+
# RUN: llvm-objdump -m --universal-headers %t-universal-64.o | FileCheck %s -check-prefixes=FAT64
9+
10+
FAT32: fat_magic FAT_MAGIC
11+
FAT64: fat_magic FAT_MAGIC_64

llvm/tools/llvm-lipo/LipoOpts.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,6 @@ def replace
5858
def output : Option<["-", "--"], "output", KIND_SEPARATE>,
5959
HelpText<"Create output file with specified name">;
6060
def o : JoinedOrSeparate<["-"], "o">, Alias<output>;
61+
62+
def fat64 : Option<["-", "--"], "fat64", KIND_FLAG>,
63+
HelpText<"Use 64 bits Universal Mach-O format">;

llvm/tools/llvm-lipo/llvm-lipo.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ struct Config {
116116
std::string ArchType;
117117
std::string OutputFile;
118118
LipoAction ActionToPerform;
119+
bool UseFat64;
119120
};
120121

121122
static Slice createSliceFromArchive(LLVMContext &LLVMCtx, const Archive &A) {
@@ -223,6 +224,8 @@ static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
223224
Twine(AlignmentValue));
224225
}
225226

227+
C.UseFat64 = InputArgs.hasArg(LIPO_fat64);
228+
226229
SmallVector<opt::Arg *, 1> ActionArgs(InputArgs.filtered(LIPO_action_group));
227230
if (ActionArgs.empty())
228231
reportError("at least one action should be specified");
@@ -596,9 +599,11 @@ buildSlices(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries,
596599
return Slices;
597600
}
598601

599-
[[noreturn]] static void createUniversalBinary(
600-
LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries,
601-
const StringMap<const uint32_t> &Alignments, StringRef OutputFileName) {
602+
[[noreturn]] static void
603+
createUniversalBinary(LLVMContext &LLVMCtx,
604+
ArrayRef<OwningBinary<Binary>> InputBinaries,
605+
const StringMap<const uint32_t> &Alignments,
606+
StringRef OutputFileName, FatHeaderType HeaderType) {
602607
assert(InputBinaries.size() >= 1 && "Incorrect number of input binaries");
603608
assert(!OutputFileName.empty() && "Create expects a single output file");
604609

@@ -609,7 +614,7 @@ buildSlices(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries,
609614
checkUnusedAlignments(Slices, Alignments);
610615

611616
llvm::stable_sort(Slices);
612-
if (Error E = writeUniversalBinary(Slices, OutputFileName))
617+
if (Error E = writeUniversalBinary(Slices, OutputFileName, HeaderType))
613618
reportError(std::move(E));
614619

615620
exit(EXIT_SUCCESS);
@@ -747,8 +752,9 @@ int llvm_lipo_main(int argc, char **argv, const llvm::ToolContext &) {
747752
C.OutputFile);
748753
break;
749754
case LipoAction::CreateUniversal:
750-
createUniversalBinary(LLVMCtx, InputBinaries, C.SegmentAlignments,
751-
C.OutputFile);
755+
createUniversalBinary(
756+
LLVMCtx, InputBinaries, C.SegmentAlignments, C.OutputFile,
757+
C.UseFat64 ? FatHeaderType::Fat64Header : FatHeaderType::FatHeader);
752758
break;
753759
case LipoAction::ReplaceArch:
754760
replaceSlices(LLVMCtx, InputBinaries, C.SegmentAlignments, C.OutputFile,

0 commit comments

Comments
 (0)