ARM汇编

Posted Lewin~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ARM汇编相关的知识,希望对你有一定的参考价值。

汇编指令和伪指令的区别

汇编指令是cpu机器指令的助记符,代表这个cpu的功能。
伪指令本质不是指令,只是和汇编指令写在同一个代码中,它由编译器提供,用于指导编译过程,经过编译后伪指令最终不会生成机器码,不同的编译器提供的伪指令不同。
所以,伪指令和指令的根本区别是经过编译后会不会生成机器码

两种不同风格的ARM指令

Windows中IDE开发环境(如ADS、MDK等)常用: LDR R0, [R1]
linux中常用:ldr r0, [r1]
可以看到,不同环境下编写的汇编指令虽然只有大小写的区别,但编写的伪指令却大有不同,因为不同的编译器提供的伪指令不同。

ARM汇编的5种特点

ARM汇编特点1:LDR/STR架构
ldr:load register 内存–>寄存器
str:store register 寄存器–>内存
ARM采用RISC架构,cpu本身不能直接访问内存(CISC架构的cpu可以),
需要先将内存中的内容装载入(load)cpu中的通用寄存器(register)后才能被cpu处理,例如在内存中修改某一值,需要先将其ldr寄存器,然后修改寄存器的内容,最后将修改后的值str内存。
(ARM CPU通过ldr/str指令完成内存的数据交换)

ARM汇编特点2:8种寻址方式

寄存器寻址			mov r1, r2
立即寻址				mov r0, #0xFF00
寄存器移位寻址		mov r0, r1, lsl #3
寄存器间接寻址		ldr r1, [r2]
基址变址寻址			ldr r1, [r2, #4]
多寄存器寻址			ldmia r1!, r2-r7, r12
堆栈寻址				stmfd sp!, r2-r7, lr
相对寻址		     	beq flag

ARM汇编特点3:指令后缀
同一指定附带不同的后缀,功能略有差异
B(byte)功能不变,操作长度变为8位
H(half word)功能不变,长度变为16位
S(signed)功能不变,操作数变为有符号
如 ldr:读四个字节到register,ldrb:读一个字节到register
ldrh:读两个字节到register ldrsb:读有正负区分的一个字节到register
ldrsh:读有正负区分的两个字节到register
S(S标志)功能不变,影响CPSR标志位
如 mov和movs movs r0, #0
mov r0, #0,不影响CPSR寄存器,执行完后Z位依然不变
movs r0, #0,影响CPSR寄存器,执行完后Z位置1

ARM汇编特点4:条件执行后缀

ARM汇编特点5:多级指令流水线
cpu是以流水线的方式工作的,流水线的级数越多,意味着工作在流水线上的工人越多,程序执行的效率就越高,这里以三级流水线为例子,三级分为取指、译码、执行三级,每个时钟周期都会同时对每个操作处理一次,而非顺序执行

为增加处理器指令流的速度,ARM使用多级流水线(S5PV210使用13级流水线,ARM11为8级),虽然流水线的级数增加了,cpu效率也提高了,但是也隐含着缺点,流水线上的工人越多,等待所有工人都进入工作的时间就越长,当程序执行b跳转命令时,流水线会被清空,此时需要等待一定的时间,流水线才会再次进入正常工作,这里的正常工作指流水线上的每个工人都没有偷懒。

综上可知,ARM CPU是32位的,那么取指时取出的指令也是32位(4Byte)的,程序计数器PC指向正被取指的指令,而非正在执行的指令,正在执行的指令的地址为pc-8。在正常操作过程中,在执行一条指令的同时对下一条指令进行译码,并将第三条指令从存储器中取出。
处理器处于ARM状态时,每条指令为4个字节,所以PC值为正在执行的指令地址加8字节,即是:
PC值 = 当前程序执行位置 + 8字节
处理器处于Thumb状态时,每条指令为2字节,所以PC值为正在执行的指令地址加4字节,即是:
PC值 = 当前程序执行位置 + 4字节

ARM汇编中的立即数

立即数分为合法立即数和非法立即数,一条ARM指令是32位的,这32位包括:操作码、操作数、以及一些状态标志位,这就使得有些数是不能写进这32位中的,能写进的称为合法立即数,不能写进的称为非法立即数。
合法立即数:经过任意位数的移位后非零部分可以用8位表示的即为合法立即数(非零部分少于或等于8位),相反的非法立即数的非零部分超过8位。

协处理器和协处理器操作指令

协处理器操作指令:mcr & mrc
mrc用于读取CP15中的寄存器
mcr用于写入CP15中的寄存器

什么是协处理器?
SoC内部另一处理核心,协助主CPU实现某些功能,被主CPU调用执行一定任务。ARM设计上支持多达16个协处理器,但是一般SoC只实现其中的CP15.(cp:coprocessor),协处理器和MMU、cache、TLB等处理有关,功能上和操作系统的虚拟地址映射、cache管理等有关。

ldm/stm与栈的处理

ldm/stm:批量读写内存指令,相较于ldr/str的区别是,ldr/str一次访存周期只能读写1个寄存器(4Byte),而ldm/stm一次访存周期能读写多个寄存器,大大提高效率。
举例:
stmia sp, r0 - r12
将r0存入sp指向的内存处(假设为0x30001000);
然后地址+4(即指向0x30001004),将r1存入该地址;
然后地址再+4(指向0x30001008),将r2存入该地址
······
直到r12内容放入(0x3001030),指令完成。

8种后缀
ia(increase after)先传输,再地址+4
ib(increase before)先地址+4,再传输
da(decrease after)先传输,再地址-4
db(decrease before)先地址-4,再传输
ea(·······)空递增堆栈,和ia同效果
fa(·······) 满递增堆栈,和ib同效果
ed(empty decrease)空递减堆栈,和da同效果
fd(full decrease)满递减堆栈,和db同效果

四种栈
空栈:栈指针指向空位,每次存入时可以直接存入然后栈指针移动一格;而取出时需要先移动一格才能取出
满栈:栈指针指向栈中最后一格数据,每次存入时需要先移动栈指针一格再存入;取出时可以直接取出,然后再移动栈指针
增栈:栈指针移动时向地址增加的方向移动的栈
减栈:栈指针移动时向地址减小的方向移动的栈

!的作用
ldmia r0, r2 - r3
ldmia r0!, r2 - r3
感叹号的作用就是r0的值在ldm过程中发生的增加或者减少最后写回到r0去,也就是说ldm时会改变r0的值。

^的作用
ldmfd sp!, r0 - r6, pc
ldmfd sp!, r0 - r6, pc^
^的作用:在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用于从异常模式返回。

总结
批量读取或写入内存时要用ldm/stm指令
各种后缀以理解为主,不需记忆,最常见的是stmia和stmfd
谨记:操作栈时使用相同的后缀就不会出错,不管是满栈还是空栈、增栈还是减栈

gnu汇编中的一些符号

@ 用来做注释,和C语言中//类似
:以冒号结尾的是标号
. 点号在gnu汇编中表示当前指令的地址
C语言中的死循环

while(1);

汇编中的死循环

b .
	或 
flag: 
	b flag

#立即数前面要加#或$,表示这是个立即数,即数字

常用gnu伪指令

.global _start				@ 给_start外部链接属性
.section .text				@ 指定当前段为代码段
.ascii .byte .word .string 	@ 定义数据
.align 4					@ 以2^4 = 16字节对齐
.balignl 16 0xabcdefgh 		@ 16字节对齐填充
/*
 *.balignl 16 0xabcdefgh解析
 *0008:上一条指令
 *@;上一条指令完成后出现.balignl 16 0xabcdefgh
 *则直到出现”16字节对齐的地址“之前的地址的内容都会被填充0xabcdefgh
 *000c:0xabcdefgh
 *0010:下一条指令		@;二进制的0010=十进制的16
 */

ARM中有一个ldr指令,还有一个ldr伪指令
一般都使用ldr伪指令而不用ldr指令,因为ldr指令常需要判断后面的立即数合不合法,而ldr伪指令不需要判断,编译后会自动生成对应的arm指令。
举例:

ldr r0, #0xff		@;是指令,
					@;0xff1的部分的个数为8 = 8->是合法立即数,
					@;编译正常
ldr r0, #0xfff		@;是指令,
					@;0xfff1的部分的个数为12 > 8->是非法立即数,
					@;编译报错
ldr r0, =0x12345678	@;不是指令,
					@;无需判断是否合法
					@;编译正常

adr与ldr伪指令的区别
ldr 大范围的地址加载指令
adr 小范围的地址加载指令
区别1:加载的立即数的范围不同
区别2:编译后转化成的ARM指令不同,adr编译时会被1条sub或add指令替代,而ldr编译时会被一条mov指令替代或者文字池方式处理;
区别3:adr总是以PC为基准来表示地址,因此adr指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里
ldr加载的地址和链接时给定的地址有关,由链接脚本决定
综上所述:adr与ldr伪指令的根本区别:adr加载的地址在运行时确定,ldr加载的地址在链接时确定,所以我们可以通过比较adr和ldr加载的地址来判断程序是否在编写链接脚本时指定的地址运行。

以上是关于ARM汇编的主要内容,如果未能解决你的问题,请参考以下文章

arm 汇编指令

优化系列汇编优化技术:ARM架构32位汇编优化及demo

Android 逆向arm 汇编 ( 使用 IDA 解析 arm 架构的动态库文件 | 分析 malloc 函数的 arm 汇编语言 )

Android 逆向arm 汇编 ( 使用 IDA 解析 arm 架构的动态库文件 | 分析 malloc 函数的 arm 汇编语言 )

c语言转化为arm汇编指令

嵌入式:ARM内嵌汇编及C和ARM汇编相互调用