秣陵+ 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
//文件名 main.c
int g(int x)
{
return x+12;
}
int f(int x)
{
return g(x)
}
int main()
return f(21)+36
汇编命令
gcc -S -o main.s main.c -m32 // m32为编译为32位的选项
用VIM打开main.s删除所有以点开头的行
:g/^\s\?\.\+/d
//得到汇编代码如下
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $12, %eax
popl %ebp
ret
f:
pushl %ebp
movl %esp, %ebp
pushl 8(%ebp)
call g
addl $4, %esp
leave
ret
main:
pushl %ebp
movl %esp, %ebp
pushl $21
call f
addl $4, %esp
addl $36, %eax
leave
ret
汇编码解读
程序从main开始,假设此时 ebp=esp=64 内存[]
main:
pushl %ebp
movl %esp, %ebp
建立一个新的堆栈a,新栈基址为60
ebp=60,esp=60
[64]
pushl $21
将21入栈
ebp=60,esp=56
[64,21]
call f
将eip入栈,把f传入eip
ebp=60,esp=52
[64,21,main_call_next]
f:
pushl %ebp
movl %esp, %ebp
在堆栈a上建立新栈b,基址为48
esp=48,ebp=48
[64,21,main_call_next,60]
pushl 8(%ebp)
将(ebp+8)处的值压栈即将21压栈,esp为44
ebp=48,esp=44
[64,21,main_call_next,60,21]
call g
将eip入栈,把g传入eip
ebp=48,esp=40
[64,21,main_call_next,60,21,f_call_next]
g:
pushl %ebp
movl %esp, %ebp
在建立新栈c,基址为36
esp=36,ebp=36
[64,21,main_call_next,60,21,f_call_next,48]
movl 8(%ebp), %eax
将(ebp+8)处的值传入eax,即eax=21
esp=36,ebp=36
[64,21,main_call_next,60,21,f_call_next,48]
addl $12, %eax
将eax中的值与12相加并保存到eax中,即eax=21+12 eax=33
esp=36,ebp=36
[64,21,main_call_next,60,21,f_call_next,48]
popl %ebp
对堆栈C进行出栈操作
ebp=48,esp=40
[64,21,main_call_next,60,21,f_call_next]
ret
即:
popl %eip 对堆栈b进行出栈操作,出栈到eip,
ebp=48,esp=44
[64,21,main_call_next,60,21]
addl $4, %esp
ebp=48,esp=48
[64,21,main_call_next,60]
leave
相当于:
movl %ebp,%esp
popl %ebp
epb=60,esp=52
[64,21,main_call_next]
ret
即:
popl %eip 对堆栈a进行出栈操作,出栈到eip,
epb=60, esp=56
[64,21]
addl $4, %esp
esp=60,esp=60
[64]
addl $36, %eax
eax = 36 + 33 = 69
esp=60,esp=60
[64]
leave
相当于:
movl %ebp,%esp
popl %ebp
ebp=64,esp=60
[]
ret
即:
popl %eip 对堆栈a进行出栈操作,出栈到eip
ebp=64,esp=64
[]
内存示意图
总结
程序按总是顺序执行,当遇到跳转时,会保存当前的地址,跳转后总是先新建一个堆栈以免破坏之前的堆栈防止丢失回去的路。