程序调用栈

1. 栈帧

  计算机使用栈这样的结构来支持函数调用,栈用来传递过程参数、存储返回信息、保存寄存器信息用于恢复、存储局部变量等。每一次函数调用,系统都会在栈中开辟一块空间用来保存执行状态,为每次函数调用分配的栈空间成为栈帧

  上图描绘了栈帧的结构,帧指针和栈顶指针标示了栈帧的范围,帧指针指示栈帧的起始位置,通常使用寄存器%ebp来保存帧指针,使用寄存器%esp来保存栈顶指针。

  假设函数P调用函数Q,则P传递给Q的参数(即实参)保存在P帧的尾部,P帧结尾保存着调用Q完成之后的返回地址(返回地址即调用Q完成之后执行的下一条指令的地址)。Q帧首先保存寄存器%ebp的值(即P帧起始位置),然后保存一些寄存器的值以及局部变量,如果Q帧又调用了R帧,则Q帧尾部保存传递给R的实参,Q帧最后保存调用R完成之后的返回地址。

  在被调用函数栈帧中,对于传进来参数可以使用相对于寄存器%ebp的正偏移量访问。

2. call指令和ret指令

call指令用来调用另一个函数,执行call指令的效果即将返回地址入栈并跳转到被调用函数的起始指令,返回地址就是调用函数中call指令后面的那一条指令;

 ret指令用来从栈中弹出返回地址,并跳转到这个地址;

3. 函数调用的寄存器使用规则

一个函数调用另一个函数时,被调用函数可能会覆盖调用函数保存在寄存器中的值,因此我们需要有一个统一的规则规定调用时如何使用和保存寄存器的旧值。

根据规则,寄存器%eax(通常用来保存返回值)、%edx和%ecx是调用者负责保存旧值的寄存器,被调用函数可以直接覆盖这些寄存器的旧值;寄存器%ebp、%ebx、%esi和%edi是被调用者负责保存旧值的寄存器,被调用函数在覆盖这些寄存器之前要在自己的栈帧中先保存寄存器的旧值,并负责在返回前恢复这些寄存器的旧值。

时间: 2024-10-13 16:17:05

程序调用栈的相关文章

在c或c+程序里打印调用栈。转

在C/C++程序里打印调用栈信息 我们知道,GDB的backtrace命令可以查看堆栈信息.但很多时候,GDB根本用不上.比如说,在线上环境中可能没有GDB,即使有,也不太可能让我们直接在上面调试.如果能让程序自己输出调用栈,那是最好不过了.本文介绍和调用椎栈相关的几个函数. NAME       backtrace, backtrace_symbols, backtrace_symbols_fd - support for application self-debugging SYNOPSIS

编写高质量代码改善C#程序的157个建议——建议70:避免在调用栈较低的位置记录异常

建议70:避免在调用栈较低的位置记录异常 并不是所有的异常都要被记录到日志,一类情况是异常发生的场景需要被记录,还有一类就是未被捕获的异常.未被捕获的异常通常被视为一个Bug,所以,对于它的记录,应该被视为系统的一个重要组成部分. 最适合记录异常和报告的是应用程序的最上层,这通常是UI层.假设存在这样一个应用程序,它的BLL层可能被一个WinForm窗体调用,也可能被一个控制台应用程序调用,那么要在BLL模块向管理员报告异常的时候,你可能会不知道该使用MessageBox方法还是使用Consol

android native HAL程序 java程序 linux kernel打印调用栈的方法

android native HAL程序 java程序 linux kernel打印调用栈的方法 关于android java打出调用栈的方法 1)方法一:refs:frameworks/base/services/java/com/android/server/ActivityManagerService.javastartProcessLocked(){Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "amProcessStart&quo

java实现hello/hi的简单的网络聊天程序与ServerSocket调用栈跟踪

java实现hello/hi的简单的网络聊天程序 网络聊天采用TCP协议通过java实现 import java.io.*; import java.net.Socket; public class Client { public static void main(String[] args) throws Exception{ Socket socket = new Socket("192.168.31.68", 6666); BufferedReader reader = new

[daily] 查看linux程序或操作的kernel内核调用栈

一 [classic_tong @ https://www.cnblogs.com/hugetong/p/12198122.html] 查看一个命令或程序,都调用了什么系统API的方法, 可以是用strace [[email protected] OUTPUT_nginx]# strace echo execve("/usr/bin/echo", ["echo"], [/* 22 vars */]) = 0 brk(NULL) = 0x1311000 mmap(NU

Android群英传笔记——第八章:Activity与Activity调用栈分析

Android群英传笔记--第八章:Activity与Activity调用栈分析 开篇,我们陈述一下Activity,Activity是整个应用用户交互的核心组件,了解Activity的工作模式,生命周期和管理方式,是了解Android的基础,本节主讲 Activity的生命周期与工作模式 Activity调用栈管理 一.Activity Activity作为四大组建出现平率最高的组件,我们在哪里都能看到他,就让我们一起先来了解一下他的生命周期 1.起源 Activity是用户交互的第一接口,他

转:《链接、装载与库》里的一个错误:关于调用栈

<链接.装载与库>里的一个错误:关于调用栈 按照原文中描述做了一个PPT: 每次执行push指令时,esp都会减4(因为栈是向低地址增长的),每次pop时esp都会加4. 指令:push a 指令:push b 指令: 1.把main方法当前指令的下一条指定地址(即return address))push到栈中. 2.使用call指令调用目标函数体. 指令:将ebp的当前值push到栈中,即saved ebp. 指令:将esp的值赋给ebp,则意味着进入了foo方法的调用栈. 指令:push

寒假捉虫记——从一段损坏的调用栈开始折腾

放假在家,继续调试<家园>.目前的进度是MinGW上的编译链接都已通过,游戏程序也已经可以跑起来并进入主菜单界面,但加载关卡之后就会闪退.这让我想起了以前上中学时玩盗版游戏的日子.那个年代的单机游戏估计大多是用C/C++写的,一个不小心的内存操作就会让进程崩掉:而且那个年代的操作系统没现在稳定,可能破解技术也不够先进,从电脑城里买来的五六块钱的盗版游戏质量参差不齐.很多游戏跑着跑着就闪退,有的甚至连打都打不开,让人甚为恼火.如今源代码在手,并且我也是程序员了,可以对闪退的原因一探究竟,再也不用

VC和LUA混合开发之VC程序调用Lua脚本函数

http://www.cnblogs.com/clever101/archive/2010/04/14/1712207.html 作者:朱金灿 来源:http://www.cnblogs.com/clever101/ Lua语言以其卓越的可扩展性.简单.高效率和与平台无关性在游戏领域得到广泛应用.今天花了一些时间学习VC和Lua如何混合开发,编写了一个小例程,和大家分享一下这方面的经验. 首先在http://www.lua.org/ftp/下载Lua的最新版本:lua-5.1.4.然后用VS C