dSYM文件的汇编分析
Posted 宇仔TuT
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dSYM文件的汇编分析相关的知识,希望对你有一定的参考价值。
什么是dSYM文件?
在项目开发当中我们经常需要分析crash log
来追查线上Bug,其中我们会用到一个很重要的文件,叫做dSYM文件
。
dSYM
是内存地址与函数名,文件名,行号的映射表
,一般用于崩溃日志分析。
大概长这样:
<起始地址> <结束地址> <函数> [<文件名:行号>]
符号表文件.dSYM
实际上是从Mach-O
文件中抽取调试信息而得到的文件目录,实际用于保存调试信息的文件夹是DWARF
Xcode
编译项目后,我们会看到一个同名
的 dSYM
文件,dSYM
是保存 16 进制函数地址映射信息
的中转文件,我们调试的 symbols
都会包含在这个文件中,并且每次编译项目的时候都会生成一个新的 dSYM 文件,位于/Users/<用户名>/Library/Developer/Xcode/Archives
目录下。
dSYM文件的使用方式
通过crash log获取到崩溃的函数地址
,再通过函数地址到dSYM文件中去查询到对应的函数名
和文件名
,从而定位到错误。
出错堆栈
0 libobjc.A.dylib 0x00000001941bbbdc objc_msgSend + 28
1 UIKit 0x00000001885f0f74 0x00000001881fc000 + 4149108
2 UIKit 0x00000001882e5bec 0x00000001881fc000 + 957420
3 UIKit 0x00000001882e5960 0x00000001881fc000 + 956768
4 UIKit 0x00000001882ec5e0 0x00000001881fc000 + 984544
5 UIKit 0x00000001882095f8 0x00000001881fc000 + 54776
6 UIKit 0x000000018822aa34 0x00000001881fc000 + 191028
7 UIKit 0x0000000188209310 0x00000001881fc000 + 54032
8 UIKit 0x0000000188209310 0x00000001881fc000 + 54032
9 UIKit 0x000000018822aa34 0x00000001881fc000 + 191028
10 UIKit 0x0000000188209310 0x00000001881fc000 + 54032
11 UIKit 0x0000000188209310 0x00000001881fc000 + 54032
12 UIKit 0x0000000188209310 0x00000001881fc000 + 54032
13 UIKit 0x0000000188209310 0x00000001881fc000 + 54032
14 UIKit 0x0000000188209310 0x00000001881fc000 + 54032
15 UIKit 0x0000000188209310 0x00000001881fc000 + 54032
16 UIKit 0x00000001882089e0 0x00000001881fc000 + 51680
17 Foundation 0x0000000184891908 0x000000018483c000 + 350472
18 UIKit 0x00000001882088a0 0x00000001881fc000 + 51360
19 UIKit 0x00000001882143a0 0x00000001881fc000 + 99232
20 UIKit 0x00000001883000b0 0x00000001881fc000 + 1065136
21 UIKit 0x0000000188344128 0x00000001881fc000 + 1343784
22 UIKit 0x00000001883439f0 0x00000001881fc000 + 1341936
23 UIKit 0x000000018840d5c4 0x00000001881fc000 + 2168260
24 UIKit 0x0000000188241418 0x00000001881fc000 + 283672
25 UIKit 0x000000018840d430 0x00000001881fc000 + 2167856
26 UIKit 0x0000000188241418 0x00000001881fc000 + 283672
27 UIKit 0x000000018822a52c 0x00000001881fc000 + 189740
28 UIKit 0x000000018840d068 0x00000001881fc000 + 2166888
29 UIKit 0x0000000188241418 0x00000001881fc000 + 283672
30 UIKit 0x000000018822a52c 0x00000001881fc000 + 189740
31 UIKit 0x0000000188240db4 0x00000001881fc000 + 282036
32 UIKit 0x0000000188200750 0x00000001881fc000 + 18256
33 CoreFoundation 0x0000000183a16a50 0x0000000183938000 + 911952
34 CoreFoundation 0x0000000183a139dc 0x0000000183938000 + 899548
35 CoreFoundation 0x0000000183a13dbc 0x0000000183938000 + 900540
36 CoreFoundation 0x00000001839410a4 CFRunLoopRunSpecific + 396
37 GraphicsServices 0x000000018cadb5a4 GSEventRunModal + 168
38 UIKit 0x0000000188272aa4 UIApplicationMain + 1488
39 APP_BUNDLE_NAME 0x0000000100327938 0x00000001000a0000 + 2652472
40 libdyld.dylib 0x0000000194816a08 0x0000000194814000 + 10760
分析数据
- Stack Address(栈地址):
0x0000000100327938
- Load Address(首地址):
0x00000001000a0000
- Slide Value(32位虚拟地址):
0x00004000
- Slide Value(64位虚拟地址):
0x0000000100000000
- Symbol Offset(偏移量):
2652472
- File Address(32位文件地址):
0x28B938
- File Address(64位文件地址):
0x100287938
注意虚拟地址区分32位(
LC_SEGMENT
)和64位(LC_SEGMENT_64
)段的不同
符号化dSYM文件
每一个 xx.app
和 xx.app.dSYM
文件都有对应的 UUID
,crash 文件也有自己的 UUID
,只要这三个文件的 UUID
一致,我们就可以通过他们解析出正确的错误函数信息了。
将dSYM
文件符号化就能定位到出错的文件
与行号
信息。
Xcode自动符号化
Xcode
自带的symbolicatecrash
工具来将.Crash和.dSYM文件进行符号化,就可以得到详细崩溃的信息。
如果你没有发布包,比如是别人电脑打包的发布包,或者是一些平台上打的包,只需要你把 xcarchive
拷贝到 $HOME/Library/Developer/Xcode/Archives
目录下之后,Xcode 就可以自动帮你符号化了。
atos单/多行符号化
atos
会主动帮我们计算虚拟地址
,只要我们提供了首地址
和栈地址
-
查看
xx.app
文件的UUID
,terminal
中输入命令 :dwarfdump —uuid xx.app/xx
(xx代表你的项目名) -
查看
xx.app.dSYM
文件的UUID
,在terminal
中输入命令:
dwarfdump —uuid appName.app.dSYM
crash 文件
内第一行Incident Identifier
就是该crash 文件
的UUID
。- 使用
ATOS
命令来对单行
或多行
的堆栈进行符号化
操作,以armv7架构
为例。完整语法atos -arch \\<architecture\\> -o \\<binary filename\\> -l \\<load address\\> \\<stack address 1\\> \\<stack address 2\\> …
atos armv7 -o appName crashAddress(崩溃的地址)
结果可能像这样:
main (in ) (main.m:14)
使用lldb进行符号化
(lldb) target create —arch arm64 ./APP_BUNDLE_NAME.app.dSYM/Contents/Resources/DWARF/APP_BUNDLE_NAME*
Current executable set to ‘./APP_BUNDLE_NAME.app.dSYM/Contents/Resources/DWARF/APP_BUNDLE_NAME’ (arm64).
(lldb) image lookup *—address 0x100287938*
Address: APP_BUNDLE_NAME[0x0000000100287938] (APP_BUNDLE_NAME.__TEXT.__text + 2632468)
Summary: APP_BUNDLE_NAME`main + 88 at main.m:14
使用dwarfdump进行符号化
$ dwarfdump --lookup 0x100287938 --arch arm64 APP_BUNDLE_NAME.app.dSYM
----------------------------------------------------------------------
File: APP_BUNDLE_NAME.app.dSYM/Contents/Resources/DWARF/APP_BUNDLE_NAME (arm64)
----------------------------------------------------------------------
Looking up address: 0x0000000100287938 in .debug_info... found!
0x002942c4: Compile Unit: length = 0x0000024d version = 0x0002
abbr_offset = 0x00000000 addr_size = 0x08 (next CU at 0x00294515)
0x002942cf: TAG_compile_unit [106] *
AT_producer( "Apple LLVM version 8.1.0 (clang-802.0.38)" )
AT_language( DW_LANG_ObjC )
AT_name( "/Users/user/APP_PROJECT_NAME/APP_PROJECT_NAME/main.m" )
AT_stmt_list( 0x00128d9d )
AT_comp_dir( "/Users/user/APP_PROJECT_NAME" )
AT_APPLE_optimized( 0x01 )
AT_APPLE_major_runtime_vers( 0x02 )
AT_low_pc( 0x00000001002878d8 )
AT_high_pc( 0x0000000100287960 )
0x00294480: TAG_subprogram [226] *
AT_low_pc( 0x00000001002878e0 )
AT_high_pc( 0x0000000100287960 )
AT_frame_base( reg29 )
AT_name( "main" )
AT_decl_file( "/Users/user/APP_PROJECT_NAME/APP_PROJECT_NAME/main.m" )
AT_decl_line( 12 )
AT_prototyped( 0x01 )
AT_type( 0x002944c7 ( int ) )
AT_external( 0x01 )
AT_APPLE_optimized( 0x01 )
Line table dir : '/Users/user/APP_PROJECT_NAME/APP_PROJECT_NAME'
Line table file: 'main.m' line 14, column 16 with start address 0x0000000100287924
Looking up address: 0x0000000100287938 in .debug_frame... not found.
__debug_info数据
除了常规的符号化dYSM
文件以外,我们还能通过dwarfdump
工具获取到__debug_info
信息,结合二进制文件反汇编得到的汇编指令,我们可以对问题进行更进一步的分析:
$ dwarfdump --debug-info ./testDwarf.app.dSYM/Contents/Resources/DWARF/testDwarf
0x0004005f: TAG_subprogram [122] *
AT_low_pc( 0x0000000100006760 ) //方法代码起始地址
AT_high_pc( 0x00000074 ) //方法代码长度
AT_frame_base( reg29 ) //指明此方法的frame base是x29(也就是fp),后面会用到
AT_object_pointer( 0x00040078 )
AT_name( "-[ViewController myFunction:]" ) //当前测试方法名
AT_decl_file( "/Users/jz/bsl/Tests/testDwarf/testDwarf/ViewController.m" ) //文件路径
AT_decl_line( 22 ) //行号
AT_prototyped( true )
0x00040078: TAG_formal_parameter [123]
AT_location( fbreg -8 )
AT_name( "self" )
AT_type( 0x000400bb ( const ViewController* ) )
AT_artificial( true )
0x00040084: TAG_formal_parameter [123]
AT_location( fbreg -16 )
AT_name( "_cmd" )
AT_type( 0x000400c5 ( SEL ) )
AT_artificial( true )
0x00040090: TAG_formal_parameter [124]
AT_location( fbreg -20 ) //AT_location字段表明此变量(参数 arg)的内存地址在当前函数的 AT_frame_base 偏移 -20 处,myFunction函数的AT_frame_base 为 x29,则参数arg的实际存放地址为 $x29 - 20
AT_name( "arg" ) //参数 arg 变量名
AT_decl_file( "/Users/jz/bsl/Tests/testDwarf/testDwarf/ViewController.m" )
AT_decl_line( 22 )
AT_type( 0x000400d8 ( int ) ) //具体类型信息,见下个代码片断
0x0004009e: TAG_variable [125]
AT_location( breg31 +24 ) //局部变量 local 的存放位置为 breg31 + 24 == x31 + 24,其中:x31也就是sp
AT_name( "local" ) //局部变量local
AT_decl_file( "/Users/jz/bsl/Tests/testDwarf/testDwarf/ViewController.m" )
AT_decl_line( 23 )
AT_type( 0x000400d8 ( int ) ) //具体类型信息,见下个代码片断
0x000400ac: TAG_variable [125]
AT_location( breg31 +20 )
AT_name( "i" )
AT_decl_file( "/Users/jz/bsl/Tests/testDwarf/testDwarf/ViewController.m" )
AT_decl_line( 24 )
AT_type( 0x000400d8 ( int ) )
简单分析一下0x0004005f
这一段:
0x0004005f: TAG_subprogram [122] *
AT_low_pc( 0x0000000100006760 ) //方法代码起始地址
AT_high_pc( 0x00000074 ) //方法代码长度
AT_frame_base( reg29 ) //指明此方法的frame base是x29(也就是fp),后面会用到
AT_object_pointer( 0x00040078 )
AT_name( "-[ViewController myFunction:]" ) //当前测试方法名
AT_decl_file( "/Users/jz/bsl/Tests/testDwarf/testDwarf/ViewController.m" ) //文件路径
AT_decl_line( 22 ) //行号
AT_prototyped( true )
AT_low_pc
表示方法代码的起始地址AT_high_pc
表示方法代码长度AT_frame_base
如果我没理解错的话,这个指的就是fp
(x29)寄存器 (Frame Pointer),x29
寄存器也叫做栈基址
寄存器,用于保存栈底地址。
还有lr(x30)
寄存器(Link Register),x30
寄存器用来保存调用跳转指令 bl
指令的下一条指令的内存地址
zr(x31)
零寄存器: (Zero Register),xzr/wzr
分别代表 64/32
位的零寄存器,其作用就是 0
,写进去代表丢弃结果,读出来是 0
。
但是在ARM64
汇编中,x31
寄存器并不是永远表示XZR
,有时候也表示SP
寄存器:
sp
: (Stack Pointer),栈顶寄存器,用于保存栈顶
地址
AT_name
:方法、参数、变量等的名称AT_location
:参数/变量的内存地址
结合上面说的fp
、lr
、zr
和sp
寄存器的概念,可以看到这里的location
几种入参的含义,例如
fbreg - 20
代表fp寄存器
偏移-20
breg31 +24
代表sp寄存器
偏移24
梳理完这些信息的含义以后,我们就能够结合反汇编的汇编指令,来对崩溃信息进行更详细的分析。
—未完待续—
Swift学习群
欢迎加入本人的Swift学习微信群,一同互相监督学习,我微信:reese90
参考文章:
App崩溃现场取变量名和其实际值对应关系(不只是寄存器)
以上是关于dSYM文件的汇编分析的主要内容,如果未能解决你的问题,请参考以下文章