/thirdparty/breakpad/common/mac/macho_id.cc
C++ | 366 lines | 237 code | 66 blank | 63 comment | 56 complexity | 963d551893e62eeaf8910c33cd5d2240 MD5 | raw file
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_id.cc: Functions to gather identifying information from a macho file 31// 32// See macho_id.h for documentation 33// 34// Author: Dan Waylonis 35 36extern "C" { // necessary for Leopard 37 #include <fcntl.h> 38 #include <mach-o/loader.h> 39 #include <mach-o/swap.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <sys/time.h> 44 #include <sys/types.h> 45 #include <unistd.h> 46} 47 48#include "common/mac/macho_id.h" 49#include "common/mac/macho_walker.h" 50#include "common/mac/macho_utilities.h" 51 52namespace MacFileUtilities { 53 54using google_breakpad::MD5Init; 55using google_breakpad::MD5Update; 56using google_breakpad::MD5Final; 57 58MachoID::MachoID(const char *path) 59 : memory_(0), 60 memory_size_(0), 61 crc_(0), 62 md5_context_(), 63 update_function_(NULL) { 64 strlcpy(path_, path, sizeof(path_)); 65} 66 67MachoID::MachoID(const char *path, void *memory, size_t size) 68 : memory_(memory), 69 memory_size_(size), 70 crc_(0), 71 md5_context_(), 72 update_function_(NULL) { 73 strlcpy(path_, path, sizeof(path_)); 74} 75 76MachoID::~MachoID() { 77} 78 79// The CRC info is from http://en.wikipedia.org/wiki/Adler-32 80// With optimizations from http://www.zlib.net/ 81 82// The largest prime smaller than 65536 83#define MOD_ADLER 65521 84// MAX_BLOCK is the largest n such that 255n(n+1)/2 + (n+1)(MAX_BLOCK-1) <= 2^32-1 85#define MAX_BLOCK 5552 86 87void MachoID::UpdateCRC(unsigned char *bytes, size_t size) { 88// Unrolled loops for summing 89#define DO1(buf,i) {sum1 += (buf)[i]; sum2 += sum1;} 90#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); 91#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); 92#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); 93#define DO16(buf) DO8(buf,0); DO8(buf,8); 94 // Split up the crc 95 uint32_t sum1 = crc_ & 0xFFFF; 96 uint32_t sum2 = (crc_ >> 16) & 0xFFFF; 97 98 // Do large blocks 99 while (size >= MAX_BLOCK) { 100 size -= MAX_BLOCK; 101 int block_count = MAX_BLOCK / 16; 102 do { 103 DO16(bytes); 104 bytes += 16; 105 } while (--block_count); 106 sum1 %= MOD_ADLER; 107 sum2 %= MOD_ADLER; 108 } 109 110 // Do remaining bytes 111 if (size) { 112 while (size >= 16) { 113 size -= 16; 114 DO16(bytes); 115 bytes += 16; 116 } 117 while (size--) { 118 sum1 += *bytes++; 119 sum2 += sum1; 120 } 121 sum1 %= MOD_ADLER; 122 sum2 %= MOD_ADLER; 123 crc_ = (sum2 << 16) | sum1; 124 } 125} 126 127void MachoID::UpdateMD5(unsigned char *bytes, size_t size) { 128 MD5Update(&md5_context_, bytes, size); 129} 130 131void MachoID::Update(MachoWalker *walker, off_t offset, size_t size) { 132 if (!update_function_ || !size) 133 return; 134 135 // Read up to 4k bytes at a time 136 unsigned char buffer[4096]; 137 size_t buffer_size; 138 off_t file_offset = offset; 139 while (size > 0) { 140 if (size > sizeof(buffer)) { 141 buffer_size = sizeof(buffer); 142 size -= buffer_size; 143 } else { 144 buffer_size = size; 145 size = 0; 146 } 147 148 if (!walker->ReadBytes(buffer, buffer_size, file_offset)) 149 return; 150 151 (this->*update_function_)(buffer, buffer_size); 152 file_offset += buffer_size; 153 } 154} 155 156bool MachoID::UUIDCommand(int cpu_type, unsigned char bytes[16]) { 157 struct breakpad_uuid_command uuid_cmd; 158 uuid_cmd.cmd = 0; 159 if (!WalkHeader(cpu_type, UUIDWalkerCB, &uuid_cmd)) 160 return false; 161 162 // If we found the command, we'll have initialized the uuid_command 163 // structure 164 if (uuid_cmd.cmd == LC_UUID) { 165 memcpy(bytes, uuid_cmd.uuid, sizeof(uuid_cmd.uuid)); 166 return true; 167 } 168 169 return false; 170} 171 172bool MachoID::IDCommand(int cpu_type, unsigned char identifier[16]) { 173 struct dylib_command dylib_cmd; 174 dylib_cmd.cmd = 0; 175 if (!WalkHeader(cpu_type, IDWalkerCB, &dylib_cmd)) 176 return false; 177 178 // If we found the command, we'll have initialized the dylib_command 179 // structure 180 if (dylib_cmd.cmd == LC_ID_DYLIB) { 181 // Take the hashed filename, version, and compatability version bytes 182 // to form the first 12 bytes, pad the rest with zeros 183 184 // create a crude hash of the filename to generate the first 4 bytes 185 identifier[0] = 0; 186 identifier[1] = 0; 187 identifier[2] = 0; 188 identifier[3] = 0; 189 190 for (int j = 0, i = (int)strlen(path_)-1; i>=0 && path_[i]!='/'; ++j, --i) { 191 identifier[j%4] += path_[i]; 192 } 193 194 identifier[4] = (dylib_cmd.dylib.current_version >> 24) & 0xFF; 195 identifier[5] = (dylib_cmd.dylib.current_version >> 16) & 0xFF; 196 identifier[6] = (dylib_cmd.dylib.current_version >> 8) & 0xFF; 197 identifier[7] = dylib_cmd.dylib.current_version & 0xFF; 198 identifier[8] = (dylib_cmd.dylib.compatibility_version >> 24) & 0xFF; 199 identifier[9] = (dylib_cmd.dylib.compatibility_version >> 16) & 0xFF; 200 identifier[10] = (dylib_cmd.dylib.compatibility_version >> 8) & 0xFF; 201 identifier[11] = dylib_cmd.dylib.compatibility_version & 0xFF; 202 identifier[12] = (cpu_type >> 24) & 0xFF; 203 identifier[13] = (cpu_type >> 16) & 0xFF; 204 identifier[14] = (cpu_type >> 8) & 0xFF; 205 identifier[15] = cpu_type & 0xFF; 206 207 return true; 208 } 209 210 return false; 211} 212 213uint32_t MachoID::Adler32(int cpu_type) { 214 update_function_ = &MachoID::UpdateCRC; 215 crc_ = 0; 216 217 if (!WalkHeader(cpu_type, WalkerCB, this)) 218 return 0; 219 220 return crc_; 221} 222 223bool MachoID::MD5(int cpu_type, unsigned char identifier[16]) { 224 update_function_ = &MachoID::UpdateMD5; 225 226 MD5Init(&md5_context_); 227 228 if (!WalkHeader(cpu_type, WalkerCB, this)) 229 return false; 230 231 MD5Final(identifier, &md5_context_); 232 return true; 233} 234 235bool MachoID::WalkHeader(int cpu_type, 236 MachoWalker::LoadCommandCallback callback, 237 void *context) { 238 if (memory_) { 239 MachoWalker walker(memory_, memory_size_, callback, context); 240 return walker.WalkHeader(cpu_type); 241 } else { 242 MachoWalker walker(path_, callback, context); 243 return walker.WalkHeader(cpu_type); 244 } 245} 246 247// static 248bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, 249 bool swap, void *context) { 250 MachoID *macho_id = (MachoID *)context; 251 252 if (cmd->cmd == LC_SEGMENT) { 253 struct segment_command seg; 254 255 if (!walker->ReadBytes(&seg, sizeof(seg), offset)) 256 return false; 257 258 if (swap) 259 swap_segment_command(&seg, NXHostByteOrder()); 260 261 struct mach_header_64 header; 262 off_t header_offset; 263 264 if (!walker->CurrentHeader(&header, &header_offset)) 265 return false; 266 267 // Process segments that have sections: 268 // (e.g., __TEXT, __DATA, __IMPORT, __OBJC) 269 offset += sizeof(struct segment_command); 270 struct section sec; 271 for (unsigned long i = 0; i < seg.nsects; ++i) { 272 if (!walker->ReadBytes(&sec, sizeof(sec), offset)) 273 return false; 274 275 if (swap) 276 swap_section(&sec, 1, NXHostByteOrder()); 277 278 // sections of type S_ZEROFILL are "virtual" and contain no data 279 // in the file itself 280 if ((sec.flags & SECTION_TYPE) != S_ZEROFILL && sec.offset != 0) 281 macho_id->Update(walker, header_offset + sec.offset, sec.size); 282 283 offset += sizeof(struct section); 284 } 285 } else if (cmd->cmd == LC_SEGMENT_64) { 286 struct segment_command_64 seg64; 287 288 if (!walker->ReadBytes(&seg64, sizeof(seg64), offset)) 289 return false; 290 291 if (swap) 292 breakpad_swap_segment_command_64(&seg64, NXHostByteOrder()); 293 294 struct mach_header_64 header; 295 off_t header_offset; 296 297 if (!walker->CurrentHeader(&header, &header_offset)) 298 return false; 299 300 // Process segments that have sections: 301 // (e.g., __TEXT, __DATA, __IMPORT, __OBJC) 302 offset += sizeof(struct segment_command_64); 303 struct section_64 sec64; 304 for (unsigned long i = 0; i < seg64.nsects; ++i) { 305 if (!walker->ReadBytes(&sec64, sizeof(sec64), offset)) 306 return false; 307 308 if (swap) 309 breakpad_swap_section_64(&sec64, 1, NXHostByteOrder()); 310 311 // sections of type S_ZEROFILL are "virtual" and contain no data 312 // in the file itself 313 if ((sec64.flags & SECTION_TYPE) != S_ZEROFILL && sec64.offset != 0) 314 macho_id->Update(walker, 315 header_offset + sec64.offset, 316 (size_t)sec64.size); 317 318 offset += sizeof(struct section_64); 319 } 320 } 321 322 // Continue processing 323 return true; 324} 325 326// static 327bool MachoID::UUIDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, 328 bool swap, void *context) { 329 if (cmd->cmd == LC_UUID) { 330 struct breakpad_uuid_command *uuid_cmd = 331 (struct breakpad_uuid_command *)context; 332 333 if (!walker->ReadBytes(uuid_cmd, sizeof(struct breakpad_uuid_command), 334 offset)) 335 return false; 336 337 if (swap) 338 breakpad_swap_uuid_command(uuid_cmd, NXHostByteOrder()); 339 340 return false; 341 } 342 343 // Continue processing 344 return true; 345} 346 347// static 348bool MachoID::IDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, 349 bool swap, void *context) { 350 if (cmd->cmd == LC_ID_DYLIB) { 351 struct dylib_command *dylib_cmd = (struct dylib_command *)context; 352 353 if (!walker->ReadBytes(dylib_cmd, sizeof(struct dylib_command), offset)) 354 return false; 355 356 if (swap) 357 swap_dylib_command(dylib_cmd, NXHostByteOrder()); 358 359 return false; 360 } 361 362 // Continue processing 363 return true; 364} 365 366} // namespace MacFileUtilities