1//===- MachOObject.cpp - Mach-O Object File Wrapper -----------------------===//
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 "llvm/Object/MachOObject.h"
11#include "llvm/ADT/StringRef.h"
12#include "llvm/ADT/SmallVector.h"
13#include "llvm/Support/DataExtractor.h"
14#include "llvm/Support/Debug.h"
15#include "llvm/Support/Host.h"
16#include "llvm/Support/MemoryBuffer.h"
17#include "llvm/Support/raw_ostream.h"
18#include "llvm/Support/SwapByteOrder.h"
19
20using namespace llvm;
21using namespace llvm::object;
22
23/* Translation Utilities */
24
25template<typename T>
26static void SwapValue(T &Value) {
27  Value = sys::SwapByteOrder(Value);
28}
29
30template<typename T>
31static void SwapStruct(T &Value);
32
33template<typename T>
34static void ReadInMemoryStruct(const MachOObject &MOO,
35                               StringRef Buffer, uint64_t Base,
36                               InMemoryStruct<T> &Res) {
37  typedef T struct_type;
38  uint64_t Size = sizeof(struct_type);
39
40  // Check that the buffer contains the expected data.
41  if (Base + Size >  Buffer.size()) {
42    Res = 0;
43    return;
44  }
45
46  // Check whether we can return a direct pointer.
47  struct_type *Ptr = (struct_type *) (Buffer.data() + Base);
48  if (!MOO.isSwappedEndian()) {
49    Res = Ptr;
50    return;
51  }
52
53  // Otherwise, copy the struct and translate the values.
54  Res = *Ptr;
55  SwapStruct(*Res);
56}
57
58/* *** */
59
60MachOObject::MachOObject(MemoryBuffer *Buffer_, bool IsLittleEndian_,
61                         bool Is64Bit_)
62  : Buffer(Buffer_), IsLittleEndian(IsLittleEndian_), Is64Bit(Is64Bit_),
63    IsSwappedEndian(IsLittleEndian != sys::isLittleEndianHost()),
64    HasStringTable(false), LoadCommands(0), NumLoadedCommands(0) {
65  // Load the common header.
66  memcpy(&Header, Buffer->getBuffer().data(), sizeof(Header));
67  if (IsSwappedEndian) {
68    SwapValue(Header.Magic);
69    SwapValue(Header.CPUType);
70    SwapValue(Header.CPUSubtype);
71    SwapValue(Header.FileType);
72    SwapValue(Header.NumLoadCommands);
73    SwapValue(Header.SizeOfLoadCommands);
74    SwapValue(Header.Flags);
75  }
76
77  if (is64Bit()) {
78    memcpy(&Header64Ext, Buffer->getBuffer().data() + sizeof(Header),
79           sizeof(Header64Ext));
80    if (IsSwappedEndian) {
81      SwapValue(Header64Ext.Reserved);
82    }
83  }
84
85  // Create the load command array if sane.
86  if (getHeader().NumLoadCommands < (1 << 20))
87    LoadCommands = new LoadCommandInfo[getHeader().NumLoadCommands];
88}
89
90MachOObject::~MachOObject() {
91  delete [] LoadCommands;
92}
93
94MachOObject *MachOObject::LoadFromBuffer(MemoryBuffer *Buffer,
95                                         std::string *ErrorStr) {
96  // First, check the magic value and initialize the basic object info.
97  bool IsLittleEndian = false, Is64Bit = false;
98  StringRef Magic = Buffer->getBuffer().slice(0, 4);
99  if (Magic == "\xFE\xED\xFA\xCE") {
100  }  else if (Magic == "\xCE\xFA\xED\xFE") {
101    IsLittleEndian = true;
102  } else if (Magic == "\xFE\xED\xFA\xCF") {
103    Is64Bit = true;
104  } else if (Magic == "\xCF\xFA\xED\xFE") {
105    IsLittleEndian = true;
106    Is64Bit = true;
107  } else {
108    if (ErrorStr) *ErrorStr = "not a Mach object file (invalid magic)";
109    return 0;
110  }
111
112  // Ensure that the at least the full header is present.
113  unsigned HeaderSize = Is64Bit ? macho::Header64Size : macho::Header32Size;
114  if (Buffer->getBufferSize() < HeaderSize) {
115    if (ErrorStr) *ErrorStr = "not a Mach object file (invalid header)";
116    return 0;
117  }
118
119  OwningPtr<MachOObject> Object(new MachOObject(Buffer, IsLittleEndian,
120                                                Is64Bit));
121
122  // Check for bogus number of load commands.
123  if (Object->getHeader().NumLoadCommands >= (1 << 20)) {
124    if (ErrorStr) *ErrorStr = "not a Mach object file (unreasonable header)";
125    return 0;
126  }
127
128  if (ErrorStr) *ErrorStr = "";
129  return Object.take();
130}
131
132StringRef MachOObject::getData(size_t Offset, size_t Size) const {
133  return Buffer->getBuffer().substr(Offset,Size);
134}
135
136void MachOObject::RegisterStringTable(macho::SymtabLoadCommand &SLC) {
137  HasStringTable = true;
138  StringTable = Buffer->getBuffer().substr(SLC.StringTableOffset,
139                                           SLC.StringTableSize);
140}
141
142const MachOObject::LoadCommandInfo &
143MachOObject::getLoadCommandInfo(unsigned Index) const {
144  assert(Index < getHeader().NumLoadCommands && "Invalid index!");
145
146  // Load the command, if necessary.
147  if (Index >= NumLoadedCommands) {
148    uint64_t Offset;
149    if (Index == 0) {
150      Offset = getHeaderSize();
151    } else {
152      const LoadCommandInfo &Prev = getLoadCommandInfo(Index - 1);
153      Offset = Prev.Offset + Prev.Command.Size;
154    }
155
156    LoadCommandInfo &Info = LoadCommands[Index];
157    memcpy(&Info.Command, Buffer->getBuffer().data() + Offset,
158           sizeof(macho::LoadCommand));
159    if (IsSwappedEndian) {
160      SwapValue(Info.Command.Type);
161      SwapValue(Info.Command.Size);
162    }
163    Info.Offset = Offset;
164    NumLoadedCommands = Index + 1;
165  }
166
167  return LoadCommands[Index];
168}
169
170template<>
171void SwapStruct(macho::SegmentLoadCommand &Value) {
172  SwapValue(Value.Type);
173  SwapValue(Value.Size);
174  SwapValue(Value.VMAddress);
175  SwapValue(Value.VMSize);
176  SwapValue(Value.FileOffset);
177  SwapValue(Value.FileSize);
178  SwapValue(Value.MaxVMProtection);
179  SwapValue(Value.InitialVMProtection);
180  SwapValue(Value.NumSections);
181  SwapValue(Value.Flags);
182}
183void MachOObject::ReadSegmentLoadCommand(const LoadCommandInfo &LCI,
184                         InMemoryStruct<macho::SegmentLoadCommand> &Res) const {
185  ReadInMemoryStruct(*this, Buffer->getBuffer(), LCI.Offset, Res);
186}
187
188template<>
189void SwapStruct(macho::Segment64LoadCommand &Value) {
190  SwapValue(Value.Type);
191  SwapValue(Value.Size);
192  SwapValue(Value.VMAddress);
193  SwapValue(Value.VMSize);
194  SwapValue(Value.FileOffset);
195  SwapValue(Value.FileSize);
196  SwapValue(Value.MaxVMProtection);
197  SwapValue(Value.InitialVMProtection);
198  SwapValue(Value.NumSections);
199  SwapValue(Value.Flags);
200}
201void MachOObject::ReadSegment64LoadCommand(const LoadCommandInfo &LCI,
202                       InMemoryStruct<macho::Segment64LoadCommand> &Res) const {
203  ReadInMemoryStruct(*this, Buffer->getBuffer(), LCI.Offset, Res);
204}
205
206template<>
207void SwapStruct(macho::SymtabLoadCommand &Value) {
208  SwapValue(Value.Type);
209  SwapValue(Value.Size);
210  SwapValue(Value.SymbolTableOffset);
211  SwapValue(Value.NumSymbolTableEntries);
212  SwapValue(Value.StringTableOffset);
213  SwapValue(Value.StringTableSize);
214}
215void MachOObject::ReadSymtabLoadCommand(const LoadCommandInfo &LCI,
216                          InMemoryStruct<macho::SymtabLoadCommand> &Res) const {
217  ReadInMemoryStruct(*this, Buffer->getBuffer(), LCI.Offset, Res);
218}
219
220template<>
221void SwapStruct(macho::DysymtabLoadCommand &Value) {
222  SwapValue(Value.Type);
223  SwapValue(Value.Size);
224  SwapValue(Value.LocalSymbolsIndex);
225  SwapValue(Value.NumLocalSymbols);
226  SwapValue(Value.ExternalSymbolsIndex);
227  SwapValue(Value.NumExternalSymbols);
228  SwapValue(Value.UndefinedSymbolsIndex);
229  SwapValue(Value.NumUndefinedSymbols);
230  SwapValue(Value.TOCOffset);
231  SwapValue(Value.NumTOCEntries);
232  SwapValue(Value.ModuleTableOffset);
233  SwapValue(Value.NumModuleTableEntries);
234  SwapValue(Value.ReferenceSymbolTableOffset);
235  SwapValue(Value.NumReferencedSymbolTableEntries);
236  SwapValue(Value.IndirectSymbolTableOffset);
237  SwapValue(Value.NumIndirectSymbolTableEntries);
238  SwapValue(Value.ExternalRelocationTableOffset);
239  SwapValue(Value.NumExternalRelocationTableEntries);
240  SwapValue(Value.LocalRelocationTableOffset);
241  SwapValue(Value.NumLocalRelocationTableEntries);
242}
243void MachOObject::ReadDysymtabLoadCommand(const LoadCommandInfo &LCI,
244                        InMemoryStruct<macho::DysymtabLoadCommand> &Res) const {
245  ReadInMemoryStruct(*this, Buffer->getBuffer(), LCI.Offset, Res);
246}
247
248template<>
249void SwapStruct(macho::LinkeditDataLoadCommand &Value) {
250  SwapValue(Value.Type);
251  SwapValue(Value.Size);
252  SwapValue(Value.DataOffset);
253  SwapValue(Value.DataSize);
254}
255void MachOObject::ReadLinkeditDataLoadCommand(const LoadCommandInfo &LCI,
256                    InMemoryStruct<macho::LinkeditDataLoadCommand> &Res) const {
257  ReadInMemoryStruct(*this, Buffer->getBuffer(), LCI.Offset, Res);
258}
259
260template<>
261void SwapStruct(macho::IndirectSymbolTableEntry &Value) {
262  SwapValue(Value.Index);
263}
264void
265MachOObject::ReadIndirectSymbolTableEntry(const macho::DysymtabLoadCommand &DLC,
266                                          unsigned Index,
267                   InMemoryStruct<macho::IndirectSymbolTableEntry> &Res) const {
268  uint64_t Offset = (DLC.IndirectSymbolTableOffset +
269                     Index * sizeof(macho::IndirectSymbolTableEntry));
270  ReadInMemoryStruct(*this, Buffer->getBuffer(), Offset, Res);
271}
272
273
274template<>
275void SwapStruct(macho::Section &Value) {
276  SwapValue(Value.Address);
277  SwapValue(Value.Size);
278  SwapValue(Value.Offset);
279  SwapValue(Value.Align);
280  SwapValue(Value.RelocationTableOffset);
281  SwapValue(Value.NumRelocationTableEntries);
282  SwapValue(Value.Flags);
283  SwapValue(Value.Reserved1);
284  SwapValue(Value.Reserved2);
285}
286void MachOObject::ReadSection(const LoadCommandInfo &LCI,
287                              unsigned Index,
288                              InMemoryStruct<macho::Section> &Res) const {
289  assert(LCI.Command.Type == macho::LCT_Segment &&
290         "Unexpected load command info!");
291  uint64_t Offset = (LCI.Offset + sizeof(macho::SegmentLoadCommand) +
292                     Index * sizeof(macho::Section));
293  ReadInMemoryStruct(*this, Buffer->getBuffer(), Offset, Res);
294}
295
296template<>
297void SwapStruct(macho::Section64 &Value) {
298  SwapValue(Value.Address);
299  SwapValue(Value.Size);
300  SwapValue(Value.Offset);
301  SwapValue(Value.Align);
302  SwapValue(Value.RelocationTableOffset);
303  SwapValue(Value.NumRelocationTableEntries);
304  SwapValue(Value.Flags);
305  SwapValue(Value.Reserved1);
306  SwapValue(Value.Reserved2);
307  SwapValue(Value.Reserved3);
308}
309void MachOObject::ReadSection64(const LoadCommandInfo &LCI,
310                                unsigned Index,
311                                InMemoryStruct<macho::Section64> &Res) const {
312  assert(LCI.Command.Type == macho::LCT_Segment64 &&
313         "Unexpected load command info!");
314  uint64_t Offset = (LCI.Offset + sizeof(macho::Segment64LoadCommand) +
315                     Index * sizeof(macho::Section64));
316  ReadInMemoryStruct(*this, Buffer->getBuffer(), Offset, Res);
317}
318
319template<>
320void SwapStruct(macho::RelocationEntry &Value) {
321  SwapValue(Value.Word0);
322  SwapValue(Value.Word1);
323}
324void MachOObject::ReadRelocationEntry(uint64_t RelocationTableOffset,
325                                      unsigned Index,
326                            InMemoryStruct<macho::RelocationEntry> &Res) const {
327  uint64_t Offset = (RelocationTableOffset +
328                     Index * sizeof(macho::RelocationEntry));
329  ReadInMemoryStruct(*this, Buffer->getBuffer(), Offset, Res);
330}
331
332template<>
333void SwapStruct(macho::SymbolTableEntry &Value) {
334  SwapValue(Value.StringIndex);
335  SwapValue(Value.Flags);
336  SwapValue(Value.Value);
337}
338void MachOObject::ReadSymbolTableEntry(uint64_t SymbolTableOffset,
339                                       unsigned Index,
340                           InMemoryStruct<macho::SymbolTableEntry> &Res) const {
341  uint64_t Offset = (SymbolTableOffset +
342                     Index * sizeof(macho::SymbolTableEntry));
343  ReadInMemoryStruct(*this, Buffer->getBuffer(), Offset, Res);
344}
345
346template<>
347void SwapStruct(macho::Symbol64TableEntry &Value) {
348  SwapValue(Value.StringIndex);
349  SwapValue(Value.Flags);
350  SwapValue(Value.Value);
351}
352void MachOObject::ReadSymbol64TableEntry(uint64_t SymbolTableOffset,
353                                       unsigned Index,
354                         InMemoryStruct<macho::Symbol64TableEntry> &Res) const {
355  uint64_t Offset = (SymbolTableOffset +
356                     Index * sizeof(macho::Symbol64TableEntry));
357  ReadInMemoryStruct(*this, Buffer->getBuffer(), Offset, Res);
358}
359
360template<>
361void SwapStruct(macho::DataInCodeTableEntry &Value) {
362  SwapValue(Value.Offset);
363  SwapValue(Value.Length);
364  SwapValue(Value.Kind);
365}
366void MachOObject::ReadDataInCodeTableEntry(uint64_t TableOffset,
367                                           unsigned Index,
368                       InMemoryStruct<macho::DataInCodeTableEntry> &Res) const {
369  uint64_t Offset = (TableOffset +
370                     Index * sizeof(macho::DataInCodeTableEntry));
371  ReadInMemoryStruct(*this, Buffer->getBuffer(), Offset, Res);
372}
373
374void MachOObject::ReadULEB128s(uint64_t Index,
375                               SmallVectorImpl<uint64_t> &Out) const {
376  DataExtractor extractor(Buffer->getBuffer(), true, 0);
377
378  uint32_t offset = Index;
379  uint64_t data = 0;
380  while (uint64_t delta = extractor.getULEB128(&offset)) {
381    data += delta;
382    Out.push_back(data);
383  }
384}
385
386/* ** */
387// Object Dumping Facilities
388void MachOObject::dump() const { print(dbgs()); dbgs() << '\n'; }
389void MachOObject::dumpHeader() const { printHeader(dbgs()); dbgs() << '\n'; }
390
391void MachOObject::printHeader(raw_ostream &O) const {
392  O << "('cputype', " << Header.CPUType << ")\n";
393  O << "('cpusubtype', " << Header.CPUSubtype << ")\n";
394  O << "('filetype', " << Header.FileType << ")\n";
395  O << "('num_load_commands', " << Header.NumLoadCommands << ")\n";
396  O << "('load_commands_size', " << Header.SizeOfLoadCommands << ")\n";
397  O << "('flag', " << Header.Flags << ")\n";
398
399  // Print extended header if 64-bit.
400  if (is64Bit())
401    O << "('reserved', " << Header64Ext.Reserved << ")\n";
402}
403
404void MachOObject::print(raw_ostream &O) const {
405  O << "Header:\n";
406  printHeader(O);
407  O << "Load Commands:\n";
408
409  O << "Buffer:\n";
410}
411