brintos

brintos / llvm-project-archived public Read only

0
0
Text · 22.6 KiB · e9a371f Raw
602 lines · cpp
1//===- MachOObjcopy.cpp -----------------------------------------*- C++ -*-===//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#include "llvm/ObjCopy/MachO/MachOObjcopy.h"10#include "Archive.h"11#include "MachOReader.h"12#include "MachOWriter.h"13#include "llvm/ADT/DenseSet.h"14#include "llvm/ObjCopy/CommonConfig.h"15#include "llvm/ObjCopy/MachO/MachOConfig.h"16#include "llvm/ObjCopy/MultiFormatConfig.h"17#include "llvm/ObjCopy/ObjCopy.h"18#include "llvm/Object/ArchiveWriter.h"19#include "llvm/Object/MachOUniversal.h"20#include "llvm/Object/MachOUniversalWriter.h"21#include "llvm/Support/Errc.h"22#include "llvm/Support/Error.h"23#include "llvm/Support/FileOutputBuffer.h"24#include "llvm/Support/Path.h"25#include "llvm/Support/SmallVectorMemoryBuffer.h"26 27using namespace llvm;28using namespace llvm::objcopy;29using namespace llvm::objcopy::macho;30using namespace llvm::object;31 32using SectionPred = std::function<bool(const std::unique_ptr<Section> &Sec)>;33using LoadCommandPred = std::function<bool(const LoadCommand &LC)>;34 35#ifndef NDEBUG36static bool isLoadCommandWithPayloadString(const LoadCommand &LC) {37  // TODO: Add support for LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB and38  // LC_LAZY_LOAD_DYLIB39  return LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH ||40         LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_ID_DYLIB ||41         LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_DYLIB ||42         LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_WEAK_DYLIB;43}44#endif45 46static StringRef getPayloadString(const LoadCommand &LC) {47  assert(isLoadCommandWithPayloadString(LC) &&48         "unsupported load command encountered");49 50  return StringRef(reinterpret_cast<const char *>(LC.Payload.data()),51                   LC.Payload.size())52      .rtrim('\0');53}54 55static Error removeSections(const CommonConfig &Config, Object &Obj) {56  SectionPred RemovePred = [](const std::unique_ptr<Section> &) {57    return false;58  };59 60  if (!Config.ToRemove.empty()) {61    RemovePred = [&Config, RemovePred](const std::unique_ptr<Section> &Sec) {62      return Config.ToRemove.matches(Sec->CanonicalName);63    };64  }65 66  if (Config.StripAll || Config.StripDebug) {67    // Remove all debug sections.68    RemovePred = [RemovePred](const std::unique_ptr<Section> &Sec) {69      if (Sec->Segname == "__DWARF")70        return true;71 72      return RemovePred(Sec);73    };74  }75 76  if (!Config.OnlySection.empty()) {77    // Overwrite RemovePred because --only-section takes priority.78    RemovePred = [&Config](const std::unique_ptr<Section> &Sec) {79      return !Config.OnlySection.matches(Sec->CanonicalName);80    };81  }82 83  return Obj.removeSections(RemovePred);84}85 86static void markSymbols(const CommonConfig &, Object &Obj) {87  // Symbols referenced from the indirect symbol table must not be removed.88  for (IndirectSymbolEntry &ISE : Obj.IndirectSymTable.Symbols)89    if (ISE.Symbol)90      (*ISE.Symbol)->Referenced = true;91}92 93static void updateAndRemoveSymbols(const CommonConfig &Config,94                                   const MachOConfig &MachOConfig,95                                   Object &Obj) {96  Obj.SymTable.updateSymbols([&](SymbolEntry &Sym) {97    if (Config.SymbolsToSkip.matches(Sym.Name))98      return;99 100    if (!Sym.isUndefinedSymbol() && Config.SymbolsToLocalize.matches(Sym.Name))101      Sym.n_type &= ~MachO::N_EXT;102 103    // Note: these two globalize flags have very similar names but different104    // meanings:105    //106    // --globalize-symbol: promote a symbol to global107    // --keep-global-symbol: all symbols except for these should be made local108    //109    // If --globalize-symbol is specified for a given symbol, it will be110    // global in the output file even if it is not included via111    // --keep-global-symbol. Because of that, make sure to check112    // --globalize-symbol second.113    if (!Sym.isUndefinedSymbol() && !Config.SymbolsToKeepGlobal.empty() &&114        !Config.SymbolsToKeepGlobal.matches(Sym.Name))115      Sym.n_type &= ~MachO::N_EXT;116 117    if (!Sym.isUndefinedSymbol() && Config.SymbolsToGlobalize.matches(Sym.Name))118      Sym.n_type |= MachO::N_EXT;119 120    if (Sym.isExternalSymbol() && !Sym.isUndefinedSymbol() &&121        (Config.Weaken || Config.SymbolsToWeaken.matches(Sym.Name)))122      Sym.n_desc |= MachO::N_WEAK_DEF;123 124    auto I = Config.SymbolsToRename.find(Sym.Name);125    if (I != Config.SymbolsToRename.end())126      Sym.Name = std::string(I->getValue());127  });128 129  auto RemovePred = [&Config, &MachOConfig,130                     &Obj](const std::unique_ptr<SymbolEntry> &N) {131    if (N->Referenced)132      return false;133    if (MachOConfig.KeepUndefined && N->isUndefinedSymbol())134      return false;135    if (N->n_desc & MachO::REFERENCED_DYNAMICALLY)136      return false;137    if (Config.StripAll)138      return true;139    if (Config.DiscardMode == DiscardType::All && !(N->n_type & MachO::N_EXT))140      return true;141    // This behavior is consistent with cctools' strip.142    if (Config.StripDebug && (N->n_type & MachO::N_STAB))143      return true;144    // This behavior is consistent with cctools' strip.145    if (MachOConfig.StripSwiftSymbols &&146        (Obj.Header.Flags & MachO::MH_DYLDLINK) && Obj.SwiftVersion &&147        *Obj.SwiftVersion && N->isSwiftSymbol())148      return true;149    return false;150  };151 152  Obj.SymTable.removeSymbols(RemovePred);153}154 155template <typename LCType>156static void updateLoadCommandPayloadString(LoadCommand &LC, StringRef S) {157  assert(isLoadCommandWithPayloadString(LC) &&158         "unsupported load command encountered");159 160  uint32_t NewCmdsize = alignTo(sizeof(LCType) + S.size() + 1, 8);161 162  LC.MachOLoadCommand.load_command_data.cmdsize = NewCmdsize;163  LC.Payload.assign(NewCmdsize - sizeof(LCType), 0);164  llvm::copy(S, LC.Payload.begin());165}166 167static LoadCommand buildRPathLoadCommand(StringRef Path) {168  LoadCommand LC;169  MachO::rpath_command RPathLC;170  RPathLC.cmd = MachO::LC_RPATH;171  RPathLC.path = sizeof(MachO::rpath_command);172  RPathLC.cmdsize = alignTo(sizeof(MachO::rpath_command) + Path.size() + 1, 8);173  LC.MachOLoadCommand.rpath_command_data = RPathLC;174  LC.Payload.assign(RPathLC.cmdsize - sizeof(MachO::rpath_command), 0);175  llvm::copy(Path, LC.Payload.begin());176  return LC;177}178 179static Error processLoadCommands(const MachOConfig &MachOConfig, Object &Obj) {180  // Remove RPaths.181  DenseSet<StringRef> RPathsToRemove(MachOConfig.RPathsToRemove.begin(),182                                     MachOConfig.RPathsToRemove.end());183 184  LoadCommandPred RemovePred = [&RPathsToRemove,185                                &MachOConfig](const LoadCommand &LC) {186    if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH) {187      // When removing all RPaths we don't need to care188      // about what it contains189      if (MachOConfig.RemoveAllRpaths)190        return true;191 192      StringRef RPath = getPayloadString(LC);193      if (RPathsToRemove.count(RPath)) {194        RPathsToRemove.erase(RPath);195        return true;196      }197    }198    return false;199  };200 201  if (Error E = Obj.removeLoadCommands(RemovePred))202    return E;203 204  // Emit an error if the Mach-O binary does not contain an rpath path name205  // specified in -delete_rpath.206  for (StringRef RPath : MachOConfig.RPathsToRemove) {207    if (RPathsToRemove.count(RPath))208      return createStringError(errc::invalid_argument,209                               "no LC_RPATH load command with path: %s",210                               RPath.str().c_str());211  }212 213  DenseSet<StringRef> RPaths;214 215  // Get all existing RPaths.216  for (LoadCommand &LC : Obj.LoadCommands) {217    if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH)218      RPaths.insert(getPayloadString(LC));219  }220 221  // Throw errors for invalid RPaths.222  for (const auto &OldNew : MachOConfig.RPathsToUpdate) {223    StringRef Old = OldNew.getFirst();224    StringRef New = OldNew.getSecond();225    if (!RPaths.contains(Old))226      return createStringError(errc::invalid_argument,227                               "no LC_RPATH load command with path: " + Old);228    if (RPaths.contains(New))229      return createStringError(errc::invalid_argument,230                               "rpath '" + New +231                                   "' would create a duplicate load command");232  }233 234  // Update load commands.235  for (LoadCommand &LC : Obj.LoadCommands) {236    switch (LC.MachOLoadCommand.load_command_data.cmd) {237    case MachO::LC_ID_DYLIB:238      if (MachOConfig.SharedLibId)239        updateLoadCommandPayloadString<MachO::dylib_command>(240            LC, *MachOConfig.SharedLibId);241      break;242 243    case MachO::LC_RPATH: {244      StringRef RPath = getPayloadString(LC);245      StringRef NewRPath = MachOConfig.RPathsToUpdate.lookup(RPath);246      if (!NewRPath.empty())247        updateLoadCommandPayloadString<MachO::rpath_command>(LC, NewRPath);248      break;249    }250 251    // TODO: Add LC_REEXPORT_DYLIB, LC_LAZY_LOAD_DYLIB, and LC_LOAD_UPWARD_DYLIB252    // here once llvm-objcopy supports them.253    case MachO::LC_LOAD_DYLIB:254    case MachO::LC_LOAD_WEAK_DYLIB:255      StringRef InstallName = getPayloadString(LC);256      StringRef NewInstallName =257          MachOConfig.InstallNamesToUpdate.lookup(InstallName);258      if (!NewInstallName.empty())259        updateLoadCommandPayloadString<MachO::dylib_command>(LC,260                                                             NewInstallName);261      break;262    }263  }264 265  // Add new RPaths.266  for (StringRef RPath : MachOConfig.RPathToAdd) {267    if (RPaths.contains(RPath))268      return createStringError(errc::invalid_argument,269                               "rpath '" + RPath +270                                   "' would create a duplicate load command");271    RPaths.insert(RPath);272    Obj.LoadCommands.push_back(buildRPathLoadCommand(RPath));273  }274 275  for (StringRef RPath : MachOConfig.RPathToPrepend) {276    if (RPaths.contains(RPath))277      return createStringError(errc::invalid_argument,278                               "rpath '" + RPath +279                                   "' would create a duplicate load command");280 281    RPaths.insert(RPath);282    Obj.LoadCommands.insert(Obj.LoadCommands.begin(),283                            buildRPathLoadCommand(RPath));284  }285 286  // Unlike appending rpaths, the indexes of subsequent load commands must287  // be recalculated after prepending one.288  if (!MachOConfig.RPathToPrepend.empty())289    Obj.updateLoadCommandIndexes();290 291  // Remove any empty segments if required.292  if (!MachOConfig.EmptySegmentsToRemove.empty()) {293    auto RemovePred = [&MachOConfig](const LoadCommand &LC) {294      if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_SEGMENT_64 ||295          LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_SEGMENT) {296        return LC.Sections.empty() &&297               MachOConfig.EmptySegmentsToRemove.contains(*LC.getSegmentName());298      }299      return false;300    };301    if (Error E = Obj.removeLoadCommands(RemovePred))302      return E;303  }304 305  return Error::success();306}307 308static Error dumpSectionToFile(StringRef SecName, StringRef Filename,309                               StringRef InputFilename, Object &Obj) {310  for (LoadCommand &LC : Obj.LoadCommands)311    for (const std::unique_ptr<Section> &Sec : LC.Sections) {312      if (Sec->CanonicalName == SecName) {313        Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =314            FileOutputBuffer::create(Filename, Sec->Content.size());315        if (!BufferOrErr)316          return createFileError(Filename, BufferOrErr.takeError());317        std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr);318        llvm::copy(Sec->Content, Buf->getBufferStart());319 320        if (Error E = Buf->commit())321          return createFileError(Filename, std::move(E));322        return Error::success();323      }324    }325 326  return createFileError(InputFilename, object_error::parse_failed,327                         "section '%s' not found", SecName.str().c_str());328}329 330static Error addSection(const NewSectionInfo &NewSection, Object &Obj) {331  std::pair<StringRef, StringRef> Pair = NewSection.SectionName.split(',');332  StringRef TargetSegName = Pair.first;333  Section Sec(TargetSegName, Pair.second);334  Sec.Content =335      Obj.NewSectionsContents.save(NewSection.SectionData->getBuffer());336  Sec.Size = Sec.Content.size();337 338  // Add the a section into an existing segment.339  for (LoadCommand &LC : Obj.LoadCommands) {340    std::optional<StringRef> SegName = LC.getSegmentName();341    if (SegName && SegName == TargetSegName) {342      uint64_t Addr = *LC.getSegmentVMAddr();343      for (const std::unique_ptr<Section> &S : LC.Sections)344        Addr = std::max(Addr, S->Addr + S->Size);345      LC.Sections.push_back(std::make_unique<Section>(Sec));346      LC.Sections.back()->Addr = Addr;347      return Error::success();348    }349  }350 351  // There's no segment named TargetSegName. Create a new load command and352  // Insert a new section into it.353  LoadCommand &NewSegment =354      Obj.addSegment(TargetSegName, alignTo(Sec.Size, 16384));355  NewSegment.Sections.push_back(std::make_unique<Section>(Sec));356  NewSegment.Sections.back()->Addr = *NewSegment.getSegmentVMAddr();357  return Error::success();358}359 360static Expected<Section &> findSection(StringRef SecName, Object &O) {361  StringRef SegName;362  std::tie(SegName, SecName) = SecName.split(",");363  // For compactness, intermediate object files (MH_OBJECT) contain364  // only one segment in which all sections are placed.365  // The static linker places each section in the named segment when building366  // the final product (any file that is not of type MH_OBJECT).367  //368  // Source:369  // https://math-atlas.sourceforge.net/devel/assembly/MachORuntime.pdf370  // page 57371  if (O.Header.FileType == MachO::HeaderFileType::MH_OBJECT) {372    for (const auto& LC : O.LoadCommands)373      for (const auto& Sec : LC.Sections)374        if (Sec->Segname == SegName && Sec->Sectname == SecName)375          return *Sec;376 377    StringRef ErrMsg = "could not find section with name '%s' in '%s' segment";378    return createStringError(errc::invalid_argument, ErrMsg.str().c_str(),379                             SecName.str().c_str(), SegName.str().c_str());380  }381  auto FoundSeg =382      llvm::find_if(O.LoadCommands, [SegName](const LoadCommand &LC) {383        return LC.getSegmentName() == SegName;384      });385  if (FoundSeg == O.LoadCommands.end())386    return createStringError(errc::invalid_argument,387                             "could not find segment with name '%s'",388                             SegName.str().c_str());389  auto FoundSec = llvm::find_if(FoundSeg->Sections,390                                [SecName](const std::unique_ptr<Section> &Sec) {391                                  return Sec->Sectname == SecName;392                                });393  if (FoundSec == FoundSeg->Sections.end())394    return createStringError(errc::invalid_argument,395                             "could not find section with name '%s'",396                             SecName.str().c_str());397 398  assert(FoundSec->get()->CanonicalName == (SegName + "," + SecName).str());399  return **FoundSec;400}401 402static Error updateSection(const NewSectionInfo &NewSection, Object &O) {403  Expected<Section &> SecToUpdateOrErr = findSection(NewSection.SectionName, O);404 405  if (!SecToUpdateOrErr)406    return SecToUpdateOrErr.takeError();407  Section &Sec = *SecToUpdateOrErr;408 409  if (NewSection.SectionData->getBufferSize() > Sec.Size)410    return createStringError(411        errc::invalid_argument,412        "new section cannot be larger than previous section");413  Sec.Content = O.NewSectionsContents.save(NewSection.SectionData->getBuffer());414  Sec.Size = Sec.Content.size();415  return Error::success();416}417 418// isValidMachOCannonicalName returns success if Name is a MachO cannonical name419// ("<segment>,<section>") and lengths of both segment and section names are420// valid.421static Error isValidMachOCannonicalName(StringRef Name) {422  if (Name.count(',') != 1)423    return createStringError(errc::invalid_argument,424                             "invalid section name '%s' (should be formatted "425                             "as '<segment name>,<section name>')",426                             Name.str().c_str());427 428  std::pair<StringRef, StringRef> Pair = Name.split(',');429  if (Pair.first.size() > 16)430    return createStringError(errc::invalid_argument,431                             "too long segment name: '%s'",432                             Pair.first.str().c_str());433  if (Pair.second.size() > 16)434    return createStringError(errc::invalid_argument,435                             "too long section name: '%s'",436                             Pair.second.str().c_str());437  return Error::success();438}439 440static Error handleArgs(const CommonConfig &Config,441                        const MachOConfig &MachOConfig, Object &Obj) {442  // Dump sections before add/remove for compatibility with GNU objcopy.443  for (StringRef Flag : Config.DumpSection) {444    StringRef SectionName;445    StringRef FileName;446    std::tie(SectionName, FileName) = Flag.split('=');447    if (Error E =448            dumpSectionToFile(SectionName, FileName, Config.InputFilename, Obj))449      return E;450  }451 452  if (Error E = removeSections(Config, Obj))453    return createFileError(Config.InputFilename, std::move(E));454 455  // Mark symbols to determine which symbols are still needed.456  if (Config.StripAll)457    markSymbols(Config, Obj);458 459  updateAndRemoveSymbols(Config, MachOConfig, Obj);460 461  if (Config.StripAll)462    for (LoadCommand &LC : Obj.LoadCommands)463      for (std::unique_ptr<Section> &Sec : LC.Sections)464        Sec->Relocations.clear();465 466  for (const NewSectionInfo &NewSection : Config.AddSection) {467    if (Error E = isValidMachOCannonicalName(NewSection.SectionName))468      return createFileError(Config.InputFilename, std::move(E));469    if (Error E = addSection(NewSection, Obj))470      return createFileError(Config.InputFilename, std::move(E));471  }472 473  for (const NewSectionInfo &NewSection : Config.UpdateSection) {474    if (Error E = isValidMachOCannonicalName(NewSection.SectionName))475      return createFileError(Config.InputFilename, std::move(E));476    if (Error E = updateSection(NewSection, Obj))477      return createFileError(Config.InputFilename, std::move(E));478  }479 480  if (Error E = processLoadCommands(MachOConfig, Obj))481    return createFileError(Config.InputFilename, std::move(E));482 483  return Error::success();484}485 486Error objcopy::macho::executeObjcopyOnBinary(const CommonConfig &Config,487                                             const MachOConfig &MachOConfig,488                                             object::MachOObjectFile &In,489                                             raw_ostream &Out) {490  MachOReader Reader(In);491  Expected<std::unique_ptr<Object>> O = Reader.create();492  if (!O)493    return createFileError(Config.InputFilename, O.takeError());494 495  if (O->get()->Header.FileType == MachO::HeaderFileType::MH_PRELOAD)496    return createStringError(std::errc::not_supported,497                             "%s: MH_PRELOAD files are not supported",498                             Config.InputFilename.str().c_str());499 500  if (Error E = handleArgs(Config, MachOConfig, **O))501    return E;502 503  // Page size used for alignment of segment sizes in Mach-O executables and504  // dynamic libraries.505  uint64_t PageSize;506  switch (In.getArch()) {507  case Triple::ArchType::arm:508  case Triple::ArchType::aarch64:509  case Triple::ArchType::aarch64_32:510    PageSize = 16384;511    break;512  default:513    PageSize = 4096;514  }515 516  MachOWriter Writer(**O, In.is64Bit(), In.isLittleEndian(),517                     sys::path::filename(Config.OutputFilename), PageSize, Out);518  if (auto E = Writer.finalize())519    return E;520  return Writer.write();521}522 523Error objcopy::macho::executeObjcopyOnMachOUniversalBinary(524    const MultiFormatConfig &Config, const MachOUniversalBinary &In,525    raw_ostream &Out) {526  SmallVector<OwningBinary<Binary>, 2> Binaries;527  SmallVector<Slice, 2> Slices;528  for (const auto &O : In.objects()) {529    Expected<std::unique_ptr<Archive>> ArOrErr = O.getAsArchive();530    if (ArOrErr) {531      Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr =532          createNewArchiveMembers(Config, **ArOrErr);533      if (!NewArchiveMembersOrErr)534        return NewArchiveMembersOrErr.takeError();535      auto Kind = (*ArOrErr)->kind();536      if (Kind == object::Archive::K_BSD)537        Kind = object::Archive::K_DARWIN;538      Expected<std::unique_ptr<MemoryBuffer>> OutputBufferOrErr =539          writeArchiveToBuffer(540              *NewArchiveMembersOrErr,541              (*ArOrErr)->hasSymbolTable() ? SymtabWritingMode::NormalSymtab542                                           : SymtabWritingMode::NoSymtab,543              Kind, Config.getCommonConfig().DeterministicArchives,544              (*ArOrErr)->isThin());545      if (!OutputBufferOrErr)546        return OutputBufferOrErr.takeError();547      Expected<std::unique_ptr<Binary>> BinaryOrErr =548          object::createBinary(**OutputBufferOrErr);549      if (!BinaryOrErr)550        return BinaryOrErr.takeError();551      Binaries.emplace_back(std::move(*BinaryOrErr),552                            std::move(*OutputBufferOrErr));553      Slices.emplace_back(*cast<Archive>(Binaries.back().getBinary()),554                          O.getCPUType(), O.getCPUSubType(),555                          O.getArchFlagName(), O.getAlign());556      continue;557    }558    // The methods getAsArchive, getAsObjectFile, getAsIRObject of the class559    // ObjectForArch return an Error in case of the type mismatch. We need to560    // check each in turn to see what kind of slice this is, so ignore errors561    // produced along the way.562    consumeError(ArOrErr.takeError());563 564    Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = O.getAsObjectFile();565    if (!ObjOrErr) {566      consumeError(ObjOrErr.takeError());567      return createStringError(568          std::errc::invalid_argument,569          "slice for '%s' of the universal Mach-O binary "570          "'%s' is not a Mach-O object or an archive",571          O.getArchFlagName().c_str(),572          Config.getCommonConfig().InputFilename.str().c_str());573    }574    std::string ArchFlagName = O.getArchFlagName();575 576    SmallVector<char, 0> Buffer;577    raw_svector_ostream MemStream(Buffer);578 579    Expected<const MachOConfig &> MachO = Config.getMachOConfig();580    if (!MachO)581      return MachO.takeError();582 583    if (Error E = executeObjcopyOnBinary(Config.getCommonConfig(), *MachO,584                                         **ObjOrErr, MemStream))585      return E;586 587    auto MB = std::make_unique<SmallVectorMemoryBuffer>(588        std::move(Buffer), ArchFlagName, /*RequiresNullTerminator=*/false);589    Expected<std::unique_ptr<Binary>> BinaryOrErr = object::createBinary(*MB);590    if (!BinaryOrErr)591      return BinaryOrErr.takeError();592    Binaries.emplace_back(std::move(*BinaryOrErr), std::move(MB));593    Slices.emplace_back(*cast<MachOObjectFile>(Binaries.back().getBinary()),594                        O.getAlign());595  }596 597  if (Error Err = writeUniversalBinaryToStream(Slices, Out))598    return Err;599 600  return Error::success();601}602