arm64汇编
一 基础知识
关于PC机及CPU物理结构和编程结构,在《微机原理与接口》中进行;对于计算机的一般结构、功能、性能的研究在《组成原理》中进行。这本书主要研究重点放在如何利用硬件系统的编程结构和指令集有效灵活地控制系统进行工作。
电子计算机的机器指令是一系列二进制数字。计算机将其转变为一系列高低电平,以使计算机的电子器件受到驱动,进行运算。
每一种微处理器,由于硬件设计和内部结构不同,就需要不用的电平脉冲来控制。所以每一种微处理器都有自己的指令集。
早期的程序设计均使用机器语言。程序员将0和1数字编成程序代码打在纸带或者卡片上,1打孔,0不打孔,再将程序通过纸带机或者卡片机输入计算机,进行运算。
汇编语言由三类指令组成:
- 汇编指令:有对应的机器码。
- 伪指令:没有对应的机器码,由编译器执行。计算机并不执行。
- 其它符号:如+、-、*、/ 等,也没有对应的机器码。
指令和数据是应用上的概念。在内存或者磁盘上,指令号和数据没有任何区别,都是二进制信息。CPU在工作的时候把有的信息看做指令,有的信息看做数据,为同样的信息赋予了不用的意义。
1.2 存储单元
存储器被划分成若干个存储单元,每个存贮单元从 0 开始顺序编号,假如一个存储器有 128 个存储单元,编号从0~127,如图1.2所示。
计算机最小的信息单位是bit,也就是一个二进制位。8个bit组成一个Byte,也就是通常讲的一个字节。微型机存储器的存储单元可以存储一个Byte,及8个二进制位(bit)。一个存储器有128个存储单元,它可以存储128个Byte(字节)。
1.3 CPU对存储器的读写
CPU要想进行数据的读写,必须和外部器件(标准的说法是芯片)进行下面三类信息的交互:
- 存储单元的地址(地址信息)
- 器件的选择,读或写的命令(控制信息)
- 读或写的数据(数据信息)
那么CPU是通过什么将地址、数据、和控制信息传到存储器芯片中的呢?电子计算机能处理、传输的信息都是电信号,电信号当然要用导线来传送。在计算机中专门有连接CPU和其它芯片的导线,通常称为总线。总线从物理上讲,就是一根根导线的集合。根据传送的信息不同,总线总逻辑上又分为三类,地址总线、控制总线和数据总线。
CPU从3号单元中读取数据的过程(见图1.3)如下:
1,CPU通过地址线将地址信息3发出。
2,CPU通过控制线发出内存读取命令,选中存储器芯片,并通知它,将要从中读取数据。
3,存储器将3号单元中的数据8通过数据线送入CPU。
写入操作与读取操作步骤相似。如向3号单元写入数据26:
1,CPU通过地址线将地址信息3发出。
2,CPU通过控制线发出内存写入命令,选中存储器芯片,并通知它,将要其中写入数据。
3,CPU通过数据线将数据26送入内存的3号单元中。
从上面我们知道了 CPU 是如何进行数据读写的。可是,如何命令计算机进行数据的读写呢?
要让一个计算机或微处理器工作,应向它输入能够驱动它进行工作的电平信息(机器码)。
对于 8086 CPU,下面的机器码,能够完成从3号单元读数据。
机器码: 10100001 00000011 00000000
对应的汇编指令: MOV AX,[3]
含义: 将3号单元的内容写入AX
1.4 地址总线
现在我们知道,CPU是通过地址总线来指定存储器单元的。可见地址总线上能传送多少个字节的信息,CPU就可以对多少个存储单元进行寻址。
假设,一个CPU有十根地址总线,让我们来看看它的寻址情况。我们知道,在电子信息中,一根导线可以传送的稳定状态只有两种,高电平或者低电平。用二进制表示就是1或0,10根导线可以传送10位二进制数据。而10位二进制数可以表示多少个不同的数据呢?2的10次方个。最小数为0,最大数为1023。
一个CPU有N根地址线,则可以说这个CPU的地址总线的宽度为N。这样的CPU最多可以寻找2的N次方个内存单元。
1.5 数据总线
CPU与内存或其它器件之间的数据传送是通过数据总线来进行的。数据总线的宽度决定了CPU和外界的数据传送速度。8根数据总线一次可以传送8位二进制数据(即一个字节)。16根数据总线一次可传送两个字节。
1.6 控制总线
CPU对外部器件的控制是通过控制总线来进行的。在这里控制总线是个总称,控制总线是一些不同控制线的集合。多少根控制总线,就意味着CPU提供了对外部器件的多少控制。所以,控制总线的宽度决定了CPU对外部器件的控制能力。
1.7 内存地址空间
假设一个CPU的地址总线宽度为10,那么可以寻址2^10(1024)个内存单元(Byte,8个bit)。这1024个单元就构成了CPU的内存地址空间。
一台PC机中,装有多个存储器芯片。它们从读写属性上分为两类:随机存储器(RAM)和只读存储器(ROM)。随机存储器可读可写,但必须带电存储,关机后存储的内容消失。只读存储器只能读取不能写入,关机后其中的内容不消失。
上图中所有的物理存储器被看作一个由若干个存储单元组成的逻辑存储器,每个物理存储器在这个逻辑存储器中占有一个地址段,即一段地址空间。CPU在这段地址空间中读写数据,实际上就是在相对应的物理存储器中读写数据。比如将数据写入显卡的RAM(显存),数据就会显示在屏幕上。
1.8 其它基础知识
- iOS 中的 armv7,armv7s,arm64 这些都代表什么?
ARMv7|ARM7s|ARM64都是ARM处理器的指令集 真机32位处理器需要ARMv7,或者ARMv7s架构, 真机64位处理器需要ARM64架构。
- 处理器和寄存器的位数
32位处理器,能同时处理32位的数据,所以对应寄存器为32位的; 64位处理器,能同时处理64位的数据,所以对应寄存器为64位的; 寄存器位数一般会对应处理器位数,两者一般相等,但也有例外8086
- ARM 指令长度
ARM处理器用到的指令集分为 ARM 和 THUMB 两种。ARM指令长度固定为32bit,THUMB指令长度固定为16bit。所以 ARM64指令集的指令长度为32bit
- ARM中字的长度
ARM中 一个word是32位,也就是4Byte大小
2 寄存器
ARM64 有34个寄存器,包括31个通用寄存器、SP、PC、CPSR。
| 寄存器 | 位数 | 描述 |
|---|---|---|
| x0-x30 | 64bit | 通用寄存器,如果有需要可以当做32bit使用:WO-W30 |
| FP(x29) | 64bit | 保存栈帧地址(栈底指针) |
| LR(x30,Link Register) | 64bit | 通常称X30为程序链接寄存器,保存子程序结束后需要执行的下一条指令 |
| SP | 64bit | 保存栈指针,使用 SP/WSP来进行对SP寄存器的访问。 |
| PC(Program Counter) | 64bit | 程序计数器,俗称PC指针,总是指向即将要执行的下一条指令,在arm64中,软件是不能改写PC寄存器的。 |
| CPSR | 64bit | 状态寄存器 |
- x0-x7: 用于子程序调用时的参数传递,X0还用于返回值传递
x0 - x30是31个通用整形寄存器。每个寄存器可以存取一个64位大小的数。 当使用r0 - r30访问时,它就是一个64位的数。当使用w0 - w30访问时,访问的是这些寄存器的低32位,如图:
- CPSR(状态寄存器,current program state register)
- CPSR的低8位(包括I、F、T和M[4:0])称为控制位,程序无法修改,除非CPU运行于特权模式下,程序才能修改控制位。
N、Z、C、V均为条件码标志位。它们的内容可被算术或逻辑运算的结果所改变,并且可以决定某条指令是否被执行,意义重大!
adrp指令是计算指定的数据地址到当前PC值的相对偏移。
adrp x0, 1
/*
假设当前的 PC 寄存器为 0x1002e6874
1.先将1的值左移12位二进制位则为 1 0000 0000 0000 ==> 0x1000
2.将 PC 寄存器的低12位清零,也就是 0x1002e6874 ==> 0x1002e6000
3.最后将1和2的结果相加给 x0 寄存器,
x0 = 0x000001000 + 0x1002e6000
x0 = 0x1002e7000
*/
NZCV是状态寄存器的条件标志位,分别代表运算过程中产生的状态,其中:
- N, negative condition flag,一般代表运算结果是负数
- Z, zero condition flag, 指令结果为0时Z=1,否则Z=0;
- C, carry condition flag, 无符号运算有溢出时,C=1。
- V, oVerflow condition flag 有符号运算有溢出时,V=1。
- Xcode在真机中运行项目,添加断点,lldb中查看各寄存器状态register read
2.1 cmp(compare)比较指令
cmp:把一个寄存器的内容和另一个寄存器的内容或立即数进行比较。但不存储结果,只是正确的更改标志。 一般 cmp 做完判断后会进行跳转,后面通常会跟上B指令。
- BL 标号:跳转到标号处执行
- B.LT 标号:比较结果是小于(less than),执行标号,否则不跳转;
- B.LE 标号:比较结果是小于等于(less than or equal to),执行标号,否则不跳转;
- B.GT 标号:比较结果是大于(greater than),执行标号,否则不跳转;
- B.GE 标号:比较结果是大于等于(greater than or equal to),执行标号,否则不跳转;
- B.EQ 标号:比较结果是等于,执行标号,否则不跳转;
- B.HI 标号:比较结果是无符号大于,执行标号,否则不跳转;
if...else的识别
- 源码
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void func(int a,int b){
int temp = 0;
if (a >= b) {
temp = a;
}else{
temp = b;
}
}
int main(int argc, char * argv[]) {
func(10, 20);
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
- func 函数的汇编代码
__text:0000000100006798 ; =============== S U B R O U T I N E =======================================
__text:0000000100006798
__text:0000000100006798
__text:0000000100006798 EXPORT _func
__text:0000000100006798 _func ; CODE XREF: _main+28↓p
__text:0000000100006798
__text:0000000100006798 var_C = -0xC
__text:0000000100006798 var_8 = -8
__text:0000000100006798 var_4 = -4
__text:0000000100006798
__text:0000000100006798 SUB SP, SP, #0x10
__text:000000010000679C STR W0, [SP,#0x10+var_4]
__text:00000001000067A0 STR W1, [SP,#0x10+var_8]
__text:00000001000067A4 STR WZR, [SP,#0x10+var_C]
__text:00000001000067A8 LDR W0, [SP,#0x10+var_4]
__text:00000001000067AC LDR W1, [SP,#0x10+var_8]
__text:00000001000067B0 CMP W0, W1
__text:00000001000067B4 B.LT loc_1000067C4
__text:00000001000067B8 LDR W8, [SP,#0x10+var_4]
__text:00000001000067BC STR W8, [SP,#0x10+var_C]
__text:00000001000067C0 B loc_1000067CC
__text:00000001000067C4 ; ---------------------------------------------------------------------------
__text:00000001000067C4
__text:00000001000067C4 loc_1000067C4 ; CODE XREF: _func+1C↑j
__text:00000001000067C4 LDR W8, [SP,#0x10+var_8]
__text:00000001000067C8 STR W8, [SP,#0x10+var_C]
__text:00000001000067CC
__text:00000001000067CC loc_1000067CC ; CODE XREF: _func+28↑j
__text:00000001000067CC ADD SP, SP, #0x10
__text:00000001000067D0 RET
__text:00000001000067D0 ; End of function _func
汇编代码分析
// 1. 判断条件
__text:00000001000067B0 CMP W0, W1
__text:00000001000067B4 B.LT loc_1000067C4
/*
结合这两句代码表示,判断(CMP) w0 和 w1 的比较结果是否小于。
如果是小于,则执行标号(loc_1000067C4)以下的代码,否则不跳转继续执行下 面的代码。
*/
// 2. 不满足条件不跳转继续执行
__text:00000001000067B8 LDR W8, [SP,#0x10+var_4]
__text:00000001000067BC STR W8, [SP,#0x10+var_C]
// 3. 满足条件跳转标号执行
__text:00000001000067C4 loc_1000067C4 ; CODE XREF: _func+1C↑j
__text:00000001000067C4 LDR W8, [SP,#0x10+var_8]
__text:00000001000067C8 STR W8, [SP,#0x10+var_C]
do…while 循环的识别
- 源码
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void func() {
int nSum = 0;
int i = 0;
do {
i++;
nSum = nSum + i;
} while (i < 100);
}
int main(int argc, char * argv[]) {
func();
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
- func 函数的汇编代码
__text:00000001000067AC ; =============== S U B R O U T I N E =======================================
__text:00000001000067AC
__text:00000001000067AC
__text:00000001000067AC EXPORT _func
__text:00000001000067AC _func ; CODE XREF: _main+18↓p
__text:00000001000067AC
__text:00000001000067AC var_8 = -8
__text:00000001000067AC var_4 = -4
__text:00000001000067AC
__text:00000001000067AC SUB SP, SP, #0x10
__text:00000001000067B0 STR WZR, [SP,#0x10+var_4]
__text:00000001000067B4 STR WZR, [SP,#0x10+var_8]
__text:00000001000067B8
__text:00000001000067B8 loc_1000067B8 ; CODE XREF: _func+30↓j
__text:00000001000067B8 LDR W8, [SP,#0x10+var_8]
__text:00000001000067BC ADD W8, W8, #1
__text:00000001000067C0 STR W8, [SP,#0x10+var_8]
__text:00000001000067C4 LDR W8, [SP,#0x10+var_4]
__text:00000001000067C8 LDR W9, [SP,#0x10+var_8]
__text:00000001000067CC ADD W8, W8, W9
__text:00000001000067D0 STR W8, [SP,#0x10+var_4]
__text:00000001000067D4 LDR W8, [SP,#0x10+var_8]
__text:00000001000067D8 CMP W8, #0x64
__text:00000001000067DC B.LT loc_1000067B8
__text:00000001000067E0 ADD SP, SP, #0x10
__text:00000001000067E4 RET
__text:00000001000067E4 ; End of function _func
汇编代码分析
// 1. 判断条件
__text:00000001000067D8 CMP W8, #0x64
__text:00000001000067DC B.LT loc_1000067B8
// 2. 满足条件跳转标号执行
__text:00000001000067B8 loc_1000067B8 ; CODE XREF: _func+30↓j
__text:00000001000067B8 LDR W8, [SP,#0x10+var_8]
__text:00000001000067BC ADD W8, W8, #1
__text:00000001000067C0 STR W8, [SP,#0x10+var_8]
__text:00000001000067C4 LDR W8, [SP,#0x10+var_4]
__text:00000001000067C8 LDR W9, [SP,#0x10+var_8]
__text:00000001000067CC ADD W8, W8, W9
__text:00000001000067D0 STR W8, [SP,#0x10+var_4]
__text:00000001000067D4 LDR W8, [SP,#0x10+var_8]
while 循环的识别
- 源码
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void func() {
int nSum = 0;
int i = 0;
while (i < 100){
i++;
nSum = nSum + i;
}
}
int main(int argc, char * argv[]) {
func();
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
- func 函数的汇编代码
__text:00000001000067A8 ; =============== S U B R O U T I N E =======================================
__text:00000001000067A8
__text:00000001000067A8
__text:00000001000067A8 EXPORT _func
__text:00000001000067A8 _func ; CODE XREF: _main+18↓p
__text:00000001000067A8
__text:00000001000067A8 var_8 = -8
__text:00000001000067A8 var_4 = -4
__text:00000001000067A8
__text:00000001000067A8 SUB SP, SP, #0x10
__text:00000001000067AC STR WZR, [SP,#0x10+var_4]
__text:00000001000067B0 STR WZR, [SP,#0x10+var_8]
__text:00000001000067B4
__text:00000001000067B4 loc_1000067B4 ; CODE XREF: _func+34↓j
__text:00000001000067B4 LDR W8, [SP,#0x10+var_8]
__text:00000001000067B8 CMP W8, #0x64
__text:00000001000067BC B.GE loc_1000067E0
__text:00000001000067C0 LDR W8, [SP,#0x10+var_8]
__text:00000001000067C4 ADD W8, W8, #1
__text:00000001000067C8 STR W8, [SP,#0x10+var_8]
__text:00000001000067CC LDR W8, [SP,#0x10+var_4]
__text:00000001000067D0 LDR W9, [SP,#0x10+var_8]
__text:00000001000067D4 ADD W8, W8, W9
__text:00000001000067D8 STR W8, [SP,#0x10+var_4]
__text:00000001000067DC B loc_1000067B4
__text:00000001000067E0 ; ---------------------------------------------------------------------------
__text:00000001000067E0
__text:00000001000067E0 loc_1000067E0 ; CODE XREF: _func+14↑j
__text:00000001000067E0 ADD SP, SP, #0x10
__text:00000001000067E4 RET
__text:00000001000067E4 ; End of function _func
汇编代码分析
// 1. 判断条件是否满足
__text:00000001000067B4 loc_1000067B4 ; CODE XREF: _func+34↓j
__text:00000001000067B4 LDR W8, [SP,#0x10+var_8]
__text:00000001000067B8 CMP W8, #0x64
__text:00000001000067BC B.GE loc_1000067E0
// 2.条件满足则继续执行,执行完最后一句代码之后,又执行第一步,直到条件不满足后再跳出循环
__text:00000001000067C0 LDR W8, [SP,#0x10+var_8]
__text:00000001000067C4 ADD W8, W8, #1
__text:00000001000067C8 STR W8, [SP,#0x10+var_8]
__text:00000001000067CC LDR W8, [SP,#0x10+var_4]
__text:00000001000067D0 LDR W9, [SP,#0x10+var_8]
__text:00000001000067D4 ADD W8, W8, W9
__text:00000001000067D8 STR W8, [SP,#0x10+var_4]
__text:00000001000067DC B loc_1000067B4
for 循环的识别
- 源码
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void func() {
for (int i = 0; i < 100; i++) {
printf("hello");
}
}
int main(int argc, char * argv[]) {
func();
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
- func 函数的汇编代码
__text:000000010000678C ; =============== S U B R O U T I N E =======================================
__text:000000010000678C
__text:000000010000678C ; Attributes: bp-based frame
__text:000000010000678C
__text:000000010000678C EXPORT _func
__text:000000010000678C _func ; CODE XREF: _main+18↓p
__text:000000010000678C
__text:000000010000678C var_8 = -8
__text:000000010000678C var_4 = -4
__text:000000010000678C var_s0 = 0
__text:000000010000678C
__text:000000010000678C SUB SP, SP, #0x20
__text:0000000100006790 STP X29, X30, [SP,#0x10+var_s0]
__text:0000000100006794 ADD X29, SP, #0x10
__text:0000000100006798 STUR WZR, [X29,#var_4]
__text:000000010000679C
__text:000000010000679C loc_10000679C ; CODE XREF: _func+38↓j
__text:000000010000679C LDUR W8, [X29,#var_4]
__text:00000001000067A0 CMP W8, #0x64
__text:00000001000067A4 B.GE loc_1000067C8
__text:00000001000067A8 ADRP X0, #aHello@PAGE ; "hello"
__text:00000001000067AC ADD X0, X0, #aHello@PAGEOFF ; "hello"
__text:00000001000067B0 BL _printf
__text:00000001000067B4 STR W0, [SP,#0x10+var_8]
__text:00000001000067B8 LDUR W8, [X29,#var_4]
__text:00000001000067BC ADD W8, W8, #1
__text:00000001000067C0 STUR W8, [X29,#var_4]
__text:00000001000067C4 B loc_10000679C
__text:00000001000067C8 ; ---------------------------------------------------------------------------
__text:00000001000067C8
__text:00000001000067C8 loc_1000067C8 ; CODE XREF: _func+18↑j
__text:00000001000067C8 LDP X29, X30, [SP,#0x10+var_s0]
__text:00000001000067CC ADD SP, SP, #0x20
__text:00000001000067D0 RET
__text:00000001000067D0 ; End of function _func
汇编代码分析
通过与 while 的汇编代码对比,可以得知类似于 while 循环语句。
Switch
- 假设 switch 内分支比较多的时候,在编译的时候会生成一个表(跳转表每个地址四个字节);
- 假设 switch 内分支比较少的时候(例如3,少于4的时候没有意义),就没有必要使用此结构,相当于if...else语句的结构;
- 假设 switch 内各个分支常量的差值较大的时候,编译器会在效率还是内存之间进行取舍,如果效率优先的情况下,这个时候编译器还是会编译成类似于if..else的结构。
- Switch 内分支少于4的情况下:
源码
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void func(int a) {
switch (a) {
case 1:
printf("上路");
break;
case 2:
printf("中路");
break;
case 3:
printf("下路");
break;
default:
printf("随机匹配");
break;
}
}
int main(int argc, char * argv[]) {
func(1);
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
汇编代码
__text:0000000100006704 ; =============== S U B R O U T I N E =======================================
__text:0000000100006704
__text:0000000100006704 ; Attributes: bp-based frame
__text:0000000100006704
__text:0000000100006704 EXPORT _func
__text:0000000100006704 _func ; CODE XREF: _main+20↓p
__text:0000000100006704
__text:0000000100006704 var_24 = -0x24
__text:0000000100006704 var_20 = -0x20
__text:0000000100006704 var_1C = -0x1C
__text:0000000100006704 var_18 = -0x18
__text:0000000100006704 var_14 = -0x14
__text:0000000100006704 var_10 = -0x10
__text:0000000100006704 var_C = -0xC
__text:0000000100006704 var_8 = -8
__text:0000000100006704 var_4 = -4
__text:0000000100006704 var_s0 = 0
__text:0000000100006704
__text:0000000100006704 SUB SP, SP, #0x40
__text:0000000100006708 STP X29, X30, [SP,#0x30+var_s0]
__text:000000010000670C ADD X29, SP, #0x30
__text:0000000100006710 STUR W0, [X29,#var_4]
__text:0000000100006714 LDUR W0, [X29,#var_4]
__text:0000000100006718 MOV X8, X0
__text:000000010000671C SUBS W0, W0, #1
__text:0000000100006720 STUR W8, [X29,#var_8]
__text:0000000100006724 STUR W0, [X29,#var_C]
__text:0000000100006728 B.EQ loc_100006758
__text:000000010000672C B loc_100006730
__text:0000000100006730 ; ---------------------------------------------------------------------------
__text:0000000100006730
__text:0000000100006730 loc_100006730 ; CODE XREF: _func+28↑j
__text:0000000100006730 LDUR W8, [X29,#var_8]
__text:0000000100006734 SUBS W9, W8, #2
__text:0000000100006738 STUR W9, [X29,#var_10]
__text:000000010000673C B.EQ loc_10000676C
__text:0000000100006740 B loc_100006744
__text:0000000100006744 ; ---------------------------------------------------------------------------
__text:0000000100006744
__text:0000000100006744 loc_100006744 ; CODE XREF: _func+3C↑j
__text:0000000100006744 LDUR W8, [X29,#var_8]
__text:0000000100006748 SUBS W9, W8, #3
__text:000000010000674C STUR W9, [X29,#var_14]
__text:0000000100006750 B.EQ loc_100006780
__text:0000000100006754 B loc_100006794
__text:0000000100006758 ; ---------------------------------------------------------------------------
__text:0000000100006758
__text:0000000100006758 loc_100006758 ; CODE XREF: _func+24↑j
__text:0000000100006758 ADRP X0, #asc_100007F10@PAGE ; "上路"
__text:000000010000675C ADD X0, X0, #asc_100007F10@PAGEOFF ; "上路"
__text:0000000100006760 BL _printf
__text:0000000100006764 STR W0, [SP,#0x30+var_18]
__text:0000000100006768 B loc_1000067A4
__text:000000010000676C ; ---------------------------------------------------------------------------
__text:000000010000676C
__text:000000010000676C loc_10000676C ; CODE XREF: _func+38↑j
__text:000000010000676C ADRP X0, #asc_100007F17@PAGE ; "中路"
__text:0000000100006770 ADD X0, X0, #asc_100007F17@PAGEOFF ; "中路"
__text:0000000100006774 BL _printf
__text:0000000100006778 STR W0, [SP,#0x30+var_1C]
__text:000000010000677C B loc_1000067A4
__text:0000000100006780 ; ---------------------------------------------------------------------------
__text:0000000100006780
__text:0000000100006780 loc_100006780 ; CODE XREF: _func+4C↑j
__text:0000000100006780 ADRP X0, #asc_100007F1E@PAGE ; "下路"
__text:0000000100006784 ADD X0, X0, #asc_100007F1E@PAGEOFF ; "下路"
__text:0000000100006788 BL _printf
__text:000000010000678C STR W0, [SP,#0x30+var_20]
__text:0000000100006790 B loc_1000067A4
__text:0000000100006794 ; ---------------------------------------------------------------------------
__text:0000000100006794
__text:0000000100006794 loc_100006794 ; CODE XREF: _func+50↑j
__text:0000000100006794 ADRP X0, #asc_100007F25@PAGE ; "随机匹配"
__text:0000000100006798 ADD X0, X0, #asc_100007F25@PAGEOFF ; "随机匹配"
__text:000000010000679C BL _printf
__text:00000001000067A0 STR W0, [SP,#0x30+var_24]
__text:00000001000067A4
__text:00000001000067A4 loc_1000067A4 ; CODE XREF: _func+64↑j
__text:00000001000067A4 ; _func+78↑j ...
__text:00000001000067A4 LDP X29, X30, [SP,#0x30+var_s0]
__text:00000001000067A8 ADD SP, SP, #0x40
__text:00000001000067AC RET
__text:00000001000067AC ; End of function _func
汇编代码分析
通过与 if...else 的汇编代码对比,可以得知类似于 if...else 条件判断语句。
- Switch 内分支大于等于4的情况下:
源码
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void func(int a) {
switch (a) {
case 1:
printf("上路");
break;
case 2:
printf("中路");
break;
case 3:
printf("下路");
break;
case 4:
printf("野区");
break;
default:
printf("随机匹配");
break;
}
}
int main(int argc, char * argv[]) {
func(1);
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
汇编代码
__text:00000001000066E8 ; =============== S U B R O U T I N E =======================================
__text:00000001000066E8
__text:00000001000066E8 ; Attributes: bp-based frame
__text:00000001000066E8
__text:00000001000066E8 EXPORT _func
__text:00000001000066E8 _func ; CODE XREF: _main+20↓p
__text:00000001000066E8
__text:00000001000066E8 var_28 = -0x28
__text:00000001000066E8 var_24 = -0x24
__text:00000001000066E8 var_20 = -0x20
__text:00000001000066E8 var_1C = -0x1C
__text:00000001000066E8 var_18 = -0x18
__text:00000001000066E8 var_14 = -0x14
__text:00000001000066E8 var_10 = -0x10
__text:00000001000066E8 var_4 = -4
__text:00000001000066E8 var_s0 = 0
__text:00000001000066E8
__text:00000001000066E8 SUB SP, SP, #0x40
__text:00000001000066EC STP X29, X30, [SP,#0x30+var_s0]
__text:00000001000066F0 ADD X29, SP, #0x30
__text:00000001000066F4 STUR W0, [X29,#var_4]
__text:00000001000066F8 LDUR W0, [X29,#var_4]
__text:00000001000066FC SUBS W0, W0, #1 ; switch 4 cases
__text:0000000100006700 MOV X8, X0
__text:0000000100006704 SUBS W0, W0, #3
__text:0000000100006708 STUR X8, [X29,#var_10]
__text:000000010000670C STUR W0, [X29,#var_14]
__text:0000000100006710 B.HI def_100006728 ; jumptable 0000000100006728 default case
__text:0000000100006714 ADRP X8, #jpt_100006728@PAGE
__text:0000000100006718 ADD X8, X8, #jpt_100006728@PAGEOFF
__text:000000010000671C LDUR X9, [X29,#var_10]
__text:0000000100006720 LDRSW X10, [X8,X9,LSL#2]
__text:0000000100006724 ADD X8, X10, X8
__text:0000000100006728 BR X8 ; switch jump
__text:000000010000672C ; ---------------------------------------------------------------------------
__text:000000010000672C
__text:000000010000672C loc_10000672C ; CODE XREF: _func+40↑j
__text:000000010000672C ; DATA XREF: __text:jpt_100006728↓o
__text:000000010000672C ADRP X0, #asc_100007F08@PAGE ; jumptable 0000000100006728 case 1
__text:0000000100006730 ADD X0, X0, #asc_100007F08@PAGEOFF ; "上路"
__text:0000000100006734 BL _printf
__text:0000000100006738 STR W0, [SP,#0x30+var_18]
__text:000000010000673C B loc_10000678C
__text:0000000100006740 ; ---------------------------------------------------------------------------
__text:0000000100006740
__text:0000000100006740 loc_100006740 ; CODE XREF: _func+40↑j
__text:0000000100006740 ; DATA XREF: __text:000000010000679C↓o
__text:0000000100006740 ADRP X0, #asc_100007F0F@PAGE ; jumptable 0000000100006728 case 2
__text:0000000100006744 ADD X0, X0, #asc_100007F0F@PAGEOFF ; "中路"
__text:0000000100006748 BL _printf
__text:000000010000674C STR W0, [SP,#0x30+var_1C]
__text:0000000100006750 B loc_10000678C
__text:0000000100006754 ; ---------------------------------------------------------------------------
__text:0000000100006754
__text:0000000100006754 loc_100006754 ; CODE XREF: _func+40↑j
__text:0000000100006754 ; DATA XREF: __text:00000001000067A0↓o
__text:0000000100006754 ADRP X0, #asc_100007F16@PAGE ; jumptable 0000000100006728 case 3
__text:0000000100006758 ADD X0, X0, #asc_100007F16@PAGEOFF ; "下路"
__text:000000010000675C BL _printf
__text:0000000100006760 STR W0, [SP,#0x30+var_20]
__text:0000000100006764 B loc_10000678C
__text:0000000100006768 ; ---------------------------------------------------------------------------
__text:0000000100006768
__text:0000000100006768 loc_100006768 ; CODE XREF: _func+40↑j
__text:0000000100006768 ; DATA XREF: __text:00000001000067A4↓o
__text:0000000100006768 ADRP X0, #asc_100007F1D@PAGE ; jumptable 0000000100006728 case 4
__text:000000010000676C ADD X0, X0, #asc_100007F1D@PAGEOFF ; "野区"
__text:0000000100006770 BL _printf
__text:0000000100006774 STR W0, [SP,#0x30+var_24]
__text:0000000100006778 B loc_10000678C
__text:000000010000677C ; ---------------------------------------------------------------------------
__text:000000010000677C
__text:000000010000677C def_100006728 ; CODE XREF: _func+28↑j
__text:000000010000677C ADRP X0, #asc_100007F24@PAGE ; jumptable 0000000100006728 default case
__text:0000000100006780 ADD X0, X0, #asc_100007F24@PAGEOFF ; "随机匹配"
__text:0000000100006784 BL _printf
__text:0000000100006788 STR W0, [SP,#0x30+var_28]
__text:000000010000678C
__text:000000010000678C loc_10000678C ; CODE XREF: _func+54↑j
__text:000000010000678C ; _func+68↑j ...
__text:000000010000678C LDP X29, X30, [SP,#0x30+var_s0]
__text:0000000100006790 ADD SP, SP, #0x40
__text:0000000100006794 RET
__text:0000000100006794 ; End of function _func
汇编代码分析
__text:00000001000066F4 STUR W0, [X29,#var_4]
__text:00000001000066F8 LDUR W0, [X29,#var_4]
__text:00000001000066FC SUBS W0, W0, #1 ; switch 4 cases
__text:0000000100006700 MOV X8, X0
__text:0000000100006704 SUBS W0, W0, #3
__text:0000000100006708 STUR X8, [X29,#var_10]
__text:000000010000670C STUR W0, [X29,#var_14]
__text:0000000100006710 B.HI def_100006728 ; jumptable 0000000100006728 default case
__text:0000000100006714 ADRP X8, #jpt_100006728@PAGE
__text:0000000100006718 ADD X8, X8, #jpt_100006728@PAGEOFF
__text:000000010000671C LDUR X9, [X29,#var_10]
__text:0000000100006720 LDRSW X10, [X8,X9,LSL#2]
__text:0000000100006724 ADD X8, X10, X8
__text:0000000100006728 BR X8 ; switch jump
//
1. switch 内分支比较多的时候,在编译的时候会生成一个表(跳转表每个地址四个字节);
2. 先判断是否是 default 分支;// B.HI def_100006728
3. 取 X8的地址;
ADRP X8, #jpt_100006728@PAGE
ADD X8, X8, #jpt_100006728@PAGEOFF
// ADRP + ADD表示取值,常用于取常量和全局变量的值,但是这里取的是地址值。
LDUR X9, [X29,#var_10]
LDRSW X10, [X8,X9,LSL#2]
// LDRSW
// [X8,X9,LSL#2] 表示以 X8 为基地址,X9的二进制左移2位,再相加得到一个新的地址值。
// 例如:A表示(X8 为基地址),B 表示(X9的二进制左移2位)
// 那么 X10 = [A B];
ADD X8, X10, X8
// X8 = X10 和 X8的值相加
通过这一系列的运行最后得到即将要跳转地址值X8,这种方式的计算效率非常高,只需要一次算法,就能找到 X8 的地址。
4. 跳转到X8的地址;
BR X8 ; switch jump
// 跳转到 X8为标号的地址执行
- Switch 内各个分支常量的差值较大的情况下:
源码
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void func(int a) {
switch (a) {
case 10:
printf("上路");
break;
case 102:
printf("中路");
break;
case 700:
printf("下路");
break;
case 24:
printf("野区");
break;
default:
printf("随机匹配");
break;
}
}
int main(int argc, char * argv[]) {
func(1);
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
汇编代码
__text:00000001000066D4 ; =============== S U B R O U T I N E =======================================
__text:00000001000066D4
__text:00000001000066D4 ; Attributes: bp-based frame
__text:00000001000066D4
__text:00000001000066D4 EXPORT _func
__text:00000001000066D4 _func ; CODE XREF: _main+20↓p
__text:00000001000066D4
__text:00000001000066D4 var_2C = -0x2C
__text:00000001000066D4 var_28 = -0x28
__text:00000001000066D4 var_24 = -0x24
__text:00000001000066D4 var_20 = -0x20
__text:00000001000066D4 var_1C = -0x1C
__text:00000001000066D4 var_18 = -0x18
__text:00000001000066D4 var_14 = -0x14
__text:00000001000066D4 var_10 = -0x10
__text:00000001000066D4 var_C = -0xC
__text:00000001000066D4 var_8 = -8
__text:00000001000066D4 var_4 = -4
__text:00000001000066D4 var_s0 = 0
__text:00000001000066D4
__text:00000001000066D4 SUB SP, SP, #0x40
__text:00000001000066D8 STP X29, X30, [SP,#0x30+var_s0]
__text:00000001000066DC ADD X29, SP, #0x30
__text:00000001000066E0 STUR W0, [X29,#var_4]
__text:00000001000066E4 LDUR W0, [X29,#var_4]
__text:00000001000066E8 MOV X8, X0
__text:00000001000066EC SUBS W0, W0, #0xA
__text:00000001000066F0 STUR W8, [X29,#var_8]
__text:00000001000066F4 STUR W0, [X29,#var_C]
__text:00000001000066F8 B.EQ loc_10000673C
__text:00000001000066FC B loc_100006700
__text:0000000100006700 ; ---------------------------------------------------------------------------
__text:0000000100006700
__text:0000000100006700 loc_100006700 ; CODE XREF: _func+28↑j
__text:0000000100006700 LDUR W8, [X29,#var_8]
__text:0000000100006704 SUBS W9, W8, #0x18
__text:0000000100006708 STUR W9, [X29,#var_10]
__text:000000010000670C B.EQ loc_100006778
__text:0000000100006710 B loc_100006714
__text:0000000100006714 ; ---------------------------------------------------------------------------
__text:0000000100006714
__text:0000000100006714 loc_100006714 ; CODE XREF: _func+3C↑j
__text:0000000100006714 LDUR W8, [X29,#var_8]
__text:0000000100006718 SUBS W9, W8, #0x66
__text:000000010000671C STUR W9, [X29,#var_14]
__text:0000000100006720 B.EQ loc_100006750
__text:0000000100006724 B loc_100006728
__text:0000000100006728 ; ---------------------------------------------------------------------------
__text:0000000100006728
__text:0000000100006728 loc_100006728 ; CODE XREF: _func+50↑j
__text:0000000100006728 LDUR W8, [X29,#var_8]
__text:000000010000672C SUBS W9, W8, #0x2BC
__text:0000000100006730 STR W9, [SP,#0x30+var_18]
__text:0000000100006734 B.EQ loc_100006764
__text:0000000100006738 B loc_10000678C
__text:000000010000673C ; ---------------------------------------------------------------------------
__text:000000010000673C
__text:000000010000673C loc_10000673C ; CODE XREF: _func+24↑j
__text:000000010000673C ADRP X0, #asc_100007F08@PAGE ; "上路"
__text:0000000100006740 ADD X0, X0, #asc_100007F08@PAGEOFF ; "上路"
__text:0000000100006744 BL _printf
__text:0000000100006748 STR W0, [SP,#0x30+var_1C]
__text:000000010000674C B loc_10000679C
__text:0000000100006750 ; ---------------------------------------------------------------------------
__text:0000000100006750
__text:0000000100006750 loc_100006750 ; CODE XREF: _func+4C↑j
__text:0000000100006750 ADRP X0, #asc_100007F0F@PAGE ; "中路"
__text:0000000100006754 ADD X0, X0, #asc_100007F0F@PAGEOFF ; "中路"
__text:0000000100006758 BL _printf
__text:000000010000675C STR W0, [SP,#0x30+var_20]
__text:0000000100006760 B loc_10000679C
__text:0000000100006764 ; ---------------------------------------------------------------------------
__text:0000000100006764
__text:0000000100006764 loc_100006764 ; CODE XREF: _func+60↑j
__text:0000000100006764 ADRP X0, #asc_100007F16@PAGE ; "下路"
__text:0000000100006768 ADD X0, X0, #asc_100007F16@PAGEOFF ; "下路"
__text:000000010000676C BL _printf
__text:0000000100006770 STR W0, [SP,#0x30+var_24]
__text:0000000100006774 B loc_10000679C
__text:0000000100006778 ; ---------------------------------------------------------------------------
__text:0000000100006778
__text:0000000100006778 loc_100006778 ; CODE XREF: _func+38↑j
__text:0000000100006778 ADRP X0, #asc_100007F1D@PAGE ; "野区"
__text:000000010000677C ADD X0, X0, #asc_100007F1D@PAGEOFF ; "野区"
__text:0000000100006780 BL _printf
__text:0000000100006784 STR W0, [SP,#0x30+var_28]
__text:0000000100006788 B loc_10000679C
__text:000000010000678C ; ---------------------------------------------------------------------------
__text:000000010000678C
__text:000000010000678C loc_10000678C ; CODE XREF: _func+64↑j
__text:000000010000678C ADRP X0, #asc_100007F24@PAGE ; "随机匹配"
__text:0000000100006790 ADD X0, X0, #asc_100007F24@PAGEOFF ; "随机匹配"
__text:0000000100006794 BL _printf
__text:0000000100006798 STR W0, [SP,#0x30+var_2C]
__text:000000010000679C
__text:000000010000679C loc_10000679C ; CODE XREF: _func+78↑j
__text:000000010000679C ; _func+8C↑j ...
__text:000000010000679C LDP X29, X30, [SP,#0x30+var_s0]
__text:00000001000067A0 ADD SP, SP, #0x40
__text:00000001000067A4 RET
__text:00000001000067A4 ; End of function _func
汇编代码分析
通过与 if...else 的汇编代码对比,可以得知类似于 if...else 条件判断语句。
小结: 在使用 switch语句 和 if...else语句执行效率高低的问题,是可以通过汇编代码得出结论: 当 switch 分支和 if...else的条件判断小于4的时候,执行效率是一样的; 当 switch 分支和 if...else的条件判断大于等于4的时候,switch 执行效率更高。
3 指令
- ARM64经常用到的汇编指令
MOV X1,X0 ;将寄存器X0的值传送到寄存器X1 (MOV Xd|SP, Xn|SP Move (extended register): alias for ADD Xd|SP,Xn|SP,#0, but only when one or other of the registers is SP. In other cases the ORR Xd,XZR,Xn instruction is used.)
ADD X0,X1,X2 ;寄存器X1和X2的值相加后传送到X0 (ADD Xd, Xn, Xm{, ashift #imm} Add (extended register): Xd = Xn + ashift(Xm, imm).)
SUB X0,X1,X2 ;寄存器X1和X2的值相减后传送到X0 (SUB Xd, Xn, Xm{, ashift #imm} Subtract (extended register): Xd = Xn - ashift(Xm, imm).)
AND X0,X0,#0xF ; X0的值与0xF相位与后的值传送到X0 (AND Xd, Xn, Xm{, lshift #imm} Bitwise AND (extended register): Xd = Xn AND lshift(Xm, imm).)
ORR X0,X0,#9 ; X0的值与9相位或后的值传送到X0 (ORR Xd, Xn, Xm{, lshift #imm} Bitwise inclusive OR (extended register): Xd = Xn OR lshift(Xm, imm).)
EOR X0,X0,#0xF ; X0的值与0xF相异或后的值传送到X0 (EOR Xd|SP, Xn, #bimm64 Bitwise exclusive OR (extended immediate): Xd|SP = Xn EOR bimm64.)
LDR X5,[X6,#0x08] ;X6寄存器加0x08的和的地址值内的数据传送到X5 (LDR Xt, addr Load Register (extended): loads a doubleword from memory addressed by addr to Xt.)
STR X0, [SP, #0x8] ;X0寄存器的数据传送到SP+0x8地址值指向的存储空间 (STR Xt, addr Store Register (extended): stores doubleword from Xt to memory addressed by addr.)
STP x29, x30, [sp, #0x10] ;入栈指令?(STP Xt1, Xt2, addr Store Pair Registers (extended): stores two doublewords from Xt1 and Xt2 to memory addressed by addr.)
LDP x29, x30, [sp, #0x10] ;出栈指令?(LDP Xt1, Xt2, addr Load Pair Registers (extended): loads two doublewords from memory addressed by addr to Xt1 and Xt2.)
CBZ ;Compare and Branch Zero,如果结果为零(Zero)就转移(只能跳到后面的指令)(CBZ Xn, label Compare and Branch Zero (extended): conditionally jumps to label if Xn is equal to zero.)
CBNZ ;比较,如果结果非零(Non Zero)就转移(只能跳到后面的指令)(CBNZ Xn, label Compare and Branch Not Zero (extended): conditionally jumps to label if Xn is not equal to zero.)
CMP ;比较指令,相当于SUBS,影响程序状态寄存器CPSR
B/BL ;绝对跳转#imm, 返回地址保存到LR(X30)(BL label Branch and Link: unconditionally jumps to pc-relative label, writing the address of the next sequential instruction to register X30.)
RET ;子程序返回指令,返回地址默认保存在LR(X30)(`RET {Xm}` Return: jumps to register Xm, with a hint to the CPU that this is a subroutine return. An assembler shall default to register X30 if Xm is omitted.)
其中 MOV指令只能用于寄存器之间传值,寄存器和内存之间传值通过 LDR和 STR
- ARM指令又一个重要特点就是所有指令都是带有条件的,就是说汇编中就需要根据状态寄存器中的一些状态来控制分支的执行。例如:
图中指令 b是跳转指令,后边跟着跳转的条件 eq,那么这个 eq是什么意思呢?
- ARM指令的结构(ARM指令编码表)
上图列出了不同种类ARM指令的编码格式,文章开头讲过ARM指令长度固定为 32bit即图中的0-31位。
28-31位是条件码,21-24为操作码,12-19为寄存器编号
上边提到的跳转条件eq实际就是28-31位对应的条件码,但是28-31位都是二进制数据不好记,所以就对二进制的条件码取了好记的助记符,例如 eq。
eq英文单词equal的意思,注意这里equal并不是c语言当中==的意思,这里根据状态寄存器的条件标志位Z来判断,如果Z = 1则eq成立,如果Z = 0则eq不成立,就是NE。
- ARM指令包含4位的条件码列表: | 操作码 | 条件码助记符 | 标志 | 含义 | | --- | :--- | :--- | :--- | | 0000 | EQ | Z=1 | 相等 | | 0001 | NE(Not Equal) | Z=0 | 不相等 | | 0010 | CS/HS(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 | 任何 | 从不执行 |
ARM指令所有指令都是带有条件的,默认是AL即无条件执行,当指令带有默认条件时不需要明确写出。
看个例子:
OC代码:
汇编代码:
汇编代码注释:
- 后续添加:
adrp是计算指定的符号地址到run time PC值的相对偏移
- STR Wt, addr
Store Register: stores word from Wt to memory addressed by
addr.
- STR Xt, addr
Store Register (extended): stores doubleword from Xt to
memory addressed by addr.
- STUR Wt, [base,#simm9]
Store (Unscaled) Register: stores word from Wt to memory addressed by base+simm9.
- STUR Xt, [base,#simm9]
Store (Unscaled) Register (extended): stores doubleword
from Xt to memory addressed by base+simm9.
- SCVTF Sm, Ro
Converts an integer value to a floating-point value.
- FMUL Sd, Sn, Sm
Multiplies two values.
参考:
iOS 中的 armv7,armv7s,arm64,i386,x86_64 都是什么
ARM 指令的条件码
ARM视频教程
iOS开发同学的arm64汇编入门
iOS逆向第五篇(ARM64 汇编)
《iOS应用逆向工程》