Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | // Copyright (c) 2007, Google Inc. |
michael@0 | 2 | // All rights reserved. |
michael@0 | 3 | // |
michael@0 | 4 | // Redistribution and use in source and binary forms, with or without |
michael@0 | 5 | // modification, are permitted provided that the following conditions are |
michael@0 | 6 | // met: |
michael@0 | 7 | // |
michael@0 | 8 | // * Redistributions of source code must retain the above copyright |
michael@0 | 9 | // notice, this list of conditions and the following disclaimer. |
michael@0 | 10 | // * Redistributions in binary form must reproduce the above |
michael@0 | 11 | // copyright notice, this list of conditions and the following disclaimer |
michael@0 | 12 | // in the documentation and/or other materials provided with the |
michael@0 | 13 | // distribution. |
michael@0 | 14 | // * Neither the name of Google Inc. nor the names of its |
michael@0 | 15 | // contributors may be used to endorse or promote products derived from |
michael@0 | 16 | // this software without specific prior written permission. |
michael@0 | 17 | // |
michael@0 | 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
michael@0 | 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
michael@0 | 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
michael@0 | 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
michael@0 | 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
michael@0 | 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
michael@0 | 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
michael@0 | 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
michael@0 | 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
michael@0 | 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
michael@0 | 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
michael@0 | 29 | |
michael@0 | 30 | #include "client/mac/handler/dynamic_images.h" |
michael@0 | 31 | |
michael@0 | 32 | extern "C" { // needed to compile on Leopard |
michael@0 | 33 | #include <mach-o/nlist.h> |
michael@0 | 34 | #include <stdlib.h> |
michael@0 | 35 | #include <stdio.h> |
michael@0 | 36 | } |
michael@0 | 37 | |
michael@0 | 38 | #include <assert.h> |
michael@0 | 39 | #include <AvailabilityMacros.h> |
michael@0 | 40 | #include <dlfcn.h> |
michael@0 | 41 | #include <mach/task_info.h> |
michael@0 | 42 | #include <sys/sysctl.h> |
michael@0 | 43 | #include <TargetConditionals.h> |
michael@0 | 44 | #include <unistd.h> |
michael@0 | 45 | |
michael@0 | 46 | #include <algorithm> |
michael@0 | 47 | #include <string> |
michael@0 | 48 | #include <vector> |
michael@0 | 49 | |
michael@0 | 50 | #include "breakpad_nlist_64.h" |
michael@0 | 51 | |
michael@0 | 52 | #if !TARGET_OS_IPHONE |
michael@0 | 53 | #include <CoreServices/CoreServices.h> |
michael@0 | 54 | |
michael@0 | 55 | #ifndef MAC_OS_X_VERSION_10_6 |
michael@0 | 56 | #define MAC_OS_X_VERSION_10_6 1060 |
michael@0 | 57 | #endif |
michael@0 | 58 | |
michael@0 | 59 | #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 |
michael@0 | 60 | |
michael@0 | 61 | // Fallback declarations for TASK_DYLD_INFO and friends, introduced in |
michael@0 | 62 | // <mach/task_info.h> in the Mac OS X 10.6 SDK. |
michael@0 | 63 | #define TASK_DYLD_INFO 17 |
michael@0 | 64 | struct task_dyld_info { |
michael@0 | 65 | mach_vm_address_t all_image_info_addr; |
michael@0 | 66 | mach_vm_size_t all_image_info_size; |
michael@0 | 67 | }; |
michael@0 | 68 | typedef struct task_dyld_info task_dyld_info_data_t; |
michael@0 | 69 | typedef struct task_dyld_info *task_dyld_info_t; |
michael@0 | 70 | #define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t)) |
michael@0 | 71 | |
michael@0 | 72 | #endif |
michael@0 | 73 | |
michael@0 | 74 | #endif // !TARGET_OS_IPHONE |
michael@0 | 75 | |
michael@0 | 76 | namespace google_breakpad { |
michael@0 | 77 | |
michael@0 | 78 | using std::string; |
michael@0 | 79 | using std::vector; |
michael@0 | 80 | |
michael@0 | 81 | //============================================================================== |
michael@0 | 82 | // Returns the size of the memory region containing |address| and the |
michael@0 | 83 | // number of bytes from |address| to the end of the region. |
michael@0 | 84 | // We potentially, will extend the size of the original |
michael@0 | 85 | // region by the size of the following region if it's contiguous with the |
michael@0 | 86 | // first in order to handle cases when we're reading strings and they |
michael@0 | 87 | // straddle two vm regions. |
michael@0 | 88 | // |
michael@0 | 89 | static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task, |
michael@0 | 90 | const uint64_t address, |
michael@0 | 91 | mach_vm_size_t *size_to_end) { |
michael@0 | 92 | mach_vm_address_t region_base = (mach_vm_address_t)address; |
michael@0 | 93 | mach_vm_size_t region_size; |
michael@0 | 94 | natural_t nesting_level = 0; |
michael@0 | 95 | vm_region_submap_info_64 submap_info; |
michael@0 | 96 | mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; |
michael@0 | 97 | |
michael@0 | 98 | // Get information about the vm region containing |address| |
michael@0 | 99 | vm_region_recurse_info_t region_info; |
michael@0 | 100 | region_info = reinterpret_cast<vm_region_recurse_info_t>(&submap_info); |
michael@0 | 101 | |
michael@0 | 102 | kern_return_t result = |
michael@0 | 103 | mach_vm_region_recurse(target_task, |
michael@0 | 104 | ®ion_base, |
michael@0 | 105 | ®ion_size, |
michael@0 | 106 | &nesting_level, |
michael@0 | 107 | region_info, |
michael@0 | 108 | &info_count); |
michael@0 | 109 | |
michael@0 | 110 | if (result == KERN_SUCCESS) { |
michael@0 | 111 | // Get distance from |address| to the end of this region |
michael@0 | 112 | *size_to_end = region_base + region_size -(mach_vm_address_t)address; |
michael@0 | 113 | |
michael@0 | 114 | // If we want to handle strings as long as 4096 characters we may need |
michael@0 | 115 | // to check if there's a vm region immediately following the first one. |
michael@0 | 116 | // If so, we need to extend |*size_to_end| to go all the way to the end |
michael@0 | 117 | // of the second region. |
michael@0 | 118 | if (*size_to_end < 4096) { |
michael@0 | 119 | // Second region starts where the first one ends |
michael@0 | 120 | mach_vm_address_t region_base2 = |
michael@0 | 121 | (mach_vm_address_t)(region_base + region_size); |
michael@0 | 122 | mach_vm_size_t region_size2; |
michael@0 | 123 | |
michael@0 | 124 | // Get information about the following vm region |
michael@0 | 125 | result = |
michael@0 | 126 | mach_vm_region_recurse(target_task, |
michael@0 | 127 | ®ion_base2, |
michael@0 | 128 | ®ion_size2, |
michael@0 | 129 | &nesting_level, |
michael@0 | 130 | region_info, |
michael@0 | 131 | &info_count); |
michael@0 | 132 | |
michael@0 | 133 | // Extend region_size to go all the way to the end of the 2nd region |
michael@0 | 134 | if (result == KERN_SUCCESS |
michael@0 | 135 | && region_base2 == region_base + region_size) { |
michael@0 | 136 | region_size += region_size2; |
michael@0 | 137 | } |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | *size_to_end = region_base + region_size -(mach_vm_address_t)address; |
michael@0 | 141 | } else { |
michael@0 | 142 | region_size = 0; |
michael@0 | 143 | *size_to_end = 0; |
michael@0 | 144 | } |
michael@0 | 145 | |
michael@0 | 146 | return region_size; |
michael@0 | 147 | } |
michael@0 | 148 | |
michael@0 | 149 | #define kMaxStringLength 8192 |
michael@0 | 150 | //============================================================================== |
michael@0 | 151 | // Reads a NULL-terminated string from another task. |
michael@0 | 152 | // |
michael@0 | 153 | // Warning! This will not read any strings longer than kMaxStringLength-1 |
michael@0 | 154 | // |
michael@0 | 155 | static string ReadTaskString(task_port_t target_task, |
michael@0 | 156 | const uint64_t address) { |
michael@0 | 157 | // The problem is we don't know how much to read until we know how long |
michael@0 | 158 | // the string is. And we don't know how long the string is, until we've read |
michael@0 | 159 | // the memory! So, we'll try to read kMaxStringLength bytes |
michael@0 | 160 | // (or as many bytes as we can until we reach the end of the vm region). |
michael@0 | 161 | mach_vm_size_t size_to_end; |
michael@0 | 162 | GetMemoryRegionSize(target_task, address, &size_to_end); |
michael@0 | 163 | |
michael@0 | 164 | if (size_to_end > 0) { |
michael@0 | 165 | mach_vm_size_t size_to_read = |
michael@0 | 166 | size_to_end > kMaxStringLength ? kMaxStringLength : size_to_end; |
michael@0 | 167 | |
michael@0 | 168 | vector<uint8_t> bytes; |
michael@0 | 169 | if (ReadTaskMemory(target_task, address, (size_t)size_to_read, bytes) != |
michael@0 | 170 | KERN_SUCCESS) |
michael@0 | 171 | return string(); |
michael@0 | 172 | |
michael@0 | 173 | return string(reinterpret_cast<const char*>(&bytes[0])); |
michael@0 | 174 | } |
michael@0 | 175 | |
michael@0 | 176 | return string(); |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | //============================================================================== |
michael@0 | 180 | // Reads an address range from another task. The bytes read will be returned |
michael@0 | 181 | // in bytes, which will be resized as necessary. |
michael@0 | 182 | kern_return_t ReadTaskMemory(task_port_t target_task, |
michael@0 | 183 | const uint64_t address, |
michael@0 | 184 | size_t length, |
michael@0 | 185 | vector<uint8_t> &bytes) { |
michael@0 | 186 | int systemPageSize = getpagesize(); |
michael@0 | 187 | |
michael@0 | 188 | // use the negative of the page size for the mask to find the page address |
michael@0 | 189 | mach_vm_address_t page_address = address & (-systemPageSize); |
michael@0 | 190 | |
michael@0 | 191 | mach_vm_address_t last_page_address = |
michael@0 | 192 | (address + length + (systemPageSize - 1)) & (-systemPageSize); |
michael@0 | 193 | |
michael@0 | 194 | mach_vm_size_t page_size = last_page_address - page_address; |
michael@0 | 195 | uint8_t* local_start; |
michael@0 | 196 | uint32_t local_length; |
michael@0 | 197 | |
michael@0 | 198 | kern_return_t r = mach_vm_read(target_task, |
michael@0 | 199 | page_address, |
michael@0 | 200 | page_size, |
michael@0 | 201 | reinterpret_cast<vm_offset_t*>(&local_start), |
michael@0 | 202 | &local_length); |
michael@0 | 203 | |
michael@0 | 204 | if (r != KERN_SUCCESS) |
michael@0 | 205 | return r; |
michael@0 | 206 | |
michael@0 | 207 | bytes.resize(length); |
michael@0 | 208 | memcpy(&bytes[0], |
michael@0 | 209 | &local_start[(mach_vm_address_t)address - page_address], |
michael@0 | 210 | length); |
michael@0 | 211 | mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length); |
michael@0 | 212 | return KERN_SUCCESS; |
michael@0 | 213 | } |
michael@0 | 214 | |
michael@0 | 215 | #pragma mark - |
michael@0 | 216 | |
michael@0 | 217 | //============================================================================== |
michael@0 | 218 | // Traits structs for specializing function templates to handle |
michael@0 | 219 | // 32-bit/64-bit Mach-O files. |
michael@0 | 220 | struct MachO32 { |
michael@0 | 221 | typedef mach_header mach_header_type; |
michael@0 | 222 | typedef segment_command mach_segment_command_type; |
michael@0 | 223 | typedef dyld_image_info32 dyld_image_info; |
michael@0 | 224 | typedef dyld_all_image_infos32 dyld_all_image_infos; |
michael@0 | 225 | typedef struct nlist nlist_type; |
michael@0 | 226 | static const uint32_t magic = MH_MAGIC; |
michael@0 | 227 | static const uint32_t segment_load_command = LC_SEGMENT; |
michael@0 | 228 | }; |
michael@0 | 229 | |
michael@0 | 230 | struct MachO64 { |
michael@0 | 231 | typedef mach_header_64 mach_header_type; |
michael@0 | 232 | typedef segment_command_64 mach_segment_command_type; |
michael@0 | 233 | typedef dyld_image_info64 dyld_image_info; |
michael@0 | 234 | typedef dyld_all_image_infos64 dyld_all_image_infos; |
michael@0 | 235 | typedef struct nlist_64 nlist_type; |
michael@0 | 236 | static const uint32_t magic = MH_MAGIC_64; |
michael@0 | 237 | static const uint32_t segment_load_command = LC_SEGMENT_64; |
michael@0 | 238 | }; |
michael@0 | 239 | |
michael@0 | 240 | template<typename MachBits> |
michael@0 | 241 | bool FindTextSection(DynamicImage& image) { |
michael@0 | 242 | typedef typename MachBits::mach_header_type mach_header_type; |
michael@0 | 243 | typedef typename MachBits::mach_segment_command_type |
michael@0 | 244 | mach_segment_command_type; |
michael@0 | 245 | |
michael@0 | 246 | const mach_header_type* header = |
michael@0 | 247 | reinterpret_cast<const mach_header_type*>(&image.header_[0]); |
michael@0 | 248 | |
michael@0 | 249 | if(header->magic != MachBits::magic) { |
michael@0 | 250 | return false; |
michael@0 | 251 | } |
michael@0 | 252 | |
michael@0 | 253 | const struct load_command *cmd = |
michael@0 | 254 | reinterpret_cast<const struct load_command *>(header + 1); |
michael@0 | 255 | |
michael@0 | 256 | bool found_text_section = false; |
michael@0 | 257 | bool found_dylib_id_command = false; |
michael@0 | 258 | for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) { |
michael@0 | 259 | if (!found_text_section) { |
michael@0 | 260 | if (cmd->cmd == MachBits::segment_load_command) { |
michael@0 | 261 | const mach_segment_command_type *seg = |
michael@0 | 262 | reinterpret_cast<const mach_segment_command_type *>(cmd); |
michael@0 | 263 | |
michael@0 | 264 | if (!strcmp(seg->segname, "__TEXT")) { |
michael@0 | 265 | image.vmaddr_ = static_cast<mach_vm_address_t>(seg->vmaddr); |
michael@0 | 266 | image.vmsize_ = static_cast<mach_vm_size_t>(seg->vmsize); |
michael@0 | 267 | image.slide_ = 0; |
michael@0 | 268 | |
michael@0 | 269 | if (seg->fileoff == 0 && seg->filesize != 0) { |
michael@0 | 270 | image.slide_ = |
michael@0 | 271 | (uintptr_t)image.GetLoadAddress() - (uintptr_t)seg->vmaddr; |
michael@0 | 272 | } |
michael@0 | 273 | found_text_section = true; |
michael@0 | 274 | } |
michael@0 | 275 | } |
michael@0 | 276 | } |
michael@0 | 277 | |
michael@0 | 278 | if (!found_dylib_id_command) { |
michael@0 | 279 | if (cmd->cmd == LC_ID_DYLIB) { |
michael@0 | 280 | const struct dylib_command *dc = |
michael@0 | 281 | reinterpret_cast<const struct dylib_command *>(cmd); |
michael@0 | 282 | |
michael@0 | 283 | image.version_ = dc->dylib.current_version; |
michael@0 | 284 | found_dylib_id_command = true; |
michael@0 | 285 | } |
michael@0 | 286 | } |
michael@0 | 287 | |
michael@0 | 288 | if (found_dylib_id_command && found_text_section) { |
michael@0 | 289 | return true; |
michael@0 | 290 | } |
michael@0 | 291 | |
michael@0 | 292 | cmd = reinterpret_cast<const struct load_command *> |
michael@0 | 293 | (reinterpret_cast<const char *>(cmd) + cmd->cmdsize); |
michael@0 | 294 | } |
michael@0 | 295 | |
michael@0 | 296 | return false; |
michael@0 | 297 | } |
michael@0 | 298 | |
michael@0 | 299 | //============================================================================== |
michael@0 | 300 | // Initializes vmaddr_, vmsize_, and slide_ |
michael@0 | 301 | void DynamicImage::CalculateMemoryAndVersionInfo() { |
michael@0 | 302 | // unless we can process the header, ensure that calls to |
michael@0 | 303 | // IsValid() will return false |
michael@0 | 304 | vmaddr_ = 0; |
michael@0 | 305 | vmsize_ = 0; |
michael@0 | 306 | slide_ = 0; |
michael@0 | 307 | version_ = 0; |
michael@0 | 308 | |
michael@0 | 309 | // The function template above does all the real work. |
michael@0 | 310 | if (Is64Bit()) |
michael@0 | 311 | FindTextSection<MachO64>(*this); |
michael@0 | 312 | else |
michael@0 | 313 | FindTextSection<MachO32>(*this); |
michael@0 | 314 | } |
michael@0 | 315 | |
michael@0 | 316 | //============================================================================== |
michael@0 | 317 | // The helper function template abstracts the 32/64-bit differences. |
michael@0 | 318 | template<typename MachBits> |
michael@0 | 319 | uint32_t GetFileTypeFromHeader(DynamicImage& image) { |
michael@0 | 320 | typedef typename MachBits::mach_header_type mach_header_type; |
michael@0 | 321 | |
michael@0 | 322 | const mach_header_type* header = |
michael@0 | 323 | reinterpret_cast<const mach_header_type*>(&image.header_[0]); |
michael@0 | 324 | return header->filetype; |
michael@0 | 325 | } |
michael@0 | 326 | |
michael@0 | 327 | uint32_t DynamicImage::GetFileType() { |
michael@0 | 328 | if (Is64Bit()) |
michael@0 | 329 | return GetFileTypeFromHeader<MachO64>(*this); |
michael@0 | 330 | |
michael@0 | 331 | return GetFileTypeFromHeader<MachO32>(*this); |
michael@0 | 332 | } |
michael@0 | 333 | |
michael@0 | 334 | #pragma mark - |
michael@0 | 335 | |
michael@0 | 336 | //============================================================================== |
michael@0 | 337 | // Loads information about dynamically loaded code in the given task. |
michael@0 | 338 | DynamicImages::DynamicImages(mach_port_t task) |
michael@0 | 339 | : task_(task), |
michael@0 | 340 | cpu_type_(DetermineTaskCPUType(task)), |
michael@0 | 341 | image_list_() { |
michael@0 | 342 | ReadImageInfoForTask(); |
michael@0 | 343 | } |
michael@0 | 344 | |
michael@0 | 345 | template<typename MachBits> |
michael@0 | 346 | static uint64_t LookupSymbol(const char* symbol_name, |
michael@0 | 347 | const char* filename, |
michael@0 | 348 | cpu_type_t cpu_type) { |
michael@0 | 349 | typedef typename MachBits::nlist_type nlist_type; |
michael@0 | 350 | |
michael@0 | 351 | nlist_type symbol_info[8] = {}; |
michael@0 | 352 | const char *symbolNames[2] = { symbol_name, "\0" }; |
michael@0 | 353 | nlist_type &list = symbol_info[0]; |
michael@0 | 354 | int invalidEntriesCount = breakpad_nlist(filename, |
michael@0 | 355 | &list, |
michael@0 | 356 | symbolNames, |
michael@0 | 357 | cpu_type); |
michael@0 | 358 | |
michael@0 | 359 | if(invalidEntriesCount != 0) { |
michael@0 | 360 | return 0; |
michael@0 | 361 | } |
michael@0 | 362 | |
michael@0 | 363 | assert(list.n_value); |
michael@0 | 364 | return list.n_value; |
michael@0 | 365 | } |
michael@0 | 366 | |
michael@0 | 367 | #if TARGET_OS_IPHONE |
michael@0 | 368 | static bool HasTaskDyldInfo() { |
michael@0 | 369 | return true; |
michael@0 | 370 | } |
michael@0 | 371 | #else |
michael@0 | 372 | static SInt32 GetOSVersionInternal() { |
michael@0 | 373 | SInt32 os_version = 0; |
michael@0 | 374 | Gestalt(gestaltSystemVersion, &os_version); |
michael@0 | 375 | return os_version; |
michael@0 | 376 | } |
michael@0 | 377 | |
michael@0 | 378 | static SInt32 GetOSVersion() { |
michael@0 | 379 | static SInt32 os_version = GetOSVersionInternal(); |
michael@0 | 380 | return os_version; |
michael@0 | 381 | } |
michael@0 | 382 | |
michael@0 | 383 | static bool HasTaskDyldInfo() { |
michael@0 | 384 | #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 |
michael@0 | 385 | return true; |
michael@0 | 386 | #else |
michael@0 | 387 | return GetOSVersion() >= 0x1060; |
michael@0 | 388 | #endif |
michael@0 | 389 | } |
michael@0 | 390 | #endif // TARGET_OS_IPHONE |
michael@0 | 391 | |
michael@0 | 392 | uint64_t DynamicImages::GetDyldAllImageInfosPointer() { |
michael@0 | 393 | if (HasTaskDyldInfo()) { |
michael@0 | 394 | task_dyld_info_data_t task_dyld_info; |
michael@0 | 395 | mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; |
michael@0 | 396 | if (task_info(task_, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, |
michael@0 | 397 | &count) != KERN_SUCCESS) { |
michael@0 | 398 | return 0; |
michael@0 | 399 | } |
michael@0 | 400 | |
michael@0 | 401 | return (uint64_t)task_dyld_info.all_image_info_addr; |
michael@0 | 402 | } else { |
michael@0 | 403 | const char *imageSymbolName = "_dyld_all_image_infos"; |
michael@0 | 404 | const char *dyldPath = "/usr/lib/dyld"; |
michael@0 | 405 | |
michael@0 | 406 | if (Is64Bit()) |
michael@0 | 407 | return LookupSymbol<MachO64>(imageSymbolName, dyldPath, cpu_type_); |
michael@0 | 408 | return LookupSymbol<MachO32>(imageSymbolName, dyldPath, cpu_type_); |
michael@0 | 409 | } |
michael@0 | 410 | } |
michael@0 | 411 | |
michael@0 | 412 | //============================================================================== |
michael@0 | 413 | // This code was written using dyld_debug.c (from Darwin) as a guide. |
michael@0 | 414 | |
michael@0 | 415 | template<typename MachBits> |
michael@0 | 416 | void ReadImageInfo(DynamicImages& images, |
michael@0 | 417 | uint64_t image_list_address) { |
michael@0 | 418 | typedef typename MachBits::dyld_image_info dyld_image_info; |
michael@0 | 419 | typedef typename MachBits::dyld_all_image_infos dyld_all_image_infos; |
michael@0 | 420 | typedef typename MachBits::mach_header_type mach_header_type; |
michael@0 | 421 | |
michael@0 | 422 | // Read the structure inside of dyld that contains information about |
michael@0 | 423 | // loaded images. We're reading from the desired task's address space. |
michael@0 | 424 | |
michael@0 | 425 | // Here we make the assumption that dyld loaded at the same address in |
michael@0 | 426 | // the crashed process vs. this one. This is an assumption made in |
michael@0 | 427 | // "dyld_debug.c" and is said to be nearly always valid. |
michael@0 | 428 | vector<uint8_t> dyld_all_info_bytes; |
michael@0 | 429 | if (ReadTaskMemory(images.task_, |
michael@0 | 430 | image_list_address, |
michael@0 | 431 | sizeof(dyld_all_image_infos), |
michael@0 | 432 | dyld_all_info_bytes) != KERN_SUCCESS) |
michael@0 | 433 | return; |
michael@0 | 434 | |
michael@0 | 435 | dyld_all_image_infos *dyldInfo = |
michael@0 | 436 | reinterpret_cast<dyld_all_image_infos*>(&dyld_all_info_bytes[0]); |
michael@0 | 437 | |
michael@0 | 438 | // number of loaded images |
michael@0 | 439 | int count = dyldInfo->infoArrayCount; |
michael@0 | 440 | |
michael@0 | 441 | // Read an array of dyld_image_info structures each containing |
michael@0 | 442 | // information about a loaded image. |
michael@0 | 443 | vector<uint8_t> dyld_info_array_bytes; |
michael@0 | 444 | if (ReadTaskMemory(images.task_, |
michael@0 | 445 | dyldInfo->infoArray, |
michael@0 | 446 | count * sizeof(dyld_image_info), |
michael@0 | 447 | dyld_info_array_bytes) != KERN_SUCCESS) |
michael@0 | 448 | return; |
michael@0 | 449 | |
michael@0 | 450 | dyld_image_info *infoArray = |
michael@0 | 451 | reinterpret_cast<dyld_image_info*>(&dyld_info_array_bytes[0]); |
michael@0 | 452 | images.image_list_.reserve(count); |
michael@0 | 453 | |
michael@0 | 454 | for (int i = 0; i < count; ++i) { |
michael@0 | 455 | dyld_image_info &info = infoArray[i]; |
michael@0 | 456 | |
michael@0 | 457 | // First read just the mach_header from the image in the task. |
michael@0 | 458 | vector<uint8_t> mach_header_bytes; |
michael@0 | 459 | if (ReadTaskMemory(images.task_, |
michael@0 | 460 | info.load_address_, |
michael@0 | 461 | sizeof(mach_header_type), |
michael@0 | 462 | mach_header_bytes) != KERN_SUCCESS) |
michael@0 | 463 | continue; // bail on this dynamic image |
michael@0 | 464 | |
michael@0 | 465 | mach_header_type *header = |
michael@0 | 466 | reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]); |
michael@0 | 467 | |
michael@0 | 468 | // Now determine the total amount necessary to read the header |
michael@0 | 469 | // plus all of the load commands. |
michael@0 | 470 | size_t header_size = |
michael@0 | 471 | sizeof(mach_header_type) + header->sizeofcmds; |
michael@0 | 472 | |
michael@0 | 473 | if (ReadTaskMemory(images.task_, |
michael@0 | 474 | info.load_address_, |
michael@0 | 475 | header_size, |
michael@0 | 476 | mach_header_bytes) != KERN_SUCCESS) |
michael@0 | 477 | continue; |
michael@0 | 478 | |
michael@0 | 479 | // Read the file name from the task's memory space. |
michael@0 | 480 | string file_path; |
michael@0 | 481 | if (info.file_path_) { |
michael@0 | 482 | // Although we're reading kMaxStringLength bytes, it's copied in the |
michael@0 | 483 | // the DynamicImage constructor below with the correct string length, |
michael@0 | 484 | // so it's not really wasting memory. |
michael@0 | 485 | file_path = ReadTaskString(images.task_, info.file_path_); |
michael@0 | 486 | } |
michael@0 | 487 | |
michael@0 | 488 | // Create an object representing this image and add it to our list. |
michael@0 | 489 | DynamicImage *new_image; |
michael@0 | 490 | new_image = new DynamicImage(&mach_header_bytes[0], |
michael@0 | 491 | header_size, |
michael@0 | 492 | info.load_address_, |
michael@0 | 493 | file_path, |
michael@0 | 494 | static_cast<uintptr_t>(info.file_mod_date_), |
michael@0 | 495 | images.task_, |
michael@0 | 496 | images.cpu_type_); |
michael@0 | 497 | |
michael@0 | 498 | if (new_image->IsValid()) { |
michael@0 | 499 | images.image_list_.push_back(DynamicImageRef(new_image)); |
michael@0 | 500 | } else { |
michael@0 | 501 | delete new_image; |
michael@0 | 502 | } |
michael@0 | 503 | } |
michael@0 | 504 | |
michael@0 | 505 | // sorts based on loading address |
michael@0 | 506 | sort(images.image_list_.begin(), images.image_list_.end()); |
michael@0 | 507 | // remove duplicates - this happens in certain strange cases |
michael@0 | 508 | // You can see it in DashboardClient when Google Gadgets plugin |
michael@0 | 509 | // is installed. Apple's crash reporter log and gdb "info shared" |
michael@0 | 510 | // both show the same library multiple times at the same address |
michael@0 | 511 | |
michael@0 | 512 | vector<DynamicImageRef>::iterator it = unique(images.image_list_.begin(), |
michael@0 | 513 | images.image_list_.end()); |
michael@0 | 514 | images.image_list_.erase(it, images.image_list_.end()); |
michael@0 | 515 | } |
michael@0 | 516 | |
michael@0 | 517 | void DynamicImages::ReadImageInfoForTask() { |
michael@0 | 518 | uint64_t imageList = GetDyldAllImageInfosPointer(); |
michael@0 | 519 | |
michael@0 | 520 | if (imageList) { |
michael@0 | 521 | if (Is64Bit()) |
michael@0 | 522 | ReadImageInfo<MachO64>(*this, imageList); |
michael@0 | 523 | else |
michael@0 | 524 | ReadImageInfo<MachO32>(*this, imageList); |
michael@0 | 525 | } |
michael@0 | 526 | } |
michael@0 | 527 | |
michael@0 | 528 | //============================================================================== |
michael@0 | 529 | DynamicImage *DynamicImages::GetExecutableImage() { |
michael@0 | 530 | int executable_index = GetExecutableImageIndex(); |
michael@0 | 531 | |
michael@0 | 532 | if (executable_index >= 0) { |
michael@0 | 533 | return GetImage(executable_index); |
michael@0 | 534 | } |
michael@0 | 535 | |
michael@0 | 536 | return NULL; |
michael@0 | 537 | } |
michael@0 | 538 | |
michael@0 | 539 | //============================================================================== |
michael@0 | 540 | // returns -1 if failure to find executable |
michael@0 | 541 | int DynamicImages::GetExecutableImageIndex() { |
michael@0 | 542 | int image_count = GetImageCount(); |
michael@0 | 543 | |
michael@0 | 544 | for (int i = 0; i < image_count; ++i) { |
michael@0 | 545 | DynamicImage *image = GetImage(i); |
michael@0 | 546 | if (image->GetFileType() == MH_EXECUTE) { |
michael@0 | 547 | return i; |
michael@0 | 548 | } |
michael@0 | 549 | } |
michael@0 | 550 | |
michael@0 | 551 | return -1; |
michael@0 | 552 | } |
michael@0 | 553 | |
michael@0 | 554 | //============================================================================== |
michael@0 | 555 | // static |
michael@0 | 556 | cpu_type_t DynamicImages::DetermineTaskCPUType(task_t task) { |
michael@0 | 557 | if (task == mach_task_self()) |
michael@0 | 558 | return GetNativeCPUType(); |
michael@0 | 559 | |
michael@0 | 560 | int mib[CTL_MAXNAME]; |
michael@0 | 561 | size_t mibLen = CTL_MAXNAME; |
michael@0 | 562 | int err = sysctlnametomib("sysctl.proc_cputype", mib, &mibLen); |
michael@0 | 563 | if (err == 0) { |
michael@0 | 564 | assert(mibLen < CTL_MAXNAME); |
michael@0 | 565 | pid_for_task(task, &mib[mibLen]); |
michael@0 | 566 | mibLen += 1; |
michael@0 | 567 | |
michael@0 | 568 | cpu_type_t cpu_type; |
michael@0 | 569 | size_t cpuTypeSize = sizeof(cpu_type); |
michael@0 | 570 | sysctl(mib, mibLen, &cpu_type, &cpuTypeSize, 0, 0); |
michael@0 | 571 | return cpu_type; |
michael@0 | 572 | } |
michael@0 | 573 | |
michael@0 | 574 | return GetNativeCPUType(); |
michael@0 | 575 | } |
michael@0 | 576 | |
michael@0 | 577 | } // namespace google_breakpad |