day09 ARM汇编指令
2019-07-13 19:17发布
生成海报
回顾:
面试题:谈谈对ARM处理器的认识
1.ARM编程基础
1.1.谈谈常见的处理器
x86/powerpc/dsp/fpga/mips(北京君正)/arm/单片机
1.2.ARM的定义
一种处理器架构
ARM公司设计CPU核(ARM核),IP授权
不生产处理器
1.3.ARM的版本(ARM核的版本)划分
大版本:
小版本:
1.4.ARM的三级指令流水线和五级指令流水线
ARM7(小版本):采用三级指令流水线
F->D->E
特例:
ldr:F->D->E->M->W
bl:F->D->E->L->A
中断:DI->EI->L->A
CPU核执行完指令以后才会去处理中断
ARM9(小版本以后):采用五级流水线F->D->E->M->W,除了ldr有M过程,其余指令一律没有M动作
画出三级或者五级流水线的处理示意图
1.5.ARM核的7种工作方式
SVC管理/FIQ/IRQ/Abort/Undef/System/User
明确:CPU核切换到对应模式的场景
1.6.ARM核的2种工作状态
ARM状态
ARM指令/32位
Thumb状态
Thumb指令/16位
1.7.ARM核的37个寄存器
画出37个寄存器在7种工作模式下的分布图
r0...r13(sp),r14(lr),r15(pc),cpsr(nzcviftmode),spsr
1.8.展示ARM汇编的编程格式 .text .code 32 /.code 16 .global start start: 一堆的指令
.end 1.9.ARM编程涉及的条件助记符 eq/ne/cs/cc 影响cpsr的nzcv的两种情形:指令后面加s。cmp指令不用加也影响 。 2.ARM异常
<>第二版,杜春雷
2.1.何为ARM核(CPU核)的"跳转"?
答:CPU核跳转是指CPU核到某个地址去"运行"(取指,解码,执行,(访存),写回)
2.2.何为"异常"? 答:异常不是预期的事情,具有随机性
2.3.ARM核的7种异常
问:一旦ARM的某种异常发生,ARM核要切换到什么模式
ARM核跑到哪个地址去运行(F->D->E->M->W)
答:通过一下表格来进行汇总
只需画出一下表格即可:
异常 工作模式 CPU核运行地址 场景
复位异常 SVC管理模式 0x00 系统复位
Undef未定义 Undef未定义 0x04 CPU核执行一个
指令异常 指令模式 不认识的指令
软中断异常 SVC管理模式 0x08 CPU核执行swi指令
预取指令 Abort中止模式 0x0C CPU核取指失败
异常 数据处理 Abort中止模式 0x10 CPU核访存失败
异常 IRQ中断 IRQ中断模式 0x18 外设给CPU核发送IRQ中断电信号
异常 FIQ中断 FIQ快速中断模式 0x1C 外设给CPU核发送FIQ快速中断电信号
异常 注意: 1.一旦异常发生,CPU核会毫无条件的必须的强制的跑到对应的
入口地址(0x00/0x04/0x08/0x0c/0x10/0x18/0x1c)去运行
2.4.问:ARM核一旦触发某个异常,ARM核如何处理异常呢?
答:以UART控制器给CPU核发送IRQ中断电信号为例,
理清IRQ异常的处理流程:
1.当UART控制器准备好数据以后,首先会给
中断控制器发送一个中断电信号
2.中断控制器接收到这个中断电信号以后
经过一番的判断之后,中断控制器最终
以IRQ的形式给CPU核发送一个IRQ中断电信号
3.CPU核一旦接收到IRQ中断电信号,就会触发
一个IRQ异常,CPU核立马要处理这个IRQ异常
3.1.CPU核硬件上自动完成以下工作:
1.把当前程序对应的cpsr的值保存到IRQ中断模式下的spsr_irq
问:为什么保存cpsr到spsr_irq
答:为了将来IRQ异常处理完毕返回到原先
被打断的地方
2.设置cpsr,将cpsr的:
bit[4:0]=IRQ工作模式对应的值,让CPU核
切换到IRQ中断模式
bit[5]=0,让ARM核切换到ARM状态下
bit[7:6]=11,禁止ARM核响应IRQ和FIQ中断
3.保存返回地址:lr=pc-4 问:为什么是lr=pc-4呢? 答:例如:
内存地址 指令
0x8000 add
0x8004 sub
0x8008 orr
当add指令执行的时候:pc=0x8008
当add指令执行的时候,触发IRQ异常
将来IRQ异常处理完毕,CPU核返回到0x8004
去运行,所以lr=pc-4=0x8008-4=0x8004
4.设置pc=0x18
问:为什么0x18呢?
答:因为IRQ异常处理的入口地址为0x18
问:pc=0x18代表什么含义呢?
答:本质就是让CPU核跑到0x18地址去
运行(F->D->E->M->W)
举一反三:
pc=0x48000000:让CPU核跑到0x48000000去运行
pc=uart_init:让CPU核跑到uart_init函数对应的
地址去运行
问:0x18地址放什么东西?
答:0x18势必放自己编写的软件代码
例如:add指令可以放到0x18地址
切记:只要给pc赋值(地址),那么CPU核就会
跑到对应的地址去运行
结论:一旦ARM核做pc=0x18,让CPU核跑到0x18
地址去运行,至此正式开启了软件进一步
处理IRQ异常的流程
3.2.软件处理IRQ异常的流程:
软件如何处理IRQ异常完全由程序员自行决定
软件处理IRQ异常结束以后,最后让CPU
核返回到原先被打断的地方(某个程序)
继续执行,软件只需完成一下两个动作即可
实现返回:
cpsr=spsr_irq:将IRQ模式下的spsr的值归还给cpsr
spsr_irq保存原先被打断
的程序运行的状态
pc=lr:lr保存的返回地址,让CPU核跑到
原先被打断的地方继续执行
至此:IRQ异常处理完毕!
2.5.切记切记:ARM核跳转的方式有三种
1.ARM核发生异常,一旦发生异常,ARM核跑到
对应的异常入口地址去
2.ARM核执行b或者bl跳转指令
3.直接向pc赋值 此法最给力!
3.ARM指令集之分支跳转指令:b/bl/bx
1.b为不带返回的跳转指令 跳转范围相对PC,+-32MB
b loop 无条件跳转 beq loop 有条件跳转 2.bl为待返回的跳转指令 跳转范围相对PC,+-32MB
切记:当CPU核执行bl指令时,CPU核硬件上自动将
下一条指令的地址保存在lr中 所以将来软件返回的指令为:mov pc, lr
例如: 内存地址 指令 0x8000 bl strcmp @当bl执行时,pc=0x8008,lr=0x8004
0x8004 add r0, r0, r1
0x8008 sub r0, r0, r1
strcmp: 一大堆指令
mov pc, lr @pc=lr=0x8004,让CPU核跑到0x8004去运行
案例:分析一下代码的漏洞 init: bl main b .
main: stmfd sp!, {lr} @将lr压栈
bl func1
ldmfd sp!, {pc} @出栈,pc=lr,实现返回 func1: bl func2
mov pc, lr
func2: mov pc, lr
3.bx带状态切换的指令(了解) bx跳转的地址的bit[0]=0:切换到ARM状态
bx跳转的地址的bit[0]=1:切换到Thumb状态
例如: ldr r0, =0x48000000
bx r0 @让CPU核切换到ARM状态并且跳转到0x48000000去运行
ldr r0, =0x48000001
bx r0 @让CPU核切换到Thumb状态并且跳转到0x48000001去运行
案例:编写ARM汇编代码,实现两个数的减法
实施步骤: 1.上位机(自己安装的纯系统)安装qemu模拟器,用此模拟器来模拟一个ARM核
sudo apt-get install qemu
2.达内虚拟机已经安装完毕,无需安装
3.编写测试 mkdir /opt/arm/day09/1.0 -p
cd /opt/arm/day09/1.0
vim do_sub.s 添加如下内容
.text .code 32 .global _start _start: mov r0, #0 @r0=0
mov r1, #2 @r1=2
mov r2, #9 @r2=9
subs r0, r1, r2 @r0=r1-r2=2-9=-7
b . @死循环
.end 保存退出 arm-cortex_a9-linux-gnueabi-as -g -o do_sub.o do_sub.s
说明: arm...as:汇编器,将汇编文件编译生成目标文件
-g:添加调试信息 arm-cortex_a9-linux-gnueabi-ld -o do_sub do_sub.o
在一个终端中执行: cd /opt/arm/day09/1.0
qemu-arm -g 1234 do_sub //执行qemu-arm模拟器
将来用于运行do_sub
并且指定端口号为1234
此时此刻,命令卡主不动! 再打开一个新的终端执行: cd /opt/arm/day09/1.0
arm-cortex_a9-linux-gnueabi-gdb do_sub //调试do_sub程序
出现“(gdb)”命令提示符,输入一下命令调试do_sub
(gdb)target remote localhost:1234 //让gdb连接qemu
(gdb)l //查看代码 (gdb)b 5 //在第五行设置一个断点
(gdb)s //让CPU执行下一条指令,单步执行
(gdb)info reg //查看ARM寄存器的值
寄存器 寄存器值(10) 寄存器值(16)
r0 0 0
r1 ... ...
r2 ... cpsr 各种s,各种info reg 最终查看r0是否为-7,cpsr的bit31是否为1
(gdb)quit //退出gdb 4.ARM指令集之数据处理指令
数据处理指令又分四类: 数据传送指令 算数运算指令 位运算指令 比较指令 4.1.数据处理指令的移位操作符 lsl(左移)/lsr/asr/ror/rrx
4.2.数据处理指令之数据传送指令:mov/mvn
注意:mov和mvn指令操作的立即数的范围为0x00~0xff
例如: mov r0, #0xff @合法,编译通过
mov r0, #0x1ff @不合法,编译不通过
mov r0, #0xffffffff @不合法
mvn r0, #0x0 @合法,r0=0xffffffff
moveqs r0, r1
说明: 1.判断cpsr的z位是否为1
如果为1条件成立,执行mov
把r1的值给r0
2.又由于指令后面加了s,根据运算结果r0
影响cpsr的nzc
mvneqs r0, r1
说明: 1.判断cpsr的z位是否为1
如果为1条件成立,执行mov
把r1的值取反给r0
2.又由于指令后面加了s,根据运算结果r0
影响cpsr的nzc
4.3.切记切记切记:影响cpsr的两种情形 1.指令后面加s,并且目标寄存器是pc,将来的
运算过程为: cpsr=spsr:实现程序状态的恢复
pc=地址:实现CPU核的跳转
问:何为目标寄存器呢?
答:离指令最近的寄存器
例如: moveqs pc, lr
1.首先判断当前cpsr的z位是否为1
如果为1条件成立,执行mov
就是将lr的值给pc,即:pc=lr
本质实现返回,实现CPU核的跳转
2.又由于目标寄存器是pc,并且指令
后面加了s,最后还要实现把当前工作模式
下的spsr赋值给cpsr,即:cpsr=spsr_mode
本质实现程序状态的恢复
2.指令后面加s(除了比较指令),但是目标寄存器不是pc
运算的结果仅仅影响cpsr的nzcv位
例如: moveqs r0, r1
1.首先判断当前cpsr的z位是否为1
如果为1条件成立,执行mov
就是将r1的值给r0
2.指令后面加了s,根据运算结果r0影响
cpsr的nzc位
4.3.数据处理指令之算数运算指令 add:不带进位的加法指令
adc:带进位的加法指令 例如: add r0, r0, r1 @r0=r0+r1
adc r0, r0, r1 @r0=r0+r1+cpsr的c位的值
例如:实现两个64位数的加法
低32位 高32位
数据A r0 r1
数据B r2 r3
答案: adds r0, r0, r2
adc r1, r1, r3
sub:不带借位的减法指令
sbc:带借位的减法指令
例如: sub r0, r0, r1 @r0 = r0-r1
sbc r0, r0, r1 @r0 = r0-r1-cpsr的c
4.4.数据处理指令之位运算指令:and/orr/eor/bic
例如:biceqs r0, r0, #0x07 @r0=r0 & ~(0x07)
4.5.数据传送指令之比较指令:cmp/cmn/tst/teq
cmp本质做减法运算
cmp r0, r1 @运算结果=r0-r1
cmn本质做加法运算
cmn r0, r1 @运算结果=r0+r1
tst本质做位与运算
tst r0, r1 @运算结果=r0&r1
teq本质做位异或运算
teq r0, r1 @运算结果=r0^r1
比较指令后面不加s,运算结果同样影响cpsr的nzc位
5.案例:编写汇编代码,实现:10+9+8+0
实施步骤: mkdir /opt/arm/day09/2.0 cd /opt/arm/day09/2.0
vim sum.s 添加如下内容 .text .code 32 .global _start
_start: mov r0, #0
mov r1, #10
_sum: add r0, r0, r1
sub r1, r1, #1
cmp r1, #0
bne _sum
b . .end 保存退出 arm-cortex_a9-linux-gnueabi-as -g -o sum.o sum.s
arm-cortex_a9-linux-gnueabi-ld -o sum sum.o
一个终端输入: cd /opt/arm/day09/2.0
qemu-arm -g 1234 sum
另一个终端输入: arm-cortex_a9-linux-gnueabi-gdb sum
(gdb)target remote localhost:1234
(gdb)l (gdb)b ... (gdb)s (gdb)info reg
(gdb)s ... 最终查看r0的值 6.案例: 编写汇编代码,实现求两个数的最大公约数
求两个数的最大公约数的算法: 20 12 12 8
8 4
4 4
0
实施步骤: mkdir /opt/arm/day09/3.0
cd /opt/arm/day09/3.0
vim gcd.s 添加如下内容
.text .code 32 .global _start _start: mov r0, #12 mov r1, #20 loop: cmp r0, r1 beq loop_end
subcs r0, r0, r1
subcc r1, r1, r0
b loop loop_end: b . 保存退出 编译 qemu-arm+gdb进行测试
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮