一、 dSYM文件
.dSYM(debugging SYMbols)
又称为调试符号表,是起源于贝尔实验室的DWARF(Debugging With Attributed Record Formats)
.
二、dSYM文件和Crash Log一致性
二者的对应关系可以通过UUID来确定。
1、从崩溃日志中获取UUID
崩溃日志有个Binary Images模块
Binary Images:
0x100e30000 - 0x106983fff XXXXAPP arm64-unknown <3671a0be1d663f4789dbfeb9190fdacf> /var/containers/Bundle/Application/643F8D1D-9000-4211-8DA0-0C2D42DB03C4/XXXXAPP.app/XXXXAPP
0x181bee000 - 0x181c72fff IOKit arm64-unknown <54433b44779d39378d0789a4017a2948> /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit
0x181f0e000 - 0x1822bcfff CFNetwork arm64-unknown /System/Library/Frameworks/CFNetwork.framework/CFNetwork
0x188503000 - 0x189081fff JavaScriptCore arm64-unknown <8be29f5ee31a302bbc16e47869e799ac> /System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore
0x181845000 - 0x181bdbfff CoreFoundation arm64-unknown <533c841ed6e9313d8adb02388744e2ef> /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
0x1839de000 - 0x183f8efff ImageIO arm64-unknown <728aeffd50883729bc1ac09900f05cf2> /System/Library/Frameworks/ImageIO.framework/ImageIO
0x18323e000 - 0x183785fff CoreGraphics arm64-unknown <59a6f76dbc9f3e44bdf30351088b637d> /System/Library/Frameworks/CoreGraphics.framework/CoreGraphics
0x1872c6000 - 0x1873bcfff AVFAudio arm64-unknown /System/Library/Frameworks/AVFoundation.framework/Frameworks/AVFAudio.framework/AVFAudio
0x18131d000 - 0x18139afff libsystem_c.dylib arm64-unknown <61d2e950add73139aea459b55997ea48> /usr/lib/system/libsystem_c.dylib
0x18382b000 - 0x18383efff GraphicsServices arm64-unknown <5011ec2511d73a56af501e8207d54962> /System/Library/PrivateFrameworks/GraphicsServices.framework/GraphicsServices
0x18127b000 - 0x1812e0fff libdispatch.dylib arm64-unknown <0c931ac760133de187bb6f440beed5eb> /usr/lib/system/libdispatch.dylib
0x189ad3000 - 0x18b1a5fff WebCore arm64-unknown <2fe4173593ba35c4a5e04b40b40f1a7e> /System/Library/PrivateFrameworks/WebCore.framework/WebCore
0x182a6d000 - 0x182a77fff IOSurface arm64-unknown <1f24b80565013f8d94b5e18c144f24bc> /System/Library/Frameworks/IOSurface.framework/IOSurface
EOF
从中可以看到若干信息:
代码段的起终地址为:0x100e30000 - 0x106983fff
运行你应用的CPU指令集为:arm64
应用的UUID为:3671a0be1d663f4789dbfeb9190fdacf(不区分大小写)
2、从符号表中获取UUID
执行以下命令从符号表中提取UUID:
user$ dwarfdump --uuid xxx.dSYM
//或者命令dwarfdump --uuid xxx.dSYM/Contents/Resources/DWARF/xxx
UUID: 815AF08E-2A1E-359C-9082-7C98326D51F4 (armv7) xxx.dSYM/Contents/Resources/DWARF/xxx
UUID: 3671A0BE-1D66-3F47-89DB-FEB9190FDACF (arm64) xxx.dSYM/Contents/Resources/DWARF/xxx
得到armv7指令集的UUID为:815AF08E-2A1E-359C-9082-7C98326D51F4
得到arm64指令集的UUID为:3671A0BE-1D66-3F47-89DB-FEB9190FDACF
(如果你的二进制文件支持多个指令集,这里会列出每个指令集对应符号表的UUID),通过和崩溃日志中的对比发现二者一致,才可进行进一步的解析操作。
三、计算崩溃符号表地址
Thread 0 Lagged:
0 libsystem_kernel.dylib 0x1813efde8 0x1813ef000 + _mach_msg_trap
1 libsystem_kernel.dylib 0x1813efc60 0x1813ef000 + _mach_msg
2 IOKit 0x181c57bb8 0x181bee000 + _io_connect_method
3 IOKit 0x181bf4038 0x181bee000 + _IOConnectCallMethod
4 IOSurface 0x182a71f9c 0x182a6d000 + _IOSurfaceClientWrapClientImage
5 IOSurface 0x182a7545c 0x182a6d000 + _IOSurfaceWrapClientImage
6 ImageIO 0x183aa8c78 0x1839de000 + __Z34IIO_CreateImageIOSurfaceWithFormatmmmj
7 ImageIO 0x183a312d8 0x1839de000 + __ZN14JPEGReadPlugin37createImageBlockSetWithHardware_aspenEP7InfoRecP15CGImageProviderPK8__CFData6CGSizePb
8 ImageIO 0x183b8cfc8 0x1839de000 + __ZN19AppleJPEGReadPlugin37createImageBlockSetWithHardwareDecodeEP7InfoRecP15CGImageProvider6CGSizePb
9 ImageIO 0x183b8c8b0 0x1839de000 + __ZN19AppleJPEGReadPlugin17copyImageBlockSetEP7InfoRecP15CGImageProvider6CGRect6CGSizePK14__CFDictionary
10 ImageIO 0x183b8b690 0x1839de000 + __ZN19AppleJPEGReadPlugin21CopyImageBlockSetProcEPvP15CGImageProvider6CGRect6CGSizePK14__CFDictionary
11 ImageIO 0x183a13684 0x1839de000 + __ZN20IIOImageProviderInfo28copyImageBlockSetWithOptionsEP15CGImageProvider6CGRect6CGSizePK14__CFDictionary
12 ImageIO 0x183a10e98 0x1839de000 + __ZN20IIOImageProviderInfo28CopyImageBlockSetWithOptionsEPvP15CGImageProvider6CGRect6CGSizePK14__CFDictionary
13 CoreGraphics 0x18341d360 0x18323e000 + _img_blocks_create
14 CoreGraphics 0x183424b8c 0x18323e000 + _img_data_lock
15 CoreGraphics 0x183424444 0x18323e000 + _CGSImageDataLock
16 CoreGraphics 0x183242440 0x18323e000 + _ripc_AcquireRIPImageData
17 CoreGraphics 0x183438f5c 0x18323e000 + _ripc_DrawImage
18 CoreGraphics 0x183428de8 0x18323e000 + _CGContextDrawImageWithOptions
19 xxx 0x100ec4040 0x100e30000 + 606272
20 xxx 0x100eb3868 0x100e30000 + 538728
21 xxx 0x100ec9a70 0x100e30000 + 629360
22 xxx 0x100eca478 0x100e30000 + 631928
23 xxx 0x100f47dd8 0x100e30000 + 1146328
24 xxx 0x101698d10 0x100e30000 + 8817936
25 xxx 0x1016bcf54 0x100e30000 + 8965972
26 libdispatch.dylib 0x18127caa0 0x18127b000 + __dispatch_call_block_and_release
27 libdispatch.dylib 0x18127ca60 0x18127b000 + __dispatch_client_callout
28 libdispatch.dylib 0x18128949c 0x18127b000 + __dispatch_main_queue_callback_4CF$VARIANT$mp
29 CoreFoundation 0x181933070 0x181845000 + ___CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
30 CoreFoundation 0x181930bc8 0x181845000 + ___CFRunLoopRun
31 CoreFoundation 0x181850da8 0x181845000 + _CFRunLoopRunSpecific
32 GraphicsServices 0x183836020 0x18382b000 + _GSEventRunModal
33 UIKit 0x18b870758 0x18b553000 + _UIApplicationMain
34 xxx 0x100e3413c 0x100e30000 + 16700
35 libdyld.dylib 0x1812e1fc0 0x1812e1000 + _start
1、符号表堆栈地址计算方式
要想利用符号表解析出崩溃对应位置,需要计算出符号表中对应的崩溃堆栈地址。
而从上述堆栈中第19行可以看到,应用崩溃发生在运行时地址0x100ec4040
,该进程的运行时起始地址是0x100e30000
,崩溃处距离进程起始地址的偏移量为十进制的606272(对应十六进制为0x94040)
。
三者对应关系:
0x100ec4040 = 0x100e30000 + 0x94040
对应的公式为:
运行时堆栈地址 = 运行时起始地址 + 偏移量
崩溃堆栈中的起始地址和崩溃地址均为运行时地址,根据虚拟内存偏移量不变原理,只要提供了符号表TEXT段的起始地址,再加上偏移量(这里为0x94040)就能得到符号表中的堆栈地址,即:
符号表堆栈地址 = 符号表起始地址 + 偏移量
2、获取符号表中的TEXT段起始地址
执行命令
otool -l xxx.dSYM/Contents/Resources/DWARF/xxx
Load command 0
cmd LC_UUID
cmdsize 24
uuid 815AF08E-2A1E-359C-9082-7C98326D51F4
Load command 1
cmd LC_SYMTAB
cmdsize 24
symoff 8192
nsyms 475187
stroff 5710436
strsize 18159960
Load command 2
cmd LC_SEGMENT
cmdsize 56
segname __PAGEZERO
vmaddr 0x00000000
vmsize 0x00004000
fileoff 0
filesize 0
maxprot 0x00000000
initprot 0x00000000
nsects 0
flags 0x0
Load command 3
cmd LC_SEGMENT
cmdsize 940
segname __TEXT
vmaddr 0x00004000
vmsize 0x053e4000
fileoff 0
filesize 0
maxprot 0x00000005
initprot 0x00000005
nsects 13
flags 0x0
segname __TEXT
vmaddr 0x00004000
即为TEXT段的起始地址
3、计算符号表地址
由公式:
符号表堆栈地址 = 符号表起始地址 + 偏移量
可得:
0x98040 = 0x94040 + 0x4000
即符号表中的崩溃地址为0x98040
,接下来就可以根据这个地址解析出崩溃位置了。
四、崩溃信息还原
1、dwarfdump命令
dwarfdump --lookup 0x98040 --arch armv7 xxx.dSYM/
也可以使用:
dwarfdump --arch armv7 xxx.dSYM --lookup 0x98040 | grep 'Line table'
输出结果如下
----------------------------------------------------------------------
File: xxx.dSYM/Contents/Resources/DWARF/xxx (armv7)
----------------------------------------------------------------------
Looking up address: 0x0000000000098040 in .debug_info... found!
0x00091812: Compile Unit: length = 0x000051dd version = 0x0002 abbr_offset = 0x00000000 addr_size = 0x04 (next CU at 0x000969f3)
0x0009181d: TAG_compile_unit [127] *
AT_producer( "Apple LLVM version 9.1.0 (clang-902.0.39.1)" )
AT_language( DW_LANG_ObjC )
AT_name( "/Users/xxx/xxx/xxController.m" )
AT_stmt_list( 0x00041b6c )
AT_comp_dir( "/Users/xxx/Pods" )
AT_APPLE_major_runtime_vers( 0x02 )
AT_low_pc( 0x00095394 )
AT_high_pc( 0x0009bcf8 )
0x00094a3f: TAG_subprogram [178] *
AT_low_pc( 0x00098036 )
AT_high_pc( 0x00098056 )
AT_frame_base( r7 )
AT_name( "__copy_helper_block_" )
AT_decl_file( "/Users/xx/xxxController.m" )
AT_decl_line( 185 )
AT_APPLE_isa( 0x01 )
Line table dir : '/Users/xxxController.m' line 176, column 74 with start address 0x0000000000098040
Looking up address: 0x0000000000098040 in .debug_frame... not found.
编译时文件目录,崩溃发生的文件名称以及文件中具体行号等信息,这些信息就能准确定位崩溃原因。
2、atos命令
atos是另一种更加简洁的崩溃日志解析方法,使用方式如下:
$atos -o LuBao -arch armv7 0x98040
相对dwarfdump命令的解析结果,更加简洁直观的指出了崩溃发生的位置。
3、无需符号表崩溃地址的解析方式
实际上,atos还提供了另外一种无需计算崩溃地址对应的符号表地址的方式,命令格式如下:
$atos -o xxx.dSYM/Contents/Resources/DWARF/xxx -arch armv7 -l 0x100e30000 0x100ec4040
其中-l选项指定了二进制文件在运行时的起始地址0x100e30000(获取方式见Binary Images相关内容),后面跟的是崩溃发生的运行时地址0x100ec4040.