Linux x86-64 函数调用栈实例分析
文章目录
前言
动手实践并写文章花5倍的时间一次性把事情做到90分,好过读别人文章只能做到60分,后面还需要花时间继续深入学习(做事情一定要做到有效的阈值)。本文目的希望通过分析一个简单的函数调用加深对x86-64寄存器及栈帧的结构的认识,以便在定位问题需要的时候能够熟练运用。
环境
1.操作系统和内核
|
|
2.GCC版本
|
|
3.GDB版本
|
|
GDB 分析调用栈
1. 准备代码
|
|
2. 生成可执行文件
|
|
3. 分析调用栈
主要通过gdb分析调用栈变化情况
3.1 初始化
|
|
栈内存(堆栈向下生长):
|
|
3.2 调用caller
寄存器信息:
|
|
栈内存(堆栈向下生长):
|
|
main函数的汇编代码如下:
|
|
3.3调用sum
寄存器信息:
|
|
caller对应汇编代码:
|
|
通过上面汇编与寄存器信息,可以了解到函数调用的参数一般先通过寄存器传递。但是可用于传递函数的参数有限,超出的参数怎么传递呢?入调用函数的栈空间,具体如下:
|
|
栈内存(栈向下生长):
|
|
3.4 sum获取参数
|
|
栈内存(栈向下生长):
|
|
sum函数对应汇编如下:
|
|
注意的是调用sum的时候并没有分配栈空间?主要原因是sum函数内没有调用其他函数,sum就只能被别的函数调用,sum的局部变量直接由rsp向下生长,调用结束后,直接恢复调用者的栈,这样的好处有两个:
- 少一个指令,提高执行效率
- 编译的时候并不需要计算需要开辟多少栈空间
为什么需要将值从寄存器取出?直接利用寄存器取值不可以吗? 传递参数的寄存器在函数执行指令的时候有其他用处,调用函数需要将参数从寄存器取出到栈内存中(在一定的情况下即调用函数的指令不需要占用传递参数的寄存器,如果加大编译的优化级别,是否不会从参数寄存器取出参数到函数的栈内存中而是直接使用寄存器?)另外可以从这里看出,从代码层面优化性能可以考虑减少函数调用以及优化一些不必要的参数传递,尽管只能尽一丝绵薄之力。
3.5 sum返回值
|
|
x86-64寄存器说明
- rsp:对应32位esp寄存器,保存当前堆栈栈顶指针的寄存器
- rbp:对应32位ebp寄存器,保存了当前堆栈基地址指针的寄存器
- rax: 临时寄存器,当我们调用系统调用的时候,rax保外系统调用号
- rdx:传递第3个参数到函数
- rdi:传递第1个参数到函数
- rsi:传递第2个参数到函数
- rip: 下一个执行指令地址
参考
文章作者 沉风网事
上次更新 2016-03-09