1 //===------ macho2yaml.cpp - obj2yaml conversion tool -----------*- C++ -*-===//
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 #include "Error.h"
11 #include "obj2yaml.h"
12 #include "llvm/Object/MachOUniversal.h"
13 #include "llvm/ObjectYAML/MachOYAML.h"
14 #include "llvm/Support/ErrorHandling.h"
15 #include "llvm/Support/LEB128.h"
16 
17 #include <string.h> // for memcpy
18 
19 using namespace llvm;
20 
21 class MachODumper {
22 
23   template <typename StructType>
24   const char *processLoadCommandData(
25       MachOYAML::LoadCommand &LC,
26       const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd);
27 
28   const object::MachOObjectFile &Obj;
29   void dumpHeader(std::unique_ptr<MachOYAML::Object> &Y);
30   void dumpLoadCommands(std::unique_ptr<MachOYAML::Object> &Y);
31   void dumpLinkEdit(std::unique_ptr<MachOYAML::Object> &Y);
32   void dumpRebaseOpcodes(std::unique_ptr<MachOYAML::Object> &Y);
33   void dumpBindOpcodes(std::vector<MachOYAML::BindOpcode> &BindOpcodes,
34                        ArrayRef<uint8_t> OpcodeBuffer, bool Lazy = false);
35 
36 public:
37   MachODumper(const object::MachOObjectFile &O) : Obj(O) {}
38   Expected<std::unique_ptr<MachOYAML::Object>> dump();
39 };
40 
41 #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct)                         \
42   case MachO::LCName:                                                          \
43     memcpy((void *) & (LC.Data.LCStruct##_data), LoadCmd.Ptr,                  \
44            sizeof(MachO::LCStruct));                                           \
45     if (Obj.isLittleEndian() != sys::IsLittleEndianHost)                       \
46       MachO::swapStruct(LC.Data.LCStruct##_data);                              \
47     EndPtr = processLoadCommandData<MachO::LCStruct>(LC, LoadCmd);             \
48     break;
49 
50 template <typename SectionType>
51 MachOYAML::Section constructSectionCommon(SectionType Sec) {
52   MachOYAML::Section TempSec;
53   memcpy(reinterpret_cast<void *>(&TempSec.sectname[0]), &Sec.sectname[0], 16);
54   memcpy(reinterpret_cast<void *>(&TempSec.segname[0]), &Sec.segname[0], 16);
55   TempSec.addr = Sec.addr;
56   TempSec.size = Sec.size;
57   TempSec.offset = Sec.offset;
58   TempSec.align = Sec.align;
59   TempSec.reloff = Sec.reloff;
60   TempSec.nreloc = Sec.nreloc;
61   TempSec.flags = Sec.flags;
62   TempSec.reserved1 = Sec.reserved1;
63   TempSec.reserved2 = Sec.reserved2;
64   TempSec.reserved3 = 0;
65   return TempSec;
66 }
67 
68 template <typename SectionType>
69 MachOYAML::Section constructSection(SectionType Sec);
70 
71 template <> MachOYAML::Section constructSection(MachO::section Sec) {
72   MachOYAML::Section TempSec = constructSectionCommon(Sec);
73   TempSec.reserved3 = 0;
74   return TempSec;
75 }
76 
77 template <> MachOYAML::Section constructSection(MachO::section_64 Sec) {
78   MachOYAML::Section TempSec = constructSectionCommon(Sec);
79   TempSec.reserved3 = Sec.reserved3;
80   return TempSec;
81 }
82 
83 template <typename SectionType, typename SegmentType>
84 const char *
85 extractSections(const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
86                 std::vector<MachOYAML::Section> &Sections,
87                 bool IsLittleEndian) {
88   auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize;
89   const SectionType *Curr =
90       reinterpret_cast<const SectionType *>(LoadCmd.Ptr + sizeof(SegmentType));
91   for (; reinterpret_cast<const void *>(Curr) < End; Curr++) {
92     if (IsLittleEndian != sys::IsLittleEndianHost) {
93       SectionType Sec;
94       memcpy((void *)&Sec, Curr, sizeof(SectionType));
95       MachO::swapStruct(Sec);
96       Sections.push_back(constructSection(Sec));
97     } else {
98       Sections.push_back(constructSection(*Curr));
99     }
100   }
101   return reinterpret_cast<const char *>(Curr);
102 }
103 
104 template <typename StructType>
105 const char *MachODumper::processLoadCommandData(
106     MachOYAML::LoadCommand &LC,
107     const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
108   return LoadCmd.Ptr + sizeof(StructType);
109 }
110 
111 template <>
112 const char *MachODumper::processLoadCommandData<MachO::segment_command>(
113     MachOYAML::LoadCommand &LC,
114     const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
115   return extractSections<MachO::section, MachO::segment_command>(
116       LoadCmd, LC.Sections, Obj.isLittleEndian());
117 }
118 
119 template <>
120 const char *MachODumper::processLoadCommandData<MachO::segment_command_64>(
121     MachOYAML::LoadCommand &LC,
122     const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
123   return extractSections<MachO::section_64, MachO::segment_command_64>(
124       LoadCmd, LC.Sections, Obj.isLittleEndian());
125 }
126 
127 template <typename StructType>
128 const char *
129 readString(MachOYAML::LoadCommand &LC,
130            const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
131   auto Start = LoadCmd.Ptr + sizeof(StructType);
132   auto MaxSize = LoadCmd.C.cmdsize - sizeof(StructType);
133   auto Size = strnlen(Start, MaxSize);
134   LC.PayloadString = StringRef(Start, Size).str();
135   return Start + Size;
136 }
137 
138 template <>
139 const char *MachODumper::processLoadCommandData<MachO::dylib_command>(
140     MachOYAML::LoadCommand &LC,
141     const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
142   return readString<MachO::dylib_command>(LC, LoadCmd);
143 }
144 
145 template <>
146 const char *MachODumper::processLoadCommandData<MachO::dylinker_command>(
147     MachOYAML::LoadCommand &LC,
148     const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
149   return readString<MachO::dylinker_command>(LC, LoadCmd);
150 }
151 
152 Expected<std::unique_ptr<MachOYAML::Object>> MachODumper::dump() {
153   auto Y = make_unique<MachOYAML::Object>();
154   dumpHeader(Y);
155   dumpLoadCommands(Y);
156   dumpLinkEdit(Y);
157   return std::move(Y);
158 }
159 
160 void MachODumper::dumpHeader(std::unique_ptr<MachOYAML::Object> &Y) {
161   Y->Header.magic = Obj.getHeader().magic;
162   Y->Header.cputype = Obj.getHeader().cputype;
163   Y->Header.cpusubtype = Obj.getHeader().cpusubtype;
164   Y->Header.filetype = Obj.getHeader().filetype;
165   Y->Header.ncmds = Obj.getHeader().ncmds;
166   Y->Header.sizeofcmds = Obj.getHeader().sizeofcmds;
167   Y->Header.flags = Obj.getHeader().flags;
168   Y->Header.reserved = 0;
169 }
170 
171 void MachODumper::dumpLoadCommands(std::unique_ptr<MachOYAML::Object> &Y) {
172   for (auto LoadCmd : Obj.load_commands()) {
173     MachOYAML::LoadCommand LC;
174     const char *EndPtr = LoadCmd.Ptr;
175     switch (LoadCmd.C.cmd) {
176     default:
177       memcpy((void *)&(LC.Data.load_command_data), LoadCmd.Ptr,
178              sizeof(MachO::load_command));
179       if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
180         MachO::swapStruct(LC.Data.load_command_data);
181       EndPtr = processLoadCommandData<MachO::load_command>(LC, LoadCmd);
182       break;
183 #include "llvm/Support/MachO.def"
184     }
185     auto RemainingBytes = LoadCmd.C.cmdsize - (EndPtr - LoadCmd.Ptr);
186     if (!std::all_of(EndPtr, &EndPtr[RemainingBytes],
187                      [](const char C) { return C == 0; })) {
188       LC.PayloadBytes.insert(LC.PayloadBytes.end(), EndPtr,
189                              &EndPtr[RemainingBytes]);
190       RemainingBytes = 0;
191     }
192     LC.ZeroPadBytes = RemainingBytes;
193     Y->LoadCommands.push_back(std::move(LC));
194   }
195 }
196 
197 void MachODumper::dumpLinkEdit(std::unique_ptr<MachOYAML::Object> &Y) {
198   dumpRebaseOpcodes(Y);
199   dumpBindOpcodes(Y->LinkEdit.BindOpcodes, Obj.getDyldInfoBindOpcodes());
200   dumpBindOpcodes(Y->LinkEdit.WeakBindOpcodes,
201                   Obj.getDyldInfoWeakBindOpcodes());
202   dumpBindOpcodes(Y->LinkEdit.LazyBindOpcodes,
203                   Obj.getDyldInfoLazyBindOpcodes(), true);
204 }
205 
206 void MachODumper::dumpRebaseOpcodes(std::unique_ptr<MachOYAML::Object> &Y) {
207   MachOYAML::LinkEditData &LEData = Y->LinkEdit;
208 
209   auto RebaseOpcodes = Obj.getDyldInfoRebaseOpcodes();
210   for (auto OpCode = RebaseOpcodes.begin(); OpCode != RebaseOpcodes.end();
211        ++OpCode) {
212     MachOYAML::RebaseOpcode RebaseOp;
213     RebaseOp.Opcode =
214         static_cast<MachO::RebaseOpcode>(*OpCode & MachO::REBASE_OPCODE_MASK);
215     RebaseOp.Imm = *OpCode & MachO::REBASE_IMMEDIATE_MASK;
216 
217     unsigned Count;
218     uint64_t ULEB = 0;
219 
220     switch (RebaseOp.Opcode) {
221     case MachO::REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
222 
223       ULEB = decodeULEB128(OpCode + 1, &Count);
224       RebaseOp.ExtraData.push_back(ULEB);
225       OpCode += Count;
226     // Intentionally no break here -- This opcode has two ULEB values
227     case MachO::REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
228     case MachO::REBASE_OPCODE_ADD_ADDR_ULEB:
229     case MachO::REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
230     case MachO::REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
231 
232       ULEB = decodeULEB128(OpCode + 1, &Count);
233       RebaseOp.ExtraData.push_back(ULEB);
234       OpCode += Count;
235       break;
236     default:
237       break;
238     }
239 
240     LEData.RebaseOpcodes.push_back(RebaseOp);
241 
242     if (RebaseOp.Opcode == MachO::REBASE_OPCODE_DONE)
243       break;
244   }
245 }
246 
247 void MachODumper::dumpBindOpcodes(
248     std::vector<MachOYAML::BindOpcode> &BindOpcodes,
249     ArrayRef<uint8_t> OpcodeBuffer, bool Lazy) {
250   for (auto OpCode = OpcodeBuffer.begin(); OpCode != OpcodeBuffer.end();
251        ++OpCode) {
252     MachOYAML::BindOpcode BindOp;
253     BindOp.Opcode =
254         static_cast<MachO::BindOpcode>(*OpCode & MachO::BIND_OPCODE_MASK);
255     BindOp.Imm = *OpCode & MachO::BIND_IMMEDIATE_MASK;
256 
257     unsigned Count;
258     uint64_t ULEB = 0;
259     int64_t SLEB = 0;
260     const uint8_t *SymStart;
261 
262     switch (BindOp.Opcode) {
263     case MachO::BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
264       ULEB = decodeULEB128(OpCode + 1, &Count);
265       BindOp.ULEBExtraData.push_back(ULEB);
266       OpCode += Count;
267     // Intentionally no break here -- this opcode has two ULEB values
268 
269     case MachO::BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
270     case MachO::BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
271     case MachO::BIND_OPCODE_ADD_ADDR_ULEB:
272     case MachO::BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
273       ULEB = decodeULEB128(OpCode + 1, &Count);
274       BindOp.ULEBExtraData.push_back(ULEB);
275       OpCode += Count;
276       break;
277 
278     case MachO::BIND_OPCODE_SET_ADDEND_SLEB:
279       SLEB = decodeSLEB128(OpCode + 1, &Count);
280       BindOp.SLEBExtraData.push_back(SLEB);
281       OpCode += Count;
282       break;
283 
284     case MachO::BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
285       SymStart = ++OpCode;
286       while (*OpCode) {
287         ++OpCode;
288       }
289       BindOp.Symbol = StringRef(reinterpret_cast<const char *>(SymStart),
290                                 OpCode - SymStart);
291       break;
292     default:
293       break;
294     }
295 
296     BindOpcodes.push_back(BindOp);
297 
298     // Lazy bindings have DONE opcodes between operations, so we need to keep
299     // processing after a DONE.
300     if (!Lazy && BindOp.Opcode == MachO::BIND_OPCODE_DONE)
301       break;
302   }
303 }
304 
305 Error macho2yaml(raw_ostream &Out, const object::MachOObjectFile &Obj) {
306   MachODumper Dumper(Obj);
307   Expected<std::unique_ptr<MachOYAML::Object>> YAML = Dumper.dump();
308   if (!YAML)
309     return YAML.takeError();
310 
311   yaml::Output Yout(Out);
312   Yout << *(YAML.get());
313   return Error::success();
314 }
315 
316 Error macho2yaml(raw_ostream &Out, const object::MachOUniversalBinary &Obj) {
317   return make_error<Obj2YamlError>(obj2yaml_error::not_implemented);
318 }
319 
320 std::error_code macho2yaml(raw_ostream &Out, const object::ObjectFile &Obj) {
321   if (const auto *MachOObj = dyn_cast<object::MachOUniversalBinary>(&Obj)) {
322     if (auto Err = macho2yaml(Out, *MachOObj)) {
323       return errorToErrorCode(std::move(Err));
324     }
325     return obj2yaml_error::success;
326   }
327 
328   if (const auto *MachOObj = dyn_cast<object::MachOObjectFile>(&Obj)) {
329     if (auto Err = macho2yaml(Out, *MachOObj)) {
330       return errorToErrorCode(std::move(Err));
331     }
332     return obj2yaml_error::success;
333   }
334 
335   return obj2yaml_error::unsupported_obj_file_format;
336 }
337