前言
- 汇编语言是面向机器的程序设计语言,可以将其看成是各种 CPU 的机器指令的助记符集合。程序员可以使用汇编代码直接控制硬件系统工作,而且用汇编语言编写的程序具备执行速度快和占用内存少等优点。
在 Apple 平台上主流的汇编语言有 x86 汇编 和 ARM 汇编,
在移动设备上使用的是 ARM 汇编
- 这主要是因为 ARM 采用的是 RISC 架构,具备功耗低的优势
桌面平台使用的则是 x86 汇编。
- iOS 模拟器的程序实际就是以 iOS 模拟器作为容器,运行在该容器中的 Mac OS 程序,所以它使用的汇编也是 x86 汇编。
AT&T 和 Intel
x86 汇编语言演变出两个语法分支: Intel和 AT&T,其中 Intel 语法在 MS-DOS 和 Windows 家族中占主导地位,而 AT&T 语法则常见于 UNIX 家族中。
- 值得注意的是在 Xcode 中的汇编语法使用的是 AT&T
我经常使用的 Hopper Disassemble V4 主要用来查看OC的伪代码,那Hopper是属于AT&T 还是 Intel分支?
- 答案是:反汇编工具 Hopper Disassemble 和 IDA Pro 使用的是 Intel 汇编语法。
寄存器
内存(Memory)为 CPU 存放指令和数据,内存本质上就是一个字节数组。虽然访问内存的速度相对来说已经很快了,但是仍然需要一个容量更小的,速度更快的存储单元来加快 CPU 执行指令的速度,这种存储单元就是寄存器。处理器在执行指令的过程中,所有数据在寄存器里面都只是临时存放的,然后又会被送往别处,这也是“寄存器”得名的原因
使用 LLDB 的
register read
命令可以 dump 出当前帧的寄存器的值。register read -a 或 register read --all
函数
一个函数调用包括将数据(以参数和返回值的形式)和控制从代码的一部分转移到另一部分。在函数调用过程中,数据传递、局部变量的分配和释放是通过栈来实现的,而为单个函数调用分配的那部分栈称为栈帧(Stack Frame)。
栈帧
使用 LLDB 调试的过程中我们可能会使用 bt
命令打印出当前线程的回溯信息,如下:
I、arm 架构和指令集
iPhone5s之前的手机都是armv7及以下的架构,iPhone5s及之后的手机都是已经支持armv8架构
- arm 定义了3中体系结构
- Microcontroller 体系,面向各类嵌入式应用
- 支持T32指令集的变体
- Real-time 系统,为具有严格的实时响应限制的深层嵌入式系统提供高性能的计算解决方案
- 支持A32、T32 指令集
- application体系
- 支持基于内存管理的虚拟内存体系结构
- 支持A64、A32、T32指令集
https://developer.arm.com/ 下载官方手册
II、 aarch64寄存器
2.1 寄存器
R0-R30 是31个通用寄存器,每个寄存器有如下两种访问方式
64位通用寄存器名为x0-x30
32位通用寄存器名为w0-w30
2.2 进程状态pstate
- 所有的指令集都提供了操作pstate元素的指令
- N: 正负标志
- Z:零标志
- C:
- 当加法运算产生进位的时候,值为1,否则为0;
- 当减法运算产生借位的时候,值为0,否则为1;
- V: 在加减运算指令中,当操作数和运算结果为二进制的补码表示的带符号数时,1表示符号溢出
以上均为条件标志位.
III、指令集编码
IDA、lldb 利用“arm的指令编码规则”将机器码(0、1)翻译成汇编代码
III、aarch64 指令
根据功能分类有
数据处理指令
算术指令
CMN: cmn w0,#0x10
- w0和0x10 相加,并影响条件标志位
CMP : amp w0,#0x0
- w0和0 相减,并影响条件标志位
SUB sub w0,w1,w2
- w0 =w1-w2
ADDS、SUBs : adds x0,x1,x2 ; 后面带s 表示计算的结果影响条件标志位
ADD add x0,x1,x2
- x0 =x1+x2
逻辑指令
- ORR orr w0,w1,w2
wo = w1 w2 - EOR
- eor w0,w1,w2 w0=w1^w2
- AND and xo,x1,x2
- x0 = x1 & x2, ;ANDS 表示影响条件标志位
数据传输指令
- mov: mov x0,x1
- x0 = x1
地址偏移指令
移位运算指令
- LSL lsl Xd,Xn,#uimm 逻辑左移,移位后寄存器空出的低位补0
加载存储指令
- 加载指令可以使用立即数、寄存器、对齐寄存器作为偏移
- STR
- STR Xn/Wn,addr,将 Xn/Wn写入addr地址指向的内存
- str w0,[sp,#0xc];保存参数1
- LDR
- LDR Xn/Wn,addr 从addr地址中读取8/4字节内容到Xn/Wn中
- ldr x1,[x2,#4]; 读取地址x2+4 处的值到x1中
- ldr x1,[x2,#4]!; 读取地址x2+4 处的值到x1中,然后执行x2=x2+4
- ldr x1,[sp],#0x4; 读取地址sp处的值到x1中,然后执行sp=sp+4
- ldr x1,[x2,x3]; 读取地址x2+x3 处的值到x1中
- ldr x1,[x2,x3,LSL #3]; 读取地址x2+x3x8 处的值到x1中
跳转指令
- 条件调转指令
- 无条件跳转指令:
- ret 子程序返回
IV 、栈和方法
- 进程中的一种特殊的内存区域, 在调用一个函数时用于保存一些临时数据(局部变量和上下文环境);根据栈地址的增长方向和栈顶指针指向的位置,可以将其分为4类
- 向高地址方向增长,称为递增堆栈
- 向低地址方向增长,称为递减堆栈
- 堆栈指针指向下一个要放入的位置,称为空堆栈
- 堆栈指针指向最后压入堆栈的有效数据项,称为满堆栈
- 数据结构中的栈
- 限定只能在在一端进行插入和删除操作的线性表
arm 堆栈具有后进先出、满递减的特点。
- 函数调用会开辟栈帧。在aarch64中,函数的参数是通过x0-x7传递的;如果是小数则是通过d0,d1传递的(P $d0);与函数堆栈相关的寄存器
- PC(Program Counter)记录当前执行代码的地址
- SP(stack pointer)指向栈帧的指针,在内存操作指令中通过x31寄存器来访问
- LR(link register)指向返回地址,对应于寄存器x30
- FP(frame pointer) 指向栈帧的底部,对应于寄存器x29———R7 是aarch32的FP寄存器
栈帧
- 参数区(parameter area) 存放调用函数传递的参数
- 连接区(linkage area)存放调用者(caller)的下一条指令
- 栈帧指针存放区(frame pointer)存放调用函数的栈帧的底部
- 寄存器存储区(saved registers area)被调用函数(callee)返回需要恢复的寄存器内容
- 局部存储区(local storage area)用于存放被调用函数(callee)的局部变量
- 读取栈参数
- register red 读取寄存器参数
- memory read -f A $sp $fp
See Also
iPhone尺寸规格
iPhone尺寸规格
/Users/devzkn/bin//knpost arm arm汇编 -t Assembly #原来""的参数,需要自己加上""