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/Errc.h" 19 #include "llvm/Support/Error.h" 20 #include "llvm/Support/FormatVariadic.h" 21 #include "llvm/Support/LEB128.h" 22 #include "llvm/Support/SystemZ/zOSSupport.h" 23 #include "llvm/Support/YAMLTraits.h" 24 #include "llvm/Support/raw_ostream.h" 25 26 using namespace llvm; 27 28 namespace { 29 30 class MachOWriter { 31 public: 32 MachOWriter(MachOYAML::Object &Obj) : Obj(Obj), fileStart(0) { 33 is64Bit = Obj.Header.magic == MachO::MH_MAGIC_64 || 34 Obj.Header.magic == MachO::MH_CIGAM_64; 35 memset(reinterpret_cast<void *>(&Header), 0, sizeof(MachO::mach_header_64)); 36 } 37 38 Error writeMachO(raw_ostream &OS); 39 40 private: 41 void writeHeader(raw_ostream &OS); 42 void writeLoadCommands(raw_ostream &OS); 43 Error writeSectionData(raw_ostream &OS); 44 void writeRelocations(raw_ostream &OS); 45 void writeLinkEditData(raw_ostream &OS); 46 47 void writeBindOpcodes(raw_ostream &OS, 48 std::vector<MachOYAML::BindOpcode> &BindOpcodes); 49 // LinkEdit writers 50 void writeRebaseOpcodes(raw_ostream &OS); 51 void writeBasicBindOpcodes(raw_ostream &OS); 52 void writeWeakBindOpcodes(raw_ostream &OS); 53 void writeLazyBindOpcodes(raw_ostream &OS); 54 void writeNameList(raw_ostream &OS); 55 void writeStringTable(raw_ostream &OS); 56 void writeExportTrie(raw_ostream &OS); 57 void writeDynamicSymbolTable(raw_ostream &OS); 58 void writeFunctionStarts(raw_ostream &OS); 59 void writeChainedFixups(raw_ostream &OS); 60 void writeDyldExportsTrie(raw_ostream &OS); 61 void writeDataInCode(raw_ostream &OS); 62 63 void dumpExportEntry(raw_ostream &OS, MachOYAML::ExportEntry &Entry); 64 void ZeroToOffset(raw_ostream &OS, size_t offset); 65 66 MachOYAML::Object &Obj; 67 bool is64Bit; 68 uint64_t fileStart; 69 MachO::mach_header_64 Header; 70 71 // Old PPC Object Files didn't have __LINKEDIT segments, the data was just 72 // stuck at the end of the file. 73 bool FoundLinkEditSeg = false; 74 }; 75 76 Error MachOWriter::writeMachO(raw_ostream &OS) { 77 fileStart = OS.tell(); 78 writeHeader(OS); 79 writeLoadCommands(OS); 80 if (Error Err = writeSectionData(OS)) 81 return Err; 82 writeRelocations(OS); 83 if (!FoundLinkEditSeg) 84 writeLinkEditData(OS); 85 return Error::success(); 86 } 87 88 void MachOWriter::writeHeader(raw_ostream &OS) { 89 Header.magic = Obj.Header.magic; 90 Header.cputype = Obj.Header.cputype; 91 Header.cpusubtype = Obj.Header.cpusubtype; 92 Header.filetype = Obj.Header.filetype; 93 Header.ncmds = Obj.Header.ncmds; 94 Header.sizeofcmds = Obj.Header.sizeofcmds; 95 Header.flags = Obj.Header.flags; 96 Header.reserved = Obj.Header.reserved; 97 98 if (Obj.IsLittleEndian != sys::IsLittleEndianHost) 99 MachO::swapStruct(Header); 100 101 auto header_size = 102 is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); 103 OS.write((const char *)&Header, header_size); 104 } 105 106 template <typename SectionType> 107 SectionType constructSection(const MachOYAML::Section &Sec) { 108 SectionType TempSec; 109 memcpy(reinterpret_cast<void *>(&TempSec.sectname[0]), &Sec.sectname[0], 16); 110 memcpy(reinterpret_cast<void *>(&TempSec.segname[0]), &Sec.segname[0], 16); 111 TempSec.addr = Sec.addr; 112 TempSec.size = Sec.size; 113 TempSec.offset = Sec.offset; 114 TempSec.align = Sec.align; 115 TempSec.reloff = Sec.reloff; 116 TempSec.nreloc = Sec.nreloc; 117 TempSec.flags = Sec.flags; 118 TempSec.reserved1 = Sec.reserved1; 119 TempSec.reserved2 = Sec.reserved2; 120 return TempSec; 121 } 122 123 template <typename StructType> 124 size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, raw_ostream &OS, 125 bool IsLittleEndian) { 126 return 0; 127 } 128 129 template <> 130 size_t writeLoadCommandData<MachO::segment_command>(MachOYAML::LoadCommand &LC, 131 raw_ostream &OS, 132 bool IsLittleEndian) { 133 size_t BytesWritten = 0; 134 for (const auto &Sec : LC.Sections) { 135 auto TempSec = constructSection<MachO::section>(Sec); 136 if (IsLittleEndian != sys::IsLittleEndianHost) 137 MachO::swapStruct(TempSec); 138 OS.write(reinterpret_cast<const char *>(&(TempSec)), 139 sizeof(MachO::section)); 140 BytesWritten += sizeof(MachO::section); 141 } 142 return BytesWritten; 143 } 144 145 template <> 146 size_t writeLoadCommandData<MachO::segment_command_64>( 147 MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { 148 size_t BytesWritten = 0; 149 for (const auto &Sec : LC.Sections) { 150 auto TempSec = constructSection<MachO::section_64>(Sec); 151 TempSec.reserved3 = Sec.reserved3; 152 if (IsLittleEndian != sys::IsLittleEndianHost) 153 MachO::swapStruct(TempSec); 154 OS.write(reinterpret_cast<const char *>(&(TempSec)), 155 sizeof(MachO::section_64)); 156 BytesWritten += sizeof(MachO::section_64); 157 } 158 return BytesWritten; 159 } 160 161 size_t writePayloadString(MachOYAML::LoadCommand &LC, raw_ostream &OS) { 162 size_t BytesWritten = 0; 163 if (!LC.Content.empty()) { 164 OS.write(LC.Content.c_str(), LC.Content.length()); 165 BytesWritten = LC.Content.length(); 166 } 167 return BytesWritten; 168 } 169 170 template <> 171 size_t writeLoadCommandData<MachO::dylib_command>(MachOYAML::LoadCommand &LC, 172 raw_ostream &OS, 173 bool IsLittleEndian) { 174 return writePayloadString(LC, OS); 175 } 176 177 template <> 178 size_t writeLoadCommandData<MachO::dylinker_command>(MachOYAML::LoadCommand &LC, 179 raw_ostream &OS, 180 bool IsLittleEndian) { 181 return writePayloadString(LC, OS); 182 } 183 184 template <> 185 size_t writeLoadCommandData<MachO::rpath_command>(MachOYAML::LoadCommand &LC, 186 raw_ostream &OS, 187 bool IsLittleEndian) { 188 return writePayloadString(LC, OS); 189 } 190 191 template <> 192 size_t writeLoadCommandData<MachO::sub_framework_command>( 193 MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { 194 return writePayloadString(LC, OS); 195 } 196 197 template <> 198 size_t writeLoadCommandData<MachO::sub_umbrella_command>( 199 MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { 200 return writePayloadString(LC, OS); 201 } 202 203 template <> 204 size_t writeLoadCommandData<MachO::sub_client_command>( 205 MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { 206 return writePayloadString(LC, OS); 207 } 208 209 template <> 210 size_t writeLoadCommandData<MachO::sub_library_command>( 211 MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { 212 return writePayloadString(LC, OS); 213 } 214 215 template <> 216 size_t writeLoadCommandData<MachO::build_version_command>( 217 MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { 218 size_t BytesWritten = 0; 219 for (const auto &T : LC.Tools) { 220 struct MachO::build_tool_version tool = T; 221 if (IsLittleEndian != sys::IsLittleEndianHost) 222 MachO::swapStruct(tool); 223 OS.write(reinterpret_cast<const char *>(&tool), 224 sizeof(MachO::build_tool_version)); 225 BytesWritten += sizeof(MachO::build_tool_version); 226 } 227 return BytesWritten; 228 } 229 230 void ZeroFillBytes(raw_ostream &OS, size_t Size) { 231 std::vector<uint8_t> FillData(Size, 0); 232 OS.write(reinterpret_cast<char *>(FillData.data()), Size); 233 } 234 235 void Fill(raw_ostream &OS, size_t Size, uint32_t Data) { 236 std::vector<uint32_t> FillData((Size / 4) + 1, Data); 237 OS.write(reinterpret_cast<char *>(FillData.data()), Size); 238 } 239 240 void MachOWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) { 241 auto currOffset = OS.tell() - fileStart; 242 if (currOffset < Offset) 243 ZeroFillBytes(OS, Offset - currOffset); 244 } 245 246 void MachOWriter::writeLoadCommands(raw_ostream &OS) { 247 for (auto &LC : Obj.LoadCommands) { 248 size_t BytesWritten = 0; 249 llvm::MachO::macho_load_command Data = LC.Data; 250 251 #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ 252 case MachO::LCName: \ 253 if (Obj.IsLittleEndian != sys::IsLittleEndianHost) \ 254 MachO::swapStruct(Data.LCStruct##_data); \ 255 OS.write(reinterpret_cast<const char *>(&(Data.LCStruct##_data)), \ 256 sizeof(MachO::LCStruct)); \ 257 BytesWritten = sizeof(MachO::LCStruct); \ 258 BytesWritten += \ 259 writeLoadCommandData<MachO::LCStruct>(LC, OS, Obj.IsLittleEndian); \ 260 break; 261 262 switch (LC.Data.load_command_data.cmd) { 263 default: 264 if (Obj.IsLittleEndian != sys::IsLittleEndianHost) 265 MachO::swapStruct(Data.load_command_data); 266 OS.write(reinterpret_cast<const char *>(&(Data.load_command_data)), 267 sizeof(MachO::load_command)); 268 BytesWritten = sizeof(MachO::load_command); 269 BytesWritten += 270 writeLoadCommandData<MachO::load_command>(LC, OS, Obj.IsLittleEndian); 271 break; 272 #include "llvm/BinaryFormat/MachO.def" 273 } 274 275 if (LC.PayloadBytes.size() > 0) { 276 OS.write(reinterpret_cast<const char *>(LC.PayloadBytes.data()), 277 LC.PayloadBytes.size()); 278 BytesWritten += LC.PayloadBytes.size(); 279 } 280 281 if (LC.ZeroPadBytes > 0) { 282 ZeroFillBytes(OS, LC.ZeroPadBytes); 283 BytesWritten += LC.ZeroPadBytes; 284 } 285 286 // Fill remaining bytes with 0. This will only get hit in partially 287 // specified test cases. 288 auto BytesRemaining = LC.Data.load_command_data.cmdsize - BytesWritten; 289 if (BytesRemaining > 0) { 290 ZeroFillBytes(OS, BytesRemaining); 291 } 292 } 293 } 294 295 Error MachOWriter::writeSectionData(raw_ostream &OS) { 296 uint64_t LinkEditOff = 0; 297 for (auto &LC : Obj.LoadCommands) { 298 switch (LC.Data.load_command_data.cmd) { 299 case MachO::LC_SEGMENT: 300 case MachO::LC_SEGMENT_64: 301 uint64_t segOff = is64Bit ? LC.Data.segment_command_64_data.fileoff 302 : LC.Data.segment_command_data.fileoff; 303 if (0 == 304 strncmp(&LC.Data.segment_command_data.segname[0], "__LINKEDIT", 16)) { 305 FoundLinkEditSeg = true; 306 LinkEditOff = segOff; 307 if (Obj.RawLinkEditSegment) 308 continue; 309 writeLinkEditData(OS); 310 } 311 for (auto &Sec : LC.Sections) { 312 ZeroToOffset(OS, Sec.offset); 313 // Zero Fill any data between the end of the last thing we wrote and the 314 // start of this section. 315 if (OS.tell() - fileStart > Sec.offset && Sec.offset != (uint32_t)0) 316 return createStringError( 317 errc::invalid_argument, 318 llvm::formatv( 319 "wrote too much data somewhere, section offsets in " 320 "section {0} for segment {1} don't line up: " 321 "[cursor={2:x}], [fileStart={3:x}], [sectionOffset={4:x}]", 322 Sec.sectname, Sec.segname, OS.tell(), fileStart, 323 Sec.offset.value)); 324 325 StringRef SectName(Sec.sectname, 326 strnlen(Sec.sectname, sizeof(Sec.sectname))); 327 // If the section's content is specified in the 'DWARF' entry, we will 328 // emit it regardless of the section's segname. 329 if (Obj.DWARF.getNonEmptySectionNames().count(SectName.substr(2))) { 330 if (Sec.content) 331 return createStringError(errc::invalid_argument, 332 "cannot specify section '" + SectName + 333 "' contents in the 'DWARF' entry and " 334 "the 'content' at the same time"); 335 auto EmitFunc = DWARFYAML::getDWARFEmitterByName(SectName.substr(2)); 336 if (Error Err = EmitFunc(OS, Obj.DWARF)) 337 return Err; 338 continue; 339 } 340 341 // Skip if it's a virtual section. 342 if (MachO::isVirtualSection(Sec.flags & MachO::SECTION_TYPE)) 343 continue; 344 345 if (Sec.content) { 346 yaml::BinaryRef Content = *Sec.content; 347 Content.writeAsBinary(OS); 348 ZeroFillBytes(OS, Sec.size - Content.binary_size()); 349 } else { 350 // Fill section data with 0xDEADBEEF. 351 Fill(OS, Sec.size, 0xDEADBEEFu); 352 } 353 } 354 uint64_t segSize = is64Bit ? LC.Data.segment_command_64_data.filesize 355 : LC.Data.segment_command_data.filesize; 356 ZeroToOffset(OS, segOff + segSize); 357 break; 358 } 359 } 360 361 if (Obj.RawLinkEditSegment) { 362 ZeroToOffset(OS, LinkEditOff); 363 if (OS.tell() - fileStart > LinkEditOff || !LinkEditOff) 364 return createStringError(errc::invalid_argument, 365 "section offsets don't line up"); 366 Obj.RawLinkEditSegment->writeAsBinary(OS); 367 } 368 return Error::success(); 369 } 370 371 // The implementation of makeRelocationInfo and makeScatteredRelocationInfo is 372 // consistent with how libObject parses MachO binary files. For the reference 373 // see getStruct, getRelocation, getPlainRelocationPCRel, 374 // getPlainRelocationLength and related methods in MachOObjectFile.cpp 375 static MachO::any_relocation_info 376 makeRelocationInfo(const MachOYAML::Relocation &R, bool IsLE) { 377 assert(!R.is_scattered && "non-scattered relocation expected"); 378 MachO::any_relocation_info MRE; 379 MRE.r_word0 = R.address; 380 if (IsLE) 381 MRE.r_word1 = ((unsigned)R.symbolnum << 0) | ((unsigned)R.is_pcrel << 24) | 382 ((unsigned)R.length << 25) | ((unsigned)R.is_extern << 27) | 383 ((unsigned)R.type << 28); 384 else 385 MRE.r_word1 = ((unsigned)R.symbolnum << 8) | ((unsigned)R.is_pcrel << 7) | 386 ((unsigned)R.length << 5) | ((unsigned)R.is_extern << 4) | 387 ((unsigned)R.type << 0); 388 return MRE; 389 } 390 391 static MachO::any_relocation_info 392 makeScatteredRelocationInfo(const MachOYAML::Relocation &R) { 393 assert(R.is_scattered && "scattered relocation expected"); 394 MachO::any_relocation_info MRE; 395 MRE.r_word0 = (((unsigned)R.address << 0) | ((unsigned)R.type << 24) | 396 ((unsigned)R.length << 28) | ((unsigned)R.is_pcrel << 30) | 397 MachO::R_SCATTERED); 398 MRE.r_word1 = R.value; 399 return MRE; 400 } 401 402 void MachOWriter::writeRelocations(raw_ostream &OS) { 403 for (const MachOYAML::LoadCommand &LC : Obj.LoadCommands) { 404 switch (LC.Data.load_command_data.cmd) { 405 case MachO::LC_SEGMENT: 406 case MachO::LC_SEGMENT_64: 407 for (const MachOYAML::Section &Sec : LC.Sections) { 408 if (Sec.relocations.empty()) 409 continue; 410 ZeroToOffset(OS, Sec.reloff); 411 for (const MachOYAML::Relocation &R : Sec.relocations) { 412 MachO::any_relocation_info MRE = 413 R.is_scattered ? makeScatteredRelocationInfo(R) 414 : makeRelocationInfo(R, Obj.IsLittleEndian); 415 if (Obj.IsLittleEndian != sys::IsLittleEndianHost) 416 MachO::swapStruct(MRE); 417 OS.write(reinterpret_cast<const char *>(&MRE), 418 sizeof(MachO::any_relocation_info)); 419 } 420 } 421 } 422 } 423 } 424 425 void MachOWriter::writeBindOpcodes( 426 raw_ostream &OS, std::vector<MachOYAML::BindOpcode> &BindOpcodes) { 427 428 for (const auto &Opcode : BindOpcodes) { 429 uint8_t OpByte = Opcode.Opcode | Opcode.Imm; 430 OS.write(reinterpret_cast<char *>(&OpByte), 1); 431 for (auto Data : Opcode.ULEBExtraData) { 432 encodeULEB128(Data, OS); 433 } 434 for (auto Data : Opcode.SLEBExtraData) { 435 encodeSLEB128(Data, OS); 436 } 437 if (!Opcode.Symbol.empty()) { 438 OS.write(Opcode.Symbol.data(), Opcode.Symbol.size()); 439 OS.write('\0'); 440 } 441 } 442 } 443 444 void MachOWriter::dumpExportEntry(raw_ostream &OS, 445 MachOYAML::ExportEntry &Entry) { 446 encodeULEB128(Entry.TerminalSize, OS); 447 if (Entry.TerminalSize > 0) { 448 encodeULEB128(Entry.Flags, OS); 449 if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT) { 450 encodeULEB128(Entry.Other, OS); 451 OS << Entry.ImportName; 452 OS.write('\0'); 453 } else { 454 encodeULEB128(Entry.Address, OS); 455 if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) 456 encodeULEB128(Entry.Other, OS); 457 } 458 } 459 OS.write(static_cast<uint8_t>(Entry.Children.size())); 460 for (const auto &EE : Entry.Children) { 461 OS << EE.Name; 462 OS.write('\0'); 463 encodeULEB128(EE.NodeOffset, OS); 464 } 465 for (auto EE : Entry.Children) 466 dumpExportEntry(OS, EE); 467 } 468 469 void MachOWriter::writeExportTrie(raw_ostream &OS) { 470 dumpExportEntry(OS, Obj.LinkEdit.ExportTrie); 471 } 472 473 template <typename NListType> 474 void writeNListEntry(MachOYAML::NListEntry &NLE, raw_ostream &OS, 475 bool IsLittleEndian) { 476 NListType ListEntry; 477 ListEntry.n_strx = NLE.n_strx; 478 ListEntry.n_type = NLE.n_type; 479 ListEntry.n_sect = NLE.n_sect; 480 ListEntry.n_desc = NLE.n_desc; 481 ListEntry.n_value = NLE.n_value; 482 483 if (IsLittleEndian != sys::IsLittleEndianHost) 484 MachO::swapStruct(ListEntry); 485 OS.write(reinterpret_cast<const char *>(&ListEntry), sizeof(NListType)); 486 } 487 488 void MachOWriter::writeLinkEditData(raw_ostream &OS) { 489 typedef void (MachOWriter::*writeHandler)(raw_ostream &); 490 typedef std::pair<uint64_t, writeHandler> writeOperation; 491 std::vector<writeOperation> WriteQueue; 492 493 MachO::dyld_info_command *DyldInfoOnlyCmd = nullptr; 494 MachO::symtab_command *SymtabCmd = nullptr; 495 MachO::dysymtab_command *DSymtabCmd = nullptr; 496 MachO::linkedit_data_command *FunctionStartsCmd = nullptr; 497 MachO::linkedit_data_command *ChainedFixupsCmd = nullptr; 498 MachO::linkedit_data_command *DyldExportsTrieCmd = nullptr; 499 MachO::linkedit_data_command *DataInCodeCmd = nullptr; 500 for (auto &LC : Obj.LoadCommands) { 501 switch (LC.Data.load_command_data.cmd) { 502 case MachO::LC_SYMTAB: 503 SymtabCmd = &LC.Data.symtab_command_data; 504 WriteQueue.push_back( 505 std::make_pair(SymtabCmd->symoff, &MachOWriter::writeNameList)); 506 WriteQueue.push_back( 507 std::make_pair(SymtabCmd->stroff, &MachOWriter::writeStringTable)); 508 break; 509 case MachO::LC_DYLD_INFO_ONLY: 510 DyldInfoOnlyCmd = &LC.Data.dyld_info_command_data; 511 WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->rebase_off, 512 &MachOWriter::writeRebaseOpcodes)); 513 WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->bind_off, 514 &MachOWriter::writeBasicBindOpcodes)); 515 WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->weak_bind_off, 516 &MachOWriter::writeWeakBindOpcodes)); 517 WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->lazy_bind_off, 518 &MachOWriter::writeLazyBindOpcodes)); 519 WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->export_off, 520 &MachOWriter::writeExportTrie)); 521 break; 522 case MachO::LC_DYSYMTAB: 523 DSymtabCmd = &LC.Data.dysymtab_command_data; 524 WriteQueue.push_back(std::make_pair( 525 DSymtabCmd->indirectsymoff, &MachOWriter::writeDynamicSymbolTable)); 526 break; 527 case MachO::LC_FUNCTION_STARTS: 528 FunctionStartsCmd = &LC.Data.linkedit_data_command_data; 529 WriteQueue.push_back(std::make_pair(FunctionStartsCmd->dataoff, 530 &MachOWriter::writeFunctionStarts)); 531 break; 532 case MachO::LC_DYLD_CHAINED_FIXUPS: 533 ChainedFixupsCmd = &LC.Data.linkedit_data_command_data; 534 WriteQueue.push_back(std::make_pair(ChainedFixupsCmd->dataoff, 535 &MachOWriter::writeChainedFixups)); 536 break; 537 case MachO::LC_DYLD_EXPORTS_TRIE: 538 DyldExportsTrieCmd = &LC.Data.linkedit_data_command_data; 539 WriteQueue.push_back(std::make_pair(DyldExportsTrieCmd->dataoff, 540 &MachOWriter::writeDyldExportsTrie)); 541 break; 542 case MachO::LC_DATA_IN_CODE: 543 DataInCodeCmd = &LC.Data.linkedit_data_command_data; 544 WriteQueue.push_back(std::make_pair(DataInCodeCmd->dataoff, 545 &MachOWriter::writeDataInCode)); 546 break; 547 } 548 } 549 550 llvm::sort(WriteQueue, llvm::less_first()); 551 552 for (auto writeOp : WriteQueue) { 553 ZeroToOffset(OS, writeOp.first); 554 (this->*writeOp.second)(OS); 555 } 556 } 557 558 void MachOWriter::writeRebaseOpcodes(raw_ostream &OS) { 559 MachOYAML::LinkEditData &LinkEdit = Obj.LinkEdit; 560 561 for (const auto &Opcode : LinkEdit.RebaseOpcodes) { 562 uint8_t OpByte = Opcode.Opcode | Opcode.Imm; 563 OS.write(reinterpret_cast<char *>(&OpByte), 1); 564 for (auto Data : Opcode.ExtraData) 565 encodeULEB128(Data, OS); 566 } 567 } 568 569 void MachOWriter::writeBasicBindOpcodes(raw_ostream &OS) { 570 writeBindOpcodes(OS, Obj.LinkEdit.BindOpcodes); 571 } 572 573 void MachOWriter::writeWeakBindOpcodes(raw_ostream &OS) { 574 writeBindOpcodes(OS, Obj.LinkEdit.WeakBindOpcodes); 575 } 576 577 void MachOWriter::writeLazyBindOpcodes(raw_ostream &OS) { 578 writeBindOpcodes(OS, Obj.LinkEdit.LazyBindOpcodes); 579 } 580 581 void MachOWriter::writeNameList(raw_ostream &OS) { 582 for (auto NLE : Obj.LinkEdit.NameList) { 583 if (is64Bit) 584 writeNListEntry<MachO::nlist_64>(NLE, OS, Obj.IsLittleEndian); 585 else 586 writeNListEntry<MachO::nlist>(NLE, OS, Obj.IsLittleEndian); 587 } 588 } 589 590 void MachOWriter::writeStringTable(raw_ostream &OS) { 591 for (auto Str : Obj.LinkEdit.StringTable) { 592 OS.write(Str.data(), Str.size()); 593 OS.write('\0'); 594 } 595 } 596 597 void MachOWriter::writeDynamicSymbolTable(raw_ostream &OS) { 598 for (auto Data : Obj.LinkEdit.IndirectSymbols) 599 OS.write(reinterpret_cast<const char *>(&Data), 600 sizeof(yaml::Hex32::BaseType)); 601 } 602 603 void MachOWriter::writeFunctionStarts(raw_ostream &OS) { 604 uint64_t Addr = 0; 605 for (uint64_t NextAddr : Obj.LinkEdit.FunctionStarts) { 606 uint64_t Delta = NextAddr - Addr; 607 encodeULEB128(Delta, OS); 608 Addr = NextAddr; 609 } 610 611 OS.write('\0'); 612 } 613 614 void MachOWriter::writeDataInCode(raw_ostream &OS) { 615 for (const auto &Entry : Obj.LinkEdit.DataInCode) { 616 MachO::data_in_code_entry DICE{Entry.Offset, Entry.Length, Entry.Kind}; 617 if (Obj.IsLittleEndian != sys::IsLittleEndianHost) 618 MachO::swapStruct(DICE); 619 OS.write(reinterpret_cast<const char *>(&DICE), 620 sizeof(MachO::data_in_code_entry)); 621 } 622 } 623 624 void MachOWriter::writeChainedFixups(raw_ostream &OS) { 625 if (Obj.LinkEdit.ChainedFixups.size() > 0) 626 OS.write(reinterpret_cast<const char *>(Obj.LinkEdit.ChainedFixups.data()), 627 Obj.LinkEdit.ChainedFixups.size()); 628 } 629 630 void MachOWriter::writeDyldExportsTrie(raw_ostream &OS) { 631 dumpExportEntry(OS, Obj.LinkEdit.ExportTrie); 632 } 633 634 class UniversalWriter { 635 public: 636 UniversalWriter(yaml::YamlObjectFile &ObjectFile) 637 : ObjectFile(ObjectFile), fileStart(0) {} 638 639 Error writeMachO(raw_ostream &OS); 640 641 private: 642 void writeFatHeader(raw_ostream &OS); 643 void writeFatArchs(raw_ostream &OS); 644 645 void ZeroToOffset(raw_ostream &OS, size_t offset); 646 647 yaml::YamlObjectFile &ObjectFile; 648 uint64_t fileStart; 649 }; 650 651 Error UniversalWriter::writeMachO(raw_ostream &OS) { 652 fileStart = OS.tell(); 653 if (ObjectFile.MachO) { 654 MachOWriter Writer(*ObjectFile.MachO); 655 return Writer.writeMachO(OS); 656 } 657 658 writeFatHeader(OS); 659 writeFatArchs(OS); 660 661 auto &FatFile = *ObjectFile.FatMachO; 662 if (FatFile.FatArchs.size() < FatFile.Slices.size()) 663 return createStringError( 664 errc::invalid_argument, 665 "cannot write 'Slices' if not described in 'FatArches'"); 666 667 for (size_t i = 0; i < FatFile.Slices.size(); i++) { 668 ZeroToOffset(OS, FatFile.FatArchs[i].offset); 669 MachOWriter Writer(FatFile.Slices[i]); 670 if (Error Err = Writer.writeMachO(OS)) 671 return Err; 672 673 auto SliceEnd = FatFile.FatArchs[i].offset + FatFile.FatArchs[i].size; 674 ZeroToOffset(OS, SliceEnd); 675 } 676 677 return Error::success(); 678 } 679 680 void UniversalWriter::writeFatHeader(raw_ostream &OS) { 681 auto &FatFile = *ObjectFile.FatMachO; 682 MachO::fat_header header; 683 header.magic = FatFile.Header.magic; 684 header.nfat_arch = FatFile.Header.nfat_arch; 685 if (sys::IsLittleEndianHost) 686 swapStruct(header); 687 OS.write(reinterpret_cast<const char *>(&header), sizeof(MachO::fat_header)); 688 } 689 690 template <typename FatArchType> 691 FatArchType constructFatArch(MachOYAML::FatArch &Arch) { 692 FatArchType FatArch; 693 FatArch.cputype = Arch.cputype; 694 FatArch.cpusubtype = Arch.cpusubtype; 695 FatArch.offset = Arch.offset; 696 FatArch.size = Arch.size; 697 FatArch.align = Arch.align; 698 return FatArch; 699 } 700 701 template <typename StructType> 702 void writeFatArch(MachOYAML::FatArch &LC, raw_ostream &OS) {} 703 704 template <> 705 void writeFatArch<MachO::fat_arch>(MachOYAML::FatArch &Arch, raw_ostream &OS) { 706 auto FatArch = constructFatArch<MachO::fat_arch>(Arch); 707 if (sys::IsLittleEndianHost) 708 swapStruct(FatArch); 709 OS.write(reinterpret_cast<const char *>(&FatArch), sizeof(MachO::fat_arch)); 710 } 711 712 template <> 713 void writeFatArch<MachO::fat_arch_64>(MachOYAML::FatArch &Arch, 714 raw_ostream &OS) { 715 auto FatArch = constructFatArch<MachO::fat_arch_64>(Arch); 716 FatArch.reserved = Arch.reserved; 717 if (sys::IsLittleEndianHost) 718 swapStruct(FatArch); 719 OS.write(reinterpret_cast<const char *>(&FatArch), 720 sizeof(MachO::fat_arch_64)); 721 } 722 723 void UniversalWriter::writeFatArchs(raw_ostream &OS) { 724 auto &FatFile = *ObjectFile.FatMachO; 725 bool is64Bit = FatFile.Header.magic == MachO::FAT_MAGIC_64; 726 for (auto Arch : FatFile.FatArchs) { 727 if (is64Bit) 728 writeFatArch<MachO::fat_arch_64>(Arch, OS); 729 else 730 writeFatArch<MachO::fat_arch>(Arch, OS); 731 } 732 } 733 734 void UniversalWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) { 735 auto currOffset = OS.tell() - fileStart; 736 if (currOffset < Offset) 737 ZeroFillBytes(OS, Offset - currOffset); 738 } 739 740 } // end anonymous namespace 741 742 namespace llvm { 743 namespace yaml { 744 745 bool yaml2macho(YamlObjectFile &Doc, raw_ostream &Out, ErrorHandler EH) { 746 UniversalWriter Writer(Doc); 747 if (Error Err = Writer.writeMachO(Out)) { 748 handleAllErrors(std::move(Err), 749 [&](const ErrorInfoBase &Err) { EH(Err.message()); }); 750 return false; 751 } 752 return true; 753 } 754 755 } // namespace yaml 756 } // namespace llvm 757