nspluginwrapper/src/npw-malloc.c

376 lines
9.1 KiB
C

/*
* npw-malloc.c - Memory allocation
*
* nspluginwrapper (C) 2005-2009 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "sysdeps.h"
/* Hack to workaround the NPW_MemAlloc macros. TODO: don't make macros
* and function names conflict. It's insane. */
#undef ENABLE_MALLOC_CHECK
#include "npw-malloc.h"
#define DEBUG 1
#include "debug.h"
typedef void *(*NPW_MemAllocProcPtr) (uint32_t);
typedef void (*NPW_MemFreeProcPtr) (void *, uint32_t);
typedef struct _NPW_MallocHooks NPW_MallocHooks;
struct _NPW_MallocHooks
{
NPW_MemAllocProcPtr memalloc;
NPW_MemAllocProcPtr memalloc0;
NPW_MemFreeProcPtr memfree;
};
#define NPW_MALLOC_MAGIC 0x4e50574d /* 'NPWM' */
typedef struct _NPW_MemBlock NPW_MemBlock;
struct _NPW_MemBlock
{
uint32_t magic;
uint32_t real_size;
uint32_t alloc_size;
uint32_t alloc_lineno;
const char *alloc_file;
};
static void *npw_mem_alloc (uint32_t size, const char *file, int lineno);
static void *npw_mem_alloc0 (uint32_t size, const char *file, int lineno);
static void *npw_mem_alloc_copy (uint32_t size, const void *ptr, const char *file, int lineno);
static void npw_mem_free (void *ptr, const char *file, int lineno);
/* ====================================================================== */
/* === Standard C library === */
/* ====================================================================== */
#ifndef USE_MALLOC_LIBC
#define USE_MALLOC_LIBC 0
#endif
#if USE_MALLOC_LIBC
#include <stdlib.h>
static void *
NPW_Libc_MemAlloc (uint32_t size)
{
return malloc (size);
}
static void *
NPW_Libc_MemAlloc0 (uint32_t size)
{
return calloc (1, size);
}
static void
NPW_Libc_MemFree (void *ptr, uint32_t size)
{
free (ptr);
}
static const NPW_MallocHooks g_libc_hooks = {
NPW_Libc_MemAlloc,
NPW_Libc_MemAlloc0,
NPW_Libc_MemFree
};
#endif
/* ====================================================================== */
/* === Glib support === */
/* ====================================================================== */
#ifndef USE_MALLOC_GLIB
#define USE_MALLOC_GLIB 0
#endif
#if USE_MALLOC_GLIB
#include <glib.h>
static void *
NPW_Glib_MemAlloc (uint32_t size)
{
return g_slice_alloc (size);
}
static void *
NPW_Glib_MemAlloc0 (uint32_t size)
{
return g_slice_alloc0 (size);
}
static void
NPW_Glib_MemFree (void *ptr, uint32_t size)
{
g_slice_free1 (size, ptr);
}
static const NPW_MallocHooks g_glib_hooks = {
NPW_Glib_MemAlloc,
NPW_Glib_MemAlloc0,
NPW_Glib_MemFree
};
#endif
/* ====================================================================== */
/* === Public interface === */
/* ====================================================================== */
#define N_MALLOC_LIBS (USE_MALLOC_LIBC + USE_MALLOC_GLIB)
#ifndef CONCAT
#define CONCAT_(a,b) a##b
#define CONCAT(a,b) CONCAT_(a,b)
#endif
#define get_default_malloc_hooks() \
(&CONCAT(CONCAT(g_,DEFAULT_MALLOC_LIB),_hooks))
#if N_MALLOC_LIBS > 1
static const NPW_MallocHooks *
do_get_malloc_hooks (void)
{
const char *malloc_lib;
if ((malloc_lib = getenv ("NPW_MALLOC_LIB")) != NULL)
{
#if USE_MALLOC_LIBC
if (strcmp (malloc_lib, "libc") == 0)
return &g_libc_hooks;
#endif
#if USE_MALLOC_GLIB
if (strcmp (malloc_lib, "glib") == 0)
return &g_glib_hooks;
#endif
}
return get_default_malloc_hooks ();
}
static inline const NPW_MallocHooks *
get_malloc_hooks (void)
{
static const NPW_MallocHooks *malloc_hooks = NULL;
if (malloc_hooks == NULL)
malloc_hooks = do_get_malloc_hooks ();
return malloc_hooks;
}
#else
#define get_malloc_hooks() get_default_malloc_hooks()
#endif
void *
NPW_MemAlloc (uint32_t size)
{
return npw_mem_alloc (size, NULL, 0);
}
void *
NPW_MemAlloc0 (uint32_t size)
{
return npw_mem_alloc0 (size, NULL, 0);
}
void *
NPW_MemAllocCopy (uint32_t size, const void *ptr)
{
return npw_mem_alloc_copy (size, ptr, NULL, 0);
}
void
NPW_MemFree (void *ptr)
{
npw_mem_free (ptr, NULL, 0);
}
void *
NPW_Debug_MemAlloc (uint32_t size, const char *file, int lineno)
{
return npw_mem_alloc (size, file, lineno);
}
void *
NPW_Debug_MemAlloc0 (uint32_t size, const char *file, int lineno)
{
return npw_mem_alloc0 (size, file, lineno);
}
void *
NPW_Debug_MemAllocCopy (uint32_t size, const void *ptr, const char *file, int lineno)
{
return npw_mem_alloc_copy (size, ptr, file, lineno);
}
void
NPW_Debug_MemFree (void *ptr, const char *file, int lineno)
{
npw_mem_free (ptr, file, lineno);
}
/* ====================================================================== */
/* === Implementation allowing basic underflow/overflow checks === */
/* ====================================================================== */
#ifdef ENABLE_MALLOC_CHECK
static bool
is_malloc_check_enabled_1 (void)
{
const char *malloc_check_str;
if ((malloc_check_str = getenv ("NPW_MALLOC_CHECK")) != NULL)
return ((strcmp (malloc_check_str, "yes") == 0) ||
(strcmp (malloc_check_str, "1") == 0));
/* enable malloc-checks by default for all builds from snapshots */
return NPW_SNAPSHOT > 0;
}
#endif
#define MALLOC_CHECK_GUARD_MARK 'E'
#define MALLOC_CHECK_GUARD_SIZE malloc_check_guards_size ()
static inline bool
is_malloc_check_enabled (void)
{
#ifdef ENABLE_MALLOC_CHECK
static int malloc_check = -1;
if (malloc_check < 0)
malloc_check = is_malloc_check_enabled_1 ();
return malloc_check;
#else
return false;
#endif
}
static inline uint32_t
malloc_check_guards_size (void)
{
return is_malloc_check_enabled () ? 16 : 0;
}
static void
malloc_check_guards_init (uint8_t *ptr, uint32_t size)
{
if (!is_malloc_check_enabled ())
return;
memset (ptr - MALLOC_CHECK_GUARD_SIZE,
MALLOC_CHECK_GUARD_MARK,
MALLOC_CHECK_GUARD_SIZE);
memset (ptr + size,
MALLOC_CHECK_GUARD_MARK,
MALLOC_CHECK_GUARD_SIZE);
}
static bool
malloc_check_guards_ok (uint8_t *ptr, uint32_t size, int *punderflow, int *poverflow)
{
if (!is_malloc_check_enabled ())
return true;
int i, underflow = 0, overflow = 0;
for (i = 0; i < MALLOC_CHECK_GUARD_SIZE; i++)
{
if (ptr[-(1 + i)] != MALLOC_CHECK_GUARD_MARK)
++underflow;
if (ptr[size + i] != MALLOC_CHECK_GUARD_MARK)
++overflow;
}
if (punderflow)
*punderflow = underflow;
if (poverflow)
*poverflow = overflow;
return !underflow && !overflow;
}
static inline void *
npw_do_mem_alloc (NPW_MemAllocProcPtr mem_alloc_func, uint32_t size, const char *file, int lineno)
{
uint32_t real_size;
NPW_MemBlock *mem;
real_size = sizeof (*mem) + size + 2 * MALLOC_CHECK_GUARD_SIZE;
if ((mem = mem_alloc_func (real_size)) == NULL)
return NULL;
mem->magic = NPW_MALLOC_MAGIC;
mem->real_size = real_size;
mem->alloc_size = size;
mem->alloc_file = file;
mem->alloc_lineno = lineno;
uint8_t *ptr = (uint8_t *)mem + sizeof (*mem) + MALLOC_CHECK_GUARD_SIZE;
malloc_check_guards_init (ptr, size);
return ptr;
}
static void *
npw_mem_alloc (uint32_t size, const char *file, int lineno)
{
return npw_do_mem_alloc (get_malloc_hooks ()->memalloc, size, file, lineno);
}
static void *
npw_mem_alloc0 (uint32_t size, const char *file, int lineno)
{
return npw_do_mem_alloc (get_malloc_hooks ()->memalloc0, size, file, lineno);
}
static void *
npw_mem_alloc_copy (uint32_t size, const void *src, const char *file, int lineno)
{
void *ptr = npw_mem_alloc (size, file, lineno);
if (ptr)
memcpy (ptr, src, size);
return ptr;
}
static void
npw_mem_free (void *ptr, const char *file, int lineno)
{
if (ptr == NULL)
return;
NPW_MemBlock *mem = (NPW_MemBlock *)((char *)ptr - (sizeof (*mem) + MALLOC_CHECK_GUARD_SIZE));
if (mem->magic == NPW_MALLOC_MAGIC)
{
int underflow, overflow;
if (!malloc_check_guards_ok (ptr, mem->alloc_size, &underflow, &overflow))
{
if (underflow)
npw_printf ("ERROR: detected underflow of %d bytes\n"
" for block allocated at %s:%d\n"
" and released at %s:%d\n",
underflow,
mem->alloc_file, mem->alloc_lineno,
file, lineno);
if (overflow)
npw_printf ("ERROR: detected overflow of %d bytes\n"
" for block allocated at %s:%d\n"
" and released at %s:%d\n",
overflow,
mem->alloc_file, mem->alloc_lineno,
file, lineno);
}
get_malloc_hooks ()->memfree (mem, mem->real_size);
}
else
{
npw_printf("ERROR: block %p was not allocated with NPW_MemAlloc(), reverting to libc free()\n", ptr);
free (ptr);
}
}