iOS 基础汇编知识

1 寄存器 内存 栈

在高级语言里面,操作对象是变量,在ARM汇编里面,操作对象是寄存器(register),内存和栈(stack)

2 数据操作指令

1
op {cond}{s} Rd, Rn, Op2

其中,cond和s是两个可选后缀,cond用来指定指令op在什么条件下执行,共有下面17种条件。

指令 说明
EQ 结果为0, Equal to 0
NE 结果不为0,Not Equal to 0
CS 有进位或者借位 Carry Set
HS 同CS,unsigned Higher or same
CC 没有进位或者借位 Carry clear
LO 同CC unsigned Lower
MI 结果小于0 , Minus
PL 结果大于0 , Plus
VS 溢出, oVerflow set
VC 无溢出 , oVerflow clear
HI 无符号比较大于 ,unsigned higher
LS 无符号比较小于,unsigned lower or same
GE 有符号比较大于等于 ,signed greater than or Equal
LT 有符号比较小于,signed less than
GT 有符号比较大于,signed greater than
LE 无符号比较小于等于,signed less than equal
AL 无条件(Always ,默认)

例如:
比较 R0,R1
移动 GE R2, R1
移动 LT R2, R0
比较R0和R1的值,如果R0大于R1,那么R2等于R1,如果R0小于R1,那么R2等于R0。


s的作用是指定指令op是否设置flag,共有下面四种flag。

符号 说明
N(Negative) 如果结果小于0则置1,否则置0
Z (Zero) 如果结果是0则置1,否则置0
C (Carry) 对于加操作来说,如果产生进位则置1,否则置0,对于减操作来说,如果产生借位则置0,否则置1,对于有位移的非加/减操作来说,C置移出值的最后一位,对于其他的非加/减操作来说,C的值一般不变
V(Overflos) 如果操作溢出则置1,否则置0

需要注意的是,Cflag表示的是无符号位运算结果是否溢出,Vflag表示的是有符号位数运算结果是否溢出。


算术操作

ADD R0, R1, R2 ; R0 = R1 + R2

ADC R0, R1, R2 ; R0 = R1 + R2 + C(arry)

SUB R0, R1, R2 ; R0 = R1 - R2

SBC R0, R1, R2 ; R0 = R1 - R2 - !C

RSB R0, R1, R2 ; R0 = R2 - R1

RSC R0, R1, R2 ; R0 = R2 - R1 - !C


逻辑操作

AND R0, R1, R2 ; R0 = R1 & R2 //按位与

ORR R0, R1, R2 ; R0 = R1 | R2 //按位或

EOR R0, R1, R2 ; R0 = R1 ^ R2 //按位异或

BIC R0, R1, R2 ; R0 = R1 &~ R2 //按位取反与

MOV R0, R2 ; R0 = R2 //赋值

MVN R0, R2 ; R0 = ~R2 //按位取反


比较操作

CMP R1, R2 ; 执行R1 - R2并依结果设置flag

CMN R1, R2 ; 执行R1 + R2并依结果设置flag

TST R1, R2 ; 执行R1 & R2并依结果设置flag

TEQ R1, R2 ; 执行R1 ^ R2并依结果设置flag


乘法操作

MUL R4, R3, R2 ; R4 = R3 * R2

MLA R4, R3, R2, R1 ; R4 = R3 * R2 + R1

乘法操作的操作数必须来自寄存器

###3 内存操作指令
内存操作指令的基本格式是:

1
op{cond}{type} Rn, [Rn,?Op2]

其中Rn是基址寄存器,用户存放基地址

cond的作用于数据操作指令相同,都是用来指定op是在什么条件下执行

type指定指令op操作的数据类型,共有四种

指令 说明
B(unsigned Byte) 无符号byte,执行时扩展到32bit,以0填充
SB(Signed Byte) 有符号byte,仅用于LDR指令,执行时扩展到32bit,以符号位填充
H (signed Halfword) 无符号halfword,执行时扩展到32bitj,以0填充
SH (signed halfword) 有符号halfword 仅用于LDR指令,执行时扩展到32bit,以符号位填充

如果不指定byte,则默认的数据类型是word
ARM内存操作基础指令只有两个: LDR (LoaD Register)将数据从内存中读出来,存到寄存器中。以及STR(STore Register)将寄存器的数据读出来,存到内存中。两个指令的使用情况如下:

LDR
-
LDR Rt, [Rn {,#offset}] ; Rt = *(Rn {+ offset}),{}代表可选

LDR Rt, [Rn, #offset]! ; Rt = *(Rn + offset); Rn
= Rn + offset

LDR Rt, [Rn], #offset ; Rt = *Rn; Rn = Rn +
offset

STR
-

STR Rt, [Rn {, #offset}] ; *(Rn {+ offset}) = Rt

STR Rt, [Rn, #offset]! ; *(Rn {+ offset}) = Rt; Rn
= Rn + offset

STR Rt, [Rn], #offset ; *Rn = Rt; Rn = Rn +
offset

此外, LDR和STR的变种LDRD和STRD还可以操
作双字( Doubleword),即一次性操作2个寄存器,
其基本格式如下:
op{cond} Rt, Rt2, [Rn {, #offset}]

用法及原型类似,如下:
STRD R4, R5, [R9,#offset] ; *(R9+offset) = R4 , *(R9+offset+R4) = R5

LDRD R4, R5, [R9,#offset] ; R4 = *(R9 + offset);
R5 = *(R9 + offset + 4)

除了LDR和STR外,还可以通过LDM( LoaD
Multiple)和STM( STore Multiple)进行块传输,一
次性操作多个寄存器。块传输指令的基本格式是:

1
op{cond}{mode} Rd{!}, reglist

其中Rd是基址寄存器,可选的“!”指定Rd变化后
的值是否写回Rd; reglist是一系列寄存器,用大括号
括起来,它们之间可以用“,”分隔,也可以用“-”表示
一个范围,比如, {R4–R6,R8}表示寄存器R4、 R5、
R6、 R8;这些寄存器的顺序是按照自身的编号由小
到大排列的,与大括号内的排列顺序无关。
需要特别注意的是, LDM和STM的操作方向与
LDR和STR完全相反: LDM是把从Rd开始,地址连续
的内存数据存入reglist中, STM是把reglist中的值存入
从Rd开始,地址连续的内存中。

“cond”的作用与数据操作指令相同。 “mode”指定
Rd值的4种变化规律,如下所示:

IA( Increment After)
每次传输后增加Rd的值;

IB( Increment Before)
每次传输前增加Rd的值;

DA( Decrement After)
每次传输后减少Rd的值;

DB( Decrement Before)
每次传输前减少Rd的值

示例: 假设R0的值为5

执行以下命令后, R4、 R5、 R6的值分别变成:
foo():
LDMIA R0, {R4 – R6} ; R4 = 5, R5 = 6,
R6 = 7
LDMIB R0, {R4 – R6} ; R4 = 6, R5 = 7,
R6 = 8
LDMDA R0, {R4 – R6} ; R4 = 5, R5 = 4,
R6 = 3
LDMDB R0, {R4 – R6} ; R4 = 4, R5 = 3,
R6 = 2

4 分支指令

分支指令可以分为无条件分支和条件分支两种。

. 无条件分支
foo():
B Label ; 跳转到Label处往下执行
…… ; 得不到执行
Label:
……

. 条件分支

条件分支的cond是依照6.2.1节提到的4种flag来判
断的,它们的对应关系如下:
cond flag
EQ Z = 1

NE Z = 0

CS C = 1

HS C = 1

CC C = 0

LO C = 0

MI N = 1

PL N = 0

VS V = 1

VC V = 0

HI C = 1 & Z = 0

LS C = 0 | Z = 1

GE N = V

LT N != V

GT Z = 0 & N = V

LE Z = 1 | N != V

在条件分支指令前会有一条数据操作指令来设置
flag,分支指令根据flag的值来决定代码走向,举例如
下:
Label:
LDR R0, [R1], #4
CMP R0, 0 ; 如果R0 == 0, Z = 1;否则Z = 0
BNE Label ; Z == 0则跳转

-------评论系统采用disqus,如果看不到需要翻墙-------------