作者 云青
原创作品转载请注明出处
基本的汇编知识:
movl,subl,pushl,topl,ret,addl,leave,enter
参考资料下载地址。http://pan.baidu.com/s/1cdISDC
课程地址:《Linux内核分析》MOOC课程http://mooc.study.163.com/cou...
一、准备
创建文件,并进行编译
int g(int x)
{
return x + 3;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(8) + 1;
}
编译命令:
gcc -S -o demo1.s demo1.c -m32
运行完毕后生成如下文件:
.file "demo1.c"
.text
.globl g
.type g, @function
g:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
movl 8(%ebp), %eax
addl $3, %eax
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size g, .-g
.globl f
.type f, @function
f:
.LFB1:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE1:
.size f, .-f
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $4, %esp
movl $8, (%esp)
call f
addl $1, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
.section .note.GNU-stack,"",@progbits
我们将命令简化得到如下汇编指令:
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $3, %eax
popl %ebp
ret
f:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
ret
main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $8, (%esp)
call f
addl $1, %eax
leave
ret
备注:
%开头表示寄存器
$开头表示立即数
()表示间接寻址,样例(GAS = C语言):(%eax)= *eax [[2]](http://www.cnblogs.com/lxgeek...
Imm(Ea) ,变址寻址,样例(GAS = C语言):4(%eax) = *(4+eax)
二、汇编指令执行流程分析
我们以简化版为例:
C语言的都是从mian函数开始运行的,同理,汇编也是从main函数开始运行的,下面我们来从main函数入手。
首先执行的是18行pushl指令,
先画出内存的栈此时的情况:esp和ebp此时都指向栈底。
pushl %ebx ,即将数据压栈,指令相当于
subl $4, %esp
movl %ebx,(%esp)
- 执行pushl %ebp之后,eip指向下一条指令,变为:
2、执行movl %esp,%ebp,之后eip指向下一条指令,变为
3、执行 subl $4, %esp
4、执行movl $8, (%esp)
5 、call f
call f等价于
pushl %eip
movl f %eip
6 、pushl %ebp
7、movl %esp, %ebp
8、 subl $4, %esp
9、movl 8(%ebp), %eax
10、movl %eax, (%esp)
11、call g
12、
pushl %ebp
movl %esp,%ebp
每进入一个函数都会执行这两个汇编指令
这两步操作是个规范化步骤, 叫做前序(prologue) [1]。
执行函数时,汇编会生成一个堆栈调用框架:
具体如下:
//建立被调用者函数的堆栈框架
pushl %ebp
movl %esp,%ebp
//被调用者函数体
//do something
//拆除被调用者的函数的堆栈框架
movl %ebp,%esp
popl %ebp
ret
13、 movl 8(%ebp), %eax
14、addl $3,%eax
15、popl %ebp
popl %ebp 相当于
movl (%esp),%ebp
addl $4,%esb
16、ret
等价于 popl %eip
17、leave 等价于
movl %ebp,%esp
popl %ebp
18、ret
19、addl $1,%eax
21、ret
main函数运行结束,将%eax的值返回,即12
参考文章:
[1] X86汇编调用框架浅析与CFI简介
[2] http://www.cnblogs.com/lxgeek...