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