口号:为了熟悉arm指令而奋斗!

for

C code

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>
int test_for()
{
int i,sum = 0;
for(i = 0; i < 100; i++)
sum += i;
return sum;
}
int main(int argc, char* argv[])
{
printf("sum:%d\n",test_for());
}

arm code

main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
00008370 <main>:
8370: e92d4800 push {fp, lr}
8374: e28db004 add fp, sp, #4
8378: e24dd008 sub sp, sp, #8
837c: e50b0008 str r0, [fp, #-8]
8380: e50b100c str r1, [fp, #-12]
8384: ebffffe2 bl 8314 <test_for>
8388: e1a02000 mov r2, r0
838c: e59f3018 ldr r3, [pc, #24] ; 83ac <main+0x3c>
8390: e08f3003 add r3, pc, r3
8394: e1a00003 mov r0, r3
8398: e1a01002 mov r1, r2
839c: ebffffad bl 8258 <printf@plt>
83a0: e1a00003 mov r0, r3
83a4: e24bd004 sub sp, fp, #4
83a8: e8bd8800 pop {fp, pc}
83ac: 00000030 .word 0x00000030

push {fp, lr}

{}: 其中包含多个值;
fp(frame pointer): 帧指针,每个栈帧由fp和sp界定,fp相当于x86中的ebp,sp相当于x86中的esp;
lr(link register): 链接寄存器,在程序中调用其他函数时,存放子程序的返回地址(即调用指令的下一条指令);

本指令相当于stmfd sp!,{fp,lr}(!表示更新寄存器的值),将调用者的fp和lr寄存的值压入堆栈(顺序:编号小的寄存器的值存入低地址,编号大的寄存器的值存入高地址),
保护调用者程序的环境,子程序执行完毕后恢复环境.

add fp, sp, #4

将被调用者的fp(栈底)指向存放调用者fp的地址.

sub sp, sp, #8

抬高栈顶指针,开辟栈空间(8字节).

str r0, [fp, #-8]

将r0的值(参数1)存储到fp-8所在的地址.

str r1, [fp, #-12]

将r1的值(参数2)存储到fp-12所在的地址.
现在子程序的栈结构如下:
高地址 -> 低地址
返回地址(fp) 参数1 参数2(sp)

bl 8314

调用8314处的test_for函数.bl指令同时会将返回地址存储在r14(lr)中.

mov r2, r0

r0存储的是test_for的返回值,赋给r2.

ldr r3, [pc, #24] ; 83ac

加载pc+24地址处的一个字的数据(.word 0x00000030)放入r3.r3=0x30.

add r3, pc, r3

r3+=pc,即取”sum:%d”字符串的地址.(PC的值为当前执行指令地址+8字节)

mov r0, r3

r0为第一个参数(format字符串).

mov r1, r2

r2为第二个参数(test_for返回值).(当参数少于4个时保存在通用寄存器中,多余4个的保存在栈中)

bl 8258 printf@plt

调用printf函数.

sub sp, fp, #4

与开辟栈空间相对应.

pop {fp, pc}

将栈顶数据分别弹出至fp,pc,恢复调用者程序的环境(顺序:低地址的值放入编号小的寄存器,高地址的值放入编号大的寄存器).

test_for

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
00008314 <test_for>:
8314: e52db004 push {fp} ; (str fp, [sp, #-4]!) @没有用到lr寄存器,无需保存
8318: e28db000 add fp, sp, #0
831c: e24dd00c sub sp, sp, #12 @开辟栈空间
8320: e3a03000 mov r3, #0
8324: e50b300c str r3, [fp, #-12] @sum
8328: e3a03000 mov r3, #0
832c: e50b3008 str r3, [fp, #-8] @i
8330: ea000006 b 8350 <test_for+0x3c> @b指令直接按地址跳转,不保存返回地址
8334: e51b200c ldr r2, [fp, #-12]
8338: e51b3008 ldr r3, [fp, #-8]
833c: e0823003 add r3, r2, r3 @取i和sum相加
8340: e50b300c str r3, [fp, #-12]
8344: e51b3008 ldr r3, [fp, #-8] @取i值加1
8348: e2833001 add r3, r3, #1
834c: e50b3008 str r3, [fp, #-8]
8350: e51b3008 ldr r3, [fp, #-8] @取i值
8354: e3530063 cmp r3, #99 ; 0x63
8358: dafffff5 ble 8334 <test_for+0x20> @小于等于99继续
835c: e51b300c ldr r3, [fp, #-12]
8360: e1a00003 mov r0, r3 @sum值放入r0返回
8364: e24bd000 sub sp, fp, #0
8368: e49db004 pop {fp} ; (ldr fp, [sp], #4)
836c: e12fff1e bx lr @转到返回地址

switch

c code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
int global_v = 5;
int test_switch()
{
switch(global_v){
case 1: printf("1");
case 5: printf("5");
case 7: printf("7");
default: printf("fail");
}
}
int main(int argc, char* argv[])
{
test_switch();
}

arm code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
00008344 <test_switch>:
8344: e92d4800 push {fp, lr} @保存环境
8348: e28db004 add fp, sp, #4 @调整当前栈帧的栈底指针
834c: e59f2054 ldr r2, [pc, #84] ; 83a8 <test_switch+0x64>
8350: e08f2002 add r2, pc, r2
8354: e59f3050 ldr r3, [pc, #80] ; 83ac <test_switch+0x68> @这里可见全局变量是通过地址存取的
8358: e7923003 ldr r3, [r2, r3]
835c: e5933000 ldr r3, [r3]
8360: e3530005 cmp r3, #5 @由此可见采用了二分查找
8364: 0a000006 beq 8384 <test_switch+0x40>
8368: e3530007 cmp r3, #7
836c: 0a000007 beq 8390 <test_switch+0x4c>
8370: e3530001 cmp r3, #1
8374: 1a000008 bne 839c <test_switch+0x58>
8378: e3a00031 mov r0, #49 ; 0x31
837c: ebffffbe bl 827c <putchar@plt> @单个字符的printf变成了putchar
8380: ea000009 b 83ac <test_switch+0x68>
8384: e3a00035 mov r0, #53 ; 0x35
8388: ebffffbb bl 827c <putchar@plt>
838c: ea000006 b 83ac <test_switch+0x68> @返回
8390: e3a00037 mov r0, #55 ; 0x37
8394: ebffffb8 bl 827c <putchar@plt>
8398: ea000003 b 83ac <test_switch+0x68>
839c: e59f3018 ldr r3, [pc, #24] ; 83bc <test_switch+0x78>
83a0: e08f3003 add r3, pc, r3
83a4: e1a00003 mov r0, r3
83a8: ebffffb6 bl 8288 <printf@plt>
83ac: e1a00003 mov r0, r3 @返回值
83b0: e8bd8800 pop {fp, pc} @恢复环境
83b4: 00001c8c .word 0x00001c8c
83b8: fffffffc .word 0xfffffffc
83bc: 00000054 .word 0x00000054