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