第一课 汇编代码的工作过程
源文件main.c代码如下:
int g(int x)
{
return x + 7;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(5) + 3;
}
执行gcc -S main.c -m32后,生成main.s,即汇编指令。去除以"."开头的行,机器指令的指令如下:
<span style="font-size:18px;">
1 .file "main.c"
2 .text
3 .globl g
4 .type g, @function
5 g:
6 pushl %ebp
7 movl %esp, %ebp
8 movl 8(%ebp), %eax
1 .file "main.c"
2 .text
3 .globl g
4 .type g, @function
5 g:
6 pushl %ebp
7 movl %esp, %ebp
8 movl 8(%ebp), %eax
9 addl $7, %eax
10 popl %ebp
11 ret
12 .size g, .-g
13 .globl f
14 .type f, @function
15 f:
16 pushl %ebp
17 movl %esp, %ebp
18 subl $4, %esp
19 movl 8(%ebp), %eax
20 movl %eax, (%esp)
21 call g
22 leave
23 ret
24 .size f, .-f
25 .globl main
26 .type main, @function
27 main:
28 pushl %ebp
29 movl %esp, %ebp
30 subl $4, %esp
31 movl $5, (%esp)
32 call f
33 addl $3, %eax
34 leave
35 ret
36 .size main, .-main
37 .ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-4)"
38 .section .note.GNU-stack,"",@progbits</span>
从main函数开始,下图为不同时期中不同地址存储的数据:
0、开始时,ebp和esp均指向地址1中
1、执行pushl %ebp,即
subl $4, %esp
movl (%ebp), $esp
esp指向地址2,地址2中存放ebp的地址1。
2、movl %esp, %ebp
ebp指向esp的地址,即指向地址2.
3、subl $4, %esp
esp下移,指向地址3
4、movl $5, (%esp)
esp指向的地址(地址3)中存储值5
5、call f
执行指令pushl %eip(*);movlf, %eip(*),即esp首先指向地址4,地址4中存入当前eip指向的地址(call的下一行,即代码33行);eip指向代码段f
6、pushl %ebp
esp下移,指向地址5,地址5中存放ebp指向的地址(地址3)
7、movl %esp, %ebp
ebp指向esp指向的地址(地址5)
8、subl $4, %esp
esp下移,指向地址6
9、movl 8(%ebp), %eax
eax=ebp上移两个地址单位的地址中的值5
10、movl %eax, (%esp)
esp指向的地址(地址6)中存入eax=5
11、call g
esp指向下一个地址(地址7),地址7中存入eip指向的地址(代码22行),eip指向代码段g
12、pushl %ebp
esp指向下一个地址(地址8),地址8中存入ebp的地址(地址5)
13、movl %esp, %ebp
ebp指向esp指向的地址(地址8)
14、movl 8(%ebp), %eax
eax=ebp指向的上两个地址(地址6)中的值=5
15、addl $7, %eax
eax=5+7=12
16、popl %ebp
popl指令执行的是
movl (%esp),%ebp
addl $4,$esp
即ebp指向esp地址中的值(地址5),esp指向上一个地址(地址7)
17、ret
ret指令执行的是
popl %eip
即eip指向esp地址中的值(代码22行,函数f),esp指向上一个地址(地址6)
18、leave
leave指令执行的是
movl %ebp, %esp
popl %ebp
即esp指向ebp指向的地址(地址5),ebp指向esp地址中存放的值(地址2),esp指向上一个地址(地址4)
19、ret
ret指令执行的是
popl %eip
即eip指向当前esp指向的地址(地址4)中的值(代码33行),esp指向上一个地址(地址3)
20、addl $3, %eax
eax=3+12=15
21、leave
leave指令执行的是
movl %ebp, %esp
popl %ebp
即esp指向ebp指向的地址(地址2),ebp指向esp指向的地址(地址2)中的值(地址1),esp指向上一个地址(地址1)
22、ret
eip指向esp地址(地址1)中的值,esp指向上一个地址。
至此退出整个main函数。
课堂笔记:
1、pushl $0x11指令执行的是
subl $4, %esp
movl $0x11, (%esp)
esp指向下一个地址,地址中存入push的值
2、popl %eax 指令执行的是:
movl(%esp), %eax
addl $4, %esp
eax中存入esp中的值,esp指向上一个地址
3、call 0x12345 指令执行的是
pushl %eip
movl 0x12345, %eip
esp指向下一个地址,地址中存入当前eip的地址,eip 指向跳转的指令
4、ret 执行的是popl %eip,eip指向当前esp中存放的值,esp指向上一个地址
5、leave 执行的是
movl %ebp, %esp
popl %ebp
esp指向ebp指向的地址,ebp指向esp指向地址中的值,esp指向上一个地址。即esp指向ebp的上一个地址,ebp指向自身存储的值(为一地址)。
6、寄存器eax中存储函数返回值。
赵豫峰 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-10000290