iOS 进阶知识集

ARM汇编初识

2018-04-27  本文已影响185人  烟影很美

iOS架构及设备

架构 设备 宽度
armv6 iPhone, iPhone2, iPhone3G, 第一代、第二代iPod Touch 32
armv7 iPhone3GS, iPhone4, iPhone4S, iPad, iPad2, iPad3(The New iPad), iPad mini, iPod Touch 3G, iPod Touch4 32
armv7s iPhone5, iPhone5C, iPad4(iPad With Retina Display) 32
arm64 iPhone5s以及之后版本 64

ARM64寄存器

寄存器 描述
x0-x30 通用寄存器, 如果有需要可以当做32bit(w0-w30)使用
FP(x29) 保存栈帧地址(栈底指针)
LR(30) 通常称x30为程序链接寄存器, 保存子程序结束后需要执行的下一条指定的地址
SP 保存栈指针, 使用SP/WSP来进行对SP寄存器的访问
PC 程序计数器, 俗称PC指针, 总是指向即将执行的下一条指令, 在arm64中, 软件是不能改写PC寄存器的
SPSR 状态寄存器

寄存器概念补充

数据地址寄存器

数据地址寄存器通常用来做数据计算的临时存储、做累加、计数、地址保存等功能。定义这些寄存器的作用主要是用于在CPU指令中保存操作数,在CPU中当做一些常规变量来使用。

ARM64中

注意:
之前讲解8086汇编中有一种特殊的寄存器段寄存器:CS,DS,SS,ES四个寄存器来保存这些段的基地址,这个属于Intel架构CPU中.在ARM中并没有

浮点和向量寄存器

因为浮点数的存储以及其运算的特殊性,CPU中专门提供浮点数寄存器来处理浮点数

SP和FP寄存器

注意: ARM64开始, 取消32位的LDM,STM,PUSH,PHP指令, 取而代之的是ldr/ldp,str/stp
ARM64里面对栈的操作是16字节对齐的

关于内存读写指令

注意: 读/写数据都是往高地址读/写

str(store register)

将数据从寄存器存到栈中, 同时操作两个寄存器时, 使用stp

str x0,[sp,#0x10]

ldr(load register)

将数据从栈读到寄存器中, 同时操作两个寄存器时, 使用ldp

ldr x0,[sp,#0x10]

堆栈操作练习

将32个字节空间作为这段程序的栈空间,然后利用栈x0,x1的值进行交换

sub sp,sp,#0x20                  ;拉伸栈空间
stp x0,x1,[sp,#0x10]             ;将x0,x1存放在sp+0x10为地址的栈中
ldp x1,x0,[sp,#0x10]             ;从sp+0x10为地址的栈中读取数据到x1,x0
add sp,sp,#0x20                  ;平栈

bl和ret指令

bl 标号

ret

ARM64平台的特色指令, 它面向硬件做了优化处理

x30寄存器(lr)

注意: 在函数嵌套调用的时候. 需要将x30入栈!

ldp    x29, x30, [sp, #0x10]
add    sp, sp, #0x20             ; =0x20 
ret

函数的参数和返回值

NZCV状态寄存器

注: CPSR是32位寄存器. add/sub等指令不能影响CPSR寄存器, 如需要保存计算状态, 使用adds/subs等.

N(Negative)

CPSR的第31位(从零开始数...)是N, 符号标志位. 它记录相关指令执行后, 其结果是否为负. 如果结果为负, N=1. 反之N=0.

Z(Zero)

CPSR的第30位(...你们懂得)是Z, 0标志位,. 它记录相关指令执行后, 其结果是否为0, 如果是0, Z=1. 反之Z=0.

C(Carry)

CPSR的第29位(...)时C, 进位标志位. 一般情况下, 进行无符号数运算.
加法运算: 当运算产生进位时, C=1, 反之C=0.
减法运算(包括CMP): 当运算产生借位时, C=0, 反之C=1.

进位: 
mov w0,#0xaaaaaaaa;0xa 的二进制是 1010
adds w0,w0,w0; 执行后 相当于 1010 << 1 进位1(无符号溢出) 所以C标记 为 1
adds w0,w0,w0; 执行后 相当于 0101 << 1 进位0(无符号没溢出) 所以C标记 为 0
adds w0,w0,w0; 重复上面操作
adds w0,w0,w0
借位:
mov w0,#0x0
subs w0,w0,#0xff ;
subs w0,w0,#0xff
subs w0,w0,#0xff

V(Overflow) 溢出标志

CPSR的第28位时V, 溢出标志位. 在进行有符号运算时, 如果超过了机器所能标识的范围, 视为溢出.

实际是无符号运算影响了符号位或者产生了进位或借位

以下情况都会造成溢出

正数 + 负数 不可能溢出.

指令条件码(逻辑对照)

竟然跟NZCV对应的值不匹配, 虽然理解, 但还是好难受.
比如不相等, 就不能确定N位的值. 猜测

编码 助记符 描述 标记
0000 EQ (equal) 相等 Z=1
0001 NE(Not Equal) 不相等 Z=0
0010 CS/(Carry Set/High or Same) 无符号数大于/等于 C = 1
0011 CC/LO(Carry Clear/LOwer) 无符号数小于 C = 0
0100 MI(MInus) 负数 N=1
0101 PL(PLus) 非负数 N=0
0110 VS(oVerflow set) 上溢出 V=1
0111 VC(oVerflow clear) 没有上溢出 V=0
1000 HI(HIgh) 无符号数大于 C=1 且 Z=0
1001 LS(Lower or Same) 无符号数小于/等于 C=0 且 Z=1
1010 GE(Greater or Equal) 有符号数大于或等于 N=V
1011 LT(Less Than) 有符号数小于 N!=V
1100 GT(Greater Than) 有符号数大于 Z=0,N=V
1101 LE(Less or Equal) 有符号数小于等于 Z=1,N!=V
1110 AL 无条件执行 任何
1111 NV 从不执行 任何

全局变量/静态变量/常量

  • 较大的数字(ARM64中二进制表示大于0xffff, 即长度超过2字节), 因为ARM汇编命令是等长的(四字节), 比较大的数不能包含在命令中, ARM通过偏移的方式, 使用adrp指令读取静态区.
  • 同理字符串也不能保存在代码区, 通过偏移的方式, 同样使用adrp指令读取.

下面代码是读取一个常量/静态变量到x8寄存器

adrp x0,1        ; 将当前PC寄存器指向的地址低12位清零, 加上 (`1`左移12位), 将结果保存到`x0`寄存器
add x0,#0xf28    ; `x0`保存的值加上偏移0xf28再次保存到`x0`寄存器.
ldr x8, [x0]     ; 将x0存储的值作为地址, 读取地址指向的数据, 保存到x8寄存器

上面代码中, 最后x8寄存器保存的就是一个静态变量或者字符串常量的指针.

待续

// 知识有限, 随时修改..

// ## #### ######

上一篇 下一篇

猜你喜欢

热点阅读