// BUG DEMO for gcc 4.1.2 (and 4.1.1) for 32-bit x86 // // If operator delete() is over-ridden in a base class, then // if the implementation of delete() stores into the storage used // by the object, the store seems to be removed by the compiler // (maybe because the compiler thinks stores to a deleted object // are dead code? Or may be the casts of void * cause trouble // with the C99 aliasing rules??) // // Only occurs with g++ -O3 // Occurs using native gcc on 32-bit x86. // Does NOT occur with 64-bit x86. // Does NOT occur with gcc 3.2.3 // // The following program is a skeletal memory manager which maintains // a separate free-list for each size object. Deleted objects are // kept in a singly-linked list. operator delete() casts the void * // pointer to be a pointer to an internal struct in order to store // a "next" pointer in the first word of the storage block. The // store through this cast pointer seems to be lost. // // TO DEMO THE PROBLEM: // g++ -O3 thisfile.cpp && ./a.out // (prints "ERROR" and then segfaults if the bug is there) // // -Jim Avera ([EMAIL PROTECTED]) // Please copy [EMAIL PROTECTED] [EMAIL PROTECTED] on all correspondence // #include <stdio.h> #include <stdlib.h>
const size_t MaxSize = 100; class MyHeap { public: MyHeap(); ~MyHeap() {} void * Malloc(const size_t bytes_needed); void Free(void * ptr_to_free, size_t how_big_it_is); void Dump(void); struct Node { Node *next; }; private: Node *Heads[MaxSize]; }; MyHeap::MyHeap() { printf("MyHeap constructed\n"); for (int i=0; i < MaxSize; ++i) { Heads[i] = 0; } } void MyHeap::Dump(void) { printf("Dump of free-list data:\n"); int num_empties = 0; for (int i=0; i < MaxSize; ++i) { if (Heads[i] == 0) { ++num_empties; } else { printf(" Heads[%d] = %p\n", i, Heads[i]); Node * next_node = Heads[i]->next; if (next_node != 0) { // For this test program, there should be exactly one item printf(" ERROR: Expecting only one free item on list\n"); while (next_node != 0) { printf(" next = %p\n", next_node); next_node = next_node->next; } exit(1); // usually segfaults before getting here } } } printf("(%d lists are empty)\n", num_empties); } void * MyHeap::Malloc(const size_t bytes_needed) { if (bytes_needed < sizeof(Node) || bytes_needed > MaxSize) { printf("MyHeap::Malloc - object too small or too large\n"); exit(1); } if (Heads[bytes_needed] == 0) { // Pre-charge the free list with more storage // (just one object for this test) Node * newnode = static_cast<Node *>( malloc(bytes_needed) ); newnode->next = 0; Heads[bytes_needed] = newnode; } // Detach the first from the list Node * node = Heads[bytes_needed]; Heads[bytes_needed] = node->next; return node; } void MyHeap::Free(void * ptr_to_free, size_t how_big_it_is) { // Put object on the free list Node * node = static_cast<Node *>(ptr_to_free); node->next = Heads[how_big_it_is]; Heads[how_big_it_is] = node; } // The memory-manager object MyHeap my_pool; class base { public: void * operator new(size_t bytes_needed) { void * raw_mem = my_pool.Malloc(bytes_needed); return(raw_mem); } void operator delete(void * ptr_to_free, size_t how_big_it_is) { my_pool.Free(ptr_to_free, how_big_it_is); } }; class middle : public base { public: virtual ~middle() { } }; class derived : public middle { public: derived() { i = 11111111; j = 22222222; k = 33333333; } virtual ~derived() { } int i,j,k; }; int main() { setbuf(stdout,NULL); setbuf(stderr,NULL); // disable buffering my_pool.Dump(); printf("new...\n"); derived *d = new derived; printf("Got %p, sizeof=%u, raw dump follows:\n", d, (unsigned)sizeof(*d)); unsigned * up = reinterpret_cast<unsigned *>(d); const size_t size_in_unsigneds = sizeof(*d)/sizeof(unsigned); for (int ix=0; ix < size_in_unsigneds; ++ix) { printf(" 0x%08x = %d\n", up[ix], up[ix]); } my_pool.Dump(); printf("delete...\n"); delete d; my_pool.Dump(); printf("The bug was not detected\n"); return 0; } Environment: System: Linux xba24 2.4.21-37.ELsmp #1 SMP Wed Sep 7 13:28:55 EDT 2005 i686 i686 i386 GNU/Linux Architecture: i686 host: i686-pc-linux-gnu build: i686-pc-linux-gnu target: i686-pc-linux-gnu configured with: /grid/sfi/ct_src/gcc-v4.1.2/platforms/linux86/matrix_bootstrap_000/gcc-4.1.2/configure --prefix=/grid/common/pkgs/gcc/v4.1.2 --mandir=/grid/common/pkgs/gcc/v4.1.2/man --infodir=/grid/common/pkgs/gcc/v4.1.2/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --with-gnu-as --with-as=/grid/common/pkgs/binutils/v2.17/bin/as --with-gnu-ld --with-ld=/grid/common/pkgs/binutils/v2.17/bin/ld --disable-libgcj --with-local-prefix=/grid/common/pkgs/gcc/v4.1.2 --enable-clocale=gnu --with-gmp=/grid/common/pkgs/gmp/v4.1.4 --enable-languages=c,c++,objc,fortran How-To-Repeat: Compile the supplied demo program with g++ -O3 and run it -- Summary: gcc412 elides code in operator delete() override method Product: gcc Version: unknown Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ AssignedTo: unassigned at gcc dot gnu dot org ReportedBy: jima at cadence dot com GCC build triplet: i686-pc-linux-gnu GCC host triplet: i686-pc-linux-gnu GCC target triplet: i686-pc-linux-gnu http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31289