7c48de6cc4
Change-Id: Ibc1b7f7cd45ad7295285e59c6ce55888266fece8
2170 lines
81 KiB
C
2170 lines
81 KiB
C
|
|
#ifdef _FILE_OFFSET_BITS
|
|
#undef _FILE_OFFSET_BITS
|
|
#endif
|
|
#define _FILE_OFFSET_BITS 64
|
|
|
|
#include <magic_mem.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <magic_asr.h>
|
|
|
|
#ifdef __MINIX
|
|
#define util_time_tsc_read_ns(x) 0
|
|
#define util_strhash(x, y) 0
|
|
#define util_stacktrace_hash() 0
|
|
#define util_stacktrace_print_custom(x)
|
|
#define util_stacktrace_hash_skip(x) 0
|
|
#else
|
|
#include <common/util/stacktrace.h>
|
|
#include <common/util/time.h>
|
|
#include <common/util/string.h>
|
|
#endif
|
|
|
|
#define DEBUG MAGIC_DEBUG_SET(0)
|
|
#define DEBUG_TYPE_SIZE_MISMATCH MAGIC_DEBUG_SET(0)
|
|
|
|
#if DEBUG
|
|
#define MAGIC_MEM_PRINTF _magic_printf
|
|
#else
|
|
#define MAGIC_MEM_PRINTF magic_null_printf
|
|
#endif
|
|
|
|
/* CPU frequency (used for timestamp logging) */
|
|
PUBLIC double magic_cycles_per_ns = 0;
|
|
|
|
/*
|
|
* External callbacks.
|
|
*/
|
|
PUBLIC magic_mem_heap_alloc_cb_t magic_mem_heap_alloc_cb = NULL;
|
|
PUBLIC magic_mem_create_dsentry_cb_t magic_mem_create_dsentry_cb = NULL;
|
|
PUBLIC magic_mem_heap_free_cb_t magic_mem_heap_free_cb = NULL;
|
|
|
|
PUBLIC short magic_mem_create_dsentry_site_id = 0;
|
|
PUBLIC THREAD_LOCAL short magic_mem_wrapper_active = 0;
|
|
PUBLIC THREAD_LOCAL short magic_mempool_mgmt_active_level = 0;
|
|
PUBLIC THREAD_LOCAL short magic_mempool_ids[MAGIC_MEMPOOL_MAX_FUNC_RECURSIONS];
|
|
|
|
char* const MAGIC_MEMPOOL_NAME_UNKNOWN = "_magic_mempool_unknown#";
|
|
char* const MAGIC_MEMPOOL_NAME_DETACHED = "_magic_mempool_detached#";
|
|
|
|
__attribute__((weak)) int magic_mempool_allow_reset = 1;
|
|
__attribute__((weak)) int magic_mempool_allow_reuse = 1;
|
|
__attribute__((weak)) int magic_mempool_allow_external_alloc = 0;
|
|
|
|
#define MAGIC_MEM_FAILED ((void*) -1)
|
|
|
|
#ifndef SHM_REMAP
|
|
#define SHM_REMAP 0
|
|
#endif
|
|
|
|
EXTERN char **environ;
|
|
|
|
PRIVATE magic_dsentry_cb_t magic_destroy_dsentry_ext_cb = NULL;
|
|
|
|
/* Magic real mem function definitions. */
|
|
PUBLIC void *(*magic_real_malloc)(size_t size) = &malloc;
|
|
PUBLIC void *(*magic_real_calloc)(size_t nmemb, size_t size) = &calloc;
|
|
PUBLIC void (*magic_real_free)(void *ptr) = &free;
|
|
PUBLIC void *(*magic_real_realloc)(void *ptr, size_t size) = &realloc;
|
|
|
|
PUBLIC int (*magic_real_posix_memalign)(void **memptr, size_t alignment, size_t size) = &posix_memalign;
|
|
|
|
#ifndef __MINIX
|
|
PUBLIC void *(*magic_real_valloc)(size_t size) = &valloc;
|
|
PUBLIC void *(*magic_real_memalign)(size_t boundary, size_t size) = &memalign;
|
|
#endif
|
|
|
|
PUBLIC void *(*magic_real_mmap)(void *start, size_t length, int prot, int flags,
|
|
int fd, off_t offset) = &mmap;
|
|
PUBLIC int (*magic_real_munmap)(void *start, size_t length) = &munmap;
|
|
|
|
PUBLIC int (*magic_real_brk)(void *addr) = &brk;
|
|
PUBLIC void *(*magic_real_sbrk)(intptr_t increment) = &sbrk;
|
|
|
|
#ifndef __MINIX
|
|
PUBLIC void *(*magic_real_shmat)(int shmid, const void *shmaddr, int shmflg) = &shmat;
|
|
PUBLIC int (*magic_real_shmdt)(const void *shmaddr) = &shmdt;
|
|
|
|
PUBLIC void *(*magic_real_mmap64)(void *start, size_t length, int prot, int flags,
|
|
int fd, off_t pgoffset) = &mmap64;
|
|
#else
|
|
PUBLIC void *(*magic_real_vm_map_cacheblock)(dev_t dev, off_t dev_offset,
|
|
ino_t ino, off_t ino_offset, u32_t *flags, int blocksize) = &vm_map_cacheblock;
|
|
#endif
|
|
|
|
/* Use magic_real* functions in the rest of the file. */
|
|
#include <magic_real_mem.h>
|
|
|
|
/* Macros for memory usage logging. */
|
|
#if MAGIC_MEM_USAGE_OUTPUT_CTL
|
|
#define MAGIC_MEM_DEBUG_PREFIX "MEM_USAGE: "
|
|
#define TIMESTAMP_STR "%llu"
|
|
#define TIMESTAMP_ARG (util_time_tsc_read_ns(magic_cycles_per_ns))
|
|
#define MAGIC_MEM_DEBUG_EVENT(EVENT, FORMAT, ...) \
|
|
_magic_printf(MAGIC_MEM_DEBUG_PREFIX TIMESTAMP_STR " " #EVENT " " FORMAT "\n", TIMESTAMP_ARG, __VA_ARGS__)
|
|
#define MAGIC_MEM_DEBUG_EVENT_1(event, ptr) MAGIC_MEM_DEBUG_EVENT(event, "%u", (unsigned) ptr)
|
|
#define MAGIC_MEM_DEBUG_EVENT_2(event, ptr, type) MAGIC_MEM_DEBUG_EVENT(event, "%u %lu", (unsigned) ptr, type)
|
|
#define MAGIC_MEM_DEBUG_EVENT_3(event, ptr, type, size) MAGIC_MEM_DEBUG_EVENT(event, "%u %lu %d", (unsigned) ptr, type, size)
|
|
|
|
#if (MAGIC_MEM_USAGE_OUTPUT_CTL == 1)
|
|
/* use the hash of the name (extended with line number & file name) as dynamic type */
|
|
#define MAGIC_MEM_GET_DTYPE() util_strhash(0, name)
|
|
#elif (MAGIC_MEM_USAGE_OUTPUT_CTL == 2)
|
|
/* use the hash of the stacktrace as a dynamic type */
|
|
#define MAGIC_MEM_GET_DTYPE() util_stacktrace_hash()
|
|
#endif
|
|
#define MAGIC_MEM_DEBUG_ALLOC(ptr, size) MAGIC_MEM_DEBUG_EVENT_3(alloc, ptr, (MAGIC_MEMPOOL_MGMT_IS_ACTIVE() ? MAGIC_MEMPOOL_GET_DTYPE() : MAGIC_MEM_GET_DTYPE()), size)
|
|
#define MAGIC_MEM_DEBUG_FREE(ptr) MAGIC_MEM_DEBUG_EVENT_1(dealloc, ptr)
|
|
#define MAGIC_MEM_DEBUG_RESET(ptr) MAGIC_MEM_DEBUG_EVENT_1(reset, ptr)
|
|
#define MAGIC_MEM_DEBUG_REUSE(ptr, type) MAGIC_MEM_DEBUG_EVENT_2(reuse, ptr, type)
|
|
#else
|
|
#define MAGIC_MEM_GET_DTYPE() 0
|
|
#define MAGIC_MEM_DEBUG_ALLOC(ptr, size)
|
|
#define MAGIC_MEM_DEBUG_FREE(ptr)
|
|
#define MAGIC_MEM_DEBUG_RESET(ptr)
|
|
#define MAGIC_MEM_DEBUG_REUSE(ptr, type)
|
|
#endif
|
|
|
|
/*===========================================================================*
|
|
* magic_mempool_alloc_id *
|
|
*===========================================================================*/
|
|
MAGIC_MACRO_FUNC short magic_mempool_alloc_id()
|
|
{
|
|
short i, id = -1;
|
|
|
|
MAGIC_MPDESC_LOCK();
|
|
|
|
/* Find a free slot. */
|
|
for(i = 0; i < MAGIC_MAX_MEMPOOLS; i++) {
|
|
if(MAGIC_MPDESC_IS_FREE(&_magic_mpdescs[i])) {
|
|
MAGIC_MPDESC_ALLOC(&_magic_mpdescs[i]);
|
|
id = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
MAGIC_MPDESC_UNLOCK();
|
|
|
|
assert((id > 0) && (id <= MAGIC_MAX_MEMPOOLS) && "Ran out of memory pool descriptors!");
|
|
|
|
return id;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_mempool_create_begin *
|
|
*===========================================================================*/
|
|
MAGIC_HOOK void magic_mempool_create_begin(__MDEBUG_ARGS__)
|
|
{
|
|
MAGIC_MEMPOOL_MGMT_SET_ACTIVE();
|
|
assert(MAGIC_MEMPOOL_MGMT_IS_ACTIVE());
|
|
MAGIC_MEMPOOL_SET_ID(magic_mempool_alloc_id());
|
|
MAGIC_MEMPOOL_SET_DTYPE(MAGIC_MEM_GET_DTYPE());
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_mempool_create_end *
|
|
*===========================================================================*/
|
|
MAGIC_HOOK void magic_mempool_create_end(void* addr, int indirection)
|
|
{
|
|
void* pool;
|
|
pool = (indirection && addr) ? *((void**)addr) : addr;
|
|
assert(pool && "Cannot have a NULL pool pointer.");
|
|
_magic_mpdescs[MAGIC_MEMPOOL_GET_ID() - 1].addr = pool;
|
|
magic_mempool_mgmt_end();
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_mempool_lookup_by_addr *
|
|
*===========================================================================*/
|
|
MAGIC_MACRO_FUNC short magic_mempool_lookup_by_addr(void* addr)
|
|
{
|
|
short i, id = MAGIC_MEMPOOL_ID_UNKNOWN;
|
|
|
|
if (addr) {
|
|
for(i = 0; i < MAGIC_MAX_MEMPOOLS; i++) {
|
|
if(_magic_mpdescs[i].addr == addr) {
|
|
id = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_mempool_reset *
|
|
*===========================================================================*/
|
|
MAGIC_MACRO_FUNC void magic_mempool_reset(char* mempool_name, int reset_name)
|
|
{
|
|
struct _magic_dsentry *prev_dsentry, *dsentry, *block_dsentry;
|
|
struct _magic_sentry* sentry;
|
|
|
|
MAGIC_DSENTRY_LOCK();
|
|
MAGIC_DSENTRY_MEMPOOL_ALIVE_ITER(_magic_first_mempool_dsentry, prev_dsentry, dsentry, sentry,
|
|
if (sentry->name == mempool_name) {
|
|
block_dsentry = MAGIC_DSENTRY_NEXT_MEMBLOCK(dsentry);
|
|
if (block_dsentry != NULL) {
|
|
struct _magic_dsentry *tmp_block_dsentry = MAGIC_CAS(&MAGIC_DSENTRY_NEXT_MEMBLOCK(dsentry), block_dsentry, NULL);
|
|
assert(tmp_block_dsentry == block_dsentry && "New blocks have been allocated from a reseted mempool!");
|
|
}
|
|
if (reset_name) {
|
|
char *tmp_name = MAGIC_CAS(&sentry->name, mempool_name, MAGIC_MEMPOOL_NAME_UNKNOWN);
|
|
assert(tmp_name == mempool_name && "The name of the mempool has changed while being reseted!");
|
|
}
|
|
MAGIC_MEM_DEBUG_RESET((char*)dsentry);
|
|
}
|
|
);
|
|
|
|
MAGIC_DSENTRY_UNLOCK();
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_mempool_destroy_begin *
|
|
*===========================================================================*/
|
|
MAGIC_HOOK void magic_mempool_destroy_begin(void* addr, int memory_reuse)
|
|
{
|
|
magic_mempool_mgmt_begin(addr);
|
|
if (addr && memory_reuse) {
|
|
assert(MAGIC_MEMPOOL_ID_IS_SET() && "Cannot destroy a pool with an unknown id.");
|
|
magic_mempool_reset(MAGIC_MEMPOOL_GET_NAME(), TRUE);
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_mempool_destroy_end *
|
|
*===========================================================================*/
|
|
MAGIC_HOOK void magic_mempool_destroy_end()
|
|
{
|
|
MAGIC_MPDESC_LOCK();
|
|
|
|
MAGIC_MPDESC_FREE(&_magic_mpdescs[MAGIC_MEMPOOL_GET_ID() - 1]);
|
|
|
|
MAGIC_MPDESC_UNLOCK();
|
|
|
|
magic_mempool_mgmt_end();
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_mempool_mgmt_begin *
|
|
*===========================================================================*/
|
|
MAGIC_HOOK void magic_mempool_mgmt_begin(void* addr)
|
|
{
|
|
short id;
|
|
|
|
MAGIC_MEMPOOL_MGMT_SET_ACTIVE();
|
|
assert(MAGIC_MEMPOOL_MGMT_IS_ACTIVE());
|
|
|
|
id = magic_mempool_lookup_by_addr(addr);
|
|
/* For some reason, this mempool has not been registered yet, reserve a new slot in the mempool array. */
|
|
if (addr && (id == MAGIC_MEMPOOL_ID_UNKNOWN)) {
|
|
id = magic_mempool_alloc_id();
|
|
_magic_mpdescs[id - 1].addr = addr;
|
|
}
|
|
MAGIC_MEMPOOL_SET_ID(id);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_mempool_mgmt_end *
|
|
*===========================================================================*/
|
|
MAGIC_HOOK void magic_mempool_mgmt_end()
|
|
{
|
|
MAGIC_MEMPOOL_SET_ID(MAGIC_MEMPOOL_ID_UNKNOWN);
|
|
assert(MAGIC_MEMPOOL_MGMT_IS_ACTIVE());
|
|
MAGIC_MEMPOOL_MGMT_UNSET_ACTIVE();
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_mempool_reset_begin *
|
|
*===========================================================================*/
|
|
MAGIC_HOOK void magic_mempool_reset_begin(void* addr)
|
|
{
|
|
magic_mempool_mgmt_begin(addr);
|
|
/* skip reset when it has been disabled by the application. */
|
|
if (magic_mempool_allow_reset) {
|
|
if (addr != NULL) {
|
|
assert(MAGIC_MEMPOOL_ID_IS_SET() && "Cannot reset a pool with an unknown id.");
|
|
magic_mempool_reset(MAGIC_MEMPOOL_GET_NAME(), TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_mempool_dsentry_set_name *
|
|
*===========================================================================*/
|
|
MAGIC_MACRO_FUNC void magic_mempool_dsentry_set_name(struct _magic_dsentry* dsentry, char* name)
|
|
{
|
|
char *old_name, *ret;
|
|
|
|
if ((name == MAGIC_MEMPOOL_NAME_UNKNOWN) || (name == MAGIC_MEMPOOL_NAME_DETACHED)) {
|
|
do {
|
|
old_name = MAGIC_DSENTRY_TO_SENTRY(dsentry)->name;
|
|
} while (MAGIC_CAS(&MAGIC_DSENTRY_TO_SENTRY(dsentry)->name, old_name, name) != old_name && old_name != name);
|
|
} else {
|
|
old_name = MAGIC_DSENTRY_TO_SENTRY(dsentry)->name;
|
|
if (old_name != name) {
|
|
if (!strncmp(old_name, MAGIC_MEMPOOL_NAME_PREFIX, strlen(MAGIC_MEMPOOL_NAME_PREFIX))) {
|
|
assert(((old_name == MAGIC_MEMPOOL_NAME_UNKNOWN) || (old_name == MAGIC_MEMPOOL_NAME_DETACHED))
|
|
&& "Cannot overwrite an already existing valid memory pool name!");
|
|
}
|
|
ret = MAGIC_CAS(&MAGIC_DSENTRY_TO_SENTRY(dsentry)->name, old_name, name);
|
|
assert((ret == old_name || ret == name) && "Cannot overwrite an already existing valid memory pool name!");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_mempool_dsentry_update *
|
|
*===========================================================================*/
|
|
MAGIC_MACRO_FUNC void magic_mempool_dsentry_update(struct _magic_dsentry* dsentry, char* name)
|
|
{
|
|
struct _magic_sentry* sentry = MAGIC_DSENTRY_TO_SENTRY(dsentry);
|
|
struct _magic_dsentry* next_mempool_dsentry;
|
|
int flags;
|
|
/* set the magic state mempool flag atomically */
|
|
do {
|
|
flags = sentry->flags;
|
|
} while (MAGIC_CAS(&sentry->flags, flags, flags | MAGIC_STATE_MEMPOOL) != flags && !(flags & MAGIC_STATE_MEMPOOL));
|
|
magic_mempool_dsentry_set_name(dsentry, name);
|
|
/* the thread that updates the id adds the dsentry to the mempool dsentry list */
|
|
if (!(flags & MAGIC_STATE_MEMPOOL)) {
|
|
/* Add the new dsentry before the first dsentry atomically. */
|
|
do {
|
|
next_mempool_dsentry = _magic_first_mempool_dsentry;
|
|
MAGIC_DSENTRY_NEXT_MEMPOOL(dsentry) = next_mempool_dsentry;
|
|
} while(MAGIC_CAS(&_magic_first_mempool_dsentry, next_mempool_dsentry, dsentry) != next_mempool_dsentry);
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* mempool_block_alloc_template *
|
|
*===========================================================================*/
|
|
MAGIC_FUNC void *mempool_block_alloc_template(void* addr, size_t size)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_mempool_block_alloc_template *
|
|
*===========================================================================*/
|
|
PUBLIC void *magic_mempool_block_alloc_template(__MA_ARGS__ void* addr, size_t size)
|
|
{
|
|
void *ptr, *data_ptr;
|
|
struct _magic_sentry* mempool_sentry;
|
|
struct _magic_dsentry* mempool_dsentry, *next_block_dsentry;
|
|
|
|
magic_mempool_mgmt_begin(addr);
|
|
/* don't set the memory wrapper flag, this function is supposed to allocate memory from a pool
|
|
and not using the standard allocators. it might also call other memory pool management functions */
|
|
if(size > 0) {
|
|
/* this call should be replaced with a call to a "real" block allocation function, when generating custom wrappers */
|
|
ptr = mempool_block_alloc_template(addr, MAGIC_SIZE_TO_REAL(size) + magic_asr_get_padding_size(MAGIC_STATE_HEAP));
|
|
MAGIC_MEM_PRINTF("%s: ptr = malloc(size) <-> 0x%08x = malloc(%d)\n", __FUNCTION__,(unsigned) ptr, MAGIC_SIZE_TO_REAL(size));
|
|
data_ptr = magic_alloc(__MA_VALUES__ ptr, size, MAGIC_STATE_HEAP | MAGIC_STATE_MEMBLOCK);
|
|
if(data_ptr == MAGIC_MEM_FAILED) {
|
|
/* cannot free individual blocks inside the pool */
|
|
data_ptr = NULL;
|
|
errno = ENOMEM;
|
|
} else if (ptr) {
|
|
/* lookup the pool buffer dsentry from which this block was allocated */
|
|
mempool_sentry = magic_mempool_sentry_lookup_by_range(ptr, NULL);
|
|
if (!mempool_sentry && magic_mempool_allow_external_alloc) {
|
|
mempool_sentry = magic_sentry_lookup_by_range(ptr, NULL);
|
|
if (mempool_sentry) {
|
|
magic_mempool_dsentry_update(MAGIC_DSENTRY_FROM_SENTRY(mempool_sentry), MAGIC_MEMPOOL_GET_NAME());
|
|
}
|
|
}
|
|
|
|
assert(mempool_sentry && "XXX Mempool dsentry not found for this memblock dsentry: memory not allocated from a memory pool management function?");
|
|
mempool_dsentry = MAGIC_DSENTRY_FROM_SENTRY(mempool_sentry);
|
|
|
|
/* Reuse of buffers across pools - propagate the new pool name */
|
|
if (MAGIC_MEMPOOL_ID_IS_SET() && (mempool_sentry->name != MAGIC_MEMPOOL_GET_NAME())) {
|
|
assert(magic_mempool_allow_reuse && "Pool memory reuse is disabled!");
|
|
magic_mempool_dsentry_set_name(mempool_dsentry, MAGIC_MEMPOOL_GET_NAME());
|
|
}
|
|
MAGIC_DSENTRY_TO_SENTRY(MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr)))->name = mempool_sentry->name;
|
|
|
|
/* Add the new dsentry before the first block dsentry chained to the memory pool dsentry, atomically.
|
|
The list should be circular - the last block dsentry (first one added) points to the memory pool dsentry. */
|
|
do {
|
|
next_block_dsentry = MAGIC_DSENTRY_NEXT_MEMBLOCK(mempool_dsentry);
|
|
MAGIC_DSENTRY_NEXT_MEMBLOCK(MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))) = next_block_dsentry ? next_block_dsentry : mempool_dsentry;
|
|
}while(MAGIC_CAS(&(MAGIC_DSENTRY_NEXT_MEMBLOCK(mempool_dsentry)), next_block_dsentry, MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))) != next_block_dsentry);
|
|
|
|
/* First write to this pool buffer, potential reuse */
|
|
if (next_block_dsentry == NULL) {
|
|
MAGIC_MEM_DEBUG_REUSE((char*)mempool_dsentry, MAGIC_MEMPOOL_GET_DTYPE());
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* Some applications return a valid pointer even if size is 0... */
|
|
data_ptr = mempool_block_alloc_template(addr, size);
|
|
}
|
|
magic_mempool_mgmt_end();
|
|
|
|
return data_ptr;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_create_dsentry *
|
|
*===========================================================================*/
|
|
PUBLIC int magic_create_dsentry(struct _magic_dsentry *dsentry,
|
|
void *data_ptr, struct _magic_type *type, size_t size, int flags,
|
|
char *name, char *parent_name)
|
|
{
|
|
/* This function does not require any dsentry locking. */
|
|
struct _magic_sentry *sentry = MAGIC_DSENTRY_TO_SENTRY(dsentry);
|
|
struct _magic_dsentry *next_dsentry, *next_mempool_dsentry;
|
|
size_t type_size;
|
|
int is_varsized = 0;
|
|
int num_vsa_elements = 0;
|
|
|
|
struct _magic_dsentry *saved_next = dsentry->next;
|
|
int save_linkage = ((flags & MAGIC_STATE_HEAP) && _magic_vars->fake_malloc);
|
|
|
|
MAGIC_MEM_PRINTF("Dsentry created from stacktrace:\n");
|
|
if (MAGIC_MEM_PRINTF != magic_null_printf) {
|
|
MAGIC_MEM_WRAPPER_BEGIN();
|
|
util_stacktrace_print_custom(MAGIC_MEM_PRINTF);
|
|
MAGIC_MEM_WRAPPER_END();
|
|
}
|
|
|
|
if (!type) {
|
|
type = MAGIC_VOID_TYPE;
|
|
}
|
|
type_size = type->size;
|
|
assert(size > 0);
|
|
|
|
memcpy(dsentry, &magic_default_dsentry, sizeof(struct _magic_dsentry));
|
|
|
|
/* Catch variable-sized struct allocation. */
|
|
if (magic_type_alloc_needs_varsized_array(type, size, &num_vsa_elements)) {
|
|
is_varsized = 1;
|
|
}
|
|
|
|
if (size % type_size != 0 && !is_varsized) {
|
|
/* This should only happen for uncaught variable-sized struct allocations. */
|
|
#if DEBUG_TYPE_SIZE_MISMATCH
|
|
_magic_printf("magic_create_dsentry: type <-> size mismatch, reverting to void type: size=%d, type=", size);
|
|
MAGIC_TYPE_PRINT(type, MAGIC_EXPAND_TYPE_STR);
|
|
_magic_printf("\n");
|
|
#endif
|
|
type = MAGIC_VOID_TYPE;
|
|
type_size = type->size;
|
|
flags |= MAGIC_STATE_TYPE_SIZE_MISMATCH;
|
|
}
|
|
|
|
if (size == type_size && !is_varsized) {
|
|
sentry->type = type;
|
|
}
|
|
else {
|
|
struct _magic_type *array_type = &(dsentry->type);
|
|
MAGIC_TYPE_ARRAY_CREATE_FROM_SIZE(array_type, type,
|
|
MAGIC_DSENTRY_TO_TYPE_ARR(dsentry), size, num_vsa_elements);
|
|
array_type->id = MAGIC_FAA(&_magic_types_next_id, 1);
|
|
assert(_magic_types_next_id < MAGIC_ID_MAX);
|
|
sentry->type = array_type;
|
|
}
|
|
|
|
sentry->flags |= flags;
|
|
sentry->address = data_ptr;
|
|
|
|
if (name) {
|
|
sentry->name = name;
|
|
}
|
|
if (parent_name) {
|
|
dsentry->parent_name = parent_name;
|
|
}
|
|
sentry->id = MAGIC_FAA(&_magic_sentries_next_id, 1);
|
|
assert(_magic_sentries_next_id < MAGIC_ID_MAX);
|
|
|
|
/*
|
|
* TODO: Also add per-callsite index to handle the following:
|
|
* for (;;) { p = malloc(); }
|
|
*/
|
|
if (magic_mem_create_dsentry_site_id) {
|
|
MAGIC_MEM_WRAPPER_BEGIN();
|
|
/*
|
|
* XXX: This is so damn ugly, but we don't want to include
|
|
* any magic_* functions in the stacktrace hash.
|
|
* This should probably be done in a much more elegant manner.
|
|
*/
|
|
dsentry->site_id = util_stacktrace_hash_skip((char *)"("MAGIC_PREFIX_STR);
|
|
MAGIC_MEM_WRAPPER_END();
|
|
}
|
|
|
|
if (save_linkage) {
|
|
dsentry->next = saved_next;
|
|
return 0;
|
|
}
|
|
|
|
/* Add the new dsentry before the first dsentry atomically.
|
|
* Skip memblock dsentries to make pool reset/destruction faster.
|
|
*/
|
|
if (!MAGIC_STATE_FLAG(sentry, MAGIC_STATE_MEMBLOCK)) {
|
|
do {
|
|
next_dsentry = _magic_first_dsentry;
|
|
MAGIC_DSENTRY_NEXT(dsentry) = next_dsentry;
|
|
} while(MAGIC_CAS(&_magic_first_dsentry, next_dsentry, dsentry) != next_dsentry);
|
|
}
|
|
|
|
#if MAGIC_DSENTRY_ALLOW_PREV
|
|
next_dsentry = MAGIC_DSENTRY_NEXT(dsentry);
|
|
if (next_dsentry) {
|
|
MAGIC_DSENTRY_PREV(next_dsentry) = dsentry;
|
|
}
|
|
MAGIC_DSENTRY_PREV(dsentry) = NULL;
|
|
#endif
|
|
|
|
if (MAGIC_STATE_FLAG(sentry, MAGIC_STATE_MEMPOOL)) {
|
|
/* Add the new dsentry before the first mempool dsentry atomically. */
|
|
do {
|
|
next_mempool_dsentry = _magic_first_mempool_dsentry;
|
|
MAGIC_DSENTRY_NEXT_MEMPOOL(dsentry) = next_mempool_dsentry;
|
|
} while(MAGIC_CAS(&_magic_first_mempool_dsentry, next_mempool_dsentry, dsentry) != next_mempool_dsentry);
|
|
}
|
|
magic_update_dsentry_ranges = 1;
|
|
|
|
if (magic_mem_create_dsentry_cb)
|
|
magic_mem_create_dsentry_cb(dsentry);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_create_obdsentry *
|
|
*===========================================================================*/
|
|
PUBLIC struct _magic_obdsentry* magic_create_obdsentry(void *data_ptr,
|
|
struct _magic_type *type, size_t size, int flags,
|
|
const char *name, char *parent_name)
|
|
{
|
|
struct _magic_obdsentry *obdsentry = NULL;
|
|
int i, ret;
|
|
|
|
/* Check name. */
|
|
if(!name || !strcmp(name, "")) {
|
|
return NULL;
|
|
}
|
|
else if(strlen(name) >= MAGIC_MAX_OBDSENTRY_NAME_LEN) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Check parent name. */
|
|
if(!parent_name || !strcmp(parent_name, "")) {
|
|
parent_name = MAGIC_OBDSENTRY_DEFAULT_PARENT_NAME;
|
|
}
|
|
if(strlen(parent_name) >= MAGIC_MAX_OBDSENTRY_PARENT_NAME_LEN) {
|
|
return NULL;
|
|
}
|
|
|
|
MAGIC_MEM_WRAPPER_LBEGIN();
|
|
|
|
/* Find a free slot. */
|
|
for(i=0;i<MAGIC_MAX_OBDSENTRIES;i++) {
|
|
if(MAGIC_OBDSENTRY_IS_FREE(&_magic_obdsentries[i])) {
|
|
obdsentry = &_magic_obdsentries[i];
|
|
break;
|
|
}
|
|
}
|
|
if(!obdsentry) {
|
|
MAGIC_MEM_WRAPPER_LEND();
|
|
return NULL;
|
|
}
|
|
|
|
/* Create the dsentry. */
|
|
strcpy(obdsentry->name, name);
|
|
strcpy(obdsentry->parent_name, parent_name);
|
|
flags |= MAGIC_STATE_OUT_OF_BAND;
|
|
ret = magic_create_dsentry(MAGIC_OBDSENTRY_TO_DSENTRY(obdsentry), data_ptr, type,
|
|
size, flags, obdsentry->name, obdsentry->parent_name);
|
|
|
|
MAGIC_MEM_WRAPPER_LEND();
|
|
|
|
if(ret < 0) {
|
|
return NULL;
|
|
}
|
|
assert(!MAGIC_OBDSENTRY_IS_FREE(obdsentry));
|
|
|
|
return obdsentry;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_update_dsentry_state *
|
|
*===========================================================================*/
|
|
PUBLIC int magic_update_dsentry_state(struct _magic_dsentry *dsentry,
|
|
unsigned long state)
|
|
{
|
|
int ret = 0;
|
|
unsigned long old_state;
|
|
unsigned long num_dead_dsentries;
|
|
unsigned long size, size_dead_dsentries;
|
|
size = MAGIC_DSENTRY_TO_SENTRY(dsentry)->type->size;
|
|
|
|
switch(state) {
|
|
case MAGIC_DSENTRY_MSTATE_FREED:
|
|
old_state = MAGIC_CAS(&dsentry->magic_state, MAGIC_DSENTRY_MSTATE_DEAD,
|
|
MAGIC_DSENTRY_MSTATE_FREED);
|
|
if(old_state != MAGIC_DSENTRY_MSTATE_DEAD) {
|
|
ret = MAGIC_EBADMSTATE;
|
|
break;
|
|
}
|
|
if (!MAGIC_STATE_FLAG(MAGIC_DSENTRY_TO_SENTRY(dsentry), MAGIC_STATE_MEMBLOCK)) {
|
|
num_dead_dsentries = MAGIC_FAS(&magic_num_dead_dsentries, 1) - 1;
|
|
size_dead_dsentries = MAGIC_FAS(&magic_size_dead_dsentries, size) - size;
|
|
MAGIC_MEM_PRINTF("magic_update_dsentry_state: --magic_num_dead_dsentries (num=%d, size=%d)\n", num_dead_dsentries, size_dead_dsentries);
|
|
}
|
|
break;
|
|
case MAGIC_DSENTRY_MSTATE_DEAD:
|
|
old_state = MAGIC_CAS(&dsentry->magic_state, MAGIC_DSENTRY_MSTATE_ALIVE,
|
|
MAGIC_DSENTRY_MSTATE_DEAD);
|
|
if(old_state != MAGIC_DSENTRY_MSTATE_ALIVE) {
|
|
ret = (old_state == MAGIC_DSENTRY_MSTATE_DEAD
|
|
|| old_state == MAGIC_DSENTRY_MSTATE_FREED)
|
|
? MAGIC_EBADMSTATE : MAGIC_EBADENT;
|
|
break;
|
|
}
|
|
MAGIC_DSENTRY_TO_SENTRY(dsentry)->id = MAGIC_FAA(&_magic_sentries_next_id, 1);
|
|
assert(_magic_sentries_next_id < MAGIC_ID_MAX);
|
|
if (!MAGIC_STATE_FLAG(MAGIC_DSENTRY_TO_SENTRY(dsentry), MAGIC_STATE_MEMBLOCK)) {
|
|
num_dead_dsentries = MAGIC_FAA(&magic_num_dead_dsentries, 1) + 1;
|
|
size_dead_dsentries = MAGIC_FAA(&magic_size_dead_dsentries, size) + size;
|
|
MAGIC_MEM_PRINTF("magic_update_dsentry_state: ++magic_num_dead_dsentries (num=%d, size=%d)\n", num_dead_dsentries, size_dead_dsentries);
|
|
if(!magic_ignore_dead_dsentries
|
|
&& MAGIC_DEAD_DSENTRIES_NEED_FREEING()) {
|
|
magic_free_dead_dsentries();
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
ret = MAGIC_EINVAL;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_free_dsentry *
|
|
*===========================================================================*/
|
|
PRIVATE int magic_free_dsentry(struct _magic_dsentry *dsentry)
|
|
{
|
|
int ret = 0;
|
|
struct _magic_sentry *sentry = MAGIC_DSENTRY_TO_SENTRY(dsentry);
|
|
int region = MAGIC_STATE_REGION(sentry);
|
|
void *ptr = MAGIC_PTR_FROM_DSENTRY(dsentry);
|
|
size_t page_size, size;
|
|
void *data_ptr, *aligned_ptr;
|
|
int from_wrapper = MAGIC_MEM_WRAPPER_IS_ACTIVE();
|
|
assert(dsentry->magic_number == MAGIC_DSENTRY_MNUM_NULL);
|
|
|
|
if (!from_wrapper) {
|
|
MAGIC_MEM_WRAPPER_BEGIN();
|
|
}
|
|
|
|
if (magic_mem_heap_free_cb) {
|
|
ret = magic_mem_heap_free_cb(dsentry);
|
|
if (ret != MAGIC_ENOENT)
|
|
return ret;
|
|
|
|
/*
|
|
* If the callback returned MAGIC_ENOENT, fallback to
|
|
* the default behavior.
|
|
*/
|
|
ret = 0;
|
|
}
|
|
|
|
/* A MAP_SHARED region will have both MAGIC_STATE_MAP and MAGIC_STATE_SHM. */
|
|
if (region == (MAGIC_STATE_MAP | MAGIC_STATE_SHM))
|
|
region = MAGIC_STATE_MAP;
|
|
switch (region) {
|
|
case MAGIC_STATE_HEAP:
|
|
MAGIC_MEM_DEBUG_FREE(ptr);
|
|
free(ptr);
|
|
break;
|
|
case MAGIC_STATE_MAP:
|
|
case MAGIC_STATE_SHM:
|
|
page_size = MAGIC_PAGE_SIZE;
|
|
size = MAGIC_DSENTRY_TO_SENTRY(dsentry)->type->size;
|
|
data_ptr = MAGIC_PTR_TO_DATA(ptr);
|
|
aligned_ptr = ((char *)data_ptr) - page_size;
|
|
|
|
if (!MAGIC_STATE_FLAG(sentry, MAGIC_STATE_DETACHED)) {
|
|
size_t padding_size = (size_t) dsentry->ext;
|
|
MAGIC_MEM_DEBUG_FREE(ptr);
|
|
ret = munmap((char *)aligned_ptr, page_size + size + padding_size);
|
|
}
|
|
else {
|
|
#ifndef __MINIX
|
|
if (MAGIC_STATE_FLAG(sentry, MAGIC_STATE_SHM))
|
|
ret = shmdt(data_ptr);
|
|
else
|
|
#endif
|
|
ret = munmap(data_ptr, size);
|
|
MAGIC_MEM_DEBUG_FREE(ptr);
|
|
munmap(aligned_ptr, page_size);
|
|
}
|
|
if (ret != 0) {
|
|
ret = MAGIC_EBADENT;
|
|
}
|
|
break;
|
|
default:
|
|
ret = MAGIC_EBADENT;
|
|
break;
|
|
}
|
|
if (!from_wrapper) {
|
|
MAGIC_MEM_WRAPPER_END();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_free_dead_dsentries *
|
|
*===========================================================================*/
|
|
PUBLIC void magic_free_dead_dsentries()
|
|
{
|
|
struct _magic_dsentry *prev_dsentry, *dsentry, *next_first_dsentry, *skipped_dsentry;
|
|
struct _magic_sentry *sentry;
|
|
unsigned long num_dead_dsentries;
|
|
int dead_dsentries_left;
|
|
int ret;
|
|
|
|
MAGIC_DSENTRY_LOCK();
|
|
|
|
skipped_dsentry = NULL;
|
|
num_dead_dsentries = magic_num_dead_dsentries;
|
|
next_first_dsentry = _magic_first_dsentry;
|
|
if (next_first_dsentry) {
|
|
/* if the first dsentry is dead, skip it to eliminate contention on the list head */
|
|
if (next_first_dsentry->magic_state == MAGIC_DSENTRY_MSTATE_DEAD){
|
|
num_dead_dsentries--;
|
|
}
|
|
}
|
|
|
|
if(!next_first_dsentry || num_dead_dsentries == 0) {
|
|
MAGIC_DSENTRY_UNLOCK();
|
|
return;
|
|
}
|
|
|
|
MAGIC_MEM_PRINTF("magic_free_dead_dsentries: Freeing %d dead dsentries...\n", num_dead_dsentries);
|
|
|
|
/* Eliminate the dead dsentries but always skip the first one to eliminate contention on the head. */
|
|
do {
|
|
dead_dsentries_left = 0;
|
|
MAGIC_DSENTRY_ITER(next_first_dsentry->next, prev_dsentry, dsentry, sentry,
|
|
/* normal dsentry to be freed */
|
|
if ((dsentry->magic_state != MAGIC_DSENTRY_MSTATE_DEAD) ||
|
|
(magic_update_dsentry_state(dsentry, MAGIC_DSENTRY_MSTATE_FREED) < 0)) {
|
|
next_first_dsentry = dsentry;
|
|
} else {
|
|
magic_destroy_dsentry(dsentry, prev_dsentry);
|
|
ret = magic_free_dsentry(dsentry);
|
|
if(ret != 0) {
|
|
_magic_printf("Warning: magic_free_dsentry failed with return code %d for: ", ret);
|
|
MAGIC_DSENTRY_PRINT(dsentry, MAGIC_EXPAND_TYPE_STR);
|
|
MAGIC_MEM_PRINTF("\n");
|
|
}
|
|
num_dead_dsentries--;
|
|
dead_dsentries_left = 1;
|
|
break;
|
|
}
|
|
);
|
|
} while(dead_dsentries_left && num_dead_dsentries > 0);
|
|
assert(num_dead_dsentries == 0);
|
|
|
|
MAGIC_DSENTRY_UNLOCK();
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_destroy_dsentry *
|
|
*===========================================================================*/
|
|
PUBLIC void magic_destroy_dsentry(struct _magic_dsentry *dsentry,
|
|
struct _magic_dsentry *prev_dsentry)
|
|
{
|
|
struct _magic_dsentry *next_dsentry, *next_mempool_dsentry, *prev_mempool_dsentry = NULL;
|
|
int dsentry_destroyed, mempool_dsentry_destroyed;
|
|
|
|
if(magic_destroy_dsentry_ext_cb && MAGIC_DSENTRY_HAS_EXT(dsentry)) {
|
|
magic_destroy_dsentry_ext_cb(dsentry);
|
|
}
|
|
if (MAGIC_STATE_FLAG(MAGIC_DSENTRY_TO_SENTRY(dsentry), MAGIC_STATE_MEMPOOL)) {
|
|
do {
|
|
if(!prev_mempool_dsentry) {
|
|
if(MAGIC_DSENTRY_NEXT_MEMPOOL(dsentry) != MAGIC_DSENTRY_NEXT_MEMPOOL(_magic_first_mempool_dsentry)) {
|
|
prev_mempool_dsentry = magic_mempool_dsentry_prev_lookup(dsentry);
|
|
assert(prev_mempool_dsentry != (struct _magic_dsentry *) MAGIC_ENOPTR && "Dsentry not found!");
|
|
}
|
|
}
|
|
if(prev_mempool_dsentry) {
|
|
MAGIC_DSENTRY_NEXT_MEMPOOL(prev_mempool_dsentry) = MAGIC_DSENTRY_NEXT_MEMPOOL(dsentry);
|
|
mempool_dsentry_destroyed = 1;
|
|
}
|
|
else {
|
|
/* Remove the first dsentry atomically. */
|
|
next_mempool_dsentry = MAGIC_DSENTRY_NEXT_MEMPOOL(dsentry);
|
|
mempool_dsentry_destroyed = (MAGIC_CAS(&_magic_first_mempool_dsentry,
|
|
dsentry, next_mempool_dsentry) == dsentry);
|
|
}
|
|
} while(!mempool_dsentry_destroyed);
|
|
MAGIC_DSENTRY_NEXT_MEMPOOL(dsentry) = NULL;
|
|
}
|
|
do {
|
|
#if MAGIC_DSENTRY_ALLOW_PREV
|
|
prev_dsentry = MAGIC_DSENTRY_PREV(dsentry);
|
|
#else
|
|
if(!prev_dsentry) {
|
|
if(MAGIC_DSENTRY_NEXT(dsentry) != MAGIC_DSENTRY_NEXT(_magic_first_dsentry)) {
|
|
prev_dsentry = magic_dsentry_prev_lookup(dsentry);
|
|
assert(prev_dsentry != (struct _magic_dsentry *) MAGIC_ENOPTR && "Dsentry not found!");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if(prev_dsentry) {
|
|
MAGIC_DSENTRY_NEXT(prev_dsentry) = MAGIC_DSENTRY_NEXT(dsentry);
|
|
dsentry_destroyed = 1;
|
|
}
|
|
else {
|
|
/* Remove the first dsentry atomically. */
|
|
next_dsentry = MAGIC_DSENTRY_NEXT(dsentry);
|
|
dsentry_destroyed = (MAGIC_CAS(&_magic_first_dsentry,
|
|
dsentry, next_dsentry) == dsentry);
|
|
}
|
|
} while(!dsentry_destroyed);
|
|
|
|
#if MAGIC_DSENTRY_ALLOW_PREV
|
|
next_dsentry = MAGIC_DSENTRY_NEXT(dsentry);
|
|
if(next_dsentry) {
|
|
MAGIC_DSENTRY_PREV(next_dsentry) = MAGIC_DSENTRY_PREV(dsentry);
|
|
}
|
|
MAGIC_DSENTRY_PREV(dsentry) = NULL;
|
|
#endif
|
|
|
|
dsentry->magic_number = MAGIC_DSENTRY_MNUM_NULL;
|
|
MAGIC_DSENTRY_NEXT(dsentry) = NULL;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_destroy_obdsentry_by_addr *
|
|
*===========================================================================*/
|
|
PUBLIC int magic_destroy_obdsentry_by_addr(void *data_ptr)
|
|
{
|
|
struct _magic_sentry *sentry;
|
|
struct _magic_dsentry *dsentry;
|
|
struct _magic_obdsentry *obdsentry;
|
|
int obflags = (MAGIC_STATE_DYNAMIC|MAGIC_STATE_OUT_OF_BAND);
|
|
|
|
MAGIC_MEM_WRAPPER_LBEGIN();
|
|
|
|
/* Lookup the obdsentry. */
|
|
sentry = magic_sentry_lookup_by_addr(data_ptr, NULL);
|
|
if(!sentry || ((sentry->flags & obflags) != obflags)) {
|
|
MAGIC_MEM_WRAPPER_LEND();
|
|
return MAGIC_EINVAL;
|
|
}
|
|
dsentry = MAGIC_DSENTRY_FROM_SENTRY(sentry);
|
|
obdsentry = MAGIC_OBDSENTRY_FROM_DSENTRY(dsentry);
|
|
|
|
/* Destroy it and free obdsentry slot. */
|
|
magic_destroy_dsentry(dsentry, NULL);
|
|
MAGIC_OBDSENTRY_FREE(obdsentry);
|
|
|
|
MAGIC_MEM_WRAPPER_LEND();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_destroy_dsentry_set_ext_cb *
|
|
*===========================================================================*/
|
|
PUBLIC void magic_destroy_dsentry_set_ext_cb(const magic_dsentry_cb_t cb)
|
|
{
|
|
magic_destroy_dsentry_ext_cb = cb;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_update_dsentry *
|
|
*===========================================================================*/
|
|
PUBLIC int magic_update_dsentry(void* addr, struct _magic_type *type)
|
|
{
|
|
struct _magic_dsentry *prev_dsentry, *dsentry;
|
|
struct _magic_sentry* sentry;
|
|
size_t size, type_size;
|
|
int is_varsized = 0;
|
|
int num_vsa_elements = 0;
|
|
|
|
MAGIC_DSENTRY_LOCK();
|
|
MAGIC_DSENTRY_ALIVE_BLOCK_ITER(_magic_first_dsentry, prev_dsentry, dsentry, sentry,
|
|
if(sentry->address == addr) {
|
|
size = sentry->type->size;
|
|
type_size = type->size;
|
|
|
|
/* Catch variable-sized struct allocation. */
|
|
if(magic_type_alloc_needs_varsized_array(type, size, &num_vsa_elements)) {
|
|
is_varsized = 1;
|
|
}
|
|
|
|
if(size % type_size != 0 && !is_varsized) {
|
|
return MAGIC_EBADENT;
|
|
}
|
|
if(size == type_size && !is_varsized) {
|
|
sentry->type = type;
|
|
}
|
|
else {
|
|
struct _magic_type *array_type = &(dsentry->type);
|
|
MAGIC_TYPE_ARRAY_CREATE_FROM_SIZE(array_type, type,
|
|
MAGIC_DSENTRY_TO_TYPE_ARR(dsentry), size, num_vsa_elements);
|
|
array_type->id = MAGIC_FAA(&_magic_types_next_id, 1);
|
|
assert(_magic_types_next_id < MAGIC_ID_MAX);
|
|
sentry->type = array_type;
|
|
}
|
|
return 0;
|
|
}
|
|
);
|
|
MAGIC_DSENTRY_UNLOCK();
|
|
|
|
return MAGIC_ENOENT;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_stack_init *
|
|
*===========================================================================*/
|
|
PUBLIC void magic_stack_init()
|
|
{
|
|
struct _magic_obdsentry *obdsentry;
|
|
char **ptr;
|
|
size_t size;
|
|
void *initial_stack_bottom, *initial_stack_top;
|
|
unsigned long* word_ptr;
|
|
int argc;
|
|
char **argv;
|
|
|
|
assert(!_magic_first_stack_dsentry && !_magic_last_stack_dsentry);
|
|
|
|
/* Find initial stack bottom and top, this should be portable enough */
|
|
for (ptr = environ ; *ptr ; ptr++);
|
|
if (ptr == environ){
|
|
/* the environment is empty, and ptr still points to environ.
|
|
* decrement ptr twice: once to point at the argv terminator,
|
|
* and once to point to point at the last argument, which will be the stack bottom
|
|
*/
|
|
ptr -= 2;
|
|
} else {
|
|
/* environment is not empty. decrement the pointer,
|
|
* because the for loop walked past the last env variable pointer.
|
|
*/
|
|
ptr--;
|
|
}
|
|
|
|
if (*ptr) {
|
|
initial_stack_bottom = *ptr+strlen(*ptr)+1;
|
|
word_ptr = (unsigned long*) environ;
|
|
word_ptr--;
|
|
assert(*word_ptr == 0); /* argv terminator */
|
|
word_ptr--;
|
|
argc = 0;
|
|
while(*word_ptr != (unsigned long) argc) {
|
|
argc++;
|
|
word_ptr--;
|
|
}
|
|
argv = (char**) (word_ptr+1);
|
|
initial_stack_top = argv;
|
|
} else {
|
|
/* Environ and argv empty?. Resort to defaults. */
|
|
initial_stack_top = ptr;
|
|
initial_stack_bottom = environ;
|
|
}
|
|
size = (size_t)initial_stack_bottom - (size_t)initial_stack_top + 1;
|
|
|
|
/* Create the first stack dsentry. */
|
|
obdsentry = magic_create_obdsentry(initial_stack_top, MAGIC_VOID_TYPE,
|
|
size, MAGIC_STATE_STACK, MAGIC_ALLOC_INITIAL_STACK_NAME, NULL);
|
|
assert(obdsentry);
|
|
_magic_first_stack_dsentry = _magic_last_stack_dsentry = MAGIC_OBDSENTRY_TO_DSENTRY(obdsentry);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_stack_dsentries_create *
|
|
*===========================================================================*/
|
|
PUBLIC void magic_stack_dsentries_create(
|
|
struct _magic_dsentry **prev_last_stack_dsentry, int num_dsentries,
|
|
/* struct _magic_dsentry *dsentry, struct _magic_type *type, void* data_ptr, char* function_name, char* name, */ ...)
|
|
{
|
|
int i;
|
|
struct _magic_dsentry *dsentry;
|
|
struct _magic_type *type;
|
|
void* data_ptr;
|
|
char* function_name;
|
|
char* name;
|
|
char *min_data_ptr = NULL, *max_data_ptr = NULL;
|
|
va_list va;
|
|
|
|
assert(num_dsentries > 0);
|
|
|
|
MAGIC_DSENTRY_LOCK();
|
|
assert(_magic_first_stack_dsentry && "First stack dsentry not found!");
|
|
va_start(va, num_dsentries);
|
|
*prev_last_stack_dsentry = _magic_last_stack_dsentry;
|
|
for (i = 0 ; i < num_dsentries ; i++) {
|
|
dsentry = va_arg(va, struct _magic_dsentry *);
|
|
type = va_arg(va, struct _magic_type *);
|
|
data_ptr = va_arg(va, void *);
|
|
function_name = va_arg(va, char *);
|
|
name = va_arg(va, char *);
|
|
if (i == num_dsentries - 1) {
|
|
/* Return address. */
|
|
int *value_set = (void*) type;
|
|
data_ptr = MAGIC_FRAMEADDR_TO_RETADDR_PTR(data_ptr);
|
|
type = &(dsentry->type);
|
|
magic_create_dsentry(dsentry, data_ptr, MAGIC_VOID_TYPE, MAGIC_VOID_TYPE->size,
|
|
MAGIC_STATE_STACK | MAGIC_STATE_CONSTANT | MAGIC_STATE_ADDR_NOT_TAKEN,
|
|
name, function_name);
|
|
memcpy(type, &magic_default_ret_addr_type, sizeof(struct _magic_type));
|
|
type->contained_types = MAGIC_DSENTRY_TO_TYPE_ARR(dsentry);
|
|
type->contained_types[0] = MAGIC_VOID_TYPE;
|
|
type->value_set = value_set;
|
|
value_set[0] = 1;
|
|
value_set[1] = *((int *)data_ptr);
|
|
|
|
/* Safe to override the type non-atomically.
|
|
* The new type is only more restrictive, and nobody could
|
|
* have touched this stack dsentry in the meantime.
|
|
*/
|
|
MAGIC_DSENTRY_TO_SENTRY(dsentry)->type = type;
|
|
} else {
|
|
/* Local variable. */
|
|
magic_create_dsentry(dsentry, data_ptr, type, type->size,
|
|
MAGIC_STATE_STACK, name, function_name);
|
|
if (!min_data_ptr || min_data_ptr > (char *) data_ptr) {
|
|
min_data_ptr = (char *) data_ptr;
|
|
}
|
|
if (!max_data_ptr || max_data_ptr < (char *) data_ptr) {
|
|
max_data_ptr = (char *) data_ptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if MAGIC_FORCE_DYN_MEM_ZERO_INIT
|
|
if (min_data_ptr && max_data_ptr) {
|
|
memset(min_data_ptr, 0, max_data_ptr - min_data_ptr + 1);
|
|
}
|
|
#endif
|
|
|
|
_magic_last_stack_dsentry = dsentry;
|
|
MAGIC_DSENTRY_UNLOCK();
|
|
|
|
va_end(va);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_stack_dsentries_destroy *
|
|
*===========================================================================*/
|
|
PUBLIC void magic_stack_dsentries_destroy(
|
|
struct _magic_dsentry **prev_last_stack_dsentry, int num_dsentries,
|
|
/* struct _magic_dsentry *dsentry, */ ...)
|
|
{
|
|
int i;
|
|
struct _magic_dsentry *dsentry;
|
|
va_list va;
|
|
|
|
assert(num_dsentries > 0);
|
|
|
|
MAGIC_MEM_WRAPPER_LBEGIN();
|
|
|
|
va_start(va, num_dsentries);
|
|
_magic_last_stack_dsentry = *prev_last_stack_dsentry;
|
|
for (i = 0 ; i < num_dsentries ; i++) {
|
|
dsentry = va_arg(va, struct _magic_dsentry *);
|
|
magic_destroy_dsentry(dsentry, NULL);
|
|
}
|
|
va_end(va);
|
|
|
|
MAGIC_MEM_WRAPPER_LEND();
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_create_dfunction *
|
|
*===========================================================================*/
|
|
PUBLIC int magic_create_dfunction(struct _magic_dfunction *dfunction,
|
|
void *data_ptr, struct _magic_type *type, int flags,
|
|
char *name, char *parent_name)
|
|
{
|
|
struct _magic_function *function = MAGIC_DFUNCTION_TO_FUNCTION(dfunction);
|
|
|
|
if(!type) {
|
|
type = MAGIC_VOID_TYPE;
|
|
}
|
|
|
|
memcpy(dfunction, &magic_default_dfunction, sizeof(struct _magic_dfunction));
|
|
|
|
assert(!(type->flags & MAGIC_TYPE_DYNAMIC) && "bad type!");
|
|
|
|
function->type = type;
|
|
function->flags |= flags;
|
|
function->address = data_ptr;
|
|
if(name) {
|
|
function->name = name;
|
|
}
|
|
if(parent_name) {
|
|
dfunction->parent_name = parent_name;
|
|
}
|
|
function->id = MAGIC_FAA(&_magic_functions_next_id, 1);
|
|
assert(_magic_functions_next_id < MAGIC_ID_MAX);
|
|
|
|
if(_magic_first_dfunction) {
|
|
assert(_magic_last_dfunction);
|
|
MAGIC_DFUNCTION_NEXT(_magic_last_dfunction) = dfunction;
|
|
}
|
|
else {
|
|
assert(!_magic_last_dfunction);
|
|
assert(_magic_dfunctions_num == 0);
|
|
_magic_first_dfunction = dfunction;
|
|
}
|
|
MAGIC_DFUNCTION_PREV(dfunction) = _magic_last_dfunction;
|
|
MAGIC_DFUNCTION_NEXT(dfunction) = NULL;
|
|
_magic_last_dfunction = dfunction;
|
|
_magic_dfunctions_num++;
|
|
|
|
assert(magic_check_dfunction(dfunction, 0) && "Bad magic dfunction created!");
|
|
magic_update_dfunction_ranges = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_destroy_dfunction *
|
|
*===========================================================================*/
|
|
PUBLIC void magic_destroy_dfunction(struct _magic_dfunction *dfunction)
|
|
{
|
|
dfunction->magic_number = MAGIC_DFUNCTION_MNUM_NULL;
|
|
if(MAGIC_DFUNCTION_HAS_NEXT(dfunction)) {
|
|
MAGIC_DFUNCTION_PREV(MAGIC_DFUNCTION_NEXT(dfunction)) = MAGIC_DFUNCTION_PREV(dfunction);
|
|
}
|
|
else {
|
|
_magic_last_dfunction = MAGIC_DFUNCTION_PREV(dfunction);
|
|
}
|
|
if(MAGIC_DFUNCTION_HAS_PREV(dfunction)) {
|
|
MAGIC_DFUNCTION_NEXT(MAGIC_DFUNCTION_PREV(dfunction)) = MAGIC_DFUNCTION_NEXT(dfunction);
|
|
}
|
|
else {
|
|
_magic_first_dfunction = MAGIC_DFUNCTION_NEXT(dfunction);
|
|
}
|
|
MAGIC_DFUNCTION_NEXT(dfunction) = NULL;
|
|
MAGIC_DFUNCTION_PREV(dfunction) = NULL;
|
|
_magic_dfunctions_num--;
|
|
if(_magic_dfunctions_num == 0) {
|
|
assert(!_magic_first_dfunction && !_magic_last_dfunction);
|
|
}
|
|
else {
|
|
assert(_magic_first_dfunction && _magic_last_dfunction);
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_create_sodesc *
|
|
*===========================================================================*/
|
|
PUBLIC int magic_create_sodesc(struct _magic_sodesc *sodesc)
|
|
{
|
|
if(_magic_first_sodesc) {
|
|
assert(_magic_last_sodesc);
|
|
MAGIC_SODESC_NEXT(_magic_last_sodesc) = sodesc;
|
|
}
|
|
else {
|
|
assert(!_magic_last_sodesc);
|
|
assert(_magic_sodescs_num == 0);
|
|
_magic_first_sodesc = sodesc;
|
|
}
|
|
MAGIC_SODESC_PREV(sodesc) = _magic_last_sodesc;
|
|
MAGIC_SODESC_NEXT(sodesc) = NULL;
|
|
_magic_last_sodesc = sodesc;
|
|
_magic_sodescs_num++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_destroy_sodesc *
|
|
*===========================================================================*/
|
|
PUBLIC int magic_destroy_sodesc(struct _magic_sodesc *sodesc)
|
|
{
|
|
/*
|
|
* NB!: This function requires the calling thread to already
|
|
* hold the DSENTRY and DFUNCTION locks.
|
|
*/
|
|
int ret;
|
|
const char *name;
|
|
struct _magic_dsentry *prev_dsentry, *dsentry, *last_dsentry;
|
|
struct _magic_sentry *sentry;
|
|
struct _magic_dfunction *dfunction, *last_dfunction;
|
|
|
|
/*
|
|
* Time to destroy all the dsentries and dfunctions
|
|
* linked to the descriptor.
|
|
*/
|
|
name = sodesc->lib.name;
|
|
last_dsentry = NULL;
|
|
MAGIC_DSENTRY_ALIVE_ITER(_magic_first_dsentry, prev_dsentry,
|
|
dsentry, sentry,
|
|
if (last_dsentry)
|
|
magic_destroy_dsentry(last_dsentry, NULL);
|
|
last_dsentry = dsentry->parent_name == name ? dsentry : NULL;
|
|
);
|
|
if (last_dsentry)
|
|
magic_destroy_dsentry(last_dsentry, NULL);
|
|
last_dfunction = NULL;
|
|
MAGIC_DFUNCTION_ITER(_magic_first_dfunction, dfunction,
|
|
if (last_dfunction)
|
|
magic_destroy_dfunction(last_dfunction);
|
|
last_dfunction = dfunction->parent_name == name ? dfunction : NULL;
|
|
);
|
|
if(last_dfunction) magic_destroy_dfunction(last_dfunction);
|
|
|
|
/* Now get rid of the descriptor. */
|
|
if (MAGIC_SODESC_HAS_NEXT(sodesc)) {
|
|
MAGIC_SODESC_PREV(MAGIC_SODESC_NEXT(sodesc)) =
|
|
MAGIC_SODESC_PREV(sodesc);
|
|
}
|
|
else {
|
|
_magic_last_sodesc = MAGIC_SODESC_PREV(sodesc);
|
|
}
|
|
if (MAGIC_SODESC_HAS_PREV(sodesc)) {
|
|
MAGIC_SODESC_NEXT(MAGIC_SODESC_PREV(sodesc)) =
|
|
MAGIC_SODESC_NEXT(sodesc);
|
|
}
|
|
else {
|
|
_magic_first_sodesc = MAGIC_SODESC_NEXT(sodesc);
|
|
}
|
|
MAGIC_SODESC_NEXT(sodesc) = NULL;
|
|
MAGIC_SODESC_PREV(sodesc) = NULL;
|
|
_magic_sodescs_num--;
|
|
if (_magic_sodescs_num == 0) {
|
|
assert(!_magic_first_sodesc && !_magic_last_sodesc);
|
|
}
|
|
else {
|
|
assert(_magic_first_sodesc && _magic_last_sodesc);
|
|
}
|
|
|
|
/*
|
|
* Unmap the memory area that contained the dsentries and dfunctions
|
|
* of this descriptor.
|
|
*/
|
|
ret = munmap(sodesc->lib.alloc_address, sodesc->lib.alloc_size);
|
|
assert(ret == 0 && "Unable to unmap SODESC memory segment!");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_create_dsodesc *
|
|
*===========================================================================*/
|
|
PUBLIC int magic_create_dsodesc(struct _magic_dsodesc *dsodesc)
|
|
{
|
|
/*
|
|
* NB!: This function requires the calling thread to already
|
|
* hold the DSODESC lock.
|
|
*/
|
|
if (_magic_first_dsodesc) {
|
|
assert(_magic_last_dsodesc);
|
|
MAGIC_DSODESC_NEXT(_magic_last_dsodesc) = dsodesc;
|
|
}
|
|
else {
|
|
assert(!_magic_last_dsodesc);
|
|
assert(_magic_dsodescs_num == 0);
|
|
_magic_first_dsodesc = dsodesc;
|
|
}
|
|
MAGIC_DSODESC_PREV(dsodesc) = _magic_last_dsodesc;
|
|
MAGIC_DSODESC_NEXT(dsodesc) = NULL;
|
|
_magic_last_dsodesc = dsodesc;
|
|
_magic_dsodescs_num++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_destroy_dsodesc *
|
|
*===========================================================================*/
|
|
PUBLIC int magic_destroy_dsodesc(struct _magic_dsodesc *dsodesc)
|
|
{
|
|
/*
|
|
* NB!: This function requires the calling thread to already
|
|
* hold the DSENTRY, DFUNCTION and DSODESC locks.
|
|
*/
|
|
int ret;
|
|
const char *name;
|
|
struct _magic_dsentry *prev_dsentry, *dsentry, *last_dsentry;
|
|
struct _magic_sentry *sentry;
|
|
struct _magic_dfunction *dfunction, *last_dfunction;
|
|
|
|
dsodesc->ref_count--;
|
|
/* Don't destroy the DSO descriptor quite yet, we still have references. */
|
|
if (dsodesc->ref_count > 0) {
|
|
return dsodesc->ref_count;
|
|
}
|
|
|
|
/*
|
|
* Time to destroy all the dsentries and dfunctions
|
|
* linked to the descriptor.
|
|
*/
|
|
name = dsodesc->lib.name;
|
|
last_dsentry = NULL;
|
|
MAGIC_DSENTRY_ALIVE_ITER(_magic_first_dsentry, prev_dsentry,
|
|
dsentry, sentry,
|
|
if (last_dsentry)
|
|
magic_destroy_dsentry(last_dsentry, NULL);
|
|
last_dsentry = dsentry->parent_name == name ? dsentry : NULL;
|
|
);
|
|
if (last_dsentry)
|
|
magic_destroy_dsentry(last_dsentry, NULL);
|
|
last_dfunction = NULL;
|
|
MAGIC_DFUNCTION_ITER(_magic_first_dfunction, dfunction,
|
|
if (last_dfunction)
|
|
magic_destroy_dfunction(last_dfunction);
|
|
last_dfunction = dfunction->parent_name == name ? dfunction : NULL;
|
|
);
|
|
if (last_dfunction)
|
|
magic_destroy_dfunction(last_dfunction);
|
|
|
|
/* Now get rid of the descriptor. */
|
|
if (MAGIC_DSODESC_HAS_NEXT(dsodesc)) {
|
|
MAGIC_DSODESC_PREV(MAGIC_DSODESC_NEXT(dsodesc)) =
|
|
MAGIC_DSODESC_PREV(dsodesc);
|
|
}
|
|
else {
|
|
_magic_last_dsodesc = MAGIC_DSODESC_PREV(dsodesc);
|
|
}
|
|
if (MAGIC_DSODESC_HAS_PREV(dsodesc)) {
|
|
MAGIC_DSODESC_NEXT(MAGIC_DSODESC_PREV(dsodesc)) =
|
|
MAGIC_DSODESC_NEXT(dsodesc);
|
|
}
|
|
else {
|
|
_magic_first_dsodesc = MAGIC_DSODESC_NEXT(dsodesc);
|
|
}
|
|
MAGIC_DSODESC_NEXT(dsodesc) = NULL;
|
|
MAGIC_DSODESC_PREV(dsodesc) = NULL;
|
|
_magic_dsodescs_num--;
|
|
if (_magic_dsodescs_num == 0) {
|
|
assert(!_magic_first_dsodesc && !_magic_last_dsodesc);
|
|
}
|
|
else {
|
|
assert(_magic_first_dsodesc && _magic_last_dsodesc);
|
|
}
|
|
|
|
/*
|
|
* Unmap the memory area that contained the dsentries and dfunctions
|
|
* of this descriptor.
|
|
*/
|
|
ret = munmap(dsodesc->lib.alloc_address, dsodesc->lib.alloc_size);
|
|
assert(ret == 0 && "Unable to unmap DSODESC memory segment!");
|
|
|
|
return 0; /* no more references, descriptor is gone. */
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_alloc *
|
|
*===========================================================================*/
|
|
PUBLIC void *magic_alloc(__MA_ARGS__ void *ptr, size_t size, int flags)
|
|
{
|
|
int ret;
|
|
void *data_ptr;
|
|
struct _magic_dsentry *dsentry;
|
|
|
|
if(ptr == NULL) {
|
|
return NULL;
|
|
}
|
|
data_ptr = MAGIC_PTR_TO_DATA(ptr);
|
|
dsentry = MAGIC_PTR_TO_DSENTRY(ptr);
|
|
/* Catch pool allocations and update the name & flags */
|
|
if (MAGIC_MEMPOOL_MGMT_IS_ACTIVE() && !(flags & MAGIC_STATE_MEMBLOCK)) {
|
|
flags |= MAGIC_STATE_MEMPOOL;
|
|
name = MAGIC_MEMPOOL_GET_NAME();
|
|
}
|
|
ret = magic_create_dsentry(dsentry, data_ptr, type, size, flags, name, parent_name);
|
|
MAGIC_MEM_PRINTF("magic_alloc: ret = magic_create_dsentry(dsentry, data_ptr, type, size, flags, NULL, NULL) <-> %d = magic_create_dsentry(0x%08x, 0x%08x, 0x%08x, %d, 0x%08x, NULL, NULL)\n", ret, (unsigned) dsentry, (unsigned) data_ptr, type, size, flags);
|
|
if(ret < 0) {
|
|
return MAGIC_MEM_FAILED;
|
|
}
|
|
|
|
/* this way we skip the memory pool blocks -they are not real allocations and should not be logged */
|
|
if (!(flags & MAGIC_STATE_MEMBLOCK)) {
|
|
MAGIC_MEM_DEBUG_ALLOC(ptr, (MAGIC_SIZE_TO_REAL(size)));
|
|
}
|
|
|
|
#if DEBUG
|
|
MAGIC_MEM_PRINTF("magic_alloc: magic_create_dsentry created sentry: ");
|
|
MAGIC_DSENTRY_PRINT(dsentry, MAGIC_EXPAND_TYPE_STR);
|
|
MAGIC_MEM_PRINTF("\n");
|
|
#endif
|
|
|
|
MAGIC_MEM_PRINTF("magic_alloc: return 0x%08x\n", (unsigned) data_ptr);
|
|
|
|
return data_ptr;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_malloc_positioned *
|
|
*===========================================================================*/
|
|
PUBLIC void *magic_malloc_positioned(__MA_ARGS__ size_t size, void *ptr)
|
|
{
|
|
void *data_ptr;
|
|
int dsentry_flags = MAGIC_STATE_HEAP;
|
|
|
|
#if MAGIC_FORCE_DYN_MEM_ZERO_INIT
|
|
assert(!_magic_vars->fake_malloc);
|
|
do {
|
|
return magic_calloc(__MA_VALUES__ size, 1);
|
|
} while(0);
|
|
#endif
|
|
|
|
MAGIC_MEM_WRAPPER_BEGIN();
|
|
|
|
if(size > 0) {
|
|
if (!ptr || !_magic_vars->fake_malloc) {
|
|
/*
|
|
* Check the external callback first.
|
|
*/
|
|
if (magic_mem_heap_alloc_cb)
|
|
ptr = magic_mem_heap_alloc_cb(MAGIC_SIZE_TO_REAL(size) + magic_asr_get_padding_size(MAGIC_STATE_HEAP), name, parent_name);
|
|
|
|
if (!ptr)
|
|
ptr = malloc(MAGIC_SIZE_TO_REAL(size) + magic_asr_get_padding_size(MAGIC_STATE_HEAP));
|
|
MAGIC_MEM_PRINTF("magic_malloc: ptr = malloc(size) <-> 0x%08x = malloc(%d)\n", (unsigned) ptr, MAGIC_SIZE_TO_REAL(size));
|
|
}
|
|
data_ptr = magic_alloc(__MA_VALUES__ ptr, size, dsentry_flags);
|
|
if (data_ptr == MAGIC_MEM_FAILED) {
|
|
/*
|
|
* XXX: This doesn't seem likely to happen. However, if it does,
|
|
* we need to distinguish between regular malloc() memory
|
|
* and super-objects. See llvm/shared/libst/include/heap.h for
|
|
* more information.
|
|
*/
|
|
free(ptr);
|
|
data_ptr = NULL;
|
|
errno = ENOMEM;
|
|
}
|
|
magic_heap_end = ((char *)sbrk(0)) - 1;
|
|
}
|
|
else {
|
|
data_ptr = NULL;
|
|
}
|
|
|
|
MAGIC_MEM_WRAPPER_END();
|
|
|
|
return data_ptr;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_malloc *
|
|
*===========================================================================*/
|
|
PUBLIC void *magic_malloc(__MA_ARGS__ size_t size)
|
|
{
|
|
return magic_malloc_positioned(__MA_VALUES__ size, NULL);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_calloc *
|
|
*===========================================================================*/
|
|
PUBLIC void *magic_calloc(__MA_ARGS__ size_t nmemb, size_t size)
|
|
{
|
|
void *ptr = NULL, *data_ptr;
|
|
size_t real_size;
|
|
int dsentry_flags = MAGIC_STATE_HEAP;
|
|
|
|
MAGIC_MEM_WRAPPER_BEGIN();
|
|
|
|
if(size > 0) {
|
|
real_size = MAGIC_SIZE_TO_REAL(size*nmemb);
|
|
/*
|
|
* Check the external callback first.
|
|
*/
|
|
if (magic_mem_heap_alloc_cb)
|
|
ptr = magic_mem_heap_alloc_cb(real_size + magic_asr_get_padding_size(MAGIC_STATE_HEAP), name, parent_name);
|
|
|
|
if (!ptr)
|
|
ptr = calloc(real_size + magic_asr_get_padding_size(MAGIC_STATE_HEAP), 1);
|
|
MAGIC_MEM_PRINTF("magic_calloc: ptr = calloc(nmemb, size) <-> 0x%08x = calloc(%d, %d)\n", (unsigned) ptr, nmemb, real_size);
|
|
data_ptr = magic_alloc(__MA_VALUES__ ptr, size*nmemb, dsentry_flags);
|
|
if(data_ptr == MAGIC_MEM_FAILED) {
|
|
/*
|
|
* XXX: This doesn't seem likely to happen. However, if it does,
|
|
* we need to distinguish between regular malloc() memory
|
|
* and super-objects. See llvm/shared/libst/include/heap.h for
|
|
* more information.
|
|
*/
|
|
free(ptr);
|
|
data_ptr = NULL;
|
|
errno = ENOMEM;
|
|
}
|
|
magic_heap_end = ((char*)sbrk(0))-1;
|
|
}
|
|
else {
|
|
data_ptr = NULL;
|
|
}
|
|
|
|
MAGIC_MEM_WRAPPER_END();
|
|
|
|
return data_ptr;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_free *
|
|
*===========================================================================*/
|
|
PUBLIC void magic_free(__MD_ARGS__ void *data_ptr)
|
|
{
|
|
void *ptr;
|
|
int ret;
|
|
|
|
MAGIC_MEM_WRAPPER_BEGIN();
|
|
|
|
if(data_ptr) {
|
|
ptr = MAGIC_PTR_FROM_DATA(data_ptr);
|
|
|
|
/* Check for legitimate non-indexed chunks of memory and skip. */
|
|
if((!magic_libcommon_active || !MAGIC_USE_DYN_MEM_WRAPPERS)
|
|
&& !MAGIC_DSENTRY_MNUM_OK(MAGIC_PTR_TO_DSENTRY(ptr))) {
|
|
MAGIC_MEM_WRAPPER_END();
|
|
free(data_ptr);
|
|
return;
|
|
}
|
|
|
|
MAGIC_MEM_PRINTF("magic_free: magic_free(0x%08x) / free(0x%08x)\n", (unsigned) data_ptr, (unsigned) ptr);
|
|
assert(magic_check_dsentry(MAGIC_PTR_TO_DSENTRY(ptr), MAGIC_STATE_HEAP) && "XXX Bad magic dsentry: corruption or memory not allocated from a magic wrapper?");
|
|
|
|
if(magic_allow_dead_dsentries) {
|
|
ret = magic_update_dsentry_state(MAGIC_PTR_TO_DSENTRY(ptr), MAGIC_DSENTRY_MSTATE_DEAD);
|
|
assert(ret == 0 && "Bad free!");
|
|
}
|
|
else {
|
|
MAGIC_DSENTRY_LOCK();
|
|
magic_destroy_dsentry(MAGIC_PTR_TO_DSENTRY(ptr), NULL);
|
|
ret = magic_free_dsentry(MAGIC_PTR_TO_DSENTRY(ptr));
|
|
assert(ret == 0 && "Bad free!");
|
|
MAGIC_DSENTRY_UNLOCK();
|
|
}
|
|
}
|
|
|
|
MAGIC_MEM_WRAPPER_END();
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_realloc *
|
|
*===========================================================================*/
|
|
PUBLIC void *magic_realloc(__MA_ARGS__ void *data_ptr, size_t size)
|
|
{
|
|
void *ptr, *new_ptr, *new_data_ptr;
|
|
size_t old_size;
|
|
|
|
if(!data_ptr) {
|
|
return magic_malloc(__MA_VALUES__ size);
|
|
}
|
|
if(size == 0) {
|
|
magic_free(__MD_VALUES_DEFAULT__ data_ptr);
|
|
return NULL;
|
|
}
|
|
|
|
ptr = MAGIC_PTR_FROM_DATA(data_ptr);
|
|
new_data_ptr = magic_malloc(__MA_VALUES__ size);
|
|
if(!new_data_ptr) {
|
|
return NULL;
|
|
}
|
|
new_ptr = MAGIC_PTR_FROM_DATA(new_data_ptr);
|
|
assert(magic_check_dsentry(MAGIC_PTR_TO_DSENTRY(ptr), MAGIC_STATE_HEAP) && "XXX Bad magic dsentry: corruption or memory not allocated from a magic wrapper?");
|
|
MAGIC_MEM_PRINTF("magic_realloc: ptr = realloc(ptr, size) <-> 0x%08x = realloc(0x%08x, %d)\n", (unsigned) new_ptr, (unsigned) ptr, MAGIC_SIZE_TO_REAL(size));
|
|
|
|
old_size = MAGIC_DSENTRY_TO_SENTRY(MAGIC_PTR_TO_DSENTRY(ptr))->type->size;
|
|
memcpy(new_data_ptr, data_ptr, old_size < size ? old_size : size);
|
|
magic_free(__MD_VALUES_DEFAULT__ data_ptr);
|
|
|
|
return new_data_ptr;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_posix_memalign *
|
|
*===========================================================================*/
|
|
PUBLIC int magic_posix_memalign(__MA_ARGS__ void **memptr, size_t alignment, size_t size)
|
|
{
|
|
int ret = 0;
|
|
void *ptr = NULL, *data_ptr;
|
|
int dsentry_flags = MAGIC_STATE_HEAP;
|
|
|
|
MAGIC_MEM_WRAPPER_BEGIN();
|
|
|
|
if(size > 0) {
|
|
/*
|
|
* Check the external callback first.
|
|
*/
|
|
if (magic_mem_heap_alloc_cb)
|
|
ptr = magic_mem_heap_alloc_cb(MAGIC_SIZE_TO_REAL(size), name, parent_name);
|
|
|
|
if (!ptr)
|
|
ret = posix_memalign(&ptr, alignment, MAGIC_SIZE_TO_REAL(size));
|
|
MAGIC_MEM_PRINTF("magic_posix_memalign: ret = posix_memalign(ptr, alignment, size) <-> %d = posix_memalign(%p, %d, %d)\n", ret, ptr, alignment, MAGIC_SIZE_TO_REAL(size));
|
|
if(ret == 0) {
|
|
data_ptr = magic_alloc(__MA_VALUES__ ptr, size, dsentry_flags);
|
|
if(data_ptr == MAGIC_MEM_FAILED) {
|
|
/*
|
|
* XXX: This doesn't seem likely to happen. However, if it does,
|
|
* we need to distinguish between regular malloc() memory
|
|
* and super-objects. See llvm/shared/libst/include/heap.h for
|
|
* more information.
|
|
*/
|
|
free(ptr);
|
|
ret = ENOMEM;
|
|
}
|
|
else {
|
|
*memptr = data_ptr;
|
|
#if MAGIC_FORCE_DYN_MEM_ZERO_INIT
|
|
memset(data_ptr, 0, size);
|
|
#endif
|
|
}
|
|
magic_heap_end = ((char*)sbrk(0))-1;
|
|
}
|
|
}
|
|
else {
|
|
ret = EINVAL;
|
|
}
|
|
|
|
MAGIC_MEM_WRAPPER_END();
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifndef __MINIX
|
|
/*===========================================================================*
|
|
* magic_valloc *
|
|
*===========================================================================*/
|
|
PUBLIC void *magic_valloc(__MA_ARGS__ size_t size)
|
|
{
|
|
return magic_memalign(__MA_VALUES__ MAGIC_PAGE_SIZE, size);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_memalign *
|
|
*===========================================================================*/
|
|
PUBLIC void *magic_memalign(__MA_ARGS__ size_t boundary, size_t size)
|
|
{
|
|
void *ptr;
|
|
int ret = magic_posix_memalign(__MA_VALUES__ &ptr, boundary, size);
|
|
if(ret != 0) {
|
|
return NULL;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*===========================================================================*
|
|
* magic_mmap_positioned *
|
|
*===========================================================================*/
|
|
PUBLIC void *magic_mmap_positioned(__MA_ARGS__ void *start, size_t length, int prot, int flags,
|
|
int fd, off_t offset, struct _magic_dsentry *cached_dsentry)
|
|
{
|
|
void *ptr, *data_ptr, *aligned_start, *aligned_ptr;
|
|
void *new_ptr, *new_start;
|
|
int dsentry_flags = MAGIC_STATE_MAP;
|
|
size_t alloc_length;
|
|
size_t page_size = MAGIC_PAGE_SIZE;
|
|
int padding_type, padding_size;
|
|
static THREAD_LOCAL int magic_is_first_mmap = 1;
|
|
|
|
MAGIC_MEM_WRAPPER_BEGIN();
|
|
|
|
if (flags & MAP_FIXED) {
|
|
/* Allow safe overmapping. */
|
|
struct _magic_sentry *sentry = magic_sentry_lookup_by_range(start, NULL);
|
|
if (sentry && sentry == magic_sentry_lookup_by_range((char*)start+length-1, NULL))
|
|
return mmap(start, length, prot, flags, fd, offset);
|
|
}
|
|
assert(!(flags & MAP_FIXED) && "MAP_FIXED may override existing mapping, currently not implemented!");
|
|
if (length > 0) {
|
|
if (magic_is_first_mmap) {
|
|
magic_is_first_mmap = 0;
|
|
padding_type = MAGIC_STATE_MAP | MAGIC_ASR_FLAG_INIT;
|
|
} else {
|
|
padding_type = MAGIC_STATE_MAP;
|
|
}
|
|
padding_size = start ? 0 : magic_asr_get_padding_size(padding_type);
|
|
|
|
assert(MAGIC_SIZE_TO_REAL(length) <= page_size + length);
|
|
aligned_start = start ? ((char *)start) - page_size : NULL;
|
|
alloc_length = length + (length % page_size == 0 ? 0 : page_size - (length % page_size));
|
|
#if 0
|
|
if (_magic_vars->do_skip_mmap) {
|
|
ptr = cached_dsentry ? ((char *)cached_dsentry) - (padding_size + page_size - MAGIC_SIZE_TO_REAL(0)) : NULL;
|
|
} else
|
|
#endif
|
|
if (!(flags & MAP_ANONYMOUS) && !(flags & MAP_SHARED) && ((prot & magic_mmap_dsentry_header_prot) == magic_mmap_dsentry_header_prot)) {
|
|
ptr = mmap(aligned_start, page_size + alloc_length + padding_size, prot, flags, fd, offset);
|
|
}
|
|
else {
|
|
/* Preallocate memory for metadata + data. */
|
|
ptr = mmap(aligned_start, page_size + alloc_length + padding_size, magic_mmap_dsentry_header_prot, MAP_ANONYMOUS | MAP_PRIVATE | (flags & MAP_FIXED), -1, 0);
|
|
|
|
/* Remap the data part the way the caller wants us to. */
|
|
if (ptr != MAP_FAILED) {
|
|
new_start = ((char *)ptr) + page_size;
|
|
new_ptr = mmap(new_start, length, prot, flags | MAP_FIXED, fd, offset);
|
|
if (new_ptr == MAP_FAILED) {
|
|
munmap(ptr, page_size + alloc_length + padding_size);
|
|
ptr = MAP_FAILED;
|
|
}
|
|
}
|
|
}
|
|
aligned_ptr = ptr;
|
|
MAGIC_MEM_PRINTF("magic_mmap: ptr = mmap(start, length, prot, flags, fd, offset) <-> 0x%08x = mmap(0x%08x, %d, 0x%08x, 0x%08x, %d, %d)\n", (unsigned) aligned_ptr, aligned_start, page_size + length, prot, flags, fd, offset);
|
|
if (ptr != MAP_FAILED) {
|
|
ptr = ((char *)ptr) + page_size - MAGIC_SIZE_TO_REAL(0);
|
|
}
|
|
else {
|
|
ptr = NULL;
|
|
}
|
|
if (flags & MAP_SHARED)
|
|
dsentry_flags |= MAGIC_STATE_SHM;
|
|
data_ptr = magic_alloc(__MA_VALUES__ ptr, alloc_length, dsentry_flags);
|
|
if (data_ptr == MAGIC_MEM_FAILED) {
|
|
munmap(aligned_ptr, page_size + alloc_length + padding_size);
|
|
data_ptr = NULL;
|
|
}
|
|
if (!data_ptr) {
|
|
errno = ENOMEM;
|
|
data_ptr = MAP_FAILED;
|
|
} else {
|
|
assert(data_ptr == (char *)aligned_ptr + page_size);
|
|
MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_mmap_flags = flags;
|
|
MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_mmap_prot = prot;
|
|
MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->ext = (void *) padding_size;
|
|
}
|
|
}
|
|
else {
|
|
data_ptr = MAP_FAILED;
|
|
errno = EINVAL;
|
|
}
|
|
|
|
MAGIC_MEM_WRAPPER_END();
|
|
|
|
return data_ptr;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_mmap *
|
|
*===========================================================================*/
|
|
PUBLIC void *magic_mmap(__MA_ARGS__ void *start, size_t length, int prot, int flags,
|
|
int fd, off_t offset)
|
|
{
|
|
return magic_mmap_positioned(__MA_VALUES__ start, length, prot, flags, fd, offset, NULL);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_munmap *
|
|
*===========================================================================*/
|
|
PUBLIC int magic_munmap(__MD_ARGS__ void *data_ptr, size_t length)
|
|
{
|
|
int ret;
|
|
void *ptr, *aligned_ptr;
|
|
struct _magic_sentry *sentry;
|
|
size_t alloc_length, old_size;
|
|
size_t page_size = MAGIC_PAGE_SIZE;
|
|
|
|
MAGIC_MEM_WRAPPER_BEGIN();
|
|
|
|
if(data_ptr) {
|
|
ptr = MAGIC_PTR_FROM_DATA(data_ptr);
|
|
|
|
/* Check for legitimate non-indexed chunks of memory and skip. */
|
|
if((!magic_libcommon_active || !MAGIC_USE_DYN_MEM_WRAPPERS)
|
|
&& !MAGIC_DSENTRY_MNUM_OK(MAGIC_PTR_TO_DSENTRY(ptr))) {
|
|
MAGIC_MEM_WRAPPER_END();
|
|
return munmap(data_ptr, length);
|
|
}
|
|
|
|
sentry = MAGIC_DSENTRY_TO_SENTRY(MAGIC_PTR_TO_DSENTRY(ptr));
|
|
aligned_ptr = ((char*)data_ptr) - page_size;
|
|
MAGIC_MEM_PRINTF("magic_munmap: magic_munmap(0x%08x, %d) / unmap(0x%08x, %d)\n", (unsigned) data_ptr, length, (unsigned) aligned_ptr, page_size+length);
|
|
assert(magic_check_dsentry(MAGIC_PTR_TO_DSENTRY(ptr), MAGIC_STATE_MAP) && "XXX Bad magic dsentry: corruption or memory not allocated from a magic wrapper?");
|
|
old_size = MAGIC_DSENTRY_TO_SENTRY(MAGIC_PTR_TO_DSENTRY(ptr))->type->size;
|
|
alloc_length = length + (length % page_size == 0 ? 0 : page_size-(length % page_size));
|
|
|
|
if(alloc_length != old_size) {
|
|
assert(alloc_length >= old_size && "Partial unmapping not supported!");
|
|
ret = -1;
|
|
errno = EINVAL;
|
|
}
|
|
else {
|
|
if(magic_allow_dead_dsentries) {
|
|
ret = magic_update_dsentry_state(MAGIC_PTR_TO_DSENTRY(ptr), MAGIC_DSENTRY_MSTATE_DEAD);
|
|
assert(ret == 0 && "Bad munmap!");
|
|
}
|
|
else {
|
|
MAGIC_DSENTRY_LOCK();
|
|
magic_destroy_dsentry(MAGIC_PTR_TO_DSENTRY(ptr), NULL);
|
|
ret = magic_free_dsentry(MAGIC_PTR_TO_DSENTRY(ptr));
|
|
assert(ret == 0 && "Bad munmap!");
|
|
MAGIC_DSENTRY_UNLOCK();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ret = -1;
|
|
errno = EINVAL;
|
|
}
|
|
|
|
MAGIC_MEM_WRAPPER_END();
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_brk *
|
|
*===========================================================================*/
|
|
PUBLIC int magic_brk(__MA_ARGS__ void *addr)
|
|
{
|
|
void *ptr;
|
|
void *break_addr;
|
|
int ret;
|
|
|
|
MAGIC_MEM_PRINTF("magic_brk: Warning: somebody calling magic_brk()!");
|
|
MAGIC_MEM_WRAPPER_LBLOCK( break_addr = sbrk(0); );
|
|
if(addr >= break_addr) {
|
|
ptr = magic_sbrk(__MA_VALUES__ (char*)addr - (char*)break_addr);
|
|
ret = (ptr == (void*) -1 ? -1 : 0);
|
|
if(ret == -1) {
|
|
errno = ENOMEM;
|
|
}
|
|
}
|
|
else {
|
|
magic_free(__MD_VALUES_DEFAULT__ addr);
|
|
ret = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_sbrk *
|
|
*===========================================================================*/
|
|
PUBLIC void *magic_sbrk(__MA_ARGS__ intptr_t increment)
|
|
{
|
|
void *ptr;
|
|
|
|
if(increment == 0) {
|
|
MAGIC_MEM_WRAPPER_LBLOCK( ptr = sbrk(0); );
|
|
}
|
|
else {
|
|
MAGIC_MEM_PRINTF("magic_sbrk: Warning: somebody calling magic_sbrk(), resorting to magic_malloc()!");
|
|
ptr = magic_malloc(__MA_VALUES__ increment);
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
#ifndef __MINIX
|
|
/*===========================================================================*
|
|
* magic_shmat *
|
|
*===========================================================================*/
|
|
PUBLIC void *magic_shmat(__MA_ARGS__ int shmid, const void *shmaddr, int shmflg)
|
|
{
|
|
void *ptr, *data_ptr, *aligned_shmaddr, *aligned_ptr;
|
|
void *new_ptr, *new_shmaddr;
|
|
size_t size;
|
|
struct shmid_ds buf;
|
|
int ret, flags;
|
|
size_t page_size = MAGIC_PAGE_SIZE;
|
|
|
|
MAGIC_MEM_WRAPPER_BEGIN();
|
|
|
|
assert(!(shmflg & SHM_REMAP) && "Linux-specific SHM_REMAP not supported!");
|
|
ret = shmctl(shmid, IPC_STAT, &buf);
|
|
if (ret == -1) {
|
|
MAGIC_MEM_WRAPPER_END();
|
|
return NULL;
|
|
}
|
|
size = buf.shm_segsz;
|
|
if (size > 0) {
|
|
assert(size % page_size == 0);
|
|
assert(MAGIC_SIZE_TO_REAL(size) <= size + page_size);
|
|
if (shmaddr && (shmflg & SHM_RND)) {
|
|
unsigned long shmlba = SHMLBA;
|
|
shmflg &= ~SHM_RND;
|
|
shmaddr = (void *) ((((unsigned long)shmaddr) / shmlba) * shmlba);
|
|
}
|
|
|
|
/* Preallocate memory for metadata + data. */
|
|
aligned_shmaddr = shmaddr ? ((char *)shmaddr) - page_size : NULL;
|
|
flags = MAP_ANONYMOUS | MAP_PRIVATE | (aligned_shmaddr ? MAP_FIXED : 0);
|
|
ptr = mmap(aligned_shmaddr, page_size + size, magic_mmap_dsentry_header_prot, flags, -1, 0);
|
|
|
|
/* Remap the data part the way the caller wants us to. */
|
|
if (ptr != MAP_FAILED) {
|
|
new_shmaddr = ((char *)ptr) + page_size;
|
|
munmap(new_shmaddr, size);
|
|
new_ptr = shmat(shmid, new_shmaddr, shmflg);
|
|
if(new_ptr == (void *) -1) {
|
|
munmap(ptr, page_size);
|
|
ptr = MAP_FAILED;
|
|
}
|
|
}
|
|
aligned_ptr = ptr;
|
|
MAGIC_MEM_PRINTF("magic_shmat: ptr = shmat(shmid, shmaddr, shmflg) <-> 0x%08x = shmat(%d, 0x%08x, 0x%08x)\n", (unsigned) aligned_ptr, shmid, aligned_shmaddr, shmflg);
|
|
if (ptr != MAP_FAILED) {
|
|
ptr = ((char *)ptr) + page_size - MAGIC_SIZE_TO_REAL(0);
|
|
}
|
|
else {
|
|
ptr = NULL;
|
|
}
|
|
data_ptr = magic_alloc(__MA_VALUES__ ptr, size, MAGIC_STATE_SHM | MAGIC_STATE_DETACHED);
|
|
if (data_ptr == MAGIC_MEM_FAILED) {
|
|
munmap(aligned_ptr, page_size);
|
|
munmap(new_ptr, size);
|
|
data_ptr = (void *) -1;
|
|
errno = ENOMEM;
|
|
}
|
|
else {
|
|
MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_shmat_flags = shmflg;
|
|
MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_shmat_shmid = shmid;
|
|
}
|
|
}
|
|
else {
|
|
data_ptr = (void *) -1;
|
|
errno = EINVAL;
|
|
}
|
|
|
|
MAGIC_MEM_WRAPPER_END();
|
|
|
|
return data_ptr;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_shmdt *
|
|
*===========================================================================*/
|
|
PUBLIC int magic_shmdt(__MD_ARGS__ const void *data_ptr)
|
|
{
|
|
int ret;
|
|
void *ptr, *aligned_ptr;
|
|
size_t page_size = MAGIC_PAGE_SIZE;
|
|
|
|
MAGIC_MEM_WRAPPER_LBEGIN();
|
|
|
|
if (data_ptr) {
|
|
ptr = MAGIC_PTR_FROM_DATA(data_ptr);
|
|
|
|
/* Check for legitimate non-indexed chunks of memory and skip. */
|
|
if ((!magic_libcommon_active || !MAGIC_USE_DYN_MEM_WRAPPERS)
|
|
&& !MAGIC_DSENTRY_MNUM_OK(MAGIC_PTR_TO_DSENTRY(ptr))) {
|
|
MAGIC_MEM_WRAPPER_LEND();
|
|
return shmdt(data_ptr);
|
|
}
|
|
|
|
aligned_ptr = ((char*)data_ptr) - page_size;
|
|
MAGIC_MEM_PRINTF("magic_shmdt: magic_shmdt(0x%08x) / shmdt(0x%08x)\n", (unsigned) data_ptr, (unsigned) aligned_ptr);
|
|
assert(magic_check_dsentry(MAGIC_PTR_TO_DSENTRY(ptr), MAGIC_STATE_SHM) && "XXX Bad magic dsentry: corruption or memory not allocated from a magic wrapper?");
|
|
ret = shmdt(data_ptr);
|
|
if (ret == 0) {
|
|
magic_destroy_dsentry(MAGIC_PTR_TO_DSENTRY(ptr), NULL);
|
|
munmap(aligned_ptr, page_size);
|
|
}
|
|
}
|
|
else {
|
|
ret = -1;
|
|
errno = EINVAL;
|
|
}
|
|
|
|
MAGIC_MEM_WRAPPER_LEND();
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_mmap64 *
|
|
*===========================================================================*/
|
|
PUBLIC void *magic_mmap64(__MA_ARGS__ void *start, size_t length, int prot, int flags,
|
|
int fd, off_t pgoffset)
|
|
{
|
|
void *ptr, *data_ptr, *aligned_start, *aligned_ptr;
|
|
void *new_ptr, *new_start;
|
|
int dsentry_flags = MAGIC_STATE_MAP;
|
|
size_t alloc_length;
|
|
size_t page_size = MAGIC_PAGE_SIZE;
|
|
|
|
MAGIC_MEM_WRAPPER_BEGIN();
|
|
|
|
if (flags & MAP_FIXED) {
|
|
/* Allow safe overmapping. */
|
|
struct _magic_sentry *sentry = magic_sentry_lookup_by_range(start, NULL);
|
|
if (sentry && sentry == magic_sentry_lookup_by_range((char*)start+length-1, NULL))
|
|
return mmap64(start, length, prot, flags, fd, pgoffset);
|
|
}
|
|
assert(!(flags & MAP_FIXED) && "MAP_FIXED may override existing mapping, currently not implemented!");
|
|
if(length > 0) {
|
|
assert(MAGIC_SIZE_TO_REAL(length) <= page_size+length);
|
|
aligned_start = start ? ((char*)start) - page_size : NULL;
|
|
alloc_length = length + (length % page_size == 0 ? 0 : page_size-(length % page_size));
|
|
if((flags & MAP_ANONYMOUS) && !(flags & MAP_SHARED) && ((prot & magic_mmap_dsentry_header_prot) == magic_mmap_dsentry_header_prot)) {
|
|
ptr = mmap64(aligned_start, page_size+length, prot, flags, fd, pgoffset);
|
|
}
|
|
else {
|
|
/* Preallocate memory for metadata + data. */
|
|
ptr = mmap64(aligned_start, page_size+alloc_length, magic_mmap_dsentry_header_prot, MAP_ANONYMOUS|MAP_PRIVATE|(flags & MAP_FIXED), -1, 0);
|
|
|
|
/* Remap the data part the way the caller wants us to. */
|
|
if(ptr != MAP_FAILED) {
|
|
new_start = ((char*)ptr) + page_size;
|
|
new_ptr = mmap64(new_start, length, prot, flags|MAP_FIXED, fd, pgoffset);
|
|
if(new_ptr == MAP_FAILED) {
|
|
munmap(ptr, page_size+alloc_length);
|
|
ptr = MAP_FAILED;
|
|
}
|
|
}
|
|
}
|
|
aligned_ptr = ptr;
|
|
MAGIC_MEM_PRINTF("magic_mmap64: ptr = mmap64(start, length, prot, flags, fd, pgoffset) <-> 0x%08x = mmap64(0x%08x, %d, 0x%08x, 0x%08x, %d, %d)\n", (unsigned) aligned_ptr, aligned_start, page_size+length, prot, flags, fd, pgoffset);
|
|
if(ptr != MAP_FAILED) {
|
|
ptr = ((char*)ptr) + page_size - MAGIC_SIZE_TO_REAL(0);
|
|
}
|
|
else {
|
|
ptr = NULL;
|
|
}
|
|
if (flags & MAP_SHARED)
|
|
dsentry_flags |= MAGIC_STATE_SHM;
|
|
data_ptr = magic_alloc(__MA_VALUES__ ptr, alloc_length, dsentry_flags);
|
|
if(data_ptr == MAGIC_MEM_FAILED) {
|
|
munmap(aligned_ptr, page_size+length);
|
|
data_ptr = NULL;
|
|
errno = ENOMEM;
|
|
}
|
|
if(!data_ptr) {
|
|
errno = ENOMEM;
|
|
data_ptr = MAP_FAILED;
|
|
} else {
|
|
MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_mmap_flags = flags;
|
|
MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_mmap_prot = prot;
|
|
}
|
|
}
|
|
else {
|
|
data_ptr = MAP_FAILED;
|
|
errno = EINVAL;
|
|
}
|
|
|
|
MAGIC_MEM_WRAPPER_END();
|
|
|
|
return data_ptr;
|
|
}
|
|
|
|
#else
|
|
/*===========================================================================*
|
|
* magic_vm_map_cacheblock *
|
|
*===========================================================================*/
|
|
PUBLIC void *magic_vm_map_cacheblock(__MA_ARGS__ dev_t dev, off_t dev_offset,
|
|
ino_t ino, off_t ino_offset, u32_t *flags, int length)
|
|
{
|
|
void *ptr, *data_ptr, *aligned_ptr;
|
|
int dsentry_flags = MAGIC_STATE_MAP;
|
|
size_t alloc_length;
|
|
size_t page_size = MAGIC_PAGE_SIZE;
|
|
|
|
MAGIC_MEM_WRAPPER_BEGIN();
|
|
|
|
if(length > 0) {
|
|
assert(MAGIC_SIZE_TO_REAL(length) <= page_size+length);
|
|
alloc_length = length + (length % page_size == 0 ? 0 : page_size-(length % page_size));
|
|
data_ptr = vm_map_cacheblock(dev, dev_offset, ino, ino_offset, flags, length);
|
|
if (data_ptr != MAP_FAILED) {
|
|
ptr = mmap(data_ptr-page_size, page_size, magic_mmap_dsentry_header_prot, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
|
MAGIC_MEM_PRINTF("vm_map_cacheblock: ptr = mmap(start, length, prot, flags, fd, offset) <-> 0x%08x = mmap(0x%08x, %d, 0x%08x, 0x%08x, %d, %d)\n", (unsigned) ptr, data_ptr-page_size, page_size, magic_mmap_dsentry_header_prot, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
|
assert(ptr == data_ptr-page_size); /* Ensured by VM. */
|
|
aligned_ptr = ptr;
|
|
ptr = ((char*)ptr) + page_size - MAGIC_SIZE_TO_REAL(0);
|
|
}
|
|
else {
|
|
aligned_ptr = NULL;
|
|
ptr = NULL;
|
|
}
|
|
data_ptr = magic_alloc(__MA_VALUES__ ptr, alloc_length, dsentry_flags);
|
|
if(data_ptr == MAGIC_MEM_FAILED) {
|
|
munmap(aligned_ptr, page_size+length);
|
|
data_ptr = NULL;
|
|
}
|
|
if(!data_ptr) {
|
|
data_ptr = MAP_FAILED;
|
|
errno = ENOMEM;
|
|
} else {
|
|
MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_mmap_flags = MAP_ANONYMOUS | MAP_PRIVATE;
|
|
MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_mmap_prot = magic_mmap_dsentry_header_prot;
|
|
}
|
|
}
|
|
else {
|
|
data_ptr = MAP_FAILED;
|
|
errno = EINVAL;
|
|
}
|
|
|
|
MAGIC_MEM_WRAPPER_END();
|
|
|
|
return data_ptr;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_nested_mmap *
|
|
*===========================================================================*/
|
|
void *
|
|
magic_nested_mmap(void *start, size_t length, int prot, int flags,
|
|
int fd, off_t offset)
|
|
{
|
|
void *ptr;
|
|
int i;
|
|
|
|
ptr = mmap(start, length, prot, flags, fd, offset);
|
|
|
|
if (ptr != MAP_FAILED) {
|
|
MAGIC_MEM_PRINTF("MAGIC: nested mmap (%p, %zu)\n", ptr,
|
|
length);
|
|
|
|
/*
|
|
* Find a free entry. We do not expect the malloc code to have
|
|
* more than two areas mapped at any time.
|
|
*/
|
|
for (i = 0; i < MAGIC_UNMAP_MEM_ENTRIES; i++)
|
|
if (_magic_unmap_mem[i].length == 0)
|
|
break;
|
|
assert(i < MAGIC_UNMAP_MEM_ENTRIES);
|
|
|
|
/* Store the mapping in this entry. */
|
|
_magic_unmap_mem[i].start = ptr;
|
|
_magic_unmap_mem[i].length = length;
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* magic_nested_munmap *
|
|
*===========================================================================*/
|
|
int
|
|
magic_nested_munmap(void *start, size_t length)
|
|
{
|
|
int i, r;
|
|
|
|
r = munmap(start, length);
|
|
|
|
if (r == 0) {
|
|
MAGIC_MEM_PRINTF("MAGIC: nested munmap (%p, %zu)\n", start,
|
|
length);
|
|
|
|
/* Find the corresponding entry. This must always succeed. */
|
|
for (i = 0; i < MAGIC_UNMAP_MEM_ENTRIES; i++)
|
|
if (_magic_unmap_mem[i].start == start &&
|
|
_magic_unmap_mem[i].length == length)
|
|
break;
|
|
assert(i < MAGIC_UNMAP_MEM_ENTRIES);
|
|
|
|
/* Clear the entry. */
|
|
_magic_unmap_mem[i].start = NULL;
|
|
_magic_unmap_mem[i].length = 0;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
#endif
|
|
|