'dword ptr[this]' 在 C++ 代码的 VS 反汇编中意味着什么?
What does 'dword ptr[this]' mean in VS disassembly of C++ code?
在调用对象的成员函数之前,对象的地址会被移动到ECX。
函数内部,ECX会被移动到dword ptr [this]
,什么意思?
C++ 源代码
#include <iostream>
class CAdd
{
public:
CAdd(int x, int y) : _x(x), _y(y) {}
int Do() { return _x + _y; }
private:
int _x;
int _y;
};
int main()
{
CAdd ca(1, 2);
int n = ca.Do();
std::cout << n << std::endl;
}
反汇编
...
CAdd ca(1, 2);
00A87B4F push 2
00A87B51 push 1
00A87B53 lea ecx,[ca] ; the instance address
00A87B56 call CAdd::CAdd (0A6BA32h)
int Do() { return _x + _y; }
00A7FFB0 push ebp
00A7FFB1 mov ebp,esp
00A7FFB3 sub esp,0CCh
00A7FFB9 push ebx
00A7FFBA push esi
00A7FFBB push edi
00A7FFBC push ecx
00A7FFBD lea edi,[ebp-0Ch]
00A7FFC0 mov ecx,3
00A7FFC5 mov eax,0CCCCCCCCh
00A7FFCA rep stos dword ptr es:[edi]
00A7FFCC pop ecx
00A7FFCD mov dword ptr [this],ecx ; ========= QUESTION HERE!!! =========
00A7FFD0 mov ecx,offset _CC7F790E_main@cpp (0BC51F2h)
00A7FFD5 call @__CheckForDebuggerJustMyCode@4 (0A6AC36h)
00A7FFDA mov eax,dword ptr [this] ; ========= AND HERE!!! =========
00A7FFDD mov eax,dword ptr [eax]
00A7FFDF mov ecx,dword ptr [this]
00A7FFE2 add eax,dword ptr [ecx+4]
00A7FFE5 pop edi
00A7FFE6 pop esi
00A7FFE7 pop ebx
00A7FFE8 add esp,0CCh
00A7FFEE cmp ebp,esp
00A7FFF0 call __RTC_CheckEsp (0A69561h)
00A7FFF5 mov esp,ebp
00A7FFF7 pop ebp
00A7FFF8 ret
MSVC 的 asm 输出本身 (https://godbolt.org/z/h44rW3Mxh) uses _this$[ebp]
with _this$ = -4
, in a debug build like this which wastes instructions storing/reloading incoming register args.
_this$ = -4
int CAdd::Do(void) PROC ; CAdd::Do, COMDAT
push ebp
mov ebp, esp
push ecx ; dummy push instead of sub to reserve 4 bytes
mov DWORD PTR _this$[ebp], ecx
mov eax, DWORD PTR _this$[ebp]
...
这只是将寄存器 arg 溢出到堆栈上具有该名称的局部变量。 (我在 Godbolt 上使用的 MSVC 版本的默认选项,x86 MSVC 19.29.30136,不包括 __CheckForDebuggerJustMyCode@4
或 Do()
中的运行时检查堆栈中毒(rep stos
),但是this
的用法仍然存在。)
有趣的是,push ecx
它使用 () 而不是 sub esp, 4
来保留堆栈 space 已经存储的 ECX,使 mov
存储冗余.
(AFAIK,实际上没有编译器使用 push
为本地人初始化和生成 space,但这将是对这种情况的优化:。它只是使用 push
对 ESP 的影响,而不关心它存储的内容,即使您启用了优化。在一个函数中它仍然需要溢出它,而不是将它保存在内存中。)
您的反汇编程序显然将帧指针 (EBP +
) 折叠成它定义的 this
符号/宏, 如果您不必四处查看其他行来了解它是如何定义该文本宏或它是什么的。
你用的是什么反汇编器? Visual Studio 的调试器内置的那个?
我猜它以这种方式使用 C 局部变量名是有道理的,尽管对于熟悉 asm 的人来说它看起来超级奇怪。 (因为只有静态存储可以使用 [symbol]
不涉及任何寄存器的模式寻址。)
在调用对象的成员函数之前,对象的地址会被移动到ECX。
函数内部,ECX会被移动到dword ptr [this]
,什么意思?
C++ 源代码
#include <iostream>
class CAdd
{
public:
CAdd(int x, int y) : _x(x), _y(y) {}
int Do() { return _x + _y; }
private:
int _x;
int _y;
};
int main()
{
CAdd ca(1, 2);
int n = ca.Do();
std::cout << n << std::endl;
}
反汇编
...
CAdd ca(1, 2);
00A87B4F push 2
00A87B51 push 1
00A87B53 lea ecx,[ca] ; the instance address
00A87B56 call CAdd::CAdd (0A6BA32h)
int Do() { return _x + _y; }
00A7FFB0 push ebp
00A7FFB1 mov ebp,esp
00A7FFB3 sub esp,0CCh
00A7FFB9 push ebx
00A7FFBA push esi
00A7FFBB push edi
00A7FFBC push ecx
00A7FFBD lea edi,[ebp-0Ch]
00A7FFC0 mov ecx,3
00A7FFC5 mov eax,0CCCCCCCCh
00A7FFCA rep stos dword ptr es:[edi]
00A7FFCC pop ecx
00A7FFCD mov dword ptr [this],ecx ; ========= QUESTION HERE!!! =========
00A7FFD0 mov ecx,offset _CC7F790E_main@cpp (0BC51F2h)
00A7FFD5 call @__CheckForDebuggerJustMyCode@4 (0A6AC36h)
00A7FFDA mov eax,dword ptr [this] ; ========= AND HERE!!! =========
00A7FFDD mov eax,dword ptr [eax]
00A7FFDF mov ecx,dword ptr [this]
00A7FFE2 add eax,dword ptr [ecx+4]
00A7FFE5 pop edi
00A7FFE6 pop esi
00A7FFE7 pop ebx
00A7FFE8 add esp,0CCh
00A7FFEE cmp ebp,esp
00A7FFF0 call __RTC_CheckEsp (0A69561h)
00A7FFF5 mov esp,ebp
00A7FFF7 pop ebp
00A7FFF8 ret
MSVC 的 asm 输出本身 (https://godbolt.org/z/h44rW3Mxh) uses _this$[ebp]
with _this$ = -4
, in a debug build like this which wastes instructions storing/reloading incoming register args.
_this$ = -4
int CAdd::Do(void) PROC ; CAdd::Do, COMDAT
push ebp
mov ebp, esp
push ecx ; dummy push instead of sub to reserve 4 bytes
mov DWORD PTR _this$[ebp], ecx
mov eax, DWORD PTR _this$[ebp]
...
这只是将寄存器 arg 溢出到堆栈上具有该名称的局部变量。 (我在 Godbolt 上使用的 MSVC 版本的默认选项,x86 MSVC 19.29.30136,不包括 __CheckForDebuggerJustMyCode@4
或 Do()
中的运行时检查堆栈中毒(rep stos
),但是this
的用法仍然存在。)
有趣的是,push ecx
它使用 (sub esp, 4
来保留堆栈 space 已经存储的 ECX,使 mov
存储冗余.
(AFAIK,实际上没有编译器使用 push
为本地人初始化和生成 space,但这将是对这种情况的优化:push
对 ESP 的影响,而不关心它存储的内容,即使您启用了优化。在一个函数中它仍然需要溢出它,而不是将它保存在内存中。)
您的反汇编程序显然将帧指针 (EBP +
) 折叠成它定义的 this
符号/宏, 如果您不必四处查看其他行来了解它是如何定义该文本宏或它是什么的。
你用的是什么反汇编器? Visual Studio 的调试器内置的那个?
我猜它以这种方式使用 C 局部变量名是有道理的,尽管对于熟悉 asm 的人来说它看起来超级奇怪。 (因为只有静态存储可以使用 [symbol]
不涉及任何寄存器的模式寻址。)