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
;
104 // For compatibility with cctools lipo, a file's alignment is calculated as the
105 // minimum aligment of all segments. For object files, the file's alignment is
106 // the maximum alignment of its sections.
107 static uint32_t calculateFileAlignment(const MachOObjectFile
&O
) {
108 uint32_t P2CurrentAlignment
;
109 uint32_t P2MinAlignment
= MachOUniversalBinary::MaxSectionAlignment
;
110 const bool Is64Bit
= O
.is64Bit();
112 for (const auto &LC
: O
.load_commands()) {
113 if (LC
.C
.cmd
!= (Is64Bit
? MachO::LC_SEGMENT_64
: MachO::LC_SEGMENT
))
115 if (O
.getHeader().filetype
== MachO::MH_OBJECT
) {
116 unsigned NumberOfSections
=
117 (Is64Bit
? O
.getSegment64LoadCommand(LC
).nsects
118 : O
.getSegmentLoadCommand(LC
).nsects
);
119 P2CurrentAlignment
= NumberOfSections
? 2 : P2MinAlignment
;
120 for (unsigned SI
= 0; SI
< NumberOfSections
; ++SI
) {
121 P2CurrentAlignment
= std::max(P2CurrentAlignment
,
122 (Is64Bit
? O
.getSection64(LC
, SI
).align
123 : O
.getSection(LC
, SI
).align
));
127 countTrailingZeros(Is64Bit
? O
.getSegment64LoadCommand(LC
).vmaddr
128 : O
.getSegmentLoadCommand(LC
).vmaddr
);
130 P2MinAlignment
= std::min(P2MinAlignment
, P2CurrentAlignment
);
132 // return a value >= 4 byte aligned, and less than MachO MaxSectionAlignment
134 static_cast<uint32_t>(2),
135 std::min(P2MinAlignment
, static_cast<uint32_t>(
136 MachOUniversalBinary::MaxSectionAlignment
)));
139 static uint32_t calculateAlignment(const MachOObjectFile
*ObjectFile
) {
140 switch (ObjectFile
->getHeader().cputype
) {
141 case MachO::CPU_TYPE_I386
:
142 case MachO::CPU_TYPE_X86_64
:
143 case MachO::CPU_TYPE_POWERPC
:
144 case MachO::CPU_TYPE_POWERPC64
:
145 return 12; // log2 value of page size(4k) for x86 and PPC
146 case MachO::CPU_TYPE_ARM
:
147 case MachO::CPU_TYPE_ARM64
:
148 case MachO::CPU_TYPE_ARM64_32
:
149 return 14; // log2 value of page size(16k) for Darwin ARM
151 return calculateFileAlignment(*ObjectFile
);
159 std::string ArchName
;
161 // P2Alignment field stores slice alignment values from universal
162 // binaries. This is also needed to order the slices so the total
163 // file size can be calculated before creating the output buffer.
164 uint32_t P2Alignment
;
167 Slice(const MachOObjectFile
*O
, uint32_t Align
)
168 : B(O
), CPUType(O
->getHeader().cputype
),
169 CPUSubType(O
->getHeader().cpusubtype
),
170 ArchName(O
->getArchTriple().getArchName()), P2Alignment(Align
) {}
172 explicit Slice(const MachOObjectFile
*O
) : Slice(O
, calculateAlignment(O
)){};
174 explicit Slice(const Archive
*A
) : B(A
) {
175 Error Err
= Error::success();
176 std::unique_ptr
<MachOObjectFile
> FO
= nullptr;
177 for (const Archive::Child
&Child
: A
->children(Err
)) {
178 Expected
<std::unique_ptr
<Binary
>> ChildOrErr
= Child
.getAsBinary();
180 reportError(A
->getFileName(), ChildOrErr
.takeError());
181 Binary
*Bin
= ChildOrErr
.get().get();
182 if (Bin
->isMachOUniversalBinary())
183 reportError(("archive member " + Bin
->getFileName() +
184 " is a fat file (not allowed in an archive)")
187 reportError(("archive member " + Bin
->getFileName() +
188 " is not a MachO file (not allowed in an archive)"));
189 MachOObjectFile
*O
= cast
<MachOObjectFile
>(Bin
);
191 std::tie(FO
->getHeader().cputype
, FO
->getHeader().cpusubtype
) !=
192 std::tie(O
->getHeader().cputype
, O
->getHeader().cpusubtype
)) {
193 reportError(("archive member " + O
->getFileName() + " cputype (" +
194 Twine(O
->getHeader().cputype
) + ") and cpusubtype(" +
195 Twine(O
->getHeader().cpusubtype
) +
196 ") does not match previous archive members cputype (" +
197 Twine(FO
->getHeader().cputype
) + ") and cpusubtype(" +
198 Twine(FO
->getHeader().cpusubtype
) +
199 ") (all members must match) " + FO
->getFileName())
203 ChildOrErr
.get().release();
208 reportError(A
->getFileName(), std::move(Err
));
210 reportError(("empty archive with no architecture specification: " +
211 A
->getFileName() + " (can't determine architecture for it)")
213 CPUType
= FO
->getHeader().cputype
;
214 CPUSubType
= FO
->getHeader().cpusubtype
;
215 ArchName
= FO
->getArchTriple().getArchName();
216 // Replicate the behavior of cctools lipo.
217 P2Alignment
= FO
->is64Bit() ? 3 : 2;
220 void setP2Alignment(uint32_t Align
) { P2Alignment
= Align
; }
222 const Binary
*getBinary() const { return B
; }
224 uint32_t getCPUType() const { return CPUType
; }
226 uint32_t getCPUSubType() const { return CPUSubType
; }
228 uint32_t getP2Alignment() const { return P2Alignment
; }
230 uint64_t getCPUID() const {
231 return static_cast<uint64_t>(CPUType
) << 32 | CPUSubType
;
234 std::string
getArchString() const {
235 if (!ArchName
.empty())
237 return ("unknown(" + Twine(CPUType
) + "," +
238 Twine(CPUSubType
& ~MachO::CPU_SUBTYPE_MASK
) + ")")
242 friend bool operator<(const Slice
&Lhs
, const Slice
&Rhs
) {
243 if (Lhs
.CPUType
== Rhs
.CPUType
)
244 return Lhs
.CPUSubType
< Rhs
.CPUSubType
;
245 // force arm64-family to follow after all other slices for
246 // compatibility with cctools lipo
247 if (Lhs
.CPUType
== MachO::CPU_TYPE_ARM64
)
249 if (Rhs
.CPUType
== MachO::CPU_TYPE_ARM64
)
251 // Sort by alignment to minimize file size
252 return Lhs
.P2Alignment
< Rhs
.P2Alignment
;
258 static void validateArchitectureName(StringRef ArchitectureName
) {
259 if (!MachOObjectFile::isValidArch(ArchitectureName
)) {
261 raw_string_ostream
OS(Buf
);
262 OS
<< "Invalid architecture: " << ArchitectureName
263 << "\nValid architecture names are:";
264 for (auto arch
: MachOObjectFile::getValidArchs())
266 reportError(OS
.str());
270 static Config
parseLipoOptions(ArrayRef
<const char *> ArgsArr
) {
273 unsigned MissingArgumentIndex
, MissingArgumentCount
;
274 opt::InputArgList InputArgs
=
275 T
.ParseArgs(ArgsArr
, MissingArgumentIndex
, MissingArgumentCount
);
277 if (MissingArgumentCount
)
278 reportError("missing argument to " +
279 StringRef(InputArgs
.getArgString(MissingArgumentIndex
)) +
282 if (InputArgs
.size() == 0) {
283 // PrintHelp does not accept Twine.
284 T
.PrintHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
288 if (InputArgs
.hasArg(LIPO_help
)) {
289 // PrintHelp does not accept Twine.
290 T
.PrintHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
294 if (InputArgs
.hasArg(LIPO_version
)) {
295 outs() << ToolName
+ "\n";
296 cl::PrintVersionMessage();
300 for (auto Arg
: InputArgs
.filtered(LIPO_UNKNOWN
))
301 reportError("unknown argument '" + Arg
->getAsString(InputArgs
) + "'");
303 for (auto Arg
: InputArgs
.filtered(LIPO_INPUT
))
304 C
.InputFiles
.push_back(Arg
->getValue());
305 if (C
.InputFiles
.empty())
306 reportError("at least one input file should be specified");
308 if (InputArgs
.hasArg(LIPO_output
))
309 C
.OutputFile
= InputArgs
.getLastArgValue(LIPO_output
);
311 for (auto Segalign
: InputArgs
.filtered(LIPO_segalign
)) {
312 if (!Segalign
->getValue(1))
313 reportError("segalign is missing an argument: expects -segalign "
314 "arch_type alignment_value");
316 validateArchitectureName(Segalign
->getValue(0));
318 uint32_t AlignmentValue
;
319 if (!to_integer
<uint32_t>(Segalign
->getValue(1), AlignmentValue
, 16))
320 reportError("argument to -segalign <arch_type> " +
321 Twine(Segalign
->getValue(1)) +
322 " (hex) is not a proper hexadecimal number");
323 if (!isPowerOf2_32(AlignmentValue
))
324 reportError("argument to -segalign <arch_type> " +
325 Twine(Segalign
->getValue(1)) +
326 " (hex) must be a non-zero power of two");
327 if (Log2_32(AlignmentValue
) > MachOUniversalBinary::MaxSectionAlignment
)
329 "argument to -segalign <arch_type> " + Twine(Segalign
->getValue(1)) +
330 " (hex) must be less than or equal to the maximum section align 2^" +
331 Twine(MachOUniversalBinary::MaxSectionAlignment
));
332 auto Entry
= C
.SegmentAlignments
.try_emplace(Segalign
->getValue(0),
333 Log2_32(AlignmentValue
));
335 reportError("-segalign " + Twine(Segalign
->getValue(0)) +
336 " <alignment_value> specified multiple times: " +
337 Twine(1 << Entry
.first
->second
) + ", " +
338 Twine(AlignmentValue
));
341 SmallVector
<opt::Arg
*, 1> ActionArgs(InputArgs
.filtered(LIPO_action_group
));
342 if (ActionArgs
.empty())
343 reportError("at least one action should be specified");
344 // errors if multiple actions specified other than replace
345 // multiple replace flags may be specified, as long as they are not mixed with
346 // other action flags
347 auto ReplacementArgsRange
= InputArgs
.filtered(LIPO_replace
);
348 if (ActionArgs
.size() > 1 &&
350 static_cast<size_t>(std::distance(ReplacementArgsRange
.begin(),
351 ReplacementArgsRange
.end()))) {
353 raw_string_ostream
OS(Buf
);
354 OS
<< "only one of the following actions can be specified:";
355 for (auto Arg
: ActionArgs
)
356 OS
<< " " << Arg
->getSpelling();
357 reportError(OS
.str());
360 switch (ActionArgs
[0]->getOption().getID()) {
361 case LIPO_verify_arch
:
362 for (auto A
: InputArgs
.getAllArgValues(LIPO_verify_arch
))
363 C
.VerifyArchList
.push_back(A
);
364 if (C
.VerifyArchList
.empty())
366 "verify_arch requires at least one architecture to be specified");
367 if (C
.InputFiles
.size() > 1)
368 reportError("verify_arch expects a single input file");
369 C
.ActionToPerform
= LipoAction::VerifyArch
;
373 if (C
.InputFiles
.size() > 1)
374 reportError("archs expects a single input file");
375 C
.ActionToPerform
= LipoAction::PrintArchs
;
379 C
.ActionToPerform
= LipoAction::PrintInfo
;
383 if (C
.InputFiles
.size() > 1)
384 reportError("thin expects a single input file");
385 C
.ThinArchType
= ActionArgs
[0]->getValue();
386 validateArchitectureName(C
.ThinArchType
);
387 if (C
.OutputFile
.empty())
388 reportError("thin expects a single output file");
390 C
.ActionToPerform
= LipoAction::ThinArch
;
394 if (C
.OutputFile
.empty())
395 reportError("create expects a single output file to be specified");
396 C
.ActionToPerform
= LipoAction::CreateUniversal
;
400 for (auto Action
: ActionArgs
) {
401 if (!Action
->getValue(1))
403 "replace is missing an argument: expects -replace arch_type "
405 C
.Replacements
.push_back(
406 Replacement
{Action
->getValue(0), Action
->getValue(1)});
409 if (C
.OutputFile
.empty())
410 reportError("replace expects a single output file to be specified");
411 if (C
.InputFiles
.size() > 1)
412 reportError("replace expects a single input file");
413 C
.ActionToPerform
= LipoAction::ReplaceArch
;
417 reportError("llvm-lipo action unspecified");
421 static SmallVector
<OwningBinary
<Binary
>, 1>
422 readInputBinaries(ArrayRef
<std::string
> InputFiles
) {
423 SmallVector
<OwningBinary
<Binary
>, 1> InputBinaries
;
424 for (StringRef InputFile
: InputFiles
) {
425 Expected
<OwningBinary
<Binary
>> BinaryOrErr
= createBinary(InputFile
);
427 reportError(InputFile
, BinaryOrErr
.takeError());
428 if (!BinaryOrErr
->getBinary()->isArchive() &&
429 !BinaryOrErr
->getBinary()->isMachO() &&
430 !BinaryOrErr
->getBinary()->isMachOUniversalBinary())
431 reportError("File " + InputFile
+ " has unsupported binary format");
432 InputBinaries
.push_back(std::move(*BinaryOrErr
));
434 return InputBinaries
;
437 LLVM_ATTRIBUTE_NORETURN
438 static void verifyArch(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
439 ArrayRef
<std::string
> VerifyArchList
) {
440 assert(!VerifyArchList
.empty() &&
441 "The list of architectures should be non-empty");
442 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
444 for (StringRef Arch
: VerifyArchList
)
445 validateArchitectureName(Arch
);
448 dyn_cast
<MachOUniversalBinary
>(InputBinaries
.front().getBinary())) {
449 for (StringRef Arch
: VerifyArchList
) {
450 Expected
<MachOUniversalBinary::ObjectForArch
> Obj
=
451 UO
->getObjectForArch(Arch
);
456 dyn_cast
<MachOObjectFile
>(InputBinaries
.front().getBinary())) {
457 const Triple::ArchType ObjectArch
= O
->getArch();
458 for (StringRef Arch
: VerifyArchList
)
459 if (ObjectArch
!= Triple(Arch
).getArch())
462 llvm_unreachable("Unexpected binary format");
467 static void printBinaryArchs(const Binary
*Binary
, raw_ostream
&OS
) {
468 // Prints trailing space for compatibility with cctools lipo.
469 if (auto UO
= dyn_cast
<MachOUniversalBinary
>(Binary
)) {
470 for (const auto &O
: UO
->objects()) {
471 Expected
<std::unique_ptr
<MachOObjectFile
>> MachOObjOrError
=
473 if (MachOObjOrError
) {
474 OS
<< Slice(MachOObjOrError
->get()).getArchString() << " ";
477 Expected
<std::unique_ptr
<Archive
>> ArchiveOrError
= O
.getAsArchive();
478 if (ArchiveOrError
) {
479 consumeError(MachOObjOrError
.takeError());
480 OS
<< Slice(ArchiveOrError
->get()).getArchString() << " ";
483 consumeError(ArchiveOrError
.takeError());
484 reportError(Binary
->getFileName(), MachOObjOrError
.takeError());
489 OS
<< Slice(cast
<MachOObjectFile
>(Binary
)).getArchString() << " \n";
492 LLVM_ATTRIBUTE_NORETURN
493 static void printArchs(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
) {
494 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
495 printBinaryArchs(InputBinaries
.front().getBinary(), outs());
499 LLVM_ATTRIBUTE_NORETURN
500 static void printInfo(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
) {
501 // Group universal and thin files together for compatibility with cctools lipo
502 for (auto &IB
: InputBinaries
) {
503 const Binary
*Binary
= IB
.getBinary();
504 if (Binary
->isMachOUniversalBinary()) {
505 outs() << "Architectures in the fat file: " << Binary
->getFileName()
507 printBinaryArchs(Binary
, outs());
510 for (auto &IB
: InputBinaries
) {
511 const Binary
*Binary
= IB
.getBinary();
512 if (!Binary
->isMachOUniversalBinary()) {
513 assert(Binary
->isMachO() && "expected MachO binary");
514 outs() << "Non-fat file: " << Binary
->getFileName()
515 << " is architecture: ";
516 printBinaryArchs(Binary
, outs());
522 LLVM_ATTRIBUTE_NORETURN
523 static void extractSlice(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
524 StringRef ThinArchType
, StringRef OutputFileName
) {
525 assert(!ThinArchType
.empty() && "The architecture type should be non-empty");
526 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
527 assert(!OutputFileName
.empty() && "Thin expects a single output file");
529 if (InputBinaries
.front().getBinary()->isMachO()) {
530 reportError("input file " +
531 InputBinaries
.front().getBinary()->getFileName() +
532 " must be a fat file when the -thin option is specified");
536 auto *UO
= cast
<MachOUniversalBinary
>(InputBinaries
.front().getBinary());
537 Expected
<std::unique_ptr
<MachOObjectFile
>> Obj
=
538 UO
->getMachOObjectForArch(ThinArchType
);
539 Expected
<std::unique_ptr
<Archive
>> Ar
= UO
->getArchiveForArch(ThinArchType
);
541 reportError("fat input file " + UO
->getFileName() +
542 " does not contain the specified architecture " + ThinArchType
+
544 Binary
*B
= Obj
? static_cast<Binary
*>(Obj
->get())
545 : static_cast<Binary
*>(Ar
->get());
546 Expected
<std::unique_ptr
<FileOutputBuffer
>> OutFileOrError
=
547 FileOutputBuffer::create(OutputFileName
,
548 B
->getMemoryBufferRef().getBufferSize(),
549 sys::fs::can_execute(UO
->getFileName())
550 ? FileOutputBuffer::F_executable
553 reportError(OutputFileName
, OutFileOrError
.takeError());
554 std::copy(B
->getMemoryBufferRef().getBufferStart(),
555 B
->getMemoryBufferRef().getBufferEnd(),
556 OutFileOrError
.get()->getBufferStart());
557 if (Error E
= OutFileOrError
.get()->commit())
558 reportError(OutputFileName
, std::move(E
));
562 static void checkArchDuplicates(const ArrayRef
<Slice
> &Slices
) {
563 DenseMap
<uint64_t, const Binary
*> CPUIds
;
564 for (const auto &S
: Slices
) {
565 auto Entry
= CPUIds
.try_emplace(S
.getCPUID(), S
.getBinary());
567 reportError(Entry
.first
->second
->getFileName() + " and " +
568 S
.getBinary()->getFileName() +
569 " have the same architecture " + S
.getArchString() +
570 " and therefore cannot be in the same universal binary");
574 template <typename Range
>
575 static void updateAlignments(Range
&Slices
,
576 const StringMap
<const uint32_t> &Alignments
) {
577 for (auto &Slice
: Slices
) {
578 auto Alignment
= Alignments
.find(Slice
.getArchString());
579 if (Alignment
!= Alignments
.end())
580 Slice
.setP2Alignment(Alignment
->second
);
584 static void checkUnusedAlignments(ArrayRef
<Slice
> Slices
,
585 const StringMap
<const uint32_t> &Alignments
) {
586 auto HasArch
= [&](StringRef Arch
) {
587 return llvm::find_if(Slices
, [Arch
](Slice S
) {
588 return S
.getArchString() == Arch
;
591 for (StringRef Arch
: Alignments
.keys())
593 reportError("-segalign " + Arch
+
594 " <value> specified but resulting fat file does not contain "
595 "that architecture ");
598 // Updates vector ExtractedObjects with the MachOObjectFiles extracted from
599 // Universal Binary files to transfer ownership.
600 static SmallVector
<Slice
, 2> buildSlices(
601 ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
602 const StringMap
<const uint32_t> &Alignments
,
603 SmallVectorImpl
<std::unique_ptr
<MachOObjectFile
>> &ExtractedObjects
) {
604 SmallVector
<Slice
, 2> Slices
;
605 for (auto &IB
: InputBinaries
) {
606 const Binary
*InputBinary
= IB
.getBinary();
607 if (auto UO
= dyn_cast
<MachOUniversalBinary
>(InputBinary
)) {
608 for (const auto &O
: UO
->objects()) {
609 Expected
<std::unique_ptr
<MachOObjectFile
>> BinaryOrError
=
612 reportError(InputBinary
->getFileName(), BinaryOrError
.takeError());
613 ExtractedObjects
.push_back(std::move(BinaryOrError
.get()));
614 Slices
.emplace_back(ExtractedObjects
.back().get(), O
.getAlign());
616 } else if (auto O
= dyn_cast
<MachOObjectFile
>(InputBinary
)) {
617 Slices
.emplace_back(O
);
618 } else if (auto A
= dyn_cast
<Archive
>(InputBinary
)) {
619 Slices
.emplace_back(A
);
621 llvm_unreachable("Unexpected binary format");
624 updateAlignments(Slices
, Alignments
);
628 static SmallVector
<MachO::fat_arch
, 2>
629 buildFatArchList(ArrayRef
<Slice
> Slices
) {
630 SmallVector
<MachO::fat_arch
, 2> FatArchList
;
632 sizeof(MachO::fat_header
) + Slices
.size() * sizeof(MachO::fat_arch
);
634 for (const auto &S
: Slices
) {
635 Offset
= alignTo(Offset
, 1ull << S
.getP2Alignment());
636 if (Offset
> UINT32_MAX
)
637 reportError("fat file too large to be created because the offset "
638 "field in struct fat_arch is only 32-bits and the offset " +
639 Twine(Offset
) + " for " + S
.getBinary()->getFileName() +
640 " for architecture " + S
.getArchString() + "exceeds that.");
642 MachO::fat_arch FatArch
;
643 FatArch
.cputype
= S
.getCPUType();
644 FatArch
.cpusubtype
= S
.getCPUSubType();
645 FatArch
.offset
= Offset
;
646 FatArch
.size
= S
.getBinary()->getMemoryBufferRef().getBufferSize();
647 FatArch
.align
= S
.getP2Alignment();
648 Offset
+= FatArch
.size
;
649 FatArchList
.push_back(FatArch
);
654 static void createUniversalBinary(SmallVectorImpl
<Slice
> &Slices
,
655 StringRef OutputFileName
) {
656 MachO::fat_header FatHeader
;
657 FatHeader
.magic
= MachO::FAT_MAGIC
;
658 FatHeader
.nfat_arch
= Slices
.size();
661 SmallVector
<MachO::fat_arch
, 2> FatArchList
= buildFatArchList(Slices
);
663 const bool IsExecutable
= any_of(Slices
, [](Slice S
) {
664 return sys::fs::can_execute(S
.getBinary()->getFileName());
666 const uint64_t OutputFileSize
=
667 FatArchList
.back().offset
+ FatArchList
.back().size
;
668 Expected
<std::unique_ptr
<FileOutputBuffer
>> OutFileOrError
=
669 FileOutputBuffer::create(OutputFileName
, OutputFileSize
,
670 IsExecutable
? FileOutputBuffer::F_executable
673 reportError(OutputFileName
, OutFileOrError
.takeError());
674 std::unique_ptr
<FileOutputBuffer
> OutFile
= std::move(OutFileOrError
.get());
675 std::memset(OutFile
->getBufferStart(), 0, OutputFileSize
);
677 if (sys::IsLittleEndianHost
)
678 MachO::swapStruct(FatHeader
);
679 std::memcpy(OutFile
->getBufferStart(), &FatHeader
, sizeof(MachO::fat_header
));
681 for (size_t Index
= 0, Size
= Slices
.size(); Index
< Size
; ++Index
) {
682 MemoryBufferRef BufferRef
= Slices
[Index
].getBinary()->getMemoryBufferRef();
683 std::copy(BufferRef
.getBufferStart(), BufferRef
.getBufferEnd(),
684 OutFile
->getBufferStart() + FatArchList
[Index
].offset
);
687 // FatArchs written after Slices in order to reduce the number of swaps for
688 // the LittleEndian case
689 if (sys::IsLittleEndianHost
)
690 for (MachO::fat_arch
&FA
: FatArchList
)
691 MachO::swapStruct(FA
);
692 std::memcpy(OutFile
->getBufferStart() + sizeof(MachO::fat_header
),
694 sizeof(MachO::fat_arch
) * FatArchList
.size());
696 if (Error E
= OutFile
->commit())
697 reportError(OutputFileName
, std::move(E
));
700 LLVM_ATTRIBUTE_NORETURN
701 static void createUniversalBinary(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
702 const StringMap
<const uint32_t> &Alignments
,
703 StringRef OutputFileName
) {
704 assert(InputBinaries
.size() >= 1 && "Incorrect number of input binaries");
705 assert(!OutputFileName
.empty() && "Create expects a single output file");
707 SmallVector
<std::unique_ptr
<MachOObjectFile
>, 1> ExtractedObjects
;
708 SmallVector
<Slice
, 1> Slices
=
709 buildSlices(InputBinaries
, Alignments
, ExtractedObjects
);
710 checkArchDuplicates(Slices
);
711 checkUnusedAlignments(Slices
, Alignments
);
712 createUniversalBinary(Slices
, OutputFileName
);
717 static StringMap
<Slice
>
718 buildReplacementSlices(ArrayRef
<OwningBinary
<Binary
>> ReplacementBinaries
,
719 const StringMap
<const uint32_t> &Alignments
,
720 ArrayRef
<Replacement
> Replacements
) {
721 assert(ReplacementBinaries
.size() == Replacements
.size() &&
722 "Number of replacment binaries does not match the number of "
724 StringMap
<Slice
> Slices
;
725 // populates StringMap of slices to replace with; error checks for mismatched
726 // replace flag args, fat files, and duplicate arch_types
727 for (size_t Index
= 0, Size
= Replacements
.size(); Index
< Size
; ++Index
) {
728 StringRef ReplacementArch
= Replacements
[Index
].ArchType
;
729 const Binary
*ReplacementBinary
= ReplacementBinaries
[Index
].getBinary();
730 validateArchitectureName(ReplacementArch
);
732 auto O
= dyn_cast
<MachOObjectFile
>(ReplacementBinary
);
734 reportError("replacement file: " + ReplacementBinary
->getFileName() +
735 " is a fat file (must be a thin file)");
737 if (O
->getArch() != Triple(ReplacementArch
).getArch())
738 reportError("specified architecture: " + ReplacementArch
+
739 " for replacement file: " + ReplacementBinary
->getFileName() +
740 " does not match the file's architecture");
742 auto Entry
= Slices
.try_emplace(ReplacementArch
, Slice(O
));
744 reportError("-replace " + ReplacementArch
+
745 " <file_name> specified multiple times: " +
746 Entry
.first
->second
.getBinary()->getFileName() + ", " +
749 auto SlicesMapRange
= map_range(
750 Slices
, [](StringMapEntry
<Slice
> &E
) -> Slice
& { return E
.getValue(); });
751 updateAlignments(SlicesMapRange
, Alignments
);
755 LLVM_ATTRIBUTE_NORETURN
756 static void replaceSlices(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
757 const StringMap
<const uint32_t> &Alignments
,
758 StringRef OutputFileName
,
759 ArrayRef
<Replacement
> Replacements
) {
760 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
761 assert(!OutputFileName
.empty() && "Replace expects a single output file");
763 if (InputBinaries
.front().getBinary()->isMachO())
764 reportError("input file " +
765 InputBinaries
.front().getBinary()->getFileName() +
766 " must be a fat file when the -replace option is specified");
768 SmallVector
<std::string
, 1> ReplacementFiles
;
769 for (const auto &R
: Replacements
)
770 ReplacementFiles
.push_back(R
.FileName
);
771 SmallVector
<OwningBinary
<Binary
>, 1> ReplacementBinaries
=
772 readInputBinaries(ReplacementFiles
);
774 StringMap
<Slice
> ReplacementSlices
=
775 buildReplacementSlices(ReplacementBinaries
, Alignments
, Replacements
);
776 SmallVector
<std::unique_ptr
<MachOObjectFile
>, 2> ExtractedObjects
;
777 SmallVector
<Slice
, 2> Slices
=
778 buildSlices(InputBinaries
, Alignments
, ExtractedObjects
);
780 for (auto &Slice
: Slices
) {
781 auto It
= ReplacementSlices
.find(Slice
.getArchString());
782 if (It
!= ReplacementSlices
.end()) {
784 ReplacementSlices
.erase(It
); // only keep remaining replacing arch_types
788 if (!ReplacementSlices
.empty())
789 reportError("-replace " + ReplacementSlices
.begin()->first() +
790 " <file_name> specified but fat file: " +
791 InputBinaries
.front().getBinary()->getFileName() +
792 " does not contain that architecture");
794 checkUnusedAlignments(Slices
, Alignments
);
795 createUniversalBinary(Slices
, OutputFileName
);
799 int main(int argc
, char **argv
) {
800 InitLLVM
X(argc
, argv
);
801 Config C
= parseLipoOptions(makeArrayRef(argv
+ 1, argc
));
802 SmallVector
<OwningBinary
<Binary
>, 1> InputBinaries
=
803 readInputBinaries(C
.InputFiles
);
805 switch (C
.ActionToPerform
) {
806 case LipoAction::VerifyArch
:
807 verifyArch(InputBinaries
, C
.VerifyArchList
);
809 case LipoAction::PrintArchs
:
810 printArchs(InputBinaries
);
812 case LipoAction::PrintInfo
:
813 printInfo(InputBinaries
);
815 case LipoAction::ThinArch
:
816 extractSlice(InputBinaries
, C
.ThinArchType
, C
.OutputFile
);
818 case LipoAction::CreateUniversal
:
819 createUniversalBinary(InputBinaries
, C
.SegmentAlignments
, C
.OutputFile
);
821 case LipoAction::ReplaceArch
:
822 replaceSlices(InputBinaries
, C
.SegmentAlignments
, C
.OutputFile
,