ARM64汇编0B - 函数调用约定

2024-06-20 14:20

本文主要是介绍ARM64汇编0B - 函数调用约定,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

建议先看《CSAPP》的3.7节,讲的很细。我们这里就直接看例子来分析了。

例子

static int func(int a, int b, int c, int d, int e, int f, int g, int h, int i)
{printf("%s\n", "add all");int x = a + b;return a + b + c + d + e + f + g + h + i;
}int main()
{getchar();int result = func(1, 2, 3, 4, 5, 6, 7, 8, 9);printf("result = %d\n", result);return 0;
}

上面的程序,我们写了一个 func 的调用,主要是观察其参数传递时,寄存器的使用以及栈的变化情况。

在ARM64体系结构中,栈是从高地址往低地址生长。栈在函数调用过程中起到非常重要的作用,包括存储函数使用的局部变量、传递参数等。在函数调用过程中,栈是逐步生成的。为单个函数分配的栈空间,即从该函数栈底(高地址)到栈顶(低地址)这段空间称为栈帧(Stack Frame)。

ARM64在函数参数传递时,参数1~参数8 分别保存到 X0~X7 寄存器中 ,剩下的参数从右往左依次入栈,被调用者实现栈平衡,返回值存放在 X0 中。

ARM在参数传递时,参数1~参数4 分别保存到 R0~R3 寄存器中 ,剩下的参数从右往左依次入栈,被调用者实现栈平衡,返回值存放在 R0 中。

我们看下上面程序的反汇编代码,有个地方需要注意,为了观察最原始的汇编代码需要在Applicataion.mk 里面添加一个选项,否则编译器会优化一些逻辑:

APP_OPTIM := debug
.text:0000000000000784 A8 03 5F B8                   LDUR            W8, [X29,#var_10]
.text:0000000000000788 E0 03 08 2A                   MOV             W0, W8                  ; a
.text:000000000000078C A1 C3 5E B8                   LDUR            W1, [X29,#var_14]       ; b
.text:0000000000000790 A2 83 5E B8                   LDUR            W2, [X29,#var_18]       ; c
.text:0000000000000794 A3 43 5E B8                   LDUR            W3, [X29,#var_1C]       ; d
.text:0000000000000798 E4 23 40 B9                   LDR             W4, [SP,#0x40+e]        ; e
.text:000000000000079C E5 1F 40 B9                   LDR             W5, [SP,#0x40+f]        ; f
.text:00000000000007A0 E6 1B 40 B9                   LDR             W6, [SP,#0x40+g]        ; g
.text:00000000000007A4 E7 17 40 B9                   LDR             W7, [SP,#0x40+h]        ; h
.text:00000000000007A8 EA 03 00 91                   MOV             X10, SP
.text:00000000000007AC E9 13 40 B9                   LDR             W9, [SP,#0x40+var_30]
.text:00000000000007B0 49 01 00 B9                   STR             W9, [X10,#0x40+i]       ; i
.text:00000000000007B4 0A 00 00 94                   BL              func

可以看到 a b c d e f g h,这8个参数是存放到了 W0 - W7 中,W 是 X 的32 位形式。

对于参数 i 的处理分3步:

MOV             X10, SP                    ;将sp的值给X10
LDR             W9, [SP,#0x40+var_30]      ;将9赋值给W9
STR             W9, [X10,#0x40+i]          ;将9储存到 sp+offset 中

是先将栈地址赋值给了 X10,然后将 i 的值存放到 W9 指向的地址里面,结合起来理解就是将 i 的值放到了栈里面。

我们动态的调试一下,观察栈变化:

前面的X0到X7的寄存器变化是符合预期的。

继续往下看,断点走到BL处,发现栈变化:

发现,将 9 的值储存到了 sp 的位置,偏移量为0。说明 [X10,#0x40+i] 计算出来的值就是 [X10],我们可以在这里按下Q键,看真实的汇编:

继续往下看,看跳转到 func 函数里面后栈的变化情况。一直走到 func 的 ret 指令,看栈的内容:

0000007FE24A0AE8  0000000900000003  
0000007FE24A0AF0  0000000700000008  
0000007FE24A0AF8  0000000500000006  
0000007FE24A0B00  0000000300000004  
0000007FE24A0B08  0000000100000002  
0000007FE24A0B10  0000007FE24A0B60  [stack]:0000007FE24A0B60
0000007FE24A0B18  000000632CC977B8  main+A4
0000007FE24A0B20  0000000000000009

可以看到,栈底首先储存的是第9个参数。

然后是返回地址,就是函数执行完之后需要执行的下一条指令的地址。

然后接着是栈指针,这个栈指针是 main 函数的栈地址,因为func函数执行完之后,需要将栈指针给还原,mian才好继续执行。

然后接着是参数,这是因为函数的逻辑可能需要使用到 X0 到 X7寄存器。所以先将寄存器的值储存到栈中,方便后续获取。

最后是局部变量,它的值也是放到了栈中。

这篇关于ARM64汇编0B - 函数调用约定的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1078383

相关文章

笔记整理—内核!启动!—kernel部分(2)从汇编阶段到start_kernel

kernel起始与ENTRY(stext),和uboot一样,都是从汇编阶段开始的,因为对于kernel而言,还没进行栈的维护,所以无法使用c语言。_HEAD定义了后面代码属于段名为.head .text的段。         内核起始部分代码被解压代码调用,前面关于uboot的文章中有提到过(eg:zImage)。uboot启动是无条件的,只要代码的位置对,上电就工作,kern

AutoGen Function Call 函数调用解析(一)

目录 一、AutoGen Function Call 1.1 register_for_llm 注册调用 1.2 register_for_execution 注册执行 1.3 三种注册方法 1.3.1 函数定义和注册分开 1.3.2 定义函数时注册 1.3.3  register_function 函数注册 二、实例 本文主要对 AutoGen Function Call

PHP7扩展开发之函数调用

前言 在这篇文章中我们将演示如何在扩展中调用函数,和调用对象的方法。代码示例如下: <?phpclass demo {public function get_site_name ($prefix) {return $prefix."信海龙的博客\n";}}function get_site_url ($prefix) {return $prefix."www.bo56.com\n";}

Win32函数调用约定(Calling Convention)

平常我们在C#中使用DllImportAttribute引入函数时,不指明函数调用约定(CallingConvention)这个参数,也可以正常调用。如FindWindow函数 [DllImport("user32.dll", EntryPoint="FindWindow", SetLastError = true)]public static extern IntPtr FindWindow

汇编:嵌入式软件架构学习资源

成为嵌入式软件架构设计师需要掌握多方面的知识,包括嵌入式系统、实时操作系统、硬件接口、软件设计模式等。 以下是一些推荐的博客和网站,可以帮助你深入学习嵌入式软件架构设计: ### 1. **Embedded.com**    - **网址**: [Embedded.com](https://www.embedded.com/)    - **简介**: 这是一个专注于嵌入式系统设计的专业网

C++中类的构造函数调用顺序

当建立一个对象时,首先调用基类的构造函数,然后调用下一个派生类的 构造函数,依次类推,直至到达派生类次数最多的派生次数最多的类的构造函数为止。 简而言之,对象是由“底层向上”开始构造的。因为,构造函数一开始构造时,总是 要调用它的基类的构造函数,然后才开始执行其构造函数体,调用直接基类构造函数时, 如果无专门说明,就调用直接基类的默认构造函数。在对象析构时,其顺序正好相反。

GDB 查看汇编

查看汇编 x disassemble

C语言-程序环境 #预处理 #编译 #汇编 #链接 #执行环境

文章目录 前言 一、程序的环境翻译和执行环境 二、翻译环境 (一)、整体把握 (一)、编译 1、预处理(预编译) 2、编译 a、词法分析 b、语法分析 c、语义分析 d、符号汇总 3、汇编 (二)、链接 三、运行环境 总结​​​​​​​ 前言 路漫漫其修远兮,吾将上下而求索; PS:本文参考了《程序员的自我修养》,致敬大佬们! 一、程序的

Linux 技巧汇编

10个重要的Linux ps命令实战 显示所有当前进程 根据用户过滤进程 通过cpu和内存使用来过滤进程 通过进程名和PID过滤 根据线程来过滤进程 树形显示进程 显示安全信息 格式化输出root用户(真实的或有效的UID)创建的进程 使用PS实时监控进程状态 https://linux.cn/article-4743-1.html Python 云服务器应用 | Http

正点原子阿尔法ARM开发板-IMX6ULL(二)——介绍情况以及汇编

文章目录 一、裸机开发(21个)二、嵌入式Linux驱动例程三、汇编3.1 处理器内部数据传输指令3.2 存储器访问指令3.3 压栈和出栈指令3.4 跳转指令3.5 算术运算指令3.6 逻辑运算指令 一、裸机开发(21个) 二、嵌入式Linux驱动例程 三、汇编 我们在进行嵌入式 Linux 开发的时候是绝对要掌握基本的 ARM 汇编,因为 Cortex-A 芯片一