1 //===------ macho2yaml.cpp - obj2yaml conversion tool -----------*- C++ -*-===//
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 #include "obj2yaml.h"
10 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
11 #include "llvm/Object/MachOUniversal.h"
12 #include "llvm/ObjectYAML/DWARFYAML.h"
13 #include "llvm/ObjectYAML/ObjectYAML.h"
14 #include "llvm/Support/Error.h"
15 #include "llvm/Support/ErrorHandling.h"
16 #include "llvm/Support/LEB128.h"
17 
18 #include <string.h> // for memcpy
19 
20 using namespace llvm;
21 
22 class MachODumper {
23 
24   template <typename StructType>
25   Expected<const char *> processLoadCommandData(
26       MachOYAML::LoadCommand &LC,
27       const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
28       MachOYAML::Object &Y);
29 
30   const object::MachOObjectFile &Obj;
31   std::unique_ptr<DWARFContext> DWARFCtx;
32   unsigned RawSegment;
33   void dumpHeader(std::unique_ptr<MachOYAML::Object> &Y);
34   Error dumpLoadCommands(std::unique_ptr<MachOYAML::Object> &Y);
35   void dumpLinkEdit(std::unique_ptr<MachOYAML::Object> &Y);
36   void dumpRebaseOpcodes(std::unique_ptr<MachOYAML::Object> &Y);
37   void dumpBindOpcodes(std::vector<MachOYAML::BindOpcode> &BindOpcodes,
38                        ArrayRef<uint8_t> OpcodeBuffer, bool Lazy = false);
39   void dumpExportTrie(std::unique_ptr<MachOYAML::Object> &Y);
40   void dumpSymbols(std::unique_ptr<MachOYAML::Object> &Y);
41 
42   template <typename SectionType>
43   Expected<MachOYAML::Section> constructSectionCommon(SectionType Sec,
44                                                       size_t SecIndex);
45   template <typename SectionType>
46   Expected<MachOYAML::Section> constructSection(SectionType Sec,
47                                                 size_t SecIndex);
48   template <typename SectionType, typename SegmentType>
49   Expected<const char *>
50   extractSections(const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
51                   std::vector<MachOYAML::Section> &Sections,
52                   MachOYAML::Object &Y);
53 
54 public:
55   MachODumper(const object::MachOObjectFile &O,
56               std::unique_ptr<DWARFContext> DCtx, unsigned RawSegments)
57       : Obj(O), DWARFCtx(std::move(DCtx)), RawSegment(RawSegments) {}
58   Expected<std::unique_ptr<MachOYAML::Object>> dump();
59 };
60 
61 #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct)                         \
62   case MachO::LCName:                                                          \
63     memcpy((void *)&(LC.Data.LCStruct##_data), LoadCmd.Ptr,                    \
64            sizeof(MachO::LCStruct));                                           \
65     if (Obj.isLittleEndian() != sys::IsLittleEndianHost)                       \
66       MachO::swapStruct(LC.Data.LCStruct##_data);                              \
67     if (Expected<const char *> ExpectedEndPtr =                                \
68             processLoadCommandData<MachO::LCStruct>(LC, LoadCmd, *Y.get()))    \
69       EndPtr = *ExpectedEndPtr;                                                \
70     else                                                                       \
71       return ExpectedEndPtr.takeError();                                       \
72     break;
73 
74 template <typename SectionType>
75 Expected<MachOYAML::Section>
76 MachODumper::constructSectionCommon(SectionType Sec, size_t SecIndex) {
77   MachOYAML::Section TempSec;
78   memcpy(reinterpret_cast<void *>(&TempSec.sectname[0]), &Sec.sectname[0], 16);
79   memcpy(reinterpret_cast<void *>(&TempSec.segname[0]), &Sec.segname[0], 16);
80   TempSec.addr = Sec.addr;
81   TempSec.size = Sec.size;
82   TempSec.offset = Sec.offset;
83   TempSec.align = Sec.align;
84   TempSec.reloff = Sec.reloff;
85   TempSec.nreloc = Sec.nreloc;
86   TempSec.flags = Sec.flags;
87   TempSec.reserved1 = Sec.reserved1;
88   TempSec.reserved2 = Sec.reserved2;
89   TempSec.reserved3 = 0;
90   if (!MachO::isVirtualSection(Sec.flags & MachO::SECTION_TYPE))
91     TempSec.content =
92         yaml::BinaryRef(Obj.getSectionContents(Sec.offset, Sec.size));
93 
94   if (Expected<object::SectionRef> SecRef = Obj.getSection(SecIndex)) {
95     TempSec.relocations.reserve(TempSec.nreloc);
96     for (const object::RelocationRef &Reloc : SecRef->relocations()) {
97       const object::DataRefImpl Rel = Reloc.getRawDataRefImpl();
98       const MachO::any_relocation_info RE = Obj.getRelocation(Rel);
99       MachOYAML::Relocation R;
100       R.address = Obj.getAnyRelocationAddress(RE);
101       R.is_pcrel = Obj.getAnyRelocationPCRel(RE);
102       R.length = Obj.getAnyRelocationLength(RE);
103       R.type = Obj.getAnyRelocationType(RE);
104       R.is_scattered = Obj.isRelocationScattered(RE);
105       R.symbolnum = (R.is_scattered ? 0 : Obj.getPlainRelocationSymbolNum(RE));
106       R.is_extern =
107           (R.is_scattered ? false : Obj.getPlainRelocationExternal(RE));
108       R.value = (R.is_scattered ? Obj.getScatteredRelocationValue(RE) : 0);
109       TempSec.relocations.push_back(R);
110     }
111   } else {
112     return SecRef.takeError();
113   }
114   return TempSec;
115 }
116 
117 template <>
118 Expected<MachOYAML::Section> MachODumper::constructSection(MachO::section Sec,
119                                                            size_t SecIndex) {
120   Expected<MachOYAML::Section> TempSec = constructSectionCommon(Sec, SecIndex);
121   if (TempSec)
122     TempSec->reserved3 = 0;
123   return TempSec;
124 }
125 
126 template <>
127 Expected<MachOYAML::Section>
128 MachODumper::constructSection(MachO::section_64 Sec, size_t SecIndex) {
129   Expected<MachOYAML::Section> TempSec = constructSectionCommon(Sec, SecIndex);
130   if (TempSec)
131     TempSec->reserved3 = Sec.reserved3;
132   return TempSec;
133 }
134 
135 static Error dumpDebugSection(StringRef SecName, DWARFContext &DCtx,
136                               DWARFYAML::Data &DWARF) {
137   if (SecName == "__debug_abbrev") {
138     dumpDebugAbbrev(DCtx, DWARF);
139     return Error::success();
140   }
141   if (SecName == "__debug_aranges")
142     return dumpDebugARanges(DCtx, DWARF);
143   if (SecName == "__debug_info") {
144     dumpDebugInfo(DCtx, DWARF);
145     return Error::success();
146   }
147   if (SecName == "__debug_line") {
148     dumpDebugLines(DCtx, DWARF);
149     return Error::success();
150   }
151   if (SecName.startswith("__debug_pub")) {
152     // FIXME: We should extract pub-section dumpers from this function.
153     dumpDebugPubSections(DCtx, DWARF);
154     return Error::success();
155   }
156   if (SecName == "__debug_ranges")
157     return dumpDebugRanges(DCtx, DWARF);
158   if (SecName == "__debug_str")
159     return dumpDebugStrings(DCtx, DWARF);
160   return createStringError(errc::not_supported,
161                            "dumping " + SecName + " section is not supported");
162 }
163 
164 template <typename SectionType, typename SegmentType>
165 Expected<const char *> MachODumper::extractSections(
166     const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
167     std::vector<MachOYAML::Section> &Sections, MachOYAML::Object &Y) {
168   auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize;
169   const SectionType *Curr =
170       reinterpret_cast<const SectionType *>(LoadCmd.Ptr + sizeof(SegmentType));
171   for (; reinterpret_cast<const void *>(Curr) < End; Curr++) {
172     SectionType Sec;
173     memcpy((void *)&Sec, Curr, sizeof(SectionType));
174     if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
175       MachO::swapStruct(Sec);
176     // For MachO section indices start from 1.
177     if (Expected<MachOYAML::Section> S =
178             constructSection(Sec, Sections.size() + 1)) {
179       StringRef SecName(S->sectname);
180 
181       // Copy data sections if requested.
182       if ((RawSegment & ::RawSegments::data) &&
183           StringRef(S->segname).startswith("__DATA"))
184         S->content =
185             yaml::BinaryRef(Obj.getSectionContents(Sec.offset, Sec.size));
186 
187       if (SecName.startswith("__debug_")) {
188         // If the DWARF section cannot be successfully parsed, emit raw content
189         // instead of an entry in the DWARF section of the YAML.
190         if (Error Err = dumpDebugSection(SecName, *DWARFCtx.get(), Y.DWARF))
191           consumeError(std::move(Err));
192         else
193           S->content.reset();
194       }
195       Sections.push_back(std::move(*S));
196     } else
197       return S.takeError();
198   }
199   return reinterpret_cast<const char *>(Curr);
200 }
201 
202 template <typename StructType>
203 Expected<const char *> MachODumper::processLoadCommandData(
204     MachOYAML::LoadCommand &LC,
205     const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
206     MachOYAML::Object &Y) {
207   return LoadCmd.Ptr + sizeof(StructType);
208 }
209 
210 template <>
211 Expected<const char *>
212 MachODumper::processLoadCommandData<MachO::segment_command>(
213     MachOYAML::LoadCommand &LC,
214     const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
215     MachOYAML::Object &Y) {
216   return extractSections<MachO::section, MachO::segment_command>(
217       LoadCmd, LC.Sections, Y);
218 }
219 
220 template <>
221 Expected<const char *>
222 MachODumper::processLoadCommandData<MachO::segment_command_64>(
223     MachOYAML::LoadCommand &LC,
224     const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
225     MachOYAML::Object &Y) {
226   return extractSections<MachO::section_64, MachO::segment_command_64>(
227       LoadCmd, LC.Sections, Y);
228 }
229 
230 template <typename StructType>
231 const char *
232 readString(MachOYAML::LoadCommand &LC,
233            const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
234   auto Start = LoadCmd.Ptr + sizeof(StructType);
235   auto MaxSize = LoadCmd.C.cmdsize - sizeof(StructType);
236   auto Size = strnlen(Start, MaxSize);
237   LC.Content = StringRef(Start, Size).str();
238   return Start + Size;
239 }
240 
241 template <>
242 Expected<const char *>
243 MachODumper::processLoadCommandData<MachO::dylib_command>(
244     MachOYAML::LoadCommand &LC,
245     const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
246     MachOYAML::Object &Y) {
247   return readString<MachO::dylib_command>(LC, LoadCmd);
248 }
249 
250 template <>
251 Expected<const char *>
252 MachODumper::processLoadCommandData<MachO::dylinker_command>(
253     MachOYAML::LoadCommand &LC,
254     const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
255     MachOYAML::Object &Y) {
256   return readString<MachO::dylinker_command>(LC, LoadCmd);
257 }
258 
259 template <>
260 Expected<const char *>
261 MachODumper::processLoadCommandData<MachO::rpath_command>(
262     MachOYAML::LoadCommand &LC,
263     const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
264     MachOYAML::Object &Y) {
265   return readString<MachO::rpath_command>(LC, LoadCmd);
266 }
267 
268 template <>
269 Expected<const char *>
270 MachODumper::processLoadCommandData<MachO::build_version_command>(
271     MachOYAML::LoadCommand &LC,
272     const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
273     MachOYAML::Object &Y) {
274   auto Start = LoadCmd.Ptr + sizeof(MachO::build_version_command);
275   auto NTools = LC.Data.build_version_command_data.ntools;
276   for (unsigned i = 0; i < NTools; ++i) {
277     auto Curr = Start + i * sizeof(MachO::build_tool_version);
278     MachO::build_tool_version BV;
279     memcpy((void *)&BV, Curr, sizeof(MachO::build_tool_version));
280     if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
281       MachO::swapStruct(BV);
282     LC.Tools.push_back(BV);
283   }
284   return Start + NTools * sizeof(MachO::build_tool_version);
285 }
286 
287 Expected<std::unique_ptr<MachOYAML::Object>> MachODumper::dump() {
288   auto Y = std::make_unique<MachOYAML::Object>();
289   Y->IsLittleEndian = Obj.isLittleEndian();
290   dumpHeader(Y);
291   if (Error Err = dumpLoadCommands(Y))
292     return std::move(Err);
293   if (RawSegment & ::RawSegments::linkedit)
294     Y->RawLinkEditSegment =
295         yaml::BinaryRef(Obj.getSegmentContents("__LINKEDIT"));
296   else
297     dumpLinkEdit(Y);
298 
299   return std::move(Y);
300 }
301 
302 void MachODumper::dumpHeader(std::unique_ptr<MachOYAML::Object> &Y) {
303   Y->Header.magic = Obj.getHeader().magic;
304   Y->Header.cputype = Obj.getHeader().cputype;
305   Y->Header.cpusubtype = Obj.getHeader().cpusubtype;
306   Y->Header.filetype = Obj.getHeader().filetype;
307   Y->Header.ncmds = Obj.getHeader().ncmds;
308   Y->Header.sizeofcmds = Obj.getHeader().sizeofcmds;
309   Y->Header.flags = Obj.getHeader().flags;
310   Y->Header.reserved = 0;
311 }
312 
313 Error MachODumper::dumpLoadCommands(std::unique_ptr<MachOYAML::Object> &Y) {
314   for (auto LoadCmd : Obj.load_commands()) {
315     MachOYAML::LoadCommand LC;
316     const char *EndPtr = LoadCmd.Ptr;
317     switch (LoadCmd.C.cmd) {
318     default:
319       memcpy((void *)&(LC.Data.load_command_data), LoadCmd.Ptr,
320              sizeof(MachO::load_command));
321       if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
322         MachO::swapStruct(LC.Data.load_command_data);
323       if (Expected<const char *> ExpectedEndPtr =
324               processLoadCommandData<MachO::load_command>(LC, LoadCmd,
325                                                           *Y.get()))
326         EndPtr = *ExpectedEndPtr;
327       else
328         return ExpectedEndPtr.takeError();
329       break;
330 #include "llvm/BinaryFormat/MachO.def"
331     }
332     auto RemainingBytes = LoadCmd.C.cmdsize - (EndPtr - LoadCmd.Ptr);
333     if (!std::all_of(EndPtr, &EndPtr[RemainingBytes],
334                      [](const char C) { return C == 0; })) {
335       LC.PayloadBytes.insert(LC.PayloadBytes.end(), EndPtr,
336                              &EndPtr[RemainingBytes]);
337       RemainingBytes = 0;
338     }
339     LC.ZeroPadBytes = RemainingBytes;
340     Y->LoadCommands.push_back(std::move(LC));
341   }
342   return Error::success();
343 }
344 
345 void MachODumper::dumpLinkEdit(std::unique_ptr<MachOYAML::Object> &Y) {
346   dumpRebaseOpcodes(Y);
347   dumpBindOpcodes(Y->LinkEdit.BindOpcodes, Obj.getDyldInfoBindOpcodes());
348   dumpBindOpcodes(Y->LinkEdit.WeakBindOpcodes,
349                   Obj.getDyldInfoWeakBindOpcodes());
350   dumpBindOpcodes(Y->LinkEdit.LazyBindOpcodes, Obj.getDyldInfoLazyBindOpcodes(),
351                   true);
352   dumpExportTrie(Y);
353   dumpSymbols(Y);
354 }
355 
356 void MachODumper::dumpRebaseOpcodes(std::unique_ptr<MachOYAML::Object> &Y) {
357   MachOYAML::LinkEditData &LEData = Y->LinkEdit;
358 
359   auto RebaseOpcodes = Obj.getDyldInfoRebaseOpcodes();
360   for (auto OpCode = RebaseOpcodes.begin(); OpCode != RebaseOpcodes.end();
361        ++OpCode) {
362     MachOYAML::RebaseOpcode RebaseOp;
363     RebaseOp.Opcode =
364         static_cast<MachO::RebaseOpcode>(*OpCode & MachO::REBASE_OPCODE_MASK);
365     RebaseOp.Imm = *OpCode & MachO::REBASE_IMMEDIATE_MASK;
366 
367     unsigned Count;
368     uint64_t ULEB = 0;
369 
370     switch (RebaseOp.Opcode) {
371     case MachO::REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
372 
373       ULEB = decodeULEB128(OpCode + 1, &Count);
374       RebaseOp.ExtraData.push_back(ULEB);
375       OpCode += Count;
376       LLVM_FALLTHROUGH;
377     // Intentionally no break here -- This opcode has two ULEB values
378     case MachO::REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
379     case MachO::REBASE_OPCODE_ADD_ADDR_ULEB:
380     case MachO::REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
381     case MachO::REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
382 
383       ULEB = decodeULEB128(OpCode + 1, &Count);
384       RebaseOp.ExtraData.push_back(ULEB);
385       OpCode += Count;
386       break;
387     default:
388       break;
389     }
390 
391     LEData.RebaseOpcodes.push_back(RebaseOp);
392 
393     if (RebaseOp.Opcode == MachO::REBASE_OPCODE_DONE)
394       break;
395   }
396 }
397 
398 StringRef ReadStringRef(const uint8_t *Start) {
399   const uint8_t *Itr = Start;
400   for (; *Itr; ++Itr)
401     ;
402   return StringRef(reinterpret_cast<const char *>(Start), Itr - Start);
403 }
404 
405 void MachODumper::dumpBindOpcodes(
406     std::vector<MachOYAML::BindOpcode> &BindOpcodes,
407     ArrayRef<uint8_t> OpcodeBuffer, bool Lazy) {
408   for (auto OpCode = OpcodeBuffer.begin(); OpCode != OpcodeBuffer.end();
409        ++OpCode) {
410     MachOYAML::BindOpcode BindOp;
411     BindOp.Opcode =
412         static_cast<MachO::BindOpcode>(*OpCode & MachO::BIND_OPCODE_MASK);
413     BindOp.Imm = *OpCode & MachO::BIND_IMMEDIATE_MASK;
414 
415     unsigned Count;
416     uint64_t ULEB = 0;
417     int64_t SLEB = 0;
418 
419     switch (BindOp.Opcode) {
420     case MachO::BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
421       ULEB = decodeULEB128(OpCode + 1, &Count);
422       BindOp.ULEBExtraData.push_back(ULEB);
423       OpCode += Count;
424       LLVM_FALLTHROUGH;
425     // Intentionally no break here -- this opcode has two ULEB values
426 
427     case MachO::BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
428     case MachO::BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
429     case MachO::BIND_OPCODE_ADD_ADDR_ULEB:
430     case MachO::BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
431       ULEB = decodeULEB128(OpCode + 1, &Count);
432       BindOp.ULEBExtraData.push_back(ULEB);
433       OpCode += Count;
434       break;
435 
436     case MachO::BIND_OPCODE_SET_ADDEND_SLEB:
437       SLEB = decodeSLEB128(OpCode + 1, &Count);
438       BindOp.SLEBExtraData.push_back(SLEB);
439       OpCode += Count;
440       break;
441 
442     case MachO::BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
443       BindOp.Symbol = ReadStringRef(OpCode + 1);
444       OpCode += BindOp.Symbol.size() + 1;
445       break;
446     default:
447       break;
448     }
449 
450     BindOpcodes.push_back(BindOp);
451 
452     // Lazy bindings have DONE opcodes between operations, so we need to keep
453     // processing after a DONE.
454     if (!Lazy && BindOp.Opcode == MachO::BIND_OPCODE_DONE)
455       break;
456   }
457 }
458 
459 /*!
460  * /brief processes a node from the export trie, and its children.
461  *
462  * To my knowledge there is no documentation of the encoded format of this data
463  * other than in the heads of the Apple linker engineers. To that end hopefully
464  * this comment and the implementation below can serve to light the way for
465  * anyone crazy enough to come down this path in the future.
466  *
467  * This function reads and preserves the trie structure of the export trie. To
468  * my knowledge there is no code anywhere else that reads the data and preserves
469  * the Trie. LD64 (sources available at opensource.apple.com) has a similar
470  * implementation that parses the export trie into a vector. That code as well
471  * as LLVM's libObject MachO implementation were the basis for this.
472  *
473  * The export trie is an encoded trie. The node serialization is a bit awkward.
474  * The below pseudo-code is the best description I've come up with for it.
475  *
476  * struct SerializedNode {
477  *   ULEB128 TerminalSize;
478  *   struct TerminalData { <-- This is only present if TerminalSize > 0
479  *     ULEB128 Flags;
480  *     ULEB128 Address; <-- Present if (! Flags & REEXPORT )
481  *     ULEB128 Other; <-- Present if ( Flags & REEXPORT ||
482  *                                     Flags & STUB_AND_RESOLVER )
483  *     char[] ImportName; <-- Present if ( Flags & REEXPORT )
484  *   }
485  *   uint8_t ChildrenCount;
486  *   Pair<char[], ULEB128> ChildNameOffsetPair[ChildrenCount];
487  *   SerializedNode Children[ChildrenCount]
488  * }
489  *
490  * Terminal nodes are nodes that represent actual exports. They can appear
491  * anywhere in the tree other than at the root; they do not need to be leaf
492  * nodes. When reading the data out of the trie this routine reads it in-order,
493  * but it puts the child names and offsets directly into the child nodes. This
494  * results in looping over the children twice during serialization and
495  * de-serialization, but it makes the YAML representation more human readable.
496  *
497  * Below is an example of the graph from a "Hello World" executable:
498  *
499  * -------
500  * | ''  |
501  * -------
502  *    |
503  * -------
504  * | '_' |
505  * -------
506  *    |
507  *    |----------------------------------------|
508  *    |                                        |
509  *  ------------------------      ---------------------
510  *  | '_mh_execute_header' |      | 'main'            |
511  *  | Flags: 0x00000000    |      | Flags: 0x00000000 |
512  *  | Addr:  0x00000000    |      | Addr:  0x00001160 |
513  *  ------------------------      ---------------------
514  *
515  * This graph represents the trie for the exports "__mh_execute_header" and
516  * "_main". In the graph only the "_main" and "__mh_execute_header" nodes are
517  * terminal.
518 */
519 
520 const uint8_t *processExportNode(const uint8_t *CurrPtr,
521                                  const uint8_t *const End,
522                                  MachOYAML::ExportEntry &Entry) {
523   if (CurrPtr >= End)
524     return CurrPtr;
525   unsigned Count = 0;
526   Entry.TerminalSize = decodeULEB128(CurrPtr, &Count);
527   CurrPtr += Count;
528   if (Entry.TerminalSize != 0) {
529     Entry.Flags = decodeULEB128(CurrPtr, &Count);
530     CurrPtr += Count;
531     if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT) {
532       Entry.Address = 0;
533       Entry.Other = decodeULEB128(CurrPtr, &Count);
534       CurrPtr += Count;
535       Entry.ImportName = std::string(reinterpret_cast<const char *>(CurrPtr));
536     } else {
537       Entry.Address = decodeULEB128(CurrPtr, &Count);
538       CurrPtr += Count;
539       if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) {
540         Entry.Other = decodeULEB128(CurrPtr, &Count);
541         CurrPtr += Count;
542       } else
543         Entry.Other = 0;
544     }
545   }
546   uint8_t childrenCount = *CurrPtr++;
547   if (childrenCount == 0)
548     return CurrPtr;
549 
550   Entry.Children.insert(Entry.Children.begin(), (size_t)childrenCount,
551                         MachOYAML::ExportEntry());
552   for (auto &Child : Entry.Children) {
553     Child.Name = std::string(reinterpret_cast<const char *>(CurrPtr));
554     CurrPtr += Child.Name.length() + 1;
555     Child.NodeOffset = decodeULEB128(CurrPtr, &Count);
556     CurrPtr += Count;
557   }
558   for (auto &Child : Entry.Children) {
559     CurrPtr = processExportNode(CurrPtr, End, Child);
560   }
561   return CurrPtr;
562 }
563 
564 void MachODumper::dumpExportTrie(std::unique_ptr<MachOYAML::Object> &Y) {
565   MachOYAML::LinkEditData &LEData = Y->LinkEdit;
566   auto ExportsTrie = Obj.getDyldInfoExportsTrie();
567   processExportNode(ExportsTrie.begin(), ExportsTrie.end(), LEData.ExportTrie);
568 }
569 
570 template <typename nlist_t>
571 MachOYAML::NListEntry constructNameList(const nlist_t &nlist) {
572   MachOYAML::NListEntry NL;
573   NL.n_strx = nlist.n_strx;
574   NL.n_type = nlist.n_type;
575   NL.n_sect = nlist.n_sect;
576   NL.n_desc = nlist.n_desc;
577   NL.n_value = nlist.n_value;
578   return NL;
579 }
580 
581 void MachODumper::dumpSymbols(std::unique_ptr<MachOYAML::Object> &Y) {
582   MachOYAML::LinkEditData &LEData = Y->LinkEdit;
583 
584   for (auto Symbol : Obj.symbols()) {
585     MachOYAML::NListEntry NLE =
586         Obj.is64Bit()
587             ? constructNameList<MachO::nlist_64>(
588                   Obj.getSymbol64TableEntry(Symbol.getRawDataRefImpl()))
589             : constructNameList<MachO::nlist>(
590                   Obj.getSymbolTableEntry(Symbol.getRawDataRefImpl()));
591     LEData.NameList.push_back(NLE);
592   }
593 
594   StringRef RemainingTable = Obj.getStringTableData();
595   while (RemainingTable.size() > 0) {
596     auto SymbolPair = RemainingTable.split('\0');
597     RemainingTable = SymbolPair.second;
598     LEData.StringTable.push_back(SymbolPair.first);
599   }
600 }
601 
602 Error macho2yaml(raw_ostream &Out, const object::MachOObjectFile &Obj,
603                  unsigned RawSegments) {
604   std::unique_ptr<DWARFContext> DCtx = DWARFContext::create(Obj);
605   MachODumper Dumper(Obj, std::move(DCtx), RawSegments);
606   Expected<std::unique_ptr<MachOYAML::Object>> YAML = Dumper.dump();
607   if (!YAML)
608     return YAML.takeError();
609 
610   yaml::YamlObjectFile YAMLFile;
611   YAMLFile.MachO = std::move(YAML.get());
612 
613   yaml::Output Yout(Out);
614   Yout << YAMLFile;
615   return Error::success();
616 }
617 
618 Error macho2yaml(raw_ostream &Out, const object::MachOUniversalBinary &Obj,
619                  unsigned RawSegments) {
620   yaml::YamlObjectFile YAMLFile;
621   YAMLFile.FatMachO.reset(new MachOYAML::UniversalBinary());
622   MachOYAML::UniversalBinary &YAML = *YAMLFile.FatMachO;
623   YAML.Header.magic = Obj.getMagic();
624   YAML.Header.nfat_arch = Obj.getNumberOfObjects();
625 
626   for (auto Slice : Obj.objects()) {
627     MachOYAML::FatArch arch;
628     arch.cputype = Slice.getCPUType();
629     arch.cpusubtype = Slice.getCPUSubType();
630     arch.offset = Slice.getOffset();
631     arch.size = Slice.getSize();
632     arch.align = Slice.getAlign();
633     arch.reserved = Slice.getReserved();
634     YAML.FatArchs.push_back(arch);
635 
636     auto SliceObj = Slice.getAsObjectFile();
637     if (!SliceObj)
638       return SliceObj.takeError();
639 
640     std::unique_ptr<DWARFContext> DCtx = DWARFContext::create(*SliceObj.get());
641     MachODumper Dumper(*SliceObj.get(), std::move(DCtx), RawSegments);
642     Expected<std::unique_ptr<MachOYAML::Object>> YAMLObj = Dumper.dump();
643     if (!YAMLObj)
644       return YAMLObj.takeError();
645     YAML.Slices.push_back(*YAMLObj.get());
646   }
647 
648   yaml::Output Yout(Out);
649   Yout << YAML;
650   return Error::success();
651 }
652 
653 Error macho2yaml(raw_ostream &Out, const object::Binary &Binary,
654                  unsigned RawSegments) {
655   if (const auto *MachOObj = dyn_cast<object::MachOUniversalBinary>(&Binary))
656     return macho2yaml(Out, *MachOObj, RawSegments);
657 
658   if (const auto *MachOObj = dyn_cast<object::MachOObjectFile>(&Binary))
659     return macho2yaml(Out, *MachOObj, RawSegments);
660 
661   llvm_unreachable("unexpected Mach-O file format");
662 }
663