函数调用约定

最近在研究反汇编相关的东西,看到了一些汇编中函数调用的约定,顺便记录下来。


cdecl
x86体系结构的许多C编译器使用的默认调用约定叫做C调用约定(c declaration)。cdecl调用约定规定:调用方按从右到左的顺序将函数参数放入 栈中,在被调用的函数完成其操作时,调用方(而不是被调用方)负责从栈中清除参数。从右到左在栈中放入参数的一个结果是,如果函数被调用,最左边的(第一个)参数将始终 位于栈顶。这样,无论该函数需要多少个参数,我们都可轻易找到第一个参数。因此,cdecl调用约定非常适用于那些参数数量可变的函数(如printf)。
要求调用函数从栈中删除参数,意味着你将经常看到:指令在由被调用的函数返回后,会立即对程序栈指针进行调整。如果函数能够接受数量可变的参数,则调用方非常适于进行这种调整, 因为它清楚地知道,它向函数传递了多少个参数,因而能够轻松做出正确的调整。而被调用的函数事先无法知道自己会收到多少个参数,因而很难对栈做出必要的调整。
例如我们调用一个拥有以下原型的函数:

void demo_cdecl(int w, int x, int y, int z);

默认情况下,这个函数将使用cdecl调用约定,并希望你按从右到左的顺序压入4个参数,同 时要求调用方清除栈中的参数。编译器可能会为这个函数的调用生成以下代码:

; demo_cdecl(1, 2, 3, 4);
push     4                    ;  push parameter z
push     3                    ;  push parameter y
push     2                    ;  push parameter x
push     1                    ;  push parameter w
call     demo_cdecl           ;  call the function
add      esp, 16              ;  改变 esp,因为栈是从高地址到低地址增长的

在调用函数时,栈指针都会指向最左边的参数。

stdcall
这种约定在函 数声明中使用了修饰符_stdcall,如下所示:

void _stdcall demo_stdcall(int w, int x, int y);

和cdecl调用约定一样,stdcall调用约定按从右到左的顺序将函数参数放在程序栈上。使用stdcall调用约定的区别在于:函数结束执行时,应由被调用的函数负责删除栈中的函数参数。对被调用的函数而言,要完成这个任务,它必须清楚知道栈中有多少个参数,这只有在函数接受的参数数量固定不变时才有可能。因此,printf这种接受数量可变的参数的函数不能使 用stdcall调用约定。例如,demo_stdcall函数需要3个整数参数,在栈上共占用12个字节(在 32位体系结构上为3*sizeof(int))的空间。x86编译器能够使用RET指令的一种特殊形式,同时 从栈顶提取返回地址,并给栈指针加上12,以清除函数参数。demo_stdcall可能会使用以下指 令返回到调用方:

ret 12  ;  return and clear 12 bytes from the stack

使用stdcall的主要优点在于,在每次函数调用之后,不需要通过代码从栈中清除参数,因而能够生成体积稍小、速度稍快的程序。

fastcall
fastcall约定是stdcall约定的一个变体,它向CPU寄存器(而非程序栈)最多传递两个参数。 Microsoft Visual C/C++ 和GNU gcc/g++(3.4及更低版本)编译器能够识别函数声明中的fastcall 修饰符。如果指定使用fastcall约定,则传递给函数的前两个参数将分别位于ECX和EDX寄存器 中。剩余的其他参数则以类似于stdcall约定的方式从右到左放入栈上。同样与stdcall约定类似 的是,在返回其调用方时,fastcall函数负责从栈中删除参数。下面的声明中即使用了fastcall 修饰符:

void fastcall demo_fastcall(int w, int x, int y, int z);

为调用demo_fastcall,编译器可能会生成以下代码:

; demo_fastcall(1, 2, 3, 4);
push     4                    ;  move parameter z to the second position on stack
push     3                    ;  move parameter y to the top position on stack
mov      edx, 2               ;  move parameter x to edx
mov      ecx, 1               ;  move parameter w to ecx
call     demo_fastcall        ;  call the function

注意,调用demo_fastcall返回后,并不需要调整栈,因为demo_fastcall负责在返回到调用 方时从栈中清除参数y和z。由于有两个参数被传递到寄存器中,被调用的函数仅仅需要从栈中清除8字节,即使该函数拥有4个参数也是如此,理解这一点很重要。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容

  • 关于 C/C++ 函数调用约定,大多数时候并不会影响程序逻辑,但遇到跨语言编程时,了解一下还是有好处的。 VC 中...
    王守伟阅读 2,320评论 0 2
  • 原文地址:C语言函数调用栈(一)C语言函数调用栈(二) 0 引言 程序的执行过程可看作连续的函数调用。当一个函数执...
    小猪啊呜阅读 4,610评论 1 19
  • 函数调用约定 在C语言中,假设我们有这样的一个函数: int function(int a,int b) 调用时只...
    罗蓁蓁阅读 611评论 0 4
  • 1. c调用约定 _cdecl 调用方将参数从右面到左压栈,被调用函数完成后,调用方负责从栈中清除参数。 2. s...
    jiango86阅读 876评论 0 2
  • 今天是11月29号,星期三,晴。 今天的温度跟昨天比起来有些小冷,早上吃过早饭以后就送孩子上学了,顺便我也上班,感...
    雅琳妈妈阅读 92评论 0 0