dSYM

838 阅读11分钟

调试信息生成过程探究

什么是dSYM?

dSYM文件就是保存按DWARF格式保存调试信息的文件。

什么是DWARF?

DWARF是一种被众多编译器和调试器使用的用于支持源代码级别 调试的调试文件格式。

dSYM是如何生成的

  • 读取debug map
  • 从.o文件中加载__DWARF
  • 重新定位所有地址
  • 最后将全部的DWARF打包成dSYM Bundle

编译阶段

编译器在编译的时候会将调试信息放在单独的一个段中:segname __DWARF

clang -g -c test.m -o main.o

objdump --macho --private-headers main.o

===========================================================
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64  X86_64        ALL  0x00      OBJECT     4       1240 SUBSECTIONS_VIA_SYMBOLS
Load command 0
      cmd LC_SEGMENT_64
  cmdsize 1112
  segname 
   vmaddr 0x0000000000000000
   vmsize 0x00000000000004c1
  fileoff 1272
 filesize 1217
  maxprot rwx
 initprot rwx
   nsects 13
    flags (none)

Section
  sectname __text
   segname __TEXT
      addr 0x0000000000000000
      size 0x0000000000000055
    offset 1272
     align 2^4 (16)
    reloff 2492
    nreloc 4
      type S_REGULAR
attributes PURE_INSTRUCTIONS SOME_INSTRUCTIONS
 reserved1 0
 reserved2 0
Section
  sectname __data
   segname __DATA
      addr 0x0000000000000058
      size 0x0000000000000004
    offset 1360
     align 2^2 (4)
    reloff 0
    nreloc 0
      type S_REGULAR
attributes (none)
 reserved1 0
 reserved2 0
 
Section
  sectname __objc_imageinfo
   segname __DATA
      addr 0x000000000000005c
      size 0x0000000000000008
    offset 1364
     align 2^0 (1)
    reloff 0
    nreloc 0
      type S_REGULAR
attributes NO_DEAD_STRIP
 reserved1 0
 reserved2 0
 
Section
  sectname __debug_str
   segname __DWARF
      addr 0x0000000000000064
      size 0x0000000000000107
    offset 1372
     align 2^0 (1)
    reloff 0
    nreloc 0
      type S_REGULAR
attributes DEBUG
 reserved1 0
 reserved2 0

链接源文件阶段

链接的时候,会把段 segment __DWARF删掉,将所有的调试信息放到符号表中

clang -g test.m -o main

objdump --macho --private-headers main

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
  vmaddr 0x0000000100000000
  vmsize 0x0000000000004000
 fileoff 0
filesize 16384
 maxprot r-x
initprot r-x
  nsects 2
   flags (none)
Section
 sectname __text
  segname __TEXT
     addr 0x0000000100003f60
     size 0x0000000000000055
   offset 16224
    align 2^4 (16)
   reloff 0
   nreloc 0
     type S_REGULAR
attributes PURE_INSTRUCTIONS SOME_INSTRUCTIONS
reserved1 0
reserved2 0
Section
 sectname __unwind_info
  segname __TEXT
     addr 0x0000000100003fb8
     size 0x0000000000000048
   offset 16312
    align 2^2 (4)
   reloff 0
   nreloc 0
     type S_REGULAR
attributes (none)
reserved1 0
reserved2 0
Load command 2
     cmd LC_SEGMENT_64
 cmdsize 152
 segname __DATA_CONST
  vmaddr 0x0000000100004000
  vmsize 0x0000000000004000
 fileoff 16384
filesize 16384
 maxprot rw-
initprot rw-
  nsects 1
   flags 0x00000010 (unknown flags)
Section
 sectname __objc_imageinfo
  segname __DATA_CONST
     addr 0x0000000100004000
     size 0x0000000000000008
   offset 16384
    align 2^2 (4)
   reloff 0
   nreloc 0
     type S_REGULAR
attributes (none)
reserved1 0
reserved2 0
Load command 3
     cmd LC_SEGMENT_64
 cmdsize 152
 segname __DATA
  vmaddr 0x0000000100008000
  vmsize 0x0000000000004000
 fileoff 32768
filesize 16384
 maxprot rw-
initprot rw-
  nsects 1
   flags (none)

查看符号表

nm -pa main

0000000000000000 - 00 0000    SO /Users/tangge/Downloads/第十节、MachO与lldb/上课代码/1-mach-o分析/
0000000000000000 - 00 0000    SO test.m
000000006102b586 - 03 0001   OSO /var/folders/m1/d3klz67j5cx8qjt994655hkw0000gn/T/test-288d2f.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

生成dSYM

clang -g1 test.m -o main //生成dSYM文件

查看调试信息

dwarfdump取出并验证DWARF格式调试信息:

dwarfdump a.o
dwarfdump a.dSYM
dwarfdump --lookup 0x100000f20 --arch=x86_64 a.dSYM

--lookup 查看地址的调试信息。将显示出所在的目录,文件,函数等信息
//从符号表里抽取出来 按照dWARF格式生成dSYM文件
main.dSYM/Contents/Resources/DWARF/main:	file format Mach-O 64-bit x86-64

.debug_info contents:
0x00000000: Compile Unit: length = 0x00000063 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.0 (clang-1200.0.32.28)")
              DW_AT_language	(DW_LANG_ObjC)
              DW_AT_name	("test.m")
              DW_AT_LLVM_sysroot	("/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk")
              DW_AT_APPLE_sdk	("MacOSX.sdk")
              DW_AT_stmt_list	(0x00000000)
              DW_AT_comp_dir	("/Users/tangge/Downloads/\347\254\254\345\215\201\350\212\202\343\200\201MachO\344\270\216lldb/\344\270\212\350\257\276\344\273\243\347\240\201/1-mach-o\345\210\206\346\236\220")
              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文件

dsymutil可以被理解为是调试信息链接器。它按照上面的步骤执行:

  • 读取debug map
  • 从.o文件中加载DWARF
  • 重新定位所有地址
  • 最后将全部的DWARF打包成dSYM Bundle 有了dSYM后,我们就拥有了最标准的DWARF的文件,任何可以dwarf读取工具(可以处理Mach-O二进制文件)都可以处理该标准DWARF

dsymutil操作DWARF格式的debug symbol。可以将可执行文件debug symbol的生成DWARF格式的文件:

Mach-o格式解析

重定位符号表作用

找到虚拟内存地址

虚拟内存地址与ASLR与dSYM文件关系

dSYM文件内保存的是没有偏移的虚拟地址还是偏移后的地址?

dSYM实在编译链接完成时候生成的,保存的是真实的虚拟内存地址 偏移量是在dyld加载程序的时候加上偏移量, 我们在运行时调试到的地址实际上是 调试地址 = 虚拟地址 + ASLR

获取ASLR

#import "ViewController.h"
#import <mach-o/dyld.h>
#import <mach-o/getsect.h>
#import <objc/runtime.h>

@interface ViewController ()

@end

@implementation ViewController

// 获取ASLR
uintptr_t get_slide_address(void) {
    uintptr_t vmaddr_slide = 0;
    // 使用的所有的二进制文件 = ipa + 动态库
    // ASLR Macho 二进制文件 image 偏移
    for (uint32_t i = 0; i < _dyld_image_count(); i++) {
        // 遍历的是那个image名称
        const char *image_name = (char *)_dyld_get_image_name(i);
        const struct mach_header *header = _dyld_get_image_header(i);
        if (header->filetype == MH_EXECUTE) {
            vmaddr_slide = _dyld_get_image_vmaddr_slide(i);
        }
        NSString *str = [NSString stringWithUTF8String:image_name];
        if ([str containsString:@"TestInject"]) {
              NSLog(@"Image name %s at address 0x%llx and ASLR slide 0x%lx.\n", image_name, (mach_vm_address_t)header, vmaddr_slide);
                   break;
          }
    }
    // ASLR返回出去
    return (uintptr_t)vmaddr_slide;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self getMethodVMA];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self test_dwarf];
    });
    NSLog(@"123");
}

- (void)test_dwarf {
    NSArray *array = @[];
    array[1];
}

- (void)getMethodVMA {
    // 运行中的地址(经过偏移)
    IMP imp = (IMP)class_getMethodImplementation(self.class, @selector(test_dwarf));
    unsigned long imppos = (unsigned long)imp;
    unsigned long slide =  get_slide_address();
    // 运行中的地址(偏移) - ASLR = 真正的虚拟内存地址
    unsigned long addr = imppos - slide;
    NSLog(@"%zd",addr);
}
@end
输出结果: 4294974736

配置xcode image.png

查看dSYM

e -f x -- 4294974736 = 0x000000100001d10
dwarfdump --lookup 0x000000100001d10 TestInject.app.dSYM

TestInject.app.dSYM/Contents/Resources/DWARF/TestInject:	file format Mach-O 64-bit x86-64
0x0004a51c: Compile Unit: length = 0x0000037d version = 0x0004 abbr_offset = 0x0000 addr_size = 0x08 (next unit at 0x0004a89d)

0x0004a527: DW_TAG_compile_unit
              DW_AT_producer	("Apple clang version 12.0.0 (clang-1200.0.32.29)")
              DW_AT_language	(DW_LANG_ObjC)
              DW_AT_name	("/Users/ws/Desktop/VIP\350\257\276\347\250\213/\347\254\254\345\215\201\350\212\202\343\200\201MachO\344\270\216lldb/\344\270\212\350\257\276\344\273\243\347\240\201/3-ASLR\344\270\216dSYM/TestInject/TestInject/ViewController.m")
              DW_AT_LLVM_sysroot	("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.4.sdk")
              DW_AT_APPLE_sdk	("iPhoneSimulator14.4.sdk")
              DW_AT_stmt_list	(0x0000b257)
              DW_AT_comp_dir	("/Users/ws/Desktop/VIP\350\257\276\347\250\213/\347\254\254\345\215\201\350\212\202\343\200\201MachO\344\270\216lldb/\344\270\212\350\257\276\344\273\243\347\240\201/3-ASLR\344\270\216dSYM/TestInject")
              DW_AT_APPLE_major_runtime_vers	(0x02)
              DW_AT_low_pc	(0x0000000100001a30)
              DW_AT_high_pc	(0x0000000100001dd2)

0x0004a6f7:   DW_TAG_subprogram
                DW_AT_low_pc	(0x0000000100001d10)
                DW_AT_high_pc	(0x0000000100001d77)
                DW_AT_frame_base	(DW_OP_reg6 RBP)
                DW_AT_object_pointer	(0x0004a711)
                DW_AT_name	("-[ViewController test_dwarf]")
                DW_AT_decl_file	("/Users/ws/Desktop/VIP课程/第十节、MachO与lldb/上课代码/3-ASLR与dSYM/TestInject/TestInject/ViewController.m")
                DW_AT_decl_line	(54)
                DW_AT_prototyped	(true)
Line info: file 'ViewController.m', line 54, column 0, start line 54

image.png

没有跳转到源码

image.png

image.png

跳转到源码 image.png

结论:保存的调试信息、文件信息、符号信息、地址都是有用的,lldb也可以使用,通过读取符号信息也可以找到源码. 用途:组件化二进制化可以通过有没有调试信息来控制是否可以调试源码的过程

 nm -pa /Users/tangge/Downloads/第十节、MachO与lldb/上课代码/3-ASLR与dSYM/代码调试/TestFramework/Products/TestFramework.framework/TestFramework
 
0000000000003ec0 t -[TestExample lg_test:]
0000000000008010 s __OBJC_METACLASS_RO_$_TestExample
0000000000008058 s __OBJC_$_INSTANCE_METHODS_TestExample
0000000000008078 s __OBJC_CLASS_RO_$_TestExample
0000000000008110 d __dyld_private
0000000000000000 - 00 0000    SO /Users/tangge/Library/Developer/Xcode/DerivedData/TestFramework-czmplaypblajwsfpwuymnoauygcy/Build/Intermediates.noindex/TestFramework.build/Debug-iphonesimulator/TestFramework.build/DerivedSources/
0000000000000000 - 00 0000    SO TestFramework_vers.c
00000000610953a3 - 03 0001   OSO /Users/tangge/Library/Developer/Xcode/DerivedData/TestFramework-czmplaypblajwsfpwuymnoauygcy/Build/Intermediates.noindex/TestFramework.build/Debug-iphonesimulator/TestFramework.build/Objects-normal/x86_64/TestFramework_vers.o
0000000000000000 - 00 0000  GSYM _TestFrameworkVersionString
0000000000000000 - 00 0000  GSYM _TestFrameworkVersionNumber
0000000000000000 - 01 0000    SO 
0000000000000000 - 00 0000    SO /Users/tangge/Downloads/第十节、MachO与lldb/上课代码/3-ASLR与dSYM/代码调试/TestFramework/Source/
0000000000000000 - 00 0000    SO TestExample.m
00000000610953a8 - 03 0001   OSO /Users/tangge/Library/Developer/Xcode/DerivedData/TestFramework-czmplaypblajwsfpwuymnoauygcy/Build/Intermediates.noindex/TestFramework.build/Debug-iphonesimulator/TestFramework.build/Objects-normal/x86_64/TestExample.o
0000000000003ec0 - 01 0000 BNSYM 
0000000000003ec0 - 01 0000   FUN -[TestExample lg_test:]
0000000000000048 - 00 0000   FUN 
0000000000000048 - 01 0000 ENSYM 
0000000000008010 - 0f 0000 STSYM __OBJC_METACLASS_RO_$_TestExample
0000000000008058 - 0f 0000 STSYM __OBJC_$_INSTANCE_METHODS_TestExample
0000000000008078 - 0f 0000 STSYM __OBJC_CLASS_RO_$_TestExample
0000000000000000 - 00 0000  GSYM _OBJC_METACLASS_$_TestExample
0000000000000000 - 00 0000  GSYM _OBJC_CLASS_$_TestExample
0000000000000000 - 01 0000    SO 
00000000000080e8 S _OBJC_CLASS_$_TestExample
00000000000080c0 S _OBJC_METACLASS_$_TestExample
0000000000003f78 S _TestFrameworkVersionNumber
0000000000003f40 S _TestFrameworkVersionString
                 U _NSLog
                 U _OBJC_CLASS_$_NSObject
                 U _OBJC_METACLASS_$_NSObject
                 U ___CFConstantStringClassReference
                 U __objc_empty_cache
                 U _objc_storeStrong
                 U dyld_stub_binder

.crash文件符号恢复原理探究

奔溃日志

截屏2021-07-30 下午10.34.32.png

脱符号

截屏2021-07-30 下午10.38.28.png

dyld在解析时候以macho为单位添加了个偏移地址aslr

e -f x -- 0x000000010bc22e70 - 0xbc21000
$4 = 0x0000000100001e70 //内存地址
dwarfdump --lookup 0x100001e70 --arch=x86_64 a.dSYM

image.png

dsymutil -f a -o a.dSYM
-f: .dwarf格式文件
-o <filename>: 输出.dSYM格式文件

//拷贝dSYM文件至根目录
rm -rf -- "${SRCROOT}/../dSYM"
mkdir -p -- "${SRCROOT}/../dSYM"
cp -Rv -- ${BUILT_PRODUCTS_DIR}/*.dSYM "${SRCROOT}/../dSYM"