函数调用栈

1、函数参数带入(入调用方函数的栈,从右向左入栈)
int fun(int a);
int fun(int a, int b);
int fun(int a, int b, int c);
形参字节							参数带入方式
byte<=4 push a 入栈
byte>=4 && byte<=8 push b push a 入栈
byte>=12 先让栈顶偏移12字节(参数大小) sub esp,0ch
再将实参的值写入的偏移的内存中,从栈顶方向向栈底方向写入 c、b、a 如果是数组入栈,则对应顺序应是a[0],a[1],a[2]
2、函数栈帧开辟
int fun(int a, int b)//主函数中调用该函数
{
int c = a + b;
return c;
}
int main()
{
int a = 10;
int b = 20;
int rt = 0;
rt = fun(a, b);
return 0;
}
	1.实参入栈
2.将main(调用方)栈底地址入栈
3.让 ebp=esp //ebp(栈底指针)指向esp(栈顶指针)位置,充当新函数(调用的函数fun())的栈底
4.指令:"sub esp,栈帧大小" //向低地址(因为栈是从高地址向低地址生长的)开辟空间,esp栈顶指针向低地址偏移
5.将栈帧内存初始化为0xcccccccc //这就是为初始化变量显示"燙燙燙"的原因,部分编译器会初始化成随机值



3、函数返回值的带出
	4字节					eax寄存器
4<=8字节的返回值带出 两个寄存器,eax,edx
>8字节返回值带出
在main(调用方)的栈帧上预留一部分空间
将返回值写入到main预留的空间内
将该部分的地址写入到eax寄存器,由eax寄存器带出
使用时返回值时,从eax寄存器中存储的地址找到存储的位置取出返回值
4、函数栈帧的回退
	将多个寄存器pop			//pop edi esi ebx
esp=edp //回收fun函数的栈帧,esp指向当前栈底
pop main栈底地址 //edp=pop edp回退至调用前的状态(调用方的栈底)
ret //返回调用方
esp+8 //清栈,清理开辟的形参空间,esp所加大小视形参所占空间而定

若返回值字节数小于4,则只需一个寄存器eax即可带出

007B1733  mov         eax,dword ptr [c]

若返回值字节数大于4小于8,则需要两个寄存器eax、edx带出

007B1733  mov         eax,dword ptr [c]
007B1736 mov edx,dword ptr [ebp-2Ch]

若返回值字节数大于8,则需要提前开辟内存空间,将返回值存放与此空间中,由eax带出该空间的地址

struct test	//测试大于8个字节的返回值
{
int a;
int b;
int c;
};
001441C8 mov eax,dword ptr [ebp+8]
001441CB mov ecx,dword ptr [test]
001441CE mov dword ptr [eax],ecx
001441D0 mov edx,dword ptr [ebp-40h]
001441D3 mov dword ptr [eax+4],edx
001441D6 mov ecx,dword ptr [ebp-3Ch]
001441D9 mov dword ptr [eax+8],ecx
001441DC mov eax,dword ptr [ebp+8]
5、三种调用约定的比较

函数调用约定,是指当一个函数被调用时,函数的参数会被传递给被调用的函数和返回值会被返回给调用函数。函数的调用约定就是描述参数是怎么传递和由谁平衡堆栈的,当然还有返回值。基本的函数调用约定由以下几种:

					入参顺序		入参方式				栈帧开辟				返回值带出			栈帧回退			参数清除
__cddecl 从右向左 <=8 push 被调用方(fun)开辟 <=8 寄存器 被调用方(fun) 调用方(main) ret
>8 提前开辟内存 >8 提前开辟内存
__stdcall 从右向左 <=8 push 被调用方(fun)开辟 <=8 寄存器 被调用方(fun) 被调用方(fun) ret 8 //返回到原函数且清除形参(pop + add)
>8 提前开辟内存 >8 提前开辟内存
__fastcall 从右向左 <=8 直接寄存器带入 被调用方(fun)开辟 <=8 寄存器 被调用方(fun) 被调用方(fun),没有入栈,栈帧回退直接清除
>8 超过部分开辟内存 >8 提前开辟内存
函数调用过程分析与汇编指令解析

int fun(int a, int b)
{
/*
push ebp //保存旧栈底
mov ebp,esp //ebp = esp //ebp从旧栈底(mian)跑到新栈底(fun)
sub esp,0cch //开辟栈帧
push …… //push寄存器 //pop ebx esi edi
* lea edi,[ebp-0CCh] //edi保存未把寄存器入栈前的栈顶
* mov ecx,33h
mov 0xcccccccc //初始化前 初始化范围为寄存器与栈底之间的内容
* rep stos dword ptr es:[edi] //循环初始化内存为0xcccc cccc
*/
int c = a + b; // eax,dword ptr [a] \ eax,dword ptr [b] \ dword ptr [c],eax
return c;
/*
mov eax,dword, ptr[c]
*/
}
/*
pop …… //pop 多个寄存器
mov esp,ebp //回收栈,将esp指向(fun)栈底
pop ebp //ebp=pop //ebp回到原栈底
ret //近返回的指令,将栈顶字单元保存的偏移地址作为下一条指令的偏移地址
*/ int main()
{
int c = fun(10, 20);
/*
push 14h
push 0ah
call fun(xxxxxxx) //压如下一行指令 \ 跳转到fun() add esp,8 //清栈
mov dword ptr[c],eax
*/ return 0;
}

测试环境为Microsoft Visual Studio 2019

C与C++的区别之函数调用堆栈的更多相关文章

  1. 嵌入式 linux下利用backtrace追踪函数调用堆栈以及定位段错误

    嵌入式 linux下利用backtrace追踪函数调用堆栈以及定位段错误 2015-05-27 14:19 184人阅读 评论(0) 收藏 举报  分类: 嵌入式(928)  一般察看函数运行时堆栈的 ...

  2. linux下利用backtrace追踪函数调用堆栈以及定位段错误

    一般察看函数运行时堆栈的方法是使用GDB(bt命令)之类的外部调试器,但是,有些时候为了分析程序的BUG,(主要针对长时间运行程序的分析),在程序出错时打印出函数的调用堆栈是非常有用的. 在glibc ...

  3. 用户态使用 glibc/backtrace 追踪函数调用堆栈定位段错误【转】

    转自:https://blog.csdn.net/gatieme/article/details/84189280 版权声明:本文为博主原创文章 && 转载请著名出处 @ http:/ ...

  4. Linux下函数调用堆栈帧的详细解释【转】

    转自:http://blog.chinaunix.net/uid-30339363-id-5116170.html 原文地址:Linux下函数调用堆栈帧的详细解释 作者:cssjtuer http:/ ...

  5. Linux下利用backtrace追踪函数调用堆栈以及定位段错误[转]

    来源:Linux社区  作者:astrotycoon 一般察看函数运行时堆栈的方法是使用GDB(bt命令)之类的外部调试器,但是,有些时候为了分析程序的BUG,(主要针对长时间运行程序的分析),在程序 ...

  6. 函数调用堆栈及活动记录 堆栈溢出 stack overflow

    小结: 1.当被调函数返回主调函数时,被调函数的 活动记录-activation record / 堆栈帧-stack frame 被 弹出-popping 程序执行栈-program executi ...

  7. 【转】Android下面打印进程函数调用堆栈(dump backtrace)的方法

    1. 为什么要打印函数调用堆栈? 打印调用堆栈可以直接把问题发生时的函数调用关系打出来,非常有利于理解函数调用关系.比如函数A可能被B/C/D调用,如果只看代码,B/C/D谁调用A都有可能,如果打印出 ...

  8. Lab_1:练习5——实现函数调用堆栈跟踪函数

    题目:实现函数调用堆栈跟踪函数 我们需要在lab1中完成kdebug.c中函数print_stackframe的实现,可以通过函数print_stackframe来跟踪函数调用堆栈中记录的返回地址.如 ...

  9. 利用Xposed Hook打印Java函数调用堆栈信息的几种方法

    本文博客链接:http://blog.csdn.net/QQ1084283172/article/details/79378374 在进行Android逆向分析的时候,经常需要进行动态调试栈回溯,查看 ...

随机推荐

  1. c# 自定义控件-提示框(弹框)

    分带取消按钮和不带取消按钮的 调用方法: frmMessageBox frm = new frmMessageBox("提示", "数据连接失败,请重试!", ...

  2. JZ-028-数组中出现次数超过一半的数字

    数组中出现次数超过一半的数字 题目描述 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中出现了5次,超 ...

  3. LeetCode-048-旋转图像

    旋转图像 题目描述:给定一个 n × n 的二维矩阵 matrix 表示一个图像.请你将图像顺时针旋转 90 度. 你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵.请不要 使用另一个矩 ...

  4. think php 验证码

    1.下载 composer require topthink/think-captcha 1.* // composer 下载 //过程 D:\PHP\phpstudy_pro\WWW\1906A\p ...

  5. 4、mysql的存储引擎

    存储引擎 存储引擎是负责对表中的数据进行提取和写入工作的,我们可以为不同的表设置不同的存储引擎,也就是说不同的表可以有不同的物理存储结构,不同的提取和写入方式. 1.1 InnoDB 引擎:具备外键支 ...

  6. idea Alt+Insert:构造函数

    Alt+Insert:构造函数 包含参数: Constructor:全参构造 Getter:必须的 return 字段名 Setter:this 值 Getter and Setter:选择字段以生成 ...

  7. SAS 数值存储方式和精度问题

    本文链接:https://www.cnblogs.com/snoopy1866/p/16021137.html 1 数值存储方式 SAS使用8个字节存储数值,使用浮点计数法表示数值. 浮点计数法由4个 ...

  8. IDW插值得到降水量分布图

    4 具体思路 4.1 加载数据 (1)点击[添加数据],选择"中国地面气候资料国际交换站数据集台站信息.csv"."省份.shp"和"2011年中国地 ...

  9. Mysql下载路径和安装

    下载路径 https://dev.mysql.com/downloads/mysql/ C:\Windows\system32>net start mysql 发生系统错误 2. 系统找不到指定 ...

  10. How to Rebase Git Branch

    转自:How to Rebase Git Branch (with 3 steps) | Git Rebase | W3Docs Steps to rebasing branch Fetching c ...