分析过程
1 使用gcc - g example.c -o example -m32指令在64位的机器上产生32位汇编,然后使用gdb example指令进入gdb调试器:
2 使用gdb调试代码。
3 使用break main指令在main函数处设置断点,然后调试,直到mian函数处。
4 使用diassemble指令获取汇编代码
5 依次如下指令调试汇编代码,并查看%esp、%ebp和堆栈内容:
(1)使用si指令单步跟踪一条机器指令
(2)使用i r指令查看各寄存器的值
(3)使用x/na %esp对应的值指令查看堆栈变化
6 call指令将下一条指令的地址入栈,此时%esp,%ebp和堆栈的值为:
7 将上一个函数的基址入栈,从当前%esp开始作为新基址,以及为传参做准备
8 实参的计算在%eax中进行:
9 f函数的汇编代码:
10 实参入栈:
11 call指令将下一条指令的地址入栈:
13 计算short+int:
14 pop %ebp指令将栈顶弹到%ebp中,同时%esp增加4字节:
15 ret指令将栈顶弹给%eip:
汇编代码及分析:
g:
pushl %ebp // 将%ebp入栈
movl %esp, %ebp // 将%ebp设置为目前这个程序的帧指针
movl 8(%ebp), %eax //把保存在%ebp+8处的值x传送给%eax
addl $3, %eax //执行语句x+3,将结果返回给%eax
popl %ebp //弹出%ebp的值
ret
f:
pushl %ebp //保存原来的%ebp(返回f的地址)
movl %esp, %ebp //将%ebp设置为目前这个程序的帧指针,值为原%esp的值减去4
subl $4, %esp //%esp向下移动4个单位,在栈上分配四个字节
movl 8(%ebp), %eax //将%ebp+8位置的值保存给%eax
movl %eax, (%esp) //将x的值赋值给x所指向的位置;
call g //调用g函数,将返回地址压入栈中,然后调到函数g的第一条指令
leave //为返回准备栈
ret
main: //主函数
pushl %ebp //保存原来的%ebp(返回main的地址)
movl %esp, %ebp //将%ebp设置为目前这个程序的帧指针,值为原%esp的值减去4
subl $4, %esp //%esp向下移动4个单位,在栈上分配四个字节
movl $8, (%esp) //把8赋值给%esp所指的位置
call f //调用f函数,将返回地址压入栈中,然后调到函数f的第一条指令
addl $1, %eax //执行表达式return f(8)+1,将%eax中的值加一再返回
leave
ret