iOS的调试文件dSYM与DWARF

背景

在App上线之后,我们通常使用一些工具比如bugly来收集线上的crash,收集上的函数调用栈都是一些函数地址,我们根本无法是哪个文件的下的哪段代码引起的crash,如果想要知道是哪个函数引起的crash,我们需要上传编译之后产生的dSYM文件,为什么需要dSYM文件呢?

dSYM概念

什么是DWARF

DWARF是一种被众多编译器和调试器使用的用于支持源码级别调试的调试文件格式,该格式是一个固定的数据格式

什么是dSYM

dSYM就是按照DWARF格式保存调试信息的文件,也就是说,dSYM是一个文件。

探索dSYM

带调试符号的.o

1, 我们首先使用 clangtest.m编译为.o文件,test.m的内容如下所示
test.png

clang -g -c test.m -o test.o
复制代码

2,我们使用 objcdump查看 test.oheader信息

objdump --macho --private-headers test.o

Section
  sectname __debug_str
   segname __DWARF
      addr 0x0000000000000158
      size 0x00000000000000a9
    offset 1616
     align 2^0 (1)
    reloff 0
    nreloc 0
      type S_REGULAR
attributes DEBUG
 reserved1 0
 reserved2 0
复制代码

我们可以看到,在编译的时候,调试信息存在了名为__DWARF的段中。

带调试符号的可执行文件

1,我们继续使用clang,先生成带调试符号的可执行文件,不再使用-c参数

clang -g test.m -o test
复制代码

2,查看其段信息

bel@beldeMacBook-Pro dSYM % objdump --macho --private-headers test
test:
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64  X86_64        ALL  0x00     EXECUTE    16       1048   NOUNDEFS DYLDLINK TWOLEVEL PIE
Load command 0
      cmd LC_SEGMENT_64
  cmdsize 72
  segname __PAGEZERO
   vmaddr 0x0000000000000000
   vmsize 0x0000000100000000
  fileoff 0
 filesize 0
  maxprot ---
 initprot ---
   nsects 0
    flags (none)
Load command 1
      cmd LC_SEGMENT_64
  cmdsize 232
  segname __TEXT
     ...
Section
  sectname __text
   segname __TEXT
    ...
Section
  sectname __unwind_info
   segname __TEXT
    ...
Load command 2
      cmd LC_SEGMENT_64
  cmdsize 152
  segname __DATA_CONST
    ...
Section
  sectname __objc_imageinfo
   segname __DATA_CONST
    ...
Load command 3
      cmd LC_SEGMENT_64
  cmdsize 152
  segname __DATA
    ...
Section
  sectname __data
   segname __DATA
      ...
Load command 4
      cmd LC_SEGMENT_64
  cmdsize 72
  segname __LINKEDIT
    ...
Load command 5
            cmd LC_DYLD_INFO_ONLY
        cmdsize 48
     rebase_off 0
    rebase_size 0
       bind_off 0
      bind_size 0
  weak_bind_off 0
 weak_bind_size 0
  lazy_bind_off 0
 lazy_bind_size 0
     export_off 49152
    export_size 80
Load command 6
     cmd LC_SYMTAB
 cmdsize 24
  symoff 49240
   nsyms 23
  stroff 49608
 strsize 192
Load command 7
            cmd LC_DYSYMTAB
        cmdsize 80
            ....
Load command 8
          cmd LC_LOAD_DYLINKER
      cmdsize 32
         name /usr/lib/dyld (offset 12)
Load command 9
     cmd LC_UUID
 cmdsize 24
    uuid 2890FF4F-897E-30B1-A90C-48A58B0B0146
Load command 10
       cmd LC_BUILD_VERSION
   cmdsize 32
  platform macos
       sdk 11.3
     minos 11.0
    ntools 1
      tool ld
   version 650.9
Load command 11
      cmd LC_SOURCE_VERSION
  cmdsize 16
  version 0.0
Load command 12
       cmd LC_MAIN
   cmdsize 24
  entryoff 16256
 stacksize 0
Load command 13
          cmd LC_LOAD_DYLIB
      cmdsize 56
         name /usr/lib/libSystem.B.dylib (offset 24)
        ....
复制代码

此时,我们在Mach O文件中没有找到__DWARF相关的信息,

3,查看其符号表

nm -pa test

bel@beldeMacBook-Pro dSYM % nm -pa test
0000000000000000 - 00 0000    SO /Users/bel/Desktop/dSYM/
0000000000000000 - 00 0000    SO test.m
0000000060e97f85 - 03 0001   OSO /var/folders/d2/_hhc47fd0cvd4b7n2hrcpxt80000gn/T/test-b74f6a.o
0000000100003f60 - 01 0000 BNSYM 
0000000100003f60 - 01 0000   FUN _test
0000000000000010 - 00 0000   FUN 
0000000000000010 - 01 0000 ENSYM 
0000000100003f70 - 01 0000 BNSYM 
0000000100003f70 - 01 0000   FUN _test_1
0000000000000010 - 00 0000   FUN 
0000000000000010 - 01 0000 ENSYM 
0000000100003f80 - 01 0000 BNSYM 
0000000100003f80 - 01 0000   FUN _main
0000000000000035 - 00 0000   FUN 
0000000000000035 - 01 0000 ENSYM 
0000000000000000 - 00 0000  GSYM _global
0000000000000000 - 01 0000    SO 
0000000100000000 T __mh_execute_header
0000000100008000 D _global
0000000100003f80 T _main
0000000100003f60 T _test
0000000100003f70 T _test_1
                 U dyld_stub_binder
复制代码

在其符号表里面,我们看到我们使用的_test_test_1等符号,

小结

编译器在编译的时候,会将调试信息存放到__DWARF段中,在进行链接的时候,将__DWARF段删除,并将符号信息存放到__DWARF段中。

生成dSYM文件

1,我们使用编译的 -g1参数,生成dSYM文件

clang -g1 test.m -o test
复制代码

2,使用 dwarfdump命令,查看dSYM文件

bel@beldeMacBook-Pro dSYM % dwarfdump test.dSYM 
test.dSYM/Contents/Resources/DWARF/test:	file format Mach-O 64-bit x86-64

.debug_info contents:
0x00000000: Compile Unit: length = 0x00000063, format = DWARF32, version = 0x0004, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x00000067)

0x0000000b: DW_TAG_compile_unit
              DW_AT_producer	("Apple clang version 12.0.5 (clang-1205.0.22.9)")
              DW_AT_language	(DW_LANG_ObjC)
              DW_AT_name	("test.m")
              DW_AT_LLVM_sysroot	("/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
              DW_AT_APPLE_sdk	("MacOSX.sdk")
              DW_AT_stmt_list	(0x00000000)
              DW_AT_comp_dir	("/Users/bel/Desktop/dSYM")
              DW_AT_APPLE_major_runtime_vers	(0x02)
              DW_AT_low_pc	(0x0000000100003f60)
              DW_AT_high_pc	(0x0000000100003fb5)

0x00000033:   DW_TAG_subprogram
                DW_AT_low_pc	(0x0000000100003f60)
                DW_AT_high_pc	(0x0000000100003f66)
                DW_AT_name	("test")

0x00000044:   DW_TAG_subprogram
                DW_AT_low_pc	(0x0000000100003f70)
                DW_AT_high_pc	(0x0000000100003f76)
                DW_AT_name	("test_1")

0x00000055:   DW_TAG_subprogram
                DW_AT_low_pc	(0x0000000100003f80)
                DW_AT_high_pc	(0x0000000100003fb5)
                DW_AT_name	("main")

0x00000066:   NULL
复制代码

我们可以看到,在dSYM文件中,按照一定的格式来保存调试符号,里面包含了虚拟内存地址值符号名称

如何把虚拟内存地址还原成符号

含有调试符号的调用栈

我们打开Xcode,新建一个工程,让App在启动3秒后,就crash,代码如下所示:

示例工程.png

其crsah信息如下:

截屏2021-07-10 下午8.31.48.png
crash发生在-[ViewController warfTest]方法里面。

不含有调试符号的调用栈

在默认情况下,我们在Release模式下进行App打包Xcode会使用strip命令,将本地符号脱掉,在导出ipa包的时候生成dSYM文件,在Debug模式下,我们需要将 Deployment Postprocessing设置为Yes。在每次编译完之后,就会执行strip进行脱符号
Deployment Postprocessing.png

此时其crash日志为:

截屏2021-07-10 下午8.34.15.png
从日志里面,我们就不能定位是哪个函数引起的crash了。

编译运行后,生成dSYM

此时,在Debug模式下,编译只完成了使用 strip去脱符号,我们将 PROJECT -> Build Settings -> Build Options -> Debug information Format 设置为 DWARF with dSYM File,并将Deployment Postprocessing设置为NO(不进行脱符号), 这样,我们就可以得到dSYM文件

截屏2021-07-10 下午8.57.39.png

根据虚拟地址和dSYM还原调试符号

此时我们重新运行工程,然后就crash,

截屏2021-07-10 下午9.03.45.png

在序号为3的那一行,内存地址值为 0x000000010a107eb4,在运行过程中,其等于
ASLR + 函数的虚拟地址值

首先我们要获取这次运行的ASLR值:

(lldb) image list
[  0] 3B857210-92BA-359F-A96E-856AC9E5CB8F 0x000000010a107000 /Users/bel/Library/Developer/Xcode/DerivedData/dSYMProject-ccrqorgatcanaacgnyihzhjcjcwi/Build/Products/Debug-iphonesimulator/dSYMProject.app/dSYMProject 
      /Users/bel/Library/Developer/Xcode/DerivedData/dSYMProject-ccrqorgatcanaacgnyihzhjcjcwi/Build/Products/Debug-iphonesimulator/dSYMProject.app.dSYM/Contents/Resources/DWARF/dSYMProject
复制代码

可以看出 ASLR = 0x000000010a107000 - 0x0000000100000000(vmsize),那就可以得出该函数的虚拟地址值0x000000010a107eb4 - 0x00000000a107000 = 0x100000EB4

dSYM文件中查找该地址对应的符号:

bel@beldeMacBook-Pro ~ % dwarfdump --lookup 0x100000EB4 /Users/bel/Library/Developer/Xcode/DerivedData/dSYMProject-ccrqorgatcanaacgnyihzhjcjcwi/Build/Products/Debug-iphonesimulator/dSYMProject.app.dSYM
/Users/bel/Library/Developer/Xcode/DerivedData/dSYMProject-ccrqorgatcanaacgnyihzhjcjcwi/Build/Products/Debug-iphonesimulator/dSYMProject.app.dSYM/Contents/Resources/DWARF/dSYMProject:	file format Mach-O 64-bit x86-64
0x000474bb: Compile Unit: length = 0x0000023d, format = DWARF32, version = 0x0004, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x000476fc)

0x000474c6: DW_TAG_compile_unit
              DW_AT_producer	("Apple clang version 11.0.3 (clang-1103.0.32.62)")
              DW_AT_language	(DW_LANG_ObjC)
              DW_AT_name	("/Users/bel/Desktop/dSYMProject/dSYMProject/ViewController.m")
              DW_AT_stmt_list	(0x0000a891)
              DW_AT_comp_dir	("/Users/bel/Desktop/dSYMProject")
              DW_AT_APPLE_major_runtime_vers	(0x02)
              DW_AT_low_pc	(0x0000000100000ce0)
              DW_AT_high_pc	(0x0000000100000ef6)

0x000475dd:   DW_TAG_subprogram
                DW_AT_low_pc	(0x0000000100000e70)
                DW_AT_high_pc	(0x0000000100000ef6)
                DW_AT_frame_base	(DW_OP_reg6 RBP)
                DW_AT_object_pointer	(0x000475f6)
                DW_AT_name	("-[ViewController warfTest]")
                DW_AT_decl_file	("/Users/bel/Desktop/dSYMProject/dSYMProject/ViewController.m")
                DW_AT_decl_line	(26)
                DW_AT_prototyped	(true)
Line info: file 'ViewController.m', line 29, column 18, start line 26
bel@beldeMacBook-Pro ~ % 
复制代码

这样,我们能得出结论: 0x000000010a107eb4代表的是 ViewController.m文件中的 第29行-[ViewController warfTest]方法。这样我们就能够根据地址值dSYM文件,将调试符号进行还原。

总结

1,dSYM是一个DWARF格式的文件,DWARF是保存调试信息的一种格式。
2,Deployment Postprocessing == YESstrip == All Symbols时,在编译的时候会将所有的本地符号脱掉。
3,Deployment Postprocessing == NODebug Information Format == DWAR with dSYM File时,会在编译后生成 dSYM
4,根据 偏移后的内存地址值 和 ASLR可以得到 函数的真实虚拟地址值,根据真实虚拟地址值可以在dSYM文件中获取其调试信息。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享