通过反汇编理解函数调用机制(x86和ARM)

时间:2023-03-09 17:31:12
通过反汇编理解函数调用机制(x86和ARM)

如下,一个简单的程序

 #include <stdio.h>
int add(int a, int b)
{
return a + b;
} void main()
{
int a = , b = ;
int result;
result = add(a, b);
printf("%d",result);
}

执行反汇编指令:gcc -g test.c
objdump -S

得到x86机器的汇编代码(除去一些初始化的代码)如下:

通过反汇编理解函数调用机制(x86和ARM)

通过反汇编理解函数调用机制(x86和ARM)

在分析上面的汇编程序之前,需要了解rbp、rsp为栈基址寄存器、栈顶寄存器,分别指向栈底和栈顶;edx、eax、esi、edi均为x86CPU上的通用寄存器,可以存放数据(虽然它们还有别的作用,但是本文章不涉及)

x86下栈生长是从高地址往低地址,即push操作一次,rsp减少4个字节,pop操作一次,rsp增加4个字节。

对上面汇编代码的分析:

进入main函数,保护现场,将rbp压入堆栈;

然后为main函数开拓新的堆栈框架,rbp与当前rsp相同,rsp再向上扩充16个字节(0x10);(以前的C程序只能在函数前面声明变量,是因为编译器还么有那么“智能”,它只能通过分析前部分的变量,一次性的为程序扩充堆栈)

然后向栈底上方的偏移地址为8和12的单元存入数据1和2;

把数据送入通用寄存器中,以供新的函数调用;

跳转到add;

再次将main的rbp压栈,保护;

新的rbp与当前rsp相同,把通用寄存器中的数据赋给栈底上方偏移地址为4和8的单元(此为函数参数传递的关键);

将传入新栈的参数赋给通用寄存器,进行加法操作,结果存入eax;

pop出rbp,回到main函数;

将eax中的运算结果赋给栈底上方偏移地址为4的单元;

然后调用printf函数显示结果。

使用arm-linux-gcc编译并反汇编:arm-linux-objdump -D -m arm a.out

得到arm机器的汇编代码(除去一些初始化的代码)如下:

通过反汇编理解函数调用机制(x86和ARM)

通过反汇编理解函数调用机制(x86和ARM)

这段代码的解析与x86类似,只不过需要了解几个arm汇编指令和寄存器名称。fp为帧寄存器,起“标签”作用。lr是连接寄存器,在ARM体系结构中lr的用途有两种:一是用来保存子程序返回地址;二是当异常发生时,lr保存的值等于异常发生时PC的值减4(或者减2),因此在各种异常模式下可以返回到异常发生前的相应位置继续执行。bx lr即跳转到lr存放的地址处。sp为栈顶指针。str 源寄存器 存储地址,即将源存储器数据送到存储器中,ldr为其逆操作。

ARM为堆栈提供硬件支持,它有一个专门的寄存器sp指向栈顶,ARM支持四种堆栈工作方式,最常用的也是和x86类似,即从高地址向低地址生长。

参考资料:http://mooc.study.163.com/course/USTC-1000029000#/info