1 //===- yaml2macho - Convert YAML to a Mach object file --------------------===// 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 /// \file 10 /// The Mach component of yaml2obj. 11 /// 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/BinaryFormat/MachO.h" 15 #include "llvm/ObjectYAML/DWARFEmitter.h" 16 #include "llvm/ObjectYAML/ObjectYAML.h" 17 #include "llvm/ObjectYAML/yaml2obj.h" 18 #include "llvm/Support/LEB128.h" 19 #include "llvm/Support/YAMLTraits.h" 20 #include "llvm/Support/raw_ostream.h" 21 22 #include "llvm/Support/Format.h" 23 24 using namespace llvm; 25 26 namespace { 27 28 class MachOWriter { 29 public: 30 MachOWriter(MachOYAML::Object &Obj) : Obj(Obj), is64Bit(true), fileStart(0) { 31 is64Bit = Obj.Header.magic == MachO::MH_MAGIC_64 || 32 Obj.Header.magic == MachO::MH_CIGAM_64; 33 memset(reinterpret_cast<void *>(&Header), 0, sizeof(MachO::mach_header_64)); 34 } 35 36 void writeMachO(raw_ostream &OS); 37 38 private: 39 void writeHeader(raw_ostream &OS); 40 void writeLoadCommands(raw_ostream &OS); 41 void writeSectionData(raw_ostream &OS); 42 void writeLinkEditData(raw_ostream &OS); 43 44 void writeBindOpcodes(raw_ostream &OS, 45 std::vector<MachOYAML::BindOpcode> &BindOpcodes); 46 // LinkEdit writers 47 void writeRebaseOpcodes(raw_ostream &OS); 48 void writeBasicBindOpcodes(raw_ostream &OS); 49 void writeWeakBindOpcodes(raw_ostream &OS); 50 void writeLazyBindOpcodes(raw_ostream &OS); 51 void writeNameList(raw_ostream &OS); 52 void writeStringTable(raw_ostream &OS); 53 void writeExportTrie(raw_ostream &OS); 54 55 void dumpExportEntry(raw_ostream &OS, MachOYAML::ExportEntry &Entry); 56 void ZeroToOffset(raw_ostream &OS, size_t offset); 57 58 MachOYAML::Object &Obj; 59 bool is64Bit; 60 uint64_t fileStart; 61 62 MachO::mach_header_64 Header; 63 }; 64 65 void MachOWriter::writeMachO(raw_ostream &OS) { 66 fileStart = OS.tell(); 67 writeHeader(OS); 68 writeLoadCommands(OS); 69 writeSectionData(OS); 70 } 71 72 void MachOWriter::writeHeader(raw_ostream &OS) { 73 Header.magic = Obj.Header.magic; 74 Header.cputype = Obj.Header.cputype; 75 Header.cpusubtype = Obj.Header.cpusubtype; 76 Header.filetype = Obj.Header.filetype; 77 Header.ncmds = Obj.Header.ncmds; 78 Header.sizeofcmds = Obj.Header.sizeofcmds; 79 Header.flags = Obj.Header.flags; 80 Header.reserved = Obj.Header.reserved; 81 82 if (Obj.IsLittleEndian != sys::IsLittleEndianHost) 83 MachO::swapStruct(Header); 84 85 auto header_size = 86 is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); 87 OS.write((const char *)&Header, header_size); 88 } 89 90 template <typename SectionType> 91 SectionType constructSection(MachOYAML::Section Sec) { 92 SectionType TempSec; 93 memcpy(reinterpret_cast<void *>(&TempSec.sectname[0]), &Sec.sectname[0], 16); 94 memcpy(reinterpret_cast<void *>(&TempSec.segname[0]), &Sec.segname[0], 16); 95 TempSec.addr = Sec.addr; 96 TempSec.size = Sec.size; 97 TempSec.offset = Sec.offset; 98 TempSec.align = Sec.align; 99 TempSec.reloff = Sec.reloff; 100 TempSec.nreloc = Sec.nreloc; 101 TempSec.flags = Sec.flags; 102 TempSec.reserved1 = Sec.reserved1; 103 TempSec.reserved2 = Sec.reserved2; 104 return TempSec; 105 } 106 107 template <typename StructType> 108 size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, raw_ostream &OS, 109 bool IsLittleEndian) { 110 return 0; 111 } 112 113 template <> 114 size_t writeLoadCommandData<MachO::segment_command>(MachOYAML::LoadCommand &LC, 115 raw_ostream &OS, 116 bool IsLittleEndian) { 117 size_t BytesWritten = 0; 118 for (const auto &Sec : LC.Sections) { 119 auto TempSec = constructSection<MachO::section>(Sec); 120 if (IsLittleEndian != sys::IsLittleEndianHost) 121 MachO::swapStruct(TempSec); 122 OS.write(reinterpret_cast<const char *>(&(TempSec)), 123 sizeof(MachO::section)); 124 BytesWritten += sizeof(MachO::section); 125 } 126 return BytesWritten; 127 } 128 129 template <> 130 size_t writeLoadCommandData<MachO::segment_command_64>( 131 MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { 132 size_t BytesWritten = 0; 133 for (const auto &Sec : LC.Sections) { 134 auto TempSec = constructSection<MachO::section_64>(Sec); 135 TempSec.reserved3 = Sec.reserved3; 136 if (IsLittleEndian != sys::IsLittleEndianHost) 137 MachO::swapStruct(TempSec); 138 OS.write(reinterpret_cast<const char *>(&(TempSec)), 139 sizeof(MachO::section_64)); 140 BytesWritten += sizeof(MachO::section_64); 141 } 142 return BytesWritten; 143 } 144 145 size_t writePayloadString(MachOYAML::LoadCommand &LC, raw_ostream &OS) { 146 size_t BytesWritten = 0; 147 if (!LC.PayloadString.empty()) { 148 OS.write(LC.PayloadString.c_str(), LC.PayloadString.length()); 149 BytesWritten = LC.PayloadString.length(); 150 } 151 return BytesWritten; 152 } 153 154 template <> 155 size_t writeLoadCommandData<MachO::dylib_command>(MachOYAML::LoadCommand &LC, 156 raw_ostream &OS, 157 bool IsLittleEndian) { 158 return writePayloadString(LC, OS); 159 } 160 161 template <> 162 size_t writeLoadCommandData<MachO::dylinker_command>(MachOYAML::LoadCommand &LC, 163 raw_ostream &OS, 164 bool IsLittleEndian) { 165 return writePayloadString(LC, OS); 166 } 167 168 template <> 169 size_t writeLoadCommandData<MachO::rpath_command>(MachOYAML::LoadCommand &LC, 170 raw_ostream &OS, 171 bool IsLittleEndian) { 172 return writePayloadString(LC, OS); 173 } 174 175 template <> 176 size_t writeLoadCommandData<MachO::build_version_command>( 177 MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { 178 size_t BytesWritten = 0; 179 for (const auto &T : LC.Tools) { 180 struct MachO::build_tool_version tool = T; 181 if (IsLittleEndian != sys::IsLittleEndianHost) 182 MachO::swapStruct(tool); 183 OS.write(reinterpret_cast<const char *>(&tool), 184 sizeof(MachO::build_tool_version)); 185 BytesWritten += sizeof(MachO::build_tool_version); 186 } 187 return BytesWritten; 188 } 189 190 void ZeroFillBytes(raw_ostream &OS, size_t Size) { 191 std::vector<uint8_t> FillData; 192 FillData.insert(FillData.begin(), Size, 0); 193 OS.write(reinterpret_cast<char *>(FillData.data()), Size); 194 } 195 196 void Fill(raw_ostream &OS, size_t Size, uint32_t Data) { 197 std::vector<uint32_t> FillData; 198 FillData.insert(FillData.begin(), (Size / 4) + 1, Data); 199 OS.write(reinterpret_cast<char *>(FillData.data()), Size); 200 } 201 202 void MachOWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) { 203 auto currOffset = OS.tell() - fileStart; 204 if (currOffset < Offset) 205 ZeroFillBytes(OS, Offset - currOffset); 206 } 207 208 void MachOWriter::writeLoadCommands(raw_ostream &OS) { 209 for (auto &LC : Obj.LoadCommands) { 210 size_t BytesWritten = 0; 211 llvm::MachO::macho_load_command Data = LC.Data; 212 213 #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ 214 case MachO::LCName: \ 215 if (Obj.IsLittleEndian != sys::IsLittleEndianHost) \ 216 MachO::swapStruct(Data.LCStruct##_data); \ 217 OS.write(reinterpret_cast<const char *>(&(Data.LCStruct##_data)), \ 218 sizeof(MachO::LCStruct)); \ 219 BytesWritten = sizeof(MachO::LCStruct); \ 220 BytesWritten += \ 221 writeLoadCommandData<MachO::LCStruct>(LC, OS, Obj.IsLittleEndian); \ 222 break; 223 224 switch (LC.Data.load_command_data.cmd) { 225 default: 226 if (Obj.IsLittleEndian != sys::IsLittleEndianHost) 227 MachO::swapStruct(Data.load_command_data); 228 OS.write(reinterpret_cast<const char *>(&(Data.load_command_data)), 229 sizeof(MachO::load_command)); 230 BytesWritten = sizeof(MachO::load_command); 231 BytesWritten += 232 writeLoadCommandData<MachO::load_command>(LC, OS, Obj.IsLittleEndian); 233 break; 234 #include "llvm/BinaryFormat/MachO.def" 235 } 236 237 if (LC.PayloadBytes.size() > 0) { 238 OS.write(reinterpret_cast<const char *>(LC.PayloadBytes.data()), 239 LC.PayloadBytes.size()); 240 BytesWritten += LC.PayloadBytes.size(); 241 } 242 243 if (LC.ZeroPadBytes > 0) { 244 ZeroFillBytes(OS, LC.ZeroPadBytes); 245 BytesWritten += LC.ZeroPadBytes; 246 } 247 248 // Fill remaining bytes with 0. This will only get hit in partially 249 // specified test cases. 250 auto BytesRemaining = LC.Data.load_command_data.cmdsize - BytesWritten; 251 if (BytesRemaining > 0) { 252 ZeroFillBytes(OS, BytesRemaining); 253 } 254 } 255 } 256 257 void MachOWriter::writeSectionData(raw_ostream &OS) { 258 bool FoundLinkEditSeg = false; 259 for (auto &LC : Obj.LoadCommands) { 260 switch (LC.Data.load_command_data.cmd) { 261 case MachO::LC_SEGMENT: 262 case MachO::LC_SEGMENT_64: 263 uint64_t segOff = is64Bit ? LC.Data.segment_command_64_data.fileoff 264 : LC.Data.segment_command_data.fileoff; 265 if (0 == 266 strncmp(&LC.Data.segment_command_data.segname[0], "__LINKEDIT", 16)) { 267 FoundLinkEditSeg = true; 268 writeLinkEditData(OS); 269 } 270 for (auto &Sec : LC.Sections) { 271 ZeroToOffset(OS, Sec.offset); 272 // Zero Fill any data between the end of the last thing we wrote and the 273 // start of this section. 274 assert((OS.tell() - fileStart <= Sec.offset || 275 Sec.offset == (uint32_t)0) && 276 "Wrote too much data somewhere, section offsets don't line up."); 277 if (0 == strncmp(&Sec.segname[0], "__DWARF", 16)) { 278 if (0 == strncmp(&Sec.sectname[0], "__debug_str", 16)) { 279 DWARFYAML::EmitDebugStr(OS, Obj.DWARF); 280 } else if (0 == strncmp(&Sec.sectname[0], "__debug_abbrev", 16)) { 281 DWARFYAML::EmitDebugAbbrev(OS, Obj.DWARF); 282 } else if (0 == strncmp(&Sec.sectname[0], "__debug_aranges", 16)) { 283 DWARFYAML::EmitDebugAranges(OS, Obj.DWARF); 284 } else if (0 == strncmp(&Sec.sectname[0], "__debug_pubnames", 16)) { 285 DWARFYAML::EmitPubSection(OS, Obj.DWARF.PubNames, 286 Obj.IsLittleEndian); 287 } else if (0 == strncmp(&Sec.sectname[0], "__debug_pubtypes", 16)) { 288 DWARFYAML::EmitPubSection(OS, Obj.DWARF.PubTypes, 289 Obj.IsLittleEndian); 290 } else if (0 == strncmp(&Sec.sectname[0], "__debug_info", 16)) { 291 DWARFYAML::EmitDebugInfo(OS, Obj.DWARF); 292 } else if (0 == strncmp(&Sec.sectname[0], "__debug_line", 16)) { 293 DWARFYAML::EmitDebugLine(OS, Obj.DWARF); 294 } 295 296 continue; 297 } 298 299 // Skip if it's a virtual section. 300 if (MachO::isVirtualSection(Sec.flags & MachO::SECTION_TYPE)) 301 continue; 302 303 if (Sec.content) { 304 yaml::BinaryRef Content = *Sec.content; 305 Content.writeAsBinary(OS); 306 ZeroFillBytes(OS, Sec.size - Content.binary_size()); 307 } else { 308 // Fill section data with 0xDEADBEEF. 309 Fill(OS, Sec.size, 0xDEADBEEFu); 310 } 311 } 312 uint64_t segSize = is64Bit ? LC.Data.segment_command_64_data.filesize 313 : LC.Data.segment_command_data.filesize; 314 ZeroToOffset(OS, segOff + segSize); 315 break; 316 } 317 } 318 // Old PPC Object Files didn't have __LINKEDIT segments, the data was just 319 // stuck at the end of the file. 320 if (!FoundLinkEditSeg) 321 writeLinkEditData(OS); 322 } 323 324 void MachOWriter::writeBindOpcodes( 325 raw_ostream &OS, std::vector<MachOYAML::BindOpcode> &BindOpcodes) { 326 327 for (auto Opcode : BindOpcodes) { 328 uint8_t OpByte = Opcode.Opcode | Opcode.Imm; 329 OS.write(reinterpret_cast<char *>(&OpByte), 1); 330 for (auto Data : Opcode.ULEBExtraData) { 331 encodeULEB128(Data, OS); 332 } 333 for (auto Data : Opcode.SLEBExtraData) { 334 encodeSLEB128(Data, OS); 335 } 336 if (!Opcode.Symbol.empty()) { 337 OS.write(Opcode.Symbol.data(), Opcode.Symbol.size()); 338 OS.write('\0'); 339 } 340 } 341 } 342 343 void MachOWriter::dumpExportEntry(raw_ostream &OS, 344 MachOYAML::ExportEntry &Entry) { 345 encodeSLEB128(Entry.TerminalSize, OS); 346 if (Entry.TerminalSize > 0) { 347 encodeSLEB128(Entry.Flags, OS); 348 if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT) { 349 encodeSLEB128(Entry.Other, OS); 350 OS << Entry.ImportName; 351 OS.write('\0'); 352 } else { 353 encodeSLEB128(Entry.Address, OS); 354 if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) 355 encodeSLEB128(Entry.Other, OS); 356 } 357 } 358 OS.write(static_cast<uint8_t>(Entry.Children.size())); 359 for (auto EE : Entry.Children) { 360 OS << EE.Name; 361 OS.write('\0'); 362 encodeSLEB128(EE.NodeOffset, OS); 363 } 364 for (auto EE : Entry.Children) 365 dumpExportEntry(OS, EE); 366 } 367 368 void MachOWriter::writeExportTrie(raw_ostream &OS) { 369 dumpExportEntry(OS, Obj.LinkEdit.ExportTrie); 370 } 371 372 template <typename NListType> 373 void writeNListEntry(MachOYAML::NListEntry &NLE, raw_ostream &OS, 374 bool IsLittleEndian) { 375 NListType ListEntry; 376 ListEntry.n_strx = NLE.n_strx; 377 ListEntry.n_type = NLE.n_type; 378 ListEntry.n_sect = NLE.n_sect; 379 ListEntry.n_desc = NLE.n_desc; 380 ListEntry.n_value = NLE.n_value; 381 382 if (IsLittleEndian != sys::IsLittleEndianHost) 383 MachO::swapStruct(ListEntry); 384 OS.write(reinterpret_cast<const char *>(&ListEntry), sizeof(NListType)); 385 } 386 387 void MachOWriter::writeLinkEditData(raw_ostream &OS) { 388 typedef void (MachOWriter::*writeHandler)(raw_ostream &); 389 typedef std::pair<uint64_t, writeHandler> writeOperation; 390 std::vector<writeOperation> WriteQueue; 391 392 MachO::dyld_info_command *DyldInfoOnlyCmd = 0; 393 MachO::symtab_command *SymtabCmd = 0; 394 for (auto &LC : Obj.LoadCommands) { 395 switch (LC.Data.load_command_data.cmd) { 396 case MachO::LC_SYMTAB: 397 SymtabCmd = &LC.Data.symtab_command_data; 398 WriteQueue.push_back( 399 std::make_pair(SymtabCmd->symoff, &MachOWriter::writeNameList)); 400 WriteQueue.push_back( 401 std::make_pair(SymtabCmd->stroff, &MachOWriter::writeStringTable)); 402 break; 403 case MachO::LC_DYLD_INFO_ONLY: 404 DyldInfoOnlyCmd = &LC.Data.dyld_info_command_data; 405 WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->rebase_off, 406 &MachOWriter::writeRebaseOpcodes)); 407 WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->bind_off, 408 &MachOWriter::writeBasicBindOpcodes)); 409 WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->weak_bind_off, 410 &MachOWriter::writeWeakBindOpcodes)); 411 WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->lazy_bind_off, 412 &MachOWriter::writeLazyBindOpcodes)); 413 WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->export_off, 414 &MachOWriter::writeExportTrie)); 415 break; 416 } 417 } 418 419 llvm::sort(WriteQueue, [](const writeOperation &a, const writeOperation &b) { 420 return a.first < b.first; 421 }); 422 423 for (auto writeOp : WriteQueue) { 424 ZeroToOffset(OS, writeOp.first); 425 (this->*writeOp.second)(OS); 426 } 427 } 428 429 void MachOWriter::writeRebaseOpcodes(raw_ostream &OS) { 430 MachOYAML::LinkEditData &LinkEdit = Obj.LinkEdit; 431 432 for (auto Opcode : LinkEdit.RebaseOpcodes) { 433 uint8_t OpByte = Opcode.Opcode | Opcode.Imm; 434 OS.write(reinterpret_cast<char *>(&OpByte), 1); 435 for (auto Data : Opcode.ExtraData) 436 encodeULEB128(Data, OS); 437 } 438 } 439 440 void MachOWriter::writeBasicBindOpcodes(raw_ostream &OS) { 441 writeBindOpcodes(OS, Obj.LinkEdit.BindOpcodes); 442 } 443 444 void MachOWriter::writeWeakBindOpcodes(raw_ostream &OS) { 445 writeBindOpcodes(OS, Obj.LinkEdit.WeakBindOpcodes); 446 } 447 448 void MachOWriter::writeLazyBindOpcodes(raw_ostream &OS) { 449 writeBindOpcodes(OS, Obj.LinkEdit.LazyBindOpcodes); 450 } 451 452 void MachOWriter::writeNameList(raw_ostream &OS) { 453 for (auto NLE : Obj.LinkEdit.NameList) { 454 if (is64Bit) 455 writeNListEntry<MachO::nlist_64>(NLE, OS, Obj.IsLittleEndian); 456 else 457 writeNListEntry<MachO::nlist>(NLE, OS, Obj.IsLittleEndian); 458 } 459 } 460 461 void MachOWriter::writeStringTable(raw_ostream &OS) { 462 for (auto Str : Obj.LinkEdit.StringTable) { 463 OS.write(Str.data(), Str.size()); 464 OS.write('\0'); 465 } 466 } 467 468 class UniversalWriter { 469 public: 470 UniversalWriter(yaml::YamlObjectFile &ObjectFile) 471 : ObjectFile(ObjectFile), fileStart(0) {} 472 473 void writeMachO(raw_ostream &OS); 474 475 private: 476 void writeFatHeader(raw_ostream &OS); 477 void writeFatArchs(raw_ostream &OS); 478 479 void ZeroToOffset(raw_ostream &OS, size_t offset); 480 481 yaml::YamlObjectFile &ObjectFile; 482 uint64_t fileStart; 483 }; 484 485 void UniversalWriter::writeMachO(raw_ostream &OS) { 486 fileStart = OS.tell(); 487 if (ObjectFile.MachO) { 488 MachOWriter Writer(*ObjectFile.MachO); 489 Writer.writeMachO(OS); 490 return; 491 } 492 493 writeFatHeader(OS); 494 writeFatArchs(OS); 495 496 auto &FatFile = *ObjectFile.FatMachO; 497 assert(FatFile.FatArchs.size() >= FatFile.Slices.size() && 498 "Cannot write Slices if not decribed in FatArches"); 499 for (size_t i = 0; i < FatFile.Slices.size(); i++) { 500 ZeroToOffset(OS, FatFile.FatArchs[i].offset); 501 MachOWriter Writer(FatFile.Slices[i]); 502 Writer.writeMachO(OS); 503 504 auto SliceEnd = FatFile.FatArchs[i].offset + FatFile.FatArchs[i].size; 505 ZeroToOffset(OS, SliceEnd); 506 } 507 } 508 509 void UniversalWriter::writeFatHeader(raw_ostream &OS) { 510 auto &FatFile = *ObjectFile.FatMachO; 511 MachO::fat_header header; 512 header.magic = FatFile.Header.magic; 513 header.nfat_arch = FatFile.Header.nfat_arch; 514 if (sys::IsLittleEndianHost) 515 swapStruct(header); 516 OS.write(reinterpret_cast<const char *>(&header), sizeof(MachO::fat_header)); 517 } 518 519 template <typename FatArchType> 520 FatArchType constructFatArch(MachOYAML::FatArch &Arch) { 521 FatArchType FatArch; 522 FatArch.cputype = Arch.cputype; 523 FatArch.cpusubtype = Arch.cpusubtype; 524 FatArch.offset = Arch.offset; 525 FatArch.size = Arch.size; 526 FatArch.align = Arch.align; 527 return FatArch; 528 } 529 530 template <typename StructType> 531 void writeFatArch(MachOYAML::FatArch &LC, raw_ostream &OS) {} 532 533 template <> 534 void writeFatArch<MachO::fat_arch>(MachOYAML::FatArch &Arch, raw_ostream &OS) { 535 auto FatArch = constructFatArch<MachO::fat_arch>(Arch); 536 if (sys::IsLittleEndianHost) 537 swapStruct(FatArch); 538 OS.write(reinterpret_cast<const char *>(&FatArch), sizeof(MachO::fat_arch)); 539 } 540 541 template <> 542 void writeFatArch<MachO::fat_arch_64>(MachOYAML::FatArch &Arch, 543 raw_ostream &OS) { 544 auto FatArch = constructFatArch<MachO::fat_arch_64>(Arch); 545 FatArch.reserved = Arch.reserved; 546 if (sys::IsLittleEndianHost) 547 swapStruct(FatArch); 548 OS.write(reinterpret_cast<const char *>(&FatArch), 549 sizeof(MachO::fat_arch_64)); 550 } 551 552 void UniversalWriter::writeFatArchs(raw_ostream &OS) { 553 auto &FatFile = *ObjectFile.FatMachO; 554 bool is64Bit = FatFile.Header.magic == MachO::FAT_MAGIC_64; 555 for (auto Arch : FatFile.FatArchs) { 556 if (is64Bit) 557 writeFatArch<MachO::fat_arch_64>(Arch, OS); 558 else 559 writeFatArch<MachO::fat_arch>(Arch, OS); 560 } 561 } 562 563 void UniversalWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) { 564 auto currOffset = OS.tell() - fileStart; 565 if (currOffset < Offset) 566 ZeroFillBytes(OS, Offset - currOffset); 567 } 568 569 } // end anonymous namespace 570 571 namespace llvm { 572 namespace yaml { 573 574 bool yaml2macho(YamlObjectFile &Doc, raw_ostream &Out, ErrorHandler /*EH*/) { 575 UniversalWriter Writer(Doc); 576 Writer.writeMachO(Out); 577 return true; 578 } 579 580 } // namespace yaml 581 } // namespace llvm 582