macho_walker.cc
1 // Copyright 2006 Google LLC 2 // 3 // Redistribution and use in source and binary forms, with or without 4 // modification, are permitted provided that the following conditions are 5 // met: 6 // 7 // * Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above 10 // copyright notice, this list of conditions and the following disclaimer 11 // in the documentation and/or other materials provided with the 12 // distribution. 13 // * Neither the name of Google LLC nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 // macho_walker.cc: Iterate over the load commands in a mach-o file 30 // 31 // See macho_walker.h for documentation 32 // 33 // Author: Dan Waylonis 34 35 #ifdef HAVE_CONFIG_H 36 #include <config.h> // Must come first 37 #endif 38 39 #include <assert.h> 40 #include <fcntl.h> 41 #include <mach-o/fat.h> 42 #include <mach-o/loader.h> 43 #include <string.h> 44 #include <unistd.h> 45 46 #include "common/mac/arch_utilities.h" 47 #include "common/mac/byteswap.h" 48 #include "common/mac/macho_utilities.h" 49 #include "common/mac/macho_walker.h" 50 51 namespace MacFileUtilities { 52 53 MachoWalker::MachoWalker(const char* path, LoadCommandCallback callback, 54 void* context) 55 : file_(-1), 56 memory_(NULL), 57 memory_size_(0), 58 callback_(callback), 59 callback_context_(context), 60 current_header_(NULL), 61 current_header_size_(0), 62 current_header_offset_(0) { 63 file_ = open(path, O_RDONLY); 64 } 65 66 MachoWalker::MachoWalker(void* memory, size_t size, 67 LoadCommandCallback callback, void* context) 68 : file_(-1), 69 memory_(memory), 70 memory_size_(size), 71 callback_(callback), 72 callback_context_(context), 73 current_header_(NULL), 74 current_header_size_(0), 75 current_header_offset_(0) { 76 } 77 78 MachoWalker::~MachoWalker() { 79 if (file_ != -1) 80 close(file_); 81 } 82 83 bool MachoWalker::WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) { 84 cpu_type_t valid_cpu_type = cpu_type; 85 cpu_subtype_t valid_cpu_subtype = cpu_subtype; 86 // if |cpu_type| is 0, use the native cpu type. 87 if (cpu_type == 0) { 88 ArchInfo arch = GetLocalArchInfo(); 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 breakpad_swap_mach_header(&header); 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 breakpad_swap_fat_header(&fat); 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 breakpad_swap_fat_arch(&arch, 1); 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 breakpad_swap_mach_header(&header); 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); 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 breakpad_swap_load_command(&cmd); 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