汇编的递归过程
习惯写C程序后,一般很少会去敲汇编代码,最近在研究系统学习方面知识,需要用到汇编,考虑到编译器是如何将C程序编译成汇编代码的,这里对于C语言中的语句(赋值,运算,选择,循环)就不着重介绍,这些对应到汇编指令相对来说简单(当然忽略了编译器是如何把C语言编译成汇编代码的实现,只研究对应问题),今天主要分析C语言中函数在汇编中的实现,因为这里面涉及到栈的问题,对于汇编语言,指令完成命令无疑就是操作地址中的操作数,对于通用寄存器来说,地址是很好确定的,而对于栈的地址,我们必须清楚的知道我们的指令对栈的操作。所以,我针对C语言中的递归函数来分析汇编指令的实现过程,这不仅学习了汇编,同时也更让我清楚的了解了C语言递归函数的实质,函数调用之间的关系。
fibonacci数列
C语言实现:
#include <stdio.h>
int fu(int x)
{
if(x<1)
return 1;
else
return fu(x-2)+fu(x-1);
}
int main()
{
int x;
fu(x);
return 0;
}
机器反汇编代码:(每一步的执行都对应一条汇编指令,真正用汇编语言实现不用如此麻烦,因为,汇编语言也支持选择,循环,调用)
00401020 push %ebp
00401021 mov %esp,%ebp
00401023 push %ebx
00401024 sub $0x34,%esp
00401027 movl $0x401150,(%esp)
0040102E call 0x4019b4 <SetUnhandledExceptionFilter@4>
00401033 sub $0x4,%esp
00401036 call 0x401360 <__cpu_features_init>
0040103B call 0x401770 <fpreset>
00401040 lea -0x10(%ebp),%eax
00401043 movl $0x0,-0x10(%ebp)
0040104A mov %eax,0x10(%esp)
0040104E mov 0x402000,%eax
00401053 movl $0x404004,0x4(%esp)
0040105B movl $0x404000,(%esp)
00401062 mov %eax,0xc(%esp)
00401066 lea -0xc(%ebp),%eax
00401069 mov %eax,0x8(%esp)
0040106D call 0x40195c <__getmainargs>
00401072 mov 0x404008,%eax
00401077 test %eax,%eax
00401079 jne 0x4010c5 <__mingw_CRTStartup+165>
0040107B call 0x401964 <__p__fmode>
00401080 mov 0x402004,%edx
00401086 mov %edx,(%eax)
00401088 call 0x401520 <_pei386_runtime_relocator>
0040108D and $0xfffffff0,%esp
00401090 call 0x401750 <__main>
00401095 call 0x40196c <__p__environ>
0040109A mov (%eax),%eax
0040109C mov %eax,0x8(%esp)
004010A0 mov 0x404004,%eax
004010A5 mov %eax,0x4(%esp)
004010A9 mov 0x404000,%eax
004010AE mov %eax,(%esp)
004010B1 call 0x40134d <main>
004010B6 mov %eax,%ebx
004010B8 call 0x401974 <_cexit>
004010BD mov %ebx,(%esp)
004010C0 call 0x4019bc <ExitProcess@4>
004010C5 mov 0x4050f4,%ebx
004010CB mov %eax,0x402004
004010D0 mov %eax,0x4(%esp)
004010D4 mov 0x10(%ebx),%eax
004010D7 mov %eax,(%esp)
004010DA call 0x40197c <_setmode>
004010DF mov 0x404008,%eax
004010E4 mov %eax,0x4(%esp)
004010E8 mov 0x30(%ebx),%eax
004010EB mov %eax,(%esp)
004010EE call 0x40197c <_setmode>
004010F3 mov 0x404008,%eax
004010F8 mov %eax,0x4(%esp)
004010FC mov 0x50(%ebx),%eax
004010FF mov %eax,(%esp)
00401102 call 0x40197c <_setmode>
00401107 jmp 0x40107b <__mingw_CRTStartup+91>
0040110C lea 0x0(%esi,%eiz,1),%esi
汇编代码:
code segment
;主程序
main proc far
assume cs:code
start:
mov ax,0 ;ax存储函数值,清零
mov bx,8 ;设置函数x
push bx ;参数压栈
call digui ;ip压栈
ret
main endp
;递归子程序
digui proc near
push bp
mov bp,sp
push dx ;保存和的后一个加数
push bx
sub sp,2
mov bx,(bp+4)
cmp bx,2
je out1
cmp bx,1
je out1
sub bx,1
mov (bp-6),bx
call digui
mov dx,ax ;将和的第一个加数保存到dx,去计算后一个加数
dec bx ;求f(n-2)
push bx
call digui
pop bx ;清空n
add ax,dx ;将第二个加数加到第一个加数上,即求和
jmp out2 ;作为第二个加数的参数压栈
out1:
mov ax,1
out2:
add sp,2
pop bx
pop dx
pop bp
ret
digui endp
code ends
end main
栈的实现过程:
参数先给到bx,把bx压栈就是把参数压栈,假设参数为5
栈值 Sp 栈操作
n=4 fe Push bx
Ip Fc Call digui
Bp=0000 Fa Push bp
Dx=0 F8 Push dx
Bx=4 F6 Push bx
n=3 Sub sp,2 mov (bp-6),bx
Ip Call digui
Bp Push bp
Dx=0 Push dx
Bx=3 Push bx
N=2 Sub sp,2 mov (bp-6),bx
Ip Call digui
Bp=f2 Push bp
Bx=2 Push bx
N=1 Sub sp,2 mov (bp-6),bx
Ip Call digui
Bp Push bp
Dx Push bx
Bx=2 Sub sp,2 mov (bp-6),bx
Mov bx,(bp+4) bx=2
Cmp bx,2 je out1 mov ax,1 Ax=1