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/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"
27 
28 using namespace llvm;
29 using namespace llvm::object;
30 
31 static const StringRef ToolName = "llvm-lipo";
32 
reportError(Twine Message)33 LLVM_ATTRIBUTE_NORETURN static void reportError(Twine Message) {
34   WithColor::error(errs(), ToolName) << Message << "\n";
35   errs().flush();
36   exit(EXIT_FAILURE);
37 }
38 
reportError(StringRef File,Error E)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);
47 }
48 
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
57 };
58 
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
64 
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
74 };
75 
76 class LipoOptTable : public opt::OptTable {
77 public:
LipoOptTable()78   LipoOptTable() : OptTable(LipoInfoTable) {}
79 };
80 
81 enum class LipoAction {
82   PrintArchs,
83   PrintInfo,
84   VerifyArch,
85   ThinArch,
86   ExtractArch,
87   CreateUniversal,
88   ReplaceArch,
89 };
90 
91 struct InputFile {
92   Optional<StringRef> ArchType;
93   StringRef FileName;
94 };
95 
96 struct Config {
97   SmallVector<InputFile, 1> InputFiles;
98   SmallVector<std::string, 1> VerifyArchList;
99   SmallVector<InputFile, 1> ReplacementFiles;
100   StringMap<const uint32_t> SegmentAlignments;
101   std::string ArchType;
102   std::string OutputFile;
103   LipoAction ActionToPerform;
104 };
105 
106 // For compatibility with cctools lipo, a file's alignment is calculated as the
107 // minimum aligment of all segments. For object files, the file's alignment is
108 // the maximum alignment of its sections.
calculateFileAlignment(const MachOObjectFile & O)109 static uint32_t calculateFileAlignment(const MachOObjectFile &O) {
110   uint32_t P2CurrentAlignment;
111   uint32_t P2MinAlignment = MachOUniversalBinary::MaxSectionAlignment;
112   const bool Is64Bit = O.is64Bit();
113 
114   for (const auto &LC : O.load_commands()) {
115     if (LC.C.cmd != (Is64Bit ? MachO::LC_SEGMENT_64 : MachO::LC_SEGMENT))
116       continue;
117     if (O.getHeader().filetype == MachO::MH_OBJECT) {
118       unsigned NumberOfSections =
119           (Is64Bit ? O.getSegment64LoadCommand(LC).nsects
120                    : O.getSegmentLoadCommand(LC).nsects);
121       P2CurrentAlignment = NumberOfSections ? 2 : P2MinAlignment;
122       for (unsigned SI = 0; SI < NumberOfSections; ++SI) {
123         P2CurrentAlignment = std::max(P2CurrentAlignment,
124                                       (Is64Bit ? O.getSection64(LC, SI).align
125                                                : O.getSection(LC, SI).align));
126       }
127     } else {
128       P2CurrentAlignment =
129           countTrailingZeros(Is64Bit ? O.getSegment64LoadCommand(LC).vmaddr
130                                      : O.getSegmentLoadCommand(LC).vmaddr);
131     }
132     P2MinAlignment = std::min(P2MinAlignment, P2CurrentAlignment);
133   }
134   // return a value >= 4 byte aligned, and less than MachO MaxSectionAlignment
135   return std::max(
136       static_cast<uint32_t>(2),
137       std::min(P2MinAlignment, static_cast<uint32_t>(
138                                    MachOUniversalBinary::MaxSectionAlignment)));
139 }
140 
calculateAlignment(const MachOObjectFile * ObjectFile)141 static uint32_t calculateAlignment(const MachOObjectFile *ObjectFile) {
142   switch (ObjectFile->getHeader().cputype) {
143   case MachO::CPU_TYPE_I386:
144   case MachO::CPU_TYPE_X86_64:
145   case MachO::CPU_TYPE_POWERPC:
146   case MachO::CPU_TYPE_POWERPC64:
147     return 12; // log2 value of page size(4k) for x86 and PPC
148   case MachO::CPU_TYPE_ARM:
149   case MachO::CPU_TYPE_ARM64:
150   case MachO::CPU_TYPE_ARM64_32:
151     return 14; // log2 value of page size(16k) for Darwin ARM
152   default:
153     return calculateFileAlignment(*ObjectFile);
154   }
155 }
156 
157 class Slice {
158   const Binary *B;
159   uint32_t CPUType;
160   uint32_t CPUSubType;
161   std::string ArchName;
162 
163   // P2Alignment field stores slice alignment values from universal
164   // binaries. This is also needed to order the slices so the total
165   // file size can be calculated before creating the output buffer.
166   uint32_t P2Alignment;
167 
168 public:
Slice(const MachOObjectFile * O,uint32_t Align)169   Slice(const MachOObjectFile *O, uint32_t Align)
170       : B(O), CPUType(O->getHeader().cputype),
171         CPUSubType(O->getHeader().cpusubtype),
172         ArchName(std::string(O->getArchTriple().getArchName())),
173         P2Alignment(Align) {}
174 
Slice(const MachOObjectFile * O)175   explicit Slice(const MachOObjectFile *O) : Slice(O, calculateAlignment(O)){};
176 
Slice(const Archive * A)177   explicit Slice(const Archive *A) : B(A) {
178     Error Err = Error::success();
179     std::unique_ptr<MachOObjectFile> FO = nullptr;
180     for (const Archive::Child &Child : A->children(Err)) {
181       Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
182       if (!ChildOrErr)
183         reportError(A->getFileName(), ChildOrErr.takeError());
184       Binary *Bin = ChildOrErr.get().get();
185       if (Bin->isMachOUniversalBinary())
186         reportError(("archive member " + Bin->getFileName() +
187                      " is a fat file (not allowed in an archive)")
188                         .str());
189       if (!Bin->isMachO())
190         reportError(("archive member " + Bin->getFileName() +
191                      " is not a MachO file (not allowed in an archive)"));
192       MachOObjectFile *O = cast<MachOObjectFile>(Bin);
193       if (FO &&
194           std::tie(FO->getHeader().cputype, FO->getHeader().cpusubtype) !=
195               std::tie(O->getHeader().cputype, O->getHeader().cpusubtype)) {
196         reportError(("archive member " + O->getFileName() + " cputype (" +
197                      Twine(O->getHeader().cputype) + ") and cpusubtype(" +
198                      Twine(O->getHeader().cpusubtype) +
199                      ") does not match previous archive members cputype (" +
200                      Twine(FO->getHeader().cputype) + ") and cpusubtype(" +
201                      Twine(FO->getHeader().cpusubtype) +
202                      ") (all members must match) " + FO->getFileName())
203                         .str());
204       }
205       if (!FO) {
206         ChildOrErr.get().release();
207         FO.reset(O);
208       }
209     }
210     if (Err)
211       reportError(A->getFileName(), std::move(Err));
212     if (!FO)
213       reportError(("empty archive with no architecture specification: " +
214                    A->getFileName() + " (can't determine architecture for it)")
215                       .str());
216     CPUType = FO->getHeader().cputype;
217     CPUSubType = FO->getHeader().cpusubtype;
218     ArchName = std::string(FO->getArchTriple().getArchName());
219     // Replicate the behavior of cctools lipo.
220     P2Alignment = FO->is64Bit() ? 3 : 2;
221   }
222 
setP2Alignment(uint32_t Align)223   void setP2Alignment(uint32_t Align) { P2Alignment = Align; }
224 
getBinary() const225   const Binary *getBinary() const { return B; }
226 
getCPUType() const227   uint32_t getCPUType() const { return CPUType; }
228 
getCPUSubType() const229   uint32_t getCPUSubType() const { return CPUSubType; }
230 
getP2Alignment() const231   uint32_t getP2Alignment() const { return P2Alignment; }
232 
getCPUID() const233   uint64_t getCPUID() const {
234     return static_cast<uint64_t>(CPUType) << 32 | CPUSubType;
235   }
236 
getArchString() const237   std::string getArchString() const {
238     if (!ArchName.empty())
239       return ArchName;
240     return ("unknown(" + Twine(CPUType) + "," +
241             Twine(CPUSubType & ~MachO::CPU_SUBTYPE_MASK) + ")")
242         .str();
243   }
244 
operator <(const Slice & Lhs,const Slice & Rhs)245   friend bool operator<(const Slice &Lhs, const Slice &Rhs) {
246     if (Lhs.CPUType == Rhs.CPUType)
247       return Lhs.CPUSubType < Rhs.CPUSubType;
248     // force arm64-family to follow after all other slices for
249     // compatibility with cctools lipo
250     if (Lhs.CPUType == MachO::CPU_TYPE_ARM64)
251       return false;
252     if (Rhs.CPUType == MachO::CPU_TYPE_ARM64)
253       return true;
254     // Sort by alignment to minimize file size
255     return Lhs.P2Alignment < Rhs.P2Alignment;
256   }
257 };
258 
259 } // end namespace
260 
validateArchitectureName(StringRef ArchitectureName)261 static void validateArchitectureName(StringRef ArchitectureName) {
262   if (!MachOObjectFile::isValidArch(ArchitectureName)) {
263     std::string Buf;
264     raw_string_ostream OS(Buf);
265     OS << "Invalid architecture: " << ArchitectureName
266        << "\nValid architecture names are:";
267     for (auto arch : MachOObjectFile::getValidArchs())
268       OS << " " << arch;
269     reportError(OS.str());
270   }
271 }
272 
parseLipoOptions(ArrayRef<const char * > ArgsArr)273 static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
274   Config C;
275   LipoOptTable T;
276   unsigned MissingArgumentIndex, MissingArgumentCount;
277   opt::InputArgList InputArgs =
278       T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
279 
280   if (MissingArgumentCount)
281     reportError("missing argument to " +
282                 StringRef(InputArgs.getArgString(MissingArgumentIndex)) +
283                 " option");
284 
285   if (InputArgs.size() == 0) {
286     // PrintHelp does not accept Twine.
287     T.PrintHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
288     exit(EXIT_FAILURE);
289   }
290 
291   if (InputArgs.hasArg(LIPO_help)) {
292     // PrintHelp does not accept Twine.
293     T.PrintHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
294     exit(EXIT_SUCCESS);
295   }
296 
297   if (InputArgs.hasArg(LIPO_version)) {
298     outs() << ToolName + "\n";
299     cl::PrintVersionMessage();
300     exit(EXIT_SUCCESS);
301   }
302 
303   for (auto Arg : InputArgs.filtered(LIPO_UNKNOWN))
304     reportError("unknown argument '" + Arg->getAsString(InputArgs) + "'");
305 
306   for (auto Arg : InputArgs.filtered(LIPO_INPUT))
307     C.InputFiles.push_back({None, Arg->getValue()});
308   for (auto Arg : InputArgs.filtered(LIPO_arch)) {
309     validateArchitectureName(Arg->getValue(0));
310     if (!Arg->getValue(1))
311       reportError(
312           "arch is missing an argument: expects -arch arch_type file_name");
313     C.InputFiles.push_back({StringRef(Arg->getValue(0)), Arg->getValue(1)});
314   }
315 
316   if (C.InputFiles.empty())
317     reportError("at least one input file should be specified");
318 
319   if (InputArgs.hasArg(LIPO_output))
320     C.OutputFile = std::string(InputArgs.getLastArgValue(LIPO_output));
321 
322   for (auto Segalign : InputArgs.filtered(LIPO_segalign)) {
323     if (!Segalign->getValue(1))
324       reportError("segalign is missing an argument: expects -segalign "
325                   "arch_type alignment_value");
326 
327     validateArchitectureName(Segalign->getValue(0));
328 
329     uint32_t AlignmentValue;
330     if (!to_integer<uint32_t>(Segalign->getValue(1), AlignmentValue, 16))
331       reportError("argument to -segalign <arch_type> " +
332                   Twine(Segalign->getValue(1)) +
333                   " (hex) is not a proper hexadecimal number");
334     if (!isPowerOf2_32(AlignmentValue))
335       reportError("argument to -segalign <arch_type> " +
336                   Twine(Segalign->getValue(1)) +
337                   " (hex) must be a non-zero power of two");
338     if (Log2_32(AlignmentValue) > MachOUniversalBinary::MaxSectionAlignment)
339       reportError(
340           "argument to -segalign <arch_type> " + Twine(Segalign->getValue(1)) +
341           " (hex) must be less than or equal to the maximum section align 2^" +
342           Twine(MachOUniversalBinary::MaxSectionAlignment));
343     auto Entry = C.SegmentAlignments.try_emplace(Segalign->getValue(0),
344                                                  Log2_32(AlignmentValue));
345     if (!Entry.second)
346       reportError("-segalign " + Twine(Segalign->getValue(0)) +
347                   " <alignment_value> specified multiple times: " +
348                   Twine(1 << Entry.first->second) + ", " +
349                   Twine(AlignmentValue));
350   }
351 
352   SmallVector<opt::Arg *, 1> ActionArgs(InputArgs.filtered(LIPO_action_group));
353   if (ActionArgs.empty())
354     reportError("at least one action should be specified");
355   // errors if multiple actions specified other than replace
356   // multiple replace flags may be specified, as long as they are not mixed with
357   // other action flags
358   auto ReplacementArgsRange = InputArgs.filtered(LIPO_replace);
359   if (ActionArgs.size() > 1 &&
360       ActionArgs.size() !=
361           static_cast<size_t>(std::distance(ReplacementArgsRange.begin(),
362                                             ReplacementArgsRange.end()))) {
363     std::string Buf;
364     raw_string_ostream OS(Buf);
365     OS << "only one of the following actions can be specified:";
366     for (auto Arg : ActionArgs)
367       OS << " " << Arg->getSpelling();
368     reportError(OS.str());
369   }
370 
371   switch (ActionArgs[0]->getOption().getID()) {
372   case LIPO_verify_arch:
373     for (auto A : InputArgs.getAllArgValues(LIPO_verify_arch))
374       C.VerifyArchList.push_back(A);
375     if (C.VerifyArchList.empty())
376       reportError(
377           "verify_arch requires at least one architecture to be specified");
378     if (C.InputFiles.size() > 1)
379       reportError("verify_arch expects a single input file");
380     C.ActionToPerform = LipoAction::VerifyArch;
381     return C;
382 
383   case LIPO_archs:
384     if (C.InputFiles.size() > 1)
385       reportError("archs expects a single input file");
386     C.ActionToPerform = LipoAction::PrintArchs;
387     return C;
388 
389   case LIPO_info:
390     C.ActionToPerform = LipoAction::PrintInfo;
391     return C;
392 
393   case LIPO_thin:
394     if (C.InputFiles.size() > 1)
395       reportError("thin expects a single input file");
396     if (C.OutputFile.empty())
397       reportError("thin expects a single output file");
398     C.ArchType = ActionArgs[0]->getValue();
399     validateArchitectureName(C.ArchType);
400     C.ActionToPerform = LipoAction::ThinArch;
401     return C;
402 
403   case LIPO_extract:
404     if (C.InputFiles.size() > 1)
405       reportError("extract expects a single input file");
406     if (C.OutputFile.empty())
407       reportError("extract expects a single output file");
408     C.ArchType = ActionArgs[0]->getValue();
409     validateArchitectureName(C.ArchType);
410     C.ActionToPerform = LipoAction::ExtractArch;
411     return C;
412 
413   case LIPO_create:
414     if (C.OutputFile.empty())
415       reportError("create expects a single output file to be specified");
416     C.ActionToPerform = LipoAction::CreateUniversal;
417     return C;
418 
419   case LIPO_replace:
420     for (auto Action : ActionArgs) {
421       if (!Action->getValue(1))
422         reportError(
423             "replace is missing an argument: expects -replace arch_type "
424             "file_name");
425       validateArchitectureName(Action->getValue(0));
426       C.ReplacementFiles.push_back(
427           {StringRef(Action->getValue(0)), Action->getValue(1)});
428     }
429 
430     if (C.OutputFile.empty())
431       reportError("replace expects a single output file to be specified");
432     if (C.InputFiles.size() > 1)
433       reportError("replace expects a single input file");
434     C.ActionToPerform = LipoAction::ReplaceArch;
435     return C;
436 
437   default:
438     reportError("llvm-lipo action unspecified");
439   }
440 }
441 
442 static SmallVector<OwningBinary<Binary>, 1>
readInputBinaries(ArrayRef<InputFile> InputFiles)443 readInputBinaries(ArrayRef<InputFile> InputFiles) {
444   SmallVector<OwningBinary<Binary>, 1> InputBinaries;
445   for (const InputFile &IF : InputFiles) {
446     Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(IF.FileName);
447     if (!BinaryOrErr)
448       reportError(IF.FileName, BinaryOrErr.takeError());
449     const Binary *B = BinaryOrErr->getBinary();
450     if (!B->isArchive() && !B->isMachO() && !B->isMachOUniversalBinary())
451       reportError("File " + IF.FileName + " has unsupported binary format");
452     if (IF.ArchType && (B->isMachO() || B->isArchive())) {
453       const auto S = B->isMachO() ? Slice(cast<MachOObjectFile>(B))
454                                   : Slice(cast<Archive>(B));
455       const auto SpecifiedCPUType = MachO::getCPUTypeFromArchitecture(
456                                         MachO::getArchitectureFromName(
457                                             Triple(*IF.ArchType).getArchName()))
458                                         .first;
459       // For compatibility with cctools' lipo the comparison is relaxed just to
460       // checking cputypes.
461       if (S.getCPUType() != SpecifiedCPUType)
462         reportError("specified architecture: " + *IF.ArchType +
463                     " for file: " + B->getFileName() +
464                     " does not match the file's architecture (" +
465                     S.getArchString() + ")");
466     }
467     InputBinaries.push_back(std::move(*BinaryOrErr));
468   }
469   return InputBinaries;
470 }
471 
472 LLVM_ATTRIBUTE_NORETURN
verifyArch(ArrayRef<OwningBinary<Binary>> InputBinaries,ArrayRef<std::string> VerifyArchList)473 static void verifyArch(ArrayRef<OwningBinary<Binary>> InputBinaries,
474                        ArrayRef<std::string> VerifyArchList) {
475   assert(!VerifyArchList.empty() &&
476          "The list of architectures should be non-empty");
477   assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
478 
479   for (StringRef Arch : VerifyArchList)
480     validateArchitectureName(Arch);
481 
482   if (auto UO =
483           dyn_cast<MachOUniversalBinary>(InputBinaries.front().getBinary())) {
484     for (StringRef Arch : VerifyArchList) {
485       Expected<MachOUniversalBinary::ObjectForArch> Obj =
486           UO->getObjectForArch(Arch);
487       if (!Obj)
488         exit(EXIT_FAILURE);
489     }
490   } else if (auto O =
491                  dyn_cast<MachOObjectFile>(InputBinaries.front().getBinary())) {
492     const Triple::ArchType ObjectArch = O->getArch();
493     for (StringRef Arch : VerifyArchList)
494       if (ObjectArch != Triple(Arch).getArch())
495         exit(EXIT_FAILURE);
496   } else {
497     llvm_unreachable("Unexpected binary format");
498   }
499   exit(EXIT_SUCCESS);
500 }
501 
printBinaryArchs(const Binary * Binary,raw_ostream & OS)502 static void printBinaryArchs(const Binary *Binary, raw_ostream &OS) {
503   // Prints trailing space for compatibility with cctools lipo.
504   if (auto UO = dyn_cast<MachOUniversalBinary>(Binary)) {
505     for (const auto &O : UO->objects()) {
506       Expected<std::unique_ptr<MachOObjectFile>> MachOObjOrError =
507           O.getAsObjectFile();
508       if (MachOObjOrError) {
509         OS << Slice(MachOObjOrError->get()).getArchString() << " ";
510         continue;
511       }
512       Expected<std::unique_ptr<Archive>> ArchiveOrError = O.getAsArchive();
513       if (ArchiveOrError) {
514         consumeError(MachOObjOrError.takeError());
515         OS << Slice(ArchiveOrError->get()).getArchString() << " ";
516         continue;
517       }
518       consumeError(ArchiveOrError.takeError());
519       reportError(Binary->getFileName(), MachOObjOrError.takeError());
520     }
521     OS << "\n";
522     return;
523   }
524   OS << Slice(cast<MachOObjectFile>(Binary)).getArchString() << " \n";
525 }
526 
527 LLVM_ATTRIBUTE_NORETURN
printArchs(ArrayRef<OwningBinary<Binary>> InputBinaries)528 static void printArchs(ArrayRef<OwningBinary<Binary>> InputBinaries) {
529   assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
530   printBinaryArchs(InputBinaries.front().getBinary(), outs());
531   exit(EXIT_SUCCESS);
532 }
533 
534 LLVM_ATTRIBUTE_NORETURN
printInfo(ArrayRef<OwningBinary<Binary>> InputBinaries)535 static void printInfo(ArrayRef<OwningBinary<Binary>> InputBinaries) {
536   // Group universal and thin files together for compatibility with cctools lipo
537   for (auto &IB : InputBinaries) {
538     const Binary *Binary = IB.getBinary();
539     if (Binary->isMachOUniversalBinary()) {
540       outs() << "Architectures in the fat file: " << Binary->getFileName()
541              << " are: ";
542       printBinaryArchs(Binary, outs());
543     }
544   }
545   for (auto &IB : InputBinaries) {
546     const Binary *Binary = IB.getBinary();
547     if (!Binary->isMachOUniversalBinary()) {
548       assert(Binary->isMachO() && "expected MachO binary");
549       outs() << "Non-fat file: " << Binary->getFileName()
550              << " is architecture: ";
551       printBinaryArchs(Binary, outs());
552     }
553   }
554   exit(EXIT_SUCCESS);
555 }
556 
557 LLVM_ATTRIBUTE_NORETURN
thinSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,StringRef ArchType,StringRef OutputFileName)558 static void thinSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,
559                       StringRef ArchType, StringRef OutputFileName) {
560   assert(!ArchType.empty() && "The architecture type should be non-empty");
561   assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
562   assert(!OutputFileName.empty() && "Thin expects a single output file");
563 
564   if (InputBinaries.front().getBinary()->isMachO()) {
565     reportError("input file " +
566                 InputBinaries.front().getBinary()->getFileName() +
567                 " must be a fat file when the -thin option is specified");
568     exit(EXIT_FAILURE);
569   }
570 
571   auto *UO = cast<MachOUniversalBinary>(InputBinaries.front().getBinary());
572   Expected<std::unique_ptr<MachOObjectFile>> Obj =
573       UO->getMachOObjectForArch(ArchType);
574   Expected<std::unique_ptr<Archive>> Ar = UO->getArchiveForArch(ArchType);
575   if (!Obj && !Ar)
576     reportError("fat input file " + UO->getFileName() +
577                 " does not contain the specified architecture " + ArchType +
578                 " to thin it to");
579   Binary *B = Obj ? static_cast<Binary *>(Obj->get())
580                   : static_cast<Binary *>(Ar->get());
581   Expected<std::unique_ptr<FileOutputBuffer>> OutFileOrError =
582       FileOutputBuffer::create(OutputFileName,
583                                B->getMemoryBufferRef().getBufferSize(),
584                                sys::fs::can_execute(UO->getFileName())
585                                    ? FileOutputBuffer::F_executable
586                                    : 0);
587   if (!OutFileOrError)
588     reportError(OutputFileName, OutFileOrError.takeError());
589   std::copy(B->getMemoryBufferRef().getBufferStart(),
590             B->getMemoryBufferRef().getBufferEnd(),
591             OutFileOrError.get()->getBufferStart());
592   if (Error E = OutFileOrError.get()->commit())
593     reportError(OutputFileName, std::move(E));
594   exit(EXIT_SUCCESS);
595 }
596 
checkArchDuplicates(ArrayRef<Slice> Slices)597 static void checkArchDuplicates(ArrayRef<Slice> Slices) {
598   DenseMap<uint64_t, const Binary *> CPUIds;
599   for (const auto &S : Slices) {
600     auto Entry = CPUIds.try_emplace(S.getCPUID(), S.getBinary());
601     if (!Entry.second)
602       reportError(Entry.first->second->getFileName() + " and " +
603                   S.getBinary()->getFileName() +
604                   " have the same architecture " + S.getArchString() +
605                   " and therefore cannot be in the same universal binary");
606   }
607 }
608 
609 template <typename Range>
updateAlignments(Range & Slices,const StringMap<const uint32_t> & Alignments)610 static void updateAlignments(Range &Slices,
611                              const StringMap<const uint32_t> &Alignments) {
612   for (auto &Slice : Slices) {
613     auto Alignment = Alignments.find(Slice.getArchString());
614     if (Alignment != Alignments.end())
615       Slice.setP2Alignment(Alignment->second);
616   }
617 }
618 
checkUnusedAlignments(ArrayRef<Slice> Slices,const StringMap<const uint32_t> & Alignments)619 static void checkUnusedAlignments(ArrayRef<Slice> Slices,
620                                   const StringMap<const uint32_t> &Alignments) {
621   auto HasArch = [&](StringRef Arch) {
622     return llvm::find_if(Slices, [Arch](Slice S) {
623              return S.getArchString() == Arch;
624            }) != Slices.end();
625   };
626   for (StringRef Arch : Alignments.keys())
627     if (!HasArch(Arch))
628       reportError("-segalign " + Arch +
629                   " <value> specified but resulting fat file does not contain "
630                   "that architecture ");
631 }
632 
633 // Updates vector ExtractedObjects with the MachOObjectFiles extracted from
634 // Universal Binary files to transfer ownership.
buildSlices(ArrayRef<OwningBinary<Binary>> InputBinaries,const StringMap<const uint32_t> & Alignments,SmallVectorImpl<std::unique_ptr<MachOObjectFile>> & ExtractedObjects)635 static SmallVector<Slice, 2> buildSlices(
636     ArrayRef<OwningBinary<Binary>> InputBinaries,
637     const StringMap<const uint32_t> &Alignments,
638     SmallVectorImpl<std::unique_ptr<MachOObjectFile>> &ExtractedObjects) {
639   SmallVector<Slice, 2> Slices;
640   for (auto &IB : InputBinaries) {
641     const Binary *InputBinary = IB.getBinary();
642     if (auto UO = dyn_cast<MachOUniversalBinary>(InputBinary)) {
643       for (const auto &O : UO->objects()) {
644         Expected<std::unique_ptr<MachOObjectFile>> BinaryOrError =
645             O.getAsObjectFile();
646         if (!BinaryOrError)
647           reportError(InputBinary->getFileName(), BinaryOrError.takeError());
648         ExtractedObjects.push_back(std::move(BinaryOrError.get()));
649         Slices.emplace_back(ExtractedObjects.back().get(), O.getAlign());
650       }
651     } else if (auto O = dyn_cast<MachOObjectFile>(InputBinary)) {
652       Slices.emplace_back(O);
653     } else if (auto A = dyn_cast<Archive>(InputBinary)) {
654       Slices.emplace_back(A);
655     } else {
656       llvm_unreachable("Unexpected binary format");
657     }
658   }
659   updateAlignments(Slices, Alignments);
660   return Slices;
661 }
662 
663 static SmallVector<MachO::fat_arch, 2>
buildFatArchList(ArrayRef<Slice> Slices)664 buildFatArchList(ArrayRef<Slice> Slices) {
665   SmallVector<MachO::fat_arch, 2> FatArchList;
666   uint64_t Offset =
667       sizeof(MachO::fat_header) + Slices.size() * sizeof(MachO::fat_arch);
668 
669   for (const auto &S : Slices) {
670     Offset = alignTo(Offset, 1ull << S.getP2Alignment());
671     if (Offset > UINT32_MAX)
672       reportError("fat file too large to be created because the offset "
673                   "field in struct fat_arch is only 32-bits and the offset " +
674                   Twine(Offset) + " for " + S.getBinary()->getFileName() +
675                   " for architecture " + S.getArchString() + "exceeds that.");
676 
677     MachO::fat_arch FatArch;
678     FatArch.cputype = S.getCPUType();
679     FatArch.cpusubtype = S.getCPUSubType();
680     FatArch.offset = Offset;
681     FatArch.size = S.getBinary()->getMemoryBufferRef().getBufferSize();
682     FatArch.align = S.getP2Alignment();
683     Offset += FatArch.size;
684     FatArchList.push_back(FatArch);
685   }
686   return FatArchList;
687 }
688 
createUniversalBinary(SmallVectorImpl<Slice> & Slices,StringRef OutputFileName)689 static void createUniversalBinary(SmallVectorImpl<Slice> &Slices,
690                                   StringRef OutputFileName) {
691   MachO::fat_header FatHeader;
692   FatHeader.magic = MachO::FAT_MAGIC;
693   FatHeader.nfat_arch = Slices.size();
694 
695   stable_sort(Slices);
696   SmallVector<MachO::fat_arch, 2> FatArchList = buildFatArchList(Slices);
697 
698   const bool IsExecutable = any_of(Slices, [](Slice S) {
699     return sys::fs::can_execute(S.getBinary()->getFileName());
700   });
701   const uint64_t OutputFileSize =
702       static_cast<uint64_t>(FatArchList.back().offset) +
703       FatArchList.back().size;
704   Expected<std::unique_ptr<FileOutputBuffer>> OutFileOrError =
705       FileOutputBuffer::create(OutputFileName, OutputFileSize,
706                                IsExecutable ? FileOutputBuffer::F_executable
707                                             : 0);
708   if (!OutFileOrError)
709     reportError(OutputFileName, OutFileOrError.takeError());
710   std::unique_ptr<FileOutputBuffer> OutFile = std::move(OutFileOrError.get());
711   std::memset(OutFile->getBufferStart(), 0, OutputFileSize);
712 
713   if (sys::IsLittleEndianHost)
714     MachO::swapStruct(FatHeader);
715   std::memcpy(OutFile->getBufferStart(), &FatHeader, sizeof(MachO::fat_header));
716 
717   for (size_t Index = 0, Size = Slices.size(); Index < Size; ++Index) {
718     MemoryBufferRef BufferRef = Slices[Index].getBinary()->getMemoryBufferRef();
719     std::copy(BufferRef.getBufferStart(), BufferRef.getBufferEnd(),
720               OutFile->getBufferStart() + FatArchList[Index].offset);
721   }
722 
723   // FatArchs written after Slices in order to reduce the number of swaps for
724   // the LittleEndian case
725   if (sys::IsLittleEndianHost)
726     for (MachO::fat_arch &FA : FatArchList)
727       MachO::swapStruct(FA);
728   std::memcpy(OutFile->getBufferStart() + sizeof(MachO::fat_header),
729               FatArchList.begin(),
730               sizeof(MachO::fat_arch) * FatArchList.size());
731 
732   if (Error E = OutFile->commit())
733     reportError(OutputFileName, std::move(E));
734 }
735 
736 LLVM_ATTRIBUTE_NORETURN
createUniversalBinary(ArrayRef<OwningBinary<Binary>> InputBinaries,const StringMap<const uint32_t> & Alignments,StringRef OutputFileName)737 static void createUniversalBinary(ArrayRef<OwningBinary<Binary>> InputBinaries,
738                                   const StringMap<const uint32_t> &Alignments,
739                                   StringRef OutputFileName) {
740   assert(InputBinaries.size() >= 1 && "Incorrect number of input binaries");
741   assert(!OutputFileName.empty() && "Create expects a single output file");
742 
743   SmallVector<std::unique_ptr<MachOObjectFile>, 1> ExtractedObjects;
744   SmallVector<Slice, 1> Slices =
745       buildSlices(InputBinaries, Alignments, ExtractedObjects);
746   checkArchDuplicates(Slices);
747   checkUnusedAlignments(Slices, Alignments);
748   createUniversalBinary(Slices, OutputFileName);
749 
750   exit(EXIT_SUCCESS);
751 }
752 
753 LLVM_ATTRIBUTE_NORETURN
extractSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,const StringMap<const uint32_t> & Alignments,StringRef ArchType,StringRef OutputFileName)754 static void extractSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,
755                          const StringMap<const uint32_t> &Alignments,
756                          StringRef ArchType, StringRef OutputFileName) {
757   assert(!ArchType.empty() &&
758          "The architecture type should be non-empty");
759   assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
760   assert(!OutputFileName.empty() && "Thin expects a single output file");
761 
762   if (InputBinaries.front().getBinary()->isMachO()) {
763     reportError("input file " +
764                 InputBinaries.front().getBinary()->getFileName() +
765                 " must be a fat file when the -extract option is specified");
766   }
767 
768   SmallVector<std::unique_ptr<MachOObjectFile>, 2> ExtractedObjects;
769   SmallVector<Slice, 2> Slices =
770       buildSlices(InputBinaries, Alignments, ExtractedObjects);
771   erase_if(Slices, [ArchType](const Slice &S) {
772     return ArchType != S.getArchString();
773   });
774 
775   if (Slices.empty())
776     reportError(
777         "fat input file " + InputBinaries.front().getBinary()->getFileName() +
778         " does not contain the specified architecture " + ArchType);
779   createUniversalBinary(Slices, OutputFileName);
780   exit(EXIT_SUCCESS);
781 }
782 
783 static StringMap<Slice>
buildReplacementSlices(ArrayRef<OwningBinary<Binary>> ReplacementBinaries,const StringMap<const uint32_t> & Alignments)784 buildReplacementSlices(ArrayRef<OwningBinary<Binary>> ReplacementBinaries,
785                        const StringMap<const uint32_t> &Alignments) {
786   StringMap<Slice> Slices;
787   // populates StringMap of slices to replace with; error checks for mismatched
788   // replace flag args, fat files, and duplicate arch_types
789   for (const auto &OB : ReplacementBinaries) {
790     const Binary *ReplacementBinary = OB.getBinary();
791     auto O = dyn_cast<MachOObjectFile>(ReplacementBinary);
792     if (!O)
793       reportError("replacement file: " + ReplacementBinary->getFileName() +
794                   " is a fat file (must be a thin file)");
795     Slice S(O);
796     auto Entry = Slices.try_emplace(S.getArchString(), S);
797     if (!Entry.second)
798       reportError("-replace " + S.getArchString() +
799                   " <file_name> specified multiple times: " +
800                   Entry.first->second.getBinary()->getFileName() + ", " +
801                   O->getFileName());
802   }
803   auto SlicesMapRange = map_range(
804       Slices, [](StringMapEntry<Slice> &E) -> Slice & { return E.getValue(); });
805   updateAlignments(SlicesMapRange, Alignments);
806   return Slices;
807 }
808 
809 LLVM_ATTRIBUTE_NORETURN
replaceSlices(ArrayRef<OwningBinary<Binary>> InputBinaries,const StringMap<const uint32_t> & Alignments,StringRef OutputFileName,ArrayRef<InputFile> ReplacementFiles)810 static void replaceSlices(ArrayRef<OwningBinary<Binary>> InputBinaries,
811                           const StringMap<const uint32_t> &Alignments,
812                           StringRef OutputFileName,
813                           ArrayRef<InputFile> ReplacementFiles) {
814   assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
815   assert(!OutputFileName.empty() && "Replace expects a single output file");
816 
817   if (InputBinaries.front().getBinary()->isMachO())
818     reportError("input file " +
819                 InputBinaries.front().getBinary()->getFileName() +
820                 " must be a fat file when the -replace option is specified");
821 
822   SmallVector<OwningBinary<Binary>, 1> ReplacementBinaries =
823       readInputBinaries(ReplacementFiles);
824 
825   StringMap<Slice> ReplacementSlices =
826       buildReplacementSlices(ReplacementBinaries, Alignments);
827   SmallVector<std::unique_ptr<MachOObjectFile>, 2> ExtractedObjects;
828   SmallVector<Slice, 2> Slices =
829       buildSlices(InputBinaries, Alignments, ExtractedObjects);
830 
831   for (auto &Slice : Slices) {
832     auto It = ReplacementSlices.find(Slice.getArchString());
833     if (It != ReplacementSlices.end()) {
834       Slice = It->second;
835       ReplacementSlices.erase(It); // only keep remaining replacing arch_types
836     }
837   }
838 
839   if (!ReplacementSlices.empty())
840     reportError("-replace " + ReplacementSlices.begin()->first() +
841                 " <file_name> specified but fat file: " +
842                 InputBinaries.front().getBinary()->getFileName() +
843                 " does not contain that architecture");
844 
845   checkUnusedAlignments(Slices, Alignments);
846   createUniversalBinary(Slices, OutputFileName);
847   exit(EXIT_SUCCESS);
848 }
849 
main(int argc,char ** argv)850 int main(int argc, char **argv) {
851   InitLLVM X(argc, argv);
852   Config C = parseLipoOptions(makeArrayRef(argv + 1, argc));
853   SmallVector<OwningBinary<Binary>, 1> InputBinaries =
854       readInputBinaries(C.InputFiles);
855 
856   switch (C.ActionToPerform) {
857   case LipoAction::VerifyArch:
858     verifyArch(InputBinaries, C.VerifyArchList);
859     break;
860   case LipoAction::PrintArchs:
861     printArchs(InputBinaries);
862     break;
863   case LipoAction::PrintInfo:
864     printInfo(InputBinaries);
865     break;
866   case LipoAction::ThinArch:
867     thinSlice(InputBinaries, C.ArchType, C.OutputFile);
868     break;
869   case LipoAction::ExtractArch:
870     extractSlice(InputBinaries, C.SegmentAlignments, C.ArchType, C.OutputFile);
871     break;
872   case LipoAction::CreateUniversal:
873     createUniversalBinary(InputBinaries, C.SegmentAlignments, C.OutputFile);
874     break;
875   case LipoAction::ReplaceArch:
876     replaceSlices(InputBinaries, C.SegmentAlignments, C.OutputFile,
877                   C.ReplacementFiles);
878     break;
879   }
880   return EXIT_SUCCESS;
881 }
882