[InstCombine] Signed saturation tests. NFC
[llvm-complete.git] / tools / llvm-lipo / llvm-lipo.cpp
bloba063fcec7574c46e5e1c8c722d8b8aeee79fc24e
1 //===-- llvm-lipo.cpp - a tool for manipulating universal binaries --------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
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"
28 using namespace llvm;
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";
35 errs().flush();
36 exit(EXIT_FAILURE);
39 LLVM_ATTRIBUTE_NORETURN static void reportError(StringRef File, Error E) {
40 assert(E);
41 std::string Buf;
42 raw_string_ostream OS(Buf);
43 logAllUnhandledErrors(std::move(E), OS);
44 OS.flush();
45 WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf;
46 exit(EXIT_FAILURE);
49 namespace {
50 enum LipoID {
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) \
54 LIPO_##ID,
55 #include "LipoOpts.inc"
56 #undef OPTION
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"
63 #undef PREFIX
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"
73 #undef OPTION
76 class LipoOptTable : public opt::OptTable {
77 public:
78 LipoOptTable() : OptTable(LipoInfoTable) {}
81 enum class LipoAction {
82 PrintArchs,
83 PrintInfo,
84 VerifyArch,
85 ThinArch,
86 CreateUniversal,
87 ReplaceArch,
90 struct InputFile {
91 Optional<StringRef> ArchType;
92 StringRef FileName;
95 struct Config {
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))
115 continue;
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));
126 } else {
127 P2CurrentAlignment =
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
134 return std::max(
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
151 default:
152 return calculateFileAlignment(*ObjectFile);
156 class Slice {
157 const Binary *B;
158 uint32_t CPUType;
159 uint32_t CPUSubType;
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;
167 public:
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();
180 if (!ChildOrErr)
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)")
186 .str());
187 if (!Bin->isMachO())
188 reportError(("archive member " + Bin->getFileName() +
189 " is not a MachO file (not allowed in an archive)"));
190 MachOObjectFile *O = cast<MachOObjectFile>(Bin);
191 if (FO &&
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())
201 .str());
203 if (!FO) {
204 ChildOrErr.get().release();
205 FO.reset(O);
208 if (Err)
209 reportError(A->getFileName(), std::move(Err));
210 if (!FO)
211 reportError(("empty archive with no architecture specification: " +
212 A->getFileName() + " (can't determine architecture for it)")
213 .str());
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())
237 return ArchName;
238 return ("unknown(" + Twine(CPUType) + "," +
239 Twine(CPUSubType & ~MachO::CPU_SUBTYPE_MASK) + ")")
240 .str();
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)
249 return false;
250 if (Rhs.CPUType == MachO::CPU_TYPE_ARM64)
251 return true;
252 // Sort by alignment to minimize file size
253 return Lhs.P2Alignment < Rhs.P2Alignment;
257 } // end namespace
259 static void validateArchitectureName(StringRef ArchitectureName) {
260 if (!MachOObjectFile::isValidArch(ArchitectureName)) {
261 std::string Buf;
262 raw_string_ostream OS(Buf);
263 OS << "Invalid architecture: " << ArchitectureName
264 << "\nValid architecture names are:";
265 for (auto arch : MachOObjectFile::getValidArchs())
266 OS << " " << arch;
267 reportError(OS.str());
271 static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
272 Config C;
273 LipoOptTable T;
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)) +
281 " option");
283 if (InputArgs.size() == 0) {
284 // PrintHelp does not accept Twine.
285 T.PrintHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
286 exit(EXIT_FAILURE);
289 if (InputArgs.hasArg(LIPO_help)) {
290 // PrintHelp does not accept Twine.
291 T.PrintHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
292 exit(EXIT_SUCCESS);
295 if (InputArgs.hasArg(LIPO_version)) {
296 outs() << ToolName + "\n";
297 cl::PrintVersionMessage();
298 exit(EXIT_SUCCESS);
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))
309 reportError(
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)
337 reportError(
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));
343 if (!Entry.second)
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 &&
358 ActionArgs.size() !=
359 static_cast<size_t>(std::distance(ReplacementArgsRange.begin(),
360 ReplacementArgsRange.end()))) {
361 std::string Buf;
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())
374 reportError(
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;
379 return C;
381 case LIPO_archs:
382 if (C.InputFiles.size() > 1)
383 reportError("archs expects a single input file");
384 C.ActionToPerform = LipoAction::PrintArchs;
385 return C;
387 case LIPO_info:
388 C.ActionToPerform = LipoAction::PrintInfo;
389 return C;
391 case LIPO_thin:
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;
400 return C;
402 case LIPO_create:
403 if (C.OutputFile.empty())
404 reportError("create expects a single output file to be specified");
405 C.ActionToPerform = LipoAction::CreateUniversal;
406 return C;
408 case LIPO_replace:
409 for (auto Action : ActionArgs) {
410 if (!Action->getValue(1))
411 reportError(
412 "replace is missing an argument: expects -replace arch_type "
413 "file_name");
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;
424 return C;
426 default:
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);
436 if (!BinaryOrErr)
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()))
447 .first;
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);
471 if (auto UO =
472 dyn_cast<MachOUniversalBinary>(InputBinaries.front().getBinary())) {
473 for (StringRef Arch : VerifyArchList) {
474 Expected<MachOUniversalBinary::ObjectForArch> Obj =
475 UO->getObjectForArch(Arch);
476 if (!Obj)
477 exit(EXIT_FAILURE);
479 } else if (auto O =
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())
484 exit(EXIT_FAILURE);
485 } else {
486 llvm_unreachable("Unexpected binary format");
488 exit(EXIT_SUCCESS);
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 =
496 O.getAsObjectFile();
497 if (MachOObjOrError) {
498 OS << Slice(MachOObjOrError->get()).getArchString() << " ";
499 continue;
501 Expected<std::unique_ptr<Archive>> ArchiveOrError = O.getAsArchive();
502 if (ArchiveOrError) {
503 consumeError(MachOObjOrError.takeError());
504 OS << Slice(ArchiveOrError->get()).getArchString() << " ";
505 continue;
507 consumeError(ArchiveOrError.takeError());
508 reportError(Binary->getFileName(), MachOObjOrError.takeError());
510 OS << "\n";
511 return;
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());
520 exit(EXIT_SUCCESS);
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()
530 << " are: ";
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());
543 exit(EXIT_SUCCESS);
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");
557 exit(EXIT_FAILURE);
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);
564 if (!Obj && !Ar)
565 reportError("fat input file " + UO->getFileName() +
566 " does not contain the specified architecture " + ThinArchType +
567 " to thin it to");
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
575 : 0);
576 if (!OutFileOrError)
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));
583 exit(EXIT_SUCCESS);
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());
590 if (!Entry.second)
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;
613 }) != Slices.end();
615 for (StringRef Arch : Alignments.keys())
616 if (!HasArch(Arch))
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 =
634 O.getAsObjectFile();
635 if (!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);
644 } else {
645 llvm_unreachable("Unexpected binary format");
648 updateAlignments(Slices, Alignments);
649 return Slices;
652 static SmallVector<MachO::fat_arch, 2>
653 buildFatArchList(ArrayRef<Slice> Slices) {
654 SmallVector<MachO::fat_arch, 2> FatArchList;
655 uint64_t Offset =
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);
675 return FatArchList;
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();
684 stable_sort(Slices);
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
696 : 0);
697 if (!OutFileOrError)
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),
718 FatArchList.begin(),
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);
739 exit(EXIT_SUCCESS);
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);
751 if (!O)
752 reportError("replacement file: " + ReplacementBinary->getFileName() +
753 " is a fat file (must be a thin file)");
754 Slice S(O);
755 auto Entry = Slices.try_emplace(S.getArchString(), S);
756 if (!Entry.second)
757 reportError("-replace " + S.getArchString() +
758 " <file_name> specified multiple times: " +
759 Entry.first->second.getBinary()->getFileName() + ", " +
760 O->getFileName());
762 auto SlicesMapRange = map_range(
763 Slices, [](StringMapEntry<Slice> &E) -> Slice & { return E.getValue(); });
764 updateAlignments(SlicesMapRange, Alignments);
765 return Slices;
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()) {
793 Slice = It->second;
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);
806 exit(EXIT_SUCCESS);
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);
818 break;
819 case LipoAction::PrintArchs:
820 printArchs(InputBinaries);
821 break;
822 case LipoAction::PrintInfo:
823 printInfo(InputBinaries);
824 break;
825 case LipoAction::ThinArch:
826 extractSlice(InputBinaries, C.ThinArchType, C.OutputFile);
827 break;
828 case LipoAction::CreateUniversal:
829 createUniversalBinary(InputBinaries, C.SegmentAlignments, C.OutputFile);
830 break;
831 case LipoAction::ReplaceArch:
832 replaceSlices(InputBinaries, C.SegmentAlignments, C.OutputFile,
833 C.ReplacementFiles);
834 break;
836 return EXIT_SUCCESS;