toolkit/crashreporter/google-breakpad/src/common/mac/macho_id.cc

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_id.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,371 @@
     1.4 +// Copyright (c) 2006, Google Inc.
     1.5 +// All rights reserved.
     1.6 +//
     1.7 +// Redistribution and use in source and binary forms, with or without
     1.8 +// modification, are permitted provided that the following conditions are
     1.9 +// met:
    1.10 +//
    1.11 +//     * Redistributions of source code must retain the above copyright
    1.12 +// notice, this list of conditions and the following disclaimer.
    1.13 +//     * Redistributions in binary form must reproduce the above
    1.14 +// copyright notice, this list of conditions and the following disclaimer
    1.15 +// in the documentation and/or other materials provided with the
    1.16 +// distribution.
    1.17 +//     * Neither the name of Google Inc. nor the names of its
    1.18 +// contributors may be used to endorse or promote products derived from
    1.19 +// this software without specific prior written permission.
    1.20 +//
    1.21 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    1.22 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    1.23 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.24 +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    1.25 +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.26 +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    1.27 +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.28 +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    1.29 +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    1.30 +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    1.31 +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.32 +
    1.33 +// macho_id.cc: Functions to gather identifying information from a macho file
    1.34 +//
    1.35 +// See macho_id.h for documentation
    1.36 +//
    1.37 +// Author: Dan Waylonis
    1.38 +
    1.39 +extern "C" {  // necessary for Leopard
    1.40 +  #include <fcntl.h>
    1.41 +  #include <mach-o/loader.h>
    1.42 +  #include <mach-o/swap.h>
    1.43 +  #include <stdio.h>
    1.44 +  #include <stdlib.h>
    1.45 +  #include <string.h>
    1.46 +  #include <sys/time.h>
    1.47 +  #include <sys/types.h>
    1.48 +  #include <unistd.h>
    1.49 +}
    1.50 +
    1.51 +#include "common/mac/macho_id.h"
    1.52 +#include "common/mac/macho_walker.h"
    1.53 +#include "common/mac/macho_utilities.h"
    1.54 +
    1.55 +namespace MacFileUtilities {
    1.56 +
    1.57 +using google_breakpad::MD5Init;
    1.58 +using google_breakpad::MD5Update;
    1.59 +using google_breakpad::MD5Final;
    1.60 +
    1.61 +MachoID::MachoID(const char *path)
    1.62 +   : memory_(0),
    1.63 +     memory_size_(0),
    1.64 +     crc_(0), 
    1.65 +     md5_context_(), 
    1.66 +     update_function_(NULL) {
    1.67 +  strlcpy(path_, path, sizeof(path_));
    1.68 +}
    1.69 +
    1.70 +MachoID::MachoID(const char *path, void *memory, size_t size)
    1.71 +   : memory_(memory),
    1.72 +     memory_size_(size),
    1.73 +     crc_(0), 
    1.74 +     md5_context_(), 
    1.75 +     update_function_(NULL) {
    1.76 +  strlcpy(path_, path, sizeof(path_));
    1.77 +}
    1.78 +
    1.79 +MachoID::~MachoID() {
    1.80 +}
    1.81 +
    1.82 +// The CRC info is from http://en.wikipedia.org/wiki/Adler-32
    1.83 +// With optimizations from http://www.zlib.net/
    1.84 +
    1.85 +// The largest prime smaller than 65536
    1.86 +#define MOD_ADLER 65521
    1.87 +// MAX_BLOCK is the largest n such that 255n(n+1)/2 + (n+1)(MAX_BLOCK-1) <= 2^32-1
    1.88 +#define MAX_BLOCK 5552
    1.89 +
    1.90 +void MachoID::UpdateCRC(unsigned char *bytes, size_t size) {
    1.91 +// Unrolled loops for summing
    1.92 +#define DO1(buf,i)  {sum1 += (buf)[i]; sum2 += sum1;}
    1.93 +#define DO2(buf,i)  DO1(buf,i); DO1(buf,i+1);
    1.94 +#define DO4(buf,i)  DO2(buf,i); DO2(buf,i+2);
    1.95 +#define DO8(buf,i)  DO4(buf,i); DO4(buf,i+4);
    1.96 +#define DO16(buf)   DO8(buf,0); DO8(buf,8);
    1.97 +  // Split up the crc
    1.98 +  uint32_t sum1 = crc_ & 0xFFFF;
    1.99 +  uint32_t sum2 = (crc_ >> 16) & 0xFFFF;
   1.100 +
   1.101 +  // Do large blocks
   1.102 +  while (size >= MAX_BLOCK) {
   1.103 +    size -= MAX_BLOCK;
   1.104 +    int block_count = MAX_BLOCK / 16;
   1.105 +    do {
   1.106 +      DO16(bytes);
   1.107 +      bytes += 16;
   1.108 +    } while (--block_count);
   1.109 +    sum1 %= MOD_ADLER;
   1.110 +    sum2 %= MOD_ADLER;
   1.111 +  }
   1.112 +
   1.113 +  // Do remaining bytes
   1.114 +  if (size) {
   1.115 +    while (size >= 16) {
   1.116 +      size -= 16;
   1.117 +      DO16(bytes);
   1.118 +      bytes += 16;
   1.119 +    }
   1.120 +    while (size--) {
   1.121 +      sum1 += *bytes++;
   1.122 +      sum2 += sum1;
   1.123 +    }
   1.124 +    sum1 %= MOD_ADLER;
   1.125 +    sum2 %= MOD_ADLER;
   1.126 +    crc_ = (sum2 << 16) | sum1;
   1.127 +  }
   1.128 +}
   1.129 +
   1.130 +void MachoID::UpdateMD5(unsigned char *bytes, size_t size) {
   1.131 +  MD5Update(&md5_context_, bytes, size);
   1.132 +}
   1.133 +
   1.134 +void MachoID::Update(MachoWalker *walker, off_t offset, size_t size) {
   1.135 +  if (!update_function_ || !size)
   1.136 +    return;
   1.137 +
   1.138 +  // Read up to 4k bytes at a time
   1.139 +  unsigned char buffer[4096];
   1.140 +  size_t buffer_size;
   1.141 +  off_t file_offset = offset;
   1.142 +  while (size > 0) {
   1.143 +    if (size > sizeof(buffer)) {
   1.144 +      buffer_size = sizeof(buffer);
   1.145 +      size -= buffer_size;
   1.146 +    } else {
   1.147 +      buffer_size = size;
   1.148 +      size = 0;
   1.149 +    }
   1.150 +
   1.151 +    if (!walker->ReadBytes(buffer, buffer_size, file_offset))
   1.152 +      return;
   1.153 +
   1.154 +    (this->*update_function_)(buffer, buffer_size);
   1.155 +    file_offset += buffer_size;
   1.156 +  }
   1.157 +}
   1.158 +
   1.159 +bool MachoID::UUIDCommand(cpu_type_t cpu_type,
   1.160 +                          cpu_subtype_t cpu_subtype,
   1.161 +                          unsigned char bytes[16]) {
   1.162 +  struct breakpad_uuid_command uuid_cmd;
   1.163 +  uuid_cmd.cmd = 0;
   1.164 +  if (!WalkHeader(cpu_type, cpu_subtype, UUIDWalkerCB, &uuid_cmd))
   1.165 +    return false;
   1.166 +
   1.167 +  // If we found the command, we'll have initialized the uuid_command
   1.168 +  // structure
   1.169 +  if (uuid_cmd.cmd == LC_UUID) {
   1.170 +    memcpy(bytes, uuid_cmd.uuid, sizeof(uuid_cmd.uuid));
   1.171 +    return true;
   1.172 +  }
   1.173 +
   1.174 +  return false;
   1.175 +}
   1.176 +
   1.177 +bool MachoID::IDCommand(cpu_type_t cpu_type,
   1.178 +                        cpu_subtype_t cpu_subtype,
   1.179 +                        unsigned char identifier[16]) {
   1.180 +  struct dylib_command dylib_cmd;
   1.181 +  dylib_cmd.cmd = 0;
   1.182 +  if (!WalkHeader(cpu_type, cpu_subtype, IDWalkerCB, &dylib_cmd))
   1.183 +    return false;
   1.184 +
   1.185 +  // If we found the command, we'll have initialized the dylib_command
   1.186 +  // structure
   1.187 +  if (dylib_cmd.cmd == LC_ID_DYLIB) {
   1.188 +    // Take the hashed filename, version, and compatability version bytes
   1.189 +    // to form the first 12 bytes, pad the rest with zeros
   1.190 +
   1.191 +    // create a crude hash of the filename to generate the first 4 bytes
   1.192 +    identifier[0] = 0;
   1.193 +    identifier[1] = 0;
   1.194 +    identifier[2] = 0;
   1.195 +    identifier[3] = 0;
   1.196 +
   1.197 +    for (int j = 0, i = (int)strlen(path_)-1; i>=0 && path_[i]!='/'; ++j, --i) {
   1.198 +      identifier[j%4] += path_[i];
   1.199 +    }
   1.200 +
   1.201 +    identifier[4] = (dylib_cmd.dylib.current_version >> 24) & 0xFF;
   1.202 +    identifier[5] = (dylib_cmd.dylib.current_version >> 16) & 0xFF;
   1.203 +    identifier[6] = (dylib_cmd.dylib.current_version >> 8) & 0xFF;
   1.204 +    identifier[7] = dylib_cmd.dylib.current_version & 0xFF;
   1.205 +    identifier[8] = (dylib_cmd.dylib.compatibility_version >> 24) & 0xFF;
   1.206 +    identifier[9] = (dylib_cmd.dylib.compatibility_version >> 16) & 0xFF;
   1.207 +    identifier[10] = (dylib_cmd.dylib.compatibility_version >> 8) & 0xFF;
   1.208 +    identifier[11] = dylib_cmd.dylib.compatibility_version & 0xFF;
   1.209 +    identifier[12] = (cpu_type >> 24) & 0xFF;
   1.210 +    identifier[13] = (cpu_type >> 16) & 0xFF;
   1.211 +    identifier[14] = (cpu_type >> 8) & 0xFF;
   1.212 +    identifier[15] = cpu_type & 0xFF;
   1.213 +
   1.214 +    return true;
   1.215 +  }
   1.216 +
   1.217 +  return false;
   1.218 +}
   1.219 +
   1.220 +uint32_t MachoID::Adler32(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
   1.221 +  update_function_ = &MachoID::UpdateCRC;
   1.222 +  crc_ = 0;
   1.223 +
   1.224 +  if (!WalkHeader(cpu_type, cpu_subtype, WalkerCB, this))
   1.225 +    return 0;
   1.226 +
   1.227 +  return crc_;
   1.228 +}
   1.229 +
   1.230 +bool MachoID::MD5(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype, unsigned char identifier[16]) {
   1.231 +  update_function_ = &MachoID::UpdateMD5;
   1.232 +
   1.233 +  MD5Init(&md5_context_);
   1.234 +
   1.235 +  if (!WalkHeader(cpu_type, cpu_subtype, WalkerCB, this))
   1.236 +    return false;
   1.237 +
   1.238 +  MD5Final(identifier, &md5_context_);
   1.239 +  return true;
   1.240 +}
   1.241 +
   1.242 +bool MachoID::WalkHeader(cpu_type_t cpu_type,
   1.243 +                         cpu_subtype_t cpu_subtype,
   1.244 +                         MachoWalker::LoadCommandCallback callback,
   1.245 +                         void *context) {
   1.246 +  if (memory_) {
   1.247 +    MachoWalker walker(memory_, memory_size_, callback, context);
   1.248 +    return walker.WalkHeader(cpu_type, cpu_subtype);
   1.249 +  } else {
   1.250 +    MachoWalker walker(path_, callback, context);
   1.251 +    return walker.WalkHeader(cpu_type, cpu_subtype);
   1.252 +  }
   1.253 +}
   1.254 +
   1.255 +// static
   1.256 +bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
   1.257 +                       bool swap, void *context) {
   1.258 +  MachoID *macho_id = (MachoID *)context;
   1.259 +
   1.260 +  if (cmd->cmd == LC_SEGMENT) {
   1.261 +    struct segment_command seg;
   1.262 +
   1.263 +    if (!walker->ReadBytes(&seg, sizeof(seg), offset))
   1.264 +      return false;
   1.265 +
   1.266 +    if (swap)
   1.267 +      swap_segment_command(&seg, NXHostByteOrder());
   1.268 +
   1.269 +    struct mach_header_64 header;
   1.270 +    off_t header_offset;
   1.271 +    
   1.272 +    if (!walker->CurrentHeader(&header, &header_offset))
   1.273 +      return false;
   1.274 +        
   1.275 +    // Process segments that have sections:
   1.276 +    // (e.g., __TEXT, __DATA, __IMPORT, __OBJC)
   1.277 +    offset += sizeof(struct segment_command);
   1.278 +    struct section sec;
   1.279 +    for (unsigned long i = 0; i < seg.nsects; ++i) {
   1.280 +      if (!walker->ReadBytes(&sec, sizeof(sec), offset))
   1.281 +        return false;
   1.282 +
   1.283 +      if (swap)
   1.284 +        swap_section(&sec, 1, NXHostByteOrder());
   1.285 +
   1.286 +      // sections of type S_ZEROFILL are "virtual" and contain no data
   1.287 +      // in the file itself
   1.288 +      if ((sec.flags & SECTION_TYPE) != S_ZEROFILL && sec.offset != 0)
   1.289 +        macho_id->Update(walker, header_offset + sec.offset, sec.size);
   1.290 +
   1.291 +      offset += sizeof(struct section);
   1.292 +    }
   1.293 +  } else if (cmd->cmd == LC_SEGMENT_64) {
   1.294 +    struct segment_command_64 seg64;
   1.295 +
   1.296 +    if (!walker->ReadBytes(&seg64, sizeof(seg64), offset))
   1.297 +      return false;
   1.298 +
   1.299 +    if (swap)
   1.300 +      breakpad_swap_segment_command_64(&seg64, NXHostByteOrder());
   1.301 +
   1.302 +    struct mach_header_64 header;
   1.303 +    off_t header_offset;
   1.304 +    
   1.305 +    if (!walker->CurrentHeader(&header, &header_offset))
   1.306 +      return false;
   1.307 +    
   1.308 +    // Process segments that have sections:
   1.309 +    // (e.g., __TEXT, __DATA, __IMPORT, __OBJC)
   1.310 +    offset += sizeof(struct segment_command_64);
   1.311 +    struct section_64 sec64;
   1.312 +    for (unsigned long i = 0; i < seg64.nsects; ++i) {
   1.313 +      if (!walker->ReadBytes(&sec64, sizeof(sec64), offset))
   1.314 +        return false;
   1.315 +
   1.316 +      if (swap)
   1.317 +        breakpad_swap_section_64(&sec64, 1, NXHostByteOrder());
   1.318 +
   1.319 +      // sections of type S_ZEROFILL are "virtual" and contain no data
   1.320 +      // in the file itself
   1.321 +      if ((sec64.flags & SECTION_TYPE) != S_ZEROFILL && sec64.offset != 0)
   1.322 +        macho_id->Update(walker, 
   1.323 +                         header_offset + sec64.offset, 
   1.324 +                         (size_t)sec64.size);
   1.325 +
   1.326 +      offset += sizeof(struct section_64);
   1.327 +    }
   1.328 +  }
   1.329 +
   1.330 +  // Continue processing
   1.331 +  return true;
   1.332 +}
   1.333 +
   1.334 +// static
   1.335 +bool MachoID::UUIDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
   1.336 +                           bool swap, void *context) {
   1.337 +  if (cmd->cmd == LC_UUID) {
   1.338 +    struct breakpad_uuid_command *uuid_cmd =
   1.339 +      (struct breakpad_uuid_command *)context;
   1.340 +
   1.341 +    if (!walker->ReadBytes(uuid_cmd, sizeof(struct breakpad_uuid_command),
   1.342 +                           offset))
   1.343 +      return false;
   1.344 +
   1.345 +    if (swap)
   1.346 +      breakpad_swap_uuid_command(uuid_cmd, NXHostByteOrder());
   1.347 +
   1.348 +    return false;
   1.349 +  }
   1.350 +
   1.351 +  // Continue processing
   1.352 +  return true;
   1.353 +}
   1.354 +
   1.355 +// static
   1.356 +bool MachoID::IDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
   1.357 +                         bool swap, void *context) {
   1.358 +  if (cmd->cmd == LC_ID_DYLIB) {
   1.359 +    struct dylib_command *dylib_cmd = (struct dylib_command *)context;
   1.360 +
   1.361 +    if (!walker->ReadBytes(dylib_cmd, sizeof(struct dylib_command), offset))
   1.362 +      return false;
   1.363 +
   1.364 +    if (swap)
   1.365 +      swap_dylib_command(dylib_cmd, NXHostByteOrder());
   1.366 +
   1.367 +    return false;
   1.368 +  }
   1.369 +
   1.370 +  // Continue processing
   1.371 +  return true;
   1.372 +}
   1.373 +
   1.374 +}  // namespace MacFileUtilities

mercurial