Lab 2:ARM指令
来源:互联网 发布:西交大网络学校 编辑:程序博客网 时间:2024/05/19 00:14
一、选用的实验器材
RaspberryPi(树莓派)一块、USB-TTL串口线一根(PL2303芯片)、以太网线一根、8G容量SD卡一张、带windows7操作系统的PC一台。
二、连接示意图
三、实物连接图
四、实验过程
1、使用交叉编译环境生成arm指令和thumb指令
在Ubuntu环境下编写一个如下所示的计算乘法的C程序:
随后用交叉编译工具进行编译(不给额外的参数):
在lab2目录下生成一个a.out文件,把这个文件从虚拟机拷贝到windows环境下,再通过FileZilla Client发送给树莓派(已经通过超级终端连接PC),随后进入树莓派的对应目录下,并且使用gdb命令对a.out文件进行调试,输入disassemble main进行反汇编结果如下:
解释生成的汇编代码如下:
0x000083b0 <+0>: push {r11} ; (str r11, [sp,#-4]!) save fp
0x000083b4 <+4>: add r11, sp, #0 ;setr11(which is just fp)=sp
0x000083b8 <+8>: sub sp, sp, #12
0x000083bc <+12>: mov r3, #10 ;set r3=10,which is the first argument
0x000083c0 <+16>: str r3, [r11, #-12] ;store r3 to memory
0x000083c4 <+20>: mov r3, #2 ;set r3=2,which isthe second argument
0x000083c8 <+24>: str r3, [r11, #-8] ;store r3 to memory
0x000083cc <+28>: ldr r2, [r11, #-12] ;get value 10
0x000083d0 <+32>: ldr r1, [r11, #-8] ;get value 2
0x000083d4 <+36>: mul r3, r1, r2 ;2*10=20
0x000083d8 <+40>: mov r0, r3 ;get final result
0x000083dc <+44>: add sp, r11, #0 ;setsp=fp
0x000083e0 <+48>: ldmfd sp!, {r11} ;get back the value to r11
0x000083e4 <+52>: bx lr ;return
我们看到,这里没两条指令之间PC相差4,可见一条指令的大小为4个字节,因此是arm指令集。
此后,重新回到Ubuntu环境下,重现交叉编译刚才的test.c文件,但这次加入-mthumb参数如下所示:
这时,先把树莓派上的a.out文件重命名为arm.out文件如下:
然后,和刚才的操作一样,把Ubuntu下刚才编译好的a.out文件弄到树莓派的lab2目录下:
使用gdb命令调试,得到如下反汇编结果:
解释生成的汇编代码如下:
0x000083b0 <+0>: push {r7, lr} ;save the value ofr7(fp) and lr
0x000083b2 <+2>: sub sp, #8 ;set sp=sp-8
0x000083b4 <+4>: add r7, sp, #0 ;set r7=sp
0x000083b6 <+6>: movs r3, #10 ;set r3=10,which isthe first argument
0x000083b8 <+8>: str r3, [r7, #0] ;store r3 to memory
0x000083ba <+10>: movs r3, #2 ;set r3=2,which isthe second argument
0x000083bc <+12>: str r3, [r7, #4] ;store r3 to memory
0x000083be <+14>: ldr r2, [r7, #0] ;get value 10
0x000083c0 <+16>: ldr r1, [r7, #4] ;get value 2
0x000083c2 <+18>: adds r3, r1, #0 ;set r3=r1=10
0x000083c4 <+20>: muls r3, r2 ;r3=r3*2=20
0x000083c6 <+22>: adds r0, r3, #0 ;get final result
0x000083c8 <+24>: mov sp, r7 ;set sp=r7
0x000083ca <+26>: add sp, #8 ;restore sp
0x000083cc <+28>: pop {r7} ;restore r7(fp)
0x000083ce <+30>: pop {r1} ;get return address
0x000083d0 <+32>: bx r1 ;return
可以看到,这里的每两条指令的地址相差2,说明一条指令的大小为2Byte,说明这确实是thumb指令。相同的程序,ARM和Thumb编译的结果的不同所在:
首先thumb指令长度是16位的,比arm短了一半。但是指令数量相对增加。
此外,在thumb指令中无法使用像ldmfd这样的复杂指令。但thumb指令生成的最终
执行程序的大小比arm指令生成的相对小一些。
2、对于ARM 指令,能否产生条件执行的指令
直接在树莓派的环境下编写如下所示的C程序:
它会将输入中的较大的数加1,然后输出两个值。
使用-O2选项进行优化编译如下:
反汇编得到如下结果:
可以看到+44和+48两个地址处的命令都是条件执行的命令。
解释生成的汇编代码如下:
0x00010348 <+0>: push {lr} ; (str lr, [sp,#-4]!) save the return address
0x0001034c <+4>: sub sp, sp, #12 ;set sp=sp-12
0x00010350 <+8>: mov r1, sp ;value of a willbe saved to address in sp
0x00010354 <+12>: ldr r0, [pc, #60] ; 0x10398<main+80>
0x00010358 <+16>: bl 0x10330 ;to get the valueof a from input, the value is saved in memory ;addresscalculated by value in r1
0x0001035c <+20>: add r1, sp, #4 ;value of b willbe saved to address in sp+4
0x00010360 <+24>: ldr r0, [pc, #48] ; 0x10398<main+80>
0x00010364 <+28>: bl 0x10330 ;to get the valueof b from input
0x00010368 <+32>: ldm sp, {r1, r2} ;copy value of a andb to r1 and r2
0x0001036c <+36>: ldr r0, [pc, #40] ; 0x1039c<main+84>
0x00010370 <+40>: cmp r1, r2 ;compare value ofa and b
0x00010374 <+44>: addle r3, r2, #1 ;if a<=b b++
0x00010378 <+48>: addgt r1, r1, #1 ;else a>b a++
0x0001037c <+52>: strgt r1, [sp] ;and restore newvalue of a to related memory address
0x00010380 <+56>: movle r2, r3 ;in case ofa<b, r2 has the value of b+1
0x00010384 <+60>: strle r3, [sp, #4] ;in case ofa<b, restore new value of b to related memory address
0x00010388 <+64>: bl 0x1030c ;go to printinformation,the value is in sp and sp+4
0x0001038c <+68>: mov r0, #0 ;set return valueas 0
0x00010390 <+72>: add sp, sp, #12 ;restore sp
0x00010394 <+76>: pop {pc} ; (ldr pc, [sp],#4) get the return address
0x00010398 <+80>: andeq r0, r1, r12, lsr r5
0x0001039c <+84>: andeq r0, r1, r0, asr #10
3、设计 C 的代码场景,观察是否产生了寄存器移位寻址;
在树莓派环境下编写如下的C代码:
同样使用-O2优化编译:
得到的反汇编结果:
容易看到,+56地址处的指令是add r1, r1, r3, asr #2,这条指令的寻址方式用到了寄存器移位寻址。实际上是先把r3右移2位(除以4),再把r3相加,然后赋给r1。
解释生成的汇编代码如下:
0x00010348 <+0>: push {r4, lr} ;save returnvalue and r4
0x0001034c <+4>: sub sp, sp, #8 ;set sp=sp-8
0x00010350 <+8>: ldr r4, [pc, #60] ;0x10394<main+76>
0x00010354 <+12>: mov r1, sp ;value of awill be saved to address in sp
0x00010358 <+16>: mov r0, r4
0x0001035c <+20>: bl 0x10330 ;to get thevalue of a from input
0x00010360 <+24>: add r1, sp, #4 ;value of bwill be saved to address in sp+4
0x00010364 <+28>: mov r0, r4
0x00010368 <+32>: bl 0x10330 ;to get thevalue of b from input
0x0001036c <+36>: ldm sp, {r1, r3} ;save a and bto r1 and r3
0x00010370 <+40>: ldr r0, [pc, #32] ;0x10398<main+80>
0x00010374 <+44>: add r2, r3, #3 ;set r2=r3+3
0x00010378 <+48>: cmp r3, #0 ;check ifb<0
0x0001037c <+52>: movlt r3, r2 ;
0x00010380 <+56>: add r1, r1, r3, asr #2 ;we find aRegister shift addressing,which calculate a+b/4
0x00010384 <+60>: bl 0x1030c ;go to print
0x00010388 <+64>: mov r0, #0 ;set returnvalue as 0
0x0001038c <+68>: add sp, sp, #8 ;restore sp
0x00010390 <+72>: pop {r4, pc} ;restore r4 and return
0x00010394 <+76>: andeq r0, r1, r8, lsr r5
0x00010398 <+80>: andeq r0, r1, r12, lsr r5
4. 设计C 的代码场景,观察一个复杂的 32 位数是如何装载到寄存器的
在树莓派环境下编写如下所示的C程序:
不使用任何优化选项的情况下编译:
反汇编结果如下:
对于简单的32位数如0x10000001,它是直接用立即数寻址的方式赋值给r3寄存器的(在+12地址处的命令)。但是当处理复杂的32位数如0xabcd1234,则是把这个32位数的值先写在内存中(+56的寻址地址)。然后ldr r3, [pc, #28]这样的指令直接载入。为了证明这一点,使用x/2uh 0x10420命令直接查看main+56地址处的数值,可以看到显示了 4660(16进制下为1234) 43981(16进制下为abcd)的结果,证明上述分析是正确的。(上述+56的地方本来是数据0xabcd1234但是被gdb误认为是指令而进行翻译了)
对于所生成的汇编代码的解释如下:
0x000103e8 <+0>: push {r11} ; (str r11, [sp,#-4]!)
0x000103ec <+4>: add r11, sp, #0 ;set r11=sp
0x000103f0 <+8>: sub sp, sp, #12 ;set sp=sp-12
0x000103f4 <+12>: mov r3, #268435457 ;0x10000001 immediate addressing for simple32bits integer
0x000103f8 <+16>: str r3, [r11, #-8] ;store value of a
0x000103fc <+20>: ldr r3, [pc, #28] ;0x10420<main+56> has to load complex 32bits integer from memory
;base indexed addressing
0x00010400 <+24>: str r3, [r11, #-12] ;store value of b
0x00010404 <+28>: ldr r2, [r11, #-8] ;load a to r2
0x00010408 <+32>: ldr r3, [r11, #-12] ;load b to r3
0x0001040c <+36>: add r3, r2, r3 ;r3=a+b
0x00010410 <+40>: mov r0, r3 ;set return valueas r3(a+b)
0x00010414 <+44>: sub sp, r11, #0
0x00010418 <+48>: pop {r11} ; (ldr r11, [sp],#4)
0x0001041c <+52>: bx lr
0x00010420 <+56>: blge 0xff354cf8 ;wrong translationlike messy code, in fact it's data ;not instruction
End ofassembler dump.
(gdb)x/2uh 0x10420 ;to see the real value in 0x10420
0x10420<main+56>: 4660 43981 ;42981=0xabcd, 4660=0x1234, thecombine of them is the value of b
5、写一个C 的多重函数调用的程序,观察和分析
编写的C函数如右所示,其中main调用f2,然后f2调用f1。
得到的反汇编代码和对应解释如下
Dump ofassembler code for function f1:
0x00010470 <+0>: push {r11} ; (str r11, [sp,#-4]!) save value of caller's fp
0x00010474 <+4>: add r11, sp, #0 ;set r11(fp)=sp
0x00010478 <+8>: sub sp, sp, #20 ;set sp=sp-20
0x0001047c <+12>: str r0, [r11, #-16] ;save argument a 参数直接通过r0寄存器传入
0x00010480 <+16>: ldr r3, [r11, #-16] ;r3=a
0x00010484 <+20>: ldr r2, [r11, #-16] ;r2=a
0x00010488 <+24>: mul r3, r2, r3 ;r3=r2*r3=a*a
0x0001048c <+28>: add r3, r3, #2 ;r3=a*a+2
0x00010490 <+32>: str r3, [r11, #-8] ;save r3
0x00010494 <+36>: ldr r3, [r11, #-8] ;load r3 back
0x00010498 <+40>: mov r0, r3 ;set r0=r3=a*a+2, which is the returnvalue of f1()function
0x0001049c <+44>: sub sp, r11, #0 ;set sp=r11restore sp of caller
0x000104a0 <+48>: pop {r11} ; (ldr r11, [sp],#4) restore value of caller's fp
0x000104a4 <+52>: bx lr ;调试时的返回地址在lr, 是调用者保存的
End ofassembler dump.
Dump ofassembler code for function f2:
0x000104a8 <+0>: push {r11, lr} ;save value ofcaller's fp and caller's lr
0x000104ac <+4>: add r11, sp, #4 ;set r11(fp)=sp+4
0x000104b0 <+8>: sub sp, sp, #16 ;set sp=sp-16
0x000104b4 <+12>: str r0, [r11, #-16] ;save argument c 参数直接通过r0寄存器传入
0x000104b8 <+16>: ldr r0, [r11, #-16] ;get c back
0x000104bc <+20>: bl 0x10470 <f1> ;call f1()function, bl指令会在跳转前把下一条指令地址(子程序的 ;返回地址)拷贝到lr寄存器
0x000104c0 <+24>: mov r2, r0 ;set r2=ro,whichis the return of f1() function
0x000104c4 <+28>: mov r3, r2 ;set r3=r2=f1(c)
0x000104c8 <+32>: lsl r3, r3, #2 ;r3=4*f1(c)
0x000104cc <+36>: add r3, r3, r2 ;r3=r3+r2=4*f1(c)+f1(c)=5*f1(c)
0x000104d0 <+40>: lsl r3, r3, #1 ;r3=2*r3=10*f1(c)
0x000104d4 <+44>: str r3, [r11, #-8] ;save r3
0x000104d8 <+48>: ldr r3, [r11, #-8] ;get r3 back
0x000104dc <+52>: mov r0, r3 ;set r0=r3, whichis the return value of f2()function
0x000104e0 <+56>: sub sp, r11, #4 ;set sp=r11(fp)-4restore sp of caller
0x000104e4 <+60>: pop {r11, pc} ;restore value ofcaller's fp and caller's lr
End ofassembler dump.
Dump ofassembler code for function main:
0x000104e8 <+0>: push {r11, lr} ;save value ofcaller's fp and caller's lr
0x000104ec <+4>: add r11, sp, #4 ;set r11(fp)=sp+4
0x000104f0 <+8>: sub sp, sp, #8 ;set sp=sp-8
0x000104f4 <+12>: sub r3, r11, #12 ;set r3=r11(fp)-12
0x000104f8 <+16>: ldr r0, [pc, #48] ; 0x10530 <main+72>
0x000104fc <+20>: mov r1, r3
0x00010500 <+24>: bl 0x10330 ;go to scanf()
0x00010504 <+28>: ldr r3, [r11, #-12] ;set r3=input value
0x00010508 <+32>: mov r0, r3 ;set r0=r3=a,which is the input argument
0x0001050c <+36>: bl 0x104a8 <f2> ;call f2()function, bl指令会在跳转前把下一条指令地址(子程序的 ;返回地址)拷贝到lr寄存器
0x00010510 <+40>: str r0, [r11, #-8] ;now r0 has thevalue to be print
0x00010514 <+44>: ldr r0, [pc, #24] ; 0x10534<main+76>
0x00010518 <+48>: ldr r1, [r11, #-8]
0x0001051c <+52>: bl 0x1030c ;go to print thevalue of r1
0x00010520 <+56>: mov r3, #0 ;set r3=0
0x00010524 <+60>: mov r0, r3 ;set r0=r3=0,which is the return value
0x00010528 <+64>: sub sp, r11, #4 ;set sp=r11-4restore sp of caller
0x0001052c <+68>: pop {r11, pc} ;restore value ofcaller's fp and caller's lr
0x00010530 <+72>: andeq r0, r1, r12, lsr #11
0x00010534 <+76>: ; <UNDEFINED>instruction: 0x000105b0
End ofassembler dump.
a. 调用时的返回地址在哪里?
lr寄存器中
b. 传入的参数在哪里?
r0寄存器中
c. 本地变量的堆栈分配是如何做的?
对f1函数来说,从sp指针开始向下开辟栈空间。
d. 寄存器是 caller 保存还是 callee 保存?是全体保存还是部分保存?
都是部分保存。其中返回地址的值由caller放入lr寄存器(bl指令),r11(fp)、sp和lr由callee保存
6、尝试要如何写C 的表达式能编译得到MLA 指令
编写如下所示的C程序:
使用-O2选项进行优化编译:
再+44处可以看到mla指令。
对于生成的汇编指令的解释如下:
0x00010348 <+0>: push {lr} ;(str lr, [sp,#-4]!)
0x0001034c <+4>: sub sp, sp, #20
0x00010350 <+8>: add r1, sp, #4
0x00010354 <+12>: add r2, sp, #8
0x00010358 <+16>: add r3, sp, #12
0x0001035c <+20>: ldr r0, [pc, #36] ;0x10388<main+64>
0x00010360 <+24>: bl 0x10330 ;call scanf()
0x00010364 <+28>: ldr r2, [sp, #4] ;r2=a
0x00010368 <+32>: ldr r1, [sp, #8] ;r1=b
0x0001036c <+36>: ldr r3, [sp, #12] ;r3=c
0x00010370 <+40>: ldr r0, [pc, #20] ; 0x1038c<main+68>
0x00010374 <+44>: mla r1, r1, r2, r3 ;r1=a*b+c
0x00010378 <+48>: bl 0x1030c ;call printf()
0x0001037c <+52>: mov r0, #0 ;set return valueas 0
0x00010380 <+56>: add sp, sp, #20 ;restore caller'ssp
0x00010384 <+60>: pop {pc} ; (ldr pc, [sp],#4)
0x00010388 <+64>: andeq r0, r1, r12, lsr #10
0x0001038c <+68>: andeq r0, r1, r8, lsr r5
7、写C 的表达式能编译得到BIC 指令
编写如下所示的C程序:
用-O2选项进行优化编译:
在+32处看到了BIC指令。
对于生成的汇编代码的解释如下:
0x00010348 <+0>: push {r4, lr} ;save r4 andlr
0x0001034c <+4>: sub sp, sp, #8
0x00010350 <+8>: ldr r4, [pc, #32] ; 0x10378<main+48> get b=0xf0000000
0x00010354 <+12>: add r1, sp, #4
0x00010358 <+16>: mov r0, r4 ;set r0=b
0x0001035c <+20>: bl 0x10330 ;go to scanfto get value of a
0x00010360 <+24>: ldr r1, [sp, #4]
0x00010364 <+28>: mov r0, r4
0x00010368 <+32>: bic r1, r1, #-268435456 ; 0xf0000000BIC insetruction
0x0001036c <+36>: bl 0x1030c ;go to print
0x00010370 <+40>: add sp, sp, #8
0x00010374 <+44>: pop {r4, pc}
0x00010378 <+48>: andeq r0, r1, r8, lsl r5 ;this data0xf0000000 instead of an instruction
8、编写一个汇编函数,接受一个整数和一个指针做为输入,指针所指应为一个字符串,该汇编函数调用C语言的 printf()函数输出这个字符串的前n个字符,n即为那个整数。在C语言写的main()函数中调用并传递参数给这个汇编函数来得到输出。
在Ubuntu交叉编译环境下,编写如下所示的main.c文件和assemble.s文件:
main.c
#include<stdio.h>
extern void asm_print(int N,char* str); //汇编函数
int main()
{
int N;
charstr[30];
printf("start of main\n");
printf("Please input N:");
scanf("%d",&N); //输入一个整数n
printf("Please input string:");
scanf("%s",str); //输入一个字符串
asm_print(N,str);//调用汇编函数去打印字符串的前n个字符
printf("end of main\n");
return 0;
}
Assemble.s
.text
.global asm_print
.extern printf ;将要调用C函数printf
asm_print:
movr3,r1 ;移入printf的第四个参数,即被打印的字符串的首地址
movr1,r0 ;移入printf的第二个参数,为main.c中传入的N
movr2,r0 ;移入printf的第三个参数,同样是main.c中传入的N
adrr0,form ;载入printf的第一个参数,也即输出格式
stmfd sp!,{lr} ;调用C函数前先要保存lr寄存器值
blprintf ;调用C的printf函数
ldmfd sp!,{pc} ;恢复pc值
over:
movpc,lr ;将lr寄存器的值放入pc,从而回到调用了这个汇编代码的
;C函数
form:
.asciz "the first Nchars:%-*.*s\n"
.end
在交叉编译环境下进行混合编译:
将生成的main文件从虚拟机拷贝到windows环境下,再通过FileZilla Client上传到树莓派:
在树莓派上给该文件加上可执行权限并运行:
输入整数5,和字符串”abcdefghijklmn”
看到确实显示出了前5个字符”abcde”如下所示:
- Lab 2:ARM指令
- 嵌入式LAB 2:ARM指令
- ARM指令详解2
- ARM指令学习2
- ARM汇编指令2
- ARM指令集2
- ARM指令集2
- ARM 汇编指令学习:[2]ARM指令集
- ARM汇编语言指令(2)
- ARM 汇编指令(2)
- ARM汇编语言指令(2) .
- ARM指令集详解2
- ARM汇编指令集2
- 【mini2440】ARM汇编指令2
- lab 2
- Lab 2
- lab 2
- LAB 2
- 让我们从0开始 知识_1
- Android ramdisk.img system.img userdata.img介绍与使用
- 检查进程是否在运行
- Makefile自动生成:cmake
- Background Modeling and Foreground Detection -- SOBS
- Lab 2:ARM指令
- "围观"设计模式(20)--行为型之策略模式(Strategy Pattern)
- IDEA, Webstorm, PyCharm, RubyMine, PhpStorm and AppCode 更换主题
- 飞机大战制作笔记6
- UVA - 247 - Calling Circles(floyd算法)
- winform中常用的控件
- Android Studio 使用gradle进行编译(附)
- Activiti中文说明文档
- zookeeper dubbo 集群