Super Early Global Variables in C++

I've had a problem for a while now where the code I'm writing in C++ can potentially run before the global constructor list is invoked by libc. Let's imagine, for example, that you are building a memory allocator to replace malloc:

class Heap {
   Heap() { printf("Heap Constructed!\n"); }
   // ...
};

static Heap the_heap;

extern "C" void *malloc(size_t size) {
   return the_heap.malloc(size);
}

Libc will call malloc before the_heap is constructed, and as a result will likely be operating on uninitialized (likely zero) data. One possible approach to solving this is to do the following:

static Heap *the_heap = nullptr;

extern "C" void *malloc(size_t size) {
   if (the_heap == nullptr) the_heap = new Heap();
   return the_heap->malloc(size);
}

This should work! But we get this output:

$ ./a.out
Program received signal SIGSEGV, Segmentation fault

This is because we are calling new from within malloc and under the hood, new is just a wrapper around malloc, so we end up infinitely recursing and eventually overflow the stack. So we need to get the memory from somewhere else:

#include <new> // for placement new, below

static Heap *the_heap = nullptr;
static uint8_t heap_storage[sizeof(Heap)];

// We'll put this behind a function now.
static Heap &getHeap(void) {
  if (the_heap == nullptr) {
    the_heap = new (heap_storage)(); // Placement new!
  }
  return *the_heap;
}

We'll use placement new, which allows us to tell new, "Don't allocate your own memory, use mine instead". Instead, we place the object in the statically allocated heap_storage block, which is in .bss.

Obviously doing this each time for all your globals can be a pain, so I wrap this up into a struct:

template<typename T>
class LazyGlobal {
  T *m_instance; // no initializer!
  uint8_t m_storage[sizeof(T)];

 public:
  T &get() {
    if (m_instance == nullptr) {
      m_instance = new (m_storage) T();
    }
    return *m_instance;
  }

  T &operator*() {
    return get();
  }
  T *operator->() {
    return &get();
  }
};

Note that m_instance has no initializer, which is "okay" because this type is a PoD struct, and when a global variable takes this type, all of its fields are initialized to zero by virtue of being loaded from .bss.

That said, with that type we can now do this:

LazyGlobal<Heap> the_heap;

extern "C" void *malloc(size_t size) {
  return the_heap->malloc(size);
}

Its important that the LazyGlobal type is a plain-old-data, and the class it is managing has a trivial (or at least zero-argument) constructor. If the LazyGlobal struct isn't PoD, its possible the object could be constructed multiple times: once when you call get() before the global constructors are invoked, and once after.

Anyways, this class solved a longstanding problem I've had, so I thought I'd share :)