今天汇编语言学到了 call指令和ret 指令,然后解决了一直以来c/c++(高级语言)是如何执行递归的过程。

首先我们来看一个简单的样例:

C/C++ 程序

#include<iostream>
using namespace std;
void clac(int a,int b)
{
int ans=a+b;
cout<<a+b<<endl;
return;
}
int main() {
clac(2,3);
return 0;
}

反汇编程序

Dump of assembler code for function main:
0x0000000000401595 <+0>: push %rbp
0x0000000000401596 <+1>: mov %rsp,%rbp
0x0000000000401599 <+4>: sub $0x20,%rsp
0x000000000040159d <+8>: callq 0x401720 <__main>
0x00000000004015a2 <+13>: mov $0x3,%edx
0x00000000004015a7 <+18>: mov $0x2,%ecx
0x00000000004015ac <+23>: callq 0x401550 <_Z4clacii>
0x00000000004015b1 <+28>: mov $0x0,%eax
0x00000000004015b6 <+33>: add $0x20,%rsp
0x00000000004015ba <+37>: pop %rbp
0x00000000004015bb <+38>: retq
End of assembler dump.

我们把每个语句的对应写下来

0x000000000040159d <+8>:     callq  0x401720 <__main>  :int main

0x00000000004015a2 <+13>: mov $0x3,%edx
0x00000000004015a7 <+18>: mov $0x2,%ecx
0x00000000004015ac <+23>: callq 0x401550 <_Z4clacii> : calc(2,3)
0x00000000004015b1 <+28>: mov $0x0,%eax
0x00000000004015b6 <+33>: add $0x20,%rsp
0x00000000004015ba <+37>: pop %rbp
0x00000000004015bb <+38>:

我们现在只研究 calc(2,3) 函数的执行过程,首先 传入参数,汇编语言是把0x3,0x2这两个十六进制,存入dx寄存器,cx寄存器,然后callq 0x401550 <_Z4clacii> 这句的功能是 push 下一句的ip(假设x)入栈,jmp到 clac()函数的 ip(假设y) 地址,接下来执行clac()的函数的执行过程, ​​mov $0x0,%eax,add $0x20,%rsp, pop %rbp,retq​​中间的加法步骤我们不用管,最后 一句retq,这句是修改当前的ip,当前的ip数据从栈中获取,修改后ip=x,此时我们cs:ip地址是clac()函数地址的下一条地址,此时函数调用完成工作。

心得体会:

由上方汇编语言的函数调用过程可以理解cpu的函数调用过程是如何实现的,从而可以理解递归的过程是如何实现的,不过是call 和 ret 的配合下不断的跳转,cs:ip,在跳转的过程中逐渐满足条件,让栈中的数据逐渐清空。