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-exception 6 // 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 27 using namespace llvm; 28 using namespace llvm::objcopy; 29 using namespace llvm::objcopy::macho; 30 using namespace llvm::object; 31 32 using SectionPred = std::function<bool(const std::unique_ptr<Section> &Sec)>; 33 using LoadCommandPred = std::function<bool(const LoadCommand &LC)>; 34 35 #ifndef NDEBUG 36 static bool isLoadCommandWithPayloadString(const LoadCommand &LC) { 37 // TODO: Add support for LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB and 38 // LC_LAZY_LOAD_DYLIB 39 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 #endif 45 46 static 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 55 static 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 86 static 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 93 static 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 different 104 // meanings: 105 // 106 // --globalize-symbol: promote a symbol to global 107 // --keep-global-symbol: all symbols except for these should be made local 108 // 109 // If --globalize-symbol is specified for a given symbol, it will be 110 // global in the output file even if it is not included via 111 // --keep-global-symbol. Because of that, make sure to check 112 // --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 155 template <typename LCType> 156 static 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 std::copy(S.begin(), S.end(), LC.Payload.begin()); 165 } 166 167 static 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 std::copy(Path.begin(), Path.end(), LC.Payload.begin()); 176 return LC; 177 } 178 179 static 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 care 188 // about what it contains 189 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 name 205 // 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_DYLIB 252 // 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 must 287 // 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 308 static Error dumpSectionToFile(StringRef SecName, StringRef Filename, 309 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 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 E; 322 return Error::success(); 323 } 324 } 325 326 return createStringError(object_error::parse_failed, "section '%s' not found", 327 SecName.str().c_str()); 328 } 329 330 static 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 and 352 // 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 360 static Expected<Section &> findSection(StringRef SecName, Object &O) { 361 StringRef SegName; 362 std::tie(SegName, SecName) = SecName.split(","); 363 auto FoundSeg = 364 llvm::find_if(O.LoadCommands, [SegName](const LoadCommand &LC) { 365 return LC.getSegmentName() == SegName; 366 }); 367 if (FoundSeg == O.LoadCommands.end()) 368 return createStringError(errc::invalid_argument, 369 "could not find segment with name '%s'", 370 SegName.str().c_str()); 371 auto FoundSec = llvm::find_if(FoundSeg->Sections, 372 [SecName](const std::unique_ptr<Section> &Sec) { 373 return Sec->Sectname == SecName; 374 }); 375 if (FoundSec == FoundSeg->Sections.end()) 376 return createStringError(errc::invalid_argument, 377 "could not find section with name '%s'", 378 SecName.str().c_str()); 379 380 assert(FoundSec->get()->CanonicalName == (SegName + "," + SecName).str()); 381 return **FoundSec; 382 } 383 384 static Error updateSection(const NewSectionInfo &NewSection, Object &O) { 385 Expected<Section &> SecToUpdateOrErr = findSection(NewSection.SectionName, O); 386 387 if (!SecToUpdateOrErr) 388 return SecToUpdateOrErr.takeError(); 389 Section &Sec = *SecToUpdateOrErr; 390 391 if (NewSection.SectionData->getBufferSize() > Sec.Size) 392 return createStringError( 393 errc::invalid_argument, 394 "new section cannot be larger than previous section"); 395 Sec.Content = O.NewSectionsContents.save(NewSection.SectionData->getBuffer()); 396 Sec.Size = Sec.Content.size(); 397 return Error::success(); 398 } 399 400 // isValidMachOCannonicalName returns success if Name is a MachO cannonical name 401 // ("<segment>,<section>") and lengths of both segment and section names are 402 // valid. 403 static Error isValidMachOCannonicalName(StringRef Name) { 404 if (Name.count(',') != 1) 405 return createStringError(errc::invalid_argument, 406 "invalid section name '%s' (should be formatted " 407 "as '<segment name>,<section name>')", 408 Name.str().c_str()); 409 410 std::pair<StringRef, StringRef> Pair = Name.split(','); 411 if (Pair.first.size() > 16) 412 return createStringError(errc::invalid_argument, 413 "too long segment name: '%s'", 414 Pair.first.str().c_str()); 415 if (Pair.second.size() > 16) 416 return createStringError(errc::invalid_argument, 417 "too long section name: '%s'", 418 Pair.second.str().c_str()); 419 return Error::success(); 420 } 421 422 static Error handleArgs(const CommonConfig &Config, 423 const MachOConfig &MachOConfig, Object &Obj) { 424 // Dump sections before add/remove for compatibility with GNU objcopy. 425 for (StringRef Flag : Config.DumpSection) { 426 StringRef SectionName; 427 StringRef FileName; 428 std::tie(SectionName, FileName) = Flag.split('='); 429 if (Error E = dumpSectionToFile(SectionName, FileName, Obj)) 430 return E; 431 } 432 433 if (Error E = removeSections(Config, Obj)) 434 return E; 435 436 // Mark symbols to determine which symbols are still needed. 437 if (Config.StripAll) 438 markSymbols(Config, Obj); 439 440 updateAndRemoveSymbols(Config, MachOConfig, Obj); 441 442 if (Config.StripAll) 443 for (LoadCommand &LC : Obj.LoadCommands) 444 for (std::unique_ptr<Section> &Sec : LC.Sections) 445 Sec->Relocations.clear(); 446 447 for (const NewSectionInfo &NewSection : Config.AddSection) { 448 if (Error E = isValidMachOCannonicalName(NewSection.SectionName)) 449 return E; 450 if (Error E = addSection(NewSection, Obj)) 451 return E; 452 } 453 454 for (const NewSectionInfo &NewSection : Config.UpdateSection) { 455 if (Error E = isValidMachOCannonicalName(NewSection.SectionName)) 456 return E; 457 if (Error E = updateSection(NewSection, Obj)) 458 return E; 459 } 460 461 if (Error E = processLoadCommands(MachOConfig, Obj)) 462 return E; 463 464 return Error::success(); 465 } 466 467 Error objcopy::macho::executeObjcopyOnBinary(const CommonConfig &Config, 468 const MachOConfig &MachOConfig, 469 object::MachOObjectFile &In, 470 raw_ostream &Out) { 471 MachOReader Reader(In); 472 Expected<std::unique_ptr<Object>> O = Reader.create(); 473 if (!O) 474 return createFileError(Config.InputFilename, O.takeError()); 475 476 if (O->get()->Header.FileType == MachO::HeaderFileType::MH_PRELOAD) 477 return createStringError(std::errc::not_supported, 478 "%s: MH_PRELOAD files are not supported", 479 Config.InputFilename.str().c_str()); 480 481 if (Error E = handleArgs(Config, MachOConfig, **O)) 482 return createFileError(Config.InputFilename, std::move(E)); 483 484 // Page size used for alignment of segment sizes in Mach-O executables and 485 // dynamic libraries. 486 uint64_t PageSize; 487 switch (In.getArch()) { 488 case Triple::ArchType::arm: 489 case Triple::ArchType::aarch64: 490 case Triple::ArchType::aarch64_32: 491 PageSize = 16384; 492 break; 493 default: 494 PageSize = 4096; 495 } 496 497 MachOWriter Writer(**O, In.is64Bit(), In.isLittleEndian(), 498 sys::path::filename(Config.OutputFilename), PageSize, Out); 499 if (auto E = Writer.finalize()) 500 return E; 501 return Writer.write(); 502 } 503 504 Error objcopy::macho::executeObjcopyOnMachOUniversalBinary( 505 const MultiFormatConfig &Config, const MachOUniversalBinary &In, 506 raw_ostream &Out) { 507 SmallVector<OwningBinary<Binary>, 2> Binaries; 508 SmallVector<Slice, 2> Slices; 509 for (const auto &O : In.objects()) { 510 Expected<std::unique_ptr<Archive>> ArOrErr = O.getAsArchive(); 511 if (ArOrErr) { 512 Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr = 513 createNewArchiveMembers(Config, **ArOrErr); 514 if (!NewArchiveMembersOrErr) 515 return NewArchiveMembersOrErr.takeError(); 516 auto Kind = (*ArOrErr)->kind(); 517 if (Kind == object::Archive::K_BSD) 518 Kind = object::Archive::K_DARWIN; 519 Expected<std::unique_ptr<MemoryBuffer>> OutputBufferOrErr = 520 writeArchiveToBuffer( 521 *NewArchiveMembersOrErr, 522 (*ArOrErr)->hasSymbolTable() ? SymtabWritingMode::NormalSymtab 523 : SymtabWritingMode::NoSymtab, 524 Kind, Config.getCommonConfig().DeterministicArchives, 525 (*ArOrErr)->isThin()); 526 if (!OutputBufferOrErr) 527 return OutputBufferOrErr.takeError(); 528 Expected<std::unique_ptr<Binary>> BinaryOrErr = 529 object::createBinary(**OutputBufferOrErr); 530 if (!BinaryOrErr) 531 return BinaryOrErr.takeError(); 532 Binaries.emplace_back(std::move(*BinaryOrErr), 533 std::move(*OutputBufferOrErr)); 534 Slices.emplace_back(*cast<Archive>(Binaries.back().getBinary()), 535 O.getCPUType(), O.getCPUSubType(), 536 O.getArchFlagName(), O.getAlign()); 537 continue; 538 } 539 // The methods getAsArchive, getAsObjectFile, getAsIRObject of the class 540 // ObjectForArch return an Error in case of the type mismatch. We need to 541 // check each in turn to see what kind of slice this is, so ignore errors 542 // produced along the way. 543 consumeError(ArOrErr.takeError()); 544 545 Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = O.getAsObjectFile(); 546 if (!ObjOrErr) { 547 consumeError(ObjOrErr.takeError()); 548 return createStringError( 549 std::errc::invalid_argument, 550 "slice for '%s' of the universal Mach-O binary " 551 "'%s' is not a Mach-O object or an archive", 552 O.getArchFlagName().c_str(), 553 Config.getCommonConfig().InputFilename.str().c_str()); 554 } 555 std::string ArchFlagName = O.getArchFlagName(); 556 557 SmallVector<char, 0> Buffer; 558 raw_svector_ostream MemStream(Buffer); 559 560 Expected<const MachOConfig &> MachO = Config.getMachOConfig(); 561 if (!MachO) 562 return MachO.takeError(); 563 564 if (Error E = executeObjcopyOnBinary(Config.getCommonConfig(), *MachO, 565 **ObjOrErr, MemStream)) 566 return E; 567 568 auto MB = std::make_unique<SmallVectorMemoryBuffer>( 569 std::move(Buffer), ArchFlagName, /*RequiresNullTerminator=*/false); 570 Expected<std::unique_ptr<Binary>> BinaryOrErr = object::createBinary(*MB); 571 if (!BinaryOrErr) 572 return BinaryOrErr.takeError(); 573 Binaries.emplace_back(std::move(*BinaryOrErr), std::move(MB)); 574 Slices.emplace_back(*cast<MachOObjectFile>(Binaries.back().getBinary()), 575 O.getAlign()); 576 } 577 578 if (Error Err = writeUniversalBinaryToStream(Slices, Out)) 579 return Err; 580 581 return Error::success(); 582 } 583