陈琛 + 原创作品转载请注明出处 +《Linux内核分析》MOOC课程
1.在Linux环境下编译如下程序
int g(int x)
{
return x + 4;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(5) + 1;
}
2.编译出汇编代码
gcc –S –o main.s main.c -m32
编译出来,经过精简的汇编代码如下:
3.汇编代码分析
- (1)main
1)栈初始化示意图:
2)
pushl %ebp ;将ebp压入栈顶
movl %esp, %ebp ;以上两条代码,构建一个新的函数栈环境
;执行完效果如下图所示
3)
subl $4, %esp ;栈顶esp指针减4
movl $5, (%esp) ;立即数5放入esp指向的位置
;执行完以上两条指令效果如下图所示
1)
call f ;调用f函数,相当于
;pushl eip 把eip压入栈(保存返回的地址),此时eip指向call的下一条指令(addl $1 %eax)
;movl f eip f函数的地址放入eip
;栈示意图如下
- (2)f函数执行过程分析
1)
pushl %ebp
movl %esp, %ebp ;同理,构造f函数栈环境
;执行完效果如下图
2)
subl $4, %esp
movl 8(%ebp), %eax ;ebp+8(变址寻址存储立即数5的地址),然后把5送入到eax寄存器
movl %eax, (%esp) ;5送入到esp指向的位置
;参数传递,执行完效果如下图
3)
call g ;调用g函数
;pushl eip eip目前指向代码15行代码
;movl g eip eip指向g函数的位置
- (3)g函数执行过程分析
1) 构造g函数栈环境
pushl %ebp
movl %esp, %ebp
2)
movl 8(%ebp), eax ;ebp变址寻址,将5放入eax
addl $4, eax ;立即数3与eax内容相加得到9,放入eax
3)
popl %ebp ;栈顶出栈,存入ebp寄存器
;目前栈顶为地址ebp15,也就是让ebp指向地址为15的位置
4)
ret ;相当于popl %eip, 所以eip现在指向代码15行
;即回到调用者f函数的leave指令
- (4)g函数返回到f函数
1) leave
leave ;movl %ebp, %esp
;popl %ebp
2) ret
ret ; popl %eip
;eip指向代码23行,返回到主函数call f指令后一条指令
- (4)f函数返回到main函数
1)
addl $1, %eax ; eax寄存器内容与立即数1相加,结果放在eax中
2)
leave ;movl %ebp, %esp
;popl %ebp
3)
ret ;
4. 重点记录
(1) leave指令
(2) ret指令
(3) eip寄存器的作用
(4) eax寄存器的作用
(5) popl, pushl 指令以及相关的寄存器ebp, esp
5.心得体会
通过分析这一段汇编程序,对函数调用过程以及参数传递理解更加清晰。