diff --git a/libports/lib/mk/libc.mk b/libports/lib/mk/libc.mk
index ed744e325b..5f95a02f27 100644
--- a/libports/lib/mk/libc.mk
+++ b/libports/lib/mk/libc.mk
@@ -12,7 +12,8 @@ LIBS += timed_semaphore cxx
SRC_CC = atexit.cc dummies.cc rlimit.cc sysctl.cc readlink.cc munmap.cc \
issetugid.cc errno.cc gai_strerror.cc clock_gettime.cc \
gettimeofday.cc malloc.cc progname.cc fd_alloc.cc file_operations.cc \
- plugin.cc plugin_registry.cc select.cc exit.cc environ.cc nanosleep.cc
+ plugin.cc plugin_registry.cc select.cc exit.cc environ.cc nanosleep.cc \
+ libc_mem_alloc.cc
#
# Files from string library that are not included in libc-raw_string because
diff --git a/libports/src/lib/libc/exit.cc b/libports/src/lib/libc/exit.cc
index f3020ae867..0b67a65846 100644
--- a/libports/src/lib/libc/exit.cc
+++ b/libports/src/lib/libc/exit.cc
@@ -31,6 +31,9 @@ extern "C" {
void exit(int status)
{
+ if (status == 4) {
+ PDBG("PT: %p %p %p", __builtin_return_address(0), __builtin_return_address(1), __builtin_return_address(2));
+ }
if (__cleanup)
(*__cleanup)();
diff --git a/libports/src/lib/libc/file_operations.cc b/libports/src/lib/libc/file_operations.cc
index cf9c8ed262..e72c0e9e19 100644
--- a/libports/src/lib/libc/file_operations.cc
+++ b/libports/src/lib/libc/file_operations.cc
@@ -12,19 +12,25 @@
* under the terms of the GNU General Public License version 2.
*/
+/* Genode includes */
#include
#include
+/* Genode-specific libc interfaces */
#include
#include
#include
+/* libc includes */
#include
#include
#include
#include
#include
+/* libc-internal includes */
+#include "libc_mem_alloc.h"
+
using namespace Libc;
@@ -258,9 +264,8 @@ extern "C" void *mmap(void *addr, ::size_t length, int prot, int flags,
int libc_fd, ::off_t offset)
{
/* handle requests for anonymous memory */
- if (!addr && libc_fd == -1) {
- return malloc(length);
- }
+ if (!addr && libc_fd == -1)
+ return Libc::mem_alloc()->alloc(length, PAGE_SHIFT);
/* lookup plugin responsible for file descriptor */
File_descriptor *fd = libc_fd_to_fd(libc_fd, "mmap");
diff --git a/libports/src/lib/libc/libc_mem_alloc.cc b/libports/src/lib/libc/libc_mem_alloc.cc
new file mode 100644
index 0000000000..48a7fb20f8
--- /dev/null
+++ b/libports/src/lib/libc/libc_mem_alloc.cc
@@ -0,0 +1,231 @@
+/*
+ * \brief Allocator for anonymous memory used by libc
+ * \author Norman Feske
+ * \date 2012-05-18
+ *
+ * The libc uses a dedicated allocator instead of 'env()->heap()' because the
+ * 'Allocator' interface of 'env()->heap()' does not allow for aligned
+ * allocations. Some libc functions, however, rely on aligned memory. For
+ * example the blocks returned by mmap for allocating anonymous memory are
+ * assumed to be page-aligned.
+ *
+ * The code is largely based on 'base/include/base/heap.h' and
+ * 'base/src/base/heap/heap.cc'.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+
+/* local includes */
+#include "libc_mem_alloc.h"
+
+using namespace Genode;
+
+
+namespace Libc {
+
+ class Mem_alloc_impl : public Mem_alloc
+ {
+ private:
+
+ enum {
+ MIN_CHUNK_SIZE = 4*1024, /* in machine words */
+ MAX_CHUNK_SIZE = 1024*1024
+ };
+
+ class Dataspace : public List::Element
+ {
+ public:
+
+ Ram_dataspace_capability cap;
+ void *local_addr;
+
+ Dataspace(Ram_dataspace_capability c, void *a)
+ : cap(c), local_addr(a) {}
+
+ inline void * operator new(Genode::size_t, void* addr) {
+ return addr; }
+
+ inline void operator delete(void*) { }
+ };
+
+ class Dataspace_pool : public List
+ {
+ private:
+
+ Ram_session *_ram_session; /* ram session for backing store */
+ Rm_session *_rm_session; /* region manager */
+
+ public:
+
+ /**
+ * Constructor
+ */
+ Dataspace_pool(Ram_session *ram_session, Rm_session *rm_session):
+ _ram_session(ram_session), _rm_session(rm_session) { }
+
+ /**
+ * Destructor
+ */
+ ~Dataspace_pool();
+
+ /**
+ * Expand dataspace by specified size
+ *
+ * \param size number of bytes to add to the dataspace pool
+ * \param md_alloc allocator to expand. This allocator is also
+ * used for meta data allocation (only after
+ * being successfully expanded).
+ * \throw Rm_session::Invalid_dataspace,
+ * Rm_session::Region_conflict
+ * \return 0 on success or negative error code
+ */
+ int expand(size_t size, Range_allocator *alloc);
+
+ void reassign_resources(Ram_session *ram, Rm_session *rm) {
+ _ram_session = ram, _rm_session = rm; }
+ };
+
+ Lock _lock;
+ Dataspace_pool _ds_pool; /* list of dataspaces */
+ Allocator_avl _alloc; /* local allocator */
+ size_t _chunk_size;
+
+ /**
+ * Try to allocate block at our local allocator
+ *
+ * \return true on success
+ *
+ * This function is a utility used by 'alloc' to avoid
+ * code duplication.
+ */
+ bool _try_local_alloc(size_t size, void **out_addr);
+
+ public:
+
+ Mem_alloc_impl()
+ :
+ _ds_pool(env()->ram_session(), env()->rm_session()),
+ _alloc(0),
+ _chunk_size(MIN_CHUNK_SIZE)
+ { }
+
+ void *alloc(Genode::size_t size, Genode::size_t align_log2);
+ void free(void *ptr);
+ };
+}
+
+
+using namespace Genode;;
+
+
+Libc::Mem_alloc_impl::Dataspace_pool::~Dataspace_pool()
+{
+ /* free all ram_dataspaces */
+ for (Dataspace *ds; (ds = first()); ) {
+
+ /*
+ * read dataspace capability and modify _ds_list before detaching
+ * possible backing store for Dataspace - we rely on LIFO list
+ * manipulation here!
+ */
+
+ Ram_dataspace_capability ds_cap = ds->cap;
+
+ remove(ds);
+ delete ds;
+ _rm_session->detach(ds->local_addr);
+ _ram_session->free(ds_cap);
+ }
+}
+
+
+int Libc::Mem_alloc_impl::Dataspace_pool::expand(size_t size, Range_allocator *alloc)
+{
+ Ram_dataspace_capability new_ds_cap;
+ void *local_addr, *ds_addr = 0;
+
+ /* make new ram dataspace available at our local address space */
+ try {
+ new_ds_cap = _ram_session->alloc(size);
+ local_addr = _rm_session->attach(new_ds_cap);
+ } catch (Ram_session::Alloc_failed) {
+ return -2;
+ } catch (Rm_session::Attach_failed) {
+ _ram_session->free(new_ds_cap);
+ return -3;
+ }
+
+ /* add new local address range to our local allocator */
+ alloc->add_range((addr_t)local_addr, size);
+
+ /* now that we have new backing store, allocate Dataspace structure */
+ if (!alloc->alloc_aligned(sizeof(Dataspace), &ds_addr, 2)) {
+ PWRN("could not allocate meta data - this should never happen");
+ return -1;
+ }
+
+ /* add dataspace information to list of dataspaces */
+ Dataspace *ds = new (ds_addr) Dataspace(new_ds_cap, local_addr);
+ insert(ds);
+
+ return 0;
+}
+
+
+void *Libc::Mem_alloc_impl::alloc(size_t size, size_t align_log2)
+{
+ /* serialize access of heap functions */
+ Lock::Guard lock_guard(_lock);
+
+ /* try allocation at our local allocator */
+ void *out_addr = 0;
+ if (_alloc.alloc_aligned(size, &out_addr, align_log2))
+ return out_addr;
+
+ /*
+ * Calculate block size of needed backing store. The block must hold the
+ * requested 'size' and a new Dataspace structure if the allocation above
+ * failed. Finally, we align the size to a 4K page.
+ */
+ size_t request_size = size + 1024;
+
+ if (request_size < _chunk_size*sizeof(umword_t)) {
+ request_size = _chunk_size*sizeof(umword_t);
+
+ /*
+ * Exponentially increase chunk size with each allocated chunk until
+ * we hit 'MAX_CHUNK_SIZE'.
+ */
+ _chunk_size = min(2*_chunk_size, (size_t)MAX_CHUNK_SIZE);
+ }
+
+ if (_ds_pool.expand(align_addr(request_size, 12), &_alloc) < 0) {
+ PWRN("could not expand dataspace pool");
+ return 0;
+ }
+
+ /* allocate originally requested block */
+ return _alloc.alloc_aligned(size, &out_addr, align_log2) ? out_addr : 0;
+}
+
+
+void Libc::Mem_alloc_impl::free(void *addr)
+{
+ /* serialize access of heap functions */
+ Lock::Guard lock_guard(_lock);
+
+ /* forward request to our local allocator */
+ _alloc.free(addr);
+}
+
+
+Libc::Mem_alloc *Libc::mem_alloc()
+{
+ static Libc::Mem_alloc_impl inst;
+ return &inst;
+}
+
+
diff --git a/libports/src/lib/libc/libc_mem_alloc.h b/libports/src/lib/libc/libc_mem_alloc.h
new file mode 100644
index 0000000000..f8fc3c2fa9
--- /dev/null
+++ b/libports/src/lib/libc/libc_mem_alloc.h
@@ -0,0 +1,27 @@
+/*
+ * \brief Allocator for anonymous memory used by libc
+ * \author Norman Feske
+ * \date 2012-05-18
+ */
+
+#ifndef _LIBC_MEM_ALLOC_H_
+#define _LIBC_MEM_ALLOC_H_
+
+/* Genode includes */
+#include
+
+namespace Libc {
+
+ struct Mem_alloc
+ {
+ virtual void *alloc(Genode::size_t size, Genode::size_t align_log2) = 0;
+ virtual void free(void *ptr) = 0;
+ };
+
+ /**
+ * Return singleton instance of the memory allocator
+ */
+ Mem_alloc *mem_alloc();
+}
+
+#endif /* _LIBC_MEM_ALLOC_H_ */