[cfe-dev] comment on r 153990

tamlin at algonet.se tamlin at algonet.se
Wed Apr 4 00:11:02 PDT 2012


 // comment on r  153990
 // /lib/CodeGen/MicrosoftCXXABI.cpp
 // Hopefully the following can help clear up unknowns.

 // The following is true for 32-bit. While expectation is that 64-bit
 // would use 8 bytes for count, I have not verified it.
 //
 // MSVC doesn't use "cookies" (an array count at p-4) when the type T
 // is trivial (f.ex. POD) - the overhead isn't needed.
 //
 // If T has has a destructor, the actual size of memory allocated is
 // n_bytes+4, as follows:
 //   int     : no_of_array_elements.
 //   n_bytes : the memory returned to the caller
 //
 // When time comes to delete[], if T is trivial it simply deallocates
 // using operator[] (f.ex. T::operator[](void*,size_t) if declared).
 // This is comparable to plain malloc/free of e.g. an array of char's.
 //
 // If non-trivial (i.e. a user-supplied or compiler generated d'tor
 // exists) it instead does the following to destruct and delete:
 //   p->T::vector_deleting_destructor(1|2); // 1 = dealloc, 2 = vector
 //
 // T::vector_deleting_destructor is a compiler-generated member
 // function of the form:
 // void /* __thiscall */ T::vector_deleting_destructor(int flags) {
 //   if (flags & 2) {
 //     vector_destructor_iterator(this, sizeof T,
 //                                ((int*)this)[-1], &T::~T);
 //     if (flags & 1)
 //       delete[] ((T**)this)[-1]; // note [1]
 //     return ((void**)this)[-1];
 //   } else {
 //     p->~T();
 //     if (flags & 1)
 //       delete this; // NOTE: Not array version.
 //     return (void*)this;
 //   }
 // }
 //
 // [1] If no T::operator delete[] function exists, this becomes a call
 // to operator delete(void*).
 // If (as in the following example) it exists, it's called as:
 //  T::operator delete[](p, sizeof T);
 //
 // Worth noting is:
 //  - The vector_deleting_destructor returns a pointer to the _actual_
 //    memory. It is not known under what circumstances it could be
 //    called without bit 0 set for its flags argument.
 //  - The signature of the vector_destructor_iterator, where
 //    elemcount is the most curious (it's signed!).
 //   void __stdcall vector_destructor_iterator(
 //     void* p,
 //     size_t elemsize,
 //     int elemcount,
 //     void (__thiscall*)(void*));

 // The result using a Microsoft 32-bit compiler able to compile the
 // following is expected to be:
 // A: 100, 1
 // B: 104, 1
 // SEGV: obvious. :-)

 #include <stdio.h>
 #include <stdlib.h>

 struct A {
   char x;
   void *operator new[](size_t blocksize) {
     printf("new: %u\n", blocksize);
     return malloc(blocksize);
   }
   void operator delete(void *p) { }
   void operator delete[](void *p, size_t elementsize) {
     printf("del: %u\n", elementsize);
     free(p);
   }
 };
 struct B : public A { ~B() { /*printf("B d'tor\n");*/ } };
 void test_A() { A *p = new A[100]; delete[] p; }
 void test_B() { B *p = new B[100]; delete[] p; }
 void test_SEGV() { A *p = new B[100]; delete[] p; }
 int main() {
   test_A();
   test_B();
   printf("The app will now crash in free() due to being given a ptr\n"
          "4 bytes into the actually allocated block of memory...\n");
   test_SEGV();
   return 0;
 }




More information about the cfe-dev mailing list