macho_walker.cc revision 1adc07f0ffd92068215567f5e1546a9c98e58e42
1// Copyright (c) 2006, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14//     * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30// macho_walker.cc: Iterate over the load commands in a mach-o file
31//
32// See macho_walker.h for documentation
33//
34// Author: Dan Waylonis
35
36extern "C" {  // necessary for Leopard
37  #include <assert.h>
38  #include <fcntl.h>
39  #include <mach-o/arch.h>
40  #include <mach-o/loader.h>
41  #include <mach-o/swap.h>
42  #include <string.h>
43  #include <unistd.h>
44}
45
46#include "common/mac/byteswap.h"
47#include "common/mac/macho_walker.h"
48#include "common/mac/macho_utilities.h"
49
50namespace MacFileUtilities {
51
52MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback,
53                         void *context)
54    : callback_(callback),
55      callback_context_(context) {
56  file_ = open(path, O_RDONLY);
57}
58
59MachoWalker::~MachoWalker() {
60  if (file_ != -1)
61    close(file_);
62}
63
64int MachoWalker::ValidateCPUType(int cpu_type) {
65  // If the user didn't specify, use the local architecture.
66  if (cpu_type == 0) {
67    const NXArchInfo *arch = NXGetLocalArchInfo();
68    assert(arch);
69    cpu_type = arch->cputype;
70  }
71
72  return cpu_type;
73}
74
75bool MachoWalker::WalkHeader(int cpu_type) {
76  int valid_cpu_type = ValidateCPUType(cpu_type);
77  off_t offset;
78  if (FindHeader(valid_cpu_type, offset)) {
79    if (cpu_type & CPU_ARCH_ABI64)
80      return WalkHeader64AtOffset(offset);
81
82    return WalkHeaderAtOffset(offset);
83  }
84
85  return false;
86}
87
88bool MachoWalker::ReadBytes(void *buffer, size_t size, off_t offset) {
89  return pread(file_, buffer, size, offset) == (ssize_t)size;
90}
91
92bool MachoWalker::CurrentHeader(struct mach_header_64 *header, off_t *offset) {
93  if (current_header_) {
94    memcpy(header, current_header_, sizeof(mach_header_64));
95    *offset = current_header_offset_;
96    return true;
97  }
98
99  return false;
100}
101
102bool MachoWalker::FindHeader(int cpu_type, off_t &offset) {
103  int valid_cpu_type = ValidateCPUType(cpu_type);
104  // Read the magic bytes that's common amongst all mach-o files
105  uint32_t magic;
106  if (!ReadBytes(&magic, sizeof(magic), 0))
107    return false;
108
109  offset = sizeof(magic);
110
111  // Figure out what type of file we've got
112  bool is_fat = false;
113  if (magic == FAT_MAGIC || magic == FAT_CIGAM) {
114    is_fat = true;
115  }
116  else if (magic != MH_MAGIC && magic != MH_CIGAM && magic != MH_MAGIC_64 &&
117           magic != MH_CIGAM_64) {
118    return false;
119  }
120
121  if (!is_fat) {
122    // If we don't have a fat header, check if the cpu type matches the single
123    // header
124    cpu_type_t header_cpu_type;
125    if (!ReadBytes(&header_cpu_type, sizeof(header_cpu_type), offset))
126      return false;
127
128    if (magic == MH_CIGAM || magic == MH_CIGAM_64)
129      header_cpu_type = ByteSwap(header_cpu_type);
130
131    if (valid_cpu_type != header_cpu_type)
132      return false;
133
134    offset = 0;
135    return true;
136  } else {
137    // Read the fat header and find an appropriate architecture
138    offset = 0;
139    struct fat_header fat;
140    if (!ReadBytes(&fat, sizeof(fat), offset))
141      return false;
142
143    if (NXHostByteOrder() != NX_BigEndian)
144      swap_fat_header(&fat, NXHostByteOrder());
145
146    offset += sizeof(fat);
147
148    // Search each architecture for the desired one
149    struct fat_arch arch;
150    for (uint32_t i = 0; i < fat.nfat_arch; ++i) {
151      if (!ReadBytes(&arch, sizeof(arch), offset))
152        return false;
153
154      if (NXHostByteOrder() != NX_BigEndian)
155        swap_fat_arch(&arch, 1, NXHostByteOrder());
156
157      if (arch.cputype == valid_cpu_type) {
158        offset = arch.offset;
159        return true;
160      }
161
162      offset += sizeof(arch);
163    }
164  }
165
166  return false;
167}
168
169bool MachoWalker::WalkHeaderAtOffset(off_t offset) {
170  struct mach_header header;
171  if (!ReadBytes(&header, sizeof(header), offset))
172    return false;
173
174  bool swap = (header.magic == MH_CIGAM);
175  if (swap)
176    swap_mach_header(&header, NXHostByteOrder());
177
178  // Copy the data into the mach_header_64 structure.  Since the 32-bit and
179  // 64-bit only differ in the last field (reserved), this is safe to do.
180  struct mach_header_64 header64;
181  memcpy((void *)&header64, (const void *)&header, sizeof(header));
182  header64.reserved = 0;
183
184  current_header_ = &header64;
185  current_header_size_ = sizeof(header); // 32-bit, not 64-bit
186  current_header_offset_ = offset;
187  offset += current_header_size_;
188  bool result = WalkHeaderCore(offset, header.ncmds, swap);
189  current_header_ = NULL;
190  current_header_size_ = 0;
191  current_header_offset_ = 0;
192  return result;
193}
194
195bool MachoWalker::WalkHeader64AtOffset(off_t offset) {
196  struct mach_header_64 header;
197  if (!ReadBytes(&header, sizeof(header), offset))
198    return false;
199
200  bool swap = (header.magic == MH_CIGAM_64);
201  if (swap)
202    breakpad_swap_mach_header_64(&header, NXHostByteOrder());
203
204  current_header_ = &header;
205  current_header_size_ = sizeof(header);
206  current_header_offset_ = offset;
207  offset += current_header_size_;
208  bool result = WalkHeaderCore(offset, header.ncmds, swap);
209  current_header_ = NULL;
210  current_header_size_ = 0;
211  current_header_offset_ = 0;
212  return result;
213}
214
215bool MachoWalker::WalkHeaderCore(off_t offset, uint32_t number_of_commands,
216                                 bool swap) {
217  for (uint32_t i = 0; i < number_of_commands; ++i) {
218    struct load_command cmd;
219    if (!ReadBytes(&cmd, sizeof(cmd), offset))
220      return false;
221
222    if (swap)
223      swap_load_command(&cmd, NXHostByteOrder());
224
225    // Call the user callback
226    if (callback_ && !callback_(this, &cmd, offset, swap, callback_context_))
227      break;
228
229    offset += cmd.cmdsize;
230  }
231
232  return true;
233}
234
235}  // namespace MacFileUtilities
236