1//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp ---------===// 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/// 10/// \file For mach-o object files, this implementation converts from 11/// mach-o on-disk binary format to in-memory normalized mach-o. 12/// 13/// +---------------+ 14/// | binary mach-o | 15/// +---------------+ 16/// | 17/// | 18/// v 19/// +------------+ 20/// | normalized | 21/// +------------+ 22 23#include "ArchHandler.h" 24#include "MachONormalizedFile.h" 25#include "MachONormalizedFileBinaryUtils.h" 26#include "lld/Common/LLVM.h" 27#include "lld/Core/Error.h" 28#include "lld/Core/SharedLibraryFile.h" 29#include "llvm/ADT/STLExtras.h" 30#include "llvm/ADT/SmallString.h" 31#include "llvm/ADT/StringRef.h" 32#include "llvm/ADT/StringSwitch.h" 33#include "llvm/ADT/Twine.h" 34#include "llvm/BinaryFormat/MachO.h" 35#include "llvm/BinaryFormat/Magic.h" 36#include "llvm/Object/MachO.h" 37#include "llvm/Support/Casting.h" 38#include "llvm/Support/Errc.h" 39#include "llvm/Support/ErrorHandling.h" 40#include "llvm/Support/FileOutputBuffer.h" 41#include "llvm/Support/Host.h" 42#include "llvm/Support/MemoryBuffer.h" 43#include "llvm/Support/raw_ostream.h" 44#include <functional> 45#include <system_error> 46 47using namespace llvm::MachO; 48using llvm::object::ExportEntry; 49using llvm::file_magic; 50using llvm::object::MachOObjectFile; 51 52namespace lld { 53namespace mach_o { 54namespace normalized { 55 56// Utility to call a lambda expression on each load command. 57static llvm::Error forEachLoadCommand( 58 StringRef lcRange, unsigned lcCount, bool isBig, bool is64, 59 std::function<bool(uint32_t cmd, uint32_t size, const char *lc)> func) { 60 const char* p = lcRange.begin(); 61 for (unsigned i=0; i < lcCount; ++i) { 62 const load_command *lc = reinterpret_cast<const load_command*>(p); 63 load_command lcCopy; 64 const load_command *slc = lc; 65 if (isBig != llvm::sys::IsBigEndianHost) { 66 memcpy(&lcCopy, lc, sizeof(load_command)); 67 swapStruct(lcCopy); 68 slc = &lcCopy; 69 } 70 if ( (p + slc->cmdsize) > lcRange.end() ) 71 return llvm::make_error<GenericError>("Load command exceeds range"); 72 73 if (func(slc->cmd, slc->cmdsize, p)) 74 return llvm::Error::success(); 75 76 p += slc->cmdsize; 77 } 78 79 return llvm::Error::success(); 80} 81 82static std::error_code appendRelocations(Relocations &relocs, StringRef buffer, 83 bool bigEndian, 84 uint32_t reloff, uint32_t nreloc) { 85 if ((reloff + nreloc*8) > buffer.size()) 86 return make_error_code(llvm::errc::executable_format_error); 87 const any_relocation_info* relocsArray = 88 reinterpret_cast<const any_relocation_info*>(buffer.begin()+reloff); 89 90 for(uint32_t i=0; i < nreloc; ++i) { 91 relocs.push_back(unpackRelocation(relocsArray[i], bigEndian)); 92 } 93 return std::error_code(); 94} 95 96static std::error_code 97appendIndirectSymbols(IndirectSymbols &isyms, StringRef buffer, bool isBig, 98 uint32_t istOffset, uint32_t istCount, 99 uint32_t startIndex, uint32_t count) { 100 if ((istOffset + istCount*4) > buffer.size()) 101 return make_error_code(llvm::errc::executable_format_error); 102 if (startIndex+count > istCount) 103 return make_error_code(llvm::errc::executable_format_error); 104 const uint8_t *indirectSymbolArray = (const uint8_t *)buffer.data(); 105 106 for(uint32_t i=0; i < count; ++i) { 107 isyms.push_back(read32( 108 indirectSymbolArray + (startIndex + i) * sizeof(uint32_t), isBig)); 109 } 110 return std::error_code(); 111} 112 113 114template <typename T> static T readBigEndian(T t) { 115 if (llvm::sys::IsLittleEndianHost) 116 llvm::sys::swapByteOrder(t); 117 return t; 118} 119 120 121static bool isMachOHeader(const mach_header *mh, bool &is64, bool &isBig) { 122 switch (read32(&mh->magic, false)) { 123 case llvm::MachO::MH_MAGIC: 124 is64 = false; 125 isBig = false; 126 return true; 127 case llvm::MachO::MH_MAGIC_64: 128 is64 = true; 129 isBig = false; 130 return true; 131 case llvm::MachO::MH_CIGAM: 132 is64 = false; 133 isBig = true; 134 return true; 135 case llvm::MachO::MH_CIGAM_64: 136 is64 = true; 137 isBig = true; 138 return true; 139 default: 140 return false; 141 } 142} 143 144 145bool isThinObjectFile(StringRef path, MachOLinkingContext::Arch &arch) { 146 // Try opening and mapping file at path. 147 ErrorOr<std::unique_ptr<MemoryBuffer>> b = MemoryBuffer::getFileOrSTDIN(path); 148 if (b.getError()) 149 return false; 150 151 // If file length < 32 it is too small to be mach-o object file. 152 StringRef fileBuffer = b->get()->getBuffer(); 153 if (fileBuffer.size() < 32) 154 return false; 155 156 // If file buffer does not start with MH_MAGIC (and variants), not obj file. 157 const mach_header *mh = reinterpret_cast<const mach_header *>( 158 fileBuffer.begin()); 159 bool is64, isBig; 160 if (!isMachOHeader(mh, is64, isBig)) 161 return false; 162 163 // If not MH_OBJECT, not object file. 164 if (read32(&mh->filetype, isBig) != MH_OBJECT) 165 return false; 166 167 // Lookup up arch from cpu/subtype pair. 168 arch = MachOLinkingContext::archFromCpuType( 169 read32(&mh->cputype, isBig), 170 read32(&mh->cpusubtype, isBig)); 171 return true; 172} 173 174bool sliceFromFatFile(MemoryBufferRef mb, MachOLinkingContext::Arch arch, 175 uint32_t &offset, uint32_t &size) { 176 const char *start = mb.getBufferStart(); 177 const llvm::MachO::fat_header *fh = 178 reinterpret_cast<const llvm::MachO::fat_header *>(start); 179 if (readBigEndian(fh->magic) != llvm::MachO::FAT_MAGIC) 180 return false; 181 uint32_t nfat_arch = readBigEndian(fh->nfat_arch); 182 const fat_arch *fstart = 183 reinterpret_cast<const fat_arch *>(start + sizeof(fat_header)); 184 const fat_arch *fend = 185 reinterpret_cast<const fat_arch *>(start + sizeof(fat_header) + 186 sizeof(fat_arch) * nfat_arch); 187 const uint32_t reqCpuType = MachOLinkingContext::cpuTypeFromArch(arch); 188 const uint32_t reqCpuSubtype = MachOLinkingContext::cpuSubtypeFromArch(arch); 189 for (const fat_arch *fa = fstart; fa < fend; ++fa) { 190 if ((readBigEndian(fa->cputype) == reqCpuType) && 191 (readBigEndian(fa->cpusubtype) == reqCpuSubtype)) { 192 offset = readBigEndian(fa->offset); 193 size = readBigEndian(fa->size); 194 if ((offset + size) > mb.getBufferSize()) 195 return false; 196 return true; 197 } 198 } 199 return false; 200} 201 202/// Reads a mach-o file and produces an in-memory normalized view. 203llvm::Expected<std::unique_ptr<NormalizedFile>> 204readBinary(std::unique_ptr<MemoryBuffer> &mb, 205 const MachOLinkingContext::Arch arch) { 206 // Make empty NormalizedFile. 207 std::unique_ptr<NormalizedFile> f(new NormalizedFile()); 208 209 const char *start = mb->getBufferStart(); 210 size_t objSize = mb->getBufferSize(); 211 const mach_header *mh = reinterpret_cast<const mach_header *>(start); 212 213 uint32_t sliceOffset; 214 uint32_t sliceSize; 215 if (sliceFromFatFile(mb->getMemBufferRef(), arch, sliceOffset, sliceSize)) { 216 start = &start[sliceOffset]; 217 objSize = sliceSize; 218 mh = reinterpret_cast<const mach_header *>(start); 219 } 220 221 // Determine endianness and pointer size for mach-o file. 222 bool is64, isBig; 223 if (!isMachOHeader(mh, is64, isBig)) 224 return llvm::make_error<GenericError>("File is not a mach-o"); 225 226 // Endian swap header, if needed. 227 mach_header headerCopy; 228 const mach_header *smh = mh; 229 if (isBig != llvm::sys::IsBigEndianHost) { 230 memcpy(&headerCopy, mh, sizeof(mach_header)); 231 swapStruct(headerCopy); 232 smh = &headerCopy; 233 } 234 235 // Validate head and load commands fit in buffer. 236 const uint32_t lcCount = smh->ncmds; 237 const char *lcStart = 238 start + (is64 ? sizeof(mach_header_64) : sizeof(mach_header)); 239 StringRef lcRange(lcStart, smh->sizeofcmds); 240 if (lcRange.end() > (start + objSize)) 241 return llvm::make_error<GenericError>("Load commands exceed file size"); 242 243 // Get architecture from mach_header. 244 f->arch = MachOLinkingContext::archFromCpuType(smh->cputype, smh->cpusubtype); 245 if (f->arch != arch) { 246 return llvm::make_error<GenericError>( 247 Twine("file is wrong architecture. Expected " 248 "(" + MachOLinkingContext::nameFromArch(arch) 249 + ") found (" 250 + MachOLinkingContext::nameFromArch(f->arch) 251 + ")" )); 252 } 253 // Copy file type and flags 254 f->fileType = HeaderFileType(smh->filetype); 255 f->flags = smh->flags; 256 257 258 // Pre-scan load commands looking for indirect symbol table. 259 uint32_t indirectSymbolTableOffset = 0; 260 uint32_t indirectSymbolTableCount = 0; 261 auto ec = forEachLoadCommand(lcRange, lcCount, isBig, is64, 262 [&](uint32_t cmd, uint32_t size, 263 const char *lc) -> bool { 264 if (cmd == LC_DYSYMTAB) { 265 const dysymtab_command *d = reinterpret_cast<const dysymtab_command*>(lc); 266 indirectSymbolTableOffset = read32(&d->indirectsymoff, isBig); 267 indirectSymbolTableCount = read32(&d->nindirectsyms, isBig); 268 return true; 269 } 270 return false; 271 }); 272 if (ec) 273 return std::move(ec); 274 275 // Walk load commands looking for segments/sections and the symbol table. 276 const data_in_code_entry *dataInCode = nullptr; 277 const dyld_info_command *dyldInfo = nullptr; 278 uint32_t dataInCodeSize = 0; 279 ec = forEachLoadCommand(lcRange, lcCount, isBig, is64, 280 [&] (uint32_t cmd, uint32_t size, const char* lc) -> bool { 281 switch(cmd) { 282 case LC_SEGMENT_64: 283 if (is64) { 284 const segment_command_64 *seg = 285 reinterpret_cast<const segment_command_64*>(lc); 286 const unsigned sectionCount = read32(&seg->nsects, isBig); 287 const section_64 *sects = reinterpret_cast<const section_64*> 288 (lc + sizeof(segment_command_64)); 289 const unsigned lcSize = sizeof(segment_command_64) 290 + sectionCount*sizeof(section_64); 291 // Verify sections don't extend beyond end of segment load command. 292 if (lcSize > size) 293 return true; 294 for (unsigned i=0; i < sectionCount; ++i) { 295 const section_64 *sect = §s[i]; 296 Section section; 297 section.segmentName = getString16(sect->segname); 298 section.sectionName = getString16(sect->sectname); 299 section.type = (SectionType)(read32(§->flags, isBig) & 300 SECTION_TYPE); 301 section.attributes = read32(§->flags, isBig) & SECTION_ATTRIBUTES; 302 section.alignment = 1 << read32(§->align, isBig); 303 section.address = read64(§->addr, isBig); 304 const uint8_t *content = 305 (const uint8_t *)start + read32(§->offset, isBig); 306 size_t contentSize = read64(§->size, isBig); 307 // Note: this assign() is copying the content bytes. Ideally, 308 // we can use a custom allocator for vector to avoid the copy. 309 section.content = llvm::makeArrayRef(content, contentSize); 310 appendRelocations(section.relocations, mb->getBuffer(), isBig, 311 read32(§->reloff, isBig), 312 read32(§->nreloc, isBig)); 313 if (section.type == S_NON_LAZY_SYMBOL_POINTERS) { 314 appendIndirectSymbols(section.indirectSymbols, mb->getBuffer(), 315 isBig, 316 indirectSymbolTableOffset, 317 indirectSymbolTableCount, 318 read32(§->reserved1, isBig), 319 contentSize/4); 320 } 321 f->sections.push_back(section); 322 } 323 } 324 break; 325 case LC_SEGMENT: 326 if (!is64) { 327 const segment_command *seg = 328 reinterpret_cast<const segment_command*>(lc); 329 const unsigned sectionCount = read32(&seg->nsects, isBig); 330 const section *sects = reinterpret_cast<const section*> 331 (lc + sizeof(segment_command)); 332 const unsigned lcSize = sizeof(segment_command) 333 + sectionCount*sizeof(section); 334 // Verify sections don't extend beyond end of segment load command. 335 if (lcSize > size) 336 return true; 337 for (unsigned i=0; i < sectionCount; ++i) { 338 const section *sect = §s[i]; 339 Section section; 340 section.segmentName = getString16(sect->segname); 341 section.sectionName = getString16(sect->sectname); 342 section.type = (SectionType)(read32(§->flags, isBig) & 343 SECTION_TYPE); 344 section.attributes = 345 read32((const uint8_t *)§->flags, isBig) & SECTION_ATTRIBUTES; 346 section.alignment = 1 << read32(§->align, isBig); 347 section.address = read32(§->addr, isBig); 348 const uint8_t *content = 349 (const uint8_t *)start + read32(§->offset, isBig); 350 size_t contentSize = read32(§->size, isBig); 351 // Note: this assign() is copying the content bytes. Ideally, 352 // we can use a custom allocator for vector to avoid the copy. 353 section.content = llvm::makeArrayRef(content, contentSize); 354 appendRelocations(section.relocations, mb->getBuffer(), isBig, 355 read32(§->reloff, isBig), 356 read32(§->nreloc, isBig)); 357 if (section.type == S_NON_LAZY_SYMBOL_POINTERS) { 358 appendIndirectSymbols( 359 section.indirectSymbols, mb->getBuffer(), isBig, 360 indirectSymbolTableOffset, indirectSymbolTableCount, 361 read32(§->reserved1, isBig), contentSize / 4); 362 } 363 f->sections.push_back(section); 364 } 365 } 366 break; 367 case LC_SYMTAB: { 368 const symtab_command *st = reinterpret_cast<const symtab_command*>(lc); 369 const char *strings = start + read32(&st->stroff, isBig); 370 const uint32_t strSize = read32(&st->strsize, isBig); 371 // Validate string pool and symbol table all in buffer. 372 if (read32((const uint8_t *)&st->stroff, isBig) + 373 read32((const uint8_t *)&st->strsize, isBig) > 374 objSize) 375 return true; 376 if (is64) { 377 const uint32_t symOffset = read32(&st->symoff, isBig); 378 const uint32_t symCount = read32(&st->nsyms, isBig); 379 if ( symOffset+(symCount*sizeof(nlist_64)) > objSize) 380 return true; 381 const nlist_64 *symbols = 382 reinterpret_cast<const nlist_64 *>(start + symOffset); 383 // Convert each nlist_64 to a lld::mach_o::normalized::Symbol. 384 for(uint32_t i=0; i < symCount; ++i) { 385 nlist_64 tempSym; 386 memcpy(&tempSym, &symbols[i], sizeof(nlist_64)); 387 const nlist_64 *sin = &tempSym; 388 if (isBig != llvm::sys::IsBigEndianHost) 389 swapStruct(tempSym); 390 Symbol sout; 391 if (sin->n_strx > strSize) 392 return true; 393 sout.name = &strings[sin->n_strx]; 394 sout.type = static_cast<NListType>(sin->n_type & (N_STAB|N_TYPE)); 395 sout.scope = (sin->n_type & (N_PEXT|N_EXT)); 396 sout.sect = sin->n_sect; 397 sout.desc = sin->n_desc; 398 sout.value = sin->n_value; 399 if (sin->n_type & N_STAB) 400 f->stabsSymbols.push_back(sout); 401 else if (sout.type == N_UNDF) 402 f->undefinedSymbols.push_back(sout); 403 else if (sin->n_type & N_EXT) 404 f->globalSymbols.push_back(sout); 405 else 406 f->localSymbols.push_back(sout); 407 } 408 } else { 409 const uint32_t symOffset = read32(&st->symoff, isBig); 410 const uint32_t symCount = read32(&st->nsyms, isBig); 411 if ( symOffset+(symCount*sizeof(nlist)) > objSize) 412 return true; 413 const nlist *symbols = 414 reinterpret_cast<const nlist *>(start + symOffset); 415 // Convert each nlist to a lld::mach_o::normalized::Symbol. 416 for(uint32_t i=0; i < symCount; ++i) { 417 const nlist *sin = &symbols[i]; 418 nlist tempSym; 419 if (isBig != llvm::sys::IsBigEndianHost) { 420 tempSym = *sin; swapStruct(tempSym); sin = &tempSym; 421 } 422 Symbol sout; 423 if (sin->n_strx > strSize) 424 return true; 425 sout.name = &strings[sin->n_strx]; 426 sout.type = (NListType)(sin->n_type & N_TYPE); 427 sout.scope = (sin->n_type & (N_PEXT|N_EXT)); 428 sout.sect = sin->n_sect; 429 sout.desc = sin->n_desc; 430 sout.value = sin->n_value; 431 if (sout.type == N_UNDF) 432 f->undefinedSymbols.push_back(sout); 433 else if (sout.scope == (SymbolScope)N_EXT) 434 f->globalSymbols.push_back(sout); 435 else if (sin->n_type & N_STAB) 436 f->stabsSymbols.push_back(sout); 437 else 438 f->localSymbols.push_back(sout); 439 } 440 } 441 } 442 break; 443 case LC_ID_DYLIB: { 444 const dylib_command *dl = reinterpret_cast<const dylib_command*>(lc); 445 f->installName = lc + read32(&dl->dylib.name, isBig); 446 f->currentVersion = read32(&dl->dylib.current_version, isBig); 447 f->compatVersion = read32(&dl->dylib.compatibility_version, isBig); 448 } 449 break; 450 case LC_DATA_IN_CODE: { 451 const linkedit_data_command *ldc = 452 reinterpret_cast<const linkedit_data_command*>(lc); 453 dataInCode = reinterpret_cast<const data_in_code_entry *>( 454 start + read32(&ldc->dataoff, isBig)); 455 dataInCodeSize = read32(&ldc->datasize, isBig); 456 } 457 break; 458 case LC_LOAD_DYLIB: 459 case LC_LOAD_WEAK_DYLIB: 460 case LC_REEXPORT_DYLIB: 461 case LC_LOAD_UPWARD_DYLIB: { 462 const dylib_command *dl = reinterpret_cast<const dylib_command*>(lc); 463 DependentDylib entry; 464 entry.path = lc + read32(&dl->dylib.name, isBig); 465 entry.kind = LoadCommandType(cmd); 466 entry.compatVersion = read32(&dl->dylib.compatibility_version, isBig); 467 entry.currentVersion = read32(&dl->dylib.current_version, isBig); 468 f->dependentDylibs.push_back(entry); 469 } 470 break; 471 case LC_RPATH: { 472 const rpath_command *rpc = reinterpret_cast<const rpath_command *>(lc); 473 f->rpaths.push_back(lc + read32(&rpc->path, isBig)); 474 } 475 break; 476 case LC_DYLD_INFO: 477 case LC_DYLD_INFO_ONLY: 478 dyldInfo = reinterpret_cast<const dyld_info_command*>(lc); 479 break; 480 case LC_VERSION_MIN_MACOSX: 481 case LC_VERSION_MIN_IPHONEOS: 482 case LC_VERSION_MIN_WATCHOS: 483 case LC_VERSION_MIN_TVOS: 484 // If we are emitting an object file, then we may take the load command 485 // kind from these commands and pass it on to the output 486 // file. 487 f->minOSVersionKind = (LoadCommandType)cmd; 488 break; 489 } 490 return false; 491 }); 492 if (ec) 493 return std::move(ec); 494 495 if (dataInCode) { 496 // Convert on-disk data_in_code_entry array to DataInCode vector. 497 for (unsigned i=0; i < dataInCodeSize/sizeof(data_in_code_entry); ++i) { 498 DataInCode entry; 499 entry.offset = read32(&dataInCode[i].offset, isBig); 500 entry.length = read16(&dataInCode[i].length, isBig); 501 entry.kind = 502 (DataRegionType)read16((const uint8_t *)&dataInCode[i].kind, isBig); 503 f->dataInCode.push_back(entry); 504 } 505 } 506 507 if (dyldInfo) { 508 // If any exports, extract and add to normalized exportInfo vector. 509 if (dyldInfo->export_size) { 510 const uint8_t *trieStart = reinterpret_cast<const uint8_t *>( 511 start + read32(&dyldInfo->export_off, isBig)); 512 ArrayRef<uint8_t> trie(trieStart, read32(&dyldInfo->export_size, isBig)); 513 Error Err = Error::success(); 514 for (const ExportEntry &trieExport : MachOObjectFile::exports(Err, trie)) { 515 Export normExport; 516 normExport.name = trieExport.name().copy(f->ownedAllocations); 517 normExport.offset = trieExport.address(); 518 normExport.kind = ExportSymbolKind(trieExport.flags() & EXPORT_SYMBOL_FLAGS_KIND_MASK); 519 normExport.flags = trieExport.flags() & ~EXPORT_SYMBOL_FLAGS_KIND_MASK; 520 normExport.otherOffset = trieExport.other(); 521 if (!trieExport.otherName().empty()) 522 normExport.otherName = trieExport.otherName().copy(f->ownedAllocations); 523 f->exportInfo.push_back(normExport); 524 } 525 if (Err) 526 return std::move(Err); 527 } 528 } 529 530 return std::move(f); 531} 532 533class MachOObjectReader : public Reader { 534public: 535 MachOObjectReader(MachOLinkingContext &ctx) : _ctx(ctx) {} 536 537 bool canParse(file_magic magic, MemoryBufferRef mb) const override { 538 return (magic == file_magic::macho_object && mb.getBufferSize() > 32); 539 } 540 541 ErrorOr<std::unique_ptr<File>> 542 loadFile(std::unique_ptr<MemoryBuffer> mb, 543 const Registry ®istry) const override { 544 std::unique_ptr<File> ret = 545 std::make_unique<MachOFile>(std::move(mb), &_ctx); 546 return std::move(ret); 547 } 548 549private: 550 MachOLinkingContext &_ctx; 551}; 552 553class MachODylibReader : public Reader { 554public: 555 MachODylibReader(MachOLinkingContext &ctx) : _ctx(ctx) {} 556 557 bool canParse(file_magic magic, MemoryBufferRef mb) const override { 558 switch (magic) { 559 case file_magic::macho_dynamically_linked_shared_lib: 560 case file_magic::macho_dynamically_linked_shared_lib_stub: 561 return mb.getBufferSize() > 32; 562 default: 563 return false; 564 } 565 } 566 567 ErrorOr<std::unique_ptr<File>> 568 loadFile(std::unique_ptr<MemoryBuffer> mb, 569 const Registry ®istry) const override { 570 std::unique_ptr<File> ret = 571 std::make_unique<MachODylibFile>(std::move(mb), &_ctx); 572 return std::move(ret); 573 } 574 575private: 576 MachOLinkingContext &_ctx; 577}; 578 579} // namespace normalized 580} // namespace mach_o 581 582void Registry::addSupportMachOObjects(MachOLinkingContext &ctx) { 583 MachOLinkingContext::Arch arch = ctx.arch(); 584 add(std::unique_ptr<Reader>(new mach_o::normalized::MachOObjectReader(ctx))); 585 add(std::unique_ptr<Reader>(new mach_o::normalized::MachODylibReader(ctx))); 586 addKindTable(Reference::KindNamespace::mach_o, ctx.archHandler().kindArch(), 587 ctx.archHandler().kindStrings()); 588 add(std::unique_ptr<YamlIOTaggedDocumentHandler>( 589 new mach_o::MachOYamlIOTaggedDocumentHandler(arch))); 590} 591 592 593} // namespace lld 594