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