以下介绍arm 对数据表的访问和指令表的访问
数据表:ldr r1,[r5,r4,lsl #2] //索引寻址(取内存)
指令表:addls pc,pc,r0,lsl #2 //相对寻址(计算数据)
arm 对数据表的访问:
1. c 程序。
#include <stdio.h>
int a[5]={
1,2,3,4,5
};
int main(void)
{
int i;
for(i=0; i<5; i++) ?
{
printf("data is %d/n",a[i]);
}
return 0;
}
2. 汇编程序
main [0xe92d4038] * stmfd r13!,{r3-r5,r14}
000080ac [0xe59f5020] ldr r5,0x000080d4 ; = #0x0000a630 // ldr 间址寻址
000080b0 [0xe3a04000] mov r4,#0
000080b4 [0xe7951104] ldr r1,[r5,r4,lsl #2] // 从表中取到数据
000080b8 [0xe28f0018] add r0,pc,#0x18 ; #0x80d8 // 寻址常量字符串
000080bc [0xeb00000a] bl _printf // 调用printf函数
000080c0 [0xe2844001] add r4,r4,#1 // 循环次数加1
000080c4 [0xe3540005] cmp r4,#5 // 小于5次,循环
000080c8 [0xbafffff9] blt 0x80b4 ; (main + 0xc)
000080cc [0xe3a00000] mov r0,#0 //return 0
000080d0 [0xe8bd8038] ldmfd r13!,{r3-r5,pc}
000080d4 [0x0000a630] dcd 0x0000a630 0... // 存储地址,相当于指针
000080d8 [0x61746164] dcd 0x61746164 data
000080dc [0x20736920] dcd 0x20736920 is
000080e0 [0x000a6425] dcd 0x000a6425 %d..
。。。。。。
0000a630 [0x00000001] dcd 0x00000001 ....
0000a634 [0x00000002] dcd 0x00000002 ....
0000a638 [0x00000003] dcd 0x00000003 ....
0000a63c [0x00000004] dcd 0x00000004 ....
0000a640 [0x00000005] dcd 0x00000005 ....
简洁呀,arm 汇编!
从表中取到数据,当然是基址加变址,但是,这里把基址送给一个寄存器,变址用两外一个寄存器加简单运算。
ldr r1,[r5,r4,lsl #2]
函数调用用寄存器传递参数。
函数返回(弹栈),压栈时保留了link寄存器,弹栈时返回给了pc 寄存器。
; ---------------------------------------------------------------------------
我们再看一下arm 的switch-case 跳转表。 发现其跳转表是由跳转指令组成,是一个跳转指令表
散转到各函数处,只有设置好PC 指针就可以了。
#include <stdio.h>
int a[5]={
1,2,3,4,5
};
void test_switch(int i);
int main(void)
{
[0xe92d4008] * stmfd r13!,{r3,r14}
test_switch(3); I
[0xe3a00003] mov r0,#3
[0xebffffdf] bl test_switch
return 0; ;
[0xe3a00000] mov r0,#0
}
[0xe8bd8008] ldmfd r13!,{r3,pc}
void test_switch(int i)
{
switch(i) { 4
[0xe3500004] cmp r0,#4
[0x908ff100] addls pc,pc,r0,lsl #2 // 相对寻址。r0 是变量。更改pc 值
[0xea00000e] b 0x80f0 ; (test_switch + 0x48) // 跳转指令表。
[0xea000003] b 0x80c8 ; (test_switch + 0x20)
[0xea000004] b 0x80d0 ; (test_switch + 0x28)
[0xea000005] b 0x80d8 ; (test_switch + 0x30)
[0xea000006] b 0x80e0 ; (test_switch + 0x38)
[0xea000007] b 0x80e8 ; (test_switch + 0x40)
case 0:
printf("it's 0/n"); l u
[0xe28f0024] add r0,pc,#0x24 ; #0x80f4
[0xea000019] b _printf // 这家伙很懒,连break也不翻译了,而是借用了printf 的返回指令。
break; // 因为编译器发现正常是一个两级返回,所以把它优化为一级返回。
case 1:
printf("it's 1/n"); / C
[0xe28f0024] add r0,pc,#0x24 ; #0x80fc
[0xea000017] b _printf
break;
case 2:
printf("it's 2/n");
[0xe28f0024] add r0,pc,#0x24 ; #0x8104
[0xea000015] b _printf
break;
case 3:
printf("it's 3/n");
[0xe28f0024] add r0,pc,#0x24 ; #0x810c
[0xea000013] b _printf
break;
case 4:
printf("it's 4/n");
[0xe28f0024] add r0,pc,#0x24 ; #0x8114
[0xea000011] b _printf
break;
default:
;
}
}
[0xe1a0f00e] mov pc,r14 // 函数返回
[0x73277469] dcd 0x73277469 it's // 常数表。
[0x000a3020] dcd 0x000a3020 0..
[0x73277469] dcd 0x73277469 it's
[0x000a3120] dcd 0x000a3120 1..
[0x73277469] dcd 0x73277469 it's
[0x000a3220] dcd 0x000a3220 2..
[0x73277469] dcd 0x73277469 it's
[0x000a3320] dcd 0x000a3320 3..
[0x73277469] dcd 0x73277469 it's
[0x000a3420] dcd 0x000a3420 4..