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