.栈的整体作用

(1)保存现场/上下文

(2)传递参数:汇编代码调用c函数时,需传递参数

(3)保存临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量。

.为什么汇编代码调用c函数需要设置栈

之前看了很多关于uboot的分析,其中就有说要为C语言的运行,准备好栈。而自己在Ubootstart.S汇编代码中,关于系统初始化,也看到有栈指针初始化这个动作。但是,从来只是看到有人说系统初始化要初始化栈,即正确给栈指针sp赋值,但是却从来没有看到有人解释,为何要初始化栈。所以,接下来的内容,就是经过一定的探究,试图来解释一下,为何要初始化栈。
要明白这个问题,首先要了解栈的作用。关于栈的作用,要详细讲解的话,要很长的篇幅,所以此处只是做简略介绍。总的来说,栈的作用就是:保存现场/上下文,传递参数,保存临时变量

1.保存现场/上下文
现场/上下文,意思就相当于案发现场,总有一些现场的情况,要记录下来的,否则被别人破坏掉之后,你就无法恢复现场了。而此处说的现场,就是指CPU运行的时候,用到了一些寄存器,比如r0,r1等等,对于这些寄存器的值,如果你不保存而直接跳转到子函数中去执行,那么很可能就被其破坏了,因为其函数执行也要用到这些寄存器。因此,在函数调用之前,应该将这些寄存器等现场,暂时保持起来(入栈push),等调用函数执行完毕返回后(出栈pop),再恢复现场。这样CPU就可以正确的继续执行了。
保存寄存器的值,一般用的是push指令,将对应的某些寄存器的值,一个个放到栈中,把对应的值压入到栈里面,即所谓的压栈。然后待被调用的子函数执行完毕的时候,再调用pop,把栈中的一个个的值,赋值给对应的那些你刚开始压栈时用到的寄存器,把对应的值从栈中弹出去,即所谓的出栈。
其中保存的寄存器中,也包括lr的值(因为用bl指令进行跳转的话,那么之前的pc的值是存在lr中的),然后在子程序执行完毕的时候,再把栈中的lr的值pop出来,赋值给pc,这样就实现了子函数的正确的返回。

2.传递参数
C语言进行函数调用的时候,常常会传递给被调用的函数一些参数,对于这些C语言级别的参数,被编译器翻译成汇编语言的时候,就要找个地方存放一下,并且让被调用的函数能够访问,否则就没发实现传递参数了。对于找个地方放一下,分两种情况。一种情况是,本身传递的参数不多于4个,就可以通过寄存器传送参数。因为在前面的保存现场的动作中,已经保存好了对应的寄存器的值,那么此时,这些寄存器就是空闲的,可以供我们使用的了,那就可以放参数。另一种情况是,参数多于4个时,寄存器不够用,就得用栈了。

3.临时变量保存在栈中

包括函数的非静态局部变量以及编译器自动生成的其他临时变量。

4.举例分析C语言函数调用是如何使用栈的
对于上面的解释的栈的作用显得有些抽象,此处再用例子来简单说明一下,就容易明白了:用:arm-inux-objdump–d u-boot >dump_u-boot.txt可以得到dump_u-boot.txt文件。该文件就是中,包含了u-boot中的程序的可执行的汇编代码,其中我们可以看到C语言的函数的源代码,到底对应着那些汇编代码。
下面贴出两个函数的汇编代码,一个是clock_init,另一个是与clock_init在同一C源文件中的,另外一个函数CopyCode2Ram
33d0091c<CopyCode2Ram>:
33d0091c:  e92d4070  push   {r4, r5, r6, lr}
33d00920:  e1a06000  mov r6, r0
33d00924:  e1a05001  mov r5, r1
33d00928:  e1a04002  mov r4, r2
33d0092c:  ebffffef  bl  33d008f0 <bBootFrmNORFlash>
......
33d00984:  ebffff14  bl  33d005dc <nand_read_ll>
......
33d009a8:  e3a00000  mov r0, #0 ; 0x0
33d009ac:  e8bd8070  pop {r4, r5, r6, pc}
33d009b0<clock_init>:
33d009b0:  e3a02313  mov r2, #1275068416   ;0x4c000000
33d009b4:  e3a03005  mov r3, #5 ; 0x5
33d009b8:  e5823014  str r3,
......
33d009f8:  e1a0f00e  mov pc, lr
1clock_init部分的代码可以看到该函数第一行:33d009b0:  e3a02313  mov r2, #1275068416   ;0x4c000000就没有我们所期望的push指令,没有去将一些寄存器的值放到栈中。这是因为,我们clock_init这部分的内容,所用到的r2,r3等等寄存器,和前面调用clock_init之前所用到的寄存器r0,没有冲突,所以此处可以不用push去保存这类寄存器的值,不过有个寄存器要注意,那就是r14,即lr,其是在前面调用clock_init的时候,用的是bl指令,所以会自动把跳转时候的pc的值赋值给lr,所以也不需要push指令去将PC的值保存到栈中。而clock_init的代码的最后一行:33d009f8:e1a0f00e mov pc, lr就是我们常见的movpc,lr,把lr的值,即之前保存的函数调用时候的PC值,赋值给现在的PC,这样就实现了函数的正确的返回,即返回到了函数调用时候下一个指令的位置。这样CPU就可以继续执行原先函数内剩下那部分的代码了。
2CopyCode2Ram部分的代码其第一行:33d0091c:e92d4070 push {r4, r5, r6, lr}就是我们所期望的,用push指令,保存了r4,r5,r以及lr。用push去保存r4,r5,r6,那是因为所谓的保存现场,以后后续函数返回时候再恢复现场,而用push去保存lr,那是因为此函数里面,还有其他函数调用:33d0092c:  ebffffef  bl  33d008f0 <bBootFrmNORFlash>
......
33d00984:  ebffff14  bl  33d005dc <nand_read_ll>
......也用到了bl指令,会改变我们最开始进入clock_init时候的lr的值,所以我们要用push也暂时保存起来。而对应地,CopyCode2Ram的最后一行:33d009ac:e8bd8070 pop {r4, r5, r6,pc}就是把之前push的值,给pop出来,还给对应的寄存器,其中最后一个是将开始pushlr的值,pop出来给赋给PC,因为实现了函数的返回。另外,我们注意到,在CopyCode2Ram的倒数第二行是:33d009a8:e3a00000 mov r0, #0 ;0x0是把0赋值给r0寄存器,这个就是我们所谓返回值的传递,是通过r0寄存器的。此处的返回值是0,也对应着C语言的源码中的“return0”.
对于使用哪个寄存器来传递返回值:当然你也可以用其他暂时空闲没有用到的寄存器来传递返回值,但是这些处理方式,本身是根据ARMAPCS的寄存器的使用的约定而设计的,你最好不要随便改变使用方式,最好还是按照其约定的来处理,这样程序更加符合规范。

汇编调用c函数为什么要设置栈相关推荐

  1. 汇编调用C函数--利用堆栈传递参数

    汇编:EXPORT F i EQU 5AREA F,CODE,READONLYIMPORT g ;使用伪操作IMPORT声明C函数g()ENTRYSTR LR,[SP,#-4]! ;保存返回地址MOV ...

  2. S5PV210开发板用汇编设置栈和调用C语言

    使用C语言前为什么要先用汇编设置栈? C语言程序运行时需要栈,因为C语言中的局部变量都是用栈来实现的,如果没有设置栈就使用C语言,局部变量就会落空,程序就会死掉,所以在使用C语言前,我们需要先在汇编编 ...

  3. Python自定义函数的创建、调用和函数的参数详解

    这篇文章主要介绍了Python自定义函数的创建.调用和函数的参数.变量作用域等常见问题,需要的朋友可以参考下 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性 ...

  4. 栈的删除函数c语言,顺序栈的插入和删除

    <顺序栈的插入和删除>由会员分享,可在线阅读,更多相关<顺序栈的插入和删除(5页珍藏版)>请在人人文库网上搜索. 1.实验四顺序栈的插入和删除姓名:学号: 日期:一.实验目的: ...

  5. 汇编语言调用c语言ads,ADS1.2 在汇编代码中调用C函数

    EDA365欢迎您登录! 您需要 登录 才可以下载或查看,没有帐号?注册 x , U) b) }+ U8 \" d/ v( \$ ~  T对于ARM体系来说,不同语言撰写的函数之间相互调用( ...

  6. (动图详解)汇编视角观察函数栈帧的创建和销毁

    目录 ​1.阅读本文的价值 ​2.函数栈帧及栈的概念 ​3.部分寄存器及汇编指令 ​4.main函数的调用 5.main函数的栈帧创建 ​6.变量的栈帧创建 ​6.函数传参 ​7.函数内部运算及销毁 ...

  7. 汇编达人视频学习6(汇编眼中的函数、CALL指令执行函数、堆栈传参、堆栈平衡、外平栈、内平栈)

    title: 汇编达人视频学习6 date: 2021年8月4日 15点15分 tags: 汇编达人 categories: 汇编达人 21.汇编眼中的函数 1.什么是函数 函数就是一系列指令的集合, ...

  8. C++对象模型3——vptr的位置、手动调用虚函数、从汇编代码看普通调用和多态调用

    一.vptr的位置 class test { public:int i; virtual void testfunc() {} };int main() {test a;char* p1 = rein ...

  9. c语言结构体调用成员函数,c语言结构体函数调用参数如何设置

    c语言结构体函数调用参数怎么设置 函数结构是下面的代码,main函数中如何调用showinfo函数,参数应该怎么设置,对参数的设置不太明白 C/C++ code#include #define SIZ ...

最新文章

  1. 信息保留的二值神经网络IR-Net,落地性能和实用性俱佳 | CVPR 2020
  2. php 防止倒链,PHP防止图片倒链
  3. 11.条件语句if,switch
  4. RDD 与 DataFrame原理-区别-操作详解
  5. 兄弟连区块链教程Fabric1.0源代码分析Peer peer根命令入口及加载子命令一
  6. 架构师论坛 创业_我在早期创业时作为设计师学到的东西
  7. 小学计算机技术指导纲要,《中小学信息技术课程指导纲要(试行)》
  8. Python变量类型
  9. 纯js实现瀑布流布局及ajax动态新增数据
  10. 解决方案:超卖(Redis原子队列)
  11. centos 7安装java开发环境
  12. 威纶触摸屏键盘不显示数字_详解 | 威纶触摸屏数值输入元件应用
  13. 微信公总测试号的申请+微信网页授权
  14. 由内而外全面造就自己
  15. jqprint插件打印去掉页眉页脚的方式
  16. 计算几何基础--线段的性质
  17. 大学生php实训总结_php实训报告心得体会
  18. Mac中的文件如何拷贝到硬盘中?
  19. #if endif 的意思
  20. git 录制简单实用好工具 LICEcap

热门文章

  1. kaggle(03)-自行车租赁预测问题(基础版)
  2. python输入数字成数组_python – Numpy:将数值插入数组的最快方法,使得数组按顺序排列...
  3. C++面试宝典 基本语言(三)
  4. 以太坊账户 相关知识
  5. Coding For Fun 32小时:充满创造、激情、团结的编程马拉松
  6. 苏宁国美盈利报警:线下乏力线上重金加码
  7. 什么是真正的高清,你知道吗?
  8. 解决:Docker 启动的容器内部时间比服务器时间晚 8 小时,容器内部时间与宿主机时间不一致
  9. 破解 IntelliJ IDEA 、免费注册方法、注册码
  10. ssm框架下 tiles框架 的使用