您的位置:首页 > 其它

RPi——ARM指令验证

2016-04-05 21:14 330 查看

实验目的

1   深入理解ARM指令和Thumb指令的区别和编译选项;
2   深入理解某些特殊的ARM指令,理解如何编写C代码来得到这些指令;
3   深入理解ARM的BL指令和C函数的堆栈保护;
4   深入理解如何实现C和汇编函数的互相调用。


实验内容

使用交叉编译工具或本机编译工具,通过C代码和反汇编工具研究:
1、生成了Thumb指令还是ARM指令:如何通过编译参数改变,相同的程序,ARM和Thumb编译的结果有何不同,如指令本身和整体目标代码的大小等;
2、对于ARM指令,能否产生条件执行的指令;
3、设计C的代码场景,观察是否产生了寄存器移位寻址;
4、设计C的代码场景,观察一个复杂的32位数是如何装载到寄存器的;
5、写一个C的多重函数调用的程序,观察和分析:a.调用时的返回地址在哪里?b.传入的参数在哪里?c.本地变量的堆栈分配是如何做的?d.寄存器是caller保存还是callee保存?是全体保存还是部分保存?
6、MLA是带累加的乘法,尝试要如何写C的表达式能编译得到MLA指令。
7、BIC是对某一个比特清零的指令,尝试要如何写C的表达式能编译得到BIC指令。
8、编写一个汇编函数,接受一个整数和一个指针做为输入,指针所指应为一个字符串,该汇编函数调用C语言的printf()函数输出这个字符串的前n个字符,n即为那个整数。在C语言写的main()函数中调用并传递参数给这个汇编函数来得到输出。


实验步骤

1、连线如下:



2、arm 指令和thumb指令

观察是ARM指令
在板子上用vi命令写测试程序如下:


#include<stdio.h>
int main(){
int a=1,b=2;
a=b+a;
return 0;
}


对其进行编译,编译命令如下:


gcc -marm test.c -o test_arm.o


用`objdump -d test_arm.o`进行查看可以得到:
000103e8 <main>:
103e8:       e52db004        push    {fp}            ; (str fp, [sp, #-4]!)
103ec:       e28db000        add     fp, sp, #0
103f0:       e24dd00c        sub     sp, sp, #12
103f4:       e3a03001        mov     r3, #1
103f8:       e50b3008        str     r3, [fp, #-8]
103fc:       e3a03002        mov     r3, #2
10400:       e50b300c        str     r3, [fp, #-12]
10404:       e51b2008        ldr     r2, [fp, #-8]
10408:       e51b300c        ldr     r3, [fp, #-12]
1040c:       e0823003        add     r3, r2, r3
10410:       e50b3008        str     r3, [fp, #-8]
10414:       e3a03000        mov     r3, #0
10418:       e1a00003        mov     r0, r3
1041c:       e24bd000        sub     sp, fp, #0
10420:       e49db004        pop     {fp}            ; (ldr fp, [sp], #4)
10424:       e12fff1e        bx      lr
可以看到指令本身是32位的,GCC默认使用的是ARM指令。

观察thumb指令:
将上面的编译代码的 –marm 换成 –mthumb发现会出现一下问题:




这是一个bug,还没有解决。


3、对于 ARM 指令,能否产生条件执行的指令

编写测试程序test.c文件,内容如下:




编译并查看结果:






出现cpm指令、ble等条件分支指令的出现,可见ARM是支持的条件执行指令的。


4、设计C的代码场景,观察是否产生了寄存器移位寻址

编写测试程序test1.c如下:


#include <stdio.h>
int main(){
int a[10];
int i=0;
int j=0;
for(i=0;i<4;i++){
a[j]=a[j*2];
}
return 0;
}


编译:编译代码:`gcc -c test1.c`
查看结果:`objdump -d test1.o`
000103e8 <main>:
103e8:       e52db004        push    {fp}            ; (str fp, [sp, #-4]!)
103ec:       e28db000        add     fp, sp, #0
103f0:       e24dd034        sub     sp, sp, #52     ; 0x34
103f4:       e3a03000        mov     r3, #0
103f8:       e50b3008        str     r3, [fp, #-8]
103fc:       e3a03000        mov     r3, #0
10400:       e50b300c        str     r3, [fp, #-12]
10404:       e3a03000        mov     r3, #0
10408:       e50b3008        str     r3, [fp, #-8]
1040c:       ea00000d        b       10448 <main+0x60>
10410:       e51b300c        ldr     r3, [fp, #-12]
10414:       e1a03083        lsl     r3, r3, #1
10418:       e1a03103        lsl     r3, r3, #2
1041c:       e24b2004        sub     r2, fp, #4
10420:       e0823003        add     r3, r2, r3
10424:       e5132030        ldr     r2, [r3, #-48]  ; 0xffffffd0
10428:       e51b300c        ldr     r3, [fp, #-12]
1042c:       e1a03103        lsl     r3, r3, #2
10430:       e24b1004        sub     r1, fp, #4
10434:       e0813003        add     r3, r1, r3
10438:       e5032030        str     r2, [r3, #-48]  ; 0xffffffd0
1043c:       e51b3008        ldr     r3, [fp, #-8]
10440:       e2833001        add     r3, r3, #1
10444:       e50b3008        str     r3, [fp, #-8]
10448:       e51b3008        ldr     r3, [fp, #-8]
1044c:       e3530003        cmp     r3, #3
10450:       daffffee        ble     10410 <main+0x28>
10454:       e1a00003        mov     r0, r3
10458:       e24bd000        sub     sp, fp, #0
1045c:       e49db004        pop     {fp}            ; (ldr fp, [sp], #4)
10460:       e12fff1e        bx      lr

可以看到有逻辑左移指令:lsl


5、设计 C 的代码场景,观察一个复杂的 32 位数是如何装载到寄存器的;

测试代码test2.c如下:


#include <stdio.h>
int main(){
unsigned a=0x12345678;
a++;
return 0;
}


编译:`gcc -c test2.c`
使用objdump结果如下(`objdump -d test2.o`):
000103e8 <main>:
103e8:       e52db004        push    {fp}            ; (str fp, [sp, #-4]!)
103ec:       e28db000        add     fp, sp, #0
103f0:       e24dd00c        sub     sp, sp, #12
103f4:       e59f301c        ldr     r3, [pc, #28]   ; 10418 <main+0x30>
103f8:       e50b3008        str     r3, [fp, #-8]
103fc:       e51b3008        ldr     r3, [fp, #-8]
10400:       e2833001        add     r3, r3, #1
10404:       e50b3008        str     r3, [fp, #-8]
10408:       e1a00003        mov     r0, r3
1040c:       e24bd000        sub     sp, fp, #0
10410:       e49db004        pop     {fp}            ; (ldr fp, [sp], #4)
10414:       e12fff1e        bx      lr
10418:       12345678        .word   0x12345678

可以看见ARM指令将大数字存在命令段[pc,#28],通过ldr指令去加载,而非MIPS等指令集使用lui和ori来实现32位大数字的实现。


6、写一个 C 的多重函数调用的程序,观察和分析

a. 调用时的返回地址在哪里?
b. 传入的参数在哪里?
c. 本地变量的堆栈分配是如何做的?
d. 寄存器是 caller 保存还是 callee 保存?是全体保存还是部分保存?

测试代码test3.c如下:




编译,查看结果如下:
test4.o:     file format elf32-littlearm
Disassembly of section .text:
00000000 <f2>:
0:   e52db004        push    {fp}            ; (str fp, [sp, #-4]!)
4:   e28db000        add     fp, sp, #0
8:   e24dd01c        sub     sp, sp, #28
c:   e50b0010        str     r0, [fp, #-16]
10:   e50b1014        str     r1, [fp, #-20]  ; 0xffffffec
14:   e50b2018        str     r2, [fp, #-24]  ; 0xffffffe8
18:   e50b301c        str     r3, [fp, #-28]  ; 0xffffffe4
1c:   e3a03000        mov     r3, #0
20:   e50b3008        str     r3, [fp, #-8]
24:   e51b3010        ldr     r3, [fp, #-16]
28:   e51b201c        ldr     r2, [fp, #-28]  ; 0xffffffe4
2c:   e0020392        mul     r2, r2, r3
30:   e51b3014        ldr     r3, [fp, #-20]  ; 0xffffffec
34:   e59b1004        ldr     r1, [fp, #4]
38:   e0030391        mul     r3, r1, r3
3c:   e0822003        add     r2, r2, r3
40:   e51b3018        ldr     r3, [fp, #-24]  ; 0xffffffe8
44:   e59b1008        ldr     r1, [fp, #8]
48:   e0030391        mul     r3, r1, r3
4c:   e0822003        add     r2, r2, r3
50:   e59b300c        ldr     r3, [fp, #12]
54:   e59b1010        ldr     r1, [fp, #16]
58:   e0030391        mul     r3, r1, r3
5c:   e0822003        add     r2, r2, r3
60:   e59b3014        ldr     r3, [fp, #20]
64:   e0823003        add     r3, r2, r3
68:   e50b3008        str     r3, [fp, #-8]
6c:   e51b3008        ldr     r3, [fp, #-8]
70:   e1a00003        mov     r0, r3
74:   e24bd000        sub     sp, fp, #0
78:   e49db004        pop     {fp}            ; (ldr fp, [sp], #4)
7c:   e12fff1e        bx      lr

00000080 <f3>:
80:   e92d4800        push    {fp, lr}
84:   e28db004        add     fp, sp, #4
88:   e24dd050        sub     sp, sp, #80     ; 0x50
8c:   e50b0030        str     r0, [fp, #-48]  ; 0xffffffd0
90:   e50b1034        str     r1, [fp, #-52]  ; 0xffffffcc
94:   e50b2038        str     r2, [fp, #-56]  ; 0xffffffc8
98:   e50b303c        str     r3, [fp, #-60]  ; 0xffffffc4
9c:   e51b2030        ldr     r2, [fp, #-48]  ; 0xffffffd0
a0:   e51b3034        ldr     r3, [fp, #-52]  ; 0xffffffcc
a4:   e0823003        add     r3, r2, r3
a8:   e50b3008        str     r3, [fp, #-8]
ac:   e51b2038        ldr     r2, [fp, #-56]  ; 0xffffffc8
b0:   e51b303c        ldr     r3, [fp, #-60]  ; 0xffffffc4
b4:   e0823003        add     r3, r2, r3
b8:   e50b300c        str     r3, [fp, #-12]
bc:   e59b2004        ldr     r2, [fp, #4]
c0:   e59b3008        ldr     r3, [fp, #8]
c4:   e0823003        add     r3, r2, r3
c8:   e50b3010        str     r3, [fp, #-16]
cc:   e59b200c        ldr     r2, [fp, #12]
d0:   e59b3010        ldr     r3, [fp, #16]
d4:   e0823003        add     r3, r2, r3
d8:   e50b3014        str     r3, [fp, #-20]  ; 0xffffffec
dc:   e59b2014        ldr     r2, [fp, #20]
e0:   e59b3018        ldr     r3, [fp, #24]
e4:   e0823003        add     r3, r2, r3
e8:   e50b3018        str     r3, [fp, #-24]  ; 0xffffffe8
ec:   e3a03005        mov     r3, #5
f0:   e58d3000        str     r3, [sp]
f4:   e3a03006        mov     r3, #6
f8:   e58d3004        str     r3, [sp, #4]
fc:   e3a03007        mov     r3, #7
100:   e58d3008        str     r3, [sp, #8]
104:   e3a03008        mov     r3, #8
108:   e58d300c        str     r3, [sp, #12]
10c:   e3a03009        mov     r3, #9
110:   e58d3010        str     r3, [sp, #16]
114:   e3a00001        mov     r0, #1
118:   e3a01002        mov     r1, #2
11c:   e3a02003        mov     r2, #3
120:   e3a03004        mov     r3, #4
124:   ebfffffe        bl      0 <f2>
128:   e51b3008        ldr     r3, [fp, #-8]
12c:   e51b200c        ldr     r2, [fp, #-12]
130:   e0030392        mul     r3, r2, r3
134:   e50b301c        str     r3, [fp, #-28]  ; 0xffffffe4
138:   e51b3010        ldr     r3, [fp, #-16]
13c:   e51b2014        ldr     r2, [fp, #-20]  ; 0xffffffec
140:   e0030392        mul     r3, r2, r3
144:   e50b3020        str     r3, [fp, #-32]  ; 0xffffffe0
148:   e51b201c        ldr     r2, [fp, #-28]  ; 0xffffffe4
14c:   e51b3020        ldr     r3, [fp, #-32]  ; 0xffffffe0
150:   e0633002        rsb     r3, r3, r2
154:   e50b3024        str     r3, [fp, #-36]  ; 0xffffffdc
158:   e51b3024        ldr     r3, [fp, #-36]  ; 0xffffffdc
15c:   e51b2018        ldr     r2, [fp, #-24]  ; 0xffffffe8
160:   e0030392        mul     r3, r2, r3
164:   e59b201c        ldr     r2, [fp, #28]
168:   e0030392        mul     r3, r2, r3
16c:   e50b3028        str     r3, [fp, #-40]  ; 0xffffffd8
170:   e51b3028        ldr     r3, [fp, #-40]  ; 0xffffffd8
174:   e1a00003        mov     r0, r3
178:   e24bd004        sub     sp, fp, #4
17c:   e8bd8800        pop     {fp, pc}

00000180 <f1>:
180:   e92d4800        push    {fp, lr}
184:   e28db004        add     fp, sp, #4
188:   e24dd030        sub     sp, sp, #48     ; 0x30
18c:   e50b0010        str     r0, [fp, #-16]
190:   e50b1014        str     r1, [fp, #-20]  ; 0xffffffec
194:   e50b2018        str     r2, [fp, #-24]  ; 0xffffffe8
198:   e3a03000        mov     r3, #0
19c:   e50b3008        str     r3, [fp, #-8]
1a0:   e3a03005        mov     r3, #5
1a4:   e58d3000        str     r3, [sp]
1a8:   e3a03006        mov     r3, #6
1ac:   e58d3004        str     r3, [sp, #4]
1b0:   e3a03007        mov     r3, #7
1b4:   e58d3008        str     r3, [sp, #8]
1b8:   e3a03008        mov     r3, #8
1bc:   e58d300c        str     r3, [sp, #12]
1c0:   e3a03009        mov     r3, #9
1c4:   e58d3010        str     r3, [sp, #16]
1c8:   e51b0010        ldr     r0, [fp, #-16]
1cc:   e51b1014        ldr     r1, [fp, #-20]  ; 0xffffffec
1d0:   e51b2018        ldr     r2, [fp, #-24]  ; 0xffffffe8
1d4:   e3a03004        mov     r3, #4
1d8:   ebfffffe        bl      0 <f2>
1dc:   e50b0008        str     r0, [fp, #-8]
1e0:   e51b3008        ldr     r3, [fp, #-8]
1e4:   e1a00003        mov     r0, r3
1e8:   e24bd004        sub     sp, fp, #4
1ec:   e8bd8800        pop     {fp, pc}

000001f0 <main>:
1f0:   e92d4800        push    {fp, lr}
1f4:   e28db004        add     fp, sp, #4
1f8:   e24dd030        sub     sp, sp, #48     ; 0x30
1fc:   e3a03000        mov     r3, #0
200:   e50b3008        str     r3, [fp, #-8]
204:   e3a03001        mov     r3, #1
208:   e50b300c        str     r3, [fp, #-12]
20c:   e3a03002        mov     r3, #2
210:   e50b3010        str     r3, [fp, #-16]
214:   e51b0008        ldr     r0, [fp, #-8]
218:   e51b100c        ldr     r1, [fp, #-12]
21c:   e51b2010        ldr     r2, [fp, #-16]
220:   ebfffffe        bl      180 <f1>
224:   e1a03000        mov     r3, r0
228:   e59f0060        ldr     r0, [pc, #96]   ; 290 <main+0xa0>
22c:   e1a01003        mov     r1, r3
230:   ebfffffe        bl      0 <printf>
234:   e3a03007        mov     r3, #7
238:   e58d3000        str     r3, [sp]

可以发现:
函数调用后的返回地址存放在LR寄存器中。
传入的参数存放在R0 R1 R2 R3四个寄存器中,多余的参数放在堆栈中。
本地变量存放在堆栈高地址,传进来的参数存放在堆栈低地址。
R0到R3由caller保存,R4以上由callee保存。


7、MLA 是带累加的乘法,尝试要如何写 C 的表达式能编译得到 MLA 指令。

测试代码test5.c如下:




查看编译结果:




使用 -o1 进行优化,查看结果:




可以看到MLA命令得到了使用。

未使用 -o1 优化时的结果:
00000000 <f1>:
0:   e52db004        push    {fp}        ; (str fp, [sp, #-4]!)
4:   e28db000        add     fp, sp, #0
8:   e24dd014        sub     sp, sp, #20
c:   e50b0008        str     r0, [fp, #-8]
10:   e50b100c        str     r1, [fp, #-12]
14:   e50b2010        str     r2, [fp, #-16]
18:   e51b3008        ldr     r3, [fp, #-8]
1c:   e51b200c        ldr     r2, [fp, #-12]
20:   e0020392        mul     r2, r2, r3
24:   e51b3010        ldr     r3, [fp, #-16]
28:   e0823003        add     r3, r2, r3
2c:   e1a00003        mov     r0, r3
30:   e24bd000        sub     sp, fp, #0
34:   e49db004        pop     {fp}         ; (ldr fp, [sp], #4)
38:   e12fff1e        bx      lr

0000003c <main>:
3c:   e92d4800        push    {fp, lr}
40:   e28db004        add     fp, sp, #4
44:   e3a00001        mov     r0, #1
48:   e3a01002        mov     r1, #2
4c:   e3a02003        mov     r2, #3
50:   ebfffffe        bl      0 <f1>
54:   e3a03000        mov     r3, #0
58:   e1a00003        mov     r0, r3


8、BIC是对某一个比特清零的指令,尝试要如何写 C 的表达式能编译得到 BIC 指令。

测试代码test6.c如下:


#include<stdio.h>
int bitand(int x){
return x&0xeeeeeeee;
}
int main(){
bitand(99);
return 0;
}


编译,查看结果:




使用 -o1 优化后的结果
编译命令:`gcc -c test6.c -o1`
查看结果:`objdump -d test6.o`
00000000 <bitand>:
0:   e3c00411        bic     r0, r0, #285212672    ; 0x11000000
4:   e3c00811        bic     r0, r0, #1114112      ; 0x110000
8:   e3c00c11        bic     r0, r0, #4352          ; 0x1100
c:   e3c00011        bic     r0, r0, #17
10:   e12fff1e        bx      lr
00000014 <main>:
14:   e3a00000        mov     r0, #0
18:   e12fff1e        bx      lr

可见BIC指令。


9、编写一个汇编函数,接受一个整数和一个指针做为输入,指针所指应为一个字符串,该汇编函数调用C语言的 printf()函数输出这个字符串的前n个字符,n即为那个整数。在C语言写的main()函数中调用并传递参数给这个汇编函数 来得到输出。

测试代码(分为汇编代码和C代码test7)如下:
汇编代码(test7.s)如下:


.global test7
test7:
push {R5, R6, R7, lr}
MOV R5, R0
MOV R6, R1
MOV R7, #0
CMP R7, R6
BGE exit
begin:
LDR R0, =char
LDR R1, [R5, R7]
CMP R1, #0
BEQ exit
bl printf
ADD R7, R7, #1
CMP R7, R6
BLT begin
exit:
LDR R0, =newline
bl printf
MOV R0, R7
pop {R5, R6, R7, pc}

.data
char: .asciz "%c"
newline: .asciz "\n"


C代码如下(test7.c)如下:


#include <stdio.h>

extern int test7(char*, int);

int main(){
int a;
char s[100];
scanf("%s %d", s, &a);
a = test7(s, a);
printf("Print %d character.\n", a);
return 0;
}


编译命令:`gcc test7.c test7.s -o test7 -g -marm`
运行:./test7




其中test7.s的逻辑类似如下:


int test7(cahr *s, int a){
int i = 0;
for (i; i<a; i++)
printf(“%c”, s[i]);
return i;
}


10、自扩展内容:编写测试程序,测试使用带条件的ARM指令和不使用时的执行效率。

noif.s汇编代码:


.global add
add:
CMP R0, #0
ADD R0, R0, R1
MOV pc, lr


useif.s汇编代码:

.global add
add:
CMP    R0, #0
ADDNE R0, R0, R1
MOV pc, lr


C代码(test.c):


#include <stdio.h>
extern int add(int, int);
int main(){
int a, b, i, times;
scanf("%d %d %d", &a, &b, ×);
for (i=0; i<times; i++)
a = add(a, b);
printf("%d\n", a);
return 0;
}


testif.in文件:

1 2 20000000


编译运行查看结果:




经过多次比对,虽然时间略有波动。但是总体而言,两程序执行时间基本没有区别,即ADDNE与ADD的执行基本没有时间差。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: