分析如下的文件:(文件名是hi.c)
int g(int x, int y)
{
return x + y + 3;
}
int f(int x, int y)
{
return g(x,y);
}
int main(void)
{
return f(3,4) + 1;
}
通过反汇编后得到如下的内容:
使用反汇编的命令:gcc -S hi.c -m32
生成如下代码:
.file "hi.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 12(%ebp), %eax
movl 8(%ebp), %edx
addl %edx, %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 $8, %esp
movl 12(%ebp), %eax
movl %eax, 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 $8, %esp
movl $4, 4(%esp)
movl $3, (%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.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
在这个代码中,核心的汇编代码与前面带有'.'符号的语句是无关的所有去掉带有'.'符号的语句就得到了如下更好分析的语句:
g:
pushl %ebp
movl %esp, %ebp
movl 12(%ebp), %eax
movl 8(%ebp), %edx
addl %edx, %eax
addl $3, %eax
popl %ebp
ret
f:
pushl %ebp
movl %esp, %ebp
subl $8, %esp ;栈中申请8个字节
movl 12(%ebp), %eax ;相当于4;中间经过了 栈如下: ebp|eip|3|4:
movl %eax, 4(%esp) ;把4入栈
movl 8(%ebp), %eax ;相当于3赋值给eax;
movl %eax, (%esp) ;
call g ;调用g
leave
ret
main:
pushl %ebp ;ebp入栈
movl %esp, %ebp ;将esp的值赋值给ebp
subl $8, %esp ;将esp的值减8
movl $4, 4(%esp);相当与esp+4的位置的内存值赋值为4;我们看到函数调用的时候4在3的右边所以是从右到左入栈
movl $3, (%esp) ;把3入栈 [这两句相当于pushl $4;push $3]
call f ;调用函数f:call会将eip入栈
addl $1, %eax ;将返回值与函数的返回值加一
leave ;相当于;movl %ebp, %esp; popl %ebp
ret ;返回给loader
分析:
1:咱们c语言的入口函数是main;
2:函数之间的调用遵从c语言的调用约定;
a)调用参数从右到左入栈
b)调用者负责清理堆栈
c)eax是函数的返回值
燕涛: 原创作品转载请注明出处 :《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000