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 | |
48 | namespace MacFileUtilities { |
49 | |
50 | MachoWalker::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 | |
63 | MachoWalker::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 | |
75 | MachoWalker::~MachoWalker() { |
76 | if (file_ != -1) |
77 | close(file_); |
78 | } |
79 | |
80 | bool MachoWalker::(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 | |
101 | bool 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 | |
119 | bool MachoWalker::(struct mach_header_64* , 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 | |
129 | bool MachoWalker::(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 ; |
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 | |
202 | bool MachoWalker::(off_t offset) { |
203 | struct mach_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 ; |
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 | |
228 | bool MachoWalker::(off_t offset) { |
229 | struct mach_header_64 ; |
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 | |
248 | bool MachoWalker::(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 | |