如下,一个简单的程序
代码语言:javascript复制 1 #include <stdio.h>
2 int add(int a, int b)
3 {
4 return a b;
5 }
6
7 void main()
8 {
9 int a = 1, b = 2;
10 int result;
11 result = add(a, b);
12 printf("%d",result);
13 }
执行反汇编指令:gcc -g test.c objdump -S
得到x86机器的汇编代码(除去一些初始化的代码)如下:
在分析上面的汇编程序之前,需要了解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汇编指令和寄存器名称。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