小览call stack(调用栈) (二)——调用约定

在上一篇博客中小览call stack(调用栈) (一)中,我展示了如何在windbg中 观察调用栈的相关信息:函数的返回地址,参数,返回值。这些信息都按照一定 的规则存储在固定的地方。这个规则就是调用约定(calling convention)。

调用约定在计算机界不是什么新鲜的概念,已经有许多相关的文献给予详细 的介绍。比较全面的介绍可以参见wikipedia上的相关页面。然而,如果你和我 一样,在第一次接触调用约定的时候,觉得这个概念是个高深神秘的冬冬,那么 就请跟随我一起,在这篇博客中看看他的由来,他的范畴以及他的用途。

为什么需要调用约定?

在具体介绍调用约定的定义之前,我们先来看看为什么我们需要一个称之为 调用约定的冬冬。如果各位了解汇编语言(不了解的话,看下面的这段会稍微有 些费力,不过我尽可能把汇编的相关知识解释的清楚一些),那么回忆一下我们 是怎么来做一个函数调用的。

汇编语言提供了一条指令,call ptr,其功能是把CS:IP (指令段:指令指针 ,决定着下一条执行指令的地址)压栈,并且修改CPU的指令指针,作一个跳转。 在函数结束的地方,我们使用另一条指令,ret,其功能是把栈中的返回地址取 出,并且跳转到那条指令。

在这里汇编语言只提供了指令跳转的命令,作为函数调用另一个重要组成部 分的参数传递,其方式就很灵活,你可以通过寄存器传值,可以通过调用栈传值 ,可以通过某一块具体的内存传值(类似全局变量)。然后在被调用函数中,从寄 存器,栈或者是内存中读取这些信息。想象一下如果被调用函数是某一个程序员 所编写的,调用者是另一个程序员,那么他俩之间对于参数的传递方式就有了一 个约定。

高级语言的出现,把这个问题隐藏了起来。我们在编写一般的c++程序的时候 ,通常不需要顾虑参数传递的底层实现,但是,这并不意味着这一问题不再出现 ——我们只是把责任推给了编译器。编译器作为一个计算机程序,总 是遵照一定的规则工作,每一个规则对应了一种调用约定。

久而久之,那些经典的规则所产生的调用约定,就成了耳熟能详的冬冬:

耳熟能详的调用约定

在介绍这些调用规范之前,我想先说明的是,下面所涉及的调用规范是在32 位x86处理器windows平台上的。把范畴限定在32位处理器的原因是:16位处理器 已经退出CPU的历史舞台,64微处理器无论是IA64还是AMD64都只有一个调用规范 ——只有32位处理器呈现百家成名,百花齐放的景象。(对了,你当 然明白调用规范是绑定在处理器架构上的概念,因为它涉及太多的诸如寄存器之 类的处理器架构细节。)聚焦于windows则是因为我现在的工作只涉及这一平台。

下表的出处来自于The Old New Thing以及张羿的csdn专栏,并作了适当修改 。

首先来看所有的调用规范都遵循的规定:返回值存储在EDX:EAX中,EDI,ESI ,EBP,EBX是保留的存储器。(即函数可以任意使用这些寄存器,无需担心破坏 了调用者的寄存器状态)

调用约定名称 清理堆栈 参数压栈顺序 备注
cdecl 调用者 (Caller) 从右往左  因为是调用者清理Stack,因此允许变参 (如 printf)
stdcall 被调用者 (Callee) 从右往左  一般在Windows API和COM中使用,也是.NET和 Native代码调用的缺省Calling Convention。
顺便提一下,Windows中API的 Calling Convention所使用到的WINAPI宏在PC机上是__stdcall,而在WinCE上则 是__cdecl,并非一成不变。
Thiscall (Microsoft) 被调用者 (Callee) 从右往左 基本上等价stdcall, 除了this指针用ECX传递
Fastcall (Microsoft) 被调用者 (Callee) 从右往左 和Stdcall类似,但是会选择两个从左往右数最 先可以放在寄存器里面的参数放在ECX和EDX中
时间: 2024-11-02 16:41:12

小览call stack(调用栈) (二)——调用约定的相关文章

小览call stack(调用栈) (一)

栈在计算机领域中是个经常提到的名词,数据结构中有栈:网络传输中有协 议栈.今天我们讨论的调用栈(call stack),指的是在程序的执行过程中存储函 数调用信息的动态数据结构. 这个定义可能太抽象了一些,在给出具体的例子之前,请大家先思考一个问 题,哪些信息是函数调用过程中所需要的?或者这么问,一个编译器,在面对一 个函数的调用指令时,该生成哪些代码? 首先,函数的返回地址要保存下来.就好像你和你的小狗玩仍飞碟游戏,每 一个函数调用好比扔一个飞碟,当你的狗狗哼兹哼兹的捡来飞碟,函数完执行的 时

小览CallStack(调用栈)(三)-用调试器脚本查看调用栈信息

在这一系列之前的两篇文章中,我介绍了如何在windbg中查看调用栈的相关 信息(详见小览call stack(调用栈)(一)),以及调用约定(详见小览call stack(调用栈) (二)--调用约定).今天的这篇博客在二者的基础 之上,介绍如何使用调式器脚本程序来观察调用栈.对CallStack感兴趣的朋友 可以在此基础上开发更加详尽的脚本来观察CallStack的信息:对调试感兴趣的 朋友则可以看一下DScript的用处. 我们先来看一个例子,下面的程序并不是一个优美的程序片段,但是它能够

工厂模式与抽象工厂模式小览(二)

一.文章来由 就等啦~~还记得工厂模式与抽象工厂模式小览(一)第一部吗?我们在第一部中,分别详细的描述了(1)简单工厂(2)工厂模式(3)抽象工厂模式,但是并没有描述他们之间的关系,也没有比较工厂模式和抽象工厂模式,这对难舍难分的好基友之间的异同点,这些工作我们在第二部中完成~ 二.工厂模式和简单工厂 话说十年前,有一个暴发户,他家有三辆汽车--Benz奔驰.Bmw宝马.Audi奥迪,还雇了司机为他开车.不过,暴发户坐车时总是怪怪的:上Benz车后跟司机说"开奔驰车!",坐上Bmw后他

前端魔法堂——调用栈,异常实例中的宝藏

前言  在上一篇<前端魔法堂--异常不仅仅是try/catch>中我们描述出一副异常及如何捕获异常的画像,但仅仅如此而已.试想一下,我们穷尽一切捕获异常实例,然后仅仅为告诉用户,运维和开发人员页面报了一个哪个哪个类型的错误吗?答案是否定的.我们的目的是收集刚刚足够的现场证据,好让我们能马上重现问题,快速修复,提供更优质的用户体验.那么问题就落在"收集足够的现场证据",那么我们又需要哪些现场证据呢?那就是异常信息,调用栈和栈帧局部状态.(异常信息我们已经获取了) 本文将围绕上

关于 调用栈 的请教

问题描述 昨天惊闻调用栈,以前听过栈,还真没有听过调用栈,高手们灌水吧,什么是调用栈,和栈什么区别啊 解决方案 解决方案二:调用栈?解决方案三:是啊,什么东东,高手不吝赐教

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

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

转换GDB调用栈到流程图

如果你想在GDB调试时把调用堆栈保存下来归档,那下面这个脚本就方便你了.原理是将调用堆栈的函数抽取出来,再完成调用关系就可以了. 首先你要安装dot (Mac OS下安装Graphviz), 如果你想转为文本格式,就可安装Perl的Graph::Easy包(命令行:sudo perl -MCPAN -e 'install Graph::Easy', Ubuntu下直接安装libgraph-easy-perl). 然后按需要执行脚本就可以了, 假定如下的调用栈:    #0  WebCore::F

java matlab 二维数组-java调用matlab中调用二维数组问题

问题描述 java调用matlab中调用二维数组问题 我是用的以下matlab函数 function [Theta, Phi, R] = infofusion(a1, b1, a1, b2, a3, b3): 其中a1,b1,a2,b2,a3,b3都是4*3的二维数组,并且每个数组里面都是常量,都是数,请问各位大神,如何在java调用matlab的基础上实现二维数组的调用,跪求了

为什么代理会被调用了二次?

问题描述 怪怪的,为什么网站计数器会刷新一次会自动计算二次呢?在代理中作了messagebox,显示代理被调用了二次.而我在webqueryopen中仅作了一次@command([ToolsRunMacro];"counter")之后我分步检查记录如下:我做了如下操作:第一步:取消掉webqueryopen中的调用代理之后,计数器(代理)无效.正常,说明问题在计数器这个代理上.第二步:删除计数器.网站显示出错,说明计数器有效正常.这说明问题应该在计数器(代理)的代码中.第三步:检查代码