//===- yaml2macho - Convert YAML to a Mach object file --------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief The Mach component of yaml2obj. /// //===----------------------------------------------------------------------===// #include "yaml2obj.h" #include "llvm/ObjectYAML/MachOYAML.h" #include "llvm/Support/Error.h" #include "llvm/Support/MachO.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; namespace { class MachOWriter { public: MachOWriter(MachOYAML::Object &Obj) : Obj(Obj) { is64Bit = Obj.Header.magic == MachO::MH_MAGIC_64 || Obj.Header.magic == MachO::MH_CIGAM_64; memset(reinterpret_cast(&Header64), 0, sizeof(MachO::mach_header_64)); assert((is64Bit || Obj.Header.reserved == 0xDEADBEEFu) && "32-bit MachO has reserved in header"); assert((!is64Bit || Obj.Header.reserved != 0xDEADBEEFu) && "64-bit MachO has missing reserved in header"); } Error writeMachO(raw_ostream &OS); private: Error writeHeader(raw_ostream &OS); Error writeLoadCommands(raw_ostream &OS); MachOYAML::Object &Obj; bool is64Bit; union { MachO::mach_header_64 Header64; MachO::mach_header Header; }; }; Error MachOWriter::writeMachO(raw_ostream &OS) { if (auto Err = writeHeader(OS)) return Err; if (auto Err = writeLoadCommands(OS)) return Err; return Error::success(); } Error MachOWriter::writeHeader(raw_ostream &OS) { Header.magic = Obj.Header.magic; Header.cputype = Obj.Header.cputype; Header.cpusubtype = Obj.Header.cpusubtype; Header.filetype = Obj.Header.filetype; Header.ncmds = Obj.Header.ncmds; Header.sizeofcmds = Obj.Header.sizeofcmds; Header.flags = Obj.Header.flags; Header64.reserved = Obj.Header.reserved; if (is64Bit) OS.write((const char *)&Header64, sizeof(MachO::mach_header_64)); else OS.write((const char *)&Header, sizeof(MachO::mach_header)); return Error::success(); } template SectionType constructSection(MachOYAML::Section Sec) { SectionType TempSec; memcpy(reinterpret_cast(&TempSec.sectname[0]), &Sec.sectname[0], 16); memcpy(reinterpret_cast(&TempSec.segname[0]), &Sec.segname[0], 16); TempSec.addr = Sec.addr; TempSec.size = Sec.size; TempSec.offset = Sec.offset; TempSec.align = Sec.align; TempSec.reloff = Sec.reloff; TempSec.nreloc = Sec.nreloc; TempSec.flags = Sec.flags; TempSec.reserved1 = Sec.reserved1; TempSec.reserved2 = Sec.reserved2; return TempSec; } template size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, raw_ostream &OS) { return 0; } template <> size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, raw_ostream &OS) { size_t BytesWritten = 0; for (auto Sec : LC.Sections) { auto TempSec = constructSection(Sec); OS.write(reinterpret_cast(&(TempSec)), sizeof(MachO::section)); BytesWritten += sizeof(MachO::section); } return BytesWritten; } template <> size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, raw_ostream &OS) { size_t BytesWritten = 0; for (auto Sec : LC.Sections) { auto TempSec = constructSection(Sec); TempSec.reserved3 = Sec.reserved3; OS.write(reinterpret_cast(&(TempSec)), sizeof(MachO::section_64)); BytesWritten += sizeof(MachO::section_64); } return BytesWritten; } size_t writePayloadString(MachOYAML::LoadCommand &LC, raw_ostream &OS) { size_t BytesWritten = 0; if (!LC.PayloadString.empty()) { OS.write(LC.PayloadString.c_str(), LC.PayloadString.length()); BytesWritten = LC.PayloadString.length(); } return BytesWritten; } template <> size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, raw_ostream &OS) { return writePayloadString(LC, OS); } template <> size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, raw_ostream &OS) { return writePayloadString(LC, OS); } Error MachOWriter::writeLoadCommands(raw_ostream &OS) { for (auto &LC : Obj.LoadCommands) { size_t BytesWritten = 0; #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ case MachO::LCName: \ OS.write(reinterpret_cast(&(LC.Data.LCStruct##_data)), \ sizeof(MachO::LCStruct)); \ BytesWritten = sizeof(MachO::LCStruct); \ BytesWritten += writeLoadCommandData(LC, OS); \ break; switch (LC.Data.load_command_data.cmd) { default: OS.write(reinterpret_cast(&(LC.Data.load_command_data)), sizeof(MachO::load_command)); BytesWritten = sizeof(MachO::load_command); BytesWritten += writeLoadCommandData(LC, OS); break; #include "llvm/Support/MachO.def" } if (LC.PayloadBytes.size() > 0) { OS.write(reinterpret_cast(LC.PayloadBytes.data()), LC.PayloadBytes.size()); BytesWritten += LC.PayloadBytes.size(); } if (LC.ZeroPadBytes > 0) { std::vector FillData; FillData.insert(FillData.begin(), LC.ZeroPadBytes, 0); OS.write(reinterpret_cast(FillData.data()), LC.ZeroPadBytes); BytesWritten += LC.ZeroPadBytes; } // Fill remaining bytes with 0. This will only get hit in partially // specified test cases. auto BytesRemaining = LC.Data.load_command_data.cmdsize - BytesWritten; if (BytesRemaining > 0) { std::vector FillData; FillData.insert(FillData.begin(), BytesRemaining, 0); OS.write(reinterpret_cast(FillData.data()), BytesRemaining); } } return Error::success(); } } // end anonymous namespace int yaml2macho(yaml::Input &YIn, raw_ostream &Out) { MachOYAML::Object Doc; YIn >> Doc; if (YIn.error()) { errs() << "yaml2obj: Failed to parse YAML file!\n"; return 1; } MachOWriter Writer(Doc); if (auto Err = Writer.writeMachO(Out)) { errs() << toString(std::move(Err)); return 1; } return 0; }