arm64e: iphone XS | iphone XS Max | iPhoneXR
arm64: iPhone 8 | iPhone 8 Plus | iPhone X | iPhone 7/7 Plus | iPad (2018) | iPhone 6/6S | iPhone 6/6S Plus | iPhone 5s
armv7s: iPhone 5 | iPhone5C | iPad4(Retina屏) |
armv7: iPhone 4 | iPhone4S | iPad 2/3 | iPad mini | iPod Touch 3G | iPod Touch4
armv7/armv7s/i386 架构使用的是 32 位的处理器
arm64/x86_64 架构使用的是 64 位的处理器
查看 framework 包含的架构命令:lipo
寄存器
|
描述
|
r0 - r30
|
通用
整形寄存器,64 位,当使用
x0 - x30 访问时,代表的是 64 位的数;当使用
w0 - w30 访问的时候,访问的是这些寄存器的低 32 位
|
fp(x29)
|
保存栈帧地址(
栈底指针)
|
lr(x30)
|
通常称x30为程序链接寄存器,保存子程序结束后需要
执行的下一条指令
|
sp
|
保存栈指针,使用 sp/wsp 来进行对 sp 寄存器的访问
|
pc
|
pc 寄存器中存的是当前执行的指令的地址,在 arm64 中,软件是不能改写 pc 寄存器的
|
SPRs
|
状态寄存器,存放状态标识,可分为 CPSR (The Current Program Status Register) 和 SPSRs(The Save Program Status Registers)。一般都是使用 CPSR,当发生异常时,CPSR 会存入 SPSR。当异常恢复,再拷贝回 CPSR
|
zr |
零寄存器,里面存的是 0 (zero register)一般使用 wzr/xzr ,w 代表 32位,x 代表 64 位
|
v0 - v31
|
向量寄存器,也可以说是浮点型寄存器,每个寄存器大小是 128 位,可以用 Bn Hn Sn Dn Qn 来访问不同的位数(8 16 32 64 128)
|
mov x1,x0 ;将寄存器x0值 赋值 给x1
add x0,x1,x2 ;x0 = x1 + x2
sub x0,x1,x2 ;x0 = x1 - x2
mul x0,x1,x2 ;x0 = x1 * x2
sdiv x0,x1,x2 ;x0 = x1 / x2;
and x0,x0,#0xF ;x0 = x0 & #0xF (与操作)
orr x0,x0,#9 ;x0 = x0 | #9 (或操作)
eor x0,x0,#0xF ;x0 = x0 ^ #0xF (异或操作)
ldr x0,[x1] ;从 x1 指向的地址里面取出一个64位大小的数存入x0
ldp x1,x2,[x10, #0x10] ;从 x10+0x10 指向的地址里面取出2个64位的数,分别存入x1、x2
str x5,[sp, #24] ;往内存中写数据(偏移值为正), 把 x5 的值(64位的数值)存到 sp+24 指向的地址内存上
stur w0,[x29, #0x8] ;往内存中写数据(偏移值为负),将 w0 的值存储到 x29 - 0x8 这个地址里
stp x29,x30,[sp, #-16]! ;把 x29、x30 的值存到 sp-16 的地址上,并且把sp-=16 Note:后面有个感叹号的,然后没有stup这个指令哈
ldp x29,x30,[sp],#16 ;从 sp 地址取出16 byte数据,分别存入x29、x30,然后 sp+=16
mov x0
[x10, #0x10] ;从 x10+0x10 的地址取值
[sp, #-16]! ;从 sp-16 地址取值,取完后再把 sp-16 writeback 回 sp
[sp], #16 v从 sp 地址取值,取完后把 sp+16 writeback 回 sp
跳转指令
bl/b bl 是有返回的跳转;b 是无返回的跳转
1.有返回的意思就是会存 lr ,存了 lr 也就意味着可以返回到本方法继续执行,一般用于不同方法直接的调用;2.无返回的一般是方法内的跳转,如 while 循环, if else 等。
int main() {
int a = 2;
int b = 3;
}
上面的代码中,系统开始执行 main 函数的时,会为它在内存里面建立一个帧(frame),所有 main 的内部变量(比如a和b)都保存在这个帧里面。main 函数执行结束后,该帧就会被回收,释放所有的内部变量,不再占用空间。
int main() {
int a = 2;
int b = 3;
return test(a, b);
}
上面的代码中,main 函数内部调动了 test 函数。当执行到这一步的时候,系统也会为 test 新建一个帧,用来存储它的内部变量。也就是说,此时同时存在两个帧:main 和 test。一般来说,调用栈有多少层,就有多少帧。
// hello.c
#include
int test(int a, int b) {
int res = a + b;
return res;
}
int main() {
int res = test(1, 2);
return 0;
}
使用clang命令将其编译成arm64指令集汇编代码
clang -S -arch arm64 -isysroot `xcrun --sdk iphoneos --show-sdk-path` hello.c
可以看到完整的汇编如下:
.section __TEXT,__text,regular,pure_instructions
.build_version ios, 13, 2 sdk_version 13, 2
.globl _test ; -- Begin function test
.p2align 2
_test: ; @test
.cfi_startproc
; %bb.0:
sub sp, sp, #16 ; =16
.cfi_def_cfa_offset 16
str w0, [sp, #12]
str w1, [sp, #8]
ldr w0, [sp, #12]
ldr w1, [sp, #8]
add w0, w0, w1
str w0, [sp, #4]
ldr w0, [sp, #4]
add sp, sp, #16 ; =16
ret
.cfi_endproc
; -- End function
.globl _main ; -- Begin function main
.p2align 2
_main: ; @main
.cfi_startproc
; %bb.0:
sub sp, sp, #32 ; =32
stp x29, x30, [sp, #16] ; 16-byte Folded Spill
add x29, sp, #16 ; =16
.cfi_def_cfa w29, 16
.cfi_offset w30, -8
.cfi_offset w29, -16
stur wzr, [x29, #-4]
orr w0, wzr, #0x1
orr w1, wzr, #0x2
bl _test
str w0, [sp, #8]
mov w0, #0
ldp x29, x30, [sp, #16] ; 16-byte Folded Reload
add sp, sp, #32 ; =32
ret
.cfi_endproc
; -- End function
.subsections_via_symbols
先看第一部分:
.section __TEXT,__text,regular,pure_instructions
.build_version ios, 13, 2 sdk_version 13, 2
.globl _test ; -- Begin function test
.p2align 2
.section 里面的 __TEXT,__text 字段用来存放代码指令.build_version 是编译版本信息.globl _test 声明了全局变量(函数); -- Begin function test 分号后面是注释的意思.p2align 2 用于指定程序的对齐方式,这类似于结构体的字节对齐,为的是加速程序的执行速度,p2align 的单位是指数,即按照 2 的 n 次方对齐,这里的 .p2align 2 表示按照 2^2 = 4 字节对齐,如果单行指令数据长度不足4字节,将用 0 补全,超过 4 但不是 4 的倍数,则按照最小倍数补全_test、_main 称之为标签(label),用于辅助定位代码或者资源地址,也方便开发者理解和记忆
.cfi_startproc ;定义函数开始
.cfi_endproc ;定义函数结束
.cfi_xxx ;call frame information xxx, cfi 是 DWARF 2.0 定义的函数栈信息,用来告诉编译器生成响应的 DWARF 调试信息,主要是和函数有关。
sub sp, sp, #32 ; =32
stp x29, x30, [sp, #16] ; 16-byte Folded Spill
add x29, sp, #16 ; =16
ldp x29, x30, [sp, #16] ; 16-byte Folded Reload
add sp, sp, #32 ; =32
ret
先看 test 函数的实现:
//源代码
int test(int a, int b) {
int res = a + b;
return res;
}
//汇编
sub sp, sp, #16 ; =16
.cfi_def_cfa_offset 16
str w0, [sp, #12]
str w1, [sp, #8]
ldr w0, [sp, #12]
ldr w1, [sp, #8]
add w0, w0, w1
str w0, [sp, #4]
ldr w0, [sp, #4]
add sp, sp, #16 ; =16
ret
sp (stack pointer)是栈指针,永远指向栈顶!
sub sp, sp, #16 ; =16
str w0, [sp, #12]
str w1, [sp, #8]
ldr w0, [sp, #12]
ldr w1, [sp, #8]
add w0, w0, w1
str w0, [sp, #4]
ldr w0, [sp, #4]
add sp, sp, #16 ; =16
ret
sub sp, sp, #32 ; =32
stp x29, x30, [sp, #16] ; 16-byte Folded Spill
add x29, sp, #16 ; =16
----------------------------------------------------prologue-----------------------------------------------------------------
.cfi_def_cfa w29, 16
.cfi_offset w30, -8
.cfi_offset w29, -16
stur wzr, [x29, #-4]
orr w0, wzr, #0x1
orr w1, wzr, #0x2
bl _test
str w0, [sp, #8]
mov w0, #0
----------------------------------------------------epilogue-----------------------------------------------------------------
ldp x29, x30, [sp, #16] ; 16-byte Folded Reload
add sp, sp, #32 ; =32
ret