MSVC 为易失性局部变量生成奇怪的代码

逆向工程 拆卸 C++ x64
2021-06-20 10:46:34

我想知道 MSVC 2010 如何为 volatile 局部变量生成代码并进行了测试。这个简单的测试函数使用了一个 volatile 局部变量:

int proc1(int a, int b) {
    int volatile volvar=0;

    int c=a;
    if (b>a) 
        c=0;
    return c;
}

volvar由于volatile关键字,优化器不应消除整数的初始化生成的64bit程序集是这样的:

_TEXT   SEGMENT
volvar$ = 8
a$ = 8
b$ = 16
?proc1@@YAHHH@Z PROC                    ; proc1, COMDAT

; 3    : int volatile volvar=0;

    xor eax, eax

; 4    : 
; 5    :    int c=a;
; 6    :    if (b>a) 

    cmp edx, ecx
    cmovg   ecx, eax
    mov DWORD PTR volvar$[rsp], eax;<---what is 'mov DWORD PTR [8+rsp], eax'?

; 7    :        c=0;
; 8    :    return c;

    mov eax, ecx

; 9    : }

    ret 0
?proc1@@YAHHH@Z ENDP                    ; proc1
_TEXT   ENDS
END

注意符号volvar$等于 8,因此为 volatile 局部变量赋值生成的指令写入地址 [8+rsp]。RSP 没有被修改,所以应该指向返回地址。但是我对64位栈布局的理解是,返回地址上方不再有任何参数。相反,[8+rsp] 应该指向 CALLING FUNCTION 的 RCX 存储位置,这与我们当前的功能无关。这是否错误地覆盖了调用函数的堆栈?

这是编译器的错误吗?

1个回答

不,这不是编译器错误。

Windows 上的 x64 调用约定表示,即使被调用函数的参数少于 4 个,调用者也需要在堆栈上为 4 个寄存器参数分配空间。虽然从理论上讲,这些是为了被调用的函数将第一个四个参数(在寄存器 RCX、RDX、R8 和 R9 中传递)溢出到,但没有要求这样做。

事实上,即使被调用函数的参数少于 4 个,这 4 个堆栈位置实际上是由被调用函数拥有的,并且除了保存参数寄存器值之外,还可能被被调用函数用于其他目的。

有关详细信息,请参阅Microsoft 的文档

在您的示例中,proc1不需要溢出任何寄存器,因此编译器可以有效地将此分配的空间用于您的volvar局部变量。