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