AAPCS对ARM结构的一些标准做了定义,在这里我们只重点介绍函数调用部分,如图8所示,AAPCS为ARM的R0~R15寄存器做了定义,明确了它们在函数中的职责:

图 8 AAPCS关于ARM寄存器的定义

一、函数调用时的规则如下:

1、 父函数与子函数间的入口参数依次通过R0~R3这4个寄存器传递。父函数在调用子函数前先将参数存入到R0~R3中,若只有一个参数则使用R0传递,2个则使用R0和R1传递,依次类推,当超过4个参数时,其它参数通过栈传递。当子函数运行时,根据自身参数个数自动从R0~R3或者栈中读取参数。

2、子函数通过R0寄存器将返回值传递给父函数。子函数返回时,将返回值存入R0,当返回到父函数时,父函数读取R0获得返回值。

3、发生函数调用时,R0~R3是传递参数的寄存器,即使是父函数没有参数需要传递,子函数也可以任意更改R0~R3寄存器,无需考虑会破坏它们在父函数中保存的数值,返回父函数前无需恢复其值。AAPCS规定,发生函数调用前,由父函数将R0~R3中有用的数据压栈,然后才能调用子函数,以防止父函数R0~R3中的有用数据被子函数破坏。

4、 R4~R11为普通的通用寄存器,若子函数需要使用这些寄存器,则需要将这些寄存器先压栈然后再使用,以免破坏了这些寄存器中保存的父函数的数值,子函数返回父函数前需要先出栈恢复其数值,然后再返回父函数。AAPCS规定,发生函数调用时,父函数无需对这些寄存器进行压栈处理,若子函数需要使用这些寄存器,则由子函数负责压栈,以防止父函数R4~R11中的数据被破坏。

5、编译器在编译时就确定了函数间的调用关系,它会使函数间的调用遵守3、4条规定。但编译器无法预知中断函数的调用,被中断的函数无法提前对R0~R3进行压栈处理,因此需要在中断函数里对它所使用的R0~R11压栈。对于中断函数,不遵守第3条规定,遵守第5条规定。

6、R12寄存器在某些版本的编译器下另有它用,用户程序不能使用,因此我们在编写汇编函数时也必须对它进行压栈处理,确保它的数值不能被破坏。

7、R13寄存器是堆栈寄存器(SP),用来保存堆栈的当前指针。

8、R14寄存器是链接寄存器(LR),用来保存函数的返回地址。

9、R15寄存器是程序寄存器(PC),指向程序当前的地址。

上述只介绍了本手册中使用到的情形,具体的情况在编写操作系统代码时会涉及到,其它规则请请读者自行查找资料。

二、例子

接下来我们再通过几个小例子熟悉一下C函数与汇编函数的调用过程。下面的C函数TestFunc1与汇编函数TestFunc2的功能是一样的。

U8 TestFunc1(void)

{

U8 ucPara1;

U8 ucPara2;

U8 ucPara3;

U8 ucPara4;

U8 ucPara5;

U8 ucPara6;ucPara1 = 1;

ucPara2 = 2;

ucPara3 = 3;

ucPara4 = 4;

ucPara5 = 5;

ucPara6 = 6;

return ucPara1 + ucPara2 + ucPara3 + ucPara4 + ucPara5 + ucPara6;12345678

}

.func TestFunc2

TestFunc2:STMDB R13!, {R5 - R6, R10}    @R5,R6,R10寄存器压栈

LDR R1, =1

LDR R3, =2

LDR R4, =3

LDR R5, =4

LDR R6, =5

LDR R10, =6

ADD R0, R1, R3

ADD R0, R0, R4

ADD R0, R0, R5

ADD R0, R0, R6

ADD R0, R0, R10

LDMIA R13!, {R5 - R6, R10}    @R5,R6,R10寄存器出栈

.endfunc1234567891011121314151617

TestFunc2函数使用了R0、R1、R3、R4、R5、R6、R10共7个寄存器,遵循AAPCS规则,在使用R0、R1和R3之前并没有对它们压栈,但对R5、R6和R10寄存器进行了压栈保存,在函数返回前又出栈还原了这3个寄存器,这样TestFunc2函数返回到它的父函数之后,R5、R6和R10寄存器的数值是没有改变的,而R0、R1和R3则分别被改写为了1、2和3。

下面我们再来看看C函数TestFunc3调用汇编函数TestFunc4完成1+2的运算。

U8 TestFunc3(void)

{

return TestFunc4(1, 2);

}.func TestFunc41

TestFunc4:ADD R0, R0, R1

BX R14;

.endfunc1234

TestFunc3函数在调用TestFunc4函数前已经将参数1和2分别存入R0和R1,并将返回地址存入到R14中,然后才跳转到TestFunc4函数,发生函数调用。这时程序将运行TestFunc4函数,它将R0和R1相加,将结果放入R0,需要通过R0将返回值返回给TestFunc3函数。此时R14中保存的就是返回TestFunc3函数的返回地址,最后TestFunc4函数跳转到R14就返回到了TestFunc3函数,TestFunc3函数从R0就可以取出TestFunc4函数计算的结果了。

下面我们再来看看汇编函数TestFunc5调用C函数TestFunc6完成1+2的运算。

.func TestFunc5

TestFunc5:MOV R0, #1

MOV R1, #2

SUB R13, R13, #4

STR R14, [R13]

BL TestFunc6

LDR R14, [R13]

ADD R13, R13, #4

BX R14

.endfunc12345678910

U8 TestFunc6(U8 ucPara1, U8 ucpara2)

{

return ucPara1 + ucPara2;

}

TestFunc5函数先将参数1和2存入R0和R1寄存器,准备调用TestFunc6函数并传递入口参数,然后将R14寄存器压栈,以防止使用BL指令时存入的R14返回地址破坏R14原有的数据,然后调用TestFunc6函数。在调用TestFunc6函数时BL指令会自动将“LDR R14, [R13]”这条指令的地址存入R14,这样就开始运行TestFunc6函数了。TestFunc6函数会自动从R0和R1寄存器中取出参数,将计算结果存入R0,通过R0将返回值返回给TestFunc5函数。TestFunc6函数跳转回TestFunc5函数后,TestFunc5函数从栈中恢复原有的R14寄存器,完成函数调用,此时R0中的数值就是TestFunc6函数的计算结果。

当函数比较简单,不需要压栈仅使用寄存器便可以完成运算的时候,那么下面的TestFunc7函数,它的返回值是多少?

U8* TestFunc7(void)

{

U8 ucPara1;ucPara1 = 1;

return &ucPara1;123

}

按照上面的分析,对于这个简单的函数,编译器是不会为局部变量ucPara1分配内存空间的,ucPara1只会保存在寄存器中,因此无从谈起它的地址。但这个这么简单的函数却偏偏要获取这个仅在寄存器中的局部变量的地址,遇到这种情况,编译器在编译时会特别为ucPara1专门在栈中分配内存,因此也就可以获取到它的地址了。

当然,这个函数没有任何意义,仅是举一个例子,而且写C语言时要避免发生这种情况,因为TestFunc7函数返回的是栈内局部变量的地址,当TestFunc7函数运行完后,ucPara1这个局部变量所在的栈空间已经被释放,这个栈空间很可能已经被其它变量占用,如果这时候还使用这个地址的话就可能会导致系统崩溃,新手要避免产生这个错误。

汇编和c语言函数的参数,C函数与汇编函数之间参数及返回值传递方法相关推荐

  1. 【读书笔记】【程序员的自我修养 -- 链接、装载与库(三)】函数调用与栈(this指针、返回值传递临时对象构建栈、运行库与多线程、_main函数、系统调用与中断向量表、Win32、可变参数、大小端

    文章目录 前言 介绍 内存 内存布局 栈与调用惯例 堆与内存管理 运行库 入口函数和程序初始化 C/C++运行库 运行库与多线程 C++全局构造与析构 fread 实现 系统调用与API 系统调用介绍 ...

  2. java方法带参数返回值_Java方法中的参数太多,第6部分:方法返回

    java方法带参数返回值 在当前的系列文章中,我正在致力于减少调用Java方法和构造函数所需的参数数量,到目前为止,我一直专注于直接影响参数本身的方法( 自定义类型 , 参数对象 , 构建器模式 , ...

  3. C++ - 函数返回多个返回值的方法总结

    1 C++函数如何像python一样返回多个返回值 使用过python的童鞋都知道,在python单个函数中可以返回多个函数值,比如: # -*- coding: utf-8 -*-def retur ...

  4. 关于ExecuteNonQuery执行存储过程的返回值 、、实例讲解存储过程的返回值与传出参数、、、C#获取存储过程的 Return返回值和Output输出参数值...

    关于ExecuteNonQuery执行存储过程的返回值 用到过ExecuteNonQuery()函数的朋友们在开发的时候肯定这么用过. if(cmd.ExecuteNonQuery("xxx ...

  5. python 返回函数 闭包_python中闭包Closure函数作为返回值的方法示例

    前言 首先看看闭包的概念:闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数.这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外 ...

  6. Golang 具名返回参数 具名返回值 定义方法

    1. 具名返回值就是在定义函数的时候,在定义函数返回值的时候,既定义返回参数的数据类型,也定义返回参数的名称. 2. 具名返回参数的优势是,可以直接在函数内部使用与操作函数的返回值. 3. 具名返回参 ...

  7. 变量的引用-函数返回值传递引用

    def test(num):print("在函数内部 %d 对应的内存地址是 %d" % (num, id(num)))# 1> 定义一个字符串变量result = &quo ...

  8. Python 返回值、方法和函数的区别

    1.返回值详解 -------------------------------------------------------------------------------------------- ...

  9. web前端-JavaScript中的函数(创建,参数,返回值,方法,函数作用域,立即执行函数)

最新文章

  1. PyTorch 版 EfficientDet 比官方 TF 实现快 25 倍?这个 GitHub 项目数天狂揽千星
  2. 干货 | 拒当调参师工程师:超参数搜索算法一览
  3. 在循环中删除list中的元素
  4. 【C++】语法小知识
  5. 修改JAVA代码,需要重启Tomcat的原因
  6. html filter 在线预览,HTML Filter
  7. 使用jstack查看某个Java进程内的线程堆栈信息
  8. Python回归 岭回归(Ridge Regression)
  9. C++生产和使用的临时对象
  10. wlan消失 网络适配器文件夹空了 设备管理器黄色感叹号 wifi那里看不到任何WiFi解决
  11. 内网穿透远程访问家里的群晖NAS 1-2
  12. react 中 ref 管理列表
  13. 计算机教育 专业知识,计算机教育
  14. 首场百度大脑开放日来袭 | 全新开放24项AI技术
  15. java中load_Load Java参数描述的细节
  16. 洛谷P1075 质因数分解C语言
  17. 在号码池取连续号码的算法
  18. 5W1H聊开源之What——开源是什么?
  19. 怎么修改淘宝主图尺寸大小?图片尺寸修改的方法
  20. 理解和应用单位化向量

热门文章

  1. AMBA总线协议APB、AHB——学习笔记
  2. 喵淘客、蚂蚁、速推单、赚推云返利机器人功能
  3. 会声会影2023序列号使用和激活
  4. FL Studio20.8.4完整版本Win/MAC系统水果音乐工作室数字音频工作站
  5. angularjs判断输入框,验证输入框
  6. 苹果手机序列号查询的4种方法!
  7. 2022计算机毕业设计选题推荐 - 计算机毕业设计题目大全
  8. C语言链表学生成绩制作成链表,C语言程序设计课程设计基于链表的学生成绩管理系统.doc...
  9. 防ios的抽屉效果,防qq的抽屉效果
  10. 如何人物抠图?介绍三种人物抠图的方法