brintos

brintos / llvm-project-archived public Read only

0
0
Text · 28.0 KiB · 3e1d416 Raw
773 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// A utility for creating / splitting / inspecting universal binaries.10//11//===----------------------------------------------------------------------===//12 13#include "llvm/ADT/STLExtras.h"14#include "llvm/BinaryFormat/MachO.h"15#include "llvm/IR/LLVMContext.h"16#include "llvm/IR/Module.h"17#include "llvm/Object/Archive.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/LLVMDriver.h"30#include "llvm/Support/TargetSelect.h"31#include "llvm/Support/WithColor.h"32#include "llvm/TargetParser/Triple.h"33#include "llvm/TextAPI/Architecture.h"34#include <optional>35 36using namespace llvm;37using namespace llvm::object;38 39static const StringRef ToolName = "llvm-lipo";40 41[[noreturn]] static void reportError(Twine Message) {42  WithColor::error(errs(), ToolName) << Message << "\n";43  errs().flush();44  exit(EXIT_FAILURE);45}46 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 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 66namespace {67enum LipoID {68  LIPO_INVALID = 0, // This is not an option ID.69#define OPTION(...) LLVM_MAKE_OPT_ID_WITH_ID_PREFIX(LIPO_, __VA_ARGS__),70#include "LipoOpts.inc"71#undef OPTION72};73 74namespace lipo {75#define OPTTABLE_STR_TABLE_CODE76#include "LipoOpts.inc"77#undef OPTTABLE_STR_TABLE_CODE78 79#define OPTTABLE_PREFIXES_TABLE_CODE80#include "LipoOpts.inc"81#undef OPTTABLE_PREFIXES_TABLE_CODE82 83using namespace llvm::opt;84static constexpr opt::OptTable::Info LipoInfoTable[] = {85#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO_WITH_ID_PREFIX(LIPO_, __VA_ARGS__),86#include "LipoOpts.inc"87#undef OPTION88};89} // namespace lipo90 91class LipoOptTable : public opt::GenericOptTable {92public:93  LipoOptTable()94      : opt::GenericOptTable(lipo::OptionStrTable, lipo::OptionPrefixesTable,95                             lipo::LipoInfoTable) {}96};97 98enum class LipoAction {99  PrintArchs,100  PrintInfo,101  VerifyArch,102  ThinArch,103  ExtractArch,104  CreateUniversal,105  ReplaceArch,106};107 108struct InputFile {109  std::optional<StringRef> ArchType;110  StringRef FileName;111};112 113struct Config {114  SmallVector<InputFile, 1> InputFiles;115  SmallVector<std::string, 1> VerifyArchList;116  SmallVector<InputFile, 1> ReplacementFiles;117  StringMap<const uint32_t> SegmentAlignments;118  std::string ArchType;119  std::string OutputFile;120  LipoAction ActionToPerform;121  bool UseFat64;122};123 124static Slice createSliceFromArchive(LLVMContext &LLVMCtx, const Archive &A) {125  Expected<Slice> ArchiveOrSlice = Slice::create(A, &LLVMCtx);126  if (!ArchiveOrSlice)127    reportError(A.getFileName(), ArchiveOrSlice.takeError());128  return *ArchiveOrSlice;129}130 131static Slice createSliceFromIR(const IRObjectFile &IRO, unsigned Align) {132  Expected<Slice> IROrErr = Slice::create(IRO, Align);133  if (!IROrErr)134    reportError(IRO.getFileName(), IROrErr.takeError());135  return *IROrErr;136}137 138} // end namespace139 140static void validateArchitectureName(StringRef ArchitectureName) {141  if (!MachOObjectFile::isValidArch(ArchitectureName)) {142    std::string Buf;143    raw_string_ostream OS(Buf);144    OS << "Invalid architecture: " << ArchitectureName145       << "\nValid architecture names are:";146    for (auto arch : MachOObjectFile::getValidArchs())147      OS << " " << arch;148    reportError(Buf);149  }150}151 152static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {153  Config C;154  LipoOptTable T;155  unsigned MissingArgumentIndex, MissingArgumentCount;156  opt::InputArgList InputArgs =157      T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);158 159  if (MissingArgumentCount)160    reportError("missing argument to " +161                StringRef(InputArgs.getArgString(MissingArgumentIndex)) +162                " option");163 164  if (InputArgs.size() == 0) {165    // printHelp does not accept Twine.166    T.printHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo");167    exit(EXIT_FAILURE);168  }169 170  if (InputArgs.hasArg(LIPO_help)) {171    // printHelp does not accept Twine.172    T.printHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo");173    exit(EXIT_SUCCESS);174  }175 176  if (InputArgs.hasArg(LIPO_version)) {177    outs() << ToolName + "\n";178    cl::PrintVersionMessage();179    exit(EXIT_SUCCESS);180  }181 182  for (auto *Arg : InputArgs.filtered(LIPO_UNKNOWN))183    reportError("unknown argument '" + Arg->getAsString(InputArgs) + "'");184 185  for (auto *Arg : InputArgs.filtered(LIPO_INPUT))186    C.InputFiles.push_back({std::nullopt, Arg->getValue()});187  for (auto *Arg : InputArgs.filtered(LIPO_arch)) {188    validateArchitectureName(Arg->getValue(0));189    assert(Arg->getValue(1) && "file_name is missing");190    C.InputFiles.push_back({StringRef(Arg->getValue(0)), Arg->getValue(1)});191  }192 193  if (C.InputFiles.empty())194    reportError("at least one input file should be specified");195 196  if (InputArgs.hasArg(LIPO_output))197    C.OutputFile = std::string(InputArgs.getLastArgValue(LIPO_output));198 199  for (auto *Segalign : InputArgs.filtered(LIPO_segalign)) {200    if (!Segalign->getValue(1))201      reportError("segalign is missing an argument: expects -segalign "202                  "arch_type alignment_value");203 204    validateArchitectureName(Segalign->getValue(0));205 206    uint32_t AlignmentValue;207    if (!to_integer<uint32_t>(Segalign->getValue(1), AlignmentValue, 16))208      reportError("argument to -segalign <arch_type> " +209                  Twine(Segalign->getValue(1)) +210                  " (hex) is not a proper hexadecimal number");211    if (!isPowerOf2_32(AlignmentValue))212      reportError("argument to -segalign <arch_type> " +213                  Twine(Segalign->getValue(1)) +214                  " (hex) must be a non-zero power of two");215    if (Log2_32(AlignmentValue) > MachOUniversalBinary::MaxSectionAlignment)216      reportError(217          "argument to -segalign <arch_type> " + Twine(Segalign->getValue(1)) +218          " (hex) must be less than or equal to the maximum section align 2^" +219          Twine(MachOUniversalBinary::MaxSectionAlignment));220    auto Entry = C.SegmentAlignments.try_emplace(Segalign->getValue(0),221                                                 Log2_32(AlignmentValue));222    if (!Entry.second)223      reportError("-segalign " + Twine(Segalign->getValue(0)) +224                  " <alignment_value> specified multiple times: " +225                  Twine(1 << Entry.first->second) + ", " +226                  Twine(AlignmentValue));227  }228 229  C.UseFat64 = InputArgs.hasArg(LIPO_fat64);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 replace235  // multiple replace flags may be specified, as long as they are not mixed with236  // other action flags237  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(Buf);248  }249 250  switch (ActionArgs[0]->getOption().getID()) {251  case LIPO_verify_arch:252    llvm::append_range(C.VerifyArchList,253                       InputArgs.getAllArgValues(LIPO_verify_arch));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 318static SmallVector<OwningBinary<Binary>, 1>319readInputBinaries(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 to340      // 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 void353verifyArch(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 382static 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 and388      // 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  if (const auto *A = dyn_cast<Archive>(Binary)) {429    OS << createSliceFromArchive(LLVMCtx, *A).getArchString() << "\n";430    return;431  }432 433  // This should be always the case, as this is tested in readInputBinaries434  const auto *IR = cast<IRObjectFile>(Binary);435  Expected<Slice> SliceOrErr = createSliceFromIR(*IR, 0);436  if (!SliceOrErr)437    reportError(IR->getFileName(), SliceOrErr.takeError());438 439  OS << SliceOrErr->getArchString() << " \n";440}441 442[[noreturn]] static void443printArchs(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries) {444  assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");445  printBinaryArchs(LLVMCtx, InputBinaries.front().getBinary(), outs());446  exit(EXIT_SUCCESS);447}448 449[[noreturn]] static void450printInfo(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries) {451  // Group universal and thin files together for compatibility with cctools lipo452  for (auto &IB : InputBinaries) {453    const Binary *Binary = IB.getBinary();454    if (Binary->isMachOUniversalBinary()) {455      outs() << "Architectures in the fat file: " << Binary->getFileName()456             << " are: ";457      printBinaryArchs(LLVMCtx, Binary, outs());458    }459  }460  for (auto &IB : InputBinaries) {461    const Binary *Binary = IB.getBinary();462    if (!Binary->isMachOUniversalBinary()) {463      assert((Binary->isMachO() || Binary->isArchive()) &&464             "expected MachO binary");465      outs() << "Non-fat file: " << Binary->getFileName()466             << " is architecture: ";467      printBinaryArchs(LLVMCtx, Binary, outs());468    }469  }470  exit(EXIT_SUCCESS);471}472 473[[noreturn]] static void thinSlice(LLVMContext &LLVMCtx,474                                   ArrayRef<OwningBinary<Binary>> InputBinaries,475                                   StringRef ArchType,476                                   StringRef OutputFileName) {477  assert(!ArchType.empty() && "The architecture type should be non-empty");478  assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");479  assert(!OutputFileName.empty() && "Thin expects a single output file");480 481  if (InputBinaries.front().getBinary()->isMachO()) {482    reportError("input file " +483                InputBinaries.front().getBinary()->getFileName() +484                " must be a fat file when the -thin option is specified");485    exit(EXIT_FAILURE);486  }487 488  auto *UO = cast<MachOUniversalBinary>(InputBinaries.front().getBinary());489  Expected<std::unique_ptr<MachOObjectFile>> Obj =490      UO->getMachOObjectForArch(ArchType);491  Expected<std::unique_ptr<IRObjectFile>> IRObj =492      UO->getIRObjectForArch(ArchType, LLVMCtx);493  Expected<std::unique_ptr<Archive>> Ar = UO->getArchiveForArch(ArchType);494  if (!Obj && !IRObj && !Ar)495    reportError("fat input file " + UO->getFileName() +496                " does not contain the specified architecture " + ArchType +497                " to thin it to");498  Binary *B;499  // Order here is important, because both Obj and IRObj will be valid with a500  // binary that has embedded bitcode.501  if (Obj)502    B = Obj->get();503  else if (IRObj)504    B = IRObj->get();505  else506    B = Ar->get();507 508  Expected<std::unique_ptr<FileOutputBuffer>> OutFileOrError =509      FileOutputBuffer::create(OutputFileName,510                               B->getMemoryBufferRef().getBufferSize(),511                               sys::fs::can_execute(UO->getFileName())512                                   ? FileOutputBuffer::F_executable513                                   : 0);514  if (!OutFileOrError)515    reportError(OutputFileName, OutFileOrError.takeError());516  std::copy(B->getMemoryBufferRef().getBufferStart(),517            B->getMemoryBufferRef().getBufferEnd(),518            OutFileOrError.get()->getBufferStart());519  if (Error E = OutFileOrError.get()->commit())520    reportError(OutputFileName, std::move(E));521  exit(EXIT_SUCCESS);522}523 524static void checkArchDuplicates(ArrayRef<Slice> Slices) {525  DenseMap<uint64_t, const Binary *> CPUIds;526  for (const auto &S : Slices) {527    auto Entry = CPUIds.try_emplace(S.getCPUID(), S.getBinary());528    if (!Entry.second)529      reportError(Entry.first->second->getFileName() + " and " +530                  S.getBinary()->getFileName() +531                  " have the same architecture " + S.getArchString() +532                  " and therefore cannot be in the same universal binary");533  }534}535 536template <typename Range>537static void updateAlignments(Range &Slices,538                             const StringMap<const uint32_t> &Alignments) {539  for (auto &Slice : Slices) {540    auto Alignment = Alignments.find(Slice.getArchString());541    if (Alignment != Alignments.end())542      Slice.setP2Alignment(Alignment->second);543  }544}545 546static void checkUnusedAlignments(ArrayRef<Slice> Slices,547                                  const StringMap<const uint32_t> &Alignments) {548  auto HasArch = [&](StringRef Arch) {549    return llvm::any_of(Slices,550                        [Arch](Slice S) { return S.getArchString() == Arch; });551  };552  for (StringRef Arch : Alignments.keys())553    if (!HasArch(Arch))554      reportError("-segalign " + Arch +555                  " <value> specified but resulting fat file does not contain "556                  "that architecture ");557}558 559// Updates vector ExtractedObjects with the MachOObjectFiles extracted from560// Universal Binary files to transfer ownership.561static SmallVector<Slice, 2>562buildSlices(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries,563            const StringMap<const uint32_t> &Alignments,564            SmallVectorImpl<std::unique_ptr<SymbolicFile>> &ExtractedObjects) {565  SmallVector<Slice, 2> Slices;566  for (auto &IB : InputBinaries) {567    const Binary *InputBinary = IB.getBinary();568    if (auto UO = dyn_cast<MachOUniversalBinary>(InputBinary)) {569      for (const auto &O : UO->objects()) {570        // Order here is important, because both MachOObjectFile and571        // IRObjectFile can be created with a binary that has embedded bitcode.572        Expected<std::unique_ptr<MachOObjectFile>> BinaryOrError =573            O.getAsObjectFile();574        if (BinaryOrError) {575          Slices.emplace_back(*(BinaryOrError.get()), O.getAlign());576          ExtractedObjects.push_back(std::move(BinaryOrError.get()));577          continue;578        }579        Expected<std::unique_ptr<IRObjectFile>> IROrError =580            O.getAsIRObject(LLVMCtx);581        if (IROrError) {582          consumeError(BinaryOrError.takeError());583          Slice S = createSliceFromIR(**IROrError, O.getAlign());584          ExtractedObjects.emplace_back(std::move(IROrError.get()));585          Slices.emplace_back(std::move(S));586          continue;587        }588        reportError(InputBinary->getFileName(), BinaryOrError.takeError());589      }590    } else if (const auto *O = dyn_cast<MachOObjectFile>(InputBinary)) {591      Slices.emplace_back(*O);592    } else if (const auto *A = dyn_cast<Archive>(InputBinary)) {593      Slices.push_back(createSliceFromArchive(LLVMCtx, *A));594    } else if (const auto *IRO = dyn_cast<IRObjectFile>(InputBinary)) {595      // Original Apple's lipo set the alignment to 0596      Expected<Slice> SliceOrErr = Slice::create(*IRO, 0);597      if (!SliceOrErr) {598        reportError(InputBinary->getFileName(), SliceOrErr.takeError());599        continue;600      }601      Slices.emplace_back(std::move(SliceOrErr.get()));602    } else {603      llvm_unreachable("Unexpected binary format");604    }605  }606  updateAlignments(Slices, Alignments);607  return Slices;608}609 610[[noreturn]] static void611createUniversalBinary(LLVMContext &LLVMCtx,612                      ArrayRef<OwningBinary<Binary>> InputBinaries,613                      const StringMap<const uint32_t> &Alignments,614                      StringRef OutputFileName, FatHeaderType HeaderType) {615  assert(InputBinaries.size() >= 1 && "Incorrect number of input binaries");616  assert(!OutputFileName.empty() && "Create expects a single output file");617 618  SmallVector<std::unique_ptr<SymbolicFile>, 1> ExtractedObjects;619  SmallVector<Slice, 1> Slices =620      buildSlices(LLVMCtx, InputBinaries, Alignments, ExtractedObjects);621  checkArchDuplicates(Slices);622  checkUnusedAlignments(Slices, Alignments);623 624  llvm::stable_sort(Slices);625  if (Error E = writeUniversalBinary(Slices, OutputFileName, HeaderType))626    reportError(std::move(E));627 628  exit(EXIT_SUCCESS);629}630 631[[noreturn]] static void632extractSlice(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries,633             const StringMap<const uint32_t> &Alignments, StringRef ArchType,634             StringRef OutputFileName) {635  assert(!ArchType.empty() &&636         "The architecture type should be non-empty");637  assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");638  assert(!OutputFileName.empty() && "Thin expects a single output file");639 640  if (InputBinaries.front().getBinary()->isMachO()) {641    reportError("input file " +642                InputBinaries.front().getBinary()->getFileName() +643                " must be a fat file when the -extract option is specified");644  }645 646  SmallVector<std::unique_ptr<SymbolicFile>, 2> ExtractedObjects;647  SmallVector<Slice, 2> Slices =648      buildSlices(LLVMCtx, InputBinaries, Alignments, ExtractedObjects);649  erase_if(Slices, [ArchType](const Slice &S) {650    return ArchType != S.getArchString();651  });652 653  if (Slices.empty())654    reportError(655        "fat input file " + InputBinaries.front().getBinary()->getFileName() +656        " does not contain the specified architecture " + ArchType);657 658  llvm::stable_sort(Slices);659  if (Error E = writeUniversalBinary(Slices, OutputFileName))660    reportError(std::move(E));661  exit(EXIT_SUCCESS);662}663 664static StringMap<Slice>665buildReplacementSlices(ArrayRef<OwningBinary<Binary>> ReplacementBinaries,666                       const StringMap<const uint32_t> &Alignments) {667  StringMap<Slice> Slices;668  // populates StringMap of slices to replace with; error checks for mismatched669  // replace flag args, fat files, and duplicate arch_types670  for (const auto &OB : ReplacementBinaries) {671    const Binary *ReplacementBinary = OB.getBinary();672    auto O = dyn_cast<MachOObjectFile>(ReplacementBinary);673    if (!O)674      reportError("replacement file: " + ReplacementBinary->getFileName() +675                  " is a fat file (must be a thin file)");676    Slice S(*O);677    auto Entry = Slices.try_emplace(S.getArchString(), S);678    if (!Entry.second)679      reportError("-replace " + S.getArchString() +680                  " <file_name> specified multiple times: " +681                  Entry.first->second.getBinary()->getFileName() + ", " +682                  O->getFileName());683  }684  auto SlicesMapRange = map_range(685      Slices, [](StringMapEntry<Slice> &E) -> Slice & { return E.getValue(); });686  updateAlignments(SlicesMapRange, Alignments);687  return Slices;688}689 690[[noreturn]] static void691replaceSlices(LLVMContext &LLVMCtx,692              ArrayRef<OwningBinary<Binary>> InputBinaries,693              const StringMap<const uint32_t> &Alignments,694              StringRef OutputFileName, ArrayRef<InputFile> ReplacementFiles) {695  assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");696  assert(!OutputFileName.empty() && "Replace expects a single output file");697 698  if (InputBinaries.front().getBinary()->isMachO())699    reportError("input file " +700                InputBinaries.front().getBinary()->getFileName() +701                " must be a fat file when the -replace option is specified");702 703  SmallVector<OwningBinary<Binary>, 1> ReplacementBinaries =704      readInputBinaries(LLVMCtx, ReplacementFiles);705 706  StringMap<Slice> ReplacementSlices =707      buildReplacementSlices(ReplacementBinaries, Alignments);708  SmallVector<std::unique_ptr<SymbolicFile>, 2> ExtractedObjects;709  SmallVector<Slice, 2> Slices =710      buildSlices(LLVMCtx, InputBinaries, Alignments, ExtractedObjects);711 712  for (auto &Slice : Slices) {713    auto It = ReplacementSlices.find(Slice.getArchString());714    if (It != ReplacementSlices.end()) {715      Slice = It->second;716      ReplacementSlices.erase(It); // only keep remaining replacing arch_types717    }718  }719 720  if (!ReplacementSlices.empty())721    reportError("-replace " + ReplacementSlices.begin()->first() +722                " <file_name> specified but fat file: " +723                InputBinaries.front().getBinary()->getFileName() +724                " does not contain that architecture");725 726  checkUnusedAlignments(Slices, Alignments);727 728  llvm::stable_sort(Slices);729  if (Error E = writeUniversalBinary(Slices, OutputFileName))730    reportError(std::move(E));731  exit(EXIT_SUCCESS);732}733 734int llvm_lipo_main(int argc, char **argv, const llvm::ToolContext &) {735  llvm::InitializeAllTargetInfos();736  llvm::InitializeAllTargetMCs();737  llvm::InitializeAllAsmParsers();738 739  Config C = parseLipoOptions(ArrayRef(argv + 1, argc - 1));740  LLVMContext LLVMCtx;741  SmallVector<OwningBinary<Binary>, 1> InputBinaries =742      readInputBinaries(LLVMCtx, C.InputFiles);743 744  switch (C.ActionToPerform) {745  case LipoAction::VerifyArch:746    verifyArch(InputBinaries, C.VerifyArchList);747    break;748  case LipoAction::PrintArchs:749    printArchs(LLVMCtx, InputBinaries);750    break;751  case LipoAction::PrintInfo:752    printInfo(LLVMCtx, InputBinaries);753    break;754  case LipoAction::ThinArch:755    thinSlice(LLVMCtx, InputBinaries, C.ArchType, C.OutputFile);756    break;757  case LipoAction::ExtractArch:758    extractSlice(LLVMCtx, InputBinaries, C.SegmentAlignments, C.ArchType,759                 C.OutputFile);760    break;761  case LipoAction::CreateUniversal:762    createUniversalBinary(763        LLVMCtx, InputBinaries, C.SegmentAlignments, C.OutputFile,764        C.UseFat64 ? FatHeaderType::Fat64Header : FatHeaderType::FatHeader);765    break;766  case LipoAction::ReplaceArch:767    replaceSlices(LLVMCtx, InputBinaries, C.SegmentAlignments, C.OutputFile,768                  C.ReplacementFiles);769    break;770  }771  return EXIT_SUCCESS;772}773