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"
26 #include "llvm/TextAPI/MachO/Architecture.h"
29 using namespace llvm::object
;
31 static const StringRef ToolName
= "llvm-lipo";
33 LLVM_ATTRIBUTE_NORETURN
static void reportError(Twine Message
) {
34 WithColor::error(errs(), ToolName
) << Message
<< "\n";
39 LLVM_ATTRIBUTE_NORETURN
static void reportError(StringRef File
, Error E
) {
42 raw_string_ostream
OS(Buf
);
43 logAllUnhandledErrors(std::move(E
), OS
);
45 WithColor::error(errs(), ToolName
) << "'" << File
<< "': " << Buf
;
51 LIPO_INVALID
= 0, // This is not an option ID.
52 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
53 HELPTEXT, METAVAR, VALUES) \
55 #include "LipoOpts.inc"
59 // LipoInfoTable below references LIPO_##PREFIX. OptionGroup has prefix nullptr.
60 const char *const *LIPO_nullptr
= nullptr;
61 #define PREFIX(NAME, VALUE) const char *const LIPO_##NAME[] = VALUE;
62 #include "LipoOpts.inc"
65 static const opt::OptTable::Info LipoInfoTable
[] = {
66 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
67 HELPTEXT, METAVAR, VALUES) \
68 {LIPO_##PREFIX, NAME, HELPTEXT, \
69 METAVAR, LIPO_##ID, opt::Option::KIND##Class, \
70 PARAM, FLAGS, LIPO_##GROUP, \
71 LIPO_##ALIAS, ALIASARGS, VALUES},
72 #include "LipoOpts.inc"
76 class LipoOptTable
: public opt::OptTable
{
78 LipoOptTable() : OptTable(LipoInfoTable
) {}
81 enum class LipoAction
{
91 Optional
<StringRef
> ArchType
;
96 SmallVector
<InputFile
, 1> InputFiles
;
97 SmallVector
<std::string
, 1> VerifyArchList
;
98 SmallVector
<InputFile
, 1> ReplacementFiles
;
99 StringMap
<const uint32_t> SegmentAlignments
;
100 std::string ThinArchType
;
101 std::string OutputFile
;
102 LipoAction ActionToPerform
;
105 // For compatibility with cctools lipo, a file's alignment is calculated as the
106 // minimum aligment of all segments. For object files, the file's alignment is
107 // the maximum alignment of its sections.
108 static uint32_t calculateFileAlignment(const MachOObjectFile
&O
) {
109 uint32_t P2CurrentAlignment
;
110 uint32_t P2MinAlignment
= MachOUniversalBinary::MaxSectionAlignment
;
111 const bool Is64Bit
= O
.is64Bit();
113 for (const auto &LC
: O
.load_commands()) {
114 if (LC
.C
.cmd
!= (Is64Bit
? MachO::LC_SEGMENT_64
: MachO::LC_SEGMENT
))
116 if (O
.getHeader().filetype
== MachO::MH_OBJECT
) {
117 unsigned NumberOfSections
=
118 (Is64Bit
? O
.getSegment64LoadCommand(LC
).nsects
119 : O
.getSegmentLoadCommand(LC
).nsects
);
120 P2CurrentAlignment
= NumberOfSections
? 2 : P2MinAlignment
;
121 for (unsigned SI
= 0; SI
< NumberOfSections
; ++SI
) {
122 P2CurrentAlignment
= std::max(P2CurrentAlignment
,
123 (Is64Bit
? O
.getSection64(LC
, SI
).align
124 : O
.getSection(LC
, SI
).align
));
128 countTrailingZeros(Is64Bit
? O
.getSegment64LoadCommand(LC
).vmaddr
129 : O
.getSegmentLoadCommand(LC
).vmaddr
);
131 P2MinAlignment
= std::min(P2MinAlignment
, P2CurrentAlignment
);
133 // return a value >= 4 byte aligned, and less than MachO MaxSectionAlignment
135 static_cast<uint32_t>(2),
136 std::min(P2MinAlignment
, static_cast<uint32_t>(
137 MachOUniversalBinary::MaxSectionAlignment
)));
140 static uint32_t calculateAlignment(const MachOObjectFile
*ObjectFile
) {
141 switch (ObjectFile
->getHeader().cputype
) {
142 case MachO::CPU_TYPE_I386
:
143 case MachO::CPU_TYPE_X86_64
:
144 case MachO::CPU_TYPE_POWERPC
:
145 case MachO::CPU_TYPE_POWERPC64
:
146 return 12; // log2 value of page size(4k) for x86 and PPC
147 case MachO::CPU_TYPE_ARM
:
148 case MachO::CPU_TYPE_ARM64
:
149 case MachO::CPU_TYPE_ARM64_32
:
150 return 14; // log2 value of page size(16k) for Darwin ARM
152 return calculateFileAlignment(*ObjectFile
);
160 std::string ArchName
;
162 // P2Alignment field stores slice alignment values from universal
163 // binaries. This is also needed to order the slices so the total
164 // file size can be calculated before creating the output buffer.
165 uint32_t P2Alignment
;
168 Slice(const MachOObjectFile
*O
, uint32_t Align
)
169 : B(O
), CPUType(O
->getHeader().cputype
),
170 CPUSubType(O
->getHeader().cpusubtype
),
171 ArchName(O
->getArchTriple().getArchName()), P2Alignment(Align
) {}
173 explicit Slice(const MachOObjectFile
*O
) : Slice(O
, calculateAlignment(O
)){};
175 explicit Slice(const Archive
*A
) : B(A
) {
176 Error Err
= Error::success();
177 std::unique_ptr
<MachOObjectFile
> FO
= nullptr;
178 for (const Archive::Child
&Child
: A
->children(Err
)) {
179 Expected
<std::unique_ptr
<Binary
>> ChildOrErr
= Child
.getAsBinary();
181 reportError(A
->getFileName(), ChildOrErr
.takeError());
182 Binary
*Bin
= ChildOrErr
.get().get();
183 if (Bin
->isMachOUniversalBinary())
184 reportError(("archive member " + Bin
->getFileName() +
185 " is a fat file (not allowed in an archive)")
188 reportError(("archive member " + Bin
->getFileName() +
189 " is not a MachO file (not allowed in an archive)"));
190 MachOObjectFile
*O
= cast
<MachOObjectFile
>(Bin
);
192 std::tie(FO
->getHeader().cputype
, FO
->getHeader().cpusubtype
) !=
193 std::tie(O
->getHeader().cputype
, O
->getHeader().cpusubtype
)) {
194 reportError(("archive member " + O
->getFileName() + " cputype (" +
195 Twine(O
->getHeader().cputype
) + ") and cpusubtype(" +
196 Twine(O
->getHeader().cpusubtype
) +
197 ") does not match previous archive members cputype (" +
198 Twine(FO
->getHeader().cputype
) + ") and cpusubtype(" +
199 Twine(FO
->getHeader().cpusubtype
) +
200 ") (all members must match) " + FO
->getFileName())
204 ChildOrErr
.get().release();
209 reportError(A
->getFileName(), std::move(Err
));
211 reportError(("empty archive with no architecture specification: " +
212 A
->getFileName() + " (can't determine architecture for it)")
214 CPUType
= FO
->getHeader().cputype
;
215 CPUSubType
= FO
->getHeader().cpusubtype
;
216 ArchName
= FO
->getArchTriple().getArchName();
217 // Replicate the behavior of cctools lipo.
218 P2Alignment
= FO
->is64Bit() ? 3 : 2;
221 void setP2Alignment(uint32_t Align
) { P2Alignment
= Align
; }
223 const Binary
*getBinary() const { return B
; }
225 uint32_t getCPUType() const { return CPUType
; }
227 uint32_t getCPUSubType() const { return CPUSubType
; }
229 uint32_t getP2Alignment() const { return P2Alignment
; }
231 uint64_t getCPUID() const {
232 return static_cast<uint64_t>(CPUType
) << 32 | CPUSubType
;
235 std::string
getArchString() const {
236 if (!ArchName
.empty())
238 return ("unknown(" + Twine(CPUType
) + "," +
239 Twine(CPUSubType
& ~MachO::CPU_SUBTYPE_MASK
) + ")")
243 friend bool operator<(const Slice
&Lhs
, const Slice
&Rhs
) {
244 if (Lhs
.CPUType
== Rhs
.CPUType
)
245 return Lhs
.CPUSubType
< Rhs
.CPUSubType
;
246 // force arm64-family to follow after all other slices for
247 // compatibility with cctools lipo
248 if (Lhs
.CPUType
== MachO::CPU_TYPE_ARM64
)
250 if (Rhs
.CPUType
== MachO::CPU_TYPE_ARM64
)
252 // Sort by alignment to minimize file size
253 return Lhs
.P2Alignment
< Rhs
.P2Alignment
;
259 static void validateArchitectureName(StringRef ArchitectureName
) {
260 if (!MachOObjectFile::isValidArch(ArchitectureName
)) {
262 raw_string_ostream
OS(Buf
);
263 OS
<< "Invalid architecture: " << ArchitectureName
264 << "\nValid architecture names are:";
265 for (auto arch
: MachOObjectFile::getValidArchs())
267 reportError(OS
.str());
271 static Config
parseLipoOptions(ArrayRef
<const char *> ArgsArr
) {
274 unsigned MissingArgumentIndex
, MissingArgumentCount
;
275 opt::InputArgList InputArgs
=
276 T
.ParseArgs(ArgsArr
, MissingArgumentIndex
, MissingArgumentCount
);
278 if (MissingArgumentCount
)
279 reportError("missing argument to " +
280 StringRef(InputArgs
.getArgString(MissingArgumentIndex
)) +
283 if (InputArgs
.size() == 0) {
284 // PrintHelp does not accept Twine.
285 T
.PrintHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
289 if (InputArgs
.hasArg(LIPO_help
)) {
290 // PrintHelp does not accept Twine.
291 T
.PrintHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
295 if (InputArgs
.hasArg(LIPO_version
)) {
296 outs() << ToolName
+ "\n";
297 cl::PrintVersionMessage();
301 for (auto Arg
: InputArgs
.filtered(LIPO_UNKNOWN
))
302 reportError("unknown argument '" + Arg
->getAsString(InputArgs
) + "'");
304 for (auto Arg
: InputArgs
.filtered(LIPO_INPUT
))
305 C
.InputFiles
.push_back({None
, Arg
->getValue()});
306 for (auto Arg
: InputArgs
.filtered(LIPO_arch
)) {
307 validateArchitectureName(Arg
->getValue(0));
308 if (!Arg
->getValue(1))
310 "arch is missing an argument: expects -arch arch_type file_name");
311 C
.InputFiles
.push_back({StringRef(Arg
->getValue(0)), Arg
->getValue(1)});
314 if (C
.InputFiles
.empty())
315 reportError("at least one input file should be specified");
317 if (InputArgs
.hasArg(LIPO_output
))
318 C
.OutputFile
= InputArgs
.getLastArgValue(LIPO_output
);
320 for (auto Segalign
: InputArgs
.filtered(LIPO_segalign
)) {
321 if (!Segalign
->getValue(1))
322 reportError("segalign is missing an argument: expects -segalign "
323 "arch_type alignment_value");
325 validateArchitectureName(Segalign
->getValue(0));
327 uint32_t AlignmentValue
;
328 if (!to_integer
<uint32_t>(Segalign
->getValue(1), AlignmentValue
, 16))
329 reportError("argument to -segalign <arch_type> " +
330 Twine(Segalign
->getValue(1)) +
331 " (hex) is not a proper hexadecimal number");
332 if (!isPowerOf2_32(AlignmentValue
))
333 reportError("argument to -segalign <arch_type> " +
334 Twine(Segalign
->getValue(1)) +
335 " (hex) must be a non-zero power of two");
336 if (Log2_32(AlignmentValue
) > MachOUniversalBinary::MaxSectionAlignment
)
338 "argument to -segalign <arch_type> " + Twine(Segalign
->getValue(1)) +
339 " (hex) must be less than or equal to the maximum section align 2^" +
340 Twine(MachOUniversalBinary::MaxSectionAlignment
));
341 auto Entry
= C
.SegmentAlignments
.try_emplace(Segalign
->getValue(0),
342 Log2_32(AlignmentValue
));
344 reportError("-segalign " + Twine(Segalign
->getValue(0)) +
345 " <alignment_value> specified multiple times: " +
346 Twine(1 << Entry
.first
->second
) + ", " +
347 Twine(AlignmentValue
));
350 SmallVector
<opt::Arg
*, 1> ActionArgs(InputArgs
.filtered(LIPO_action_group
));
351 if (ActionArgs
.empty())
352 reportError("at least one action should be specified");
353 // errors if multiple actions specified other than replace
354 // multiple replace flags may be specified, as long as they are not mixed with
355 // other action flags
356 auto ReplacementArgsRange
= InputArgs
.filtered(LIPO_replace
);
357 if (ActionArgs
.size() > 1 &&
359 static_cast<size_t>(std::distance(ReplacementArgsRange
.begin(),
360 ReplacementArgsRange
.end()))) {
362 raw_string_ostream
OS(Buf
);
363 OS
<< "only one of the following actions can be specified:";
364 for (auto Arg
: ActionArgs
)
365 OS
<< " " << Arg
->getSpelling();
366 reportError(OS
.str());
369 switch (ActionArgs
[0]->getOption().getID()) {
370 case LIPO_verify_arch
:
371 for (auto A
: InputArgs
.getAllArgValues(LIPO_verify_arch
))
372 C
.VerifyArchList
.push_back(A
);
373 if (C
.VerifyArchList
.empty())
375 "verify_arch requires at least one architecture to be specified");
376 if (C
.InputFiles
.size() > 1)
377 reportError("verify_arch expects a single input file");
378 C
.ActionToPerform
= LipoAction::VerifyArch
;
382 if (C
.InputFiles
.size() > 1)
383 reportError("archs expects a single input file");
384 C
.ActionToPerform
= LipoAction::PrintArchs
;
388 C
.ActionToPerform
= LipoAction::PrintInfo
;
392 if (C
.InputFiles
.size() > 1)
393 reportError("thin expects a single input file");
394 C
.ThinArchType
= ActionArgs
[0]->getValue();
395 validateArchitectureName(C
.ThinArchType
);
396 if (C
.OutputFile
.empty())
397 reportError("thin expects a single output file");
399 C
.ActionToPerform
= LipoAction::ThinArch
;
403 if (C
.OutputFile
.empty())
404 reportError("create expects a single output file to be specified");
405 C
.ActionToPerform
= LipoAction::CreateUniversal
;
409 for (auto Action
: ActionArgs
) {
410 if (!Action
->getValue(1))
412 "replace is missing an argument: expects -replace arch_type "
414 validateArchitectureName(Action
->getValue(0));
415 C
.ReplacementFiles
.push_back(
416 {StringRef(Action
->getValue(0)), Action
->getValue(1)});
419 if (C
.OutputFile
.empty())
420 reportError("replace expects a single output file to be specified");
421 if (C
.InputFiles
.size() > 1)
422 reportError("replace expects a single input file");
423 C
.ActionToPerform
= LipoAction::ReplaceArch
;
427 reportError("llvm-lipo action unspecified");
431 static SmallVector
<OwningBinary
<Binary
>, 1>
432 readInputBinaries(ArrayRef
<InputFile
> InputFiles
) {
433 SmallVector
<OwningBinary
<Binary
>, 1> InputBinaries
;
434 for (const InputFile
&IF
: InputFiles
) {
435 Expected
<OwningBinary
<Binary
>> BinaryOrErr
= createBinary(IF
.FileName
);
437 reportError(IF
.FileName
, BinaryOrErr
.takeError());
438 const Binary
*B
= BinaryOrErr
->getBinary();
439 if (!B
->isArchive() && !B
->isMachO() && !B
->isMachOUniversalBinary())
440 reportError("File " + IF
.FileName
+ " has unsupported binary format");
441 if (IF
.ArchType
&& (B
->isMachO() || B
->isArchive())) {
442 const auto S
= B
->isMachO() ? Slice(cast
<MachOObjectFile
>(B
))
443 : Slice(cast
<Archive
>(B
));
444 const auto SpecifiedCPUType
= MachO::getCPUTypeFromArchitecture(
445 MachO::getArchitectureFromName(
446 Triple(*IF
.ArchType
).getArchName()))
448 // For compatibility with cctools' lipo the comparison is relaxed just to
449 // checking cputypes.
450 if (S
.getCPUType() != SpecifiedCPUType
)
451 reportError("specified architecture: " + *IF
.ArchType
+
452 " for file: " + B
->getFileName() +
453 " does not match the file's architecture (" +
454 S
.getArchString() + ")");
456 InputBinaries
.push_back(std::move(*BinaryOrErr
));
458 return InputBinaries
;
461 LLVM_ATTRIBUTE_NORETURN
462 static void verifyArch(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
463 ArrayRef
<std::string
> VerifyArchList
) {
464 assert(!VerifyArchList
.empty() &&
465 "The list of architectures should be non-empty");
466 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
468 for (StringRef Arch
: VerifyArchList
)
469 validateArchitectureName(Arch
);
472 dyn_cast
<MachOUniversalBinary
>(InputBinaries
.front().getBinary())) {
473 for (StringRef Arch
: VerifyArchList
) {
474 Expected
<MachOUniversalBinary::ObjectForArch
> Obj
=
475 UO
->getObjectForArch(Arch
);
480 dyn_cast
<MachOObjectFile
>(InputBinaries
.front().getBinary())) {
481 const Triple::ArchType ObjectArch
= O
->getArch();
482 for (StringRef Arch
: VerifyArchList
)
483 if (ObjectArch
!= Triple(Arch
).getArch())
486 llvm_unreachable("Unexpected binary format");
491 static void printBinaryArchs(const Binary
*Binary
, raw_ostream
&OS
) {
492 // Prints trailing space for compatibility with cctools lipo.
493 if (auto UO
= dyn_cast
<MachOUniversalBinary
>(Binary
)) {
494 for (const auto &O
: UO
->objects()) {
495 Expected
<std::unique_ptr
<MachOObjectFile
>> MachOObjOrError
=
497 if (MachOObjOrError
) {
498 OS
<< Slice(MachOObjOrError
->get()).getArchString() << " ";
501 Expected
<std::unique_ptr
<Archive
>> ArchiveOrError
= O
.getAsArchive();
502 if (ArchiveOrError
) {
503 consumeError(MachOObjOrError
.takeError());
504 OS
<< Slice(ArchiveOrError
->get()).getArchString() << " ";
507 consumeError(ArchiveOrError
.takeError());
508 reportError(Binary
->getFileName(), MachOObjOrError
.takeError());
513 OS
<< Slice(cast
<MachOObjectFile
>(Binary
)).getArchString() << " \n";
516 LLVM_ATTRIBUTE_NORETURN
517 static void printArchs(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
) {
518 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
519 printBinaryArchs(InputBinaries
.front().getBinary(), outs());
523 LLVM_ATTRIBUTE_NORETURN
524 static void printInfo(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
) {
525 // Group universal and thin files together for compatibility with cctools lipo
526 for (auto &IB
: InputBinaries
) {
527 const Binary
*Binary
= IB
.getBinary();
528 if (Binary
->isMachOUniversalBinary()) {
529 outs() << "Architectures in the fat file: " << Binary
->getFileName()
531 printBinaryArchs(Binary
, outs());
534 for (auto &IB
: InputBinaries
) {
535 const Binary
*Binary
= IB
.getBinary();
536 if (!Binary
->isMachOUniversalBinary()) {
537 assert(Binary
->isMachO() && "expected MachO binary");
538 outs() << "Non-fat file: " << Binary
->getFileName()
539 << " is architecture: ";
540 printBinaryArchs(Binary
, outs());
546 LLVM_ATTRIBUTE_NORETURN
547 static void extractSlice(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
548 StringRef ThinArchType
, StringRef OutputFileName
) {
549 assert(!ThinArchType
.empty() && "The architecture type should be non-empty");
550 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
551 assert(!OutputFileName
.empty() && "Thin expects a single output file");
553 if (InputBinaries
.front().getBinary()->isMachO()) {
554 reportError("input file " +
555 InputBinaries
.front().getBinary()->getFileName() +
556 " must be a fat file when the -thin option is specified");
560 auto *UO
= cast
<MachOUniversalBinary
>(InputBinaries
.front().getBinary());
561 Expected
<std::unique_ptr
<MachOObjectFile
>> Obj
=
562 UO
->getMachOObjectForArch(ThinArchType
);
563 Expected
<std::unique_ptr
<Archive
>> Ar
= UO
->getArchiveForArch(ThinArchType
);
565 reportError("fat input file " + UO
->getFileName() +
566 " does not contain the specified architecture " + ThinArchType
+
568 Binary
*B
= Obj
? static_cast<Binary
*>(Obj
->get())
569 : static_cast<Binary
*>(Ar
->get());
570 Expected
<std::unique_ptr
<FileOutputBuffer
>> OutFileOrError
=
571 FileOutputBuffer::create(OutputFileName
,
572 B
->getMemoryBufferRef().getBufferSize(),
573 sys::fs::can_execute(UO
->getFileName())
574 ? FileOutputBuffer::F_executable
577 reportError(OutputFileName
, OutFileOrError
.takeError());
578 std::copy(B
->getMemoryBufferRef().getBufferStart(),
579 B
->getMemoryBufferRef().getBufferEnd(),
580 OutFileOrError
.get()->getBufferStart());
581 if (Error E
= OutFileOrError
.get()->commit())
582 reportError(OutputFileName
, std::move(E
));
586 static void checkArchDuplicates(ArrayRef
<Slice
> Slices
) {
587 DenseMap
<uint64_t, const Binary
*> CPUIds
;
588 for (const auto &S
: Slices
) {
589 auto Entry
= CPUIds
.try_emplace(S
.getCPUID(), S
.getBinary());
591 reportError(Entry
.first
->second
->getFileName() + " and " +
592 S
.getBinary()->getFileName() +
593 " have the same architecture " + S
.getArchString() +
594 " and therefore cannot be in the same universal binary");
598 template <typename Range
>
599 static void updateAlignments(Range
&Slices
,
600 const StringMap
<const uint32_t> &Alignments
) {
601 for (auto &Slice
: Slices
) {
602 auto Alignment
= Alignments
.find(Slice
.getArchString());
603 if (Alignment
!= Alignments
.end())
604 Slice
.setP2Alignment(Alignment
->second
);
608 static void checkUnusedAlignments(ArrayRef
<Slice
> Slices
,
609 const StringMap
<const uint32_t> &Alignments
) {
610 auto HasArch
= [&](StringRef Arch
) {
611 return llvm::find_if(Slices
, [Arch
](Slice S
) {
612 return S
.getArchString() == Arch
;
615 for (StringRef Arch
: Alignments
.keys())
617 reportError("-segalign " + Arch
+
618 " <value> specified but resulting fat file does not contain "
619 "that architecture ");
622 // Updates vector ExtractedObjects with the MachOObjectFiles extracted from
623 // Universal Binary files to transfer ownership.
624 static SmallVector
<Slice
, 2> buildSlices(
625 ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
626 const StringMap
<const uint32_t> &Alignments
,
627 SmallVectorImpl
<std::unique_ptr
<MachOObjectFile
>> &ExtractedObjects
) {
628 SmallVector
<Slice
, 2> Slices
;
629 for (auto &IB
: InputBinaries
) {
630 const Binary
*InputBinary
= IB
.getBinary();
631 if (auto UO
= dyn_cast
<MachOUniversalBinary
>(InputBinary
)) {
632 for (const auto &O
: UO
->objects()) {
633 Expected
<std::unique_ptr
<MachOObjectFile
>> BinaryOrError
=
636 reportError(InputBinary
->getFileName(), BinaryOrError
.takeError());
637 ExtractedObjects
.push_back(std::move(BinaryOrError
.get()));
638 Slices
.emplace_back(ExtractedObjects
.back().get(), O
.getAlign());
640 } else if (auto O
= dyn_cast
<MachOObjectFile
>(InputBinary
)) {
641 Slices
.emplace_back(O
);
642 } else if (auto A
= dyn_cast
<Archive
>(InputBinary
)) {
643 Slices
.emplace_back(A
);
645 llvm_unreachable("Unexpected binary format");
648 updateAlignments(Slices
, Alignments
);
652 static SmallVector
<MachO::fat_arch
, 2>
653 buildFatArchList(ArrayRef
<Slice
> Slices
) {
654 SmallVector
<MachO::fat_arch
, 2> FatArchList
;
656 sizeof(MachO::fat_header
) + Slices
.size() * sizeof(MachO::fat_arch
);
658 for (const auto &S
: Slices
) {
659 Offset
= alignTo(Offset
, 1ull << S
.getP2Alignment());
660 if (Offset
> UINT32_MAX
)
661 reportError("fat file too large to be created because the offset "
662 "field in struct fat_arch is only 32-bits and the offset " +
663 Twine(Offset
) + " for " + S
.getBinary()->getFileName() +
664 " for architecture " + S
.getArchString() + "exceeds that.");
666 MachO::fat_arch FatArch
;
667 FatArch
.cputype
= S
.getCPUType();
668 FatArch
.cpusubtype
= S
.getCPUSubType();
669 FatArch
.offset
= Offset
;
670 FatArch
.size
= S
.getBinary()->getMemoryBufferRef().getBufferSize();
671 FatArch
.align
= S
.getP2Alignment();
672 Offset
+= FatArch
.size
;
673 FatArchList
.push_back(FatArch
);
678 static void createUniversalBinary(SmallVectorImpl
<Slice
> &Slices
,
679 StringRef OutputFileName
) {
680 MachO::fat_header FatHeader
;
681 FatHeader
.magic
= MachO::FAT_MAGIC
;
682 FatHeader
.nfat_arch
= Slices
.size();
685 SmallVector
<MachO::fat_arch
, 2> FatArchList
= buildFatArchList(Slices
);
687 const bool IsExecutable
= any_of(Slices
, [](Slice S
) {
688 return sys::fs::can_execute(S
.getBinary()->getFileName());
690 const uint64_t OutputFileSize
=
691 static_cast<uint64_t>(FatArchList
.back().offset
) +
692 FatArchList
.back().size
;
693 Expected
<std::unique_ptr
<FileOutputBuffer
>> OutFileOrError
=
694 FileOutputBuffer::create(OutputFileName
, OutputFileSize
,
695 IsExecutable
? FileOutputBuffer::F_executable
698 reportError(OutputFileName
, OutFileOrError
.takeError());
699 std::unique_ptr
<FileOutputBuffer
> OutFile
= std::move(OutFileOrError
.get());
700 std::memset(OutFile
->getBufferStart(), 0, OutputFileSize
);
702 if (sys::IsLittleEndianHost
)
703 MachO::swapStruct(FatHeader
);
704 std::memcpy(OutFile
->getBufferStart(), &FatHeader
, sizeof(MachO::fat_header
));
706 for (size_t Index
= 0, Size
= Slices
.size(); Index
< Size
; ++Index
) {
707 MemoryBufferRef BufferRef
= Slices
[Index
].getBinary()->getMemoryBufferRef();
708 std::copy(BufferRef
.getBufferStart(), BufferRef
.getBufferEnd(),
709 OutFile
->getBufferStart() + FatArchList
[Index
].offset
);
712 // FatArchs written after Slices in order to reduce the number of swaps for
713 // the LittleEndian case
714 if (sys::IsLittleEndianHost
)
715 for (MachO::fat_arch
&FA
: FatArchList
)
716 MachO::swapStruct(FA
);
717 std::memcpy(OutFile
->getBufferStart() + sizeof(MachO::fat_header
),
719 sizeof(MachO::fat_arch
) * FatArchList
.size());
721 if (Error E
= OutFile
->commit())
722 reportError(OutputFileName
, std::move(E
));
725 LLVM_ATTRIBUTE_NORETURN
726 static void createUniversalBinary(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
727 const StringMap
<const uint32_t> &Alignments
,
728 StringRef OutputFileName
) {
729 assert(InputBinaries
.size() >= 1 && "Incorrect number of input binaries");
730 assert(!OutputFileName
.empty() && "Create expects a single output file");
732 SmallVector
<std::unique_ptr
<MachOObjectFile
>, 1> ExtractedObjects
;
733 SmallVector
<Slice
, 1> Slices
=
734 buildSlices(InputBinaries
, Alignments
, ExtractedObjects
);
735 checkArchDuplicates(Slices
);
736 checkUnusedAlignments(Slices
, Alignments
);
737 createUniversalBinary(Slices
, OutputFileName
);
742 static StringMap
<Slice
>
743 buildReplacementSlices(ArrayRef
<OwningBinary
<Binary
>> ReplacementBinaries
,
744 const StringMap
<const uint32_t> &Alignments
) {
745 StringMap
<Slice
> Slices
;
746 // populates StringMap of slices to replace with; error checks for mismatched
747 // replace flag args, fat files, and duplicate arch_types
748 for (const auto &OB
: ReplacementBinaries
) {
749 const Binary
*ReplacementBinary
= OB
.getBinary();
750 auto O
= dyn_cast
<MachOObjectFile
>(ReplacementBinary
);
752 reportError("replacement file: " + ReplacementBinary
->getFileName() +
753 " is a fat file (must be a thin file)");
755 auto Entry
= Slices
.try_emplace(S
.getArchString(), S
);
757 reportError("-replace " + S
.getArchString() +
758 " <file_name> specified multiple times: " +
759 Entry
.first
->second
.getBinary()->getFileName() + ", " +
762 auto SlicesMapRange
= map_range(
763 Slices
, [](StringMapEntry
<Slice
> &E
) -> Slice
& { return E
.getValue(); });
764 updateAlignments(SlicesMapRange
, Alignments
);
768 LLVM_ATTRIBUTE_NORETURN
769 static void replaceSlices(ArrayRef
<OwningBinary
<Binary
>> InputBinaries
,
770 const StringMap
<const uint32_t> &Alignments
,
771 StringRef OutputFileName
,
772 ArrayRef
<InputFile
> ReplacementFiles
) {
773 assert(InputBinaries
.size() == 1 && "Incorrect number of input binaries");
774 assert(!OutputFileName
.empty() && "Replace expects a single output file");
776 if (InputBinaries
.front().getBinary()->isMachO())
777 reportError("input file " +
778 InputBinaries
.front().getBinary()->getFileName() +
779 " must be a fat file when the -replace option is specified");
781 SmallVector
<OwningBinary
<Binary
>, 1> ReplacementBinaries
=
782 readInputBinaries(ReplacementFiles
);
784 StringMap
<Slice
> ReplacementSlices
=
785 buildReplacementSlices(ReplacementBinaries
, Alignments
);
786 SmallVector
<std::unique_ptr
<MachOObjectFile
>, 2> ExtractedObjects
;
787 SmallVector
<Slice
, 2> Slices
=
788 buildSlices(InputBinaries
, Alignments
, ExtractedObjects
);
790 for (auto &Slice
: Slices
) {
791 auto It
= ReplacementSlices
.find(Slice
.getArchString());
792 if (It
!= ReplacementSlices
.end()) {
794 ReplacementSlices
.erase(It
); // only keep remaining replacing arch_types
798 if (!ReplacementSlices
.empty())
799 reportError("-replace " + ReplacementSlices
.begin()->first() +
800 " <file_name> specified but fat file: " +
801 InputBinaries
.front().getBinary()->getFileName() +
802 " does not contain that architecture");
804 checkUnusedAlignments(Slices
, Alignments
);
805 createUniversalBinary(Slices
, OutputFileName
);
809 int main(int argc
, char **argv
) {
810 InitLLVM
X(argc
, argv
);
811 Config C
= parseLipoOptions(makeArrayRef(argv
+ 1, argc
));
812 SmallVector
<OwningBinary
<Binary
>, 1> InputBinaries
=
813 readInputBinaries(C
.InputFiles
);
815 switch (C
.ActionToPerform
) {
816 case LipoAction::VerifyArch
:
817 verifyArch(InputBinaries
, C
.VerifyArchList
);
819 case LipoAction::PrintArchs
:
820 printArchs(InputBinaries
);
822 case LipoAction::PrintInfo
:
823 printInfo(InputBinaries
);
825 case LipoAction::ThinArch
:
826 extractSlice(InputBinaries
, C
.ThinArchType
, C
.OutputFile
);
828 case LipoAction::CreateUniversal
:
829 createUniversalBinary(InputBinaries
, C
.SegmentAlignments
, C
.OutputFile
);
831 case LipoAction::ReplaceArch
:
832 replaceSlices(InputBinaries
, C
.SegmentAlignments
, C
.OutputFile
,