Home > Software engineering >  What does 'dword ptr[this]' mean in VS disassembly of C code?
What does 'dword ptr[this]' mean in VS disassembly of C code?

Time:11-12

Before calling a member function of an object, the address of the object will be moved to ECX.

Inside the function, ECX will be moved to dword ptr [this], what does this mean?

C Source

#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;
}

Disassembly

...
    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  

CodePudding user response:

MSVC's asm output itself (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]
  ...

This is just spilling the register arg to a local on the stack with that name. (The default options for the MSVC version I used on Godbolt, x86 MSVC 19.29.30136, don't include __CheckForDebuggerJustMyCode@4 or the runtime-check stack poisoning (rep stos) in Do(), but the usage of this is still there.)

Amusingly, the push ecx it uses (as a micro-optimization) instead of sub esp, 4 to reserve stack space already stored ECX, making the mov store redundant.

(AFAIK, no compilers actually do use push to both initialize and make space for locals, but it would be an optimization for cases like this: What C/C compiler can use push pop instructions for creating local variables, instead of just increasing esp once?. It's just using the push for its effect on ESP, not caring what it stores, even if you enabled optimization. In a function where it did still need to spill it, instead of keeping it in memory.)


Your disassembler apparently folds the frame-pointer (EBP ) into what its defining as a this symbol / macro, making it more confusing if you don't look around at other lines to find out how it defines that text macro or whatever it is.

What disassembler are you using? The one built-in to Visual Studio's debugger?

I guess that would make sense that it's using C local var names this way, even though it looks super weird to people familiar with asm. (Because only static storage is addressable with a mode like [symbol] not involving any registers.)

  • Related