• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MachoWalker(const char * path,LoadCommandCallback callback,void * context)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 
MachoWalker(void * memory,size_t size,LoadCommandCallback callback,void * context)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 
~MachoWalker()75 MachoWalker::~MachoWalker() {
76   if (file_ != -1)
77     close(file_);
78 }
79 
WalkHeader(cpu_type_t cpu_type,cpu_subtype_t cpu_subtype)80 bool 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 
ReadBytes(void * buffer,size_t size,off_t offset)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 
CurrentHeader(struct mach_header_64 * header,off_t * offset)119 bool 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 
FindHeader(cpu_type_t cpu_type,cpu_subtype_t cpu_subtype,off_t & offset)129 bool 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 
WalkHeaderAtOffset(off_t offset)202 bool 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 
WalkHeader64AtOffset(off_t offset)228 bool 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 
WalkHeaderCore(off_t offset,uint32_t number_of_commands,bool swap)248 bool 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