I'm reversing the following smart pointee-like MSVC class Buffer : public Referencable with IDA / Hexrays:
struct Referencable { int m_refs; }; struct Buffer : Referencable { void* m_pData; }; This class apparently has no vftable, which I deduce from its (base) constructor not storing any vftable-like structure:
Buffer *__thiscall Buffer::ctor(Buffer *this) { Referencable::ctor(this); this->m_pData = NULL; return this; } Referencable *__thiscall Referencable::ctor(Referencable *this) { // <-- no vftable assignment here or anywhere --> this->m_refs = 0; return this; } When this object is being deleted, I see the following method:
Buffer *__thiscall Buffer::ddtor(Buffer *this, char flags) { Buffer::dtor(this); if ( (flags & 1) != 0 ) operator delete(this); return this; } void __thiscall Buffer::dtor(Buffer *this) { free(this->m_pData); Referencable::dtor(this); } void __thiscall Referencable::dtor(Referencable *this) { ; // nop } (I can assure that this is indeed the deletion method belonging to this class due to how the capturing smart pointer calls it)
According to igorsk's Reversing Microsoft Visual C++ Part II: Classes, Methods and RTTI article, Buffer::ddtor seems to be a deletion destructor, which however are only available to classes with virtual destructors:
- Deleting Destructors
When class has a virtual destructor, compiler generates a helper function - deleting destructor. Its purpose is to make sure that a proper _operator delete_ gets called when destructing a class. Pseudo-code for a deleting destructor looks like following:
virtual void * A::'scalar deleting destructor'(uint flags) { this->~A(); if (flags&1) A::operator delete(this); };
Thus my class seems to contradict another statement in that article, mentioning a virtual deletion destructor call which does not exist in my assembly (the deletion destructor above is called directly by the smart pointer logic):
If A's destructor is virtual, it's invoked virtually:
mov ecx, pA push 3 mov eax, [ecx] ;fetch vtable pointer // <-- what vftable? I have none! call [eax] ;call deleting destructor
Now I am a little confused.
- Does this class have a virtual destructor now or not?
- Is it possible for a deletion destructor to be generated even if I do not have a virtual destructor, and what are the requirements?
- Or is this what is always generated when I call
deleteon anything and I simply misunderstood the article? - If it helps clearing my confusion, what is the exact difference between a deletion destructor and virtual destructor anyway?
On a postscriptum note I know this assembly quite well otherwise and never noticed any kind of code optimizations (lucky me); I wonder how a vftable could've been optimized out anyway.