为什么Linux32中的调用程序会执行这些额外的操作?
原因是使用由编译器注入的隐藏指针(称为返回值优化),用于按值返回结构。在SystemV的ABI,第41页的“函数返回结构或联合”部分中,它表示:
被调用的函数必须在返回之前从堆栈中删除该地址。
这就是为什么您
ret $0x4在末尾获得a的原因
test_callee5(),这是为了遵守ABI。
现在大约
sub $0x4,%esp在每个
test_callee5()调用站点之后,它是上述规则的副作用,与C编译器生成的优化代码结合在一起。由于本地存储堆栈空间是完全通过以下方式预先保留的:
3: sub $0x38,%esp
无需压入/弹出隐藏的指针,只需将其写在预先保留的空间的底部(由指向
esp),使用
mov%eax,(%esp)第9行和第17行即可。由于堆栈指针没有减少,因此
sub $0x4,%esp可以求反的效果
ret$0x4,并保持堆栈指针不变。
在Win32(我猜是使用MSVC编译器)上,没有这样的ABI规则,使用了一个简单的规则
ret(如cdecl中所预期的那样),隐藏的指针在第7行和第11行被压入堆栈。作为一种优化,但仅在被调用者退出之前,使用调用,以
addesp,1Ch释放隐藏的指针堆栈插槽(2 * 0x4字节)和本地
AStruct结构(0x14字节)。
cdecl不会为返回结构的函数定义调用约定吗?
不幸的是,事实并非如此,它随C编译器和操作系统的不同而不同。



