谈谈arm下的函数栈

引言

  最近无聊看了看征服C指针,看到函数的不定参数时想起在这方面还没有做过一些总结,只是略微了解一些,意识到其实完全不需要借用va_list,va_start,va_arg这些标准函数也可以自己实现操作,具体我们来看看。

函数栈

  首先我们需要了解一下linux下一个进程的内存地址空间是如何布局的,在linux中,0~3G的虚拟地址为进程所有,3G~4G由内核所使用,每一个进程都有自己独立的0~3G内存地址空间。当进程进行函数调用时,我们都知道传入被调用函数的参数是通过栈进行操作的,这里我们只需要简单了解一下linux的内存地址空间中的栈是自顶向下生长的,就是栈底出于高地址处,栈顶出于低地址处。

  好的,简单了解了内存地址空间的栈后,我们还需要简单了解一下EBPESP这两个寄存器,EBP是用保存栈低地址的,而ESP用于保存栈顶地址,而每一次函数调用会涉及到一个栈帧

举个实例详细说明一下一个函数帧的特点,比如

 1 /* B被A调用
 2  * 参数:data1, data2, data3
 3  * 局部变量: s1, s2, s3 */
 4 void B (int data1, int data2, int data3)
 5 {
 6     int b_s1;
 7     int b_s2;
 8     int b_s3;
 9 }
10
11 /* A调用B函数 */
12 void A (void)
13 {
14     int a_s1;
15     int a_s2;
16     int a_s3;
17
18     B (1, 2, 3);
19     printf ("1\n");
20 }

在以上例子中栈帧情况如下图所示

  从图例中可以看出,当A函数没有调用B函数时,A函数的栈帧只保存着局部变量,而EBP(栈底指针)指向的是A函数的函数栈帧头,而当A函数调用B函数时,A函数会将B函数所需要的参数从右往左压入栈(在例子中先压入3,之后是2,最后是1),之后会将A调用完B之后所需要运行的第一条指令压入栈,此时建立一个B的栈帧,具体流程:

  • 从右往左将B函数所需参数压入栈
  • 压入执行完B函数之后的第一条指令地址
  • 建立B栈帧
  • 压入A栈帧的栈底
  • 压入B函数保护的寄存器
  • 压入B函数的局部变量
时间: 2024-10-18 18:17:28

谈谈arm下的函数栈的相关文章

Linux 下函数栈帧分析

1.关于栈 对于程序,编译器会对其分配一段内存,在逻辑上可以分为代码段,数据段,堆,栈 代码段:保存程序文本,指令指针EIP就是指向代码段,可读可执行不可写 数据段:保存初始化的全局变量和静态变量,可读可写不可执行 BSS:未初始化的全局变量和静态变量 堆(Heap):动态分配内存,向地址增大的方向增长,可读可写可执行 栈(Stack):存放局部变量,函数参数,当前状态,函数调用信息等,向地址减小的方向增长,非常非常重要,可读可写可执行.如下图所示: 首先必须明确一点也是非常重要的一点,栈是向下

从零开始写一个arm下的裸板程序

从零开始写一个arm下的裸板程序.我们整个程序是基于uboot运行的. 所有我们可以借助uboot中的printf来输出,默认开发版的标准输出是串口. 电脑的默认标准输出的屏幕. 1.需要创建的文件由include文件夹,用来存放头文件. 2.创建一个hw.h头文件. 3.编写一个common.h,它定义了借用uboot的printf的宏.和NULL这个宏的定义. 4.hw.c 硬件相关的文件. 5.main.c c文件. 6.start.s 汇编文件. 7.ld.lds 链接脚本, 8.Mak

c函数调用过程原理及函数栈帧分析

转载自地址:http://blog.csdn.net/zsy2020314/article/details/9429707       今天突然想分析一下函数在相互调用过程中栈帧的变化,还是想尽量以比较清晰的思路把这一过程描述出来,关于c函数调用原理的理解是很重要的. 1.关于栈 首先必须明确一点也是非常重要的一点,栈是向下生长的,所谓向下生长是指从内存高地址->低地址的路径延伸,那么就很明显了,栈有栈底和栈顶,那么栈顶的地址要比栈底低.对x86体系的CPU而言,其中 ---> 寄存器ebp(

函数栈帧

在函数栈帧中 一般包含以下几类重要信息 1 局部变量 :为函数局部变量开辟的内存空间 2 栈帧状态值:保存前   栈帧的顶部和底部 3 函数返回地址:保存当前函数调用前的“断点”信息,也就是函数调用前的指令位置 以便在函数返回时能够恢复到函数被调用前的代码区中继续执行指令. EIP:指令寄存器:其内存放着一个指针,该指针永远指向下一条等待执行的指令地址 可以说 控制了 EIP 寄存器的内容,就控制了进程 我们让EIP指向哪里,CPU就会去执行哪里的指令. EIP劫持原理.

broadcom6838开发环境实现函数栈追踪

在嵌入式设备开发中,内核为内核模块的函数栈追踪已经提供了很好的支持,但用户层的函数栈追踪确没有很好的提供支持.在网上收集学习函数栈跟踪大部分都是描述INTER体系架构支持栈帧的实现机制,或者直接给出glibc的现成库函数.但如果开发环境是broadcom相关方案,通常使用的是MIPS32的体系架构,并且C库使用的是更小的uclibc,虽然MIPS32体系架构中也定义了栈帧寄存器s8(类似于Inter体系架构中常见的ebp寄存器),但经过GCC编译器的优化选项控制后,通常在O1以上的优化就已经去除

ARM下常见的通讯接口

2019-12-15 关键字:uart.spi.i2c 设备间的通信大体上可以分为以下两种: 1.串行通信: 2.并行通信: 其中,串行通信又可分为以下几种: 1.UART: 它的通信需要两个导线,是双全工通信模式. 2.I2C: 它的通信也压根两根导线,但它属于半双工通信模式. 3.SPI: 它的通信需要三个导线,属于全双工通信模式. 串口通信协议: 串口通信协议在嵌入式开发中是很常用的通信协议,它的通信协议结构如下图所示: 因为通信数据的发生是随机的,在不收发数据时串行通信线都是处于空闲状态

对于linux下system()函数的深度理解(整理)

对于linux下system()函数的深度理解(整理) (2013-02-07 08:58:54) 这几天调程序(嵌入式linux),发现程序有时就莫名其妙的死掉,每次都定位在程序中不同的system()函数,直接在shell下输入system()函数中调用的命令也都一切正常.就没理这个bug,以为是其他的代码影响到这个,或是内核驱动文件系统什么的异常导致,昨天有出现了这个问题,就随手百了一下度,问题出现了,很多人都说system()函数要慎用要少用要能不用则不用,system()函数不稳定?

【链接】函数栈帧

本节通过反汇编可执行文件得到的文件,研究函数栈帧的相关内容: 栈帧整体示意图如下 示例代码 #include <stdio.h> #include <iostream> using namespace std; int z = 10; int add(int x, int y) { return x+y+z; } int inc20(int x) { int y = 10; return add(x, y); } int main(void) { int a = 30; a = i

Linux下Kill函数用法

http://www.cnblogs.com/winnxm/archive/2010/01/22/1654502.html [ KILL ]功能描述: 用于向任何进程组或进程发送信号. 1 #include <sys/types.h> 2 3 #include <signal.h> 4 5 int kill(pid_t pid, int sig); 6 7 参数: pid:可能选择有以下四种 1. pid大于零时,pid是信号欲送往的进程的标识. 2. pid等于零时,信号将送往所