赞
踩
1、参考
https://developer.arm.com/products/architecture/instruction-sets
https://developer.arm.com/docs/ddi0487/a
2、ARM64位架构分析
ARM64位采用ARMv8架构,64位操作长度,对应处理器有Cortex-A53、Cortex-A57、Cortex-A73、iphones的A7和A8等。
AARCH64是全新32位固定长度指令集,支持64位操作数的新指令,大多数指令可以具有32位或64位参数。
ARM64位架构有两种主要的执行状态:
这些执行状态支持三个主要指令集:
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~X15
64位下NEON寄存器:
32个Q寄存器(V0~V31),四字 128bit
不同位数下寄存器之间的关系如下图所示:
其中S0是D0的低半部分,D0是V0的低半部分 。
注意:
64位下NEON寄存器与32位下NEON寄存器之间的关系不同!
neon寄存器 v0~v31使用说明:
v0~v7:用于参数传递和返回值,子程序不需要保存;
v8~v15:子程序调用时必须入栈保存(低64位);
v16~v31:子程序使用时不需要保存。
具体可参考:
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf 5.1.2 SIMD and Floating-Point Registers
4、ARM64位指令集A64以及参考手册
ARMv8-a指令集参考手册:
https://developer.arm.com/docs/ddi0487/a
https://static.docs.arm.com/ddi0487/a/DDI0487A_j_armv8_arm.pdf(官方标准手册)
https://developer.arm.com/products/architecture/cpu-architecture/a-profile/docs/den0024/latest/porting-to-a64
https://community.arm.com/processors/b/blog/posts/porting-to-arm-64-bit
https://www.element14.com/community/servlet/JiveServlet/previewBody/41836-102-1-229511/ARM.Reference_Manual.pdf(指令集对比手册)
http://profsite.um.ac.ir/~shoraka/ARMInstructionSet.pdf
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0056c/IHI0056C_beta_aaelf64.pdf
http://infocenter.arm.com/help/topic/com.arm.doc.den0024a/DEN0024A_v8_architecture_PG.pdf(Programmer’s Guide)
5、ARM64位程序调试
方法一: 直接打印数据
ARM64位下打印数据的方法:
(1). 打印V寄存器:
- mov w0, v0.s[0]
- mov w1, v0.s[1]
- mov w2, v0.s[2]
- mov w3, v0.s[3]
- bl _print
(2). 打印V寄存器的低64位:
- 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调试
详细调试方法可以参考: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
至于要用的是v8~v15寄存器,为什么成了压d8~d15? 具体原因可以参考:http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf 5.1.2 SIMD and Floating-Point Registers
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调试会出现下面的问题:
/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.
解决方案:
通过分析可知,这是当前gdb版本7.11.1存在的堆栈问题。可以采用x寄存器进行入栈进行替换。
https://community.arm.com/processors/b/blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop
https://community.arm.com/processors/b/blog/posts/using-the-stack-in-aarch32-and-aarch64
https://stackoverflow.com/questions/40271180/push-and-pop-a-full-128-bit-neon-register-to-from-the-stack-in-aarch64
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位下,单行注释采用//,多行注释采用/* */;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。