xref: /openbsd-src/gnu/llvm/llvm/tools/llvm-lipo/llvm-lipo.cpp (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
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.
10 //
11 //===----------------------------------------------------------------------===//
12 
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/Archive.h"
19 #include "llvm/Object/Binary.h"
20 #include "llvm/Object/IRObjectFile.h"
21 #include "llvm/Object/MachO.h"
22 #include "llvm/Object/MachOUniversal.h"
23 #include "llvm/Object/MachOUniversalWriter.h"
24 #include "llvm/Object/ObjectFile.h"
25 #include "llvm/Option/Arg.h"
26 #include "llvm/Option/ArgList.h"
27 #include "llvm/Support/CommandLine.h"
28 #include "llvm/Support/Error.h"
29 #include "llvm/Support/FileOutputBuffer.h"
30 #include "llvm/Support/InitLLVM.h"
31 #include "llvm/Support/TargetSelect.h"
32 #include "llvm/Support/WithColor.h"
33 #include "llvm/TextAPI/Architecture.h"
34 #include <optional>
35 
36 using namespace llvm;
37 using namespace llvm::object;
38 
39 static const StringRef ToolName = "llvm-lipo";
40 
reportError(Twine Message)41 [[noreturn]] static void reportError(Twine Message) {
42   WithColor::error(errs(), ToolName) << Message << "\n";
43   errs().flush();
44   exit(EXIT_FAILURE);
45 }
46 
reportError(Error E)47 [[noreturn]] static void reportError(Error E) {
48   assert(E);
49   std::string Buf;
50   raw_string_ostream OS(Buf);
51   logAllUnhandledErrors(std::move(E), OS);
52   OS.flush();
53   reportError(Buf);
54 }
55 
reportError(StringRef File,Error E)56 [[noreturn]] static void reportError(StringRef File, Error E) {
57   assert(E);
58   std::string Buf;
59   raw_string_ostream OS(Buf);
60   logAllUnhandledErrors(std::move(E), OS);
61   OS.flush();
62   WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf;
63   exit(EXIT_FAILURE);
64 }
65 
66 namespace {
67 enum LipoID {
68   LIPO_INVALID = 0, // This is not an option ID.
69 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
70                HELPTEXT, METAVAR, VALUES)                                      \
71   LIPO_##ID,
72 #include "LipoOpts.inc"
73 #undef OPTION
74 };
75 
76 namespace lipo {
77 #define PREFIX(NAME, VALUE)                                                    \
78   static constexpr llvm::StringLiteral NAME##_init[] = VALUE;                  \
79   static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME(                   \
80       NAME##_init, std::size(NAME##_init) - 1);
81 #include "LipoOpts.inc"
82 #undef PREFIX
83 
84 static constexpr opt::OptTable::Info LipoInfoTable[] = {
85 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
86                HELPTEXT, METAVAR, VALUES)                                      \
87   {PREFIX,       NAME,      HELPTEXT,                                          \
88    METAVAR,      LIPO_##ID, opt::Option::KIND##Class,                          \
89    PARAM,        FLAGS,     LIPO_##GROUP,                                      \
90    LIPO_##ALIAS, ALIASARGS, VALUES},
91 #include "LipoOpts.inc"
92 #undef OPTION
93 };
94 } // namespace lipo
95 
96 class LipoOptTable : public opt::GenericOptTable {
97 public:
LipoOptTable()98   LipoOptTable() : opt::GenericOptTable(lipo::LipoInfoTable) {}
99 };
100 
101 enum class LipoAction {
102   PrintArchs,
103   PrintInfo,
104   VerifyArch,
105   ThinArch,
106   ExtractArch,
107   CreateUniversal,
108   ReplaceArch,
109 };
110 
111 struct InputFile {
112   std::optional<StringRef> ArchType;
113   StringRef FileName;
114 };
115 
116 struct Config {
117   SmallVector<InputFile, 1> InputFiles;
118   SmallVector<std::string, 1> VerifyArchList;
119   SmallVector<InputFile, 1> ReplacementFiles;
120   StringMap<const uint32_t> SegmentAlignments;
121   std::string ArchType;
122   std::string OutputFile;
123   LipoAction ActionToPerform;
124 };
125 
createSliceFromArchive(LLVMContext & LLVMCtx,const Archive & A)126 static Slice createSliceFromArchive(LLVMContext &LLVMCtx, const Archive &A) {
127   Expected<Slice> ArchiveOrSlice = Slice::create(A, &LLVMCtx);
128   if (!ArchiveOrSlice)
129     reportError(A.getFileName(), ArchiveOrSlice.takeError());
130   return *ArchiveOrSlice;
131 }
132 
createSliceFromIR(const IRObjectFile & IRO,unsigned Align)133 static Slice createSliceFromIR(const IRObjectFile &IRO, unsigned Align) {
134   Expected<Slice> IROrErr = Slice::create(IRO, Align);
135   if (!IROrErr)
136     reportError(IRO.getFileName(), IROrErr.takeError());
137   return *IROrErr;
138 }
139 
140 } // end namespace
141 
validateArchitectureName(StringRef ArchitectureName)142 static void validateArchitectureName(StringRef ArchitectureName) {
143   if (!MachOObjectFile::isValidArch(ArchitectureName)) {
144     std::string Buf;
145     raw_string_ostream OS(Buf);
146     OS << "Invalid architecture: " << ArchitectureName
147        << "\nValid architecture names are:";
148     for (auto arch : MachOObjectFile::getValidArchs())
149       OS << " " << arch;
150     reportError(OS.str());
151   }
152 }
153 
parseLipoOptions(ArrayRef<const char * > ArgsArr)154 static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
155   Config C;
156   LipoOptTable T;
157   unsigned MissingArgumentIndex, MissingArgumentCount;
158   opt::InputArgList InputArgs =
159       T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
160 
161   if (MissingArgumentCount)
162     reportError("missing argument to " +
163                 StringRef(InputArgs.getArgString(MissingArgumentIndex)) +
164                 " option");
165 
166   if (InputArgs.size() == 0) {
167     // printHelp does not accept Twine.
168     T.printHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
169     exit(EXIT_FAILURE);
170   }
171 
172   if (InputArgs.hasArg(LIPO_help)) {
173     // printHelp does not accept Twine.
174     T.printHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
175     exit(EXIT_SUCCESS);
176   }
177 
178   if (InputArgs.hasArg(LIPO_version)) {
179     outs() << ToolName + "\n";
180     cl::PrintVersionMessage();
181     exit(EXIT_SUCCESS);
182   }
183 
184   for (auto *Arg : InputArgs.filtered(LIPO_UNKNOWN))
185     reportError("unknown argument '" + Arg->getAsString(InputArgs) + "'");
186 
187   for (auto *Arg : InputArgs.filtered(LIPO_INPUT))
188     C.InputFiles.push_back({std::nullopt, Arg->getValue()});
189   for (auto *Arg : InputArgs.filtered(LIPO_arch)) {
190     validateArchitectureName(Arg->getValue(0));
191     assert(Arg->getValue(1) && "file_name is missing");
192     C.InputFiles.push_back({StringRef(Arg->getValue(0)), Arg->getValue(1)});
193   }
194 
195   if (C.InputFiles.empty())
196     reportError("at least one input file should be specified");
197 
198   if (InputArgs.hasArg(LIPO_output))
199     C.OutputFile = std::string(InputArgs.getLastArgValue(LIPO_output));
200 
201   for (auto *Segalign : InputArgs.filtered(LIPO_segalign)) {
202     if (!Segalign->getValue(1))
203       reportError("segalign is missing an argument: expects -segalign "
204                   "arch_type alignment_value");
205 
206     validateArchitectureName(Segalign->getValue(0));
207 
208     uint32_t AlignmentValue;
209     if (!to_integer<uint32_t>(Segalign->getValue(1), AlignmentValue, 16))
210       reportError("argument to -segalign <arch_type> " +
211                   Twine(Segalign->getValue(1)) +
212                   " (hex) is not a proper hexadecimal number");
213     if (!isPowerOf2_32(AlignmentValue))
214       reportError("argument to -segalign <arch_type> " +
215                   Twine(Segalign->getValue(1)) +
216                   " (hex) must be a non-zero power of two");
217     if (Log2_32(AlignmentValue) > MachOUniversalBinary::MaxSectionAlignment)
218       reportError(
219           "argument to -segalign <arch_type> " + Twine(Segalign->getValue(1)) +
220           " (hex) must be less than or equal to the maximum section align 2^" +
221           Twine(MachOUniversalBinary::MaxSectionAlignment));
222     auto Entry = C.SegmentAlignments.try_emplace(Segalign->getValue(0),
223                                                  Log2_32(AlignmentValue));
224     if (!Entry.second)
225       reportError("-segalign " + Twine(Segalign->getValue(0)) +
226                   " <alignment_value> specified multiple times: " +
227                   Twine(1 << Entry.first->second) + ", " +
228                   Twine(AlignmentValue));
229   }
230 
231   SmallVector<opt::Arg *, 1> ActionArgs(InputArgs.filtered(LIPO_action_group));
232   if (ActionArgs.empty())
233     reportError("at least one action should be specified");
234   // errors if multiple actions specified other than replace
235   // multiple replace flags may be specified, as long as they are not mixed with
236   // other action flags
237   auto ReplacementArgsRange = InputArgs.filtered(LIPO_replace);
238   if (ActionArgs.size() > 1 &&
239       ActionArgs.size() !=
240           static_cast<size_t>(std::distance(ReplacementArgsRange.begin(),
241                                             ReplacementArgsRange.end()))) {
242     std::string Buf;
243     raw_string_ostream OS(Buf);
244     OS << "only one of the following actions can be specified:";
245     for (auto *Arg : ActionArgs)
246       OS << " " << Arg->getSpelling();
247     reportError(OS.str());
248   }
249 
250   switch (ActionArgs[0]->getOption().getID()) {
251   case LIPO_verify_arch:
252     for (auto A : InputArgs.getAllArgValues(LIPO_verify_arch))
253       C.VerifyArchList.push_back(A);
254     if (C.VerifyArchList.empty())
255       reportError(
256           "verify_arch requires at least one architecture to be specified");
257     if (C.InputFiles.size() > 1)
258       reportError("verify_arch expects a single input file");
259     C.ActionToPerform = LipoAction::VerifyArch;
260     return C;
261 
262   case LIPO_archs:
263     if (C.InputFiles.size() > 1)
264       reportError("archs expects a single input file");
265     C.ActionToPerform = LipoAction::PrintArchs;
266     return C;
267 
268   case LIPO_info:
269     C.ActionToPerform = LipoAction::PrintInfo;
270     return C;
271 
272   case LIPO_thin:
273     if (C.InputFiles.size() > 1)
274       reportError("thin expects a single input file");
275     if (C.OutputFile.empty())
276       reportError("thin expects a single output file");
277     C.ArchType = ActionArgs[0]->getValue();
278     validateArchitectureName(C.ArchType);
279     C.ActionToPerform = LipoAction::ThinArch;
280     return C;
281 
282   case LIPO_extract:
283     if (C.InputFiles.size() > 1)
284       reportError("extract expects a single input file");
285     if (C.OutputFile.empty())
286       reportError("extract expects a single output file");
287     C.ArchType = ActionArgs[0]->getValue();
288     validateArchitectureName(C.ArchType);
289     C.ActionToPerform = LipoAction::ExtractArch;
290     return C;
291 
292   case LIPO_create:
293     if (C.OutputFile.empty())
294       reportError("create expects a single output file to be specified");
295     C.ActionToPerform = LipoAction::CreateUniversal;
296     return C;
297 
298   case LIPO_replace:
299     for (auto *Action : ActionArgs) {
300       assert(Action->getValue(1) && "file_name is missing");
301       validateArchitectureName(Action->getValue(0));
302       C.ReplacementFiles.push_back(
303           {StringRef(Action->getValue(0)), Action->getValue(1)});
304     }
305 
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;
312 
313   default:
314     reportError("llvm-lipo action unspecified");
315   }
316 }
317 
318 static SmallVector<OwningBinary<Binary>, 1>
readInputBinaries(LLVMContext & LLVMCtx,ArrayRef<InputFile> InputFiles)319 readInputBinaries(LLVMContext &LLVMCtx, 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() ? Slice(*cast<MachOObjectFile>(B))
332                      : B->isArchive()
333                          ? createSliceFromArchive(LLVMCtx, *cast<Archive>(B))
334                          : createSliceFromIR(*cast<IRObjectFile>(B), 0);
335       const auto SpecifiedCPUType = MachO::getCPUTypeFromArchitecture(
336                                         MachO::getArchitectureFromName(
337                                             Triple(*IF.ArchType).getArchName()))
338                                         .first;
339       // For compatibility with cctools' lipo the comparison is relaxed just to
340       // checking cputypes.
341       if (S.getCPUType() != SpecifiedCPUType)
342         reportError("specified architecture: " + *IF.ArchType +
343                     " for file: " + B->getFileName() +
344                     " does not match the file's architecture (" +
345                     S.getArchString() + ")");
346     }
347     InputBinaries.push_back(std::move(*BinaryOrErr));
348   }
349   return InputBinaries;
350 }
351 
352 [[noreturn]] static void
verifyArch(ArrayRef<OwningBinary<Binary>> InputBinaries,ArrayRef<std::string> VerifyArchList)353 verifyArch(ArrayRef<OwningBinary<Binary>> InputBinaries,
354            ArrayRef<std::string> VerifyArchList) {
355   assert(!VerifyArchList.empty() &&
356          "The list of architectures should be non-empty");
357   assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
358 
359   for (StringRef Arch : VerifyArchList)
360     validateArchitectureName(Arch);
361 
362   if (auto UO =
363           dyn_cast<MachOUniversalBinary>(InputBinaries.front().getBinary())) {
364     for (StringRef Arch : VerifyArchList) {
365       Expected<MachOUniversalBinary::ObjectForArch> Obj =
366           UO->getObjectForArch(Arch);
367       if (!Obj)
368         exit(EXIT_FAILURE);
369     }
370   } else if (auto O =
371                  dyn_cast<MachOObjectFile>(InputBinaries.front().getBinary())) {
372     const Triple::ArchType ObjectArch = O->getArch();
373     for (StringRef Arch : VerifyArchList)
374       if (ObjectArch != Triple(Arch).getArch())
375         exit(EXIT_FAILURE);
376   } else {
377     llvm_unreachable("Unexpected binary format");
378   }
379   exit(EXIT_SUCCESS);
380 }
381 
printBinaryArchs(LLVMContext & LLVMCtx,const Binary * Binary,raw_ostream & OS)382 static void printBinaryArchs(LLVMContext &LLVMCtx, const Binary *Binary,
383                              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;
394       }
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;
403         }
404         OS << SliceOrErr.get().getArchString() << " ";
405         continue;
406       }
407       Expected<std::unique_ptr<Archive>> ArchiveOrError = O.getAsArchive();
408       if (ArchiveOrError) {
409         consumeError(MachOObjOrError.takeError());
410         consumeError(IROrError.takeError());
411         OS << createSliceFromArchive(LLVMCtx, **ArchiveOrError).getArchString()
412            << " ";
413         continue;
414       }
415       consumeError(ArchiveOrError.takeError());
416       reportError(Binary->getFileName(), MachOObjOrError.takeError());
417       reportError(Binary->getFileName(), IROrError.takeError());
418     }
419     OS << "\n";
420     return;
421   }
422 
423   if (const auto *MachO = dyn_cast<MachOObjectFile>(Binary)) {
424     OS << Slice(*MachO).getArchString() << " \n";
425     return;
426   }
427 
428   // This should be always the case, as this is tested in readInputBinaries
429   const auto *IR = cast<IRObjectFile>(Binary);
430   Expected<Slice> SliceOrErr = createSliceFromIR(*IR, 0);
431   if (!SliceOrErr)
432     reportError(IR->getFileName(), SliceOrErr.takeError());
433 
434   OS << SliceOrErr->getArchString() << " \n";
435 }
436 
437 [[noreturn]] static void
printArchs(LLVMContext & LLVMCtx,ArrayRef<OwningBinary<Binary>> InputBinaries)438 printArchs(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries) {
439   assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
440   printBinaryArchs(LLVMCtx, InputBinaries.front().getBinary(), outs());
441   exit(EXIT_SUCCESS);
442 }
443 
444 [[noreturn]] static void
printInfo(LLVMContext & LLVMCtx,ArrayRef<OwningBinary<Binary>> InputBinaries)445 printInfo(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries) {
446   // Group universal and thin files together for compatibility with cctools lipo
447   for (auto &IB : InputBinaries) {
448     const Binary *Binary = IB.getBinary();
449     if (Binary->isMachOUniversalBinary()) {
450       outs() << "Architectures in the fat file: " << Binary->getFileName()
451              << " are: ";
452       printBinaryArchs(LLVMCtx, Binary, outs());
453     }
454   }
455   for (auto &IB : InputBinaries) {
456     const Binary *Binary = IB.getBinary();
457     if (!Binary->isMachOUniversalBinary()) {
458       assert(Binary->isMachO() && "expected MachO binary");
459       outs() << "Non-fat file: " << Binary->getFileName()
460              << " is architecture: ";
461       printBinaryArchs(LLVMCtx, Binary, outs());
462     }
463   }
464   exit(EXIT_SUCCESS);
465 }
466 
thinSlice(LLVMContext & LLVMCtx,ArrayRef<OwningBinary<Binary>> InputBinaries,StringRef ArchType,StringRef OutputFileName)467 [[noreturn]] static void thinSlice(LLVMContext &LLVMCtx,
468                                    ArrayRef<OwningBinary<Binary>> InputBinaries,
469                                    StringRef ArchType,
470                                    StringRef OutputFileName) {
471   assert(!ArchType.empty() && "The architecture type should be non-empty");
472   assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
473   assert(!OutputFileName.empty() && "Thin expects a single output file");
474 
475   if (InputBinaries.front().getBinary()->isMachO()) {
476     reportError("input file " +
477                 InputBinaries.front().getBinary()->getFileName() +
478                 " must be a fat file when the -thin option is specified");
479     exit(EXIT_FAILURE);
480   }
481 
482   auto *UO = cast<MachOUniversalBinary>(InputBinaries.front().getBinary());
483   Expected<std::unique_ptr<MachOObjectFile>> Obj =
484       UO->getMachOObjectForArch(ArchType);
485   Expected<std::unique_ptr<IRObjectFile>> IRObj =
486       UO->getIRObjectForArch(ArchType, LLVMCtx);
487   Expected<std::unique_ptr<Archive>> Ar = UO->getArchiveForArch(ArchType);
488   if (!Obj && !IRObj && !Ar)
489     reportError("fat input file " + UO->getFileName() +
490                 " does not contain the specified architecture " + ArchType +
491                 " to thin it to");
492   Binary *B;
493   // Order here is important, because both Obj and IRObj will be valid with a
494   // binary that has embedded bitcode.
495   if (Obj)
496     B = Obj->get();
497   else if (IRObj)
498     B = IRObj->get();
499   else
500     B = Ar->get();
501 
502   Expected<std::unique_ptr<FileOutputBuffer>> OutFileOrError =
503       FileOutputBuffer::create(OutputFileName,
504                                B->getMemoryBufferRef().getBufferSize(),
505                                sys::fs::can_execute(UO->getFileName())
506                                    ? FileOutputBuffer::F_executable
507                                    : 0);
508   if (!OutFileOrError)
509     reportError(OutputFileName, OutFileOrError.takeError());
510   std::copy(B->getMemoryBufferRef().getBufferStart(),
511             B->getMemoryBufferRef().getBufferEnd(),
512             OutFileOrError.get()->getBufferStart());
513   if (Error E = OutFileOrError.get()->commit())
514     reportError(OutputFileName, std::move(E));
515   exit(EXIT_SUCCESS);
516 }
517 
checkArchDuplicates(ArrayRef<Slice> Slices)518 static void checkArchDuplicates(ArrayRef<Slice> Slices) {
519   DenseMap<uint64_t, const Binary *> CPUIds;
520   for (const auto &S : Slices) {
521     auto Entry = CPUIds.try_emplace(S.getCPUID(), S.getBinary());
522     if (!Entry.second)
523       reportError(Entry.first->second->getFileName() + " and " +
524                   S.getBinary()->getFileName() +
525                   " have the same architecture " + S.getArchString() +
526                   " and therefore cannot be in the same universal binary");
527   }
528 }
529 
530 template <typename Range>
updateAlignments(Range & Slices,const StringMap<const uint32_t> & Alignments)531 static void updateAlignments(Range &Slices,
532                              const StringMap<const uint32_t> &Alignments) {
533   for (auto &Slice : Slices) {
534     auto Alignment = Alignments.find(Slice.getArchString());
535     if (Alignment != Alignments.end())
536       Slice.setP2Alignment(Alignment->second);
537   }
538 }
539 
checkUnusedAlignments(ArrayRef<Slice> Slices,const StringMap<const uint32_t> & Alignments)540 static void checkUnusedAlignments(ArrayRef<Slice> Slices,
541                                   const StringMap<const uint32_t> &Alignments) {
542   auto HasArch = [&](StringRef Arch) {
543     return llvm::any_of(Slices,
544                         [Arch](Slice S) { return S.getArchString() == Arch; });
545   };
546   for (StringRef Arch : Alignments.keys())
547     if (!HasArch(Arch))
548       reportError("-segalign " + Arch +
549                   " <value> specified but resulting fat file does not contain "
550                   "that architecture ");
551 }
552 
553 // Updates vector ExtractedObjects with the MachOObjectFiles extracted from
554 // Universal Binary files to transfer ownership.
555 static SmallVector<Slice, 2>
buildSlices(LLVMContext & LLVMCtx,ArrayRef<OwningBinary<Binary>> InputBinaries,const StringMap<const uint32_t> & Alignments,SmallVectorImpl<std::unique_ptr<SymbolicFile>> & ExtractedObjects)556 buildSlices(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries,
557             const StringMap<const uint32_t> &Alignments,
558             SmallVectorImpl<std::unique_ptr<SymbolicFile>> &ExtractedObjects) {
559   SmallVector<Slice, 2> Slices;
560   for (auto &IB : InputBinaries) {
561     const Binary *InputBinary = IB.getBinary();
562     if (auto UO = dyn_cast<MachOUniversalBinary>(InputBinary)) {
563       for (const auto &O : UO->objects()) {
564         // Order here is important, because both MachOObjectFile and
565         // IRObjectFile can be created with a binary that has embedded bitcode.
566         Expected<std::unique_ptr<MachOObjectFile>> BinaryOrError =
567             O.getAsObjectFile();
568         if (BinaryOrError) {
569           Slices.emplace_back(*(BinaryOrError.get()), O.getAlign());
570           ExtractedObjects.push_back(std::move(BinaryOrError.get()));
571           continue;
572         }
573         Expected<std::unique_ptr<IRObjectFile>> IROrError =
574             O.getAsIRObject(LLVMCtx);
575         if (IROrError) {
576           consumeError(BinaryOrError.takeError());
577           Slice S = createSliceFromIR(**IROrError, O.getAlign());
578           ExtractedObjects.emplace_back(std::move(IROrError.get()));
579           Slices.emplace_back(std::move(S));
580           continue;
581         }
582         reportError(InputBinary->getFileName(), BinaryOrError.takeError());
583       }
584     } else if (const auto *O = dyn_cast<MachOObjectFile>(InputBinary)) {
585       Slices.emplace_back(*O);
586     } else if (const auto *A = dyn_cast<Archive>(InputBinary)) {
587       Slices.push_back(createSliceFromArchive(LLVMCtx, *A));
588     } else if (const auto *IRO = dyn_cast<IRObjectFile>(InputBinary)) {
589       // Original Apple's lipo set the alignment to 0
590       Expected<Slice> SliceOrErr = Slice::create(*IRO, 0);
591       if (!SliceOrErr) {
592         reportError(InputBinary->getFileName(), SliceOrErr.takeError());
593         continue;
594       }
595       Slices.emplace_back(std::move(SliceOrErr.get()));
596     } else {
597       llvm_unreachable("Unexpected binary format");
598     }
599   }
600   updateAlignments(Slices, Alignments);
601   return Slices;
602 }
603 
createUniversalBinary(LLVMContext & LLVMCtx,ArrayRef<OwningBinary<Binary>> InputBinaries,const StringMap<const uint32_t> & Alignments,StringRef OutputFileName)604 [[noreturn]] static void createUniversalBinary(
605     LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries,
606     const StringMap<const uint32_t> &Alignments, StringRef OutputFileName) {
607   assert(InputBinaries.size() >= 1 && "Incorrect number of input binaries");
608   assert(!OutputFileName.empty() && "Create expects a single output file");
609 
610   SmallVector<std::unique_ptr<SymbolicFile>, 1> ExtractedObjects;
611   SmallVector<Slice, 1> Slices =
612       buildSlices(LLVMCtx, InputBinaries, Alignments, ExtractedObjects);
613   checkArchDuplicates(Slices);
614   checkUnusedAlignments(Slices, Alignments);
615 
616   llvm::stable_sort(Slices);
617   if (Error E = writeUniversalBinary(Slices, OutputFileName))
618     reportError(std::move(E));
619 
620   exit(EXIT_SUCCESS);
621 }
622 
623 [[noreturn]] static void
extractSlice(LLVMContext & LLVMCtx,ArrayRef<OwningBinary<Binary>> InputBinaries,const StringMap<const uint32_t> & Alignments,StringRef ArchType,StringRef OutputFileName)624 extractSlice(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries,
625              const StringMap<const uint32_t> &Alignments, StringRef ArchType,
626              StringRef OutputFileName) {
627   assert(!ArchType.empty() &&
628          "The architecture type should be non-empty");
629   assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
630   assert(!OutputFileName.empty() && "Thin expects a single output file");
631 
632   if (InputBinaries.front().getBinary()->isMachO()) {
633     reportError("input file " +
634                 InputBinaries.front().getBinary()->getFileName() +
635                 " must be a fat file when the -extract option is specified");
636   }
637 
638   SmallVector<std::unique_ptr<SymbolicFile>, 2> ExtractedObjects;
639   SmallVector<Slice, 2> Slices =
640       buildSlices(LLVMCtx, InputBinaries, Alignments, ExtractedObjects);
641   erase_if(Slices, [ArchType](const Slice &S) {
642     return ArchType != S.getArchString();
643   });
644 
645   if (Slices.empty())
646     reportError(
647         "fat input file " + InputBinaries.front().getBinary()->getFileName() +
648         " does not contain the specified architecture " + ArchType);
649 
650   llvm::stable_sort(Slices);
651   if (Error E = writeUniversalBinary(Slices, OutputFileName))
652     reportError(std::move(E));
653   exit(EXIT_SUCCESS);
654 }
655 
656 static StringMap<Slice>
buildReplacementSlices(ArrayRef<OwningBinary<Binary>> ReplacementBinaries,const StringMap<const uint32_t> & Alignments)657 buildReplacementSlices(ArrayRef<OwningBinary<Binary>> ReplacementBinaries,
658                        const StringMap<const uint32_t> &Alignments) {
659   StringMap<Slice> Slices;
660   // populates StringMap of slices to replace with; error checks for mismatched
661   // replace flag args, fat files, and duplicate arch_types
662   for (const auto &OB : ReplacementBinaries) {
663     const Binary *ReplacementBinary = OB.getBinary();
664     auto O = dyn_cast<MachOObjectFile>(ReplacementBinary);
665     if (!O)
666       reportError("replacement file: " + ReplacementBinary->getFileName() +
667                   " is a fat file (must be a thin file)");
668     Slice S(*O);
669     auto Entry = Slices.try_emplace(S.getArchString(), S);
670     if (!Entry.second)
671       reportError("-replace " + S.getArchString() +
672                   " <file_name> specified multiple times: " +
673                   Entry.first->second.getBinary()->getFileName() + ", " +
674                   O->getFileName());
675   }
676   auto SlicesMapRange = map_range(
677       Slices, [](StringMapEntry<Slice> &E) -> Slice & { return E.getValue(); });
678   updateAlignments(SlicesMapRange, Alignments);
679   return Slices;
680 }
681 
682 [[noreturn]] static void
replaceSlices(LLVMContext & LLVMCtx,ArrayRef<OwningBinary<Binary>> InputBinaries,const StringMap<const uint32_t> & Alignments,StringRef OutputFileName,ArrayRef<InputFile> ReplacementFiles)683 replaceSlices(LLVMContext &LLVMCtx,
684               ArrayRef<OwningBinary<Binary>> InputBinaries,
685               const StringMap<const uint32_t> &Alignments,
686               StringRef OutputFileName, ArrayRef<InputFile> ReplacementFiles) {
687   assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
688   assert(!OutputFileName.empty() && "Replace expects a single output file");
689 
690   if (InputBinaries.front().getBinary()->isMachO())
691     reportError("input file " +
692                 InputBinaries.front().getBinary()->getFileName() +
693                 " must be a fat file when the -replace option is specified");
694 
695   SmallVector<OwningBinary<Binary>, 1> ReplacementBinaries =
696       readInputBinaries(LLVMCtx, ReplacementFiles);
697 
698   StringMap<Slice> ReplacementSlices =
699       buildReplacementSlices(ReplacementBinaries, Alignments);
700   SmallVector<std::unique_ptr<SymbolicFile>, 2> ExtractedObjects;
701   SmallVector<Slice, 2> Slices =
702       buildSlices(LLVMCtx, InputBinaries, Alignments, ExtractedObjects);
703 
704   for (auto &Slice : Slices) {
705     auto It = ReplacementSlices.find(Slice.getArchString());
706     if (It != ReplacementSlices.end()) {
707       Slice = It->second;
708       ReplacementSlices.erase(It); // only keep remaining replacing arch_types
709     }
710   }
711 
712   if (!ReplacementSlices.empty())
713     reportError("-replace " + ReplacementSlices.begin()->first() +
714                 " <file_name> specified but fat file: " +
715                 InputBinaries.front().getBinary()->getFileName() +
716                 " does not contain that architecture");
717 
718   checkUnusedAlignments(Slices, Alignments);
719 
720   llvm::stable_sort(Slices);
721   if (Error E = writeUniversalBinary(Slices, OutputFileName))
722     reportError(std::move(E));
723   exit(EXIT_SUCCESS);
724 }
725 
llvm_lipo_main(int argc,char ** argv)726 int llvm_lipo_main(int argc, char **argv) {
727   InitLLVM X(argc, argv);
728   llvm::InitializeAllTargetInfos();
729   llvm::InitializeAllTargetMCs();
730   llvm::InitializeAllAsmParsers();
731 
732   Config C = parseLipoOptions(ArrayRef(argv + 1, argc - 1));
733   LLVMContext LLVMCtx;
734   SmallVector<OwningBinary<Binary>, 1> InputBinaries =
735       readInputBinaries(LLVMCtx, C.InputFiles);
736 
737   switch (C.ActionToPerform) {
738   case LipoAction::VerifyArch:
739     verifyArch(InputBinaries, C.VerifyArchList);
740     break;
741   case LipoAction::PrintArchs:
742     printArchs(LLVMCtx, InputBinaries);
743     break;
744   case LipoAction::PrintInfo:
745     printInfo(LLVMCtx, InputBinaries);
746     break;
747   case LipoAction::ThinArch:
748     thinSlice(LLVMCtx, InputBinaries, C.ArchType, C.OutputFile);
749     break;
750   case LipoAction::ExtractArch:
751     extractSlice(LLVMCtx, InputBinaries, C.SegmentAlignments, C.ArchType,
752                  C.OutputFile);
753     break;
754   case LipoAction::CreateUniversal:
755     createUniversalBinary(LLVMCtx, InputBinaries, C.SegmentAlignments,
756                           C.OutputFile);
757     break;
758   case LipoAction::ReplaceArch:
759     replaceSlices(LLVMCtx, InputBinaries, C.SegmentAlignments, C.OutputFile,
760                   C.ReplacementFiles);
761     break;
762   }
763   return EXIT_SUCCESS;
764 }
765