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/BinaryFormat/MachO.h"
16 #include "llvm/IR/LLVMContext.h"
17 #include "llvm/IR/Module.h"
18 #include "llvm/Object/Binary.h"
19 #include "llvm/Object/IRObjectFile.h"
20 #include "llvm/Object/MachO.h"
21 #include "llvm/Object/MachOUniversal.h"
22 #include "llvm/Object/MachOUniversalWriter.h"
23 #include "llvm/Object/ObjectFile.h"
24 #include "llvm/Option/Arg.h"
25 #include "llvm/Option/ArgList.h"
26 #include "llvm/Support/CommandLine.h"
27 #include "llvm/Support/Error.h"
28 #include "llvm/Support/FileOutputBuffer.h"
29 #include "llvm/Support/InitLLVM.h"
30 #include "llvm/Support/WithColor.h"
31 #include "llvm/TextAPI/Architecture.h"
34 using namespace llvm::object
;
36 static const StringRef ToolName
= "llvm-lipo";
37 static LLVMContext LLVMCtx
;
39 [[noreturn
]] static void reportError(Twine Message
) {
40 WithColor::error(errs(), ToolName
) << Message
<< "\n";
45 [[noreturn
]] static void reportError(Error E
) {
48 raw_string_ostream
OS(Buf
);
49 logAllUnhandledErrors(std::move(E
), OS
);
54 [[noreturn
]] static void reportError(StringRef File
, Error E
) {
57 raw_string_ostream
OS(Buf
);
58 logAllUnhandledErrors(std::move(E
), OS
);
60 WithColor::error(errs(), ToolName
) << "'" << File
<< "': " << Buf
;
66 LIPO_INVALID
= 0, // This is not an option ID.
67 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
68 HELPTEXT, METAVAR, VALUES) \
70 #include "LipoOpts.inc"
74 // LipoInfoTable below references LIPO_##PREFIX. OptionGroup has prefix nullptr.
75 const char *const *LIPO_nullptr
= nullptr;
76 #define PREFIX(NAME, VALUE) const char *const LIPO_##NAME[] = VALUE;
77 #include "LipoOpts.inc"
80 const opt::OptTable::Info LipoInfoTable
[] = {
81 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
82 HELPTEXT, METAVAR, VALUES) \
83 {LIPO_##PREFIX, NAME, HELPTEXT, \
84 METAVAR, LIPO_##ID, opt::Option::KIND##Class, \
85 PARAM, FLAGS, LIPO_##GROUP, \
86 LIPO_##ALIAS, ALIASARGS, VALUES},
87 #include "LipoOpts.inc"
91 class LipoOptTable
: public opt::OptTable
{
93 LipoOptTable() : OptTable(LipoInfoTable
) {}
96 enum class LipoAction
{
107 Optional
<StringRef
> ArchType
;
112 SmallVector
<InputFile
, 1> InputFiles
;
113 SmallVector
<std::string
, 1> VerifyArchList
;
114 SmallVector
<InputFile
, 1> ReplacementFiles
;
115 StringMap
<const uint32_t> SegmentAlignments
;
116 std::string ArchType
;
117 std::string OutputFile
;
118 LipoAction ActionToPerform
;
121 static Slice
createSliceFromArchive(const Archive
&A
) {
122 Expected
<Slice
> ArchiveOrSlice
= Slice::create(A
, &LLVMCtx
);
124 reportError(A
.getFileName(), ArchiveOrSlice
.takeError());
125 return *ArchiveOrSlice
;
128 static Slice
createSliceFromIR(const IRObjectFile
&IRO
, unsigned Align
) {
129 Expected
<Slice
> IROrErr
= Slice::create(IRO
, Align
);
131 reportError(IRO
.getFileName(), IROrErr
.takeError());
137 static void validateArchitectureName(StringRef ArchitectureName
) {
138 if (!MachOObjectFile::isValidArch(ArchitectureName
)) {
140 raw_string_ostream
OS(Buf
);
141 OS
<< "Invalid architecture: " << ArchitectureName
142 << "\nValid architecture names are:";
143 for (auto arch
: MachOObjectFile::getValidArchs())
145 reportError(OS
.str());
149 static Config
parseLipoOptions(ArrayRef
<const char *> ArgsArr
) {
152 unsigned MissingArgumentIndex
, MissingArgumentCount
;
153 opt::InputArgList InputArgs
=
154 T
.ParseArgs(ArgsArr
, MissingArgumentIndex
, MissingArgumentCount
);
156 if (MissingArgumentCount
)
157 reportError("missing argument to " +
158 StringRef(InputArgs
.getArgString(MissingArgumentIndex
)) +
161 if (InputArgs
.size() == 0) {
162 // printHelp does not accept Twine.
163 T
.printHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
167 if (InputArgs
.hasArg(LIPO_help
)) {
168 // printHelp does not accept Twine.
169 T
.printHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
173 if (InputArgs
.hasArg(LIPO_version
)) {
174 outs() << ToolName
+ "\n";
175 cl::PrintVersionMessage();
179 for (auto Arg
: InputArgs
.filtered(LIPO_UNKNOWN
))
180 reportError("unknown argument '" + Arg
->getAsString(InputArgs
) + "'");
182 for (auto Arg
: InputArgs
.filtered(LIPO_INPUT
))
183 C
.InputFiles
.push_back({None
, Arg
->getValue()});
184 for (auto Arg
: InputArgs
.filtered(LIPO_arch
)) {
185 validateArchitectureName(Arg
->getValue(0));
186 if (!Arg
->getValue(1))
188 "arch is missing an argument: expects -arch arch_type file_name");
189 C
.InputFiles
.push_back({StringRef(Arg
->getValue(0)), Arg
->getValue(1)});
192 if (C
.InputFiles
.empty())
193 reportError("at least one input file should be specified");
195 if (InputArgs
.hasArg(LIPO_output
))
196 C
.OutputFile
= std::string(InputArgs
.getLastArgValue(LIPO_output
));
198 for (auto Segalign
: InputArgs
.filtered(LIPO_segalign
)) {
199 if (!Segalign
->getValue(1))
200 reportError("segalign is missing an argument: expects -segalign "
201 "arch_type alignment_value");
203 validateArchitectureName(Segalign
->getValue(0));
205 uint32_t AlignmentValue
;
206 if (!to_integer
<uint32_t>(Segalign
->getValue(1), AlignmentValue
, 16))
207 reportError("argument to -segalign <arch_type> " +
208 Twine(Segalign
->getValue(1)) +
209 " (hex) is not a proper hexadecimal number");
210 if (!isPowerOf2_32(AlignmentValue
))
211 reportError("argument to -segalign <arch_type> " +
212 Twine(Segalign
->getValue(1)) +
213 " (hex) must be a non-zero power of two");
214 if (Log2_32(AlignmentValue
) > MachOUniversalBinary::MaxSectionAlignment
)
216 "argument to -segalign <arch_type> " + Twine(Segalign
->getValue(1)) +
217 " (hex) must be less than or equal to the maximum section align 2^" +
218 Twine(MachOUniversalBinary::MaxSectionAlignment
));
219 auto Entry
= C
.SegmentAlignments
.try_emplace(Segalign
->getValue(0),
220 Log2_32(AlignmentValue
));
222 reportError("-segalign " + Twine(Segalign
->getValue(0)) +
223 " <alignment_value> specified multiple times: " +
224 Twine(1 << Entry
.first
->second
) + ", " +
225 Twine(AlignmentValue
));
228 SmallVector
<opt::Arg
*, 1> ActionArgs(InputArgs
.filtered(LIPO_action_group
));
229 if (ActionArgs
.empty())
230 reportError("at least one action should be specified");
231 // errors if multiple actions specified other than replace
232 // multiple replace flags may be specified, as long as they are not mixed with
233 // other action flags
234 auto ReplacementArgsRange
= InputArgs
.filtered(LIPO_replace
);
235 if (ActionArgs
.size() > 1 &&
237 static_cast<size_t>(std::distance(ReplacementArgsRange
.begin(),
238 ReplacementArgsRange
.end()))) {
240 raw_string_ostream
OS(Buf
);
241 OS
<< "only one of the following actions can be specified:";
242 for (auto Arg
: ActionArgs
)
243 OS
<< " " << Arg
->getSpelling();
244 reportError(OS
.str());
247 switch (ActionArgs
[0]->getOption().getID()) {
248 case LIPO_verify_arch
:
249 for (auto A
: InputArgs
.getAllArgValues(LIPO_verify_arch
))
250 C
.VerifyArchList
.push_back(A
);
251 if (C
.VerifyArchList
.empty())
253 "verify_arch requires at least one architecture to be specified");
254 if (C
.InputFiles
.size() > 1)
255 reportError("verify_arch expects a single input file");
256 C
.ActionToPerform
= LipoAction::VerifyArch
;
260 if (C
.InputFiles
.size() > 1)
261 reportError("archs expects a single input file");
262 C
.ActionToPerform
= LipoAction::PrintArchs
;
266 C
.ActionToPerform
= LipoAction::PrintInfo
;
270 if (C
.InputFiles
.size() > 1)
271 reportError("thin expects a single input file");
272 if (C
.OutputFile
.empty())
273 reportError("thin expects a single output file");
274 C
.ArchType
= ActionArgs
[0]->getValue();
275 validateArchitectureName(C
.ArchType
);
276 C
.ActionToPerform
= LipoAction::ThinArch
;
280 if (C
.InputFiles
.size() > 1)
281 reportError("extract expects a single input file");
282 if (C
.OutputFile
.empty())
283 reportError("extract expects a single output file");
284 C
.ArchType
= ActionArgs
[0]->getValue();
285 validateArchitectureName(C
.ArchType
);
286 C
.ActionToPerform
= LipoAction::ExtractArch
;
290 if (C
.OutputFile
.empty())
291 reportError("create expects a single output file to be specified");
292 C
.ActionToPerform
= LipoAction::CreateUniversal
;
296 for (auto Action
: ActionArgs
) {
297 if (!Action
->getValue(1))
299 "replace is missing an argument: expects -replace arch_type "
301 validateArchitectureName(Action
->getValue(0));
302 C
.ReplacementFiles
.push_back(
303 {StringRef(Action
->getValue(0)), Action
->getValue(1)});
306 if (C
.OutputFile
.empty())
307 reportError("replace expects a single output file to be specified");
308 if (C
.InputFiles
.size() > 1)
309 reportError("replace expects a single input file");
310 C
.ActionToPerform
= LipoAction::ReplaceArch
;
314 reportError("llvm-lipo action unspecified");
318 static SmallVector
<OwningBinary
<Binary
>, 1>
319 readInputBinaries(ArrayRef
<InputFile
> InputFiles
) {
320 SmallVector
<OwningBinary
<Binary
>, 1> InputBinaries
;
321 for (const InputFile
&IF
: InputFiles
) {
322 Expected
<OwningBinary
<Binary
>> BinaryOrErr
=
323 createBinary(IF
.FileName
, &LLVMCtx
);
325 reportError(IF
.FileName
, BinaryOrErr
.takeError());
326 const Binary
*B
= BinaryOrErr
->getBinary();
327 if (!B
->isArchive() && !B
->isMachO() && !B
->isMachOUniversalBinary() &&
329 reportError("File " + IF
.FileName
+ " has unsupported binary format");
330 if (IF
.ArchType
&& (B
->isMachO() || B
->isArchive() || B
->isIR())) {
331 const auto S
= B
->isMachO()
332 ? Slice(*cast
<MachOObjectFile
>(B
))
334 ? createSliceFromArchive(*cast
<Archive
>(B
))
335 : createSliceFromIR(*cast
<IRObjectFile
>(B
), 0);
336 const auto SpecifiedCPUType
= MachO::getCPUTypeFromArchitecture(
337 MachO::getArchitectureFromName(
338 Triple(*IF
.ArchType
).getArchName()))
340 // For compatibility with cctools' lipo the comparison is relaxed just to
341 // checking cputypes.
342 if (S
.getCPUType() != SpecifiedCPUType
)
343 reportError("specified architecture: " + *IF
.ArchType
+
344 " for file: " + B
->getFileName() +
345 " does not match the file's architecture (" +
346 S
.getArchString() + ")");
348 InputBinaries
.push_back(std::move(*BinaryOrErr
));
350 return InputBinaries
;
353 [[noreturn
]] static void
354 verifyArch(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
355 ArrayRef
<std::string
> VerifyArchList
) {
356 assert(!VerifyArchList
.empty() &&
357 "The list of architectures should be non-empty");
358 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
360 for (StringRef Arch
: VerifyArchList
)
361 validateArchitectureName(Arch
);
364 dyn_cast
<MachOUniversalBinary
>(InputBinaries
.front().getBinary())) {
365 for (StringRef Arch
: VerifyArchList
) {
366 Expected
<MachOUniversalBinary::ObjectForArch
> Obj
=
367 UO
->getObjectForArch(Arch
);
372 dyn_cast
<MachOObjectFile
>(InputBinaries
.front().getBinary())) {
373 const Triple::ArchType ObjectArch
= O
->getArch();
374 for (StringRef Arch
: VerifyArchList
)
375 if (ObjectArch
!= Triple(Arch
).getArch())
378 llvm_unreachable("Unexpected binary format");
383 static void printBinaryArchs(const Binary
*Binary
, raw_ostream
&OS
) {
384 // Prints trailing space for compatibility with cctools lipo.
385 if (auto UO
= dyn_cast
<MachOUniversalBinary
>(Binary
)) {
386 for (const auto &O
: UO
->objects()) {
387 // Order here is important, because both MachOObjectFile and
388 // IRObjectFile can be created with a binary that has embedded bitcode.
389 Expected
<std::unique_ptr
<MachOObjectFile
>> MachOObjOrError
=
391 if (MachOObjOrError
) {
392 OS
<< Slice(*(MachOObjOrError
->get())).getArchString() << " ";
395 Expected
<std::unique_ptr
<IRObjectFile
>> IROrError
=
396 O
.getAsIRObject(LLVMCtx
);
398 consumeError(MachOObjOrError
.takeError());
399 Expected
<Slice
> SliceOrErr
= Slice::create(**IROrError
, O
.getAlign());
401 reportError(Binary
->getFileName(), SliceOrErr
.takeError());
404 OS
<< SliceOrErr
.get().getArchString() << " ";
407 Expected
<std::unique_ptr
<Archive
>> ArchiveOrError
= O
.getAsArchive();
408 if (ArchiveOrError
) {
409 consumeError(MachOObjOrError
.takeError());
410 consumeError(IROrError
.takeError());
411 OS
<< createSliceFromArchive(**ArchiveOrError
).getArchString() << " ";
414 consumeError(ArchiveOrError
.takeError());
415 reportError(Binary
->getFileName(), MachOObjOrError
.takeError());
416 reportError(Binary
->getFileName(), IROrError
.takeError());
422 if (const auto *MachO
= dyn_cast
<MachOObjectFile
>(Binary
)) {
423 OS
<< Slice(*MachO
).getArchString() << " \n";
427 // This should be always the case, as this is tested in readInputBinaries
428 const auto *IR
= cast
<IRObjectFile
>(Binary
);
429 Expected
<Slice
> SliceOrErr
= createSliceFromIR(*IR
, 0);
431 reportError(IR
->getFileName(), SliceOrErr
.takeError());
433 OS
<< SliceOrErr
->getArchString() << " \n";
436 [[noreturn
]] static void
437 printArchs(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
) {
438 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
439 printBinaryArchs(InputBinaries
.front().getBinary(), outs());
443 [[noreturn
]] static void
444 printInfo(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
) {
445 // Group universal and thin files together for compatibility with cctools lipo
446 for (auto &IB
: InputBinaries
) {
447 const Binary
*Binary
= IB
.getBinary();
448 if (Binary
->isMachOUniversalBinary()) {
449 outs() << "Architectures in the fat file: " << Binary
->getFileName()
451 printBinaryArchs(Binary
, outs());
454 for (auto &IB
: InputBinaries
) {
455 const Binary
*Binary
= IB
.getBinary();
456 if (!Binary
->isMachOUniversalBinary()) {
457 assert(Binary
->isMachO() && "expected MachO binary");
458 outs() << "Non-fat file: " << Binary
->getFileName()
459 << " is architecture: ";
460 printBinaryArchs(Binary
, outs());
466 [[noreturn
]] static void thinSlice(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
468 StringRef OutputFileName
) {
469 assert(!ArchType
.empty() && "The architecture type should be non-empty");
470 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
471 assert(!OutputFileName
.empty() && "Thin expects a single output file");
473 if (InputBinaries
.front().getBinary()->isMachO()) {
474 reportError("input file " +
475 InputBinaries
.front().getBinary()->getFileName() +
476 " must be a fat file when the -thin option is specified");
480 auto *UO
= cast
<MachOUniversalBinary
>(InputBinaries
.front().getBinary());
481 Expected
<std::unique_ptr
<MachOObjectFile
>> Obj
=
482 UO
->getMachOObjectForArch(ArchType
);
483 Expected
<std::unique_ptr
<IRObjectFile
>> IRObj
=
484 UO
->getIRObjectForArch(ArchType
, LLVMCtx
);
485 Expected
<std::unique_ptr
<Archive
>> Ar
= UO
->getArchiveForArch(ArchType
);
486 if (!Obj
&& !IRObj
&& !Ar
)
487 reportError("fat input file " + UO
->getFileName() +
488 " does not contain the specified architecture " + ArchType
+
491 // Order here is important, because both Obj and IRObj will be valid with a
492 // binary that has embedded bitcode.
500 Expected
<std::unique_ptr
<FileOutputBuffer
>> OutFileOrError
=
501 FileOutputBuffer::create(OutputFileName
,
502 B
->getMemoryBufferRef().getBufferSize(),
503 sys::fs::can_execute(UO
->getFileName())
504 ? FileOutputBuffer::F_executable
507 reportError(OutputFileName
, OutFileOrError
.takeError());
508 std::copy(B
->getMemoryBufferRef().getBufferStart(),
509 B
->getMemoryBufferRef().getBufferEnd(),
510 OutFileOrError
.get()->getBufferStart());
511 if (Error E
= OutFileOrError
.get()->commit())
512 reportError(OutputFileName
, std::move(E
));
516 static void checkArchDuplicates(ArrayRef
<Slice
> Slices
) {
517 DenseMap
<uint64_t, const Binary
*> CPUIds
;
518 for (const auto &S
: Slices
) {
519 auto Entry
= CPUIds
.try_emplace(S
.getCPUID(), S
.getBinary());
521 reportError(Entry
.first
->second
->getFileName() + " and " +
522 S
.getBinary()->getFileName() +
523 " have the same architecture " + S
.getArchString() +
524 " and therefore cannot be in the same universal binary");
528 template <typename Range
>
529 static void updateAlignments(Range
&Slices
,
530 const StringMap
<const uint32_t> &Alignments
) {
531 for (auto &Slice
: Slices
) {
532 auto Alignment
= Alignments
.find(Slice
.getArchString());
533 if (Alignment
!= Alignments
.end())
534 Slice
.setP2Alignment(Alignment
->second
);
538 static void checkUnusedAlignments(ArrayRef
<Slice
> Slices
,
539 const StringMap
<const uint32_t> &Alignments
) {
540 auto HasArch
= [&](StringRef Arch
) {
541 return llvm::any_of(Slices
,
542 [Arch
](Slice S
) { return S
.getArchString() == Arch
; });
544 for (StringRef Arch
: Alignments
.keys())
546 reportError("-segalign " + Arch
+
547 " <value> specified but resulting fat file does not contain "
548 "that architecture ");
551 // Updates vector ExtractedObjects with the MachOObjectFiles extracted from
552 // Universal Binary files to transfer ownership.
553 static SmallVector
<Slice
, 2>
554 buildSlices(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
555 const StringMap
<const uint32_t> &Alignments
,
556 SmallVectorImpl
<std::unique_ptr
<SymbolicFile
>> &ExtractedObjects
) {
557 SmallVector
<Slice
, 2> Slices
;
558 for (auto &IB
: InputBinaries
) {
559 const Binary
*InputBinary
= IB
.getBinary();
560 if (auto UO
= dyn_cast
<MachOUniversalBinary
>(InputBinary
)) {
561 for (const auto &O
: UO
->objects()) {
562 // Order here is important, because both MachOObjectFile and
563 // IRObjectFile can be created with a binary that has embedded bitcode.
564 Expected
<std::unique_ptr
<MachOObjectFile
>> BinaryOrError
=
567 Slices
.emplace_back(*(BinaryOrError
.get()), O
.getAlign());
568 ExtractedObjects
.push_back(std::move(BinaryOrError
.get()));
571 Expected
<std::unique_ptr
<IRObjectFile
>> IROrError
=
572 O
.getAsIRObject(LLVMCtx
);
574 consumeError(BinaryOrError
.takeError());
575 Slice S
= createSliceFromIR(**IROrError
, O
.getAlign());
576 ExtractedObjects
.emplace_back(std::move(IROrError
.get()));
577 Slices
.emplace_back(std::move(S
));
580 reportError(InputBinary
->getFileName(), BinaryOrError
.takeError());
582 } else if (const auto *O
= dyn_cast
<MachOObjectFile
>(InputBinary
)) {
583 Slices
.emplace_back(*O
);
584 } else if (const auto *A
= dyn_cast
<Archive
>(InputBinary
)) {
585 Slices
.push_back(createSliceFromArchive(*A
));
586 } else if (const auto *IRO
= dyn_cast
<IRObjectFile
>(InputBinary
)) {
587 // Original Apple's lipo set the alignment to 0
588 Expected
<Slice
> SliceOrErr
= Slice::create(*IRO
, 0);
590 reportError(InputBinary
->getFileName(), SliceOrErr
.takeError());
593 Slices
.emplace_back(std::move(SliceOrErr
.get()));
595 llvm_unreachable("Unexpected binary format");
598 updateAlignments(Slices
, Alignments
);
602 [[noreturn
]] static void
603 createUniversalBinary(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
604 const StringMap
<const uint32_t> &Alignments
,
605 StringRef OutputFileName
) {
606 assert(InputBinaries
.size() >= 1 && "Incorrect number of input binaries");
607 assert(!OutputFileName
.empty() && "Create expects a single output file");
609 SmallVector
<std::unique_ptr
<SymbolicFile
>, 1> ExtractedObjects
;
610 SmallVector
<Slice
, 1> Slices
=
611 buildSlices(InputBinaries
, Alignments
, ExtractedObjects
);
612 checkArchDuplicates(Slices
);
613 checkUnusedAlignments(Slices
, Alignments
);
615 llvm::stable_sort(Slices
);
616 if (Error E
= writeUniversalBinary(Slices
, OutputFileName
))
617 reportError(std::move(E
));
622 [[noreturn
]] static void
623 extractSlice(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
624 const StringMap
<const uint32_t> &Alignments
, StringRef ArchType
,
625 StringRef OutputFileName
) {
626 assert(!ArchType
.empty() &&
627 "The architecture type should be non-empty");
628 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
629 assert(!OutputFileName
.empty() && "Thin expects a single output file");
631 if (InputBinaries
.front().getBinary()->isMachO()) {
632 reportError("input file " +
633 InputBinaries
.front().getBinary()->getFileName() +
634 " must be a fat file when the -extract option is specified");
637 SmallVector
<std::unique_ptr
<SymbolicFile
>, 2> ExtractedObjects
;
638 SmallVector
<Slice
, 2> Slices
=
639 buildSlices(InputBinaries
, Alignments
, ExtractedObjects
);
640 erase_if(Slices
, [ArchType
](const Slice
&S
) {
641 return ArchType
!= S
.getArchString();
646 "fat input file " + InputBinaries
.front().getBinary()->getFileName() +
647 " does not contain the specified architecture " + ArchType
);
649 llvm::stable_sort(Slices
);
650 if (Error E
= writeUniversalBinary(Slices
, OutputFileName
))
651 reportError(std::move(E
));
655 static StringMap
<Slice
>
656 buildReplacementSlices(ArrayRef
<OwningBinary
<Binary
>> ReplacementBinaries
,
657 const StringMap
<const uint32_t> &Alignments
) {
658 StringMap
<Slice
> Slices
;
659 // populates StringMap of slices to replace with; error checks for mismatched
660 // replace flag args, fat files, and duplicate arch_types
661 for (const auto &OB
: ReplacementBinaries
) {
662 const Binary
*ReplacementBinary
= OB
.getBinary();
663 auto O
= dyn_cast
<MachOObjectFile
>(ReplacementBinary
);
665 reportError("replacement file: " + ReplacementBinary
->getFileName() +
666 " is a fat file (must be a thin file)");
668 auto Entry
= Slices
.try_emplace(S
.getArchString(), S
);
670 reportError("-replace " + S
.getArchString() +
671 " <file_name> specified multiple times: " +
672 Entry
.first
->second
.getBinary()->getFileName() + ", " +
675 auto SlicesMapRange
= map_range(
676 Slices
, [](StringMapEntry
<Slice
> &E
) -> Slice
& { return E
.getValue(); });
677 updateAlignments(SlicesMapRange
, Alignments
);
681 [[noreturn
]] static void
682 replaceSlices(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
683 const StringMap
<const uint32_t> &Alignments
,
684 StringRef OutputFileName
, ArrayRef
<InputFile
> ReplacementFiles
) {
685 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
686 assert(!OutputFileName
.empty() && "Replace expects a single output file");
688 if (InputBinaries
.front().getBinary()->isMachO())
689 reportError("input file " +
690 InputBinaries
.front().getBinary()->getFileName() +
691 " must be a fat file when the -replace option is specified");
693 SmallVector
<OwningBinary
<Binary
>, 1> ReplacementBinaries
=
694 readInputBinaries(ReplacementFiles
);
696 StringMap
<Slice
> ReplacementSlices
=
697 buildReplacementSlices(ReplacementBinaries
, Alignments
);
698 SmallVector
<std::unique_ptr
<SymbolicFile
>, 2> ExtractedObjects
;
699 SmallVector
<Slice
, 2> Slices
=
700 buildSlices(InputBinaries
, Alignments
, ExtractedObjects
);
702 for (auto &Slice
: Slices
) {
703 auto It
= ReplacementSlices
.find(Slice
.getArchString());
704 if (It
!= ReplacementSlices
.end()) {
706 ReplacementSlices
.erase(It
); // only keep remaining replacing arch_types
710 if (!ReplacementSlices
.empty())
711 reportError("-replace " + ReplacementSlices
.begin()->first() +
712 " <file_name> specified but fat file: " +
713 InputBinaries
.front().getBinary()->getFileName() +
714 " does not contain that architecture");
716 checkUnusedAlignments(Slices
, Alignments
);
718 llvm::stable_sort(Slices
);
719 if (Error E
= writeUniversalBinary(Slices
, OutputFileName
))
720 reportError(std::move(E
));
724 int main(int argc
, char **argv
) {
725 InitLLVM
X(argc
, argv
);
726 Config C
= parseLipoOptions(makeArrayRef(argv
+ 1, argc
));
727 SmallVector
<OwningBinary
<Binary
>, 1> InputBinaries
=
728 readInputBinaries(C
.InputFiles
);
730 switch (C
.ActionToPerform
) {
731 case LipoAction::VerifyArch
:
732 verifyArch(InputBinaries
, C
.VerifyArchList
);
734 case LipoAction::PrintArchs
:
735 printArchs(InputBinaries
);
737 case LipoAction::PrintInfo
:
738 printInfo(InputBinaries
);
740 case LipoAction::ThinArch
:
741 thinSlice(InputBinaries
, C
.ArchType
, C
.OutputFile
);
743 case LipoAction::ExtractArch
:
744 extractSlice(InputBinaries
, C
.SegmentAlignments
, C
.ArchType
, C
.OutputFile
);
746 case LipoAction::CreateUniversal
:
747 createUniversalBinary(InputBinaries
, C
.SegmentAlignments
, C
.OutputFile
);
749 case LipoAction::ReplaceArch
:
750 replaceSlices(InputBinaries
, C
.SegmentAlignments
, C
.OutputFile
,