/prelude/tpreload.cpp

http://github.com/Eelis/geordi · 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. extern "C"
  11. {
  12. void abort() throw() // default version causes "SYS_gettid: Operation not permitted".
  13. {
  14. std::printf("%s%s", geordi::parsep, strsignal(SIGABRT));
  15. std::fclose(stdout); // Prevents things like tracked reporting leaks.
  16. std::exit(0);
  17. }
  18. }
  19. namespace
  20. {
  21. enum alloc_reg { ar_plain, ar_array, ar_deleted };
  22. typedef std::map<void *, alloc_reg, std::less<void *>, __gnu_cxx::malloc_allocator<std::pair<void * const, alloc_reg> > > Allocs;
  23. Allocs & allocs() { static Allocs * const r = new (std::malloc(sizeof(Allocs))) Allocs; return *r; }
  24. // 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.
  25. std::new_handler get_new_handler() throw()
  26. {
  27. std::new_handler const r = std::set_new_handler(0);
  28. std::set_new_handler(r);
  29. return r;
  30. }
  31. void * op_new(std::size_t const s, alloc_reg const ar) throw()
  32. {
  33. std::new_handler const h = get_new_handler();
  34. for(;;) // See 18.5.1.1 paragraph 4 (in N2691).
  35. {
  36. if(void * const r = std::malloc(s))
  37. for(;;)
  38. {
  39. try { allocs()[r] = ar; return r; }
  40. catch (std::bad_alloc const &)
  41. { if(!h) { std::free(r); return 0; } h(); }
  42. }
  43. if(!h) return 0;
  44. h();
  45. }
  46. }
  47. void del(void * const p, bool const a)
  48. {
  49. if(!p) return;
  50. Allocs::iterator const i = allocs().find(p);
  51. if(i == allocs().end())
  52. geordi::error()() << "tried to delete pointer not returned by previous matching allocation.";
  53. if(i->second == ar_deleted)
  54. geordi::error()() << "tried to delete already deleted pointer.";
  55. if(a && i->second == ar_plain)
  56. geordi::error()() << "tried to delete[] pointer returned by non-array operator new.";
  57. if(!a && i->second == ar_array)
  58. geordi::error()() << "tried to apply non-array operator delete to pointer returned by new[].";
  59. i->second = ar_deleted;
  60. // 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).
  61. mprobe(p);
  62. }
  63. }
  64. // Plain new/delete:
  65. void * operator new(size_t const i, std::nothrow_t const &) throw ()
  66. { return op_new(i, ar_plain); }
  67. void * operator new(size_t const i) throw (std::bad_alloc)
  68. { if (void * const r = op_new(i, ar_plain)) return r; throw std::bad_alloc(); }
  69. void operator delete(void * const p, std::nothrow_t const &) throw () { operator delete(p); }
  70. void operator delete(void * const p) throw() { del(p, false); }
  71. // Array new[]/delete[]:
  72. void * operator new[](size_t const i, std::nothrow_t const &) throw ()
  73. { return op_new(i, ar_array); }
  74. void * operator new[](size_t const i) throw (std::bad_alloc)
  75. { if (void * const r = op_new(i, ar_array)) return r; throw std::bad_alloc(); }
  76. void operator delete[](void * const p, std::nothrow_t const &) throw () { operator delete[](p); }
  77. void operator delete[](void * const p) throw() { del(p, true); }