1 //===-- llvm-lipo.cpp - a tool for manipulating universal binaries --------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // A utility for creating / splitting / inspecting universal binaries.
11 //===----------------------------------------------------------------------===//
13 #include "llvm/ADT/STLExtras.h"
14 #include "llvm/ADT/Triple.h"
15 #include "llvm/Object/Binary.h"
16 #include "llvm/Object/MachO.h"
17 #include "llvm/Object/MachOUniversal.h"
18 #include "llvm/Object/ObjectFile.h"
19 #include "llvm/Option/Arg.h"
20 #include "llvm/Option/ArgList.h"
21 #include "llvm/Support/CommandLine.h"
22 #include "llvm/Support/Error.h"
23 #include "llvm/Support/FileOutputBuffer.h"
24 #include "llvm/Support/InitLLVM.h"
25 #include "llvm/Support/WithColor.h"
28 using namespace llvm::object
;
30 static const StringRef ToolName
= "llvm-lipo";
32 LLVM_ATTRIBUTE_NORETURN
static void reportError(Twine Message
) {
33 WithColor::error(errs(), ToolName
) << Message
<< "\n";
38 LLVM_ATTRIBUTE_NORETURN
static void reportError(StringRef File
, Error E
) {
41 raw_string_ostream
OS(Buf
);
42 logAllUnhandledErrors(std::move(E
), OS
);
44 WithColor::error(errs(), ToolName
) << "'" << File
<< "': " << Buf
;
50 LIPO_INVALID
= 0, // This is not an option ID.
51 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
52 HELPTEXT, METAVAR, VALUES) \
54 #include "LipoOpts.inc"
58 // LipoInfoTable below references LIPO_##PREFIX. OptionGroup has prefix nullptr.
59 const char *const *LIPO_nullptr
= nullptr;
60 #define PREFIX(NAME, VALUE) const char *const LIPO_##NAME[] = VALUE;
61 #include "LipoOpts.inc"
64 static const opt::OptTable::Info LipoInfoTable
[] = {
65 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
66 HELPTEXT, METAVAR, VALUES) \
67 {LIPO_##PREFIX, NAME, HELPTEXT, \
68 METAVAR, LIPO_##ID, opt::Option::KIND##Class, \
69 PARAM, FLAGS, LIPO_##GROUP, \
70 LIPO_##ALIAS, ALIASARGS, VALUES},
71 #include "LipoOpts.inc"
75 class LipoOptTable
: public opt::OptTable
{
77 LipoOptTable() : OptTable(LipoInfoTable
) {}
80 enum class LipoAction
{
95 SmallVector
<std::string
, 1> InputFiles
;
96 SmallVector
<std::string
, 1> VerifyArchList
;
97 SmallVector
<Replacement
, 1> Replacements
;
98 StringMap
<const uint32_t> SegmentAlignments
;
99 std::string ThinArchType
;
100 std::string OutputFile
;
101 LipoAction ActionToPerform
;
105 const MachOObjectFile
*ObjectFile
;
106 // Requires Alignment field to store slice alignment values from universal
107 // binaries. Also needed to order the slices using compareSlices, so the total
108 // file size can be calculated before creating the output buffer.
114 static void validateArchitectureName(StringRef ArchitectureName
) {
115 if (!MachOObjectFile::isValidArch(ArchitectureName
)) {
117 raw_string_ostream
OS(Buf
);
118 OS
<< "Invalid architecture: " << ArchitectureName
119 << "\nValid architecture names are:";
120 for (auto arch
: MachOObjectFile::getValidArchs())
122 reportError(OS
.str());
126 static Config
parseLipoOptions(ArrayRef
<const char *> ArgsArr
) {
129 unsigned MissingArgumentIndex
, MissingArgumentCount
;
130 opt::InputArgList InputArgs
=
131 T
.ParseArgs(ArgsArr
, MissingArgumentIndex
, MissingArgumentCount
);
133 if (MissingArgumentCount
)
134 reportError("missing argument to " +
135 StringRef(InputArgs
.getArgString(MissingArgumentIndex
)) +
138 if (InputArgs
.size() == 0) {
139 // PrintHelp does not accept Twine.
140 T
.PrintHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
144 if (InputArgs
.hasArg(LIPO_help
)) {
145 // PrintHelp does not accept Twine.
146 T
.PrintHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
150 if (InputArgs
.hasArg(LIPO_version
)) {
151 outs() << ToolName
+ "\n";
152 cl::PrintVersionMessage();
156 for (auto Arg
: InputArgs
.filtered(LIPO_UNKNOWN
))
157 reportError("unknown argument '" + Arg
->getAsString(InputArgs
) + "'");
159 for (auto Arg
: InputArgs
.filtered(LIPO_INPUT
))
160 C
.InputFiles
.push_back(Arg
->getValue());
161 if (C
.InputFiles
.empty())
162 reportError("at least one input file should be specified");
164 if (InputArgs
.hasArg(LIPO_output
))
165 C
.OutputFile
= InputArgs
.getLastArgValue(LIPO_output
);
167 for (auto Segalign
: InputArgs
.filtered(LIPO_segalign
)) {
168 if (!Segalign
->getValue(1))
169 reportError("segalign is missing an argument: expects -segalign "
170 "arch_type alignment_value");
172 validateArchitectureName(Segalign
->getValue(0));
174 uint32_t AlignmentValue
;
175 if (!to_integer
<uint32_t>(Segalign
->getValue(1), AlignmentValue
, 16))
176 reportError("argument to -segalign <arch_type> " +
177 Twine(Segalign
->getValue(1)) +
178 " (hex) is not a proper hexadecimal number");
179 if (!isPowerOf2_32(AlignmentValue
))
180 reportError("argument to -segalign <arch_type> " +
181 Twine(Segalign
->getValue(1)) +
182 " (hex) must be a non-zero power of two");
183 if (Log2_32(AlignmentValue
) > MachOUniversalBinary::MaxSectionAlignment
)
185 "argument to -segalign <arch_type> " + Twine(Segalign
->getValue(1)) +
186 " (hex) must be less than or equal to the maximum section align 2^" +
187 Twine(MachOUniversalBinary::MaxSectionAlignment
));
188 auto Entry
= C
.SegmentAlignments
.try_emplace(Segalign
->getValue(0),
189 Log2_32(AlignmentValue
));
191 reportError("-segalign " + Twine(Segalign
->getValue(0)) +
192 " <alignment_value> specified multiple times: " +
193 Twine(1 << Entry
.first
->second
) + ", " +
194 Twine(AlignmentValue
));
197 SmallVector
<opt::Arg
*, 1> ActionArgs(InputArgs
.filtered(LIPO_action_group
));
198 if (ActionArgs
.empty())
199 reportError("at least one action should be specified");
200 // errors if multiple actions specified other than replace
201 // multiple replace flags may be specified, as long as they are not mixed with
202 // other action flags
203 auto ReplacementArgsRange
= InputArgs
.filtered(LIPO_replace
);
204 if (ActionArgs
.size() > 1 &&
206 static_cast<size_t>(std::distance(ReplacementArgsRange
.begin(),
207 ReplacementArgsRange
.end()))) {
209 raw_string_ostream
OS(Buf
);
210 OS
<< "only one of the following actions can be specified:";
211 for (auto Arg
: ActionArgs
)
212 OS
<< " " << Arg
->getSpelling();
213 reportError(OS
.str());
216 switch (ActionArgs
[0]->getOption().getID()) {
217 case LIPO_verify_arch
:
218 for (auto A
: InputArgs
.getAllArgValues(LIPO_verify_arch
))
219 C
.VerifyArchList
.push_back(A
);
220 if (C
.VerifyArchList
.empty())
222 "verify_arch requires at least one architecture to be specified");
223 if (C
.InputFiles
.size() > 1)
224 reportError("verify_arch expects a single input file");
225 C
.ActionToPerform
= LipoAction::VerifyArch
;
229 if (C
.InputFiles
.size() > 1)
230 reportError("archs expects a single input file");
231 C
.ActionToPerform
= LipoAction::PrintArchs
;
235 C
.ActionToPerform
= LipoAction::PrintInfo
;
239 if (C
.InputFiles
.size() > 1)
240 reportError("thin expects a single input file");
241 C
.ThinArchType
= ActionArgs
[0]->getValue();
242 validateArchitectureName(C
.ThinArchType
);
243 if (C
.OutputFile
.empty())
244 reportError("thin expects a single output file");
246 C
.ActionToPerform
= LipoAction::ThinArch
;
250 if (C
.OutputFile
.empty())
251 reportError("create expects a single output file to be specified");
252 C
.ActionToPerform
= LipoAction::CreateUniversal
;
256 for (auto Action
: ActionArgs
) {
257 if (!Action
->getValue(1))
259 "replace is missing an argument: expects -replace arch_type "
261 C
.Replacements
.push_back(
262 Replacement
{Action
->getValue(0), Action
->getValue(1)});
265 if (C
.OutputFile
.empty())
266 reportError("replace expects a single output file to be specified");
267 if (C
.InputFiles
.size() > 1)
268 reportError("replace expects a single input file");
269 C
.ActionToPerform
= LipoAction::ReplaceArch
;
273 reportError("llvm-lipo action unspecified");
277 static SmallVector
<OwningBinary
<Binary
>, 1>
278 readInputBinaries(ArrayRef
<std::string
> InputFiles
) {
279 SmallVector
<OwningBinary
<Binary
>, 1> InputBinaries
;
280 for (StringRef InputFile
: InputFiles
) {
281 Expected
<OwningBinary
<Binary
>> BinaryOrErr
= createBinary(InputFile
);
283 reportError(InputFile
, BinaryOrErr
.takeError());
284 // TODO: Add compatibility for archive files
285 if (BinaryOrErr
->getBinary()->isArchive())
286 reportError("File " + InputFile
+
287 " is an archive file and is not yet supported.");
288 if (!BinaryOrErr
->getBinary()->isMachO() &&
289 !BinaryOrErr
->getBinary()->isMachOUniversalBinary())
290 reportError("File " + InputFile
+ " has unsupported binary format");
291 InputBinaries
.push_back(std::move(*BinaryOrErr
));
293 return InputBinaries
;
296 LLVM_ATTRIBUTE_NORETURN
297 static void verifyArch(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
298 ArrayRef
<std::string
> VerifyArchList
) {
299 assert(!VerifyArchList
.empty() &&
300 "The list of architectures should be non-empty");
301 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
303 for (StringRef Arch
: VerifyArchList
)
304 validateArchitectureName(Arch
);
307 dyn_cast
<MachOUniversalBinary
>(InputBinaries
.front().getBinary())) {
308 for (StringRef Arch
: VerifyArchList
) {
309 Expected
<std::unique_ptr
<MachOObjectFile
>> Obj
=
310 UO
->getObjectForArch(Arch
);
315 dyn_cast
<MachOObjectFile
>(InputBinaries
.front().getBinary())) {
316 const Triple::ArchType ObjectArch
= O
->getArch();
317 for (StringRef Arch
: VerifyArchList
)
318 if (ObjectArch
!= Triple(Arch
).getArch())
321 llvm_unreachable("Unexpected binary format");
326 // Returns a string of the given Object file's architecture type
327 // Unknown architectures formatted unknown(CPUType,CPUSubType) for compatibility
329 static std::string
getArchString(const MachOObjectFile
&ObjectFile
) {
330 const Triple T
= ObjectFile
.getArchTriple();
331 const StringRef ObjectArch
= T
.getArchName();
332 if (!ObjectArch
.empty())
334 return ("unknown(" + Twine(ObjectFile
.getHeader().cputype
) + "," +
335 Twine(ObjectFile
.getHeader().cpusubtype
& ~MachO::CPU_SUBTYPE_MASK
) +
340 static void printBinaryArchs(const Binary
*Binary
, raw_ostream
&OS
) {
341 // Prints trailing space for compatibility with cctools lipo.
342 if (auto UO
= dyn_cast
<MachOUniversalBinary
>(Binary
)) {
343 for (const auto &O
: UO
->objects()) {
344 Expected
<std::unique_ptr
<MachOObjectFile
>> BinaryOrError
=
347 reportError(Binary
->getFileName(), BinaryOrError
.takeError());
348 OS
<< getArchString(*BinaryOrError
.get().get()) << " ";
353 OS
<< getArchString(*cast
<MachOObjectFile
>(Binary
)) << " \n";
356 LLVM_ATTRIBUTE_NORETURN
357 static void printArchs(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
) {
358 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
359 printBinaryArchs(InputBinaries
.front().getBinary(), outs());
363 LLVM_ATTRIBUTE_NORETURN
364 static void printInfo(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
) {
365 // Group universal and thin files together for compatibility with cctools lipo
366 for (auto &IB
: InputBinaries
) {
367 const Binary
*Binary
= IB
.getBinary();
368 if (Binary
->isMachOUniversalBinary()) {
369 outs() << "Architectures in the fat file: " << Binary
->getFileName()
371 printBinaryArchs(Binary
, outs());
374 for (auto &IB
: InputBinaries
) {
375 const Binary
*Binary
= IB
.getBinary();
376 if (!Binary
->isMachOUniversalBinary()) {
377 assert(Binary
->isMachO() && "expected MachO binary");
378 outs() << "Non-fat file: " << Binary
->getFileName()
379 << " is architecture: ";
380 printBinaryArchs(Binary
, outs());
386 LLVM_ATTRIBUTE_NORETURN
387 static void extractSlice(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
388 StringRef ThinArchType
, StringRef OutputFileName
) {
389 assert(!ThinArchType
.empty() && "The architecture type should be non-empty");
390 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
391 assert(!OutputFileName
.empty() && "Thin expects a single output file");
393 if (InputBinaries
.front().getBinary()->isMachO()) {
394 reportError("input file " +
395 InputBinaries
.front().getBinary()->getFileName() +
396 " must be a fat file when the -thin option is specified");
400 auto *UO
= cast
<MachOUniversalBinary
>(InputBinaries
.front().getBinary());
401 Expected
<std::unique_ptr
<MachOObjectFile
>> Obj
=
402 UO
->getObjectForArch(ThinArchType
);
404 reportError("fat input file " + UO
->getFileName() +
405 " does not contain the specified architecture " + ThinArchType
+
408 Expected
<std::unique_ptr
<FileOutputBuffer
>> OutFileOrError
=
409 FileOutputBuffer::create(OutputFileName
,
410 Obj
.get()->getMemoryBufferRef().getBufferSize(),
411 sys::fs::can_execute(UO
->getFileName())
412 ? FileOutputBuffer::F_executable
415 reportError(OutputFileName
, OutFileOrError
.takeError());
416 std::copy(Obj
.get()->getMemoryBufferRef().getBufferStart(),
417 Obj
.get()->getMemoryBufferRef().getBufferEnd(),
418 OutFileOrError
.get()->getBufferStart());
419 if (Error E
= OutFileOrError
.get()->commit())
420 reportError(OutputFileName
, std::move(E
));
424 static void checkArchDuplicates(const ArrayRef
<Slice
> &Slices
) {
425 DenseMap
<uint64_t, const MachOObjectFile
*> CPUIds
;
426 auto CPUIDForSlice
= [](const Slice
&S
) {
427 return static_cast<uint64_t>(S
.ObjectFile
->getHeader().cputype
) << 32 |
428 S
.ObjectFile
->getHeader().cpusubtype
;
430 for (const auto &S
: Slices
) {
431 auto Entry
= CPUIds
.try_emplace(CPUIDForSlice(S
), S
.ObjectFile
);
433 reportError(Entry
.first
->second
->getFileName() + " and " +
434 S
.ObjectFile
->getFileName() + " have the same architecture " +
435 getArchString(*S
.ObjectFile
) +
436 " and therefore cannot be in the same universal binary");
440 // For compatibility with cctools lipo, alignment is calculated as the minimum
441 // aligment of all segments. Each segments's alignment is the maximum alignment
443 static uint32_t calculateSegmentAlignment(const MachOObjectFile
&O
) {
444 uint32_t P2CurrentAlignment
;
445 uint32_t P2MinAlignment
= MachOUniversalBinary::MaxSectionAlignment
;
446 const bool Is64Bit
= O
.is64Bit();
448 for (const auto &LC
: O
.load_commands()) {
449 if (LC
.C
.cmd
!= (Is64Bit
? MachO::LC_SEGMENT_64
: MachO::LC_SEGMENT
))
451 if (O
.getHeader().filetype
== MachO::MH_OBJECT
) {
452 unsigned NumberOfSections
=
453 (Is64Bit
? O
.getSegment64LoadCommand(LC
).nsects
454 : O
.getSegmentLoadCommand(LC
).nsects
);
455 P2CurrentAlignment
= NumberOfSections
? 2 : P2MinAlignment
;
456 for (unsigned SI
= 0; SI
< NumberOfSections
; ++SI
) {
457 P2CurrentAlignment
= std::max(P2CurrentAlignment
,
458 (Is64Bit
? O
.getSection64(LC
, SI
).align
459 : O
.getSection(LC
, SI
).align
));
463 countTrailingZeros(Is64Bit
? O
.getSegment64LoadCommand(LC
).vmaddr
464 : O
.getSegmentLoadCommand(LC
).vmaddr
);
466 P2MinAlignment
= std::min(P2MinAlignment
, P2CurrentAlignment
);
468 // return a value >= 4 byte aligned, and less than MachO MaxSectionAlignment
470 static_cast<uint32_t>(2),
471 std::min(P2MinAlignment
, static_cast<uint32_t>(
472 MachOUniversalBinary::MaxSectionAlignment
)));
475 static uint32_t calculateAlignment(const MachOObjectFile
*ObjectFile
) {
476 switch (ObjectFile
->getHeader().cputype
) {
477 case MachO::CPU_TYPE_I386
:
478 case MachO::CPU_TYPE_X86_64
:
479 case MachO::CPU_TYPE_POWERPC
:
480 case MachO::CPU_TYPE_POWERPC64
:
481 return 12; // log2 value of page size(4k) for x86 and PPC
482 case MachO::CPU_TYPE_ARM
:
483 case MachO::CPU_TYPE_ARM64
:
484 case MachO::CPU_TYPE_ARM64_32
:
485 return 14; // log2 value of page size(16k) for Darwin ARM
487 return calculateSegmentAlignment(*ObjectFile
);
491 // This function replicates ordering from cctools lipo for consistency
492 static bool compareSlices(const Slice
&Lhs
, const Slice
&Rhs
) {
493 if (Lhs
.ObjectFile
->getHeader().cputype
==
494 Rhs
.ObjectFile
->getHeader().cputype
)
495 return Lhs
.ObjectFile
->getHeader().cpusubtype
<
496 Rhs
.ObjectFile
->getHeader().cpusubtype
;
498 // force arm64-family to follow after all other slices for compatibility
500 if (Lhs
.ObjectFile
->getHeader().cputype
== MachO::CPU_TYPE_ARM64
)
502 if (Rhs
.ObjectFile
->getHeader().cputype
== MachO::CPU_TYPE_ARM64
)
505 // Sort by alignment to minimize file size
506 return Lhs
.Alignment
< Rhs
.Alignment
;
509 template <typename Range
>
511 updateSegmentAlignments(Range
&Slices
,
512 const StringMap
<const uint32_t> &Alignments
) {
513 for (auto &Slice
: Slices
) {
514 auto Alignment
= Alignments
.find(getArchString(*Slice
.ObjectFile
));
515 if (Alignment
!= Alignments
.end())
516 Slice
.Alignment
= Alignment
->second
;
520 static void checkUnusedAlignments(ArrayRef
<Slice
> Slices
,
521 const StringMap
<const uint32_t> &Alignments
) {
522 auto HasArch
= [&](StringRef Arch
) {
523 return llvm::find_if(Slices
, [Arch
](Slice S
) {
524 return getArchString(*S
.ObjectFile
) == Arch
;
527 for (StringRef Arch
: Alignments
.keys())
529 reportError("-segalign " + Arch
+
530 " <value> specified but resulting fat file does not contain "
531 "that architecture ");
534 // Updates vector ExtractedObjects with the MachOObjectFiles extracted from
535 // Universal Binary files to transfer ownership.
536 static SmallVector
<Slice
, 2> buildSlices(
537 ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
538 const StringMap
<const uint32_t> &Alignments
,
539 SmallVectorImpl
<std::unique_ptr
<MachOObjectFile
>> &ExtractedObjects
) {
540 SmallVector
<Slice
, 2> Slices
;
541 for (auto &IB
: InputBinaries
) {
542 const Binary
*InputBinary
= IB
.getBinary();
543 if (auto UO
= dyn_cast
<MachOUniversalBinary
>(InputBinary
)) {
544 for (const auto &O
: UO
->objects()) {
545 Expected
<std::unique_ptr
<MachOObjectFile
>> BinaryOrError
=
548 reportError(InputBinary
->getFileName(), BinaryOrError
.takeError());
549 ExtractedObjects
.push_back(std::move(BinaryOrError
.get()));
550 Slices
.push_back(Slice
{ExtractedObjects
.back().get(), O
.getAlign()});
552 } else if (auto O
= dyn_cast
<MachOObjectFile
>(InputBinary
)) {
553 Slices
.push_back(Slice
{O
, calculateAlignment(O
)});
555 llvm_unreachable("Unexpected binary format");
558 updateSegmentAlignments(Slices
, Alignments
);
562 static SmallVector
<MachO::fat_arch
, 2>
563 buildFatArchList(ArrayRef
<Slice
> Slices
) {
564 SmallVector
<MachO::fat_arch
, 2> FatArchList
;
566 sizeof(MachO::fat_header
) + Slices
.size() * sizeof(MachO::fat_arch
);
568 for (size_t Index
= 0, Size
= Slices
.size(); Index
< Size
; ++Index
) {
569 Offset
= alignTo(Offset
, 1ull << Slices
[Index
].Alignment
);
570 const MachOObjectFile
*ObjectFile
= Slices
[Index
].ObjectFile
;
571 if (Offset
> UINT32_MAX
)
572 reportError("fat file too large to be created because the offset "
573 "field in struct fat_arch is only 32-bits and the offset " +
574 Twine(Offset
) + " for " + ObjectFile
->getFileName() +
575 " for architecture " + getArchString(*ObjectFile
) +
578 MachO::fat_arch FatArch
;
579 FatArch
.cputype
= ObjectFile
->getHeader().cputype
;
580 FatArch
.cpusubtype
= ObjectFile
->getHeader().cpusubtype
;
581 FatArch
.offset
= Offset
;
582 FatArch
.size
= ObjectFile
->getMemoryBufferRef().getBufferSize();
583 FatArch
.align
= Slices
[Index
].Alignment
;
584 Offset
+= FatArch
.size
;
585 FatArchList
.push_back(FatArch
);
590 static void createUniversalBinary(SmallVectorImpl
<Slice
> &Slices
,
591 StringRef OutputFileName
) {
592 MachO::fat_header FatHeader
;
593 FatHeader
.magic
= MachO::FAT_MAGIC
;
594 FatHeader
.nfat_arch
= Slices
.size();
596 stable_sort(Slices
, compareSlices
);
597 SmallVector
<MachO::fat_arch
, 2> FatArchList
= buildFatArchList(Slices
);
599 const bool IsExecutable
= any_of(Slices
, [](Slice S
) {
600 return sys::fs::can_execute(S
.ObjectFile
->getFileName());
602 const uint64_t OutputFileSize
=
603 FatArchList
.back().offset
+ FatArchList
.back().size
;
604 Expected
<std::unique_ptr
<FileOutputBuffer
>> OutFileOrError
=
605 FileOutputBuffer::create(OutputFileName
, OutputFileSize
,
606 IsExecutable
? FileOutputBuffer::F_executable
609 reportError(OutputFileName
, OutFileOrError
.takeError());
610 std::unique_ptr
<FileOutputBuffer
> OutFile
= std::move(OutFileOrError
.get());
611 std::memset(OutFile
->getBufferStart(), 0, OutputFileSize
);
613 if (sys::IsLittleEndianHost
)
614 MachO::swapStruct(FatHeader
);
615 std::memcpy(OutFile
->getBufferStart(), &FatHeader
, sizeof(MachO::fat_header
));
617 for (size_t Index
= 0, Size
= Slices
.size(); Index
< Size
; ++Index
) {
618 MemoryBufferRef BufferRef
= Slices
[Index
].ObjectFile
->getMemoryBufferRef();
619 std::copy(BufferRef
.getBufferStart(), BufferRef
.getBufferEnd(),
620 OutFile
->getBufferStart() + FatArchList
[Index
].offset
);
623 // FatArchs written after Slices in order to reduce the number of swaps for
624 // the LittleEndian case
625 if (sys::IsLittleEndianHost
)
626 for (MachO::fat_arch
&FA
: FatArchList
)
627 MachO::swapStruct(FA
);
628 std::memcpy(OutFile
->getBufferStart() + sizeof(MachO::fat_header
),
630 sizeof(MachO::fat_arch
) * FatArchList
.size());
632 if (Error E
= OutFile
->commit())
633 reportError(OutputFileName
, std::move(E
));
636 LLVM_ATTRIBUTE_NORETURN
637 static void createUniversalBinary(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
638 const StringMap
<const uint32_t> &Alignments
,
639 StringRef OutputFileName
) {
640 assert(InputBinaries
.size() >= 1 && "Incorrect number of input binaries");
641 assert(!OutputFileName
.empty() && "Create expects a single output file");
643 SmallVector
<std::unique_ptr
<MachOObjectFile
>, 1> ExtractedObjects
;
644 SmallVector
<Slice
, 1> Slices
=
645 buildSlices(InputBinaries
, Alignments
, ExtractedObjects
);
646 checkArchDuplicates(Slices
);
647 checkUnusedAlignments(Slices
, Alignments
);
648 createUniversalBinary(Slices
, OutputFileName
);
653 static StringMap
<Slice
>
654 buildReplacementSlices(ArrayRef
<OwningBinary
<Binary
>> ReplacementBinaries
,
655 const StringMap
<const uint32_t> &Alignments
,
656 ArrayRef
<Replacement
> Replacements
) {
657 assert(ReplacementBinaries
.size() == Replacements
.size() &&
658 "Number of replacment binaries does not match the number of "
660 StringMap
<Slice
> Slices
;
661 // populates StringMap of slices to replace with; error checks for mismatched
662 // replace flag args, fat files, and duplicate arch_types
663 for (size_t Index
= 0, Size
= Replacements
.size(); Index
< Size
; ++Index
) {
664 StringRef ReplacementArch
= Replacements
[Index
].ArchType
;
665 const Binary
*ReplacementBinary
= ReplacementBinaries
[Index
].getBinary();
666 validateArchitectureName(ReplacementArch
);
668 auto O
= dyn_cast
<MachOObjectFile
>(ReplacementBinary
);
670 reportError("replacement file: " + ReplacementBinary
->getFileName() +
671 " is a fat file (must be a thin file)");
673 if (O
->getArch() != Triple(ReplacementArch
).getArch())
674 reportError("specified architecture: " + ReplacementArch
+
675 " for replacement file: " + ReplacementBinary
->getFileName() +
676 " does not match the file's architecture");
679 Slices
.try_emplace(ReplacementArch
, Slice
{O
, calculateAlignment(O
)});
681 reportError("-replace " + ReplacementArch
+
682 " <file_name> specified multiple times: " +
683 Entry
.first
->second
.ObjectFile
->getFileName() + ", " +
686 auto SlicesMapRange
= map_range(
687 Slices
, [](StringMapEntry
<Slice
> &E
) -> Slice
& { return E
.getValue(); });
688 updateSegmentAlignments(SlicesMapRange
, Alignments
);
692 LLVM_ATTRIBUTE_NORETURN
693 static void replaceSlices(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
694 const StringMap
<const uint32_t> &Alignments
,
695 StringRef OutputFileName
,
696 ArrayRef
<Replacement
> Replacements
) {
697 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
698 assert(!OutputFileName
.empty() && "Replace expects a single output file");
700 if (InputBinaries
.front().getBinary()->isMachO())
701 reportError("input file " +
702 InputBinaries
.front().getBinary()->getFileName() +
703 " must be a fat file when the -replace option is specified");
705 SmallVector
<std::string
, 1> ReplacementFiles
;
706 for (const auto &R
: Replacements
)
707 ReplacementFiles
.push_back(R
.FileName
);
708 SmallVector
<OwningBinary
<Binary
>, 1> ReplacementBinaries
=
709 readInputBinaries(ReplacementFiles
);
711 StringMap
<Slice
> ReplacementSlices
=
712 buildReplacementSlices(ReplacementBinaries
, Alignments
, Replacements
);
713 SmallVector
<std::unique_ptr
<MachOObjectFile
>, 2> ExtractedObjects
;
714 SmallVector
<Slice
, 2> Slices
=
715 buildSlices(InputBinaries
, Alignments
, ExtractedObjects
);
717 for (auto &Slice
: Slices
) {
718 auto It
= ReplacementSlices
.find(getArchString(*Slice
.ObjectFile
));
719 if (It
!= ReplacementSlices
.end()) {
721 ReplacementSlices
.erase(It
); // only keep remaining replacing arch_types
725 if (!ReplacementSlices
.empty())
726 reportError("-replace " + ReplacementSlices
.begin()->first() +
727 " <file_name> specified but fat file: " +
728 InputBinaries
.front().getBinary()->getFileName() +
729 " does not contain that architecture");
731 checkUnusedAlignments(Slices
, Alignments
);
732 createUniversalBinary(Slices
, OutputFileName
);
736 int main(int argc
, char **argv
) {
737 InitLLVM
X(argc
, argv
);
738 Config C
= parseLipoOptions(makeArrayRef(argv
+ 1, argc
));
739 SmallVector
<OwningBinary
<Binary
>, 1> InputBinaries
=
740 readInputBinaries(C
.InputFiles
);
742 switch (C
.ActionToPerform
) {
743 case LipoAction::VerifyArch
:
744 verifyArch(InputBinaries
, C
.VerifyArchList
);
746 case LipoAction::PrintArchs
:
747 printArchs(InputBinaries
);
749 case LipoAction::PrintInfo
:
750 printInfo(InputBinaries
);
752 case LipoAction::ThinArch
:
753 extractSlice(InputBinaries
, C
.ThinArchType
, C
.OutputFile
);
755 case LipoAction::CreateUniversal
:
756 createUniversalBinary(InputBinaries
, C
.SegmentAlignments
, C
.OutputFile
);
758 case LipoAction::ReplaceArch
:
759 replaceSlices(InputBinaries
, C
.SegmentAlignments
, C
.OutputFile
,