博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
每天学点GDB 10
阅读量:6266 次
发布时间:2019-06-22

本文共 2874 字,大约阅读时间需要 9 分钟。

近两周在做一个trouble shooting,需要对函数调用栈进行分析以找出入参和局部变量。因为在编译生成可执行程序的时候,用gcc进行了O2的优化,许多假设的函数调用栈模型都不成立了。花了一番周折,终于正确的翻译出入参和局部变量,此一旅程中的一些经验还是值得记录下来。

在32位x86系统上,函数调用栈的布局如下图所示。

栈底在高地址段,栈顶在低地址段。

从栈底到栈顶的内容分别为:

  1. 函数入参
  2. 返回地址
  3. 保存的寄存器值
  4. 被调用函数的局部变量

如果带有调试信息,则要获取上述4个部分的值很容易,对应的指令分别如下。

  1. info args
  2. info frame
  3. info registers
  4. info locals

如果没有调试信息,则可以根据这一模型并结合反汇编的结果来算出入参与局部变量的存储位置。针对32位的具体例子比较容易找到。

现在专门提一提在x86 64位下的不同,在x86 64下,因为寄存器数量增多,为了提高效率,入参基本上都是通过寄存器来传递。示例程序入下。

#include 
#include
#include
int demo_func(char* msg, int a, int b); int main(int argc, char** argv) { char* info = "just a demo"; int a,b; a = 320; b = 100; demo_func(info, a, b); return 0;}int demo_func(char* msg, int a, int b) { int sum; sum = a + b; return sum;}

main函数反汇编结果如下

Dump of assembler code for function main:   0x00000000004004b0 <+0>:    push   %rbp   0x00000000004004b1 <+1>:    mov    %rsp,%rbp   0x00000000004004b4 <+4>:    sub    $0x20,%rsp   0x00000000004004b8 <+8>:    mov    %edi,-0x14(%rbp)   0x00000000004004bb <+11>:    mov    %rsi,-0x20(%rbp)   0x00000000004004bf <+15>:    movq   $0x400594,-0x8(%rbp)   0x00000000004004c7 <+23>:    movl   $0x140,-0xc(%rbp)   0x00000000004004ce <+30>:    movl   $0x64,-0x10(%rbp)   0x00000000004004d5 <+37>:    mov    -0x10(%rbp),%edx   0x00000000004004d8 <+40>:    mov    -0xc(%rbp),%ecx   0x00000000004004db <+43>:    mov    -0x8(%rbp),%rax   0x00000000004004df <+47>:    mov    %ecx,%esi   0x00000000004004e1 <+49>:    mov    %rax,%rdi   0x00000000004004e4 <+52>:    callq  0x4004f0 
0x00000000004004e9 <+57>: mov $0x0,%eax 0x00000000004004ee <+62>: leaveq 0x00000000004004ef <+63>: retq End of assembler dump.

注意callq 0x4004f0 <demo_func>上的三行代码,它们表示demo_func的入参,可以看出入参是通过寄存器来进行传递的。

而寄存器在demo_func中可以会被改写,于是在改写之前,这些入参会被再次保存,它们保存在stack frame中,具体位置需要根据反汇编结果进行计算。即需要根据被调用函数的反汇编代码来计算入参。

demo_func的反汇编结果如下。

Dump of assembler code for function demo_func:   0x00000000004004f0 <+0>:    push   %rbp   0x00000000004004f1 <+1>:    mov    %rsp,%rbp   0x00000000004004f4 <+4>:    mov    %rdi,-0x18(%rbp)   0x00000000004004f8 <+8>:    mov    %esi,-0x1c(%rbp)   0x00000000004004fb <+11>:    mov    %edx,-0x20(%rbp)   0x00000000004004fe <+14>:    mov    -0x20(%rbp),%eax   0x0000000000400501 <+17>:    mov    -0x1c(%rbp),%edx   0x0000000000400504 <+20>:    add    %edx,%eax   0x0000000000400506 <+22>:    mov    %eax,-0x4(%rbp)   0x0000000000400509 <+25>:    mov    -0x4(%rbp),%eax   0x000000000040050c <+28>:    pop    %rbp   0x000000000040050d <+29>:    retq   End of assembler dump.

编译时如果加上-O2优化,则反汇编结果变为

Dump of assembler code for function demo_func:   0x00000000004004b0 <+0>:    lea    (%rsi,%rdx,1),%eax   0x00000000004004b3 <+3>:    retq   End of assembler dump.

内容极度简化,在具体计算时,一定要根据反汇编的结果来进行。

转载于:https://www.cnblogs.com/hseagle/archive/2013/06/02/3114049.html

你可能感兴趣的文章
闲扯下午引爆乌云社区“盗窃”乌云币事件
查看>>
02@在类的头文件中尽量少引入其他头文件
查看>>
JAVA IO BIO NIO AIO
查看>>
input checkbox 复选框大小修改
查看>>
网吧维护工具
查看>>
BOOT.INI文件参数
查看>>
vmstat详解
查看>>
新年第一镖
查看>>
unbtu使用笔记
查看>>
MaxCompute 学习计划(一)
查看>>
OEA 中 WPF 树型表格虚拟化设计方案
查看>>
Android程序开发初级教程(一) 开始 Hello Android
查看>>
使用Gradle打RPM包
查看>>
“我意识到”的意义
查看>>
淘宝天猫上新辅助工具-新品填表
查看>>
再学 GDI+[43]: 文本输出 - 获取已安装的字体列表
查看>>
nginx反向代理
查看>>
操作系统真实的虚拟内存是什么样的(一)
查看>>
hadoop、hbase、zookeeper集群搭建
查看>>
python中一切皆对象------类的基础(五)
查看>>