/thirdparty/breakpad/client/mac/handler/dynamic_images.cc

http://github.com/tomahawk-player/tomahawk · C++ · 578 lines · 379 code · 99 blank · 100 comment · 60 complexity · fc976ab3be307d339fa7b31cbd70513c MD5 · raw file

  1. // Copyright (c) 2007, 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. #include "client/mac/handler/dynamic_images.h"
  30. extern "C" { // needed to compile on Leopard
  31. #include <mach-o/nlist.h>
  32. #include <stdlib.h>
  33. #include <stdio.h>
  34. }
  35. #include <assert.h>
  36. #include <AvailabilityMacros.h>
  37. #include <dlfcn.h>
  38. #include <mach/task_info.h>
  39. #include <sys/sysctl.h>
  40. #include <TargetConditionals.h>
  41. #include <algorithm>
  42. #include <string>
  43. #include <vector>
  44. #include "breakpad_nlist_64.h"
  45. #if !TARGET_OS_IPHONE
  46. #include <CoreServices/CoreServices.h>
  47. #ifndef MAC_OS_X_VERSION_10_6
  48. #define MAC_OS_X_VERSION_10_6 1060
  49. #endif
  50. #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
  51. // Fallback declarations for TASK_DYLD_INFO and friends, introduced in
  52. // <mach/task_info.h> in the Mac OS X 10.6 SDK.
  53. #define TASK_DYLD_INFO 17
  54. struct task_dyld_info {
  55. mach_vm_address_t all_image_info_addr;
  56. mach_vm_size_t all_image_info_size;
  57. };
  58. typedef struct task_dyld_info task_dyld_info_data_t;
  59. typedef struct task_dyld_info *task_dyld_info_t;
  60. #define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t))
  61. #endif
  62. #endif // !TARGET_OS_IPHONE
  63. namespace google_breakpad {
  64. using std::string;
  65. using std::vector;
  66. //==============================================================================
  67. // Returns the size of the memory region containing |address| and the
  68. // number of bytes from |address| to the end of the region.
  69. // We potentially, will extend the size of the original
  70. // region by the size of the following region if it's contiguous with the
  71. // first in order to handle cases when we're reading strings and they
  72. // straddle two vm regions.
  73. //
  74. static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task,
  75. const uint64_t address,
  76. mach_vm_size_t *size_to_end) {
  77. mach_vm_address_t region_base = (mach_vm_address_t)address;
  78. mach_vm_size_t region_size;
  79. natural_t nesting_level = 0;
  80. vm_region_submap_info_64 submap_info;
  81. mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
  82. // Get information about the vm region containing |address|
  83. vm_region_recurse_info_t region_info;
  84. region_info = reinterpret_cast<vm_region_recurse_info_t>(&submap_info);
  85. kern_return_t result =
  86. mach_vm_region_recurse(target_task,
  87. &region_base,
  88. &region_size,
  89. &nesting_level,
  90. region_info,
  91. &info_count);
  92. if (result == KERN_SUCCESS) {
  93. // Get distance from |address| to the end of this region
  94. *size_to_end = region_base + region_size -(mach_vm_address_t)address;
  95. // If we want to handle strings as long as 4096 characters we may need
  96. // to check if there's a vm region immediately following the first one.
  97. // If so, we need to extend |*size_to_end| to go all the way to the end
  98. // of the second region.
  99. if (*size_to_end < 4096) {
  100. // Second region starts where the first one ends
  101. mach_vm_address_t region_base2 =
  102. (mach_vm_address_t)(region_base + region_size);
  103. mach_vm_size_t region_size2;
  104. // Get information about the following vm region
  105. result =
  106. mach_vm_region_recurse(target_task,
  107. &region_base2,
  108. &region_size2,
  109. &nesting_level,
  110. region_info,
  111. &info_count);
  112. // Extend region_size to go all the way to the end of the 2nd region
  113. if (result == KERN_SUCCESS
  114. && region_base2 == region_base + region_size) {
  115. region_size += region_size2;
  116. }
  117. }
  118. *size_to_end = region_base + region_size -(mach_vm_address_t)address;
  119. } else {
  120. region_size = 0;
  121. *size_to_end = 0;
  122. }
  123. return region_size;
  124. }
  125. #define kMaxStringLength 8192
  126. //==============================================================================
  127. // Reads a NULL-terminated string from another task.
  128. //
  129. // Warning! This will not read any strings longer than kMaxStringLength-1
  130. //
  131. static string ReadTaskString(task_port_t target_task,
  132. const uint64_t address) {
  133. // The problem is we don't know how much to read until we know how long
  134. // the string is. And we don't know how long the string is, until we've read
  135. // the memory! So, we'll try to read kMaxStringLength bytes
  136. // (or as many bytes as we can until we reach the end of the vm region).
  137. mach_vm_size_t size_to_end;
  138. GetMemoryRegionSize(target_task, address, &size_to_end);
  139. if (size_to_end > 0) {
  140. mach_vm_size_t size_to_read =
  141. size_to_end > kMaxStringLength ? kMaxStringLength : size_to_end;
  142. vector<uint8_t> bytes;
  143. if (ReadTaskMemory(target_task, address, (size_t)size_to_read, bytes) !=
  144. KERN_SUCCESS)
  145. return string();
  146. return string(reinterpret_cast<const char*>(&bytes[0]));
  147. }
  148. return string();
  149. }
  150. //==============================================================================
  151. // Reads an address range from another task. The bytes read will be returned
  152. // in bytes, which will be resized as necessary.
  153. kern_return_t ReadTaskMemory(task_port_t target_task,
  154. const uint64_t address,
  155. size_t length,
  156. vector<uint8_t> &bytes) {
  157. int systemPageSize = getpagesize();
  158. // use the negative of the page size for the mask to find the page address
  159. mach_vm_address_t page_address = address & (-systemPageSize);
  160. mach_vm_address_t last_page_address =
  161. (address + length + (systemPageSize - 1)) & (-systemPageSize);
  162. mach_vm_size_t page_size = last_page_address - page_address;
  163. uint8_t* local_start;
  164. uint32_t local_length;
  165. kern_return_t r = mach_vm_read(target_task,
  166. page_address,
  167. page_size,
  168. reinterpret_cast<vm_offset_t*>(&local_start),
  169. &local_length);
  170. if (r != KERN_SUCCESS)
  171. return r;
  172. bytes.resize(length);
  173. memcpy(&bytes[0],
  174. &local_start[(mach_vm_address_t)address - page_address],
  175. length);
  176. mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length);
  177. return KERN_SUCCESS;
  178. }
  179. #pragma mark -
  180. //==============================================================================
  181. // Traits structs for specializing function templates to handle
  182. // 32-bit/64-bit Mach-O files.
  183. struct MachO32 {
  184. typedef mach_header mach_header_type;
  185. typedef segment_command mach_segment_command_type;
  186. typedef dyld_image_info32 dyld_image_info;
  187. typedef dyld_all_image_infos32 dyld_all_image_infos;
  188. typedef struct nlist nlist_type;
  189. static const uint32_t magic = MH_MAGIC;
  190. static const uint32_t segment_load_command = LC_SEGMENT;
  191. };
  192. struct MachO64 {
  193. typedef mach_header_64 mach_header_type;
  194. typedef segment_command_64 mach_segment_command_type;
  195. typedef dyld_image_info64 dyld_image_info;
  196. typedef dyld_all_image_infos64 dyld_all_image_infos;
  197. typedef struct nlist_64 nlist_type;
  198. static const uint32_t magic = MH_MAGIC_64;
  199. static const uint32_t segment_load_command = LC_SEGMENT_64;
  200. };
  201. template<typename MachBits>
  202. bool FindTextSection(DynamicImage& image) {
  203. typedef typename MachBits::mach_header_type mach_header_type;
  204. typedef typename MachBits::mach_segment_command_type
  205. mach_segment_command_type;
  206. const mach_header_type* header =
  207. reinterpret_cast<const mach_header_type*>(&image.header_[0]);
  208. if(header->magic != MachBits::magic) {
  209. return false;
  210. }
  211. const struct load_command *cmd =
  212. reinterpret_cast<const struct load_command *>(header + 1);
  213. bool found_text_section = false;
  214. bool found_dylib_id_command = false;
  215. for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) {
  216. if (!found_text_section) {
  217. if (cmd->cmd == MachBits::segment_load_command) {
  218. const mach_segment_command_type *seg =
  219. reinterpret_cast<const mach_segment_command_type *>(cmd);
  220. if (!strcmp(seg->segname, "__TEXT")) {
  221. image.vmaddr_ = seg->vmaddr;
  222. image.vmsize_ = seg->vmsize;
  223. image.slide_ = 0;
  224. if (seg->fileoff == 0 && seg->filesize != 0) {
  225. image.slide_ =
  226. (uintptr_t)image.GetLoadAddress() - (uintptr_t)seg->vmaddr;
  227. }
  228. found_text_section = true;
  229. }
  230. }
  231. }
  232. if (!found_dylib_id_command) {
  233. if (cmd->cmd == LC_ID_DYLIB) {
  234. const struct dylib_command *dc =
  235. reinterpret_cast<const struct dylib_command *>(cmd);
  236. image.version_ = dc->dylib.current_version;
  237. found_dylib_id_command = true;
  238. }
  239. }
  240. if (found_dylib_id_command && found_text_section) {
  241. return true;
  242. }
  243. cmd = reinterpret_cast<const struct load_command *>
  244. (reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
  245. }
  246. return false;
  247. }
  248. //==============================================================================
  249. // Initializes vmaddr_, vmsize_, and slide_
  250. void DynamicImage::CalculateMemoryAndVersionInfo() {
  251. // unless we can process the header, ensure that calls to
  252. // IsValid() will return false
  253. vmaddr_ = 0;
  254. vmsize_ = 0;
  255. slide_ = 0;
  256. version_ = 0;
  257. // The function template above does all the real work.
  258. if (Is64Bit())
  259. FindTextSection<MachO64>(*this);
  260. else
  261. FindTextSection<MachO32>(*this);
  262. }
  263. //==============================================================================
  264. // The helper function template abstracts the 32/64-bit differences.
  265. template<typename MachBits>
  266. uint32_t GetFileTypeFromHeader(DynamicImage& image) {
  267. typedef typename MachBits::mach_header_type mach_header_type;
  268. const mach_header_type* header =
  269. reinterpret_cast<const mach_header_type*>(&image.header_[0]);
  270. return header->filetype;
  271. }
  272. uint32_t DynamicImage::GetFileType() {
  273. if (Is64Bit())
  274. return GetFileTypeFromHeader<MachO64>(*this);
  275. return GetFileTypeFromHeader<MachO32>(*this);
  276. }
  277. #pragma mark -
  278. //==============================================================================
  279. // Loads information about dynamically loaded code in the given task.
  280. DynamicImages::DynamicImages(mach_port_t task)
  281. : task_(task),
  282. cpu_type_(DetermineTaskCPUType(task)),
  283. image_list_() {
  284. ReadImageInfoForTask();
  285. }
  286. template<typename MachBits>
  287. static uint64_t LookupSymbol(const char* symbol_name,
  288. const char* filename,
  289. cpu_type_t cpu_type) {
  290. typedef typename MachBits::nlist_type nlist_type;
  291. nlist_type symbol_info[8] = {};
  292. const char *symbolNames[2] = { symbol_name, "\0" };
  293. nlist_type &list = symbol_info[0];
  294. int invalidEntriesCount = breakpad_nlist(filename,
  295. &list,
  296. symbolNames,
  297. cpu_type);
  298. if(invalidEntriesCount != 0) {
  299. return 0;
  300. }
  301. assert(list.n_value);
  302. return list.n_value;
  303. }
  304. #if TARGET_OS_IPHONE
  305. static bool HasTaskDyldInfo() {
  306. return true;
  307. }
  308. #else
  309. static SInt32 GetOSVersionInternal() {
  310. SInt32 os_version = 0;
  311. Gestalt(gestaltSystemVersion, &os_version);
  312. return os_version;
  313. }
  314. static SInt32 GetOSVersion() {
  315. static SInt32 os_version = GetOSVersionInternal();
  316. return os_version;
  317. }
  318. static bool HasTaskDyldInfo() {
  319. #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
  320. return true;
  321. #else
  322. return GetOSVersion() >= 0x1060;
  323. #endif
  324. }
  325. #endif // TARGET_OS_IPHONE
  326. uint64_t DynamicImages::GetDyldAllImageInfosPointer() {
  327. if (HasTaskDyldInfo()) {
  328. task_dyld_info_data_t task_dyld_info;
  329. mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
  330. if (task_info(task_, TASK_DYLD_INFO, (task_info_t)&task_dyld_info,
  331. &count) != KERN_SUCCESS) {
  332. return 0;
  333. }
  334. return (uint64_t)task_dyld_info.all_image_info_addr;
  335. } else {
  336. const char *imageSymbolName = "_dyld_all_image_infos";
  337. const char *dyldPath = "/usr/lib/dyld";
  338. if (Is64Bit())
  339. return LookupSymbol<MachO64>(imageSymbolName, dyldPath, cpu_type_);
  340. return LookupSymbol<MachO32>(imageSymbolName, dyldPath, cpu_type_);
  341. }
  342. }
  343. //==============================================================================
  344. // This code was written using dyld_debug.c (from Darwin) as a guide.
  345. template<typename MachBits>
  346. void ReadImageInfo(DynamicImages& images,
  347. uint64_t image_list_address) {
  348. typedef typename MachBits::dyld_image_info dyld_image_info;
  349. typedef typename MachBits::dyld_all_image_infos dyld_all_image_infos;
  350. typedef typename MachBits::mach_header_type mach_header_type;
  351. // Read the structure inside of dyld that contains information about
  352. // loaded images. We're reading from the desired task's address space.
  353. // Here we make the assumption that dyld loaded at the same address in
  354. // the crashed process vs. this one. This is an assumption made in
  355. // "dyld_debug.c" and is said to be nearly always valid.
  356. vector<uint8_t> dyld_all_info_bytes;
  357. if (ReadTaskMemory(images.task_,
  358. image_list_address,
  359. sizeof(dyld_all_image_infos),
  360. dyld_all_info_bytes) != KERN_SUCCESS)
  361. return;
  362. dyld_all_image_infos *dyldInfo =
  363. reinterpret_cast<dyld_all_image_infos*>(&dyld_all_info_bytes[0]);
  364. // number of loaded images
  365. int count = dyldInfo->infoArrayCount;
  366. // Read an array of dyld_image_info structures each containing
  367. // information about a loaded image.
  368. vector<uint8_t> dyld_info_array_bytes;
  369. if (ReadTaskMemory(images.task_,
  370. dyldInfo->infoArray,
  371. count * sizeof(dyld_image_info),
  372. dyld_info_array_bytes) != KERN_SUCCESS)
  373. return;
  374. dyld_image_info *infoArray =
  375. reinterpret_cast<dyld_image_info*>(&dyld_info_array_bytes[0]);
  376. images.image_list_.reserve(count);
  377. for (int i = 0; i < count; ++i) {
  378. dyld_image_info &info = infoArray[i];
  379. // First read just the mach_header from the image in the task.
  380. vector<uint8_t> mach_header_bytes;
  381. if (ReadTaskMemory(images.task_,
  382. info.load_address_,
  383. sizeof(mach_header_type),
  384. mach_header_bytes) != KERN_SUCCESS)
  385. continue; // bail on this dynamic image
  386. mach_header_type *header =
  387. reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]);
  388. // Now determine the total amount necessary to read the header
  389. // plus all of the load commands.
  390. size_t header_size =
  391. sizeof(mach_header_type) + header->sizeofcmds;
  392. if (ReadTaskMemory(images.task_,
  393. info.load_address_,
  394. header_size,
  395. mach_header_bytes) != KERN_SUCCESS)
  396. continue;
  397. header = reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]);
  398. // Read the file name from the task's memory space.
  399. string file_path;
  400. if (info.file_path_) {
  401. // Although we're reading kMaxStringLength bytes, it's copied in the
  402. // the DynamicImage constructor below with the correct string length,
  403. // so it's not really wasting memory.
  404. file_path = ReadTaskString(images.task_, info.file_path_);
  405. }
  406. // Create an object representing this image and add it to our list.
  407. DynamicImage *new_image;
  408. new_image = new DynamicImage(&mach_header_bytes[0],
  409. header_size,
  410. info.load_address_,
  411. file_path,
  412. info.file_mod_date_,
  413. images.task_,
  414. images.cpu_type_);
  415. if (new_image->IsValid()) {
  416. images.image_list_.push_back(DynamicImageRef(new_image));
  417. } else {
  418. delete new_image;
  419. }
  420. }
  421. // sorts based on loading address
  422. sort(images.image_list_.begin(), images.image_list_.end());
  423. // remove duplicates - this happens in certain strange cases
  424. // You can see it in DashboardClient when Google Gadgets plugin
  425. // is installed. Apple's crash reporter log and gdb "info shared"
  426. // both show the same library multiple times at the same address
  427. vector<DynamicImageRef>::iterator it = unique(images.image_list_.begin(),
  428. images.image_list_.end());
  429. images.image_list_.erase(it, images.image_list_.end());
  430. }
  431. void DynamicImages::ReadImageInfoForTask() {
  432. uint64_t imageList = GetDyldAllImageInfosPointer();
  433. if (imageList) {
  434. if (Is64Bit())
  435. ReadImageInfo<MachO64>(*this, imageList);
  436. else
  437. ReadImageInfo<MachO32>(*this, imageList);
  438. }
  439. }
  440. //==============================================================================
  441. DynamicImage *DynamicImages::GetExecutableImage() {
  442. int executable_index = GetExecutableImageIndex();
  443. if (executable_index >= 0) {
  444. return GetImage(executable_index);
  445. }
  446. return NULL;
  447. }
  448. //==============================================================================
  449. // returns -1 if failure to find executable
  450. int DynamicImages::GetExecutableImageIndex() {
  451. int image_count = GetImageCount();
  452. for (int i = 0; i < image_count; ++i) {
  453. DynamicImage *image = GetImage(i);
  454. if (image->GetFileType() == MH_EXECUTE) {
  455. return i;
  456. }
  457. }
  458. return -1;
  459. }
  460. //==============================================================================
  461. // static
  462. cpu_type_t DynamicImages::DetermineTaskCPUType(task_t task) {
  463. if (task == mach_task_self())
  464. return GetNativeCPUType();
  465. int mib[CTL_MAXNAME];
  466. size_t mibLen = CTL_MAXNAME;
  467. int err = sysctlnametomib("sysctl.proc_cputype", mib, &mibLen);
  468. if (err == 0) {
  469. assert(mibLen < CTL_MAXNAME);
  470. pid_for_task(task, &mib[mibLen]);
  471. mibLen += 1;
  472. cpu_type_t cpu_type;
  473. size_t cpuTypeSize = sizeof(cpu_type);
  474. sysctl(mib, mibLen, &cpu_type, &cpuTypeSize, 0, 0);
  475. return cpu_type;
  476. }
  477. return GetNativeCPUType();
  478. }
  479. } // namespace google_breakpad