汇编调用c函数为什么要设置栈

.栈的整体作用

(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的寄存器的使用的约定而设计的,你最好不要随便改变使用方式,最好还是按照其约定的来处理,这样程序更加符合规范。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/463770.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++之Boost准标准库配置

下载安装 进入官网下载地址&#xff1a;https://www.boost.org/users/download/ 本教程直接下载官方已编译库&#xff0c;不涉及源代码手动编译 点击官方编号好的链接&#xff0c;然后进入一个下载地址&#xff1a;https://sourceforge.net/projects/boost/files/boost-binarie…

给.net初学者的一些建议(共勉之)[转载]

.不要以为.net就需要把代码全部写到.cs页面,不屑于使用vs提供的方便的控件操作. .不要以为.net就是把各种控件往页面上一拖,然后通过vs方便的设置一下就ok. .不要以为.net就需要在一开始从编译原理到web服务各种各样的书都看上一遍 .不要以为.net就是直接打开vs建立页面然后闷头…

大小端模式 续

union w { int a; char x[2]; }e; e.x[0]10; e.x[1]1; cout<<e.a<<endl; 那么运算结果在little-endian模式中的结果为266,那么原因是什么呢 我个人的理解 小端模式下&#xff1a; printf("0x%x", e.a); 结果为0x10a 大端模式下&#xff1a; printf("…

利用HTML中的XML数据岛记录浏览

html文件&#xff1a;shop.html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"http://www.w3.org/1999/xhtml"> <head> <me…

1.5

C Concurrency in Action Anthony Williams Chapter 1. Hello, world of concurrency in C! 1.1 What is concurrency? 1.1.1 Concurrency in computer systems 1.1.2 Approaches to concurrency 1.2 Why use concurrency? 1.2.1 Using concurrency for separation of concer…

测试一下你对IP地址的掌握水平(网管面试时会用到)

以下内容摘自《网管员面试宝典》一书。测试一下你对IP地址的理解能力&#xff0c;大家先不看题后的解答&#xff0c;看自己能做出多少题。网管面试时会用到的。面试题1&#xff1a;以下说法正确的是&#xff08; &#xff09;。A. C类地址就是局域网用的IP地址 B. A类地址的网…

中秋的秋

又是一年中秋中秋是比较特别的节日&#xff0c;因为每一年的中秋&#xff0c;我和小云总是能遇到各种事情而分开「当然今年不会」。去年的时候&#xff0c;我们因为要赶项目&#xff0c;所以中秋申请了加班&#xff0c;要申请加班的那天我还是挺不情愿的&#xff0c;然后旁边的…

OpenSSL原理与实现

1. 概念 1.1. SSL&#xff08;Secure Sockets Layer安全层套接字&#xff09;/TLS&#xff08;Transport Layer Security传输层套接字&#xff09;。 最常见的应用是在网站安全方面&#xff0c;用于http数据传输的加密。 1.2. 安全的目标&#xff1a;保密性&#xff08;confide…

shell的debug模式

如何调试shell脚本&#xff1f; 在指定shell运行版本时加上 -x #!/bin/bash -x ➜ demo git:(master) ✗ cat debug.sh #!/bin/bash -xecho "hi" date sleep 1 echo "hi" date调试结果&#xff1a; ➜ demo git:(master) ✗ ./debug.sh echo hi hidat…

C++ 重载(overload)、重写(overrride)、重定义(redefine)总结

昨晚打开论坛&#xff0c;看到有朋友问了一个关于虚函数的问题&#xff0c;因为头太疼了&#xff0c;所以今天中午起床再看。 问题传送门&#xff1a;http://www.cppleyuan.com/viewthread.php?tid7923 C的一些特性好久没使用了&#xff0c;导致有些生疏了&#xff0c;所以查了…

工作和异地,都是生活的考验

12年毕业的我&#xff0c;应该没有人比我更懂异地恋了。12年毕业拿了一份上海的ARM底层开发offer&#xff0c;薪资不算高&#xff0c;不过我们那一年竟没有一个拿到比上一届师兄薪资好的offer&#xff0c;我那时心里郁郁发闷&#xff0c;女朋友那时候考公务员&#xff0c;我们没…

strdup与strcpy具体的区别

我分别在XP的环境和linux环境下编译运行C代码&#xff0c;我发现一个不解的现象&#xff1a; 现象描述&#xff1a; 两个字符串 char* from&#xff0c; char* to;要把from的内容复制给to。 我在XP下&#xff0c;用strcpy&#xff08;to&#xff0c; from&#xff09;出现memor…

go语言基础之工程管理和工作区介绍

1、工程管理 在实际的开发工作中&#xff0c;直接调用编译器进行编译和链接的场景是少而又少&#xff0c;因为在工程中不会简单到只有一个源代码文件&#xff0c;且源文件之间会有相互的依赖关系。如果这样一个文件一个文件逐步编译&#xff0c;那不亚于一场灾难。 Go语言的设计…

用临时表的GridView分页

本例子采用sql2000下的Nowthwind数据库中的[Order Details]表 下面是存储过程脚本 Code1ALTER PROC OrderDetailsPaging 2(PageIndex int,--页码 3 PageSize int,--页尺寸 4 RowsCount int output)--总行数 5AS 6BEGIN 7set nocount on 8declare PageLowerBound int 9declar…

HTML与CSS(图解6):超链接

动态的超链接&#xff1a; <html> <head> <title>动态超链接</title> <style> <!-- body{background:url(bg9.gif); /* 页面背景图片 */margin:0px; padding:0px;cursor:pointer; /*意思就是鼠标指针变成 手 的形状&#xff0c;和放到链…

pointcut 切面表达式 切入点表达式

下面给出一些常见切入点表达式的例子。 任意公共方法的执行&#xff1a; execution(public * *(..)) 任何一个以“set”开始的方法的执行&#xff1a; execution(* set*(..)) AccountService 接口的任意方法的执行&#xff1a; execution(* com.xyz.service.AccountService.*…

keil分散加载文件浅析

什么是分散加载文件分散加载文件&#xff08;scatter file&#xff09;是一个文本文件&#xff0c;它的作用是可以用于描述 ARM 链接器生成映像文件所需要的信息。如果不使用 scatter file 文件来指定&#xff0c;那么 ARM 链接器会按照默认的方式来生成映像文件&#xff0c;但…

socket buffer套接字缓存

最近公司在开发机器人与服务器调度端的通信时需要使用socket&#xff0c;因此找到了该文章作为深刻理解socket内部运作。 Linux网络核心数据结构是套接字缓存(socket buffer)&#xff0c;简称skb。它代表一个要发送或处理的报文&#xff0c;并贯穿于整个协议栈。 1、 套接字…

LAMP 系统性能调优,第 3 部分: MySQL 服务器调优(转)

关于 MySQL 调优 有 3 种方法可以加快 MySQL 服务器的运行速度&#xff0c;效率从低到高依次为&#xff1a; 替换有问题的硬件。对 MySQL 进程的设置进行调优。对查询进行优化。迁移到 DB2? 您正在寻找一种干净利落、无成本的方法用来从 MySQL 迁移到 IBM? DB2? 吗&#xf…

Python--day 3

1 # -*- coding:utf-8 -*-2 # Author:Monarch-T3 4 for 循环5 for i in range(10):6 print("Loop:", i)7 8 for i in range(0, 10, 2): #步长29 continue 跳出本次循环进入下次循环 10 break 结束循环 转载于:https://www.cnblogs.com/Monarch-T/p/10245724…