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