调试信息生成过程探究
什么是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
查看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
没有跳转到源码
跳转到源码
结论:保存的调试信息、文件信息、符号信息、地址都是有用的,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文件符号恢复原理探究
奔溃日志
脱符号
dyld在解析时候以macho为单位添加了个偏移地址aslr
e -f x -- 0x000000010bc22e70 - 0xbc21000
$4 = 0x0000000100001e70 //内存地址
dwarfdump --lookup 0x100001e70 --arch=x86_64 a.dSYM
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"