在上一篇《GDB调试技巧实战–为release版本的函数寻找参数值》中,我们探讨了一种为函数找参数的办法,
但是,那是最理想的情况 -
- 编译时没有使用-fomit-frame-pointer。
- 编译时没有开启优化。
还是接着用上节的c代码,让我们详细看看call1函数在不同编译条件下的汇编代码。
- 没使用-fomit-frame-pointer,没优化
一切都很完美(rbp是栈基,rsp是占顶,使用edi/esi等寄存器传参,没用的变量也分配了空间):
gcc release_core_csdn.c
(gdb) disass call1
Dump of assembler code for function call1:
0x00000000004007df <+0>: push %rbp
0x00000000004007e0 <+1>: mov %rsp,%rbp
0x00000000004007e3 <+4>: sub $0x20,%rsp
0x00000000004007e7 <+8>: mov %edi,-0x14(%rbp)
0x00000000004007ea <+11>: mov %esi,-0x18(%rbp)
0x00000000004007ed <+14>: movl $0x6c6c6163,-0x10(%rbp)
0x00000000004007f4 <+21>: movw $0x31,-0xc(%rbp)
0x00000000004007fa <+27>: mov -0x18(%rbp),%edx
0x00000000004007fd <+30>: mov -0x14(%rbp),%eax
0x0000000000400800 <+33>: mov %eax,%esi
0x0000000000400802 <+35>: mov $0x4009ae,%edi
0x0000000000400807 <+40>: mov $0x0,%eax
0x000000000040080c <+45>: callq 0x4005c0 <printf@plt>
0x0000000000400811 <+50>: mov $0x16,%esi
0x0000000000400816 <+55>: mov $0x15,%edi
0x000000000040081b <+60>: callq 0x400799 <call2>
0x0000000000400820 <+65>: leaveq
0x0000000000400821 <+66>: retq
- 仅仅使用编译选项-fomit-frame-pointer
rbp不再是栈基(function prolog变了), 其它都正常。
gcc release_core_csdn.c -fomit-frame-pointer
(gdb) disass call1
Dump of assembler code for function call1:
0x00000000004007ee <+0>: sub $0x28,%rsp
0x00000000004007f2 <+4>: mov %edi,0xc(%rsp)
0x00000000004007f6 <+8>: mov %esi,0x8(%rsp)
0x00000000004007fa <+12>: movl $0x6c6c6163,0x10(%rsp)
0x0000000000400802 <+20>: movw $0x31,0x14(%rsp)
0x0000000000400809 <+27>: mov 0x8(%rsp),%edx
0x000000000040080d <+31>: mov 0xc(%rsp),%eax
0x0000000000400811 <+35>: mov %eax,%esi
0x0000000000400813 <+37>: mov $0x4009ce,%edi
0x0000000000400818 <+42>: mov $0x0,%eax
0x000000000040081d <+47>: callq 0x4005c0 <printf@plt>
0x0000000000400822 <+52>: mov $0x16,%esi
0x0000000000400827 <+57>: mov $0x15,%edi
0x000000000040082c <+62>: callq 0x4007a1 <call2>
0x0000000000400831 <+67>: add $0x28,%rsp
0x0000000000400835 <+71>: retq
- 开启优化-O1, 一眼看上去指令少了很多,函数变短了;rbp也不用了;没用过得变量如eye_catcher被优化没了。
gcc release_core_csdn.c -O1
(gdb) disass call1
Dump of assembler code for function call1:
0x000000000040074e <+0>: sub $0x8,%rsp
0x0000000000400752 <+4>: mov %esi,%edx
0x0000000000400754 <+6>: mov %edi,%esi
0x0000000000400756 <+8>: mov $0x4008ce,%edi
0x000000000040075b <+13>: mov $0x0,%eax
0x0000000000400760 <+18>: callq 0x400590 <printf@plt>
0x0000000000400765 <+23>: mov $0x16,%esi
0x000000000040076a <+28>: mov $0x15,%edi
0x000000000040076f <+33>: callq 0x400727 <call2>
0x0000000000400774 <+38>: add $0x8,%rsp
0x0000000000400778 <+42>: retq
- 开启优化-O2:除了O1优化项外,还把call2内联了进来,少了调用call2需要的额外成本。
$ gcc *csdn.c -O2
(gdb) disass call1
Dump of assembler code for function call1:
0x00000000004007d0 <+0>: sub $0x8,%rsp
0x00000000004007d4 <+4>: mov %esi,%edx
0x00000000004007d6 <+6>: xor %eax,%eax
0x00000000004007d8 <+8>: mov %edi,%esi
0x00000000004007da <+10>: mov $0x4008ee,%edi
0x00000000004007df <+15>: callq 0x400590 <printf@plt>
0x00000000004007e4 <+20>: mov $0x16,%edx
0x00000000004007e9 <+25>: mov $0x15,%esi
0x00000000004007ee <+30>: mov $0x4008d0,%edi
0x00000000004007f3 <+35>: xor %eax,%eax
0x00000000004007f5 <+37>: callq 0x400590 <printf@plt>
0x00000000004007fa <+42>: movl $0x5,0x0
0x0000000000400805 <+53>: add $0x8,%rsp
0x0000000000400809 <+57>: retq
一般用C/C++发布的包都是经过编译器优化的,这样的程序已经不能用rbp追踪每层的栈基了,只能从最后的rsp结合每个函数的汇编代码一层层的往上加offset(如sub $0x8,%rsp,offset就是8,还要注意额外的push/pop指令)找到每层的栈基。这是一个很痛苦的过程!
最佳实践:
细心的读者可能已经注意到每个函数中都有一个字符串eye_catcher, 如果它没被优化掉便可以通过查找内存的办法直接找到这个字符串,从而找到对应函数的栈。为了防止eye_cather被优化点,建议声明为:
volatile char eye_catcher[]="call1";
另外,建议每个数据结构的开头也放一个eye_catcher, 看到这个字符串从而一下就知道是某个数据结构。这样当你怀疑某个地址是某个参数时,特别是复杂数据结构的指针时,方便验证你的想法。
struct XXX{
char eye_catcher[4];
...
};
还有一个好处:通过观察eye_catcher,如果不是预期值,很有可能发生了内存overflow。内存问题可是占了C/C++程序总BUG数的70%。
会牺牲一点点performance, 但是值得!
最后简单看下O2条件下,崩溃时的栈,大致能看出从哪到哪是哪个函数的栈:
(gdb) where
(gdb) x /64s $rsp
0x7fffffffe0b0: "call1"
0x7fffffffe0b6: ""
0x7fffffffe0b7: ""
0x7fffffffe0b8: "h\352\377\367\377\177"
0x7fffffffe0bf: ""
0x7fffffffe0c0: "call2"
0x7fffffffe0c6: ""
0x7fffffffe0c7: ""
0x7fffffffe0c8: "P\341\377\367\377\177"
0x7fffffffe0cf: ""
0x7fffffffe0d0: "\003"
0x7fffffffe0d2: ""
0x7fffffffe0d3: ""
0x7fffffffe0d4: ""
0x7fffffffe0d5: ""
0x7fffffffe0d6: ""
0x7fffffffe0d7: ""
0x7fffffffe0d8: "\244\b@"
0x7fffffffe0dc: ""
0x7fffffffe0dd: ""
0x7fffffffe0de: ""
0x7fffffffe0df: ""
0x7fffffffe0e0: "call0"
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)