C/C++——函数调用栈

6 篇文章 0 订阅
2 篇文章 0 订阅

https://blog.csdn.net/xy294636185/article/details/79999311

Intel 32位体系结构(简称IA32)寄存器使用约定

程序寄存器组是唯一能被所有函数共享的资源。虽然某一时刻只有一个函数在执行,但需保证当某个函数调用其他函数时,被调函数不会修改或覆盖主调函数稍后会使用到的寄存器值。因此,IA32采用一套统一的寄存器使用约定,所有函数(包括库函数)调用都必须遵守该约定。

  1. 寄存器%eax、%edx和%ecx为主调函数保存寄存器(caller-saved registers),当函数调用时,若主调函数希望保持这些寄存器的值,则必须在调用前显式地将其保存在栈中;被调函数可以覆盖这些寄存器,而不会破坏主调函数所需的数据。
  2. 寄存器%ebx、%esi和%edi为被调函数保存寄存器(callee-saved registers),即被调函数在覆盖这些寄存器的值时,必须先将寄存器原值压入栈中保存起来,并在函数返回前从栈中恢复其原值,因为主调函数可能也在使用这些寄存

在x86处理器中,

  1. EIP(Instruction Pointer)是指令寄存器,指向处理器下条等待执行的指令地址(代码段内的偏移量),每次执行完相应汇编指令EIP值就会增加。
  2. ESP(Stack Pointer)是堆栈指针寄存器,存放执行函数对应栈帧的栈顶地址(也是系统栈的顶部),且始终指向栈顶;
  3. EBP(Base Pointer)是栈帧基址指针寄存器,存放执行函数对应栈帧的栈底地址,用于C运行库访问栈中的局部变量和参数。

在这里插入图片描述

图中给出主调函数(caller)和被调函数(callee)的栈帧布局,”m(%ebp)”表示以EBP为基地址、偏移量为m字节的内存空间(中的内容)。
该图基于两个假设:
第一,函数返回值不是结构体或联合体,否则第一个参数将位于”12(%ebp)” 处;
第二,每个参数都是4字节大小(栈的粒度为4字节)。
此外,函数可以没有参数和局部变量,故图中“Argument(参数)”和“Local Variable(局部变量)”不是函数栈帧结构的必需部分。

从图中可以看出,函数调用时入栈顺序为

实参N-1→==主调函数返回地址==→主调函数帧基指针EBP→被调函数局部变量1-N

主函数的返回地址就是主函数结束后,要返回到哪里去,负责主函数进程的资源回收等,需要保存主调函数返回地址,其实是保存当前LR的值,因为在主调函数准备调用被调函数时,LR的值会被覆盖为主调函数希望被调函数返回后继续执行的PC值,但是对于X86没有LR寄存器,希望返回的PC值由call指令在某处保存

ARM体系

ARM微处理器共有37个寄存器,其中31个为通用寄存器,6个为状态寄存器。但是这些寄存器不能被同时访问,具体哪些寄存器是可编程访问的,取决于微处理器的工作状态及具体的运行模式。
但在任何时候,通用寄存器R0~R15、一个或两个状态寄存器都是可访问的。

有三个特殊的通用寄存器:

  1. 寄存器R13:在ARM指令中常用作堆栈指针SP
  2. 寄存器R14:也称作子程序连接寄存器(Subroutine Link Register)即连接寄存器LR
  3. 寄存器R15:也称作程序计数器PC

寄存器分成了两类,一类叫做调用者保存的寄存器(caller saved registers),另一类叫做被调用者保存的寄存器(callee-saved registers)。

调用者保存的寄存器包括,R0-R3,R12,LR,PSR。
被调用者保存的寄存器包括,R4-R11。

  1. 在调用函数前,程序随意使用R4-R11的,(这里随意使用的前提还是要从该函数仍然是callee的身份去确认,在随意使用前必须把这些寄存器放在自己的栈上)因为接下来被调用的函数有义务把R4-R11恢复成调用前的样子。但是如果在调用完函数后还需要使用寄存器(R0-R3,R12,LR,PSR),那么就得先把这些寄存器保存起来,接下来得函数返回后再把这些寄存器恢复(自己保存自己恢复,时机?)。
  2. 在被调用函数中,可以随意使用(R0-R3,R12,LR,PSR),因为被调用函数对他们的值不负责。而如果要用到R4-R11,被调用函数有责任保存它们的状态。也就是说,被调用函数需要保证R4-R11在进入函数时的值和退出函数时的值是一样的。如果在被调用函数中把这些寄存器值改变了,就一定要把他们恢复成被调用前的样子。

除了以上提到的寄存器外,带浮点型运算单元的Cortex-M4内核还有额外寄存器需要处理。

  1. 调用者保存的寄存器包括,S0-S15。
  2. 被调用者保存的寄存器包括,S16-S31。

查看通用寄存器,可以发现R13(SP)和R15(PC)没有涉及到。
SP在常规的C函数中当然是保存当前的栈地址,不管是调用函数前还是在被调用函数中都要用到SP。使用的栈空间没有变化,所以SP也不用保存了。
而在函数调用前,把LR先压入栈中,然后把当前PC传给LR。这里的LR是该函数的调用者caller (称作函数A)在调用它 (称作函数B,即目前在运行的函数)时赋的值,用来callee准备返回时能够继续caller的PC值继续往下执行,前提是把callee把修改过的寄存器给恢复。
这样在函数返回时,就会把LR赋值给PC,进而恢复到函数调用前的运行位置。这里的LR在函数B调用了另一个函数C返回后,又重新被赋值为函数A要求函数B返回后继续执行的PC值

ARM 下的帧指针

在Intel平台上, BP 是用来指向堆栈帧的开始,以及访问参数 [BP + 0X ??] 和局部变量 [BP-0X ??] 。哪个寄存器ARM中使用?或者,是解决基于对SP只?
帧指针。一般来说, R7 充当Thumb模式帧指针R11 充当ARM模式帧指针。但它是操作系统的判断下,如果它要改变这种习惯。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值