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