前言

这篇主要介绍arm64程序调用规则,详细分析了程序调用过程中,参数是如何传递的。Android、iOS、Linux等基本遵循这些规则,但是各个操作系统平台也有小部分自己特定的规则。下一篇,我将介绍iOS平台的特定规则。

术语介绍

术语

意义

A32

在ARMv7架构中,使用32位固定长度指令的ARM指令集。

A64

AArch64可用时的指令集。

AAPCS64

AArch64程序调用标准。(PCS:Procedure Call Standard)

AArch32

ARMv8中的32位通用寄存器,兼容ARMv7-A。

AArch64

ARMv8中的64位通用寄存器

ABI(Application Binary Interface)

汇编接口规范,跟执行环境相关,比如Linux ABI,说的是Linux环境下的汇编接口规范;

ARM-based

基于ARM

Floating point

根据上下文有这三种意思:(1)遵循IEEE 754 2008的浮点运算; (2)ARMv8浮点指令集; (3)一个被ARMv8浮点指令集和ARMv8 SIMD指令集共享的寄存器组。

Q-o-I

Quality of Implementation

SIMD

Single Instruction Multiple Data 一条指令操作多个数据

T32

T32使用可变16bit和32bit

Routine, subroutine

Routine:调用者;subroutine:被调用者

Procedure

没有返回值的函数

Function

有返回值的函数

PIC, PID

Position-independent code, position-independent data.

Program state

指程序内存和寄存器的值

Caller- saved register

调用者在调用函数之前,保存寄存器(一般入栈),函数返回后恢复寄存器(一般出栈)

Callee-saved register

被调用者(函数内部),在起始地方保存寄存器,在结束时,恢复寄存器

NGRN(The Next General-purpose Register Number )

可以理解为,记录r0-r7(见下文寄存器)使用个数,参数传递前设为0,每放一个参数进入寄存器(整型寄存器),值加1。当等于8时候,说明r0-r7寄存器使用完了,再有参数,只能放入内存了。

NSRN (The Next SIMD and Floating-point Register Number)

同上,记录v0-v7使用个数

NSAA (The next stacked argument address)

记录参数放入内存,参数传递前设为SP,所以内存中参数范围应该是 sp~NSAA。详细见下文参数传递

数据类型和对齐

基本数据类型

Type ClassMachine TypeByte

sizeNatural

Alignment

(bytes)

IntegralUnsigned byte11

Signed byte11

Unsigned half-

word22

Signed half-

word22

Unsigned word44

Signed word44

Unsigned

double-word88

Signed double-

word88

Unsigned quad-

word1616

Signed quad-

word1616

Floating PointHalf precision22

Single precision44

Double

precision88

Quad precision1616

Short vector64-bit vector88

128-bit vector1616

PointerData pointer88

Code pointer88

程序调用规则

寄存器

arm64有两种寄存器:

处理整型和指针的寄存器

通用寄存器和AAPCS64用法

寄存器

别名

意义

SP

Stack Pointer:栈指针

r30

LR

Link Register:在调用函数时候,保存下一条要执行指令的地址。

r29

FP

Frame Pointer:保存函数栈的基地址。

r19...r28

Callee-saved registers(含义见上面术语解释)

r18

平台寄存器,有特定平台解释其用法。如果平台未把其做特殊用途,可当做临时寄存器使用。(iOS平台保留的寄存器,应用不可使用)

r17

IP1

The second intra-procedure-call temporary register (can be used by call veneers and PLT code); at other times may be used as a temporary register.

r16

IP0

The first intra-procedure-call scratch register (can be used by call veneers and PLT code); at other times may be used as a temporary register.

r9...r15

临时寄存器

r8

在一些情况下,返回值是通过r8返回的

r0...r7

r0-r7在函数调用过程中传递参数和返回值

NZCV

状态寄存器:N(Negative)负数 Z(Zero) 零 C(Carry) 进位 V(Overflow) 溢出

arm64有31个通用整型寄存器,r0-r30。当使用64bits时候,命名x0-x30;使用32bits时,命名w0-w30。当寄存器在此程序调用标准中具有固定角色时,使用大写。

SIMD 和 Floating-Point寄存器

ARM64有32个寄存器v0-v31,用于处理SIMD和浮点运算。长度不同称谓也不同,b,h,s,d,q,分别代表byte(8位),half(16位),single(32位),double(64位),quad(128位)。v0-v7在函数调用过程中传递参数和返回值;v8-v15 是Callee-saved registers(见术语解释),且是保存前64bits(更大的位数,调用者负责保存),v0-v7, v16-v31不需要保存或者调用者保存。

进程、内存、栈

一个进程的内存可分为5类:

代码区。只能被进程读,不可些。

可写静态数据。

只读静态数据。

堆。

栈。

可写静态数据可以细分为初始化,零初始化和未初始化数据。 除了栈之外,其它4类内存不需要占用连续的内存。 进程必须具有一些代码和栈,其它3类不是必须有。

堆是由进程管理的内存区域, 通常用于创建动态数据对象。

内存地址

地址空间包括一个或多个不相交的区域。 区域不能跨越零地址,但是可以从零开始。

标记寻址(tagged addressing)的使用是特定平台解释的。 当禁用标记寻址时,指针的所有64位都被传递到地址转换系统。 启用标记寻址时,为了进行地址转换,将忽略指针的前八位。注意:此tagged addressing,非iOS里的Tagged Pointer。

栈是连续的内存空间,可用于存储局部变量和参数传递(用于传递参数的寄存器不够用时候)。栈地址是从高到低,栈的地址保存在SP中。

栈使用限制:

Stack-limit < SP <= stack-base

进程只能访问这个范围内的栈空间:[SP, stack-base – 1]

SP mod 16 = 0

函数调用

A64指令集包含函数调用指令BL和BLR。

执行BL:PC(program counter)顺序的下一个值,也就是返回地址(函数调用完成返回要执行指令的地址),存放到LR中,将跳转地址传给PC。BLR跟BL类似,只不过PC的值是从寄存器中读取。

参数传递

参数可通过r0-r7、v0-v7,栈来传递;如果参数个数不多,且参数可放进寄存器,那仅用寄存器传递参数。

可变参数

可变参数可分为命名参数(已声明的)和匿名参数(可选的参数)。

当可变参数的函数,调用时候,没有可选参数时候(只有已声明的参数),调用过程和固定参数的函数一样的。

参数传递规则

参数传递从概念上可以分为2阶段:

从源语言参数类型到机器类型的映射(不同源语言,映射规则不同)

整理机器类型,生成最终参数列表

参数传递过程分为3个阶段:

阶段A – 初始化

(在开始处理参数之前,该阶段仅执行一次)

NGRN = 0 (NGRN意义,见术语)

NSRN = 0 (NSRN意义,见术语)

NSAA = SP(NSAA意义,见术语)

阶段B - 预填充和扩展参数 (把参数列表中的每一个参数,去匹配下面规则,第一个被匹配到的规则,应用到该参数上。)

如果参数类型是复合类型,调用者和被调用者都不能确定其大小,则将参数复制到内存中,并将参数替换为指向该内存的指针。 (C / C ++语言中没有这样的类型,其它语言存在。)

如果参数是HFA或HVA类型,则参数不修改。

如果参数是大于16个字节的复合类型,调用者申请一个内存,将参数复制到内存里去,并将参数替换为指向该内存的指针。

如果参数是复合类型,则参数的大小向上舍入为最接近8个字节的倍数。(例如参数大小为9字节,修改为16字节)

阶段C- 把参数放到寄存器或栈里 (参数列表中的每个参数,将依次应用以下规则,直到参数放到寄存器或栈里,此参数处理完成,然后再从参数列表中取参数。注: 将参数分配给寄存器时,寄存器中未使用的位的值不确定。 将参数分配给栈时,未填充字节的值不确定。)

(1) 如果参数是half(16bit),single(16bit),double(32bit)或quad(64bit)浮点数或Short Vector Type,并且NSRN小于8,则将参数放入寄存器v[NSRN]的最低有效位。 NSRN增加1。 此参数处理完成。

(2) 如果参数是HFA(homogeneous floating-point aggregate)或HVA(homogeneous short vector aggregate)类型,且NSRN + (HFA或HVA成员个数) ≤ 8,则每个成员依次放入SIMD and Floating-point 寄存器,NSRN=NSRN+ HFA或HVA成员个数。此参数处理完成。

(3) 如果参数是HFA(homogeneous floating-point aggregate)或HVA(homogeneous short vector aggregate)类型,但是NSRN已经等于8(说明v0-v7被使用完毕)。则参数的大小向上舍入为最接近8个字节的倍数。(例如参数大小为9字节,修改为16字节)

(4) 如果参数是HFA(homogeneous floating-point aggregate)、HVA(homogeneous short vector aggregate)、quad(64bit)浮点数或Short Vector Type,NSAA = NSAA+max(8, 参数自然对齐大小)。

(5) 如果参数是half(16bit),single(16bit)浮点数,参数扩展到8字节(放入最低有效位,其余bits值不确定)

(6) 如果参数是HFA(homogeneous floating-point aggregate)、HVA(homogeneous short vector aggregate)、half(16bit),single(16bit),double(32bit)或quad(64bit)浮点数或Short Vector Type,参数copy到内存,NSAA=NSAA+size(参数)。此参数处理完成。

(7) 如果参数是整型或指针类型、size(参数)<=8字节,且NGRN小于8,则参数复制到x[NGRN]中的最低有效位。 NGRN增加1。 此参数处理完成。

(8) 如果参数对齐后16字节,NGRN向上取偶数。(例如:NGRN为2,那值保持不变;假如NGRN为3,则取4。 注:iOS ABI没有这个规则)

(9) 如果参数是整型,对齐后16字节,且NGRN小于7,则把参数复制到x[NGRN] 和 x[NGRN+1],x[NGRN]是低位。NGRN = NGRN + 2。 此参数处理完成。

(10) 如果参数是复合类型,且参数可以完全放进x寄存器(8-NGRN>= 参数字节大小/8)。从x[NGRN]依次放入参数(低位开始)。未填充的bits的值不确定。NGRN = NGRN + 此参数用掉的寄存器个数。此参数处理完成。

(11) NGRN设为8。

(12) NSAA = NSAA+max(8, 参数自然对齐大小)。

(13) 如果参数是复合类型,参数copy到内存,NSAA=NSAA+size(参数)。此参数处理完成。

(14) 如果参数小于8字节,参数设置为8字节大小,高位bits值不确定。

(15) 参数copy到内存,NSAA=NSAA+size(参数)。此参数处理完成。

从上面规则,可以得到经验:

处理完参数列表中所有的参数后,调用者一定知道传递参数用了多少栈空间。(NSAA - SP)

浮点数和short vector types通过v寄存器和栈传递,不会通过r寄存器传递。(除非是小复合类型的成员)

寄存器和栈中,参数未填充满的部分的值,不可确定。

函数返回结果

函数返回方式取决于返回结果的类型。

如果返回是类型T,如下

void func(T arg)

复制代码

arg值通过寄存器(组)传递,返回的结果也是通过相同的寄存器(组)返回。

2. 调用者申请内存(内存大小足够放入返回结果且是内存对齐的),将内存地址放入x8中传递给子函数,子函数运行时候,可以更新x8指向内存的内容,从而将结果返回。

结语

假如文章有不对地方,欢迎大家留言指出;或者给我发邮件(wu_k_k@foxmail.com)。

引用

--EOF-- 转载请保留链接,谢谢

arm64 linux 除零正常返回,arm64程序调用规则相关推荐

  1. Ubuntu 上使用 qemu 模拟 Arm64 linux

    Ubuntu 上用 qemu 模拟 Arm64 linux 环境配置 1. 安装Arm64交叉编译工具链 2. 安装qemu 下载源码包 编译 编译Linux kernel 编译qemu 编译busy ...

  2. ARM64 Linux 内核页表的块映射

    作者 | 宋宝华  责编 | 张文 头图 | CSDN 下载自视觉中国 出品 | CSDN(ID:CSDNnews) 内核文档 Documentation/arm64/memory.rst 描述了 A ...

  3. 基于 arm64 Linux nanosleep 系统调用流程分析

    nanosleep (高分辨率睡眠)可实现纳秒级的睡眠,暂停调用线程的执行.在 Linux 内核中是如何实现的?下面基于 arm64 cpu 架构去分析. #include <time.h> ...

  4. ARM DS-5单步调试ARM64 linux 内核

    目录 1 介绍 2 开发环境 3 准备工作 3.1 Ubuntu环境准备 3.2 源代码准备 3.3 DS-5准备 3.4 使用DS-5调试源码 3.4.1 建立源码工程 3.4.2 创建debug配 ...

  5. linux中memcpy实现分析,ARM64 的 memcpy 优化与实现

    如何优化 memcpy 函数 Linux 内核用到了许多方式来加强性能以及稳定性,本文探讨的 memcpy 的汇编实现方式就是其中的一种,memcpy 的性能是否强大,拷贝延迟是否足够低都直接影响着整 ...

  6. ARM64 Linux内核起始虚拟地址

    0xFFFFFE0000000000  是ARM64 Linux起始的虚拟地址,第一个虚拟地址

  7. QEMU启动ARM64 Linux内核

    目录 前言 前置知识 virt开发板 ARM处理器家族简介 安装qemu-system-aarch64 安装交叉编译工具 交叉编译ARM64 Linux内核 交叉编译ARM64 Busybox 使用b ...

  8. Linux Zero-copy零拷贝技术:源码示例

    <Linux Zero-copy零拷贝技术:源码示例> <Linux Zero-copy零拷贝技术全面揭秘> <什么是mmap?零拷贝?DMA?> <Linu ...

  9. 面试题:如何理解 Linux 的零拷贝技术?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 本文讲解 Linux 的零拷贝技术,云计算是一门很庞大的技术学科, ...

最新文章

  1. 在Windows 7下面IIS7的安装和 配置ASP的正确方法
  2. vb mysql 字符串转日期_VB常用函数表
  3. CoreJava 笔记总结-第三章 Java的基本程序设计结构
  4. [react] react是哪个公司开发的?
  5. 云计算的概念 - 初识云计算知识专栏(1)
  6. python键盘输入转换为列表_Python键盘输入转换为列表的实例
  7. 软件版本的GA、RC的具体含义
  8. pl/sql 存储过程实例
  9. JS基础-事件模型(事件事件流自定义事件事件冒泡/代理)
  10. OPC DA 与 OPC UA区别
  11. 服装进销存2022年排行榜,新手小白必看!
  12. 一发入魂双链表(十字链表)
  13. udpping检测与对端udp协议通信状况
  14. OpenStack安装Placement组件部署(四)
  15. Linux命令——性能监控glance命令详解
  16. 你真的懂 MP4 格式吗?
  17. PHP 登录TPlink路由器
  18. 开发者还能这样开发小游戏变现
  19. 新电脑win10 改win7 要注意
  20. iPhone12概念图来袭,最为期待的样子来了

热门文章

  1. 2019工作榜单:程序员吸金榜,AI排第一,这个我服!
  2. 华为5G设备全球分布图曝光:欧洲占总量近6成;地平线发布首款车规级AI芯片,名叫征程2.0;奥迪与比亚迪达成电池供货协议……...
  3. shell 提取sql 的字段名表名_Mysql 常用SQL语句集锦(仅学习)
  4. js传中文参数 java取_js中文转码传输java后台 适用于用url传递中文参数
  5. linux:根据关键字或日期查找日志
  6. java 异常 日志_java中的异常、断言、日志(一)
  7. cygwin 远程连接linux,Cygwin解决Windows远程登录linux服务器
  8. 华为卡槽打不开怎么办_17500元!华为5G折叠手机刷屏,有人焦虑连夜开会…
  9. Arrays类详细讲解
  10. linux路由信息预览为空,route - 显示并设置Linux中静态路由表