1. 寄存器
ARMv8-aarch64 寄存器和指令集
1.1 通用寄存器(X0-31
)
- 参数寄存器(
X0 - X7
): 用于传递函数的前面8个参数,多余8个参数的,超出部分通过栈传递 - 返回值寄存器:(
X0,X8
): 用于保存返回值,如果是基本数据类型,返回值保存在X0
,如果是结构体,结果的地址将保存在X8
- 调用者保存的临时寄存器(
X9-X15
):如果调用者用到这些寄存器,在调用另外一个函数前,调用者必需要将受影响的寄存器保存在自己的堆栈中.它们可以通过被调用的子程序进行修改而无需保存并在返回调用者之前恢复它们 - 被调用者保存的寄存器(
X19-X29
):这些寄存器保存在被调用者帧中。 它们可以被被调用者修改,只要它们在返回之前保存并恢复。
1.2 特殊用途寄存器(X8,X16-X18,X29,X30,X31
):
-
XR(X8)
: 是间接结果寄存器,用于保存子程序返回地址,尽量不使用 -
X16 和 X17
: 程序内调用临时寄存器 -
X18
: 平台寄存器,保留用于平台 ABI,尽量不使用 -
FP(X29, Frame Pointer)
: 帧指针寄存器(FP),存储着当前栈底的地址 -
LR(X30, Link Register)
: 链接寄存器(LR) -
SP(X31, Stack Pointer)
: 堆栈指针寄存器 ,存储的是栈顶的地址 -
PC
:PC寄存器中存的是当前执行的指令的地址。在arm64中,软件是不能改写PC寄存器的
1.3 向量寄存器(浮点寄存器):V0 - V31
V0 - V31
是向量寄存器,也可以说是浮点型寄存器。它的特点是每个寄存器的大小是 128 位的。 分别可以用 Bn Hn Sn Dn Qn
的方式来访问不同的位数:
-
Bn
: 一个Byte的大小 -
Hn
: half word. 就是16位 -
Sn
: single word. 32位 -
Dn
: double word. 64位 -
Qn
: quad word. 128位
2. 栈
栈就是指令执行时存放临时变量的内存空间。在学习汇编代码的执行过程中,了解栈的结构非常重要。一下是栈的一些特性:
- 栈是从高地址到低地址扩展的, 栈底是高地址,栈顶是低地址。
-
FP
指向当前frame的栈底,也就是高地址。 -
SP
指向栈顶,也就是低地址。
其中3行汇编代码就是方法B的前三行汇编指令。它们做的事情就是图中描述的事情 (x29就是fp, x30就是lr):
- 将
fp, lr
保存到sp - 0x10
的地方. 也就是图中--> fp_B
的位置。然后将sp
设置为sp-0x10
- 将
fp
设置为当前sp
。也就是--> fp_B
的位置。 这一步就设置了_funcB
的fp
了 - 将
sp
设置为sp - 0x30
。 也就是将sp
指向了图中--> sp_B
的位置
注: lr 是
link register
中的值,它存的是方法_funcA的执行的最后一行指令的下一行。它的作用是:当_funcB执行完了之后要返回_funcA继续执行,但是计算机要如何知道返回到哪执行呢? 就是靠lr记录了返回的地址,方法才能得以正常返回。
当 _funcB执行完毕后,是如何把栈恢复到_funcA的过程的呢? 我们直接分析 _funcB的最后3条指令:
mov sp, fp; //sp 设置为fp, 就是图中 -->fp_B 的位置
ldp fp, lr, [sp], #0x10; // 从sp指向的地址中读取 2个64位,分别存入fp,lr。 然后将sp += 0x10
// 这一步执行完之后,fp就执行了图中 -->fp_A. lr恢复成 _funcA的返回地址。 sp指向了 -->sp_A.
// 这个时候状态已经完全恢复到了 _funcA 的环境
ret; // 返回指令,这一步直接执行lr的指令。
上面描述了方法如何调用的。我们知道在编程语言里面方法都有入参,有返回值的。在汇编里面如何体现呢?
- 一般来说 arm64上 x0 – x7 分别会存放方法的前 8 个参数
- 如果参数个数超过了8个,多余的参数会存在栈上,新方法会通过栈来读取。
- 方法的返回值一般都在 x0 上。
- 如果方法返回值是一个较大的数据结构时,结果会存在 x8 执行的地址上
2 指令
2.1 Loads and stores
LDR , []
STR , []
3.1.1 Size
LDR
和 STR
的大小由目标寄存器决定
LDR W0, [] ;32bits
LDR X0, [] ;64bits
LDR/STR指令也可以限定大小
STRB W0, [] ;stores the bottom byte (B) of W0 to
STRH W0, [] ;stores the bottom halfword (H) of W0 to :
STRW X0, [] ;stores the bottom word (W) of X0 to :
3.1.2 Zero and sign extension
-
LDR/STR
默认是0填充多余的位,如
-
LDRS/STRS
可以通过加S后缀来做符号扩展,如
3.1.3 寻址方式
-
Base register
-
Offset addressing modes
-
Pre-index addressing modes
-
Post-index addressing modes
3.1.4 Load pair and store pair
LDP W3, W7, [X0] ; loads [X0] into W3, and loads [X0 + 4] into W7
STP D0, D1, [X4] ; stores D0 into [X4] and stores D1 to [X4 + 8]:
STP X0, X1, [SP, #-16]! ; pushes X0 and X1 onto the stack:
LDP X0, X1, [SP], #16 ;pops X0 and X1 from the stack
procedure-call-standard
入参
- 前面八个参数保存在
X0
-X7
寄存器 - 如果参数个数大于8个,则后面的参数保存在栈里
返回值
-
X0
: 返回值 -
XR
(X8
):间接结果寄存器,如果返回的是结构体,则结构体地址保存在XR
参考
- ARM64 ABI 约定概述
- AArch64 Instruction Set Architecture
- OS X Assembler Reference
- iOS开发同学的arm64汇编入门
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0000000101da06b0 QQKSong`initData(forWhat="") at WSHookObjcMsgSend.cpp:141:10
frame #1: 0x0000000101da0c48 QQKSong`::startHook(forWhat="") at WSHookObjcMsgSend.cpp:193:5
frame #2: 0x0000000100de3ad8 QQKSong`main(argc=1, argv=0x000000016f027760) at main.m:76:5
frame #3: 0x00000001aad298f0 libdyld.dylib`start + 4
(lldb) register read
General Purpose Registers:
fp = 0x000000016f027640
lr = 0x0000000101da0c48 QQKSong`::startHook(char *) + 24 at WSHookObjcMsgSend.cpp
sp = 0x000000016f027620
pc = 0x0000000101da06b0 QQKSong`initData(char*) + 32 at WSHookObjcMsgSend.cpp:141:10
cpsr = 0x60000000
- 第0层栈底内容(栈底:
$fp
)
(lldb) x/20x $fp
0x16f027640: 0x000000016f027670 0x0000000101da0c48
0x16f027650: 0x00000001092b15d0 0x0000000104487880
0x16f027660: 0x00000001092452c0 0x00000001092a8040
0x16f027670: 0x000000016f027720 0x0000000100de3ad8
0x16f027680: 0x0000000042000000 0x0000000109245260
0x16f027690: 0x00000001092a8010 0x000000016f0276b0
0x16f0276a0: 0x0000000109244000 0x0000000109244000
0x16f0276b0: 0x0000000000000000 0x000000016f0276b0
0x16f0276c0: 0x0000003002000000 0x000000010924524c
0x16f0276d0: 0x0000000109245258 0x0000000000000000
0x16f0276e0: 0x0000000000000000 0x0000000000000000
0x16f0276f0: 0x000000016f027760 0x0000000000000001
0x16f027700: 0x0000000000000000 0x0000000000000000
0x16f027710: 0x0000000000000000 0x730d36aa5ab60031
0x16f027720: 0x000000016f027740 0x00000001aad298f0
0x16f027730: 0x00000001aad298f0 0x0000000000000000
0x16f027740: 0x0000000000000000 0x0000000000000000
-
0x000000016f027670
: 上一个堆栈的栈底 - '0x0000000101da0c48': 上层调用函数返回地址,也就是
QQKSong
::startHook(char *)`
(lldb) image lookup -a 0x0000000101da0c48
Address: QQKSong[0x0000000100fc8c48] (QQKSong.__TEXT.__text + 16532744)
Summary: QQKSong`::startHook(char *) + 24 at WSHookObjcMsgSend.cpp
- 第1层栈底内容(栈底:
0x16f027670
)
-
0x000000016f027720
: 上一个堆栈的栈底 - '0x0000000100de3ad8': 上层调用函数返回地址,也就是
QQKSong main
(lldb) image lookup -a 0x0000000100de3ad8
Address: QQKSong[0x000000010000bad8] (QQKSong.__TEXT.__text + 29592)
Summary: QQKSong`main + 52 at main.m
- 第2层栈底内容(栈底:
0x16f027720
)
-
0x000000016f027740
: 上一个堆栈的栈底 - '0x00000001aad298f0': 上层调用函数返回地址,也就是
libdyld.dylib start
(lldb) image lookup -a 0x00000001aad298f0
Address: libdyld.dylib[0x00000001802998f0] (libdyld.dylib.__TEXT.__text + 4)
Summary: libdyld.dylib`start + 4