反汇编及函数调用堆栈分析

非常感谢网易云课堂能提供免费的学习资源。这篇博客为孟宁老师教授的Linux内核分析的作业。

AT&T汇编的语法和学习的x86汇编语法差别很大,最明显的就是源操作数和目的操作数的位置是相反的。

在实验楼的~/Code目录下,touch main.c,用vim编辑代码如下:

int func1(int x){
    return x + 100;
}
int func0(int x){
    return func1(x) + 110;
}
int main(void){
    func0(1) + 200;
    return 0;
}

使用gcc -S -o main.S main.c转换成汇编代码,使用vim main.S查看文件内容。
实验楼截图

在每个函数调用的时候,都会构建相同的栈结构。
1. 执行call,将返回地址压入栈中。
2. 保存栈基址寄存器到栈顶。
3. 将栈顶指针寄存器内容保存到栈基址寄存器中。

如果函数中定义了局部变量,那么栈顶指针寄存器会减去相应的值(本函数内所有局部变量大小,并以寄存器大小对齐,以寄存器大小整数倍向上取整)。

在函数执行完毕之后,会销毁进入本函数时所建立的栈结构:
1. 用栈基址寄存器的内容恢复栈顶指针寄存器。(释放局部变量占用的空间。恢复栈顶指针寄存器后,栈顶指针寄存器正好指向保存的栈基址寄存器的内容)。
2. 在栈中弹出保存的栈基址寄存器到栈基址寄存器中。(将栈基址寄存器恢复到调用之前的状态,同时也释放了栈中,参数占用的空间)。
3. 执行ret指令,返回到call指令的下一条指令处继续执行。

总结:
1. 在整个过程中,堆栈是以机器字长为单位对其的。例如,使用64位编码,使用的是pushqpopq,栈中是以8字节对齐的。而在32位平台上使用的是pushlpopl,栈中是以4字节对齐的。
2. 在函数的栈结构构造完成之后,栈基址寄存器指向原栈基址寄存器的内容,加上一个机器字长,保存的是返回地址,加上两个机器字长是第一个参数。第一个参数之后以此类推。

【局部变量A】 <= 【SP】
【局部变量B】
【保存的BP】
【返回地址】
【第一个参数】
【第二个参数】
【……】
  1. C调用是从最右侧参数开始入栈,PASCAL是从最左侧参数开始入栈。所以C能相对简单地实现参数个数不确定的函数。还有其他的几种调用协议:__stdcall__fastcall__thiscall等等。有时为了加快调用(声明或者编译器优化),会用寄存器传参的方式。
  2. 一般情况下,如果数据长度小于等于机器字长,那么,调用时会统一以机器字长压入栈中。如果压入的是结构体等长于机器字长的数据,则从数据末尾开始压入栈中,以保持结构体各个域便宜位置在栈中同样适用。返回同理。

参考链接:
AT&T汇编语法
GCC中文手册


文添艺
原创作品转载请注明出处。
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-10000290

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值