[sanitizer] Improve FreeBSD ASLR detection
[llvm-project.git] / llvm / tools / llvm-lipo / llvm-lipo.cpp
blob8bf58c624cf49dcb47c7588c55df13746176c6a2
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/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"
33 using namespace llvm;
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";
41 errs().flush();
42 exit(EXIT_FAILURE);
45 [[noreturn]] static void reportError(Error E) {
46 assert(E);
47 std::string Buf;
48 raw_string_ostream OS(Buf);
49 logAllUnhandledErrors(std::move(E), OS);
50 OS.flush();
51 reportError(Buf);
54 [[noreturn]] static void reportError(StringRef File, Error E) {
55 assert(E);
56 std::string Buf;
57 raw_string_ostream OS(Buf);
58 logAllUnhandledErrors(std::move(E), OS);
59 OS.flush();
60 WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf;
61 exit(EXIT_FAILURE);
64 namespace {
65 enum LipoID {
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) \
69 LIPO_##ID,
70 #include "LipoOpts.inc"
71 #undef OPTION
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"
78 #undef PREFIX
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"
88 #undef OPTION
91 class LipoOptTable : public opt::OptTable {
92 public:
93 LipoOptTable() : OptTable(LipoInfoTable) {}
96 enum class LipoAction {
97 PrintArchs,
98 PrintInfo,
99 VerifyArch,
100 ThinArch,
101 ExtractArch,
102 CreateUniversal,
103 ReplaceArch,
106 struct InputFile {
107 Optional<StringRef> ArchType;
108 StringRef FileName;
111 struct Config {
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);
123 if (!ArchiveOrSlice)
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);
130 if (!IROrErr)
131 reportError(IRO.getFileName(), IROrErr.takeError());
132 return *IROrErr;
135 } // end namespace
137 static void validateArchitectureName(StringRef ArchitectureName) {
138 if (!MachOObjectFile::isValidArch(ArchitectureName)) {
139 std::string Buf;
140 raw_string_ostream OS(Buf);
141 OS << "Invalid architecture: " << ArchitectureName
142 << "\nValid architecture names are:";
143 for (auto arch : MachOObjectFile::getValidArchs())
144 OS << " " << arch;
145 reportError(OS.str());
149 static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
150 Config C;
151 LipoOptTable T;
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)) +
159 " option");
161 if (InputArgs.size() == 0) {
162 // printHelp does not accept Twine.
163 T.printHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
164 exit(EXIT_FAILURE);
167 if (InputArgs.hasArg(LIPO_help)) {
168 // printHelp does not accept Twine.
169 T.printHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
170 exit(EXIT_SUCCESS);
173 if (InputArgs.hasArg(LIPO_version)) {
174 outs() << ToolName + "\n";
175 cl::PrintVersionMessage();
176 exit(EXIT_SUCCESS);
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))
187 reportError(
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)
215 reportError(
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));
221 if (!Entry.second)
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 &&
236 ActionArgs.size() !=
237 static_cast<size_t>(std::distance(ReplacementArgsRange.begin(),
238 ReplacementArgsRange.end()))) {
239 std::string Buf;
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())
252 reportError(
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;
257 return C;
259 case LIPO_archs:
260 if (C.InputFiles.size() > 1)
261 reportError("archs expects a single input file");
262 C.ActionToPerform = LipoAction::PrintArchs;
263 return C;
265 case LIPO_info:
266 C.ActionToPerform = LipoAction::PrintInfo;
267 return C;
269 case LIPO_thin:
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;
277 return C;
279 case LIPO_extract:
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;
287 return C;
289 case LIPO_create:
290 if (C.OutputFile.empty())
291 reportError("create expects a single output file to be specified");
292 C.ActionToPerform = LipoAction::CreateUniversal;
293 return C;
295 case LIPO_replace:
296 for (auto Action : ActionArgs) {
297 if (!Action->getValue(1))
298 reportError(
299 "replace is missing an argument: expects -replace arch_type "
300 "file_name");
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;
311 return C;
313 default:
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);
324 if (!BinaryOrErr)
325 reportError(IF.FileName, BinaryOrErr.takeError());
326 const Binary *B = BinaryOrErr->getBinary();
327 if (!B->isArchive() && !B->isMachO() && !B->isMachOUniversalBinary() &&
328 !B->isIR())
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))
333 : B->isArchive()
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()))
339 .first;
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);
363 if (auto UO =
364 dyn_cast<MachOUniversalBinary>(InputBinaries.front().getBinary())) {
365 for (StringRef Arch : VerifyArchList) {
366 Expected<MachOUniversalBinary::ObjectForArch> Obj =
367 UO->getObjectForArch(Arch);
368 if (!Obj)
369 exit(EXIT_FAILURE);
371 } else if (auto O =
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())
376 exit(EXIT_FAILURE);
377 } else {
378 llvm_unreachable("Unexpected binary format");
380 exit(EXIT_SUCCESS);
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 =
390 O.getAsObjectFile();
391 if (MachOObjOrError) {
392 OS << Slice(*(MachOObjOrError->get())).getArchString() << " ";
393 continue;
395 Expected<std::unique_ptr<IRObjectFile>> IROrError =
396 O.getAsIRObject(LLVMCtx);
397 if (IROrError) {
398 consumeError(MachOObjOrError.takeError());
399 Expected<Slice> SliceOrErr = Slice::create(**IROrError, O.getAlign());
400 if (!SliceOrErr) {
401 reportError(Binary->getFileName(), SliceOrErr.takeError());
402 continue;
404 OS << SliceOrErr.get().getArchString() << " ";
405 continue;
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() << " ";
412 continue;
414 consumeError(ArchiveOrError.takeError());
415 reportError(Binary->getFileName(), MachOObjOrError.takeError());
416 reportError(Binary->getFileName(), IROrError.takeError());
418 OS << "\n";
419 return;
422 if (const auto *MachO = dyn_cast<MachOObjectFile>(Binary)) {
423 OS << Slice(*MachO).getArchString() << " \n";
424 return;
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);
430 if (!SliceOrErr)
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());
440 exit(EXIT_SUCCESS);
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()
450 << " are: ";
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());
463 exit(EXIT_SUCCESS);
466 [[noreturn]] static void thinSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,
467 StringRef ArchType,
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");
477 exit(EXIT_FAILURE);
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 +
489 " to thin it to");
490 Binary *B;
491 // Order here is important, because both Obj and IRObj will be valid with a
492 // binary that has embedded bitcode.
493 if (Obj)
494 B = Obj->get();
495 else if (IRObj)
496 B = IRObj->get();
497 else
498 B = Ar->get();
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
505 : 0);
506 if (!OutFileOrError)
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));
513 exit(EXIT_SUCCESS);
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());
520 if (!Entry.second)
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())
545 if (!HasArch(Arch))
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 =
565 O.getAsObjectFile();
566 if (BinaryOrError) {
567 Slices.emplace_back(*(BinaryOrError.get()), O.getAlign());
568 ExtractedObjects.push_back(std::move(BinaryOrError.get()));
569 continue;
571 Expected<std::unique_ptr<IRObjectFile>> IROrError =
572 O.getAsIRObject(LLVMCtx);
573 if (IROrError) {
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));
578 continue;
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);
589 if (!SliceOrErr) {
590 reportError(InputBinary->getFileName(), SliceOrErr.takeError());
591 continue;
593 Slices.emplace_back(std::move(SliceOrErr.get()));
594 } else {
595 llvm_unreachable("Unexpected binary format");
598 updateAlignments(Slices, Alignments);
599 return Slices;
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));
619 exit(EXIT_SUCCESS);
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();
644 if (Slices.empty())
645 reportError(
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));
652 exit(EXIT_SUCCESS);
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);
664 if (!O)
665 reportError("replacement file: " + ReplacementBinary->getFileName() +
666 " is a fat file (must be a thin file)");
667 Slice S(*O);
668 auto Entry = Slices.try_emplace(S.getArchString(), S);
669 if (!Entry.second)
670 reportError("-replace " + S.getArchString() +
671 " <file_name> specified multiple times: " +
672 Entry.first->second.getBinary()->getFileName() + ", " +
673 O->getFileName());
675 auto SlicesMapRange = map_range(
676 Slices, [](StringMapEntry<Slice> &E) -> Slice & { return E.getValue(); });
677 updateAlignments(SlicesMapRange, Alignments);
678 return Slices;
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()) {
705 Slice = It->second;
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));
721 exit(EXIT_SUCCESS);
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);
733 break;
734 case LipoAction::PrintArchs:
735 printArchs(InputBinaries);
736 break;
737 case LipoAction::PrintInfo:
738 printInfo(InputBinaries);
739 break;
740 case LipoAction::ThinArch:
741 thinSlice(InputBinaries, C.ArchType, C.OutputFile);
742 break;
743 case LipoAction::ExtractArch:
744 extractSlice(InputBinaries, C.SegmentAlignments, C.ArchType, C.OutputFile);
745 break;
746 case LipoAction::CreateUniversal:
747 createUniversalBinary(InputBinaries, C.SegmentAlignments, C.OutputFile);
748 break;
749 case LipoAction::ReplaceArch:
750 replaceSlices(InputBinaries, C.SegmentAlignments, C.OutputFile,
751 C.ReplacementFiles);
752 break;
754 return EXIT_SUCCESS;