#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/sysmacros.h>
#include "umem_base.h"
#include "misc.h"
typedef struct malloc_data {
uint32_t malloc_size;
uint32_t malloc_stat;
} malloc_data_t;
#ifndef _x86
#pragma weak malloc = umem_malloc
#pragma weak free = umem_malloc_free
#endif
void *
umem_malloc(size_t size_arg)
{
#ifdef _LP64
uint32_t high_size = 0;
#endif
size_t size;
malloc_data_t *ret;
size = size_arg + sizeof (malloc_data_t);
#ifdef _LP64
if (size > UMEM_SECOND_ALIGN) {
size += sizeof (malloc_data_t);
high_size = (size >> 32);
}
#endif
if (size < size_arg) {
errno = ENOMEM;
return (NULL);
}
ret = (malloc_data_t *)_umem_alloc(size, UMEM_DEFAULT);
if (ret == NULL) {
if (size <= UMEM_MAXBUF)
errno = EAGAIN;
else
errno = ENOMEM;
return (NULL);
#ifdef _LP64
} else if (high_size > 0) {
uint32_t low_size = (uint32_t)size;
ret->malloc_size = high_size;
ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_MAGIC, high_size);
ret++;
ret->malloc_size = low_size;
ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_OVERSIZE_MAGIC,
low_size);
ret++;
} else if (size > UMEM_SECOND_ALIGN) {
uint32_t low_size = (uint32_t)size;
ret++;
ret->malloc_size = low_size;
ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_SECOND_MAGIC,
low_size);
ret++;
#endif
} else {
ret->malloc_size = size;
ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_MAGIC, size);
ret++;
}
return ((void *)ret);
}
void *
calloc(size_t nelem, size_t elsize)
{
size_t size = nelem * elsize;
void *retval;
if (nelem > 0 && elsize > 0 && size/nelem != elsize) {
errno = ENOMEM;
return (NULL);
}
retval = malloc(size);
if (retval == NULL)
return (NULL);
(void) memset(retval, 0, size);
return (retval);
}
void *
memalign(size_t align, size_t size_arg)
{
size_t size;
uintptr_t phase;
void *buf;
malloc_data_t *ret;
size_t overhead;
if (size_arg == 0 || align == 0 || (align & (align - 1)) != 0) {
errno = EINVAL;
return (NULL);
}
if (align <= UMEM_ALIGN ||
(align <= UMEM_SECOND_ALIGN && size_arg >= UMEM_SECOND_ALIGN))
return (malloc(size_arg));
#ifdef _LP64
overhead = 2 * sizeof (malloc_data_t);
#else
overhead = sizeof (malloc_data_t);
#endif
ASSERT(overhead <= align);
size = size_arg + overhead;
phase = align - overhead;
if (umem_memalign_arena == NULL && umem_init() == 0) {
errno = ENOMEM;
return (NULL);
}
if (size < size_arg) {
errno = ENOMEM;
return (NULL);
}
buf = vmem_xalloc(umem_memalign_arena, size, align, phase,
0, NULL, NULL, VM_NOSLEEP);
if (buf == NULL) {
if ((size_arg + align) <= UMEM_MAXBUF)
errno = EAGAIN;
else
errno = ENOMEM;
return (NULL);
}
ret = (malloc_data_t *)buf;
{
uint32_t low_size = (uint32_t)size;
#ifdef _LP64
uint32_t high_size = (uint32_t)(size >> 32);
ret->malloc_size = high_size;
ret->malloc_stat = UMEM_MALLOC_ENCODE(MEMALIGN_MAGIC,
high_size);
ret++;
#endif
ret->malloc_size = low_size;
ret->malloc_stat = UMEM_MALLOC_ENCODE(MEMALIGN_MAGIC, low_size);
ret++;
}
ASSERT(P2PHASE((uintptr_t)ret, align) == 0);
ASSERT((void *)((uintptr_t)ret - overhead) == buf);
return ((void *)ret);
}
void *
valloc(size_t size)
{
return (memalign(pagesize, size));
}
static int
process_free(void *buf_arg,
int do_free,
size_t *data_size_arg)
{
malloc_data_t *buf;
void *base;
size_t size;
size_t data_size;
const char *message;
int old_errno = errno;
buf = (malloc_data_t *)buf_arg;
buf--;
size = buf->malloc_size;
switch (UMEM_MALLOC_DECODE(buf->malloc_stat, size)) {
case MALLOC_MAGIC:
base = (void *)buf;
data_size = size - sizeof (malloc_data_t);
if (do_free)
buf->malloc_stat = UMEM_FREE_PATTERN_32;
goto process_malloc;
#ifdef _LP64
case MALLOC_SECOND_MAGIC:
base = (void *)(buf - 1);
data_size = size - 2 * sizeof (malloc_data_t);
if (do_free)
buf->malloc_stat = UMEM_FREE_PATTERN_32;
goto process_malloc;
case MALLOC_OVERSIZE_MAGIC: {
size_t high_size;
buf--;
high_size = buf->malloc_size;
if (UMEM_MALLOC_DECODE(buf->malloc_stat, high_size) !=
MALLOC_MAGIC) {
message = "invalid or corrupted buffer";
break;
}
size += high_size << 32;
base = (void *)buf;
data_size = size - 2 * sizeof (malloc_data_t);
if (do_free) {
buf->malloc_stat = UMEM_FREE_PATTERN_32;
(buf + 1)->malloc_stat = UMEM_FREE_PATTERN_32;
}
goto process_malloc;
}
#endif
case MEMALIGN_MAGIC: {
size_t overhead = sizeof (malloc_data_t);
#ifdef _LP64
size_t high_size;
overhead += sizeof (malloc_data_t);
buf--;
high_size = buf->malloc_size;
if (UMEM_MALLOC_DECODE(buf->malloc_stat, high_size) !=
MEMALIGN_MAGIC) {
message = "invalid or corrupted buffer";
break;
}
size += high_size << 32;
if (do_free)
(buf + 1)->malloc_stat = UMEM_FREE_PATTERN_32;
#endif
base = (void *)buf;
data_size = size - overhead;
if (do_free)
buf->malloc_stat = UMEM_FREE_PATTERN_32;
goto process_memalign;
}
default:
if (buf->malloc_stat == UMEM_FREE_PATTERN_32)
message = "double-free or invalid buffer";
else
message = "invalid or corrupted buffer";
break;
}
umem_err_recoverable("%s(%p): %s\n",
do_free? "free" : "realloc", buf_arg, message);
errno = old_errno;
return (0);
process_malloc:
if (do_free)
_umem_free(base, size);
else
*data_size_arg = data_size;
errno = old_errno;
return (1);
process_memalign:
if (do_free)
vmem_xfree(umem_memalign_arena, base, size);
else
*data_size_arg = data_size;
errno = old_errno;
return (1);
}
void
umem_malloc_free(void *buf)
{
if (buf == NULL)
return;
(void) process_free(buf, 1, NULL);
}
void *
realloc(void *buf_arg, size_t newsize)
{
size_t oldsize;
void *buf;
if (buf_arg == NULL)
return (malloc(newsize));
if (newsize == 0) {
free(buf_arg);
return (NULL);
}
if (process_free(buf_arg, 0, &oldsize) == 0) {
errno = EINVAL;
return (NULL);
}
if (newsize == oldsize)
return (buf_arg);
buf = malloc(newsize);
if (buf == NULL)
return (NULL);
(void) memcpy(buf, buf_arg, MIN(newsize, oldsize));
free(buf_arg);
return (buf);
}