#ARM汇编书本内容
- look at 70
- 条件列表 P35
- 降序或升序 p57
- 堆栈向下增长或向上增长,前者从一个高地址开始并向更低地址前进(降序 堆栈),而后者从一个低地址开始并向更高地址前进(升序 堆栈)。
- 满或空 p57
- 堆栈指针可指向堆栈中的最后一项(满 堆栈),也可指向堆栈中的下一个空闲空间(空 堆栈)。
- ARM使用加载-存储模式控制对内存的访问,这意味着只有加载/存储(LDR或者STR)才能访问内存。这就意味着如果要增加一个32位的在内存中的值,需要做三种类型的操作(加载,加一,存储)将数据从内存中取到寄存器,对寄存器中的值加一,再将结果放回到内存中。
#ARM有三十个32位通用寄存器
- ARM 状态下以一个字(存储单元)(四字节)为增量
- 所有 ARM 指令的长度都是 32 位。这些指令是按字对齐方式存储的(所以每条指令地址偏移4),因此在
ARM 状态下,指令地址的两个最低有效位始终为零。
register | function | 含义 |
---|---|---|
x0 - x7 | arguments and result registers | 参数和结果寄存器 |
x8 | indirect result (large struct) location | 间接结果(大结构)位置 |
x9 - x15 | scratch registers | 刮擦寄存器 想用就拿来用 |
x16 - x17 | intra call use registers (PTL, linker) | 内部调用使用寄存器(PTL,链接器),一个函数呼叫另一个函数,暂存资料 自己不要用 |
x18 | TLS | TLS |
x19 - x28 | saved registers | 保存的寄存器 函数呼叫后 保存内容 |
x29 | frame pointer | 帧指针 |
x30 | link register | 链接寄存器 |
x31 | stack pointer | 堆栈指针 |
ARM指令的基本格式
{} ,{,}
首选语法 {} ,{,}
MNEMONIC , Operand1, Operand2
助记符{是否使用CPSR}{是否条件执行以及条件} {目的寄存器}, 操作符1, 操作符2
- 格式中<>内容必不可少,{}中的内容可以省略
:表示操作码,指令 - {
}:表示指令执行的条件域,如EQ,NE等 - :决定指令执行结果是否影响CPSR的值,使用该后缀则指令执行的结果影响CPSR的值 ,否则不影响
:表示目的寄存器 :表示第一个操作数,为寄存器 :表示第二个操作数,可以是立即数,寄存器,或者寄存器移位操作数
ARM64下的寄存器
-
通用寄存器(用来存放一般性的数据),放通用数据,32bit
- x0~x30 (64位)
- x29 又名fp (用于保存栈底的地址)
- x30 又名lr 链接寄存器,存储子程序返回地址(bl跳转后就会把下一条指令地址写到lr中)
- w0~w30 (32位)它些就是x0~x30的低32位
- PC 程序计数器 ,指向当前指令地址(正在取指的指令)。由于CPU的三级流水(FDE)PC指向的是相对于执行的那条指令(E)第三条指令,pc=当前程序执行位置+4,或者+8,可以看成游标卡尺的游标
- 栈寄存器
- SP (任意时刻会保存我们栈顶的地址) 可用于切换进程
- FP = x29 (用于保存栈底的地址)
-
浮点寄存器(CPU中专门提供浮点数寄存器来处理浮点数)
- D0~D31(64位)
- S0~S31 (32位)这些就是D0~D31的低32位
-
向量寄存器 (现在的CPU支持向量运算.(向量运算在图形处理相关的领域用得非常的多)为了支持向量计算系统了也提供了众多的向量寄存器.
- V0-V31(128位)
-
状态寄存器(又称 CPSR【current program status register】寄存器),由32bit组成 youtube
- CPSR寄存器是32位的,每一位的功能如下 31 30 29 28 27~8 7 6 5 4 3 2 1 0 N Z C V 保留 I F T M4 M3 M2 M1 M0
- CPSR的低8位(包括I、F、T和M[4:0])称为控制位,程序无法修改,除非CPU运行于特权模式下,程序才能修改控制位! M有5位,控制CPU运行模式
- N【负数标志】、Z【0标志】、C【进位标志】、V【溢出标志】均为条件码标志位。
- 高4位31-28,条件标志
- 27-8, 保留位
- 后8位7-0, 控制位
31 30 29 28 27 ... 7 6 5 4 3 2 1 0 N Z C V Q ... I F T M4 M3 M2 M1 M0 - N:nagetive 正负标志,N=1表示运算结果为负,N=0表示运算结果为正或0
- Z:zero零标志, Z=1表示运算结果为0,Z=0表示运算结果为非0
- C carray
- 进位标志,加法运算产生了进位时则C=1,否则C=0;
- 借位标志,减法运算产生了借位则C=0,否则C=1;
- V:overflow 溢出标志,V=1表示有溢出,V=0表示无溢出
- Q:累计饱和instructions
- T:说明执行的指令是ARM指令(0),还是Thumb指令(1)
- F: 6,第7位 FIQ的缩写,是否禁用FIQ 为1的话表示不允许快速中断
- I: disable IRQ
- A: 8 是否disable异步abort
- E: 9,endian 操作储存的字节顺序,0=;1=
- 32位一个字长为4个字节
- 进行存取时都是按字长进行存取。一小片一小片的进行存取,不进行分割,存储到地址里面,字节的顺序,
- 低位存低地址,高位存高地址。小端
- 低位存高地址,高位存低地址。大端
- 10-15: 针对Thumb2指令
- 16-19: SIMD(单指令,多数据)指令使用
- 20-23: 保留
- 24: 是否jzalle状态
- 25-26: Thumb-2指令的if...then条件执行
#ARM64下常用的汇编指令
这里我只是简单整理一下,后面再写一下代码案例来介绍
####基础指令
- MOV - MOV X1,X0 ; 将寄存器X0的值传送到寄存器X1
- ADD - ADD X0,X1,X2 ; 寄存器X1和X2的值相加后传送到X0
- SUB - SUB X0,X1,X2 ; 寄存器X1和X2的值相减后传送到X0
- AND - AND X0,X0,#0xF ; X0的值与0xF相位与后的值传送到X0
- ORR - ORR X0,X0,#9 ; X0的值与9相或后的值传送到X0
- EOR - EOR X0,X0,#0xF ; X0的值与0xF相异或后的值传送到X0
####跳转操作
- 跳转有两种方式
- 使用跳转执令直接跳转
- 直接向PC寄存器赋值实现跳转
- BL 将下一条指令的地址放入lr(x30)寄存器
- RET 默认使用lr(x30)寄存器的值,通过底层指令提示CPU此处作为下条指令地址!
- CMP 比较指令,相当于SUBS,影响程序状态寄存器
- B.GT 比较结果是大于,执行标号,否则不跳转
- B.GE 比较结果是大于等于,执行标号,否则不跳转
- B.EQ 比较结果是等于,执行标号,否则不跳转
- B.HI 比较结果是无符号大于,执行标号,否则不跳转
- CBZ - CBZ ; 比较(Compare),如果结果为零(Zero)就转移(只能跳到后面的指令)
- CBNZ - CBNZ ; 比较,如果结果非零(Non Zero)就转移(只能跳到后面的指令)
#寻址,就是找地址ARM最核心的工作之一
- 立即数寻址, add x0, x1, #0x2f
- 寄存器寻址, add x0, x1, x2
- 寄存器间接寻址 load store
- ldr x0, [x1] //x1寄存器记录的内存地址
- str x0, [x1]
- stur x0, [x1, #-0x4] 多于负地址偏移寻址
- 寄存器移位寻址 add x3,x2,x1,lsl #2 //左乘右除
- 基地址寻址
- ldr x0, [x1, #4] //x1+4 的地址的值,读到x0 => x0=x1+4
- ldr x0, [x1], #4 //x1的地址的值+4后,给x0 => x1=x1+4;x0=x1
- ldr x0,[x1, x2] //x0=x1+x2,两个地址之和的地址的值
- 多寄存器寻址 ldmia x0,{x1,x2,x3,x4} 用的少一点 m代表多,ia立刻
- 相对寻址
- bl next //跳转到next,并把当前状态保存到 LR
- mov pc,lr //把lr给到pc,下一条指令是pc,相当于返回,跳转
#ARM汇编算术操作
####ADRP 是计算指定的数据地址 (eg:adrp x0, 1)
- 将1的值,左移12位 1 0000 0000 0000 == 0x1000
- 将PC寄存器的低12位清零 0x1002e6874 ==> 0x1002e6000
- 将1 和 2 的结果相加 给 X0 寄存器!
0x1002a6764 <+68>: adrp x8, 2
0x1002a6768 <+72>: add x8, x8, #0x88 ; =0x88
# x8 最后地址 = 2 << 12 + 0x1002a6000 + 0x88 = 0x1002a8088
####ARM汇编内存操作
####单寄存器读写指令
-
- 内存最小的单元是Byte,但是为了便于管理,存储器会分页,会用到另外一个单位叫字长word。在32位里1个word=4Byte,以字长为一个单位。
-
- ARM采用的是小端模式,低位放低地址,高位放高地址。先判断字空间(0-3,4-7这样分),起始位顺读1234,内存地址就是 4321,4是高位,1是低位 ,从右往左读就OK了
- ldr(load registor)把内存中的地址加载到寄存器 按字长操作
move x1, #0x12
ldr x0, [x1] - str (store registor) 把寄存器的值写入到内存地址 按字长操作
move x1, #0x0f
str x1, [x0, #ox0C] //写到 0c - ldrb 按字节操作
mov x1, #0x0f
strb x1, [x0, #0x0c]
ldrb x0, [x4, #0x2c] - ldrh 半字操作
- strh 半字存储
- ldrbt user模式下
- strbt
- ldrt 不带b表示字节
- strt
- ldrsb s-代表有符号 会影响CPSR
- ldrsh
- STR - 将数据从寄存器中读出来,存到内存中. STR - STR X0, [SP, #0x8] ;X0寄存器的数据传送到SP+0x8地址值指向的存储空间
- STP - STR 的变种指令,可以同时操作两个寄存器 STP x29, x30, [sp, #0x10] ; 将x29,x30存入栈中
- LDR - 将数据从内存中读出来,存到寄存器中 LDR X5,[X6,#0x08] ;X6寄存器加0x08的和的地址值内的数据传送到X5
- LDP - LDR 的变种指令,可以同时操作两个寄存器 LDP x29, x30, [sp, #0x10] ; 将栈中的值取出存放到x29, x30
####多寄存器的读写指令,操作一片连续内存
- ldm (load multiple) LDMIA(IA 地址模式) R0!(基址寄存器 -> 指向存储器, !:意思是最后把值保存到r0){R1,R2,R3或者 R1-R3} 意思是把R0的值加载到这一推寄存器里
- 感叹号的意思 是最后把值 保存到这个地方
- LDMIA R0(基址寄存器,表示基本地址),
- 地址模式;I-加法,D-减法 A-after B-before
- 数据块模式:IA(传输后地址加4个字节); IB(付送前地址加4);DA(传输后地址减4); DB(传送前减4)
- 堆栈模式:满底层堆栈
- STM (存储)
move x1, #0x00
stmia x1,
####数据交换指令
- swp (swap) swp r0, r1, [r2] 内存和寄存器字交换 r0相当于一个中间值
mov r1, #0x0f
mov r2, #0x12
swp r1, r1, [r2] - swpb 字节交换
####跳转指令,状态操作
在语言学习的时候,经常有函数调用,这个过程用指令来描述 就是跳转
实际上我们的跳转就是通过往PC计数器传入相应的值,来告诉下一步应该去哪执行
如果没有跳转指令的话,每次在想要跳转的地方都要写
mov lr, pc 长指令跳转
mov pc, x1
- B (break 跳转) 跳转过去 语法 B 标签
b sundy - BR 无条件跳转到寄存器地址
- BL 带返回的连接跳转
bl sundy
bl sundy 跳过去执行完了 再回来继续执行 - BX
- BLX
####状态寄存器操作
- mrs (move to registor form status registor) 把程序状态寄存器的值传送到通用寄存器
mrs x1, cpsr - msr (move to status registor form registor) 通用寄存器到程序状态寄存器
msr cpsr, x2
####异常产生指令
- swi 软中断指令
- bkpt 断点中断指令
####伪指令
我们的指令已经可以做各类操作了,但我们操作起来太麻烦了。
比如我现在要设置一个值给寄存器R0,但下次我修改了寄存器R0后又需要读出来刚才的值,那我们就要先临时
保存值 到SPSR or CPSR,然后为断切换
再比如,我们要做一个循环,就要用label结合BL 不断进行,但如果我们要循环很多次。
我们就定义了一些类似于带参数的宏的操作一样,来定义我们的伪指令,方便我们更好的实现汇编程序逻辑。伪指令只是在汇编器之前作用,汇编之后会翻译为标准的汇编指令集。
又分为ARM汇编伪指令和GUN汇编伪指令。
- 常用伪指令
- AREA 声明区域段,数据区,代码区等等
- CODE16/CODE32 声明以下是32位还是16位。
- ENITY 用于指定程序的入口点。程序至少得有一个 就像C语言的 main函数。
- END 用于通知编译器已经到了源程序的结尾
- EQU 用于为程序中的常量 定义一个标识符 SUNDYCON1 EQU 0x32000000
- EXPORT 导出标识,其他文件可以引用到
- IMPORT 相当于静态引用
- EXTERN 相当于动态引用
- GET 相当于引用文件 GET "file.S"
- RN 用于给一个寄存器定义一个别名,采用这种方式可以方便程序员记忆该寄存器的功能。 和EQU有点像
- 符号命名约定
- 符号区分大小写,同名的大,小写符号会被编译器认为是两个不同的符号
- 符号在其作用范围内必需唯一
- 自定义的符号不能与系统的保留字相同
- 符号名不应与指令或者伪指令同名
- 符号,变量
- GBLA/GBLL/GBLS gbla test3 定义全局数字/逻辑/字符串变量
- LBLA/LBLL/LBLS lbal test3 声明局部变量
- SETA/SETL/SETS test3 seta 0xaa 设定变量值
- RLIST(reglist RLIST {R0-R5,R8,R10}) 将寄存器列表名称定义为reglist,可以ARM指令LD,/STM中通过该名称访问寄存器列表
- $可以变量代换 ,有点像 shell $
#寄存器操作
- LDR 大范围寻址到寄存器-绝对寻址
- ADR 小范围寻址到寄存器 +- 255- 相对寻址
- ADRL 中范围寻址到寄存器- 相对寻址
#实际反编译中不懂的指令
- STP x29, x30, [sp, #0x10] ;入栈指令 //1.把x29 和 x30存方到sp-8 sp-10的位置 2.sp -= 0x18
- LDP x29, x30, [sp, #0x10] ;出栈指令 //1.将sp-8 sp-10位置的值取出来,放入x29 和 x30,2.sp += 0x18
- CBZ ;比较(Compare),如果结果为零(Zero)就转移(只能跳到后面的指令)
- CBNZ ;比较,如果结果非零(Non Zero)就转移(只能跳到后面的指令)
- CMP ;比较指令,相当于SUBS,影响程序状态寄存器CPSR status = op1 - (op2)
- CMN -- 比较取负的值 CMN R0, #1 @把R0与-1进行比较 status = op1 - (-op2)
- RET ;子程序返回指令,返回地址默认保存在LR(X30)
- tbz ;寄存器的 #imm位上 为0就跳转 imm是立即缩写
- cset ;
- adr ; x1, off_100381190, 到当前PC值的相对偏移, 相对寻址。
链接
sub_ 指令和子函数起点
locret_ 返回指令
loc_ 指令
off_ 数据,包含偏移量
seg_ 数据,包含段地址值
asc_ 数据,ASCII字符串
byte_ 数据,字节(或字节数组)
word_ 数据,16位数据(或字数组)
dword_ 数据,32位数据(或双字数组)
qword_ 数据,64位数据(或4字数组)
flt_ 浮点数据,32位(或浮点数组)
dbl_ 浮点数,64位(或双精度数组)
tbyte_ 浮点数,80位(或扩展精度浮点数)
stru_ 结构体(或结构体数组)
algn_ 对齐指示
unk_ 未处理字节