我们有必要了解我们手中的板子到底最根本的机制——汇编语言。树莓派是建立在ARM指令的基础上的。接下来的几篇文章并不试图从机器码的角度来详细分析ARM汇编的详细原理,而是通过实际的通过C语言反汇编的分析以及自行编写简单的汇编指令,来对ARM指令有一些基础的了解。
这里我们假设读者已经有了MIPS汇编基础或者Intel处理器汇编的基础。关于基本的运算指令、存储/读取指令、转移指令不会详细介绍。不过下面部分代码还是给出了部分汇编代码的注释,以供理解。
器材
硬件
- 实验主板一块,本试验使用树莓派
- 5V/1A电源一个
- microUSB线一根
自备器材
- PC(Linux)一台;
- USB无线网卡;
软件
- 交叉编译工具链。
步骤
1. 交叉编译之生成Thumb指令&ARM指令
Thumb是ARM体系结构中一种16位的指令集。Thumb指令集可以看作是ARM指令压缩形式的子集,它是为减小代码量而提出,具有16bit的代码密度。
Thumb指令体系并不完整,只支持通用功能,必要时仍需要使用ARM指令,如进入异常时。其指令的格式与使用方式与ARM指令集类似,而且使用并不频繁,Thumb指令集作一般了解。
这一步我们通过编译参数改变,相同的程序,ARM和Thumb编译的结果有何不同, 包括比较指令本身和整体目标代码的大小等。首先我们写一段用于交叉编译的代码:
#include <stdio.h>
int main(){
int t = 72;
printf("%d Hello world!\n",t);
}
然后复制为arm.c以及thumb.c来分别进行编译,首先对arm.c编译为ARM指令,使用指令:arm-bcm2708hardfp-linux-gnueabi-gcc -c arm.c
对thumb.c进行编译,这时我们需要设置对应的参数,使用命令arm-bcm2708hardfp-linux-gnueabi-gcc -c -mthumb -msoft-float thumb.c
此时使用交叉编译工具链中的objdump工具来进行反汇编,查看我们获得的对应的.o文件中的指令内容,使用指令分别查看,结果截图如下:
可以看到明显ARM指令是32位指令,编码为8位十六进制,而Thumb指令则是16位指令,从指令条数来看Thumb是要比ARM要复杂,但是从整体的文件大小来看,是Thumb要比较小:
2. ARM 指令中的条件执行指令
下面我们来对指令中的条件执行指令,查看具体翻译之后的结果观察是否出现我们想要看到的指令。首先编写一段具有条件执行指令的代码:
#include <stdio.h>
int main(){
int a=72,b=15;
int res;
if(a>b)
res = a-b;
else
res = b-a;
}
编译后反汇编得到代码如下图所示:
此时观察到ble指令出现,整个流程的注释可以如下所示:
00000000 <main>:
0: e52db004 push {fp} ;(str fp, [sp, #-4]!) 保护fp帧指针
4: e28db000 add fp, sp, #0 ;将sp的值赋给fp,保护sp指针值
8: e24dd014 sub sp, sp, #20 ;栈增长5个字节
c: e3a03048 mov r3, #72 ;0x48,将r3寄存器赋值为72
10: e50b3008 str r3, [fp, #-8] ;将r3的值(72)存到fp-8
14: e3a0300f mov r3, #15 ;将r3寄存器赋值位15
18: e50b300c str r3, [fp, #-12] ;存入fp-12
1c: e51b2008 ldr r2, [fp, #-8] ;将fp-8的值载入r2=72
20: e51b300c ldr r3, [fp, #-12] ;r3=15
24: e1520003 cmp r2, r3 ;比较r2和r3的值的大小
28: da000004 ble 40 <main+0x40> ;如果结果为小于则跳转到main+0x40的地址
2c: e51b2008 ldr r2, [fp, #-8]
30: e51b300c ldr r3, [fp, #-12]
34: e0633002 rsb r3, r3, r2
38: e50b3010 str r3, [fp, #-16]
3c: ea000003 b 50 <main+0x50> ;直接跳转到main+0x50地址
40: e51b200c ldr r2, [fp, #-12]
44: e51b3008 ldr r3, [fp, #-8]
48: e0633002 rsb r3, r3, r2
4c: e50b3010 str r3, [fp, #-16]
50: e1a00003 mov r0, r3
54: e28bd000 add sp, fp, #0
58: e8bd0800 pop {fp} ;恢复fp指针
5c: e12fff1e bx lr ;返回倒link register 中的值
3. C 代码场景下的寄存器观察
(1)观察是否产生了寄存器移位寻址:
编写测试代码如下所示:
#include <stdio.h>
int main() {
int t[10];
int i = 0;
int j = 0;
for(j=0; j<4; j++) {
t[8] = t[i*2];
}
printf("%d", t[8]);
return 0;
}
汇编代码如下所示: