问题描述
我试图查看MSVC如何分配其32字节的影子空间,但似乎只分配8字节的影子空间。
// Test.c
int main() {int var1 = 1;}
上面的程序将产生以下.asm文件:
var1$ = 0
main PROC
; Test.c
sub rsp,24 ; allocates 24 bytes
mov DWORD PTR var1$[rsp],1
xor eax,eax
add rsp,24
ret 0
main ENDP
它仅分配24个字节。当我声明4个变量时,它分配相同的数量,并且由于每个变量为4个字节,因此必须表示24个字节中的16个字节用于声明的变量,而阴影空间保留8个字节。
仅在声明5个变量时才分配40个字节的影子空间。为什么只分配8个字节的影子空间?
我使用命令CL Test.c /Fa
解决方法
从 RSP 中减去24与此处的阴影空间没有任何关系。仅当main
调用某些其他64位Microsoft ABI兼容函数时,影子空间才适用。您的main
函数是叶子函数(它不会调用其他任何函数),因此不需要为影子空间分配额外的空间。如果您修改了main
以调用 C / C ++ 库或WinAPI中的某些内容,您会发现将为影子空间添加额外的空间以进行这样的调用。
鉴于您的函数正在处理32位值(且没有数组),并且不调用其他任何内容,我认为没有理由为什么它需要与16字节边界对齐或添加额外的填充,但这就是原因它似乎正在做。堆栈上的返回地址将堆栈未对齐8。减24会将其在16字节边界上对齐,并在变量后填充。
这很可能是由于未进行任何优化(例如/O1
,/O2
等)编译时,或编译器将局部变量空间填充到首选数量时,代码生成效率低下的结果。从理论上讲,在这种情况下,不必分配任何堆栈空间。它可能已经重用了 C / C ++ 启动代码为main
函数分配的返回地址上方的影子空间。
注意:通过优化,除非您将var1
设置为volatile
变量,否则将完全消除代码。编译器应该识别出您编写的代码除了返回给调用者之外没有任何作用。
以下示例调用ExitProcess
以显示已添加阴影空间;重用由调用main
的 C / C ++ 启动代码分配的影子空间作为局部变量;并使用一些堆栈空间作为变量,使其无法容纳在阴影空间中。由于称为ExitProcess
的WinAPI需要在调用之前分配32字节的影子空间。如果从本示例中将其删除,编译器将不会为其分配额外的空间。
test.c
// Test.c
// Get prototype for ExitProcess
#include <windows.h>
int main()
{
volatile int var1 = 1;
volatile int var2 = 2;
volatile int var3 = 3;
volatile int var4 = 4;
volatile int var5 = 5;
// Since this is a WinAPI call it needs shadow space allocated
ExitProcess(var1+var2+var3+var4+var5);
// We won't get this far
return 0;
}
如果使用CL Test.c /Fa /O2
用/O2
optimizations进行编译以获得最大速度,则可能会看到与 类似的:
var1$ = 32
var5$ = 64
var4$ = 72
var3$ = 80
var2$ = 88
main PROC
sub rsp,56 ; 00000038H
mov DWORD PTR var1$[rsp],1
mov DWORD PTR var2$[rsp],2
mov DWORD PTR var3$[rsp],3
mov DWORD PTR var4$[rsp],4
mov DWORD PTR var5$[rsp],5
mov edx,DWORD PTR var5$[rsp]
mov eax,DWORD PTR var4$[rsp]
add edx,eax
mov ecx,DWORD PTR var3$[rsp]
add ecx,edx
mov edx,DWORD PTR var2$[rsp]
add edx,ecx
mov ecx,DWORD PTR var1$[rsp]
add ecx,edx
call QWORD PTR __imp_ExitProcess
int 3
main ENDP
var1
与 RSP 的偏移量为32,因为阴影空间是从 RSP 开始的前32个字节,以调用{{1} }。其他变量ExitProcess
,var2
,var3
和var4
均以> = 64的偏移量开始。编译器将 RSP 调整为56。 >。返回地址为 RSP + 56 ,var5
在 RSP + 64 到 RSP + 96 处有阴影空间,因此{{ 1}}至main
放置在为var2
分配的阴影空间中。