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