引言
在gdb调试的过程中,除了会使用基本命令外,我们还可以学会使用一些进阶命令,比如dissassemble,它是一个反汇编命令,即将目标代码反汇编成汇编代码。有时候我们使用的可执行程序并不带有调试信息,也就无法看到实际代码的执行结果,但是我们可以通过汇编代码来查看程序在执行过程中出现的异常崩溃等问题。
dissassemble的使用
disass/dissassemble是反汇编命令,主要用法如下:
- disass [function]:function是需要进行反汇编的函数名。
(gdb) bt
#0 0x00007fa316fc837f in raise () from /lib64/libc.so.6
#1 0x00007fa316fb2db5 in abort () from /lib64/libc.so.6
#2 0x00007fa31700b4e7 in __libc_message () from /lib64/libc.so.6
#3 0x00007fa3170125ec in malloc_printerr () from /lib64/libc.so.6
#4 0x00007fa317014390 in _int_free () from /lib64/libc.so.6
#5 0x0000000000400a49 in dumpTest::test (this=0x7ffe22f4c7a0) at hello.cpp:31
#6 0x0000000000400a8b in main () at hello.cpp:41
(gdb) disassemble main
Dump of assembler code for function main():
0x0000000000400a6b <+0>: push %rbp
0x0000000000400a6c <+1>: mov %rsp,%rbp
0x0000000000400a6f <+4>: sub $0x20,%rsp
0x0000000000400a73 <+8>: lea -0x20(%rbp),%rax
0x0000000000400a77 <+12>: mov %rax,%rdi
0x0000000000400a7a <+15>: callq 0x400ae6 <dumpTest::dumpTest()>
0x0000000000400a7f <+20>: lea -0x20(%rbp),%rax
0x0000000000400a83 <+24>: mov %rax,%rdi
0x0000000000400a86 <+27>: callq 0x400a0c <dumpTest::test()>
0x0000000000400a8b <+32>: mov $0x0,%eax
0x0000000000400a90 <+37>: leaveq
0x0000000000400a91 <+38>: retq
End of assembler dump.
- disass [address]:address是需要进行反汇编的函数地址。
(gdb) disassemble 0x0000000000400a49
Dump of assembler code for function dumpTest::test():
0x0000000000400a0c <+0>: push %rbp
0x0000000000400a0d <+1>: mov %rsp,%rbp
0x0000000000400a10 <+4>: sub $0x10,%rsp
0x0000000000400a14 <+8>: mov %rdi,-0x8(%rbp)
0x0000000000400a18 <+12>: mov $0x400bb3,%esi
0x0000000000400a1d <+17>: mov $0x602060,%edi
0x0000000000400a22 <+22>: callq 0x400870 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x0000000000400a27 <+27>: mov $0x400850,%esi
0x0000000000400a2c <+32>: mov %rax,%rdi
0x0000000000400a2f <+35>: callq 0x400890 <_ZNSolsEPFRSoS_E@plt>
0x0000000000400a34 <+40>: mov -0x8(%rbp),%rax
0x0000000000400a38 <+44>: mov 0x10(%rax),%rax
0x0000000000400a3c <+48>: mov $0x1,%esi
0x0000000000400a41 <+53>: mov %rax,%rdi
0x0000000000400a44 <+56>: callq 0x400880 <_ZdlPvm@plt>
0x0000000000400a49 <+61>: nop
0x0000000000400a4a <+62>: leaveq
0x0000000000400a4b <+63>: retq
End of assembler dump.
- 0x0000000000400a49是函数dumpTest::test的地址。
- disass [addr_start] [addr_end]:反汇编起始地址和结束地址之间的代码。
(gdb) disassemble 0x0000000000400a4c,0x0000000000400a63
Dump of assembler code from 0x400a4c to 0x400a63:
0x0000000000400a4c <dumpCrash()+0>: push %rbp
0x0000000000400a4d <dumpCrash()+1>: mov %rsp,%rbp
0x0000000000400a50 <dumpCrash()+4>: sub $0x10,%rsp
0x0000000000400a54 <dumpCrash()+8>: movq $0x400bbc,-0x8(%rbp)
0x0000000000400a5c <dumpCrash()+16>: mov -0x8(%rbp),%rax
0x0000000000400a60 <dumpCrash()+20>: mov %rax,%rdi
End of assembler dump.
- disass [addr_start],+[offset]:反汇编起始地址+偏移量之间的代码。
(gdb) disassemble 0x0000000000400a49,+10
Dump of assembler code from 0x400a49 to 0x400a53:
0x0000000000400a49 <dumpTest::test()+61>: nop
0x0000000000400a4a <dumpTest::test()+62>: leaveq
0x0000000000400a4b <dumpTest::test()+63>: retq
0x0000000000400a4c <dumpCrash()+0>: push %rbp
0x0000000000400a4d <dumpCrash()+1>: mov %rsp,%rbp
0x0000000000400a50 <dumpCrash()+4>: sub $0x10,%rsp
End of assembler dump.
- disass /m …:/m参数用于显示反汇编代码与源码中对应的行:
(gdb) disassemble /m 0x0000000000400a49
Dump of assembler code for function dumpTest::test():
29 {
0x0000000000400a0c <+0>: push %rbp
0x0000000000400a0d <+1>: mov %rsp,%rbp
0x0000000000400a10 <+4>: sub $0x10,%rsp
0x0000000000400a14 <+8>: mov %rdi,-0x8(%rbp)
30 cout<<"dumpTest"<<endl;
0x0000000000400a18 <+12>: mov $0x400bb3,%esi
0x0000000000400a1d <+17>: mov $0x602060,%edi
0x0000000000400a22 <+22>: callq 0x400870 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x0000000000400a27 <+27>: mov $0x400850,%esi
0x0000000000400a2c <+32>: mov %rax,%rdi
0x0000000000400a2f <+35>: callq 0x400890 <_ZNSolsEPFRSoS_E@plt>
31 delete childPStr;
0x0000000000400a34 <+40>: mov -0x8(%rbp),%rax
0x0000000000400a38 <+44>: mov 0x10(%rax),%rax
0x0000000000400a3c <+48>: mov $0x1,%esi
0x0000000000400a41 <+53>: mov %rax,%rdi
0x0000000000400a44 <+56>: callq 0x400880 <_ZdlPvm@plt>
32 }
0x0000000000400a49 <+61>: nop
0x0000000000400a4a <+62>: leaveq
0x0000000000400a4b <+63>: retq
End of assembler dump.
- 目标文件在编译时添加了调试信息,即编译参数中有’-g’,此选项才会生效,否则无效。
disassemble实践
(gdb) bt
#0 0x0000000000400de3 in dumpTest::test(char const*) ()
#1 0x0000000000400eaa in main ()
(gdb) f 0
#0 0x0000000000400de3 in dumpTest::test(char const*) ()
(gdb) disassemble
Dump of assembler code for function _ZN8dumpTest4testEPKc:
0x0000000000400dc0 <+0>: push %rbp
0x0000000000400dc1 <+1>: mov %rsp,%rbp
0x0000000000400dc4 <+4>: sub $0x20,%rsp
0x0000000000400dc8 <+8>: mov %rdi,-0x18(%rbp)
0x0000000000400dcc <+12>: mov %rsi,-0x20(%rbp)
0x0000000000400dd0 <+16>: movl $0x1,-0x4(%rbp)
0x0000000000400dd7 <+23>: movq $0x0,-0x10(%rbp)
0x0000000000400ddf <+31>: mov -0x10(%rbp),%rax
=> 0x0000000000400de3 <+35>: movzbl (%rax),%eax
0x0000000000400de6 <+38>: movsbl %al,%eax
0x0000000000400de9 <+41>: mov %eax,%esi
0x0000000000400deb <+43>: mov $0x6020a0,%edi
0x0000000000400df0 <+48>: callq 0x400c00 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c@plt>
0x0000000000400df5 <+53>: mov $0x400b80,%esi
0x0000000000400dfa <+58>: mov %rax,%rdi
0x0000000000400dfd <+61>: callq 0x400be0 <_ZNSolsEPFRSoS_E@plt>
0x0000000000400e02 <+66>: mov -0x18(%rbp),%rax
0x0000000000400e06 <+70>: mov 0x10(%rax),%rax
0x0000000000400e0a <+74>: mov $0x1,%esi
0x0000000000400e0f <+79>: mov %rax,%rdi
0x0000000000400e12 <+82>: callq 0x400bd0 <_ZdlPvm@plt>
0x0000000000400e17 <+87>: nop
0x0000000000400e18 <+88>: leaveq
0x0000000000400e19 <+89>: retq
End of assembler dump.
可以看出程序在<+35>这个位置(箭头指向的位置)崩溃了。其中rax寄存器里保存了一个地址值,通过(%rax)去寻址,从而导致程序崩溃。猜测可能原因是:对空指针或者野指针进行访问时导致程序崩溃。接下来简单验证下就知道了:
(gdb) i r rax
rax 0x0 0
通过打印rax寄存器rax中的值也可以看出其保存的地址值是0,也就是空指针,那么对0进行寻址,必然会失败。接下来再来定位这个core在程序中的大概位置。
(gdb) x/xg 0x6020a0
0x6020a0 <_ZSt4cout@@GLIBCXX_3.4>: 0x00007f82e10b9ec0
可以看到在<+43>的位置大概就是程序中离core位置最近的地方,通过查看其指令,可以看出是一个cout输出流。所以可以去dumpTest::test函数中找到cout的代码,然后查看该代码之前是不是有访问空指针的可能性。
实际上,生产环境中程序崩溃的定位会复杂很多,此处结合disass的使用只提供了一点点的思路。