概述:

为什么 crash log 内 Exception Backtrace 部分的地址(stack address)不能从 dsym 文件中查出对应的代码?

因为 ASLR(Address space layout randomization),因为 ASLR 引入了一个 slide (偏移) 。

symbol address = stack address - slide;

slide 可以在运行时 由 API 获取到

  1. dyld_get_image_vmaddr_slide()

也可以根据运行时的 binary image 和   ELF 文件的 load command 计算的到。

slide = (运行时)load address - (链接时)load address;

注意,如果你没有在运行时用 api 获取slide,那么 binary image 就必须要收集,否则你无法从dsym 文件中解析出符号。

这是一个 iOS crash log 文件,为了简洁删除了部分不需要的内容

  1. Incident Identifier: 975CF16A-5259-4DD1-BFDA-D1B155EF5BF0
  2. CrashReporter Key:   562a7cefe034ac086cae453c61278cdd9a4b3288
  3. Hardware Model:      iPad4,1
  4. Process:             MedicalRecordsFolder [382]
  5. Path:                /var/mobile/Applications/05C398CE-21E9-43C2-967F-26DD0A327932/MedicalRecordsFolder.app/MedicalRecordsFolder
  6. Identifier:          com.xingshulin.MedicalRecordIOS
  7. Version:             1 (4.14.0)
  8. Code Type:           ARM-64 (Native)
  9. Parent Process:      launchd [1]
  10. Date/Time:           2015-12-03 19:14:59.921 +0800
  11. OS Version:          iOS 7.1.2 (11D257)
  12. Report Version:      104
  13. Exception Type:  EXC_CRASH (SIGABRT)
  14. Exception Codes: 0x0000000000000000, 0x0000000000000000
  15. Triggered by Thread:  0
  16. Last Exception Backtrace:
  17. 0   CoreFoundation               0x189127100 __exceptionPreprocess + 132
  18. 1   libobjc.A.dylib              0x1959e01fc objc_exception_throw + 60
  19. 2   CoreFoundation               0x189127040 +[NSException raise:format:] + 128
  20. 3   MedicalRecordsFolder         0x100a8666c 0x10003c000 + 10790508
  21. 4   libsystem_platform.dylib     0x19614bb0c _sigtramp + 56
  22. 5   MedicalRecordsFolder         0x1006ef164 0x10003c000 + 7024996
  23. 6   MedicalRecordsFolder         0x1006e8580 0x10003c000 + 6997376
  24. 7   MedicalRecordsFolder         0x1006e8014 0x10003c000 + 6995988
  25. 8   MedicalRecordsFolder         0x1006e7c94 0x10003c000 + 6995092
  26. 9   MedicalRecordsFolder         0x1006f2460 0x10003c000 + 7038048
  27. 10  libdispatch.dylib            0x195fb8014 _dispatch_call_block_and_release + 24
  28. 11  libdispatch.dylib            0x195fb7fd4 _dispatch_client_callout + 16
  29. 12  libdispatch.dylib            0x195fbe4a8 _dispatch_queue_drain + 640
  30. 13  libdispatch.dylib            0x195fba4c0 _dispatch_queue_invoke + 68
  31. 14  libdispatch.dylib            0x195fbf0f4 _dispatch_root_queue_drain + 104
  32. 15  libdispatch.dylib            0x195fbf4fc _dispatch_worker_thread2 + 76
  33. 16  libsystem_pthread.dylib      0x19614d6bc _pthread_wqthread + 356
  34. 17  libsystem_pthread.dylib      0x19614d54c start_wqthread + 4
  35. Thread 0 Crashed:
  36. 0   libsystem_kernel.dylib        0x00000001960ce58c __pthread_kill + 8
  37. 1   libsystem_c.dylib             0x0000000196062804 abort + 108
  38. 2   libc++abi.dylib               0x0000000195288990 abort_message + 84
  39. 3   libc++abi.dylib               0x00000001952a5c28 default_terminate_handler() + 296
  40. 4   libobjc.A.dylib               0x00000001959e04d0 _objc_terminate() + 124
  41. 5   libc++abi.dylib               0x00000001952a3164 std::__terminate(void (*)()) + 12
  42. 6   libc++abi.dylib               0x00000001952a2d38 __cxa_rethrow + 140
  43. 7   libobjc.A.dylib               0x00000001959e03a4 objc_exception_rethrow + 40
  44. 8   CoreFoundation                0x0000000189025e48 CFRunLoopRunSpecific + 572
  45. 9   GraphicsServices              0x000000018ecb5c08 GSEventRunModal + 164
  46. 10  UIKit                         0x000000018c156fc0 UIApplicationMain + 1152
  47. 11  MedicalRecordsFolder          0x000000010018fc70 0x10003c000 + 1391728
  48. 12  libdyld.dylib                 0x0000000195fd3a9c start + 0
  49. Thread 1:
  50. 0   libsystem_kernel.dylib        0x00000001960b5aa8 kevent64 + 8
  51. 1   libdispatch.dylib             0x0000000195fb9998 _dispatch_mgr_thread + 48
  52. ....
  53. Thread 0 crashed with ARM Thread State (64-bit):
  54. x0: 0x0000000000000000   x1: 0x0000000000000000   x2: 0x0000000000000000   x3: 0xffffffffffffffff
  55. x4: 0x0000000000003060   x5: 0x000000016fdc3530   x6: 0x000000000000006e   x7: 0x0000000000000640
  56. x8: 0x0000000008000000   x9: 0x0000000004000000  x10: 0x0000000098efe6f7  x11: 0x0000000198efde94
  57. x12: 0x000000000000006f  x13: 0x0000000000000000  x14: 0x0000000000000000  x15: 0x000000019607bdcb
  58. x16: 0x0000000000000148  x17: 0x004b96d3524ed02c  x18: 0x0000000000000000  x19: 0x0000000000000006
  59. x20: 0x0000000198f112a0  x21: 0x434c4e47432b2b00  x22: 0x434c4e47432b2b00  x23: 0x0000000000000001
  60. x24: 0x00000001701578c0  x25: 0x0000000000000001  x26: 0x0000000170002ea0  x27: 0x00000001963e1410
  61. x28: 0x0000000000000000  fp: 0x000000016fdc34b0   lr: 0x000000019615116c
  62. sp: 0x000000016fdc3490   pc: 0x00000001960ce58c cpsr: 0x00000000
  63. Binary Images:
  64. 0x10003c000 - 0x100f7bfff MedicalRecordsFolder arm64  <b5ae3570a013386688c7007ee2e73978> /var/mobile/Applications/05C398CE-21E9-43C2-967F-26DD0A327932/MedicalRecordsFolder.app/MedicalRecordsFolder
  65. 0x12007c000 - 0x1200a3fff dyld arm64  <628da833271c3f9bb8d44c34060f55e0> /usr/lib/dyld
  66. .......

现在来指出其中比较重要的部分

uuid信息

  1. Incident Identifier: 975CF16A-5259-4DD1-BFDA-D1B155EF5BF0

这行指出文件的 uuid ,根据次 uuid 可确定 dsym 文件是否匹配

确定方法如下,后面会打印出该 dsym 文件内所有 架构的 uuid ,看一下 是否包含 就知道了

  1. dwarfdump --uuid MedicalRecordsFolder.app.dSYM/

arch信息不解释

  1. Code Type:           ARM-64 (Native)

下面是 异常信息,异常线程 为 thread 0

  1. Exception Type:  EXC_CRASH (SIGABRT)
  2. Exception Codes: 0x0000000000000000, 0x0000000000000000
  3. Triggered by Thread:  0

接下来看 抛出异常的线程的函数调用栈信息

左侧
第一列,调用顺序 
第二列,对应函数所属的 binary image 
第三列,stack address  
第四列,地址的符号+偏移的表示法,实质内容跟第三列一样(此列不理解也无影响)
  1. Last Exception Backtrace:
  2. 0   CoreFoundation                0x189127100 __exceptionPreprocess + 132
  3. 1   libobjc.A.dylib               0x1959e01fc objc_exception_throw + 60
  4. 2   CoreFoundation                0x189127040 +[NSException raise:format:] + 128
  5. 3   MedicalRecordsFolder          0x100a8666c 0x10003c000 + 10790508
  6. 4   libsystem_platform.dylib      0x19614bb0c _sigtramp + 56
  7. 5   MedicalRecordsFolder          0x1006ef164 0x10003c000 + 7024996
  8. 6   MedicalRecordsFolder          0x1006e8580 0x10003c000 + 6997376
  9. 7   MedicalRecordsFolder          0x1006e8014 0x10003c000 + 6995988
  10. 8   MedicalRecordsFolder          0x1006e7c94 0x10003c000 + 6995092
  11. 9   MedicalRecordsFolder          0x1006f2460 0x10003c000 + 7038048
  12. 10  libdispatch.dylib             0x195fb8014 _dispatch_call_block_and_release + 24
  13. 11  libdispatch.dylib             0x195fb7fd4 _dispatch_client_callout + 16
  14. 12  libdispatch.dylib             0x195fbe4a8 _dispatch_queue_drain + 640
  15. 13  libdispatch.dylib             0x195fba4c0 _dispatch_queue_invoke + 68
  16. 14  libdispatch.dylib             0x195fbf0f4 _dispatch_root_queue_drain + 104
  17. 15  libdispatch.dylib             0x195fbf4fc _dispatch_worker_thread2 + 76
  18. 16  libsystem_pthread.dylib       0x19614d6bc _pthread_wqthread + 356
  19. 17  libsystem_pthread.dylib       0x19614d54c start_wqthread + 4

我们从 binary image 这列里面可以看出 好多都是 动态库调用,动态库也就是说 这是 sdk 里面的东西,即使出了bug 你也修复不了,所以我们需要关心的就只有

第 3、5、6、7、8、9、这些行 ,只有这行行对应的代码 才是你自己的 创作(我工程名就是MedicalRecordsFolder)

是 函数调用顺序是 从下往上,也就是说 第 3 行对应的函数才是出问题时 正在执行的代码片段。

所以 从 dsym 中找到 第三行对应的 符号信息 才可能定位到 问题代码。

第三行第三列

0x100a8666c ,这是 stack address ,注意是 stack address,如果系统没有 ASLR 的话,用这个 stack address 就能在dsym 中找到对应符号信息,但是事实 iOS是有 ASLR 的 。

ASLR 技术Address space layout randomization,ASLR通过将系统可执行程序随机装载到内存里,从而防止缓冲区溢出攻击

由于 ASLR 的缘故,导致 程序crash后生成的crash log 中的  stack address 与 对应的 symbol address 不一致,有一个偏移量 slide,slide是程序装在时随机生成的随机数。

引入新的概念:

stack address

: 程序运行时线程栈中 所有 函数调用的地址

symble address

: dsym文件中函数符号对应的地址,用此地址 在 dsym 文件中可以 查出对应的 符号信息。

无 ASLR 机制时  stack address 等于symble address 。

定义 slide

在ASLR机制下每次启动APP 装在之前 ,会在连接时指定的 进程空间上 加上一个随意的 偏移量,这个偏移量就是 slide。

很简单  symble address = stack address -slide;

但是这个 slide 每次 启动 程序都不同,如何 知道 当时启动时 slide 的值呢 ? 带着疑问继续吧

Load Command

一个 iOS 程序编译链接完之后,生成一个 ELF 二进制文件(也就是程序运行时的映射文件),该文件的详细格式不再赘述,这里只强调一个 segment  _TEXT

下面是 使用 otool 工具查看到的  MedicalRecordsFolder(我的demo程序)的 加载命令 。

  1. $otool -l MedicalRecordsFolder.app/MedicalRecordsFolder
  2. MedicalRecordsFolder.app/MedicalRecordsFolder:
  3. Load command 0
  4. cmd LC_SEGMENT_64
  5. cmdsize 72
  6. segname __PAGEZERO
  7. vmaddr 0x0000000000000000
  8. vmsize 0x0000000100000000
  9. fileoff 0
  10. filesize 0
  11. maxprot 0x00000000
  12. initprot 0x00000000
  13. nsects 0
  14. flags 0x0
  15. Load command 1
  16. cmd LC_SEGMENT_64
  17. cmdsize 792
  18. segname __TEXT
  19. vmaddr 0x0000000100000000
  20. vmsize 0x000000000000c000
  21. fileoff 0
  22. filesize 49152
  23. maxprot 0x00000005
  24. initprot 0x00000005
  25. nsects 9
  26. flags 0x0
  27. ……
  28. Load command 2
  29. cmd LC_SEGMENT_64
  30. cmdsize 1352
  31. segname __DATA
  32. vmaddr 0x000000010000c000
  33. vmsize 0x0000000000004000
  34. fileoff 49152
  35. filesize 16384
  36. maxprot 0x00000003
  37. initprot 0x00000003
  38. nsects 16
  39. flags 0x0
  40. ……
  41. Load command 3
  42. cmd LC_SEGMENT_64
  43. cmdsize 72
  44. segname __LINKEDIT
  45. vmaddr 0x0000000100010000
  46. vmsize 0x000000000000c000
  47. fileoff 65536
  48. filesize 35056
  49. maxprot 0x00000001
  50. initprot 0x00000001
  51. nsects 0
  52. flags 0x0

_TEXT 段的加载命令如下,可知到映射文件中segment _TEXT 对应的  虚拟地址空间从 0x0000000100000000 开始 。

  1. segname __TEXT
  2. vmaddr 0x0000000100000000
  3. vmsize 0x000000000000c000
  4. fileoff 0
  5. filesize 49152



segname __TEXT  就是代码段,也就是说所有的二进制指令

没有 ASLR机制时:

加载时 装载器会将此 ELF 文件的 前 49152 (offset 0 ,filesize 49152)个字节(因为 offset 0 ,filesize 49152)映射到 进程空间以  0x0000000100000000 开始的一块虚拟内存空间里.

有ASLR 机制时:

加载时 装载器会将此 ELF 文件的 前 49152 (offset 0 ,filesize 49152)个字节(因为 offset 0 ,filesize 49152)映射到 进程空间以  0x0000000100000000 (+slide)开始的一块虚拟内存空间里.

所以 : 如果没有 ASLR 机制,那么运行时的内存布局 就和  Load command 中指定的布局一致,也就意味着stack address和 symbol address 一致

有 ASLR 的情况也不复杂,只是 加了一个 随意的偏移量 slide

binary image

程序运行时 的  映射 信息,

  1. Binary Images:
  2. 0x10003c000 - 0x100f7bfff MedicalRecordsFolder arm64  <b5ae3570a013386688c7007ee2e73978> /var/.../MedicalRecordsFolder
  3. 0x12007c000 - 0x1200a3fff dyld arm64  <628da833271c3f9bb8d44c34060f55e0> /usr/lib/dyld

左侧

第一列,虚拟地址空间区块

第二列,映射文件 名

第三列,uuid吧,还不知道,以后再补上

第四列,映射文件路径

计算 slide 和 symbol address

第一行可以看出 进程空间的 0x10003c000 - 0x100f7bfff 这个区域 在运行时被映射为 MedicalRecordsFolder 内的内容,也就是我们的 ELF 文件。

注意这个 区域起始地址 为  0x10003c000

而我们在 Load Command 中看到的却是  0x0000000100000000

  1. segname __TEXT
  2. vmaddr 0x0000000100000000
  3. vmsize 0x000000000000c000

显而易见:

slide =  0x10003c000 - 0x100000000 = 0x3c000;

symbol address = stack address - slide;

stack address 在crash log 中已经找到了。

用的到的symbol地址去 dsym 文件中 查询,命令如下

  1. $dwarfdump --arch arm64 --lookup 0x00123 MedicalRecordsFolder.app.dSYM/

就可以定位下来具体的 代码 函数名,所处的文件,行 等信息了

读取 slide 的 API

这个 slide 的计算还是挺 恶心的 ,要 查看 binary image 的到 load address ,还要查看  对用 ELF 中 _TEXT 的 Load Command 虚拟空间范围.

如果 自己写一个 模块 来 收集 NSException 的话 ,大可不必这么繁琐,因为 程序 运行时 有 api 是可以 直接获取这个 binary image 对应的  slide 值的 。

如下:

  1. #import <mach-o/dyld.h>
  2. void calculate(void) {
  3. for (uint32_t i = 0; i < _dyld_image_count(); i++) {
  4. if (_dyld_get_image_header(i)->filetype == MH_EXECUTE) {
  5. long slide = _dyld_get_image_vmaddr_slide(i);
  6. break;
  7. }
  8. }
  9. }

这样 就可以 将 stack address 直接 减去 slide 之后 再 上传到自己的  服务端,岂不是 很完美。

iOS crash log 解析 symbol address = stack address - slide 运行时获取slide的api 利用dwarfdump从dsym文件中得到symbol的更多相关文章

  1. iOS crash log 解析

    iOS开发中,经常遇到App在开发及测试时不会有问题,但是装在别人的设备中会出现各种不定时的莫名的 crash,因为iOS设备会保存应用的大部分的 crash Log,所以可以通过 crash Log ...

  2. iOS开发——运行时OC篇&使用运行时获取系统的属性:使用自己的手势修改系统自带的手势

    使用运行时获取系统的属性:使用自己的手势修改系统自带的手势 有的时候我需要实现一个功能,但是没有想到很好的方法或者想到了方法只是那个方法实现起来太麻烦,一或者确实为了装逼,我们就会想到iOS开发中最牛 ...

  3. ios crash log

    1.IOS策略 1.1 低内存闪退 前面提到大多数crash日志都包含着执行线程的栈调用信息,但是低内存闪退日志除外,这里就先看看低内存闪退日志是什么样的.我们使用Xcode 5和iOS 7的设备模拟 ...

  4. ios Crash Log 分析汇总

    方法一: 1.xcode 有自带的symbolicatecrash,可以将.crash文件中的16进制地址转换成可读的函数地址. symbolicatecrash位于: /Applications/X ...

  5. AtoS查看iOS Crash log中的16进制代码日志

    注意:crash_log一定要和打包时的archive对应上: 方法1)在Orgnizer里找到某一个archive,即:/Users/handywang/Library/Developer/Xcod ...

  6. iOS学习之Objective-C 2.0 运行时系统编程

    0 导言 本主主要内容包括: 1.概述2.参考3.运行时系统的版本和平台4.和运行时系统的交互5.消息6.动态方法解析7.消息转发8.类型编码9.属性声明 1 概述 Objective-C语言将决定尽 ...

  7. 别用symbolicatecrash来解析crash Log了

    今天突然发现了一个解析iOS crash log的好方法,忍不住来分享一下. 相信每个做iOS开发的TX都应该不会对symbolicatecrash陌生,我们第一次遇到真机上产生的崩溃日志时,在网上搜 ...

  8. iOS 几种常用的 crash log 崩溃信息调试方法

    前言:crash log 对 定位崩溃问题 ,并且不容易复现,尤其是及时对appstore 上正在运营的 app 的迭代改进来说 非常重要. 1 crash两种情况 1.1 测试环境下 追踪bug 1 ...

  9. 了解和分析iOS Crash

    WeTest 导读 北京时间凌晨一点,苹果一年一度的发布会如期而至.新机型的发布又会让适配相关的同学忙上一阵子啦,并且iOS Crash的问题始终伴随着移动开发者.本文将从三个阶段,由浅入深的介绍如何 ...

随机推荐

  1. Spring Cloud简介/版本选择/ZooKeeper例子搭建简单说明

    一.什么是Spring Cloud 官方的说法就是Spring Cloud 给开发者提供一套按照一定套路快速开发分布式系统的工具. 具体点就是Spring Boot实现的微服务架构开发工具.它为微服务 ...

  2. DELPHI新版本WEBSERVICE的变化

    DELPHI新版本WEBSERVICE,不仅可以编译成ISAPI DLL,依靠IIS部署, 并且还可以编译成单独的EXE,不再依赖IIS就可以独立运行,这一点未尝不可以说是非常方便的改进.

  3. Java - this的使用方法

    this在内部获得当前对象的引用时调用: (1) return返回当前对象; (2) 构造器调用还有一个构造器, 带參数; (3) 參数的名称和数据成员的名称同样; 注意: this构造器在方法中仅仅 ...

  4. 编程算法 - 求1+2+...+n(函数继承) 代码(C++)

    求1+2+...+n(函数继承) 代码(C++) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 求1+2+...+n, 要求不能使用乘除法\for\whi ...

  5. HDU 4005 The war(双连通好题)

    HDU 4005 The war pid=4005" target="_blank" style="">题目链接 题意:给一个连通的无向图.每条 ...

  6. 【基础练习】【高速幂】codevs3285 转圈游戏题解

    转载请注明出处 来自CSDN用户ametake 题目来自NOIP2013TGD1T1 题目描写叙述 Description n 个小伙伴(编号从 0 到 n-1)围坐一圈玩游戏.依照顺时针方向给 n ...

  7. nginx + mysql + php-fpm 环境

    安装 Nginx 想在 CentOS 系统上安装 Nginx ,你得先去加入一个资源库.像这样: vim /etc/yum.repos.d/nginx.repo 使用 vim 命令去打开 /etc/y ...

  8. Android BroadcastAnyWhere(Google Bug 17356824)漏洞具体分析

    Android BroadcastAnyWhere(Google Bug 17356824)漏洞具体分析 作者:简行(又名 低端码农) 继上次Android的LaunchAnyWhere组件安全漏洞后 ...

  9. Android数据存储之IO

    Android开发中免不了数据本地的存储,今天我们来说一说怎样利用IO流来进行数据存储. 这里我们通过模拟一个QQ登陆界面的小demo来实际操作IO流. 功能描写叙述:点击button能够保存用户输入 ...

  10. LeetCode总结--二分查找篇

    二分查找算法尽管简单,但面试中也比較常见.经经常使用来在有序的数列查找某个特定的位置.在LeetCode用到此算法的主要题目有: Search Insert Position Search for a ...