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/byteswap.h"
47 #include "common/mac/macho_walker.h"
48 #include "common/mac/macho_utilities.h"
49 
50 namespace MacFileUtilities {
51 
MachoWalker(const char * path,LoadCommandCallback callback,void * context)52 MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback,
53                          void *context)
54     : file_(-1),
55       memory_(NULL),
56       memory_size_(0),
57       callback_(callback),
58       callback_context_(context),
59       current_header_(NULL),
60       current_header_size_(0),
61       current_header_offset_(0) {
62   file_ = open(path, O_RDONLY);
63 }
64 
MachoWalker(void * memory,size_t size,LoadCommandCallback callback,void * context)65 MachoWalker::MachoWalker(void *memory, size_t size,
66                          LoadCommandCallback callback, void *context)
67     : file_(-1),
68       memory_(memory),
69       memory_size_(size),
70       callback_(callback),
71       callback_context_(context),
72       current_header_(NULL),
73       current_header_size_(0),
74       current_header_offset_(0) {
75 }
76 
~MachoWalker()77 MachoWalker::~MachoWalker() {
78   if (file_ != -1)
79     close(file_);
80 }
81 
WalkHeader(cpu_type_t cpu_type,cpu_subtype_t cpu_subtype)82 bool MachoWalker::WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
83   cpu_type_t valid_cpu_type = cpu_type;
84   cpu_subtype_t valid_cpu_subtype = cpu_subtype;
85   // if |cpu_type| is 0, use the native cpu type.
86   if (cpu_type == 0) {
87     const NXArchInfo *arch = NXGetLocalArchInfo();
88     assert(arch);
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       swap_mach_header(&header, NXHostByteOrder());
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       swap_fat_header(&fat, NXHostByteOrder());
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         swap_fat_arch(&arch, 1, NXHostByteOrder());
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     swap_mach_header(&header, NXHostByteOrder());
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, NXHostByteOrder());
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       swap_load_command(&cmd, NXHostByteOrder());
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