brintos

brintos / llvm-project-archived public Read only

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