生成汇编文件
将c语言源文件编译成arm64汇编代码:
xcrun --sdk iphones clang -S -arch arm64 main.c -o main.s
寄存器
arm64有34个寄存器,包括通用寄存器和fp、lr、sp、pc、cpsr。
-
有些书上说有
x0~x30
是通用寄存器,有些则把fp
(x29)和lr
(x30)从中区分出来。 -
32bit的
w0~w28
相当于64bit的x0~x28
的低32bit
。
寄存器 | 描述 |
---|---|
x0~x7 | 传递子程序的参数和返回值,多余的参数用堆栈传递,使用时不需要保存。64bit将x0用来存放子程序的返回值 |
x8 | 用于保存子程序的返回地址,使用时不需要保存 |
x9~x15 | 临时寄存器,也叫可变寄存器,子程序使用时不需要保存 |
x16~x17 | 子程序内部调用寄存器,IPx ,使用时不需要保存,尽量不要使用 |
x18 | 平台寄存器,它的使用与平台相关,尽量不要使用 |
x19~x28 | 临时寄存器,子程序使用时必须保存 |
fp (x29) |
帧指针寄存器,(Frame Pointer),用于保存栈帧地址(栈底指针),使用时必须保存 |
lr (x30) |
链接寄存器,(Link Register),用于保存子程序的返回地址,也就是子程序结束后需要执行的下一条指令 |
sp |
堆栈指针寄存器,(Stack Pointer),用于指向每个函数的栈顶(栈顶指针) |
pc |
程序计数器,(Program Counter),俗称pc指针,总是指向即将要执行的下一条指令。在arm64中,软件是不能改写pc寄存器的 |
cpsr |
程序状态寄存器,(Current Program Status Register) |
spsr |
程序状态寄存器,(Saved Program Status Register),异常状态下使用 |
常用指令
ret:return
的缩写。子程序返回,将 lr
(x30)寄存器的值赋值给 pc
寄存器
ret
mov:move
的缩写。将后面一个寄存器的值或立即数加载到前面一个寄存器
mov x0, #0x5
mov x1, x0
(lldb) register read x0
x0 = 0x0000000000000005
(lldb) register read x1
x1 = 0x0000000000000005
add:将后面两个寄存器的值相加的结果,加载到第一个寄存器
mov x0, #0x1
mov x1, #0x2
add x2, x0, x1
(lldb) register read x0
x0 = 0x0000000000000001
(lldb) register read x1
x1 = 0x0000000000000002
(lldb) register read x2
x2 = 0x0000000000000003
sub:subtract
的缩写。将后面两个寄存器相减的结果,加载到第一个寄存器
mov x0, #0x5
mov x1, #0x2
sub x2, x0, x1
(lldb) register read x0
x0 = 0x0000000000000005
(lldb) register read x1
x1 = 0x0000000000000002
(lldb) register read x2
x2 = 0x0000000000000003
cmp:compare
的缩写。将两个寄存器相减,相减的结果会影响 cpsr
中条件标志位的值
mov x0, #0x3
mov x1, #0x1
cmp x0, x1
cpsr
结构:
cpsr
标志位含义:
b:不带返回的跳转指令。可以带条件跳转,带条件后一般跟 cmp
指令配合使用
;b指令带条件
mov x0, #0x1
mov x1, #0x2
cmp x0, x1
beq mycode1
mov x0, #0x3
mycode1:
mov x0, #0x4
;b指令不带条件
b mycode2
mov x0, #0x5
mycode2:
mov x0, #0x6
指令的条件码:
bl:带返回的跳转指令。有点类似函数调用,执行完子函数后会回到上一个函数。先将下一条指令的地址存储到 lr
(x30)寄存器中,然后跳转到标记处开始执行代码。
mycode:
mov x0, #0x1
mov x1, #0x2
add x2, x0, x1
ret
_test:
bl mycode
mov x3, #0x3
mov x4, #0x4
ret
load:从内存中读取数据
ldr
(立即数是正数用这个)、ldur
(立即数是负数用这个)
;64bit是读取8字节,32bit是读取4字节
ldr x0, [x1] ;从x1寄存器读取数据到x0寄存器
ldr x0, [x1, #0x4] ;x1寄存器地址偏移4,再从偏移后的地址读取数据存到x0寄存器
ldr x0, [x1, #0x4]! ;x1寄存器地址偏移4,读取数据存储到x1寄存器,再存到x0寄存器
ldr x0, [x1], #0x4 ;x1寄存器读取数据到x0寄存器,再将x1寄存器地址偏移4,然后读取数据存储到x1寄存器
ldr x0, [x1, x2] ;x1寄存器地址偏移x2寄存器存储的值,然后读取数据存储到x0寄存器
ldp
,p是pair的简称,翻译成一对。
ldp x0, x1, [x2, #0x10] ;x2寄存器地址偏移10,再从偏移后的地址读取数据存储到x0寄存器和x1寄存器
store:将数据写入到内存中
str
、stur
str x0, [x1] ;从x0寄存器读取数据,写入x1寄存器的地址中
stp
stp x0, x1, [x2] ;从x0寄存器和x1寄存器读取数据,写入x2寄存器的地址中
零寄存器,里面存储的值是0:wzr
(32bit,Word Zero Register)、xzr
(64bit)
函数的类型
叶子函数:函数里面没有再调用其他函数。
非叶子函数:函数里面还调用了其他函数。
简单调用汇编代码
arm.h
#ifndef arm_h
#define arm_h
int add(int a, int b);
int sub(int a, int b);
#endif /* arm_h */
arm.s
.text ;存放到代码段
.global _add, _sub ;公开函数访问权限
_add:
add x0, x0, x1 ;x0 x1是函数的参数,最终计算结果存入x0,并作为返回值返回
ret
_sub:
sub x0, x0, x1 ;x0 x1是函数的参数,最终计算结果存入x0,并作为返回值返回
ret
汇编代码里的单行注释用;
,立即数的格式为#十六进制
,比如#0x0
。