YiFei
YiFei

Reputation: 1834

C++ destructor fail on custom buffer - How does compiler really generate destructors' code?

I'm using visual studio 2015 building some single-thread C++ project.

I have a very crude pool that the rest of project acquire memory and return back to it. It runs with no problem before return 0; in main. After that, I know there's some compiler generated code for exiting, in which it called some scalar deleting destructor of my own class (class foo below).

Here's some assembly code of the scalar deleting destructor (I hide the name of those class and namespace by aaaaa and foo):

000000013FC30FC0  mov         dword ptr [rsp+10h],edx  
000000013FC30FC4  mov         qword ptr [rsp+8],rcx  
000000013FC30FC9  push        rbp  
000000013FC30FCA  push        rdi  
000000013FC30FCB  sub         rsp,0E8h  
000000013FC30FD2  lea         rbp,[rsp+20h]  
000000013FC30FD7  mov         rdi,rsp  
000000013FC30FDA  mov         ecx,3Ah  
000000013FC30FDF  mov         eax,0CCCCCCCCh  
000000013FC30FE4  rep stos    dword ptr [rdi]  
000000013FC30FE6  mov         rcx,qword ptr [rsp+108h]  
000000013FC30FEE  mov         rcx,qword ptr [this]  
000000013FC30FF5  call        aaaaa::foo::~foo (013FC2381Bh)  
000000013FC30FFA  mov         eax,dword ptr [rbp+0E8h]  
000000013FC31000  and         eax,1  
000000013FC31003  test        eax,eax  
000000013FC31005  je          aaaaa::foo::`scalar deleting destructor'+58h (013FC31018h)  
000000013FC31007  mov         edx,30h  
000000013FC3100C  mov         rcx,qword ptr [this]  
000000013FC31013  call        operator delete (013FC2661Ah)  
000000013FC31018  mov         rax,qword ptr [this]  
000000013FC3101F  lea         rsp,[rbp+0C8h]  
000000013FC31026  pop         rdi  
000000013FC31027  pop         rbp  
000000013FC31028  ret

In debugging session I finally find out where the problem arose. In the normal running phase (i.e. in the major part of main function), 000000013FC30FFA mov eax,dword ptr [rbp+0E8h] would set eax to 0, and thus bypass those several lines of code after je, and most crucially bypass the call to operator delete.

But on the last hit of those lines, eax was set to 1 after the mov, and then execute the call to operator delete and at exactly that line it triggered a break point at operator delete.

So my questions are:

  1. Is scalar deleting destructor a unique concept in Visual Studio, that is, it's not seen in any other compilers? (This is perfectly answered in immibis' answer, and it's yes)
  2. In what case the above code would set eax to 1 after call to my custom destructor? Why is the compiler doing this, that is, what's the object operator delete originally to delete?
  3. Is the execution of operator delete a double deletion?
  4. Must I trace back to the change on rbp+0E8h to see how the error occurs? (Any other way to do?)

EDIT about the class: The foo class (which is my own class mentioned above) have all (raw) pointer members, which itself have all ownership of those pointers, i.e. responsible for deleting them. And the buffer_t below is the underlying pool or buffer which is basically a large region of raw memory partitioned providing interface of allocate and deallocate for single (i.e. not array or alike) object.

Minimal reproduce of the classes:

template <typename T>
struct other_t{
    // members, but none of them are pointer member
    other_t(){}// ctor with argument in reality
    static buffer_t buffer;
};

struct foo{
    other_t<some_T>* ptr;
    foo(const foo&)=delete;
    foo& operator=(const foo&)=delete;
    //~foo{ delete ptr; } //originally
    ~foo{ other_t<some_T>::buffer.deallocate(ptr); }; // actually
};

PS: I'm not familiar with assembly language and if the above is not enough for solving the problem, please comment below, thanks.

Upvotes: 0

Views: 104

Answers (1)

Is scalar deleting destructor a unique concept in Visual Studio, that is, it's not seen in any other compilers?

Yes.

In what case the above code would set eax to 1 after call to my custom destructor?

In the case where the low bit of the 4-byte value at rbp + 0x0E8 is 1.

What is your actual problem?

Upvotes: 1

Related Questions