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 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 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 77 MachoWalker::~MachoWalker() { 78 if (file_ != -1) 79 close(file_); 80 } 81 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 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 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 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 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 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 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