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

栈底在高地址段,栈顶在低地址段。
从栈底到栈顶的内容分别为:
函数入参
返回地址
保存的寄存器值
被调用函数的局部变量
如果带有调试信息,则要获取上述4个部分的值很容易,对应的指令分别如下。
info args
info frame
info registers
info locals
如果没有调试信息,则可以根据这一模型并结合反汇编的结果来算出入参与局部变量的存储位置。针对32位的具体例子比较容易找到。
现在专门提一提在x86 64位下的不同,在x86 64下,因为寄存器数量增多,为了提高效率,入参基本上都是通过寄存器来传递。示例程序如下:
<b>main函数反汇编结果如下:</b>
注意callq 0x4004f0 <demo_func>上的三行代码,它们表示demo_func的入参,可以看出入参是通过寄存器来进行传递的。
而寄存器在demo_func中可以会被改写,于是在改写之前,这些入参会被再次保存,它们保存在stack frame中,具体位置需要根据反汇编结果进行计算。即需要根据被调用函数的反汇编代码来计算入参。
<b>demo_func的反汇编结果如下:</b>
<b>编译时如果加上-o2优化,则反汇编结果变为:</b>
内容极度简化,在具体计算时,一定要根据反汇编的结果来进行。