iOS crash log 解析 symbol address = stack address - slide 运行时获取slide的api 利用dwarfdump从dsym文件中得到symbol
概述:
为什么 crash log 内 Exception Backtrace 部分的地址(stack address)不能从 dsym 文件中查出对应的代码?
因为 ASLR(Address space layout randomization),因为 ASLR 引入了一个 slide (偏移) 。
symbol address = stack address - slide;
slide 可以在运行时 由 API 获取到
- dyld_get_image_vmaddr_slide()
也可以根据运行时的 binary image 和 ELF 文件的 load command 计算的到。
slide = (运行时)load address - (链接时)load address;
注意,如果你没有在运行时用 api 获取slide,那么 binary image 就必须要收集,否则你无法从dsym 文件中解析出符号。
这是一个 iOS crash log 文件,为了简洁删除了部分不需要的内容
- Incident Identifier: 975CF16A-5259-4DD1-BFDA-D1B155EF5BF0
- CrashReporter Key: 562a7cefe034ac086cae453c61278cdd9a4b3288
- Hardware Model: iPad4,1
- Process: MedicalRecordsFolder [382]
- Path: /var/mobile/Applications/05C398CE-21E9-43C2-967F-26DD0A327932/MedicalRecordsFolder.app/MedicalRecordsFolder
- Identifier: com.xingshulin.MedicalRecordIOS
- Version: 1 (4.14.0)
- Code Type: ARM-64 (Native)
- Parent Process: launchd [1]
- Date/Time: 2015-12-03 19:14:59.921 +0800
- OS Version: iOS 7.1.2 (11D257)
- Report Version: 104
- Exception Type: EXC_CRASH (SIGABRT)
- Exception Codes: 0x0000000000000000, 0x0000000000000000
- Triggered by Thread: 0
- Last Exception Backtrace:
- 0 CoreFoundation 0x189127100 __exceptionPreprocess + 132
- 1 libobjc.A.dylib 0x1959e01fc objc_exception_throw + 60
- 2 CoreFoundation 0x189127040 +[NSException raise:format:] + 128
- 3 MedicalRecordsFolder 0x100a8666c 0x10003c000 + 10790508
- 4 libsystem_platform.dylib 0x19614bb0c _sigtramp + 56
- 5 MedicalRecordsFolder 0x1006ef164 0x10003c000 + 7024996
- 6 MedicalRecordsFolder 0x1006e8580 0x10003c000 + 6997376
- 7 MedicalRecordsFolder 0x1006e8014 0x10003c000 + 6995988
- 8 MedicalRecordsFolder 0x1006e7c94 0x10003c000 + 6995092
- 9 MedicalRecordsFolder 0x1006f2460 0x10003c000 + 7038048
- 10 libdispatch.dylib 0x195fb8014 _dispatch_call_block_and_release + 24
- 11 libdispatch.dylib 0x195fb7fd4 _dispatch_client_callout + 16
- 12 libdispatch.dylib 0x195fbe4a8 _dispatch_queue_drain + 640
- 13 libdispatch.dylib 0x195fba4c0 _dispatch_queue_invoke + 68
- 14 libdispatch.dylib 0x195fbf0f4 _dispatch_root_queue_drain + 104
- 15 libdispatch.dylib 0x195fbf4fc _dispatch_worker_thread2 + 76
- 16 libsystem_pthread.dylib 0x19614d6bc _pthread_wqthread + 356
- 17 libsystem_pthread.dylib 0x19614d54c start_wqthread + 4
- Thread 0 Crashed:
- 0 libsystem_kernel.dylib 0x00000001960ce58c __pthread_kill + 8
- 1 libsystem_c.dylib 0x0000000196062804 abort + 108
- 2 libc++abi.dylib 0x0000000195288990 abort_message + 84
- 3 libc++abi.dylib 0x00000001952a5c28 default_terminate_handler() + 296
- 4 libobjc.A.dylib 0x00000001959e04d0 _objc_terminate() + 124
- 5 libc++abi.dylib 0x00000001952a3164 std::__terminate(void (*)()) + 12
- 6 libc++abi.dylib 0x00000001952a2d38 __cxa_rethrow + 140
- 7 libobjc.A.dylib 0x00000001959e03a4 objc_exception_rethrow + 40
- 8 CoreFoundation 0x0000000189025e48 CFRunLoopRunSpecific + 572
- 9 GraphicsServices 0x000000018ecb5c08 GSEventRunModal + 164
- 10 UIKit 0x000000018c156fc0 UIApplicationMain + 1152
- 11 MedicalRecordsFolder 0x000000010018fc70 0x10003c000 + 1391728
- 12 libdyld.dylib 0x0000000195fd3a9c start + 0
- Thread 1:
- 0 libsystem_kernel.dylib 0x00000001960b5aa8 kevent64 + 8
- 1 libdispatch.dylib 0x0000000195fb9998 _dispatch_mgr_thread + 48
- ....
- Thread 0 crashed with ARM Thread State (64-bit):
- x0: 0x0000000000000000 x1: 0x0000000000000000 x2: 0x0000000000000000 x3: 0xffffffffffffffff
- x4: 0x0000000000003060 x5: 0x000000016fdc3530 x6: 0x000000000000006e x7: 0x0000000000000640
- x8: 0x0000000008000000 x9: 0x0000000004000000 x10: 0x0000000098efe6f7 x11: 0x0000000198efde94
- x12: 0x000000000000006f x13: 0x0000000000000000 x14: 0x0000000000000000 x15: 0x000000019607bdcb
- x16: 0x0000000000000148 x17: 0x004b96d3524ed02c x18: 0x0000000000000000 x19: 0x0000000000000006
- x20: 0x0000000198f112a0 x21: 0x434c4e47432b2b00 x22: 0x434c4e47432b2b00 x23: 0x0000000000000001
- x24: 0x00000001701578c0 x25: 0x0000000000000001 x26: 0x0000000170002ea0 x27: 0x00000001963e1410
- x28: 0x0000000000000000 fp: 0x000000016fdc34b0 lr: 0x000000019615116c
- sp: 0x000000016fdc3490 pc: 0x00000001960ce58c cpsr: 0x00000000
- Binary Images:
- 0x10003c000 - 0x100f7bfff MedicalRecordsFolder arm64 <b5ae3570a013386688c7007ee2e73978> /var/mobile/Applications/05C398CE-21E9-43C2-967F-26DD0A327932/MedicalRecordsFolder.app/MedicalRecordsFolder
- 0x12007c000 - 0x1200a3fff dyld arm64 <628da833271c3f9bb8d44c34060f55e0> /usr/lib/dyld
- .......
现在来指出其中比较重要的部分
uuid信息
- Incident Identifier: 975CF16A-5259-4DD1-BFDA-D1B155EF5BF0
这行指出文件的 uuid ,根据次 uuid 可确定 dsym 文件是否匹配
确定方法如下,后面会打印出该 dsym 文件内所有 架构的 uuid ,看一下 是否包含 就知道了
- dwarfdump --uuid MedicalRecordsFolder.app.dSYM/
arch信息不解释
- Code Type: ARM-64 (Native)
下面是 异常信息,异常线程 为 thread 0
- Exception Type: EXC_CRASH (SIGABRT)
- Exception Codes: 0x0000000000000000, 0x0000000000000000
- Triggered by Thread: 0
接下来看 抛出异常的线程的函数调用栈信息
- Last Exception Backtrace:
- 0 CoreFoundation 0x189127100 __exceptionPreprocess + 132
- 1 libobjc.A.dylib 0x1959e01fc objc_exception_throw + 60
- 2 CoreFoundation 0x189127040 +[NSException raise:format:] + 128
- 3 MedicalRecordsFolder 0x100a8666c 0x10003c000 + 10790508
- 4 libsystem_platform.dylib 0x19614bb0c _sigtramp + 56
- 5 MedicalRecordsFolder 0x1006ef164 0x10003c000 + 7024996
- 6 MedicalRecordsFolder 0x1006e8580 0x10003c000 + 6997376
- 7 MedicalRecordsFolder 0x1006e8014 0x10003c000 + 6995988
- 8 MedicalRecordsFolder 0x1006e7c94 0x10003c000 + 6995092
- 9 MedicalRecordsFolder 0x1006f2460 0x10003c000 + 7038048
- 10 libdispatch.dylib 0x195fb8014 _dispatch_call_block_and_release + 24
- 11 libdispatch.dylib 0x195fb7fd4 _dispatch_client_callout + 16
- 12 libdispatch.dylib 0x195fbe4a8 _dispatch_queue_drain + 640
- 13 libdispatch.dylib 0x195fba4c0 _dispatch_queue_invoke + 68
- 14 libdispatch.dylib 0x195fbf0f4 _dispatch_root_queue_drain + 104
- 15 libdispatch.dylib 0x195fbf4fc _dispatch_worker_thread2 + 76
- 16 libsystem_pthread.dylib 0x19614d6bc _pthread_wqthread + 356
- 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程序)的 加载命令 。
- $otool -l MedicalRecordsFolder.app/MedicalRecordsFolder
- MedicalRecordsFolder.app/MedicalRecordsFolder:
- Load command 0
- cmd LC_SEGMENT_64
- cmdsize 72
- segname __PAGEZERO
- vmaddr 0x0000000000000000
- vmsize 0x0000000100000000
- fileoff 0
- filesize 0
- maxprot 0x00000000
- initprot 0x00000000
- nsects 0
- flags 0x0
- Load command 1
- cmd LC_SEGMENT_64
- cmdsize 792
- segname __TEXT
- vmaddr 0x0000000100000000
- vmsize 0x000000000000c000
- fileoff 0
- filesize 49152
- maxprot 0x00000005
- initprot 0x00000005
- nsects 9
- flags 0x0
- ……
- Load command 2
- cmd LC_SEGMENT_64
- cmdsize 1352
- segname __DATA
- vmaddr 0x000000010000c000
- vmsize 0x0000000000004000
- fileoff 49152
- filesize 16384
- maxprot 0x00000003
- initprot 0x00000003
- nsects 16
- flags 0x0
- ……
- Load command 3
- cmd LC_SEGMENT_64
- cmdsize 72
- segname __LINKEDIT
- vmaddr 0x0000000100010000
- vmsize 0x000000000000c000
- fileoff 65536
- filesize 35056
- maxprot 0x00000001
- initprot 0x00000001
- nsects 0
- flags 0x0
_TEXT 段的加载命令如下,可知到映射文件中segment _TEXT 对应的 虚拟地址空间从 0x0000000100000000 开始 。
- segname __TEXT
- vmaddr 0x0000000100000000
- vmsize 0x000000000000c000
- fileoff 0
- 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
程序运行时 的 映射 信息,
- Binary Images:
- 0x10003c000 - 0x100f7bfff MedicalRecordsFolder arm64 <b5ae3570a013386688c7007ee2e73978> /var/.../MedicalRecordsFolder
- 0x12007c000 - 0x1200a3fff dyld arm64 <628da833271c3f9bb8d44c34060f55e0> /usr/lib/dyld
左侧
第一列,虚拟地址空间区块
第二列,映射文件 名
第三列,uuid吧,还不知道,以后再补上
第四列,映射文件路径
计算 slide 和 symbol address
第一行可以看出 进程空间的 0x10003c000 - 0x100f7bfff 这个区域 在运行时被映射为 MedicalRecordsFolder 内的内容,也就是我们的 ELF 文件。
注意这个 区域起始地址 为 0x10003c000
而我们在 Load Command 中看到的却是 0x0000000100000000
- segname __TEXT
- vmaddr 0x0000000100000000
- vmsize 0x000000000000c000
显而易见:
slide = 0x10003c000 - 0x100000000 = 0x3c000;
symbol address = stack address - slide;
stack address 在crash log 中已经找到了。
用的到的symbol地址去 dsym 文件中 查询,命令如下
- $dwarfdump --arch arm64 --lookup 0x00123 MedicalRecordsFolder.app.dSYM/
就可以定位下来具体的 代码 函数名,所处的文件,行 等信息了
读取 slide 的 API
这个 slide 的计算还是挺 恶心的 ,要 查看 binary image 的到 load address ,还要查看 对用 ELF 中 _TEXT 的 Load Command 虚拟空间范围.
如果 自己写一个 模块 来 收集 NSException 的话 ,大可不必这么繁琐,因为 程序 运行时 有 api 是可以 直接获取这个 binary image 对应的 slide 值的 。
如下:
- #import <mach-o/dyld.h>
- void calculate(void) {
- for (uint32_t i = 0; i < _dyld_image_count(); i++) {
- if (_dyld_get_image_header(i)->filetype == MH_EXECUTE) {
- long slide = _dyld_get_image_vmaddr_slide(i);
- break;
- }
- }
- }
这样 就可以 将 stack address 直接 减去 slide 之后 再 上传到自己的 服务端,岂不是 很完美。
iOS crash log 解析 symbol address = stack address - slide 运行时获取slide的api 利用dwarfdump从dsym文件中得到symbol的更多相关文章
- iOS crash log 解析
iOS开发中,经常遇到App在开发及测试时不会有问题,但是装在别人的设备中会出现各种不定时的莫名的 crash,因为iOS设备会保存应用的大部分的 crash Log,所以可以通过 crash Log ...
- iOS开发——运行时OC篇&使用运行时获取系统的属性:使用自己的手势修改系统自带的手势
使用运行时获取系统的属性:使用自己的手势修改系统自带的手势 有的时候我需要实现一个功能,但是没有想到很好的方法或者想到了方法只是那个方法实现起来太麻烦,一或者确实为了装逼,我们就会想到iOS开发中最牛 ...
- ios crash log
1.IOS策略 1.1 低内存闪退 前面提到大多数crash日志都包含着执行线程的栈调用信息,但是低内存闪退日志除外,这里就先看看低内存闪退日志是什么样的.我们使用Xcode 5和iOS 7的设备模拟 ...
- ios Crash Log 分析汇总
方法一: 1.xcode 有自带的symbolicatecrash,可以将.crash文件中的16进制地址转换成可读的函数地址. symbolicatecrash位于: /Applications/X ...
- AtoS查看iOS Crash log中的16进制代码日志
注意:crash_log一定要和打包时的archive对应上: 方法1)在Orgnizer里找到某一个archive,即:/Users/handywang/Library/Developer/Xcod ...
- iOS学习之Objective-C 2.0 运行时系统编程
0 导言 本主主要内容包括: 1.概述2.参考3.运行时系统的版本和平台4.和运行时系统的交互5.消息6.动态方法解析7.消息转发8.类型编码9.属性声明 1 概述 Objective-C语言将决定尽 ...
- 别用symbolicatecrash来解析crash Log了
今天突然发现了一个解析iOS crash log的好方法,忍不住来分享一下. 相信每个做iOS开发的TX都应该不会对symbolicatecrash陌生,我们第一次遇到真机上产生的崩溃日志时,在网上搜 ...
- iOS 几种常用的 crash log 崩溃信息调试方法
前言:crash log 对 定位崩溃问题 ,并且不容易复现,尤其是及时对appstore 上正在运营的 app 的迭代改进来说 非常重要. 1 crash两种情况 1.1 测试环境下 追踪bug 1 ...
- 了解和分析iOS Crash
WeTest 导读 北京时间凌晨一点,苹果一年一度的发布会如期而至.新机型的发布又会让适配相关的同学忙上一阵子啦,并且iOS Crash的问题始终伴随着移动开发者.本文将从三个阶段,由浅入深的介绍如何 ...
随机推荐
- - > 最大公约数(辗转相除法)和最小公倍数(公式法)
最大公约数 #include<iostream> using namespace std; int a,b; int gcd(int x,int y){ return x==0?y:gcd ...
- 1.5 - 动态路由协议ISIS
IS-IS 特征: 1:协议操作起来,比OSPF要简单 2:扩展性比OSPF要好,易于扩展 3:对IPv6有很好的支持 4:能够同时支持IP网络,和CLNS网络(OSI网络)(集成的IS-IS) 5: ...
- 转 java synchronized详解
转自 http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能 ...
- automaticallyAdjustsScrollViewInsets 使用
automaticallyAdjustsScrollViewInsets(个人认为iOS7中略坑爹的属性) @当我们在一个UIViewController中同时创建2个tableView的时候,如果把 ...
- 具体解释kernel中watchdog 驱动程序
watchdog不管在小系统还是大的project系统中都是必须存在的.在解决线程挂死.系统死循环等都用非常重要的应用,算是系统出问题恢复初始状态的救命稻草. 在kernel中wdt的应用不是非经常见 ...
- SpringBoot在Impl类中调用其它service层失败解决办法
在AImpl.java文件中引用BImpl.java的方法,编译正常,运行到调用的地方,报空指针异常,跟踪到异常位置,发现service为空,也就是按照之前controller层通过@Autowire ...
- 数据库操作语句大全(sql)
一.基础 1.说明:创建数据库CREATE DATABASE database-name 2.说明:删除数据库drop database dbname3.说明:备份sql server--- 创建 备 ...
- Delphi中ARC内存管理的方向
随着即将发布的10.3版本,RAD Studio R&D和PM团队正在制作Delphi在内存管理方面的新方向. 几年前,当Embarcadero开始为Windows以外的平台构建新的Delph ...
- go语言笔记——包的概念本质上和java是一样的,通过大小写来区分private,fmt的Printf不就是嘛!
示例 4.1 hello_world.go package main import "fmt" func main() { fmt.Println("hello, wor ...
- luogu 3834 【模板】可持久化线段树 1(主席树)
我这种菜鸡还是%一下棒神比较好 #include<iostream> #include<cstdio> #include<cmath> #include<cs ...