飞鸟 原创作品转载请注明出处 《Linux操作系统分析》MOOC课程
1. 编写C语言代码main.c
int g(int x)
{
return x + 7;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(4) + 2;
}
2.编译输出为汇编代码
在64位Linux虚拟机上执行以下命令:
gcc –S –o main.s main.c -m32
-m32指明输出为32位汇编代码
3.汇编代码解释
1.32位通用寄存器
eax 累加器
乘、除、输入、输出等
ebx 基地址寄存器
可作为存储器指针
ecx 计数寄存器
指示循环及位移次数
edx 数据寄存器
存放I/O端口地址
ebp 堆栈基指针
esp 堆栈顶指针
存放堆栈内存储单元的偏移量
esi edi 变址寄存器
存放存储单元在段内的偏移量
2.段寄存器
CS 代码段
代码段的段值
DS 数据段
数据段的段值
ES 附加段
附加数据段的段值
SS 堆栈段
堆栈段的段值
FS 附加段
附加数据段的段值
GS 附加段
附加数据段的段值
汇编代码程序截图
常见汇编指令介绍
- movl %eax, %edx
- edx = eax; 寄存器寻址
- movl $0x123, %edx
- edx = 0x123 立即寻址,立即数是以$开头的数值
- movl 0x123, %edx
- edx = (int32_t)0x123 直接寻址 直接访问一个指定的内存地址的数据
- movl (%ebx), %edx
- edx = (int32_t)ebx 间接寻址 将寄存器的值作为一个内存地址来访问内存
movl 4(%ebx), %edx
- edx = (int32_t)(ebx + 4) 变址寻址 在间接寻址之时改变寄存器的数值
pushl %eax
- subl $4, %esp
- movl %eax, (%esp)
- popl %eax
- movl (%esp), %eax
- addl $4, %esp
- call 0x12345
- pushl %eip
- movl $0x12345, %eip
- ret
- popl %eip
- enter
- pushl %ebp
- movl %esp,%ebp
- leave
- movl %ebp, %esp
- popl %ebp
注:
- eip寄存器不能被直接修改,只能通过特殊指令间接修改;
eip 指示了当前要执行的指令; - ebp esp 指示了程序执行的堆栈空间;
- eax 作为运算结果的保存值使用;函数的返回值默认使用eax寄存器存储返回给上一级函数
汇编代码说明
1g:
2 pushl %ebp
3 movl %esp, %ebp
4 movl 8(%ebp), %eax
5 addl $7, %eax
6 popl %ebp
7 ret
8f:
9 pushl %ebp
10 movl %esp, %ebp
11 subl $4, %esp
12 movl 8(%ebp), %eax
13 movl %eax, (%esp)
14 call g
15 leave
16 ret
17main: //主函数
18 pushl %ebp
19 movl %esp, %ebp
20 subl $4, %esp
21 movl $8, (%esp)
22 call f
23 addl $2, %eax
24 leave
25 ret
程序从main函数开始
首先,保存ebp的值,然后将ebp的值设为esp,相当于保存原函数的堆栈,然后建立当前函数的堆栈
把立即数4压栈,然后调用函数f
将当前ebp所指向的堆栈位置向上数两个的位置,也就是放4的那个位置,压栈
做一个变址寻址,即将4放入累加器eax中
累加器再加7,累加器为11.
然后pop %ebp,相当于leave,由于g函数中没有额外压栈,所以不做mov指令
返回f函数
esp向上移动一个堆栈位置然后leave
返回main
同样esp向上移动一个堆栈位置然后leave,与此同时eax加2,得到最终值13
程序结束
小结:
通过汇编代码的逐行解释,了解了重要寄存器的功能及CPU执行命令的实现过程。按照冯诺依曼计算机结构,计算机工作的过程就是CPU不断从通过指针从寄存器中读取、执行指令。