/prelude/tpreload.cpp
C++ | 91 lines | 73 code | 14 blank | 4 comment | 18 complexity | 888b1090fa9aefb17183e49cce5ee964 MD5 | raw file
1#include <stdlib.h> 2#include <string.h> 3#include <signal.h> 4#include <map> 5#include <cstdlib> 6#include <cstdio> 7#include <ext/malloc_allocator.h> 8#include <mcheck.h> 9#include "geordi.hpp" 10 11extern "C" 12{ 13 void abort() throw() // default version causes "SYS_gettid: Operation not permitted". 14 { 15 std::printf("%s%s", geordi::parsep, strsignal(SIGABRT)); 16 std::fclose(stdout); // Prevents things like tracked reporting leaks. 17 std::exit(0); 18 } 19} 20 21namespace 22{ 23 enum alloc_reg { ar_plain, ar_array, ar_deleted }; 24 25 typedef std::map<void *, alloc_reg, std::less<void *>, __gnu_cxx::malloc_allocator<std::pair<void * const, alloc_reg> > > Allocs; 26 27 Allocs & allocs() { static Allocs * const r = new (std::malloc(sizeof(Allocs))) Allocs; return *r; } 28 // We can't use an ordinary variable because when the construction of an earlier static-storage variable uses dynamic storage, it would cause us to operate on a not-yet-constructed container. We use malloc to avoid infinite mutual recursion with op_new. We don't ever destruct/deallocate r, because there is no way to ensure that that happens after all other destruction (which could involve dynamic storage) has finished. 29 30 std::new_handler get_new_handler() throw() 31 { 32 std::new_handler const r = std::set_new_handler(0); 33 std::set_new_handler(r); 34 return r; 35 } 36 37 void * op_new(std::size_t const s, alloc_reg const ar) throw() 38 { 39 std::new_handler const h = get_new_handler(); 40 41 for(;;) // See 18.5.1.1 paragraph 4 (in N2691). 42 { 43 if(void * const r = std::malloc(s)) 44 for(;;) 45 { 46 try { allocs()[r] = ar; return r; } 47 catch (std::bad_alloc const &) 48 { if(!h) { std::free(r); return 0; } h(); } 49 } 50 if(!h) return 0; 51 h(); 52 } 53 } 54 55 void del(void * const p, bool const a) 56 { 57 if(!p) return; 58 Allocs::iterator const i = allocs().find(p); 59 if(i == allocs().end()) 60 geordi::error()() << "tried to delete pointer not returned by previous matching allocation."; 61 if(i->second == ar_deleted) 62 geordi::error()() << "tried to delete already deleted pointer."; 63 if(a && i->second == ar_plain) 64 geordi::error()() << "tried to delete[] pointer returned by non-array operator new."; 65 if(!a && i->second == ar_array) 66 geordi::error()() << "tried to apply non-array operator delete to pointer returned by new[]."; 67 i->second = ar_deleted; 68 69 // We don't actually deallocate the memory, because then it could be reallocated, causing UB in { int * const p = new int; delete p; new int; delete p; } to go unnoticed if the second allocation returns the same address (which is not unlikely). 70 71 mprobe(p); 72 } 73} 74 75// Plain new/delete: 76 77void * operator new(size_t const i, std::nothrow_t const &) throw () 78{ return op_new(i, ar_plain); } 79void * operator new(size_t const i) throw (std::bad_alloc) 80{ if (void * const r = op_new(i, ar_plain)) return r; throw std::bad_alloc(); } 81void operator delete(void * const p, std::nothrow_t const &) throw () { operator delete(p); } 82void operator delete(void * const p) throw() { del(p, false); } 83 84// Array new[]/delete[]: 85 86void * operator new[](size_t const i, std::nothrow_t const &) throw () 87{ return op_new(i, ar_array); } 88void * operator new[](size_t const i) throw (std::bad_alloc) 89{ if (void * const r = op_new(i, ar_array)) return r; throw std::bad_alloc(); } 90void operator delete[](void * const p, std::nothrow_t const &) throw () { operator delete[](p); } 91void operator delete[](void * const p) throw() { del(p, true); }