汇编基础语法

指令格式

1、立即数

一个常数,该常数必须对应8位位图,即一个8位的常数通过循环右移偶数位得到该数,该数位合法立即数

在指令中表示方法:#数字,例如:#100

快速判定是否是合法立即数:

首先将这个数转换为32bit的16进制形式,例如218=0xDA=0x000000DA

①除零外,仅有一位数为合法立即数

②除零外,仅有二位数,并且相邻(包括首尾)的为合法立即数

③除零外,仅有三位数,并且相邻(包括中间有0,例如0x10800000,包括首尾相邻),这三位数中,第一位仅能为1、2、3最低位仅能为4、8、c,中间位0x0~0xf

这种组合的为合法立即数

2、寄存器移位

将寄存器值读取后,进行移位运算后,作为操作数2参与运算。支持的移位方式如下:

LSL(Logical Shift Left) 逻辑左移

LSR(Logical Shift Right)逻辑右移

ASR(Arithmetic Shift Right)算术右移

复制代码
r0,lsr #4 表示r0>>4
r0,lsr r1 表示r0>>r1
#3,lsl #4 错误,只能是寄存器移位,不能是立即数移位

常用ARM核指令

1、数据传送指令

(1)MOV指令

复制代码
格式:mov 目标寄存器,操作数
功能:将操作数的值赋值给目标寄存器
mov r0,#12
mov r1,r0
mov r1,r0,lsl #2 //r1=r0<<2

(2)MVN指令

复制代码
格式:mvn 目标寄存器,操作数2
功能:将操作数2取反的值给目标寄存器
mvn r0,#0 //r0=~0  =>r0=0xffff,ffff

(3)LDR指令

复制代码
格式:LDR 目标寄存器,数据
功能:完成任意的数据传送到目标寄存器
注意:数据前面不能加#,因为此时数据不按立即数来处理

举例:
LDR r0,=0x12345678

2、数据计算指令

(1)ADD指令

复制代码
格式:add 目标寄存器,操作数1,操作数2
功能:将操作数1加上操作数2的结果放在目标寄存器中
举例:
add r0,r1,#3 //r0=r1+3
add r0,r1,r2//r0=r1+r2
add r0,r1,r2,lsl #2//r0=r1+( r2<<2 )

(2)SUB指令

复制代码
格式:sub 目标寄存器,操作数1,操作数2
功能:将操作数1减去操作数2的结果放在目标寄存器中

(3)MUL指令

复制代码
格式:mul 目标寄存器,操作数1,操作数2
功能:将操作数1乘以操作数2的结果放在目标寄存器中
注意:操作数1和操作数2必须都是寄存器,并且操作数1的寄存器编号不能和目标寄存器一样
举例:mul r0 ,r0 ,#3//error r0=r0*3

3、位运算指令

(1)AND指令

复制代码
格式:and 目标寄存器,操作数1,操作数2
功能:将操作数1按位与操作数2,结果放在目标寄存器中

(2)ORR指令

复制代码
格式:orr 目标寄存器,操作数1,操作数2
功能:将操作1按位或操作2的结果存放在目标寄存器中
举例:orr r0,r1,r2 //r0=r1|r2
     orr r0,r1,#10 //r0=r1|10
     orr r0,r1,r2,lsl #2 //r0=r1|(r2<<2)

(3)EOR指令

复制代码
格式:eor 目标寄存器,操作1,操作2
功能:将操作数1按位异或操作数2的结果存放在目标寄存器中
举例:
eor r0,r1,r2//r0=r1^r2
eor r0,r1,#10//r0=r1^10
eor r0,r1,r2,lsl #2 //r0=r1^(r2<<2)

(4)BIC指令

复制代码
格式:bic目标寄存器,操作1,操作2
功能:将操作数1按位与操作数2取反的结果存放在目标寄存器中
目标寄存=操作数1& ~操作数2

将0x55中所有的1位清零

复制代码
.global _start
	
_start:
   mov r0,#0x55  @ 0101 0101
   bic r1,r0,#0x55
stop:
	b stop

4、比较指令

复制代码
格式:cmp 寄存器 ,操作数2
功能:将寄存器的值与操作2比较,比较的结果会自动影像CPSR的NZCV

 .global _start
_start:
   mov r0,#10
   mov r1,#20
   cmp r0,r1
   addgt r0,r0,#1 //如果r0>r1 则执行这行代码
   

   
stop:
	b stop

5、跳转指令

复制代码
格式:B/BL  标签
功能:跳到一个指定的标签,BL跳转之前,将跳转前PC的值保存在LR,跳转范围+/- 32M

.global _start
_start:
   mov r0,#10
   mov r1,#20
   b add_label //跳到add_label标签,然后并不会执行下面的语句
   mov r3,#10

   
stop:
	b stop
	
add_label:
   add r0,r0,r1

如果跳转之后,还想跳转回来,有两种办法

①使用bl指令,因为bl跳转之前,将跳转前的pc的值保存在lr,跳转范围+/-32M

复制代码
.global _start
_start:
   mov r0,#10
   mov r1,#20
   bl add_label
   mov r3,#10

   
stop:
	b stop
	
add_label:
   add r0,r0,r1
   mov pc,lr

②使用ldr直接赋值

复制代码
.global _start
_start:
   mov r0,#10
   mov r1,#20
   ldr pc,=add_label //直接给pc赋值
back_label:
   mov r3,#10

   
stop:
	b stop
	
add_label:
   add r0,r0,r1
   ldr pc,=back_label //给需要跳转回来的标签赋值

练习:实现1-100累加和

复制代码
.global _start
_start:
   mov r0,#0
   mov r1,#1

   
loop:
   add r0,r0,r1 //r0=r0+r1
   add r1,r1,#1 //r1+1
   cmp r1,#100 //r1与100比较
   ble  loop //r1<=100则跳进循环loop
   
stop:
	b stop

6、内存访问指令

复制代码
LDR 将内存中的值加载到寄存器中(读内存)
STR 将寄存器中的值写入内存中(写内存)

寄存器间接寻址:寄存器的值是一个地址
LDR r0,[r1] //r0=*r1
STR r0,[r1] //*r1=r0

.global _start
_start:
   mov r0,#0x40000000
   ldr r1,=0x12345678
   str r1,[r0]
   ldr r3,[r0]
  
stop:
	b stop

在debug->memory map中设置读写内存

复制代码
0x40000000,0x4000ffff
复制代码
基址变址寻址
A.前索引
STR r0,[r1,#4]//*(r1+4)=r0  =>地址先+4在赋值
LDR r0,[r1,#4]//r0=*(r1+4)  =>地址先+4在写入寄存器中 
r1的值不变

B.后索引
STR r0,[r1],#4  //*r1=r0&&r1=r1+4  =>先写入内存,内存地址再+4
LDR r0,[r1],#4  //r0=*r1 &&r1=r1+4 =>先从内存中读取,内存地址在+4
r1的值改变

C.自动索引
STR r0,[r1,#4]! //*(r1+4)=r0 &&r1=r1+4 =>将r0中的值写入到(r1+4)中并且内存地址+4

LDR r0,[r1,#4]! //r0=*(r1+4)&&r1=r1+4 =>将内存地址+4的值赋值给r0,并且内存地址+4

多个数据访问

复制代码
LDM 将一块内存的数据,加载到多个寄存器中
STM 将多个寄存器的值,存储到一块内存
格式:
    LDM{条件} <MODE> 基址寄存器{!},{Reglist}^
    STM{条件} <MODE> 基址寄存器{!},{Reglist}^
参数:
   MODE:-IA 后增加地址
        -IB 先增加地址
        -DA 后减少地址
        -DB 先减少地址
   基址寄存器:用来存放内存的起始地址
   !:最后更新基址寄存器的值
   Reglist:多个寄存器,从小到大,中间用','隔开 如{r0,r1,r3} 或{r0-r2}
   ^:它存在,如果reglist没有pc的时候,这个时候如果操作的寄存器是用户模式下的寄存器,在LDM指令中,有pc的时候,在数据传送的时候,会将SPSR的值拷贝到CPSR,用于异常的返回

栈操作指令:

进栈:stmfd sp!,{寄存器列表}

​ ldmfd sp!,{寄存器列表}

注意:在对栈操作之前,必须先设置sp的值,进栈和出栈的方式一样,ATPCS标准规定满减栈(从高往低)

复制代码
.global _start
	@0x40000000,0x4000ffff
_start:
   ldr sp,=0x4000fff0  //设置高位
   mov r0,#0x11
   mov r1,#0x22
   mov r2,#0x33
   stmfd sp!,{r0,r1,r2} //从高位先入栈
   mov r0,#0
   mov r1,#0
   mov r2,#0
   ldmfd sp!,{r0,r1,r2}
   

   
stop:
	b stop

7、CPSR/SPSR操作指令

读操作:MRS rn ,CPSR/SPSR

​ 将状态寄存器的值,读到通用寄存器中

写操作:MSR CPSR/SPSR,rn

​ 将通用寄存器的值,写到状态寄存器中

汇编与C混合编程

汇编内嵌C

复制代码
.global _start
	@0x40000000,0x4000ffff
_start:  
   mov r0,#2
   mov r1,#3
   ldr sp,=0x4000fff0 //设置栈sp指针的偏移量
   bl add
   
stop:
	b stop
	

int add(int a ,int b){
	int c=a+b;
	return c;
}

C内嵌汇编

复制代码
asm(
"指令1\n"
"指令2\n"
...
:输出列表
:输入列表
:修改列表(通用的寄存器)
);
指令:ARM汇编指令
输出列表:将内嵌汇编中的寄存器值输出到c变量中
输入列表:将C变量输入到内联汇编中使用的寄存器
修改列表:内联汇编中修改的寄存器
修饰符 说明
被修饰的操作符是只读的
= 被修饰的操作符是只写的
+ 被修饰的操作符具有可读写的属性
& 被修饰的操作符只能作为输出
复制代码
.global _start
	@0x40000000,0x4000ffff
_start:  
   mov r0,#2
   mov r1,#3
   ldr sp,=0x4000fff0
   bl add
   
stop:
	b stop
	

int add(int a ,int b){
int c;
	asm(
	"add r0 %1,%2\n",
	"mov %0,r0\n"
	//从输出列表到输入列表开始编号
	:"=r"(c)//%0
	:"r"(a),"r"(b) //%1 %2
	:"r0"
	);
	
	return c;
}

ARM的异常源

相关推荐
浩浩测试一下1 天前
汇编 16位32位64位通用寄存器(逆向分析)
汇编·windows·stm32·单片机·嵌入式硬件·逆向·二进制
浩浩测试一下2 天前
汇编常用的(JCC 串 判断)指令 通用寄存器 标志寄存器 段寄存器(逆向分析)
汇编·通用寄存器·逆向二进制·标志寄存器·段寄存器·串 jcc 常用指令
浩浩测试一下2 天前
汇编 标志位寄存器 (逆向分析 )
c语言·汇编·逆向·windows编程·标志寄存器
浩浩测试一下2 天前
汇编 数组与串指令(逆向分析)
汇编·逆向·二进制·免杀·串指令·汇编数组
浩浩测试一下3 天前
汇编 内联汇编与混合编程 (逆向分析)
汇编·混合编程·windows编程·内联汇编·二进制逆向·c语言混合汇编
浩浩测试一下3 天前
汇编 结构体与宏
汇编··免杀·结构体·windows编程·逆向二进制
浩浩测试一下3 天前
汇编中的JCC指令 (逆向分析)
汇编·逆向·标志位·jcc指令·跳转指令·标志位寄存器
浩浩测试一下3 天前
汇编中的段与段寄存器(大小)段序 (逆向分析)
汇编·逆向·二进制·字节序·windows编程·内存地址排序
浩浩测试一下4 天前
汇编 call与ret 函数与堆栈 (逆向分析)
汇编·push·函数·pop·call·ret·堆栈逆向
山屿落星辰5 天前
昇腾NPU算子开发:从“手写汇编“到“搭积木“
汇编