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