研究目的:了解汇编如何实现C语言中的函数调用
编译器:TDM-GCC 4.9.2 64-bit Release
反汇编:Dev-C++ 5.11
C语言代码:
该函数调用了一个简单的add函数,add函数将输入的两个参数值相加,并返回两参数之和。
main函数:
1、2行:rbp的作用是标记基址,在一个函数的执行过程中是固定不变的,因此可借助基址与偏移量来访问栈内数据。rsp的作用是标识当前栈所占空间。main函数实际上也是被调用的一方,即在第一行代码执行前,当前rsp与rbp之间的数据为调用方数据,需要进行保护。保护方式为将调用函数的rbp保存进栈,以备时候恢复现场。然后让rbp指向rsp,以便准备使用新的栈空间。我们课本上提到x86中使用32bit的esp(extended stack pointer)和ebp(extended base pointer)作为栈指针寄存器与基址指针寄存器。然而我们的编译器是x64的,因此使用的是64bit的rbp与rsp。
3行:rsp下移0x30,这相当于开辟了一片空间以便存储临时变量
4、5行:对rbp以上的地址进行操作,那部分是调用函数的变量区,将ecx、rdx保存到那里
6行:不知用途
7、8行:对变量进行初始化,一个int变量为4个字节,即DOUBLE WORD。
9、10行:两变量由内存移入寄存器edx,eax
11行:eax存入ecx。至此两变量已分别存到了ecx与edx。借助寄存器我们可以实现函数间参数的传递。
12行:调用add函数
13行:返回值被存在eax中,因此需要把它取回赋给区域[rbp-0xc],即变量c
14行:eax用来存储返回值,因此该行对应return 0语句
15行:将rsp移回到rbp处,以此释放内存。这意味着堆栈空间的释放不代表数据被清除。只有数据被覆盖才是真正的消失
16行:将rbp恢复到调用函数前的位置
17行:结束程序,返回操作系统
add函数:
1、2、3行:类似main中的操作,开辟自己的堆栈空间。
4、5行:ecx、edx寄存器中存储了调用函数传递的参数,把它移到堆栈区以便进行各类运算。由于是调用函数的变量,把它存在rbp上方空余的空间中。
6、7行:重新把变量从堆栈区取到寄存器。虽然这样取来取去的过程显得很傻,但当这个函数变得复杂时,这样的操作是相当有必要的。在寄存器中运算使得运算速度加快,并且大量运算时不需要来回地取数据。
8行:计算a+b
9行:把计算结果存到局部堆栈区中
10行:将返回值写到eax
11、12、13行:类似main中那样,清空堆栈区后返回。