xref: /freebsd/contrib/llvm-project/llvm/lib/ObjectYAML/MachOEmitter.cpp (revision 9f23cbd6cae82fd77edfad7173432fa8dccd0a95)
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