arm架构64位⼊门基础:架构分析、寄存器、调⽤规则、指令
集以及参考⼿册
1、参考
2、ARM64位架构分析
ARM64位采⽤ARMv8架构,64位操作长度,对应处理器有Cortex-A53、Cortex-A57、Cortex-A73、iphones的A7和A8等。AARCH64是全新32位固定长度指令集,⽀持64位操作数的新指令,⼤多数指令可以具有32位或64位参数。
ARM64位架构有两种主要的执⾏状态:
1. AArch64 ——64 位执⾏状态,包括该状态的异常模型、内存模型、程序员模型和指令集⽀持
2. AArch32 ——32 位执⾏状态,包括该状态的异常模型、内存模型、程序员模型和指令集⽀持
这些执⾏状态⽀持三个主要指令集:
1. A32(或 ARM):32 位固定长度指令集,通过不同架构变体增强部分 32 位架构执⾏环境现在称为 A
Arch32。
2. T32 (Thumb) 是以 16 位固定长度指令集的形式引⼊的,随后在引⼊ Thumb-2 技术时增强为 16 位和 32 位混合长度指令集。部
分 32 位架构执⾏环境现在称为 AArch32。
3. A64:提供与 ARM 和 Thumb 指令集类似功能的 32 位固定长度指令集。随 ARMv8-A ⼀起引⼊,它是⼀种 全新的AArch64 指
令集。
ARM ISA 不断改进,以满⾜前沿应⽤程序开发⼈员⽇益增长的要求,同时保留了必要的向后兼容性,以保护软件开发投资。在ARMv8-A 中,对 A32 和 T32 进⾏了⼀些增补,以保持与 A64 指令集⼀致。
3、ARM64位寄存器
主要包括64位下的ARM寄存器和NEON寄存器。
ARM架构64位寄存器:
31个通⽤寄存器X0~X30,以及SP(x31)和PC,共33个。其中W0~W31分别是X0~X31的低32位,如下图所⽰:
64位下通⽤寄存器关系图
ARM64位参数调⽤规则遵循AAPCS64,规定堆栈为满递减堆栈。
寄存器调⽤规则如下:
- X0~X7:⽤于传递⼦程序参数和结果,使⽤时不需要保存,多余参数采⽤堆栈传递,64位返回结果采⽤X0表⽰,128位返回结果采⽤X1:X0表⽰。
- X8:⽤于保存⼦程序返回地址, 尽量不要使⽤ 。
- X9~X15:临时寄存器,使⽤时不需要保存。
- X16~X17:⼦程序内部调⽤寄存器,使⽤时不需要保存,尽量不要使⽤。
- X18:平台寄存器,它的使⽤与平台相关,尽量不要使⽤。
- X19~X28:临时寄存器,使⽤时必须保存。
- X29:帧指针寄存器,⽤于连接栈帧,使⽤时需要保存。
- X30:链接寄存器LR
- X31:堆栈指针寄存器SP或零寄存器ZXR
注意:
⼦程序调⽤时必须要保存的寄存器:X19~X29和SP(X31)。
不需要保存的寄存器:X0~X7,X9~X1564位下NEON寄存器:32个B寄存器(B0~B31),8bit 32个H寄存器(H0~H31),半字 16bit 32个S寄存器(S0~S31),单⼦ 32bit 32个D寄存器(D0~D31),双字 64bit
32个Q寄存器(V0~V31),四字 128bit
不同位数下寄存器之间的关系如下图所⽰:
其中S0是D0的低半部分,D0是V0的低半部分 。
4、ARM64位指令集A64以及参考⼿册
5、ARM64位程序调试
⽅法⼀: 直接打印数据
ARM64位下打印数据的⽅法:
(1). 打印V寄存器:(2). 打印V寄存器的低64位:
mov    w0, v0.s[0]
mov    w1, v0.s[1]
mov    w2, v0.s[2]
mov    w3, v0.s[3]
bl      _print
mov    w0, v2.s[0]
mov    w1, v2.s[1]
bl      _print
(3). 打印w寄存器
mov    w0, w12
mov    w1, w3
bl      _print
其中print函数的定义如下:
void print(int a, int b, int c, int d)
{
printf("%08x %08x %08x %08x\n",a,b,c,d);
}
(4).将V寄存器打印到内存的⽅法
.
macro printf_m in1=x0, in2=x1
st1  {\in2\().2D}, [\in1\()]
mov  x0,  \in1
bl    cprintf
.endm
c printf定义如下:
void cprint(unsigned char *src8)
{
signed char* srcs8 = (signed char*)src8;
short* srcs16 = (short*)src8;
unsigned short* srcu16 = (unsigned short*)src8;
int* srcs32 = (int*)src8;
printf("u8:\n");
for(int i=0; i < 16; i++)
{
printf("%d", src8[i]);
}
printf("s8:\n");
for(int i=0; i < 16; i++)
{
printf("%d", srcs8[i]);
}
printf("u16:\n");
for(int i=0; i < 8; i++)
{
printf("%d", srcu16[i]);
}
printf("s16:\n");
for(int i=0; i < 8; i++)
{
printf("%d", srcs16[i]);
}
printf("s32:\n");
for(int i=0; i < 4; i++)
{
printf("%d", srcs32[i]);
}
}
⽅法⼆: GDB调试
详细调试⽅法可以参考:
对于neon寄存器⼊栈:
.macro push_v_regs
stp    d8,  d9,  [sp, #-16]!
stp    d10, d11, [sp, #-16]!
stp    d12, d13, [sp, #-16]!
stp    d14, d15, [sp, #-16]!
.endm
.macro pop_v_regs
ldp    d14, d15, [sp], #16
ldp    d12, d13, [sp], #16
ldp    d10, d11, [sp], #16
ldp    d8,  d9,  [sp], #16
.endm
Registers v8-v15 must be preserved by a callee across subroutine calls; the remaining registers (v0-v7, v16-v31) do not need to be preserved (or should be preserved by the caller). Additionally, only the bottom 64-bits of each value stored in v8-v15 need to be preserved; it is the responsibility of the
caller to preserve larger values.
但是在对v寄存器⼊栈后,采⽤gdb调试会出现下⾯的问题:
x86架构和arm架构区别
/build/gdb-qLNsm9/gdb-7.11.1/gdb/aarch64-tdep.c:334: internal-error: aarch64_analyze_prologue: Assertion inst.operands[0].type == AACH64_OPAND_Rt failed.
6、IOS64和ARM64的参数传递差异和编译差异
(1)ARM64参数⼊栈都要保证8字节对齐,跟数据类型⽆关,⽽IOS64的参数⼊栈跟数据类型有关;
(2)ARM64参数传递是成对传递的,⽐如(x0,x1),(x2,x3)等,⽽IOS64的参数传递并不应遵守这⼀准则;
(3)ARM和IOS编译的差别:
ARM在Linux下编译gcc早期版本函数名前需要添加下划线,⽬前最新版本的gcc(4.4.7)不需要添加,这与gcc编译版本相关;IOS平台下编译都需要添加下划线:“_”。
7、ARM64位加载和存储数据的⼏种格式
ld1 {v20.8H, v21.8H}, [x1]  @ 从x1指向的存储单元位置⼀次性加载128*2位数据到v20和v21中
ld1 {v1.8B},    [x1],  x2  @ 从x1指向的存储单元位置加载64位数据到v1的低64位中,然后x1=x1+x2
ld1 {v18.S}[0], [x0],  x1  @ 将x0地址⾥⾯的数据取32位加载到v18的最低32位,然后x0=x0+x1
ld1r {v30.8H},  [x1]        @ 从x1地址中以16位为单位取128位加载到v30中。
st1 {v30.8H},  [x1],  #16 @ 将寄存器v30中128位数据存储到x1地址处,然后x1=x1+16
st1 {v0.S}[0],  [x0],  x2  @ 将寄存器v0的低32位数据存储到x0地址处吗,然后x0=x0+x2
8、ARM64位下程序注释
在ARM32位下,单⾏注释采⽤@或者//,多⾏注释可以采⽤/* */;
在ARM64位下,单⾏注释采⽤//,多⾏注释采⽤/* */;
备注转⾃

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。