首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

有关调用约定的历史–第三部分

好了,可以讲讲大家熟悉的东西了

今天的第三部分,主要内容是x86平台上的32位调用约定。

这里先说明一下:今天的内容只会涵盖Windows操作系统或者Windows编程中涉及到的调用约定,没有关于其他操作系统或者特定于某个语言或者编译器厂商的内容。特别需要注意的是,如果尝试在C++中调用一个成员函数,则函数的第一个参数是一个”隐藏”的this指针。

All

所有在x86平台上的32位调用约定,都会保留EDI, ESI, EBP和EBX寄存器,并使用EDX:EAX寄存器对来返回值。

C(__cdecl)

32位的C调用约定上的约束限制和它的16位版本一样。参数从右至左入栈(所有,第一个参数会位于栈顶),并且调用者负责清栈。函数名称通过一个前置的下划线进行修饰。

__stdcall

这个调用约定主要用在Win32中,一个例外情况是可变参数的情况(可变参数将使用__cdecl),还有一些很少的函数会使用__fastcall。

参数从右至左入栈,被调用者负责清栈。函数名称通过一个前置下划线和指示参数字节数的结束字符(@-)来进行修饰。

__fastcall

在这种调用约定下,前面两个参数使用ECX和EDX来传递,剩余的参数则和__stdcall一样通过栈来传递。请注意,被调用者负责清栈。函数名称通过一个前置字符(@-)和一个指示参数字节数的结束字符(@-)来进行修饰。

thiscall

第一个参数(也就是上面提到的this指针)通过ECX传递,剩余的参数则通过栈来传递。被调用者负责清栈。函数名称被C++编译器使用了一种十分复杂的方式进行修饰,里面包含了每个参数的类型。

这个修饰手法十分有必要,因为它可以实现类似于函数重载的特性。也就是说:对于不同的重载的函数,C++编译器采用不同的名称进行修饰,以区分它们。

总结

调用约定,说白了,就是调用者和被调用者之间的一种合约。

如果你编写汇编代码,则调用约定意味着:你编写的回调函数需要保留调用约定所规定的寄存器,因为调用者(可能是操作系统本身)依赖于它。

举例来说,如果在一次函数中EBX寄存器中的数据搞乱了,那么就会出现不符合预期的行为,这个时候,请不要感到惊讶。后面我们会有关于这个主题的更多内容,敬请期待。

最后

Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。

本文来自:《The history of calling conventions, part 3》

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200929A0HD1F00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券