diff --git a/base/src/base/cxx/malloc_free.cc b/base/src/base/cxx/malloc_free.cc index d0e66a370..25c204a45 100644 --- a/base/src/base/cxx/malloc_free.cc +++ b/base/src/base/cxx/malloc_free.cc @@ -15,10 +15,40 @@ */ #include +#include #include using namespace Genode; + +/** + * Return heap partition for the private use within the cxx library. + * + * If we used the 'env()->heap()' with the C++ runtime, we would run into a + * deadlock when a 'Ram_session::Alloc_failed' exception is thrown from within + * 'Heap::alloc'. For creating the exception object, the C++ runtime calls + * '__cxa_allocate_exception', which, in turn, calls 'malloc'. If our 'malloc' + * implementation called 'env()->heap()->alloc()', we would end up in a + * recursive attempt to obtain the 'env()->heap()' lock. + * + * By using a dedicated heap instance for the cxx library, we avoid this + * circular condition. + */ +static Heap *cxx_heap() +{ + /* + * Exception frames are small (ca. 100 bytes). Hence, a small static + * backing store suffices for the cxx heap partition in the normal + * case. The 'env()->ram_session' is used only if the demand exceeds + * the capacity of the 'initial_block'. + */ + static char initial_block[512]; + static Heap heap(env()->ram_session(), env()->rm_session(), + Heap::UNLIMITED, initial_block, sizeof(initial_block)); + return &heap; +} + + typedef unsigned long Block_header; @@ -35,7 +65,7 @@ extern "C" void *malloc(unsigned size) */ unsigned long real_size = size + sizeof(Block_header); void *addr = 0; - if (!Genode::env()->heap()->alloc(real_size, &addr)) + if (!cxx_heap()->alloc(real_size, &addr)) return 0; *(Block_header *)addr = real_size; @@ -56,7 +86,7 @@ extern "C" void free(void *ptr) if (!ptr) return; unsigned long *addr = ((unsigned long *)ptr) - 1; - Genode::env()->heap()->free(addr, *addr); + cxx_heap()->free(addr, *addr); }