反汇编一个简单的C程序

server/2024/12/27 2:18:44/

基于最简单的代码示例,一步步分析每一段汇编代码,对于堆栈变化

一、编译C代码

int g(int x) {return x + 3;
}
int f(int x) {return g(x);
}
int main(void) {return f(8) + 1;
}

编译

gcc -S -o main.s main.c -m32

得到汇编代码

g:pushl   %ebpmovl    %esp, %ebpmovl    8(%ebp), %eaxaddl    $3, %eaxpopl    %ebpretf:pushl   %ebpmovl    %esp, %ebpsubl    $4, %espmovl    8(%ebp), %eaxmovl    %eax, (%esp)call    gleaveret
main:pushl   %ebpmovl    %esp, %ebpsubl    $4, %espmovl    $8, (%esp)call    faddl    $1, %eaxleaveret

二、关键信息

%ebp:栈帧基址,用于指向当前函数的栈帧基址,使得函数内部可以方便地访问参数和局部变量。
%esp:栈顶指针,始终指向当前栈顶的位置
%eax:寄存器
leave:恢复调用者的栈帧

movl %ebp, %esp
popl %ebp

ret:将 %eax 中的值作为返回值。

三、压栈出栈详细步骤

第一步:进入main函数

pushl %ebp:将基指%ebp 压入栈,保存上一层栈帧基址
堆栈内容

[调用者的 %ebp]

movl %esp, %ebp:将当前栈顶(%esp)存入 %ebp,设定新的栈帧基址
[调用者的 %ebp]

subl $4, %esp:栈顶指针%esp向下移动 4 字节,为局部变量分配空间

[调用者的 %ebp]
[局部变量空间:4字节]

movl $8, (%esp):将立即数 8 压入栈顶,为 f 函数调用提供参数

[调用者的 %ebp]
[参数 x = 8]

call f:调用 f,请看f的堆栈变化,将返回值存入 %eax,栈在跳转到 f 时变化

addl $1, %eax
leave
ret

第二步:f 函数堆栈变化

pushl %ebp:保存调用者的 %ebp 到栈中

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]

movl %esp, %ebp:设置新的栈帧基址,将%esp赋给%ebp

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]

subl $4, %esp:为局部变量分配 4 字节的栈空间

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]
[局部变量空间:4字节]

movl 8(%ebp), %eax:将%ebp的值 x(即 8)加载到 寄存器%eax
movl %eax, (%esp):将 %eax(x = 8)压入栈,为 g 函数调用提供参数

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]
[局部变量空间]
[参数 x = 8]

call g:调用 g,栈跳转到 g 的堆栈变化,可以获得%eax=11

leave
ret

第三步:g 函数堆栈变化

pushl %ebp:保存调用者的 %ebp

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]
[局部变量空间]
[参数 x = 8]
[f 的 %ebp]

movl %esp, %ebp:设置新的栈帧基址 堆栈内容不变

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]
[局部变量空间]
[参数 x = 8]
[f 的 %ebp]

movl 8(%ebp), %eax:从栈中取出参数 x(8),存入 %eax
addl $3, %eax:将 3 加到 %eax,计算 x + 3 = 8 + 3 = 11

popl %ebp:恢复调用者的 %ebp,清理当前栈帧 %ebp。

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]
[局部变量空间]
[参数 x = 8]

ret:返回调用者,%eax 中的值 11 作为返回值,看回f

第四步:回到 f 函数堆栈变化

Call g :返回值 11 存入 %eax,并释放掉x,恢复调用者的栈帧

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]
[局部变量空间]

leave:恢复调用者的栈帧。

[调用者的 %ebp]
[参数 x = 8]

ret:返回调用者,%eax 中的值 11 作为返回值

第五步:回到 main 函数堆栈变化

call f 返回:f 返回值 11 存入 %eax。

[调用者的 %ebp]

addl $1, %eax:将 1 加到 %eax 中,计算 f(8) + 1 = 11 + 1 = 12。
leave:恢复调用者的栈帧。

堆栈内容为空

ret:返回调用者,%eax 中的值 12 作为程序最终返回值。

最终结果:程序返回值为 12,存储在寄存器 %eax 中。


http://www.ppmy.cn/server/153114.html

相关文章

Avalonia实例实战五:Carousel自动轮播图

文章目录 一、加载图片不使用avares:直接路径访问:使用avares:访问:二、使用Carousel控件官网示例:增加间隔3秒自动滑动三、使用自定义用户控件实现带RadioButton的轮播控件四、效果一、加载图片 将三张Png图片加载到项目中,在访问前,必须将路径添加到资源中,在csprojec…

GitHub 桌面版配置 |可视化界面进行上传到远程仓库 | gitLab 配置【把密码存在本地服务器】

🥇 版权: 本文由【墨理学AI】原创首发、各位读者大大、敬请查阅、感谢三连 🎉 声明: 作为全网 AI 领域 干货最多的博主之一,❤️ 不负光阴不负卿 ❤️ 文章目录 桌面版安装包下载clone 仓库操作如下GitLab 配置不再重复输入账户和密码的两个方…

自动驾驶---Parking端到端架构

​​​​​​1 背景 自动泊车也是智能驾驶低速功能中比较重要的一部分,低速功能其中还包括记忆泊车,代客泊车等。传统的泊车算法通常使用基于规则或者搜索优化的方案来实现。然而,由于算法的复杂设计,这些方法在复杂的泊车场景中效…

算法day_5 字符串处理专题

碎碎念 这是我在2024年12月21日的算法练习,加油! 题目一:不常见单词查找 884. 两句话中的不常见单词 题目描述 给定两个句子 s1 和 s2,找出仅在其中一个句子中出现一次的单词。也就是说,这些单词在两个句子中只出现…

【YashanDB知识库】Oracle pipelined函数在YashanDB中的改写

本文内容来自YashanDB官网,原文内容请见 https://www.yashandb.com/newsinfo/7802940.html?templateId1718516 【问题分类】功能使用 【关键字】pipelined 【问题描述】 Oracle PL/SQL中包含pipelined函数的对象迁移到YashanDB会出现不兼容现象。 【问题原因分…

MySQL什么情况下会导致索引失效

MySQL什么情况下会导致索引失效 索引(Index)是数据库中一种用于快速查找和访问表中数据的结构,它类似于书的目录,通过索引可以快速定位到目标数据,而无需遍历整个表,索引的存在可以显著提高查询速度&#x…

【产品更新】汇匠源保证金保函平台v2.0.23

汇匠源保证金保函平台 2.0,是汇匠源科技深耕行业需求的又一力作。该平台专为政府机构及企业量身打造,通过高度集成化的系统管理,实现对工资保证金从缴纳、管理到使用的全流程监控,确保每一笔资金都能精准到位,有效预防…

界面化管理Nginx的工具—NginxUI简介与搭建

转载说明:如果您喜欢这篇文章并打算转载它,请私信作者取得授权。感谢您喜爱本文,请文明转载,谢谢。 1. NginxUI简介 1.1 NginxUI介绍 Nginx UI 是一个全新的 Nginx 网络管理界面,旨在简化 Nginx 服务器的管理和配置。…