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
36#include <assert.h>
37#include <fcntl.h>
38#include <mach-o/arch.h>
39#include <mach-o/fat.h>
40#include <mach-o/loader.h>
41#include <string.h>
42#include <unistd.h>
43
44#include "common/mac/byteswap.h"
45#include "common/mac/macho_walker.h"
46#include "common/mac/macho_utilities.h"
47
48namespace MacFileUtilities {
49
50MachoWalker::MachoWalker(const char* path, LoadCommandCallback callback,
51 void* context)
52 : file_(-1),
53 memory_(NULL),
54 memory_size_(0),
55 callback_(callback),
56 callback_context_(context),
57 current_header_(NULL),
58 current_header_size_(0),
59 current_header_offset_(0) {
60 file_ = open(path, O_RDONLY);
61}
62
63MachoWalker::MachoWalker(void* memory, size_t size,
64 LoadCommandCallback callback, void* context)
65 : file_(-1),
66 memory_(memory),
67 memory_size_(size),
68 callback_(callback),
69 callback_context_(context),
70 current_header_(NULL),
71 current_header_size_(0),
72 current_header_offset_(0) {
73}
74
75MachoWalker::~MachoWalker() {
76 if (file_ != -1)
77 close(file_);
78}
79
80bool MachoWalker::WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
81 cpu_type_t valid_cpu_type = cpu_type;
82 cpu_subtype_t valid_cpu_subtype = cpu_subtype;
83 // if |cpu_type| is 0, use the native cpu type.
84 if (cpu_type == 0) {
85 const NXArchInfo* arch = NXGetLocalArchInfo();
86 assert(arch);
87 valid_cpu_type = arch->cputype;
88 valid_cpu_subtype = CPU_SUBTYPE_MULTIPLE;
89 }
90 off_t offset;
91 if (FindHeader(valid_cpu_type, valid_cpu_subtype, offset)) {
92 if (cpu_type & CPU_ARCH_ABI64)
93 return WalkHeader64AtOffset(offset);
94
95 return WalkHeaderAtOffset(offset);
96 }
97
98 return false;
99}
100
101bool MachoWalker::ReadBytes(void* buffer, size_t size, off_t offset) {
102 if (memory_) {
103 if (offset < 0)
104 return false;
105 bool result = true;
106 if (offset + size > memory_size_) {
107 if (static_cast<size_t>(offset) >= memory_size_)
108 return false;
109 size = memory_size_ - static_cast<size_t>(offset);
110 result = false;
111 }
112 memcpy(buffer, static_cast<char*>(memory_) + offset, size);
113 return result;
114 } else {
115 return pread(file_, buffer, size, offset) == (ssize_t)size;
116 }
117}
118
119bool MachoWalker::CurrentHeader(struct mach_header_64* header, off_t* offset) {
120 if (current_header_) {
121 memcpy(header, current_header_, sizeof(mach_header_64));
122 *offset = current_header_offset_;
123 return true;
124 }
125
126 return false;
127}
128
129bool MachoWalker::FindHeader(cpu_type_t cpu_type,
130 cpu_subtype_t cpu_subtype,
131 off_t& offset) {
132 // Read the magic bytes that's common amongst all mach-o files
133 uint32_t magic;
134 if (!ReadBytes(&magic, sizeof(magic), 0))
135 return false;
136
137 offset = sizeof(magic);
138
139 // Figure out what type of file we've got
140 bool is_fat = false;
141 if (magic == FAT_MAGIC || magic == FAT_CIGAM) {
142 is_fat = true;
143 }
144 else if (magic != MH_MAGIC && magic != MH_CIGAM && magic != MH_MAGIC_64 &&
145 magic != MH_CIGAM_64) {
146 return false;
147 }
148
149 if (!is_fat) {
150 // If we don't have a fat header, check if the cpu type matches the single
151 // header
152 struct mach_header header;
153 if (!ReadBytes(&header, sizeof(header), 0))
154 return false;
155
156 if (magic == MH_CIGAM || magic == MH_CIGAM_64)
157 breakpad_swap_mach_header(&header);
158
159 if (cpu_type != header.cputype ||
160 (cpu_subtype != CPU_SUBTYPE_MULTIPLE &&
161 cpu_subtype != header.cpusubtype)) {
162 return false;
163 }
164
165 offset = 0;
166 return true;
167 } else {
168 // Read the fat header and find an appropriate architecture
169 offset = 0;
170 struct fat_header fat;
171 if (!ReadBytes(&fat, sizeof(fat), offset))
172 return false;
173
174 if (NXHostByteOrder() != NX_BigEndian)
175 breakpad_swap_fat_header(&fat);
176
177 offset += sizeof(fat);
178
179 // Search each architecture for the desired one
180 struct fat_arch arch;
181 for (uint32_t i = 0; i < fat.nfat_arch; ++i) {
182 if (!ReadBytes(&arch, sizeof(arch), offset))
183 return false;
184
185 if (NXHostByteOrder() != NX_BigEndian)
186 breakpad_swap_fat_arch(&arch, 1);
187
188 if (arch.cputype == cpu_type &&
189 (cpu_subtype == CPU_SUBTYPE_MULTIPLE ||
190 arch.cpusubtype == cpu_subtype)) {
191 offset = arch.offset;
192 return true;
193 }
194
195 offset += sizeof(arch);
196 }
197 }
198
199 return false;
200}
201
202bool MachoWalker::WalkHeaderAtOffset(off_t offset) {
203 struct mach_header header;
204 if (!ReadBytes(&header, sizeof(header), offset))
205 return false;
206
207 bool swap = (header.magic == MH_CIGAM);
208 if (swap)
209 breakpad_swap_mach_header(&header);
210
211 // Copy the data into the mach_header_64 structure. Since the 32-bit and
212 // 64-bit only differ in the last field (reserved), this is safe to do.
213 struct mach_header_64 header64;
214 memcpy((void*)&header64, (const void*)&header, sizeof(header));
215 header64.reserved = 0;
216
217 current_header_ = &header64;
218 current_header_size_ = sizeof(header); // 32-bit, not 64-bit
219 current_header_offset_ = offset;
220 offset += current_header_size_;
221 bool result = WalkHeaderCore(offset, header.ncmds, swap);
222 current_header_ = NULL;
223 current_header_size_ = 0;
224 current_header_offset_ = 0;
225 return result;
226}
227
228bool MachoWalker::WalkHeader64AtOffset(off_t offset) {
229 struct mach_header_64 header;
230 if (!ReadBytes(&header, sizeof(header), offset))
231 return false;
232
233 bool swap = (header.magic == MH_CIGAM_64);
234 if (swap)
235 breakpad_swap_mach_header_64(&header);
236
237 current_header_ = &header;
238 current_header_size_ = sizeof(header);
239 current_header_offset_ = offset;
240 offset += current_header_size_;
241 bool result = WalkHeaderCore(offset, header.ncmds, swap);
242 current_header_ = NULL;
243 current_header_size_ = 0;
244 current_header_offset_ = 0;
245 return result;
246}
247
248bool MachoWalker::WalkHeaderCore(off_t offset, uint32_t number_of_commands,
249 bool swap) {
250 for (uint32_t i = 0; i < number_of_commands; ++i) {
251 struct load_command cmd;
252 if (!ReadBytes(&cmd, sizeof(cmd), offset))
253 return false;
254
255 if (swap)
256 breakpad_swap_load_command(&cmd);
257
258 // Call the user callback
259 if (callback_ && !callback_(this, &cmd, offset, swap, callback_context_))
260 break;
261
262 offset += cmd.cmdsize;
263 }
264
265 return true;
266}
267
268} // namespace MacFileUtilities
269