libc: implement 'posix_memalign()'

Fixes #3915
This commit is contained in:
Christian Prochaska
2020-10-13 15:40:49 +02:00
committed by Christian Helmuth
parent 0aaed47652
commit 405955eaef
2 changed files with 37 additions and 22 deletions

View File

@ -493,6 +493,7 @@ popen T
posix2time T posix2time T
posix_fadvise T posix_fadvise T
posix_madvise T posix_madvise T
posix_memalign T
posix_spawn T posix_spawn T
posix_spawn_file_actions_addclose T posix_spawn_file_actions_addclose T
posix_spawn_file_actions_adddup2 T posix_spawn_file_actions_adddup2 T

View File

@ -32,6 +32,7 @@ extern "C" {
/* libc-internal includes */ /* libc-internal includes */
#include <internal/init.h> #include <internal/init.h>
#include <internal/clone_session.h> #include <internal/clone_session.h>
#include <internal/errno.h>
namespace Libc { namespace Libc {
@ -81,14 +82,16 @@ class Libc::Malloc
typedef Genode::addr_t addr_t; typedef Genode::addr_t addr_t;
enum { enum {
SLAB_START = 5, /* 32 bytes (log2) */ SLAB_START = 5, /* 32 bytes (log2) */
SLAB_STOP = 11, /* 2048 bytes (log2) */ SLAB_STOP = 11, /* 2048 bytes (log2) */
NUM_SLABS = (SLAB_STOP - SLAB_START) + 1 NUM_SLABS = (SLAB_STOP - SLAB_START) + 1,
DEFAULT_ALIGN = 16
}; };
struct Metadata struct Metadata
{ {
unsigned long long value; /* bits 63..5 size and 4..0 offset */ size_t size;
size_t offset;
/** /**
* Allocation metadata * Allocation metadata
@ -96,11 +99,8 @@ class Libc::Malloc
* \param size allocation size * \param size allocation size
* \param offset offset of pointer from allocation * \param offset offset of pointer from allocation
*/ */
Metadata(size_t size, unsigned offset) Metadata(size_t size, size_t offset)
: value(((unsigned long long)size << 5) | (offset & 0x1f)) { } : size(size), offset(offset) { }
size_t size() const { return value >> 5; }
unsigned offset() const { return value & 0x1f; }
}; };
/** /**
@ -108,15 +108,18 @@ class Libc::Malloc
* *
* We store the metadata of the allocation right before the pointer * We store the metadata of the allocation right before the pointer
* returned to the caller and can then retrieve the information when * returned to the caller and can then retrieve the information when
* freeing the block. Therefore, we add room for metadata and 16-byte * freeing the block. Therefore, we add room for metadata and
* alignment. * alignment.
* *
* Note, the worst case is an allocation that starts at * Note, the worst case is an allocation that starts at
* 16 byte - sizeof(Metadata) + 1 because it misses one byte of space * 'align' byte - sizeof(Metadata) + 1 because it misses one byte of space
* for the metadata and therefore increases the worst-case allocation * for the metadata and therefore increases the worst-case allocation
* by 15 bytes additionally to the metadata space. * by ('align' - 1) bytes additionally to the metadata space.
*/ */
static constexpr size_t _room() { return sizeof(Metadata) + 15; } static constexpr size_t _room(size_t align)
{
return sizeof(Metadata) + (align - 1);
}
Allocator &_backing_store; /* back-end allocator */ Allocator &_backing_store; /* back-end allocator */
@ -153,11 +156,11 @@ class Libc::Malloc
* Allocator interface * Allocator interface
*/ */
void * alloc(size_t size) void * alloc(size_t size, size_t align = DEFAULT_ALIGN)
{ {
Mutex::Guard guard(_mutex); Mutex::Guard guard(_mutex);
size_t const real_size = size + _room(); size_t const real_size = size + _room(align);
unsigned const msb = _slab_log2(real_size); unsigned const msb = _slab_log2(real_size);
void *alloc_addr = nullptr; void *alloc_addr = nullptr;
@ -172,9 +175,9 @@ class Libc::Malloc
/* correctly align the allocation address */ /* correctly align the allocation address */
Metadata * const aligned_addr = Metadata * const aligned_addr =
(Metadata *)(((addr_t)alloc_addr + _room()) & ~15UL); (Metadata *)(((addr_t)alloc_addr + _room(align)) & ~(align - 1));
unsigned const offset = (addr_t)aligned_addr - (addr_t)alloc_addr; size_t const offset = (addr_t)aligned_addr - (addr_t)alloc_addr;
*(aligned_addr - 1) = Metadata(real_size, offset); *(aligned_addr - 1) = Metadata(real_size, offset);
@ -183,8 +186,8 @@ class Libc::Malloc
void *realloc(void *ptr, size_t size) void *realloc(void *ptr, size_t size)
{ {
size_t const real_size = size + _room(); size_t const real_size = size + _room(DEFAULT_ALIGN);
size_t const old_real_size = ((Metadata *)ptr - 1)->size(); size_t const old_real_size = ((Metadata *)ptr - 1)->size;
/* do not reallocate if new size is less than the current size */ /* do not reallocate if new size is less than the current size */
if (real_size <= old_real_size) if (real_size <= old_real_size)
@ -195,7 +198,7 @@ class Libc::Malloc
if (new_addr) { if (new_addr) {
/* copy content from old block into new block */ /* copy content from old block into new block */
::memcpy(new_addr, ptr, old_real_size - _room()); ::memcpy(new_addr, ptr, old_real_size - _room(DEFAULT_ALIGN));
/* free old block */ /* free old block */
free(ptr); free(ptr);
@ -210,10 +213,10 @@ class Libc::Malloc
Metadata *md = (Metadata *)ptr - 1; Metadata *md = (Metadata *)ptr - 1;
size_t const real_size = md->size(); size_t const real_size = md->size;
unsigned const msb = _slab_log2(real_size); unsigned const msb = _slab_log2(real_size);
void *alloc_addr = (void *)((addr_t)ptr - md->offset()); void *alloc_addr = (void *)((addr_t)ptr - md->offset);
if (msb > SLAB_STOP) { if (msb > SLAB_STOP) {
_backing_store.free(alloc_addr, real_size); _backing_store.free(alloc_addr, real_size);
@ -264,6 +267,17 @@ extern "C" void *realloc(void *ptr, size_t size)
} }
int posix_memalign(void **memptr, size_t alignment, size_t size)
{
*memptr = mallocator->alloc(size, alignment);
if (!*memptr)
return Errno(ENOMEM);
return 0;
}
static Genode::Constructible<Malloc> &constructible_malloc() static Genode::Constructible<Malloc> &constructible_malloc()
{ {
return *unmanaged_singleton<Genode::Constructible<Malloc> >(); return *unmanaged_singleton<Genode::Constructible<Malloc> >();