/*
 * DUMA - Red-Zone memory allocator.
 * Copyright (C) 2006 Michael Eddington <meddington@gmail.com>
 * Copyright (C) 2002-2009 Hayati Ayguen <h_ayguen@web.de>, Procitec GmbH
 * Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>
 * License: GNU GPL (GNU General Public License, see COPYING-GPL)
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 * FILE CONTENTS:
 * --------------
 * This is a special version of malloc() and company for debugging software
 * that is suspected of overrunning or underrunning the boundaries of a
 * malloc buffer, or touching free memory.
 *
 * It arranges for each malloc buffer to be followed (or preceded)
 * in the address space by an inaccessable virtual memory page,
 * and for free memory to be inaccessable. If software touches the
 * inaccessable page, it will get an immediate segmentation
 * fault. It is then trivial to uncover the offending code using a debugger.
 *
 * An advantage of this product over most malloc debuggers is that this one
 * detects reading out of bounds as well as writing, and this one stops on
 * the exact instruction that causes the error, rather than waiting until the
 * next boundary check.
 *
 * There is one product that debugs malloc buffer overruns
 * better than DUMA: "Purify" from Purify Systems, and that's only
 * a small part of what Purify does. I'm not affiliated with Purify, I just
 * respect a job well done.
 *
 * This version of malloc() should not be linked into production software,
 * since it tremendously increases the time and memory overhead of malloc().
 * Each malloc buffer will consume a minimum of two virtual memory pages,
 * this is 16 kilobytes on many systems. On some systems it will be necessary
 * to increase the amount of swap space in order to debug large programs that
 * perform lots of allocation, because of the per-buffer overhead.
 */

#ifndef DUMA_NO_DUMA

#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <memory.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <stdarg.h>

#ifndef WIN32
  #include <unistd.h>
  #include <fcntl.h>
  #include <sys/mman.h>
  #include <sys/types.h>
#else
  #define WIN32_LEAN_AND_MEAN 1
  #include <windows.h>
  #include <winbase.h>
  #include <io.h>

#ifndef __CYGWIN__
  /* already defined in cygwin headers */
  typedef LPVOID caddr_t;
  typedef unsigned u_int;
#endif

#endif

#ifdef _MSC_VER
  #include <crtdbg.h>
#endif

#include "duma.h"
#include "noduma.h"
#include "print.h"
#include "duma_sem.h"
#include "paging.h"
#include "verinfo.h"

#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__)
DUMA_EXTERN_C void StackTraceCleanup();
DUMA_EXTERN_C void printStackTrace(char* buffer, int bufferSize, char* mapFilename);
#endif

static const char  version[] =
#ifndef GIT_SOURCE_VERSION
"DUMA ("
#else
GIT_SOURCE_VERSION
#endif
#ifdef DUMA_SO_LIBRARY
"shared library"
#elif DUMA_DLL_LIBRARY
"DLL library"
#elif DUMA_DETOURS
"detours"
#else
"static library"
#endif
#ifdef DUMA_NO_GLOBAL_MALLOC_FREE
", NO_GLOBAL_MALLOC_FREE"
#endif
#ifdef DUMA_NO_THREAD_SAFETY
", NO_THREAD_SAFETY"
#endif
#ifdef DUMA_NO_CPP_SUPPORT
", NO_CPP_SUPPORT"
#endif
#ifdef DUMA_NO_LEAKDETECTION
", NO_LEAKDETECTION"
#endif
#ifdef DUMA_EXPLICIT_INIT
", EXPLICIT_INIT"
#endif
#ifdef DUMA_PREFER_ATEXIT
", PREFER_ATEXIT"
#endif
#ifdef DUMA_PREFER_GETENV
", PREFER_GETENV"
#endif
")\n"
"Copyright (C) 2006 Michael Eddington <meddington@gmail.com>\n"
"Copyright (C) 2002-2009 Hayati Ayguen <h_ayguen@web.de>, Procitec GmbH\n"
"Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>\n\n";


static const char unknown_file[] =
  "UNKNOWN (use #include \"duma.h\")";


#ifndef DUMA_NO_LEAKDETECTION
#define DUMA_PARAMLIST_FL       , const char * filename, int lineno
#define DUMA_PARAMS_FL          , filename, lineno
#define DUMA_PARAMS_UK          , unknown_file, 0
#else
#define DUMA_PARAMLIST_FL
#define DUMA_PARAMS_FL
#define DUMA_PARAMS_UK
#endif

#ifndef DUMA_PREFER_GETENV
#define DUMA_GETENV     duma_getenv
#else
#define DUMA_GETENV     getenv
#endif


/* Variable: MEMORY_CREATION_SIZE
 *
 * MEMORY_CREATION_SIZE is the amount of memory to get from the operating
 * system at one time. We'll break that memory down into smaller pieces for
 * malloc buffers. One megabyte is probably a good value.
 */
#define      MEMORY_CREATION_SIZE  1024 * 1024

/* Enum: _DUMA_SlotState
 *
 * State of slot values (empty, free, etc)
 */
enum _DUMA_SlotState
{
	DUMAST_EMPTY            /* slot not in use */
  , DUMAST_FREE             /* internal memory reserved, unused by user */
  , DUMAST_IN_USE           /* memory in use by allocator; see following enum AllocType */
  , DUMAST_ALL_PROTECTED    /* memory no more used by allocator; memory is not deallocated but protected */
  , DUMAST_BEGIN_PROTECTED  /* most memory deallocated, but not page covering userAddress:
							 * slot holds userAddress, userSize and allocator.
							 */
};

enum _DUMA_Slot_FileSource
{
	DUMAFS_EMPTY            /* no filename, lineno */
  , DUMAFS_ALLOCATION       /* filename, lineno from allocation */
  , DUMAFS_DEALLOCATION     /* filename, lineno from deallocation */
};

enum _DUMA_InitState
{
	DUMAIS_UNINITIALIZED = 0x1611  /* not initialized */
  , DUMAIS_IN_CONSTRUCTOR   /* in constructor _duma_init() */
  , DUMAIS_OUT_CONSTRUCTOR  /* construction _duma_init() finished */
  , DUMAIS_IN_INIT          /* in initializer duma_init() */
  , DUMAIS_OUT_INIT         /* initialization duma_init() finished */
};

/*
 * Struct Slot contains all of the information about a malloc buffer except
 * for the contents of its memory.
 */
struct _DUMA_Slot
{
  void            * internalAddress;
  void            * userAddress;
  void            * protAddress;
  size_t            internalSize;
  size_t            userSize;

  /* save (some) space in production */
  unsigned short    state       :16;
  #ifdef DUMA_NO_LEAKDETECTION
  unsigned short    allocator   :16;
  #else
  unsigned short    allocator   :8;
  unsigned short    fileSource  :8;
  #endif

#ifndef DUMA_NO_LEAKDETECTION
  char            * filename;   /* filename of allocation */
  int               lineno;     /* linenumber of allocation
								 * -1 == memory was allocated before duma_init()
								 *  0 == no leak information present
								 * >0 == leak information present
								 */
#endif

/* Feature currently only works on win32 */
#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__)
  char            * stacktrace; /* stacktrace of allocation */
#endif

#ifdef DUMA_EXPLICIT_INIT
  int               slackfill;
#endif
};

enum _DUMA_AllocType
{
	DUMAAT_INTERNAL
  , DUMAAT_MALLOC
  , DUMAAT_NEW_ELEM
  , DUMAAT_NEW_ARRAY
  , DUMAAT_MEMBER_NEW_ELEM
  , DUMAAT_MEMBER_NEW_ARRAY
};

enum _DUMA_AllocStd
{
	DUMAAS_C
  , DUMAAS_CPP
};

static const struct _DUMA_AllocDesc
{
  char                * name;
  enum _DUMA_AllocType  type;
  enum _DUMA_AllocStd   std;
}
_duma_allocDesc[] =
{
	{ "duma allocate()"       , DUMAAT_INTERNAL        , DUMAAS_C   }
  , { "duma deallocate()"     , DUMAAT_INTERNAL        , DUMAAS_C   }
  , { "malloc()"              , DUMAAT_MALLOC          , DUMAAS_C   }
  , { "calloc()"              , DUMAAT_MALLOC          , DUMAAS_C   }
  , { "free()"                , DUMAAT_MALLOC          , DUMAAS_C   }
  , { "memalign()"            , DUMAAT_MALLOC          , DUMAAS_C   }
  , { "posix_memalign()"      , DUMAAT_MALLOC          , DUMAAS_C   }
  , { "realloc()"             , DUMAAT_MALLOC          , DUMAAS_C   }
  , { "valloc()"              , DUMAAT_MALLOC          , DUMAAS_C   }
  , { "strdup()"              , DUMAAT_MALLOC          , DUMAAS_C   }
  , { "scalar new"            , DUMAAT_NEW_ELEM        , DUMAAS_CPP }
  , { "scalar delete"         , DUMAAT_NEW_ELEM        , DUMAAS_CPP }
  , { "vector new[]"          , DUMAAT_NEW_ARRAY       , DUMAAS_CPP }
  , { "vector delete[]"       , DUMAAT_NEW_ARRAY       , DUMAAS_CPP }
  , { "member scalar new"     , DUMAAT_MEMBER_NEW_ELEM , DUMAAS_CPP }
  , { "member scalar delete"  , DUMAAT_MEMBER_NEW_ELEM , DUMAAS_CPP }
  , { "member vector new[]"   , DUMAAT_MEMBER_NEW_ARRAY, DUMAAS_CPP }
  , { "member vector delete[]", DUMAAT_MEMBER_NEW_ARRAY, DUMAAS_CPP }
};

#ifdef DUMA_EXPLICIT_INIT
#define IF__DUMA_INIT_DONE if (DUMAIS_OUT_INIT == _duma_s.init_state)
#else
#define IF__DUMA_INIT_DONE
#endif


/* Collection of all global static non const variables for DUMA */

static struct _DUMA_GlobalStaticVars
{
  /* Protection Space A */
  char  acSpaceA[2 * DUMA_PAGE_SIZE];

  int   DUMA_IN_DUMA;

  /* Variable: DUMA_DISABLE_BANNER
   *
   * DUMA_DISABLE_BANNER is a global variable used to control whether DUMA prints
   * its usual startup message. Default is 0, meaning that the startup message
   * gets printed.
   */
  int   DISABLE_BANNER;

  /* Variable: DUMA_SKIPCOUNT_INIT
   *
   * DUMA_SKIPCOUNT_INIT controls after how many DUMA allocations the full internal
   * initialization is done. Default is 0.
   */
  int   SKIPCOUNT_INIT;


  /* Variable: CHECK_FREQ
   *
   * DUMA_CHECK_FREQ controls the frequency to check all memory blocks no man's land.
   * The frequency counter is incremented at each memory allocation and deallocation.
   * Whenever the counter reaches the value of DUMA_CHECK_FREQ the check is performed.
   * 0 means no checks. 1 means to check always. Be careful with this value, it may
   * get very time consuming.
   * Default is 0.
   */
  int   CHECK_FREQ;


  /* Variable: DUMA_REPORT_ALL_LEAKS
   *
   * DUMA_REPORT_ALL_LEAKS is a global variable used to control whether DUMA should
   * all leaks - even without source filename/line number. Default is 0, meaning that
   * only leaks with source information will get reported.
   */
  int   REPORT_ALL_LEAKS;

  /* Variable: DUMA_SLACKFILL
   *
   * DUMA_SLACKFILL is set to 0-255. The slack / no mans land of all new allocated
   * memory is filled with the specified value.
   * default is set to initialise with 0xAA (=binary 10101010)
   * initialisation to 0!
   */
  int   SLACKFILL;

  /* Variable: DUMA_PROTECT_FREE
   *
   * DUMA_PROTECT_FREE is used to control the disposition of memory that is
   * released using free(). It is all-caps so that its name
   * matches the name of the environment variable that is used to set it.
   * If its value is non-zero, memory released by free is made inaccessable.
   * Any software that touches free memory will then get a segmentation fault.
   * Depending on your application and your resources you may tell
   * DUMA not to use this memory ever again by setting a negative
   * value f.e. -1.
   * You can tell DUMA to limit the sum of protected memory by setting
   * a positive value, which is interpreted in kB.
   * If its value is zero, freed memory will be available for reallocation,
   * but will still be inaccessable until it is reallocated.
   */
  long  PROTECT_FREE;

  /* Variable: DUMA_MAX_ALLOC
   *
   * DUMA_MAX_ALLOC is used to control the maximum memory print of the program
   * in total: When the sum of allocated and protected memory would exceed
   * this value in kB, the protected memory is freed/deleted.
   */
  long  MAX_ALLOC;

  /* Variable: DUMA_MALLOC_0_STRATEGY
   *
   * DUMA_MALLOC_0_STRATEGY how DUMA should behave on malloc(0).
   *   0 - like having former ALLOW_MALLOC_0 = 0  ==> abort program with segfault
   *   1 - return NULL pointer
   *   2 - return always the same pointer to some protected page
   *   3 - return unique protected page (=default)
   * ATTENTION: only 1 and 3 are ANSI conform. But value 1 will break most programs,
   *   cause value 3 is the usual one, the system libraries implement
   */
  int   MALLOC_0_STRATEGY;

  /* Variable: DUMA_NEW_0_STRATEGY
   *
   * DUMA_NEW_0_STRATEGY how DUMA should behave on C++ operator new or new[]
   *   with size 0
   *   2 - return always the same pointer to some protected page
   *   3 - return unique protected page (=default)
   * ATTENTION: only 3 is standard conform. Value 2 may break some but will
   * work for most programs. With value 2 you may reduce the memory consumption.
   */
  int   NEW_0_STRATEGY;

  /* Variable: DUMA_MALLOC_FAILEXIT
   *
   * DUMA_MALLOC_FAILEXIT controls the behaviour of DUMA when
   * malloc() fails and would return NULL. But most applications don't
   * check the return value for errors ... so
   * default to Exit on Fail
   */
  int   MALLOC_FAILEXIT;

  /* Variable: DUMA_FREE_ACCESS
   *
   * DUMA_FREE_ACCESS is set if DUMA is to write access memory before
   * freeing it. This makes easier using watch expressions in debuggers as the
   * process is interrupted even if the memory is going to be freed.
   */
  int   FREE_ACCESS;

  /* Variable: DUMA_SHOW_ALLOC
   *
   * DUMA_SHOW_ALLOC is set if DUMA is to print all allocations
   * and deallocations to the console. Although this generates a lot
   * of messages, the option can be useful to detect inefficient code
   * containing many allocations / deallocations
   */
  int   SHOW_ALLOC;

  /* Variable: DUMA_SUPPRESS_ATEXIT
   *
   * DUMA_SUPPRESS_ATEXIT is set if DUMA is to suppress the installation of
   * an exit handler, called at the exit of the main program. This handler allows for
   * the detection of memory areas that have not been freed correctly before
   * program exit, so the handler's installation should *normally* not be
   * suppressed. One reason for doing so regardless are some buggy environments,
   * where calls to the atexit()-function hang.
   */
  int   SUPPRESS_ATEXIT;

  /* Variable: DUMA_MEMCPY_OVERLAP
   *
   * DUMA_MEMCPY_OVERLAP is set if the platform memcpy implementation allows copying
   * overlapping regions when source > destination.
   */
  int   MEMCPY_OVERLAP;


  /* Variable: _duma_allocListSize
   *
   * _duma_allocListSize is the size of the allocation list. This will always
   * be a multiple of the page size.
   */
  size_t  allocListSize;

  /* Variable: slotCount
   *
   * slotCount is the number of Slot structures in allocationList.
   */
  size_t  slotCount;

  /* Variable: unUsedSlots
   *
   * unUsedSlots is the number of Slot structures that are currently available
   * to represent new malloc buffers. When this number gets too low, we will
   * create new slots.
   */
  size_t  unUsedSlots;

  /* Variable: slotsPerPage
   *
   * slotsPerPage is the number of slot structures that fit in a virtual
   * memory page.
   */
  size_t  slotsPerPage;

  /* Variable: sumAllocatedMem
   *
   * internal variable: sum of allocated -freed +protected memory in kB
   */
  long    sumAllocatedMem;

  /* Variable: sumTotalAllocatedMem
   *
   * internal variable: sum of allocated memory in kB
   */
  long    sumTotalAllocatedMem;

  /* Variable: sumProtectedMem
   *
   * internal variable: sum of protected memory in kB
   */
  long    sumProtectedMem;

  /* Variable: numDeallocs
   *
   * internal variable: number of deallocations processed so far
   */
  long    numDeallocs;

  /* Variable: numAllocs
   *
   * internal variable: number of allocations processed so far
   */
  long    numAllocs;


  /* Variable checkFreqCounter
   *
   * number of (de)allocations since last checks
   */
  int     checkFreqCounter;

  /* Variable: duma_init_state
   *
   * internal variable: state of initialization
   */
  enum _DUMA_InitState  init_state;

  /* memory block for malloc() or new with size 0 */
  void *  null_block;

  /* Protection Space B */
  char  acSpaceB[2 * DUMA_PAGE_SIZE];
}

_duma_s =

{
   "Static Protection Space Front"   /* Protection Space A */

  , 0       /* int DUMA_IN_DUMA; */

  , 0       /* Variable: DISABLE_BANNER */
  , 0       /* Variable: SKIPCOUNT_INIT */
  , 0       /* Variable: CHECK_FREQ */
  , 0       /* Variable: REPORT_ALL_LEAKS */
  , 0xAA    /* Variable: SLACKFILL */
  , -1L     /* Variable: PROTECT_FREE */
  , -1L     /* Variable: MAX_ALLOC */
  , 3       /* Variable: MALLOC_0_STRATEGY; see above */
  , 3       /* Variable: NEW_0_STRATEGY; see above */
  , 1       /* Variable: MALLOC_FAILEXIT */
  , 0       /* Variable: FREE_ACCESS */
  , 0       /* Variable: SHOW_ALLOC */
  , 0       /* Variable: SUPPRESS_ATEXIT */
  , 0       /* Variable: MEMCPY_OVERLAP */

  , 0       /* Variable: allocListSize */
  , 0       /* Variable: slotCount */
  , 0       /* Variable: unUsedSlots */
  , 0       /* Variable: slotsPerPage */
  , 0L      /* Variable: sumAllocatedMem */
  , 0L      /* Variable: sumTotalAllocatedMem */
  , 0L      /* Variable: sumProtectedMem */
  , 0L      /* Variable: numDeallocs */
  , 0L      /* Variable: numAllocs */
  , 0       /* Variable: checkFreqCounter */
  , DUMAIS_UNINITIALIZED  /* Variable: duma_init_done */
  , (void *)0 /* Variable: null_block */

  , "Static Protection Space Back"   /* Protection Space B */
};



DUMA_GLOBALVARS_T _duma_g =
{
   "Global Protection Space Front"   /* Protection Space A */

  , (void*)0    /* Variable: allocList */
  , (void*)0    /* Variable: null_addr */

  , {   DUMA_MIN_ALIGNMENT
	  , 0     /* PROTECT_BELOW */
	  , 255   /* FILL */
	  #if !defined(DUMA_NO_CPP_SUPPORT) && !defined(DUMA_NO_LEAKDETECTION)
		, 0     /* Magic */
		, 0     /* DelPtr */
		, { 0 }
		, { 0 }
	  #endif
	}

  , "Global Protection Space Back"   /* Protection Space B */
};



/* Variable: DUMA_OUTPUT_DEBUG
 *
 * DUMA_OUTPUT_DEBUG is a global variable used to control if DUMA
 * output is printed to the win32 debugging console.  Default is 0,
 * meaning that output is not by default sent to the debugging console.
 *
 * OS: WIN32 Only
 */
int DUMA_OUTPUT_DEBUG = 0;

/* Variable: DUMA_OUTPUT_STDOUT
 *
 * DUMA_OUTPUT_STDOUT is a global variable used to control if DUMA
 * output is printed to STDOUT.  Default is 0,
 * meaning that output is not by default sent to STDOUT.
 */
int DUMA_OUTPUT_STDOUT = 0;

/* Variable: DUMA_OUTPUT_STDERR
 *
 * DUMA_OUTPUT_STDERR is a global variable used to control if DUMA
 * output is printed to STDERR.  Default is 1,
 * meaning that output is by default sent to STDERR.
 */
int DUMA_OUTPUT_STDERR = 1;

/* Variable: DUMA_OUTPUT_FILE
 *
 * DUMA_OUTPUT_FILE is a global variable used to control if DUMA
 * output is printed to a specified file.  Default is NULL,
 * meaning that output is not by default sent to a file.
 */
char* DUMA_OUTPUT_FILE = NULL;
/* char* DUMA_OUTPUT_FILE = "c:\\duma.txt"; */

/* Variable: DUMA_OUTPUT_STACKTRACE
 *
 * DUMA_OUTPUT_STACKTRACE is a global variable used to control if DUMA
 * outputs a stacktrace of the allocation that is not free'd. Default is 0,
 * meaning that this option is disabled.
 *
 * OS: WIN32 Only
 */
int DUMA_OUTPUT_STACKTRACE = 0;

/* Variable: DUMA_OUTPUT_STACKTRACE_MAPFILE
 *
 * DUMA_OUTPUT_STACKTRACE_MAPFILE is a global variable used to control
 * what mapfile is used for stack traces.  This is needed when using
 * detours and duma.  Default is NULL, indicating the system will try
 * and guess.
 *
 * OS: WIN32 Only
 */
char* DUMA_OUTPUT_STACKTRACE_MAPFILE = NULL;



/*
 * include helper functions
 */
#include "duma_hlp.h"

#ifndef DUMA_NO_LEAKDETECTION
/*
 * declare exit function
 */
void
#if ( defined(DUMA_GNU_INIT_ATTR) && !defined(DUMA_PREFER_ATEXIT) )
__attribute ((destructor))
#endif
_duma_exit(void);
#endif


/* Function: _duma_assert
 *
 * Print message and halt program execution in crazy way.
 */
void _duma_assert(const char * exprstr, const char * filename, int lineno)
{
  int *pcAddr = 0;
  DUMA_Print("\nDUMA: DUMA_ASSERT(%s) failed at\n%s(%i)\n", exprstr, filename, lineno );

  /* this is "really" bad, but it works. assert() from assert.h system header
   * stops only the current thread but the program goes on running under MS Visual C++.
   * This way the program definitely halts.
   */
  while (1)
	*pcAddr++ = 0;

}

#ifndef DUMA_PREFER_GETENV

extern char **environ;

/* Function: duma_getenv
 *
 * replacement for standard C library function
 */
static
const char * duma_getenv( const char * varname )
{
  const char * ret = NULL;
  int varno = 0;

  if ( !varname )
	return ret;

  if ( varname[0] == '\0' )
	return ret;

  while ( environ[varno] )
  {
	const char * v = environ[varno++];
	int idx = 0;

	while ( varname[idx] != '\0' && v[idx] == varname[idx] )
	  ++idx;

	if ( idx > 0 && varname[idx] == '\0' && v[idx] == '=' )
	  return v + (idx +1);
  }
  return ret;
}

#endif


static
void duma_getenvvars( DUMA_TLSVARS_T * duma_tls )
{
  const char * string;

  /*
   * Import the user's environment specification of the default
   * alignment for malloc(). We want that alignment to be under
   * user control, since smaller alignment lets us catch more bugs,
   * however some software will break if malloc() returns a buffer
   * that is not word-aligned.
   *
   * I would like
   * alignment to be zero so that we could catch all one-byte
   * overruns, however if malloc() is asked to allocate an odd-size
   * buffer and returns an address that is not word-aligned, or whose
   * size is not a multiple of the word size, software breaks.
   * This was the case with the Sun string-handling routines,
   * which can do word fetches up to three bytes beyond the end of a
   * string. I handle this problem in part by providing
   * byte-reference-only versions of the string library functions, but
   * there are other functions that break, too. Some in X Windows, one
   * in Sam Leffler's TIFF library, and doubtless many others.
   */
  if ( (string = DUMA_GETENV("DUMA_ALIGNMENT")) != 0 )
  {
	duma_tls->ALIGNMENT = (size_t)atoi(string);
	/* we could check for DUMA_MIN_ALIGNMENT. should we do so? */
	if (!duma_tls->ALIGNMENT)
	  duma_tls->ALIGNMENT = 1;
  }

  /*
   * See if the user wants to protect the address space below a buffer,
   * rather than that above a buffer.
   */
  if ( (string = DUMA_GETENV("DUMA_PROTECT_BELOW")) != 0 )
	duma_tls->PROTECT_BELOW = (atoi(string) != 0);

  /* Should we report all leaks? */
  if ( (string = DUMA_GETENV("DUMA_REPORT_ALL_LEAKS")) != 0 )
	_duma_s.REPORT_ALL_LEAKS = (atoi(string) != 0);

  /*
   * See if the user wants to protect memory that has been freed until
   * the program exits, rather than until it is re-allocated.
   * =-1 protect as much free'd memory as possible
   * =0 do not protect free'd memory
   * =N protect memory up to N kB
   */
  if ( (string = DUMA_GETENV("DUMA_PROTECT_FREE")) != 0 )
	_duma_s.PROTECT_FREE = atol(string);

  /*
   * See if the user has a memory usage limit. This controls the maximum
   * memory print of the program in total: The sum of allocated and protected
   * memory won't exceed this limit.
   * =-1 use as much memory as possible
   * =N limit total memory usage to N kB
   */
  if ( (string = DUMA_GETENV("DUMA_MAX_ALLOC")) != 0 )
	_duma_s.MAX_ALLOC = atol(string);

  /*
   * See what strategy the user wants for malloc(0).
   */
  if ( (string = DUMA_GETENV("MALLOC_0_STRATEGY")) != 0 )
  {
	int tmp = atoi(string);
	if ( tmp >= 0 && tmp <= 3 )
	  _duma_s.MALLOC_0_STRATEGY = tmp;
  }

  /*
   * See what strategy the user wants for C++ operator new with size zero.
   */
  if ( (string = DUMA_GETENV("NEW_0_STRATEGY")) != 0 )
  {
	int tmp = atoi(string);
	if ( tmp >= 2 && tmp <= 3 )
	  _duma_s.NEW_0_STRATEGY = tmp;
  }

  /*
   * See if the user wants to exit on malloc() failure
   */
  if ( (string = DUMA_GETENV("DUMA_MALLOC_FAILEXIT")) != 0 )
	_duma_s.MALLOC_FAILEXIT = (atoi(string) != 0);

  /*
   * See if the user wants to write access freed memory
   */
  if ( (string = DUMA_GETENV("DUMA_FREE_ACCESS")) != 0 )
	_duma_s.FREE_ACCESS = (atoi(string) != 0);

  /*
   * Check if we should be filling new memory with a value.
   */
  if ( (string = DUMA_GETENV("DUMA_FILL")) != 0)
  {
	duma_tls->FILL = atoi(string);
	if ( -1 != duma_tls->FILL )
	  duma_tls->FILL &= 255;
  }

  /*
   * Check with which value the memories no mans land is filled
   */
  if ( (string = DUMA_GETENV("DUMA_SLACKFILL")) != 0)
	_duma_s.SLACKFILL = atoi(string);
  _duma_s.SLACKFILL &= 255;

  /*
   * See if the user wants to see allocations / frees
   */
  if ( (string = DUMA_GETENV("DUMA_SHOW_ALLOC")) != 0 )
	_duma_s.SHOW_ALLOC = (atoi(string) != 0);

  /*
   * See if the user wants to call atexit()
   */
  if ( (string = DUMA_GETENV("DUMA_SUPPRESS_ATEXIT")) != 0 )
	_duma_s.SUPPRESS_ATEXIT = (atoi(string) != 0);

  /*
   * See if user allows memcpy region overlapping
   */
  if ( (string = DUMA_GETENV("DUMA_MEMCPY_OVERLAP")) != 0 )
	_duma_s.MEMCPY_OVERLAP = (atoi(string) != 0);

  /*
   * DUMA_OUTPUT_STACKTRACE is a global variable used to control if DUMA
   * outputs a stacktrace of the allocation that is not free'd. Default is 0,
   * meaning that this option is disabled.
   */
  if ( (string = DUMA_GETENV("DUMA_OUTPUT_STACKTRACE")) != 0 )
	DUMA_OUTPUT_STACKTRACE = (atoi(string) != 0);

  /*
   * DUMA_OUTPUT_STACKTRACE is a global variable used to control if DUMA
   * outputs a stacktrace of the allocation that is not free'd. Default is 0,
   * meaning that this option is disabled.
   */
  if ( (string = DUMA_GETENV("DUMA_OUTPUT_STACKTRACE_MAPFILE")) != 0 )
	DUMA_OUTPUT_STACKTRACE_MAPFILE = strdup(string);

  /*
   * DUMA_OUTPUT_DEBUG is a global variable used to control if DUMA
   * output is printed to the win32 debugging console.  Default is 0,
   * meaning that output is not by default sent to the debugging console.
   */
  if ( (string = DUMA_GETENV("DUMA_OUTPUT_DEBUG")) != 0 )
	DUMA_OUTPUT_DEBUG = (atoi(string) != 0);

  /*
   * DUMA_OUTPUT_STDOUT is a global variable used to control if DUMA
   * output is printed to STDOUT.  Default is 0,
   * meaning that output is not by default sent to STDOUT.
   */
  if ( (string = DUMA_GETENV("DUMA_OUTPUT_STDOUT")) != 0 )
	DUMA_OUTPUT_STDOUT = (atoi(string) != 0);

  /*
   * DUMA_OUTPUT_STDERR is a global variable used to control if DUMA
   * output is printed to STDERR.  Default is 1,
   * meaning that output is by default sent to STDERR.
   */
  if ( (string = DUMA_GETENV("DUMA_OUTPUT_STDERR")) != 0 )
	DUMA_OUTPUT_STDERR = (atoi(string) != 0);

  /*
   * DUMA_OUTPUT_FILE is a global variable used to control if DUMA
   * output is printed to a specified file.  Default is NULL,
   * meaning that output is not by default sent to a file.
   */
  if ( (string = DUMA_GETENV("DUMA_OUTPUT_FILE")) != 0 )
	DUMA_OUTPUT_FILE = strdup(string);

  /* Get Value for DUMA_SKIPCOUNT_INIT */
  if ( (string = DUMA_GETENV("DUMA_SKIPCOUNT_INIT")) != 0 )
	_duma_s.SKIPCOUNT_INIT = (atoi(string) != 0);

  /* Get Value for DUMA_CHECK_FREQ */
  if ( (string = DUMA_GETENV("DUMA_CHECK_FREQ")) != 0 )
  {
	int tmp = atoi(string);
	if ( tmp > 0 )
	_duma_s.CHECK_FREQ = tmp;
  }

  /* Should we send banner? */
  if ( (string = DUMA_GETENV("DUMA_DISABLE_BANNER")) != 0 )
	_duma_s.DISABLE_BANNER = (atoi(string) != 0);

  if ( !_duma_s.DISABLE_BANNER )
	DUMA_Print(version);
}



/* Function: duma_init
 *
 * duma_init sets configuration settings.  Can sometimes cause problems
 * when called from _duma_init.
 *
 * duma_init is called from _duma_init unless DUMA_EXPLICIT_INIT
 * is defined at compile time.
 *
 * See Also: <_duma_init>
 */
#ifndef DUMA_EXPLICIT_INIT
static
#endif
void duma_init(void)
{
  void            * testAlloc;
  DUMA_TLSVARS_T  * duma_tls;


  /* avoid double call, when initialization already in progress */
  if ( _duma_s.init_state >= DUMAIS_IN_INIT && _duma_s.init_state <= DUMAIS_OUT_INIT )
	return;
  else
  {
#if DUMA_DETOURS
	_duma_init();
#endif

	_duma_s.init_state = DUMAIS_IN_INIT;
  }

  duma_tls = GET_DUMA_TLSVARS();

  duma_getenvvars( duma_tls );




#if ( !defined(DUMA_NO_LEAKDETECTION) && ( defined(DUMA_PREFER_ATEXIT) || !defined(DUMA_GNU_INIT_ATTR) ) )
  /*
   * Register atexit()
   *  a) when we have Leak Detection and atexit() is preferred over GNU_INIT_ATTR
   *  b) when we have Leak Detection and GNU_INIT_ATTR is not set
   */

  #ifndef DUMA_NO_HANG_MSG
	if (0 == _duma_s.SUPPRESS_ATEXIT)
	  DUMA_Print("\nDUMA: Registering with atexit().\n"
	#ifdef WIN32
				 "DUMA: If this hangs, change the library initialization order with DUMA_EXPLICIT_INIT.\n");
	#else
				 "DUMA: If this hangs, change the library load/init order with DUMA_EXPLICIT_INIT or LD_PRELOAD.\n");
	#endif
	else
	  DUMA_Print("\nDUMA: Skipping registering with atexit(). Set DUMA_SUPPRESS_ATEXIT to 0 to register.\n");
  #endif /* DUMA_NO_HANG_MSG */

  if (!_duma_s.SUPPRESS_ATEXIT)
  {
	if ( atexit( _duma_exit ) )
	  DUMA_Abort("Cannot register exit function.\n");

	#ifndef DUMA_NO_HANG_MSG
	  DUMA_Print("DUMA: Registration was successful.\n");
	#endif /* DUMA_NO_HANG_MSG */
  }

#endif /* ( !defined(DUMA_NO_LEAKDETECTION) && ( defined(DUMA_PREFER_ATEXIT) || !defined(DUMA_GNU_INIT_ATTR) ) ) */

  /* initialize semaphoring */
  DUMA_INIT_SEMAPHORE();

#ifndef DUMA_NO_GLOBAL_MALLOC_FREE
  /*
   * Check whether malloc and free is available
   */
  testAlloc = malloc(123);
  if (_duma_s.numAllocs == 0)
	DUMA_Abort("malloc() is not bound to duma.\nDUMA Aborting: Preload lib with 'LD_PRELOAD=libduma.so <prog>'.\n");

  free(testAlloc);
  if (_duma_s.numDeallocs == 0)
	DUMA_Abort("free() is not bound to duma.\nDUMA Aborting: Preload lib with 'LD_PRELOAD=libduma.so <prog>'.\n");
#endif

  /* initialization finished */
  _duma_s.init_state = DUMAIS_OUT_INIT;
}


/* Function: _duma_init
 *
 * _duma_init sets up the memory allocation arena and the run-time
 * configuration information.  We will call duma_init unless DUMA_EXPLICIT_INIT
 * is defined at compile time.
 *
 * See Also: <duma_init>
 */
void
#ifdef DUMA_GNU_INIT_ATTR
__attribute ((constructor))
#endif
_duma_init(void)
{
  size_t size = MEMORY_CREATION_SIZE;
  struct _DUMA_Slot * slot;
  int               inRecursion = (_duma_s.init_state >= DUMAIS_IN_CONSTRUCTOR && _duma_s.init_state <= DUMAIS_OUT_INIT);

  /* constuction already done? this should not happen! */
  if (_duma_s.init_state >= DUMAIS_OUT_CONSTRUCTOR && _duma_s.init_state <= DUMAIS_OUT_INIT)
  {
#ifndef DUMA_EXPLICIT_INIT
	goto duma_constructor_callinit;
#else
	return;
#endif
  }
  else
	_duma_s.init_state = DUMAIS_IN_CONSTRUCTOR;

  if ( DUMA_PAGE_SIZE != Page_Size() )
	DUMA_Abort("DUMA_PAGE_SIZE is not correct. Run createconf and save results as duma_config.h");

  if(!inRecursion)
	DUMA_GET_SEMAPHORE();

  /* call of DUMA_GET_SEMAPHORE() may already have done the construction recursively! */
  if ( _duma_s.init_state >= DUMAIS_OUT_CONSTRUCTOR )
	goto duma_constructor_relsem;

  /*
   * Allocate special memory for malloc() or C++ operator new, when size is 0
   */
  _duma_s.null_block = Page_Create(2*DUMA_PAGE_SIZE, 1/*=exitonfail*/, 1/*=printerror*/);
  if ( NULL == _duma_s.null_block)
  {
	DUMA_Abort (" MMAP failed for null block creation in init \n");
  }

  Page_DenyAccess(_duma_s.null_block, 2*DUMA_PAGE_SIZE);
	_duma_g.null_addr  = (void*)( (DUMA_ADDR)_duma_s.null_block + DUMA_PAGE_SIZE );

  /*
   * Figure out how many Slot structures to allocate at one time.
   */
  _duma_s.slotCount = _duma_s.slotsPerPage = DUMA_PAGE_SIZE / sizeof(struct _DUMA_Slot);
  _duma_s.allocListSize = DUMA_PAGE_SIZE;

  if ( size < _duma_s.allocListSize )
	size = _duma_s.allocListSize;

  size = ( size + DUMA_PAGE_SIZE -1 ) & ~( DUMA_PAGE_SIZE -1 );

  /*
   * Allocate memory, and break it up into two malloc buffers. The
   * first buffer will be used for Slot structures, the second will
   * be marked free.
   */
  slot = _duma_g.allocList = (struct _DUMA_Slot *)Page_Create(size, 0/*=exitonfail*/, 0/*=printerror*/);
  if ( 0 == _duma_g.allocList  &&  0L != _duma_s.PROTECT_FREE )
  {
	int reduce_more;
	do
	{
	  /* reduce as much protected memory as we need - or at least try so */
	  reduce_more = reduceProtectedMemory( (size+1023) >>10 );
	  /* simply try again */
	  slot = _duma_g.allocList = (struct _DUMA_Slot *)Page_Create( size, 0/*=exitonfail*/, 0/*= printerror*/ );
	}
	while ( reduce_more && 0 == _duma_g.allocList );

	if ( 0 == _duma_g.allocList )
	  slot = _duma_g.allocList = (struct _DUMA_Slot *)Page_Create( size, 1/*=exitonfail*/, 1/*= printerror*/ );
  }

  memset((char *)_duma_g.allocList, 0, _duma_s.allocListSize);

  /* enter _duma_g.allocList as slot to allow call to free() when doing allocateMoreSlots() */
  slot[0].internalAddress   = slot[0].userAddress = _duma_g.allocList;
  slot[0].internalSize      = slot[0].userSize    = _duma_s.allocListSize;
  slot[0].state             = DUMAST_IN_USE;
  slot[0].allocator         = EFA_INT_ALLOC;
#ifndef DUMA_NO_LEAKDETECTION
  slot[0].fileSource        = DUMAFS_ALLOCATION;
  slot[0].filename          = __FILE__;
  slot[0].lineno            = __LINE__;
#endif

  if ( size > _duma_s.allocListSize )
  {
	slot[1].internalAddress = slot[1].userAddress
		= ((char *)slot[0].internalAddress) + slot[0].internalSize;
	slot[1].internalSize    = slot[1].userSize
		=   size - slot[0].internalSize;
	slot[1].state           = DUMAST_FREE;
	slot[1].allocator       = EFA_INT_ALLOC;
  #ifndef DUMA_NO_LEAKDETECTION
	slot[1].fileSource      = DUMAFS_ALLOCATION;
	slot[1].filename        = __FILE__;
	slot[1].lineno          = __LINE__;
  #endif
  }

  /*
   * Deny access to the free page, so that we will detect any software
   * that treads upon free memory.
   */
  Page_DenyAccess(slot[1].internalAddress, slot[1].internalSize);

  /*
   * Account for the two slot structures that we've used.
   */
  _duma_s.unUsedSlots = _duma_s.slotCount - 2;

  /* construction done */
  if ( _duma_s.init_state < DUMAIS_OUT_CONSTRUCTOR )
	_duma_s.init_state = DUMAIS_OUT_CONSTRUCTOR;

  duma_constructor_relsem:
  /***********************/

  if ( !inRecursion )
	DUMA_RELEASE_SEMAPHORE(0);

#ifndef DUMA_EXPLICIT_INIT
  duma_constructor_callinit:
  /*************************/
  if ( _duma_s.init_state < DUMAIS_OUT_INIT )
	duma_init();
#elif 0
  /* this output produces other problems !!! */
  DUMA_Print("\nDUMA: This platform needs an explicit call of duma_init() (DUMA_EXPLICIT_INIT).");
  DUMA_Print("\n  Take care that duma_init() is called early in main()!\n\n");
#endif
}


/* Function: allocateMoreSlots
 *
 * allocateMoreSlots is called when there are only enough slot structures
 * left to support the allocation of a single malloc buffer.
 *
 * See Also: <_duma_allocate>
 */
static void allocateMoreSlots(void)
{
  size_t  newSize = _duma_s.allocListSize + DUMA_PAGE_SIZE;
  void *  newAllocation;
  void *  oldAllocation = _duma_g.allocList;

  #ifndef DUMA_NO_LEAKDETECTION
	newAllocation = _duma_allocate( 1 /*=alignment*/
								  , newSize
								  , 0 /*=protectBelow*/
								  , -1 /*=fillByte*/
								  , 0 /*=protectAllocList*/
								  , EFA_INT_ALLOC
								  , DUMA_FAIL_NULL
								  , __FILE__, __LINE__
								  );
  #else
	newAllocation = _duma_allocate( 1 /*=alignment*/
								  , newSize
								  , 0 /*=protectBelow*/
								  , -1 /*=fillByte*/
								  , 0 /*=protectAllocList*/
								  , EFA_INT_ALLOC
								  , DUMA_FAIL_NULL
								  );
  #endif

  if ( ! newAllocation )
	return;

  memcpy(newAllocation, _duma_g.allocList, _duma_s.allocListSize);
  memset(&(((char *)newAllocation)[_duma_s.allocListSize]), 0, DUMA_PAGE_SIZE);

  _duma_g.allocList = (struct _DUMA_Slot *)newAllocation;
  _duma_s.allocListSize = newSize;
  _duma_s.slotCount   += _duma_s.slotsPerPage;
  _duma_s.unUsedSlots += _duma_s.slotsPerPage;

  #ifndef DUMA_NO_LEAKDETECTION
	_duma_deallocate( oldAllocation, 0 /*=protectAllocList*/, EFA_INT_DEALLOC, __FILE__, __LINE__ );
  #else
	_duma_deallocate( oldAllocation, 0 /*=protectAllocList*/, EFA_INT_DEALLOC);
  #endif
}


/* Function: duma_alloc_return
 *
 * set your conditional breakpoint here to catch a specific allocation
 */
void * duma_alloc_return( void * address )
{
  return address;
}


/* Function: _duma_allocate
 *
 * This is the memory allocator. When asked to allocate a buffer, allocate
 * it in such a way that the end of the buffer is followed by an inaccessable
 * memory page. If software overruns that buffer, it will touch the bad page
 * and get an immediate segmentation fault. It's then easy to zero in on the
 * offending code with a debugger.
 *
 * There are a few complications. If the user asks for an odd-sized buffer,
 * we would have to have that buffer start on an odd address if the byte after
 * the end of the buffer was to be on the inaccessable page. Unfortunately,
 * there is lots of software that asks for odd-sized buffers and then
 * requires that the returned address be word-aligned, or the size of the
 * buffer be a multiple of the word size. An example are the string-processing
 * functions on Sun systems, which do word references to the string memory
 * and may refer to memory up to three bytes beyond the end of the string.
 * For this reason, I take the alignment requests to memalign() and valloc()
 * seriously, and
 *
 * DUMA wastes lots of memory.
 *
 * See Also: <_duma_deallocate>
 */
void * _duma_allocate(size_t alignment, size_t userSize, int protectBelow, int fillByte, int protectAllocList, enum _DUMA_Allocator allocator, enum _DUMA_FailReturn fail  DUMA_PARAMLIST_FL)
{
  size_t            count;
  struct _DUMA_Slot * slot;
  struct _DUMA_Slot * fullSlot;
  struct _DUMA_Slot * emptySlots[2];
  DUMA_ADDR           intAddr, userAddr, protAddr, endAddr;
  size_t              internalSize;
  int                 allocationStrategy;
#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__)
  char        stacktrace[601];
  char*       ptrStacktrace;
#endif
  DUMA_TLSVARS_T    * duma_tls = GET_DUMA_TLSVARS();

  /* check allocation strategy to use */
  switch ( _duma_allocDesc[allocator].std )
  {
	default:
	case DUMAAS_C:
	  allocationStrategy = _duma_s.MALLOC_0_STRATEGY;
	  break;
	case DUMAAS_CPP:
	  allocationStrategy = _duma_s.NEW_0_STRATEGY;
	  break;
  }

  DUMA_ASSERT( 0 != _duma_g.allocList );

#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__)
  /* When getting the stack trace memory will be allocated
   * via DUMA.  In situations were additional slots must
   * be allocated we must do this prior to getting a pointer
   * to the new empty slot.  For this reason please leave
   * this code at the top of this function.
   */
  if(!_duma_s.DUMA_IN_DUMA && _duma_s.init_state && DUMA_OUTPUT_STACKTRACE)
  {
	_duma_s.DUMA_IN_DUMA = 1;

	printStackTrace(stacktrace, sizeof(stacktrace), DUMA_OUTPUT_STACKTRACE_MAPFILE);
	internalSize = strlen(stacktrace) * sizeof(char) + 1;
	ptrStacktrace = (char*) LocalAlloc(NULL, internalSize);
	if ( NULL != ptrStacktrace )
		strcpy(ptrStacktrace, stacktrace);
	memset(stacktrace, 0, 600);

	_duma_s.DUMA_IN_DUMA = 0;
  }
#endif


  /* initialize return value */
  userAddr = 0;

  /* count and show allocation, if requested */
  _duma_s.numAllocs++;
  if (_duma_s.SHOW_ALLOC)
  {
	#ifndef DUMA_NO_LEAKDETECTION
	  DUMA_Print("\nDUMA: Allocating %d bytes at %s(%i).", (DUMA_SIZE)userSize, filename, lineno);
	#else
	  DUMA_Print("\nDUMA: Allocating %d bytes.", (DUMA_SIZE)userSize);
	#endif
	if ( 0 == userSize )
	  DUMA_Print(" This is ANSI conform but probably a bug. See DUMA_ALLOW_MALLOC_0.");
  }

  /* check userSize */
  if ( 0 == userSize )
  {
	switch ( allocationStrategy )
	{
	  case 0: /* like having former ALLOW_MALLOC_0 = 0  ==> abort program with segfault */
		#ifndef DUMA_NO_LEAKDETECTION
		  DUMA_Abort("Allocating 0 bytes, probably a bug at %s(%i). See DUMA_ALLOW_MALLOC_0.", filename, lineno);
		#else
		  DUMA_Abort("Allocating 0 bytes, probably a bug. See DUMA_ALLOW_MALLOC_0.");
		#endif
		return (void*)userAddr;
		break;
	  case 1: /* return NULL pointer */
		return (void*)userAddr;
		break;
	  case 2: /* return always the same pointer to some protected page */
	  default:
		userAddr = (DUMA_ADDR)_duma_g.null_addr;
		return (void*)userAddr;
		break;
	  case 3: /* return unique protected page */
		/* continue allocation! */
		break;
	} /* end switch () */

	/* only case 3 */
	internalSize = DUMA_PAGE_SIZE;
  }
  else /* if ( userSize ) */
  {
	/* check alignment */
	if ( ! alignment )
	{
	  DUMA_SIZE a = (DUMA_SIZE)duma_tls->ALIGNMENT;
	  DUMA_SIZE s = (DUMA_SIZE)userSize;

	  if ( s < a )
	  {
		/* to next lower power of 2 */
		for (a = s; a & (a-1); a &= a-1)  ;
	  }

	  alignment = (size_t)a; /* this is new alignment */
	}

	if ( (int)alignment != ((int)alignment & -(int)alignment) )
	{
	  #ifndef DUMA_NO_LEAKDETECTION
		DUMA_Abort("Alignment (=%d) is not a power of 2 requested from %s(%i)", (DUMA_SIZE)alignment, filename, lineno);
	  #else
		DUMA_Abort("Alignment (=%d) is not a power of 2", (DUMA_SIZE)alignment);
	  #endif
	}

	/*
	 * If protectBelow is set, all addresses returned by malloc()
	 * and company will be page-aligned.
	 *
	 * The internal size of the buffer is rounded up to the next alignment and page-size
	 * boundary, and then we add another page's worth of memory for the dead page.
	 */
	/* a bit tricky but no modulo and no if () */
	internalSize = ( (userSize + DUMA_PAGE_SIZE -1) & ~(DUMA_PAGE_SIZE -1) ) + DUMA_PAGE_SIZE;

	if ( alignment > DUMA_PAGE_SIZE )
	  internalSize += alignment - DUMA_PAGE_SIZE;
  } /* end if ( userSize ) */

  /*
   * These will hold the addresses of two empty Slot structures, that
   * can be used to hold information for any memory I create, and any
   * memory that I mark free.
   */
  fullSlot = emptySlots[0] = emptySlots[1] = 0;

  /*
   * The internal memory used by the allocator is currently
   * inaccessable, so that errant programs won't scrawl on the
   * allocator's arena. I'll un-protect it here so that I can make
   * a new allocation. I'll re-protect it before I return.
   */
  if ( protectAllocList )
  {
	IF__DUMA_INIT_DONE
	  DUMA_GET_SEMAPHORE();

	Page_AllowAccess(_duma_g.allocList, _duma_s.allocListSize);
  }

  if ( _duma_s.CHECK_FREQ > 0 )
  {
	if ( (++ _duma_s.checkFreqCounter) == _duma_s.CHECK_FREQ )
	{
	  _duma_check_all_slacks();
	  _duma_s.checkFreqCounter = 0;
	}
  }

  /*
   * If I'm running out of empty slots, create some more before
   * I don't have enough slots left to make an allocation.
   */
  if ( DUMAAT_INTERNAL != _duma_allocDesc[allocator].type  &&  _duma_s.unUsedSlots < 7 )
	allocateMoreSlots();

  /*
   * Iterate through all of the slot structures. Attempt to find a slot
   * containing free memory of the exact right size. Accept a slot with
   * more memory than we want, if the exact right size is not available.
   * Find two slot structures that are not in use. We will need one if
   * we split a buffer into free and allocated parts, and the second if
   * we have to create new memory and mark it as free.
   *
   */
  for ( slot = _duma_g.allocList, count = _duma_s.slotCount ; count > 0; --count, ++slot )
  {
	/*
	 * Windows needs special treatment, cause Page_Delete() needs exactly
	 * the same memory region as Page_Create()!
	 * Thus as a quick hack no memory management is done by DUMA.
	 */
#if !defined(WIN32)
	if ( DUMAST_FREE == slot->state  &&  slot->internalSize >= internalSize )
	{
	  if ( !fullSlot || slot->internalSize < fullSlot->internalSize )
	  {
		fullSlot = slot;
		if ( slot->internalSize == internalSize )
		  break;  /* All done; no empty slot needed in this case */
	  }
	}
	else
#endif
	  if ( DUMAST_EMPTY == slot->state )
	  {
		if(!emptySlots[0])
		  emptySlots[0] = slot;
		else if ( !emptySlots[1] )
		  emptySlots[1] = slot;

		#if defined(WIN32)
		  break;
		#endif
	  }
  }

  if ( !fullSlot )
  {
	/*
	  * I get here if I haven't been able to find a free buffer
	  * with all of the memory I need. I'll have to create more
	  * memory. I'll mark it all as free, and then split it into
	  * free and allocated portions later.
	  */
	size_t  chunkSize;
	long    chunkSizekB;

	#if defined(WIN32)
	  chunkSize = internalSize;
	#else
	  chunkSize = MEMORY_CREATION_SIZE;

	  if ( chunkSize < internalSize )
		chunkSize = internalSize;

	  chunkSize = ( chunkSize + DUMA_PAGE_SIZE -1 ) & ~( DUMA_PAGE_SIZE -1 );
	#endif

	chunkSizekB = (long)( (chunkSize+1023) >>10 );


	/* Use up one of the empty slots to make the full slot. */
	if ( !emptySlots[0] )
	  DUMA_Abort("Internal error in allocator: No empty slot 0.\n");

	#if !defined(WIN32)
	  if ( !emptySlots[1] )
		DUMA_Abort("Internal error in allocator: No empty slot 1.\n");
	#endif

	fullSlot      = emptySlots[0];
	emptySlots[0] = emptySlots[1];

	/* reduce protected memory when we would exceed _duma_s.MAX_ALLOC */
	if ( _duma_s.MAX_ALLOC > 0L  &&  _duma_s.sumAllocatedMem + chunkSizekB > _duma_s.MAX_ALLOC )
	  reduceProtectedMemory( chunkSizekB );

	#ifndef SIZE_MAX
	#define SIZE_MAX ((size_t)(-1))
	#endif
	size_t numChunks = SIZE_MAX;
	if( userSize > (SIZE_MAX) - (chunkSize - 1) )
	  fullSlot->internalAddress = 0;
	else
	{
	  numChunks = ( userSize <= 0 ) ? 1 : ( userSize + (chunkSize - 1) )/chunkSize;
	  DUMA_ASSERT( numChunks >= 1 );
	  fullSlot->internalAddress = Page_Create( chunkSize*numChunks, 0/*= exitonfail*/, 0/*= printerror*/ );
	}

	if ( 0 == fullSlot->internalAddress  &&  0L != _duma_s.PROTECT_FREE )
	{
	  int reduce_more;
	  do
	  {
		/* reduce as much protected memory as we need - or at least try so */
		reduce_more = reduceProtectedMemory( (chunkSize+1023) >>10 );
		/* simply try again */
		fullSlot->internalAddress = Page_Create( chunkSize*numChunks, 0/*= exitonfail*/, 0/*= printerror*/ );
	  }
	  while ( reduce_more && 0 == fullSlot->internalAddress );

	  if ( 0 == fullSlot->internalAddress  &&  DUMA_FAIL_ENV == fail )
		fullSlot->internalAddress = Page_Create( chunkSize*numChunks, _duma_s.MALLOC_FAILEXIT, 1/*= printerror*/ );
	}

	if ( fullSlot->internalAddress )
	{
	  _duma_s.sumAllocatedMem       += ( (chunkSize +1023) >>10 )*numChunks;
	  _duma_s.sumTotalAllocatedMem  += ( (chunkSize +1023) >>10 )*numChunks;
	  fullSlot->internalSize  = chunkSize*numChunks;
	  fullSlot->state         = DUMAST_FREE;
	  --_duma_s.unUsedSlots;
	}
  } /* end if ( !fullSlot ) */

  if ( fullSlot->internalSize )
  {

#if !defined(WIN32)
	/*
	 * If the buffer I've found is larger than I need, split it into
	 * an allocated buffer with the exact amount of memory I need, and
	 * a free buffer containing the surplus memory.
	 */
	if ( fullSlot->internalSize > internalSize )
	{
	  /* copy and adjust contents for free slot */
	  *emptySlots[0]                 = *fullSlot;
	  emptySlots[0]->internalAddress = (char *)emptySlots[0]->internalAddress + internalSize;
	  emptySlots[0]->internalSize   -= internalSize;
	  emptySlots[0]->userAddress     = emptySlots[0]->internalAddress;
	  emptySlots[0]->userSize        = emptySlots[0]->internalSize;

	  /* adjust size of fullSlot */
	  fullSlot->internalSize         = internalSize;

	  --_duma_s.unUsedSlots;
	}
#endif

	if ( 0 == userSize )
	{
	  /*
	   * we need just a single page
	   * may deny any access to it
	   *
	   */

	  /* Figure out what address to give the user: mid of protected page */
	  intAddr  = (DUMA_ADDR)fullSlot->internalAddress;
	  endAddr  = intAddr + internalSize;
	  userAddr = intAddr  + (DUMA_PAGE_SIZE >> 1);
	  protAddr = intAddr;

	  /* Set up the "dead" page(s). */
	  Page_DenyAccess( (char*)protAddr, endAddr - protAddr );
	}
	else if ( !protectBelow )
	{
	  /*
	   * Arrange the buffer so that it is followed by an inaccessable
	   * memory page. A buffer overrun that touches that page will
	   * cause a segmentation fault.
	   * internalAddr <= userAddr < protectedAddr
	   */

	  /* Figure out what address to give the user. */
	  intAddr  = (DUMA_ADDR)fullSlot->internalAddress;
	  endAddr  = intAddr + internalSize;
	  userAddr = ( intAddr  + internalSize - DUMA_PAGE_SIZE - userSize ) & ~(alignment -1);
	  protAddr = ( userAddr + userSize     + DUMA_PAGE_SIZE -1)     & ~(DUMA_PAGE_SIZE -1);

	  /* DUMA_ASSERT(intAddr <= userAddr && intAddr < protAddr ); */

	  /* Set up the "live" page(s). */
	  Page_AllowAccess( (char*)intAddr, protAddr - intAddr );
	  /* Set up the "dead" page(s). */
	  Page_DenyAccess( (char*)protAddr, endAddr - protAddr );
	}
	else /* if (protectBelow) */
	{
	  /*
	   * Arrange the buffer so that it is preceded by an inaccessable
	   * memory page. A buffer underrun that touches that page will
	   * cause a segmentation fault.
	   */
	  /* Figure out what address to give the user. */
	  intAddr  = (DUMA_ADDR)fullSlot->internalAddress;
	  endAddr  = intAddr + internalSize;
	  userAddr = ( intAddr + DUMA_PAGE_SIZE + alignment -1) & ~(alignment -1);
	  protAddr = ( userAddr & ~(DUMA_PAGE_SIZE -1) ) - DUMA_PAGE_SIZE;

	  /* DUMA_ASSERT(intAddr < userAddr && intAddr <= protAddr ); */

	  /* Set up the "live" page(s). userAddr == protAddr + DUMA_PAGE_SIZE ! */
	  Page_AllowAccess( (char*)userAddr, internalSize - (userAddr - protAddr) );
	  /* Set up the "dead" page(s). */
	  Page_DenyAccess( (char*)intAddr, userAddr - intAddr );
	}

	/* => userAddress = internalAddress + DUMA_PAGE_SIZE */
	fullSlot->userAddress = (char*)userAddr;
	fullSlot->protAddress = (char*)protAddr;
	fullSlot->userSize    = userSize;
	fullSlot->state       = DUMAST_IN_USE;
	fullSlot->allocator   = allocator;

	#ifndef DUMA_NO_LEAKDETECTION
	  fullSlot->fileSource  = DUMAFS_ALLOCATION;
	  fullSlot->filename    = (char*)filename;
	  #ifdef DUMA_EXPLICIT_INIT

		/* mark allocations from standard libraries
		 * before duma_init() is finished with lineno = -1
		 * to allow special treatment in leak_checking
		 */
		fullSlot->lineno    = (DUMAIS_OUT_INIT == _duma_s.init_state) ? lineno : -1;
	  #else
		fullSlot->lineno    = lineno;
	  #endif
	#endif

	/* initialise no mans land of slot */
	_duma_init_slack( fullSlot );

#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__)
	if(!_duma_s.DUMA_IN_DUMA && _duma_s.init_state && DUMA_OUTPUT_STACKTRACE)
	{
	  _duma_s.DUMA_IN_DUMA = 1;

	  /* Get stacktrace */
	  if(fullSlot->stacktrace)
		LocalFree(fullSlot->stacktrace);

	  fullSlot->stacktrace = ptrStacktrace;

	  _duma_s.DUMA_IN_DUMA = 0;
	}
	else
	  fullSlot->stacktrace = 0;
#endif

  } /* end if ( fullSlot->internalSize ) */

  /*
   * Make the pool's internal memory inaccessable, so that the program
   * being debugged can't stomp on it.
   */
  if ( protectAllocList )
  {
	Page_DenyAccess(_duma_g.allocList, _duma_s.allocListSize);

	IF__DUMA_INIT_DONE
	  DUMA_RELEASE_SEMAPHORE(0);
  }

  /* Fill the memory if it was specified to do so. */
  if ( ((char*)userAddr) && fillByte != -1 && userSize )
	memset( (char*)userAddr, fillByte, userSize);

  return duma_alloc_return( (char*)userAddr );
}


/* Function: _duma_deallocate
 *
 * Deallocate allocated memory after running some checks, then open
 * slot for use.  Uses Page_Delete to free the underlying memory.
 *
 * See Also: <Page_Delete> <_duma_allocate>
 */
void _duma_deallocate(void * address, int protectAllocList, enum _DUMA_Allocator allocator  DUMA_PARAMLIST_FL)
{
  struct _DUMA_Slot   * slot;
  long                internalSizekB;

  if ( 0 == _duma_g.allocList )
  {
#ifdef DUMA_DETOURS
	/* Odd things happen with detours sometimes... */
	DUMA_Print("DUMA_Warning: free() called before first malloc().");
	return;
#else
	DUMA_Abort("free() called before first malloc().");
#endif
  }

  if ( 0 == address || _duma_g.null_addr == address )
	return;

  if ( protectAllocList )
  {
	IF__DUMA_INIT_DONE
	  DUMA_GET_SEMAPHORE();

	Page_AllowAccess(_duma_g.allocList, _duma_s.allocListSize);
  }

  if ( _duma_s.CHECK_FREQ > 0 )
  {
	if ( (++ _duma_s.checkFreqCounter) == _duma_s.CHECK_FREQ )
	{
	  _duma_check_all_slacks();
	  _duma_s.checkFreqCounter = 0;
	}
  }

  if ( !(slot = slotForUserAddress(address)) )
  {
	if ( (slot = nearestSlotForUserAddress(address)) )
	{
#ifndef DUMA_NO_LEAKDETECTION
	  if ( DUMAFS_ALLOCATION == slot->fileSource )
		DUMA_Abort("free(%a): address not from DUMA or already freed. Address may be corrupted from %a allocated from %s(%i)",
		  (DUMA_ADDR)address, (DUMA_ADDR)slot->userAddress, slot->filename, slot->lineno);
	  else if ( DUMAFS_DEALLOCATION == slot->fileSource )
		DUMA_Abort("free(%a): address not from DUMA or already freed. Address may be corrupted from %a deallocated at %s(%i)",
		  (DUMA_ADDR)address, (DUMA_ADDR)slot->userAddress, slot->filename, slot->lineno);
	  else
#endif
		DUMA_Abort("free(%a): address not from DUMA or already freed. Address may be corrupted from %a.",
		  (DUMA_ADDR)address, (DUMA_ADDR)slot->userAddress);
	}
	else
	{
#if DUMA_DETOURS
	  /* For Detours we need to not dump out, we get one extra free up front for some reason. */
	  DUMA_Print("DUMA_Warning: free(%a): address not from DUMA or already freed.", (DUMA_ADDR)address);
	  return;
#else
	  DUMA_Abort("free(%a): address not from DUMA or already freed.", (DUMA_ADDR)address);
#endif
	}
  }

  if ( DUMAST_ALL_PROTECTED == slot->state || DUMAST_BEGIN_PROTECTED == slot->state )
  {
#ifndef DUMA_NO_LEAKDETECTION
	if ( DUMAFS_ALLOCATION == slot->fileSource )
	  DUMA_Abort("free(%a): memory already freed. allocated from %s(%i)",
		(DUMA_ADDR)address, slot->filename, slot->lineno);
	else if ( DUMAFS_DEALLOCATION == slot->fileSource )
	  DUMA_Abort("free(%a): memory already freed at %s(%i)",
		(DUMA_ADDR)address, slot->filename, slot->lineno);
	else
#endif
	  DUMA_Abort("free(%a): memory already freed.", (DUMA_ADDR)address);
  }
  else if ( _duma_allocDesc[slot->allocator].type != _duma_allocDesc[allocator].type )
  {
#ifndef DUMA_NO_LEAKDETECTION
	if ( DUMAFS_ALLOCATION == slot->fileSource )
	  /*                                    1            2  3                        4             5  6 */
	  DUMA_Abort("Free mismatch: allocator '%s' used  at %s(%i)\n  but  deallocator '%s' called at %s(%i)!",
		_duma_allocDesc[slot->allocator].name, slot->filename, slot->lineno,
		_duma_allocDesc[allocator].name, filename, lineno );
	else if ( DUMAFS_DEALLOCATION == slot->fileSource )
	  /*                                    1                           2             3  4 */
	  DUMA_Abort("Free mismatch: allocator '%s' used \nbut deallocator '%s' called at %s(%i)!",
		_duma_allocDesc[slot->allocator].name,
		_duma_allocDesc[allocator].name, filename, lineno );
	else
#endif
	  DUMA_Abort("Free mismatch: allocator '%s' used  but  deallocator '%s' called!",
		_duma_allocDesc[slot->allocator].name, _duma_allocDesc[allocator].name );
  }

  /* count and show deallocation, if requested */
  _duma_s.numDeallocs++;
  if (_duma_s.SHOW_ALLOC)
#ifndef DUMA_NO_LEAKDETECTION
	DUMA_Print("\nDUMA: Freeing %d bytes at %s(%i) (Allocated from %s(%i)).",
	  (DUMA_SIZE)slot->userSize, filename, lineno, slot->filename, slot->lineno);
#else
	DUMA_Print("\nDUMA: Freeing %d bytes.", (DUMA_SIZE)slot->userSize);
#endif

  /* CHECK INTEGRITY OF NO MANS LAND */
  _duma_check_slack( slot );

  if ( _duma_s.FREE_ACCESS )
  {
	volatile char *start = slot->userAddress;
	volatile char *cur;

	for (cur = (char*)slot->userAddress+slot->userSize; --cur >= start; )
	{
	  char c = *cur;
	  *cur = c-1;
	  *cur = c;
	}
  }

  internalSizekB = (slot->internalSize+1023) >>10;

  /* protect memory, that nobody can access it */
  /* Free as much protected memory, that we can protect this one */
  /* is there need? and is there a way to free such much? */
  if ( _duma_s.PROTECT_FREE > 0L
	&& _duma_s.sumProtectedMem  + internalSizekB >  _duma_s.PROTECT_FREE
	&&                            internalSizekB <  _duma_s.PROTECT_FREE
	&& _duma_s.sumProtectedMem >= internalSizekB)
  {
	reduceProtectedMemory( internalSizekB );
  }

  if (( EFA_INT_ALLOC != slot->allocator )
	&& ( _duma_s.PROTECT_FREE < 0L
	|| ( _duma_s.PROTECT_FREE > 0L
	&& _duma_s.sumProtectedMem + internalSizekB <= _duma_s.PROTECT_FREE
	)   )
	)
  {
	slot->state = DUMAST_ALL_PROTECTED;
	Page_DenyAccess(slot->internalAddress, slot->internalSize);
	_duma_s.sumProtectedMem += internalSizekB;

#ifndef DUMA_NO_LEAKDETECTION
	if ( lineno )
	{
	  slot->fileSource  = DUMAFS_DEALLOCATION;
	  slot->filename    = (char*)filename;
	  slot->lineno      = lineno;
	}
#endif
  }
  else
  {
	/* free all the memory */
	Page_Delete(slot->internalAddress, slot->internalSize);
	_duma_s.sumAllocatedMem -= internalSizekB;

	/* free slot and userAddr */
	slot->internalAddress = slot->userAddress = 0;
	slot->internalSize    = slot->userSize    = 0;
	slot->state           = DUMAST_EMPTY;
	slot->allocator       = EFA_INT_ALLOC;
#ifndef DUMA_NO_LEAKDETECTION
	slot->fileSource      = DUMAFS_EMPTY;
	slot->filename      = 0;
	slot->lineno        = 0;
#endif
#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__)
	if(slot->stacktrace)
	{
	  slot->stacktrace = 0;
	  LocalFree(slot->stacktrace);
	}
#endif
	++_duma_s.unUsedSlots;
  }

  if ( protectAllocList )
  {
	Page_DenyAccess(_duma_g.allocList, _duma_s.allocListSize);
	IF__DUMA_INIT_DONE
	  DUMA_RELEASE_SEMAPHORE(0);
  }
}


/* Function: duma_check
 *
 * Check No Mans Land of a memory block.
 *
 */
void duma_check(void * address)
{
  struct _DUMA_Slot   * slot;

  if ( 0 == address )
	return;

  IF__DUMA_INIT_DONE
	DUMA_GET_SEMAPHORE();

  Page_AllowAccess(_duma_g.allocList, _duma_s.allocListSize);

  if ( !(slot = slotForUserAddress(address)) )
  {
	if ( (slot = nearestSlotForUserAddress(address)) )
	{
#ifndef DUMA_NO_LEAKDETECTION
	  if ( DUMAFS_ALLOCATION == slot->fileSource )
		DUMA_Abort("check(%a): address not from DUMA or already freed. Address may be corrupted from %a allocated from %s(%i)",
		  (DUMA_ADDR)address, (DUMA_ADDR)slot->userAddress, slot->filename, slot->lineno);
	  else if ( DUMAFS_DEALLOCATION == slot->fileSource )
		DUMA_Abort("check(%a): address not from DUMA or already freed. Address may be corrupted from %a deallocated at %s(%i)",
		  (DUMA_ADDR)address, (DUMA_ADDR)slot->userAddress, slot->filename, slot->lineno);
	  else
#endif
		DUMA_Abort("check(%a): address not from DUMA or already freed. Address may be corrupted from %a.",
		  (DUMA_ADDR)address, (DUMA_ADDR)slot->userAddress);
	}
	else
	  DUMA_Abort("check(%a): address not from DUMA or already freed.", (DUMA_ADDR)address);
  }

  if ( DUMAST_ALL_PROTECTED == slot->state || DUMAST_BEGIN_PROTECTED == slot->state )
  {
#ifndef DUMA_NO_LEAKDETECTION
	if ( DUMAFS_ALLOCATION == slot->fileSource )
	  DUMA_Abort("check(%a): memory already freed. allocated from %s(%i)",
		(DUMA_ADDR)address, slot->filename, slot->lineno);
	else if ( DUMAFS_DEALLOCATION == slot->fileSource )
	  DUMA_Abort("check(%a): memory already freed at %s(%i)",
		(DUMA_ADDR)address, slot->filename, slot->lineno);
	else
#endif
	  DUMA_Abort("check(%a): memory already freed.", (DUMA_ADDR)address);
  }

  /* CHECK INTEGRITY OF NO MANS LAND */
  _duma_check_slack( slot );


  Page_DenyAccess(_duma_g.allocList, _duma_s.allocListSize);
  IF__DUMA_INIT_DONE
	DUMA_RELEASE_SEMAPHORE(0);
}


/* Function: duma_checkAll
 *
 * Check No Mans Land of all memory blocks.
 *
 */
void duma_checkAll()
{
  IF__DUMA_INIT_DONE
	DUMA_GET_SEMAPHORE();

  Page_AllowAccess(_duma_g.allocList, _duma_s.allocListSize);

  _duma_check_all_slacks();

  Page_DenyAccess(_duma_g.allocList, _duma_s.allocListSize);
  IF__DUMA_INIT_DONE
	DUMA_RELEASE_SEMAPHORE(0);
}


/*********************************************************/

/* Function: _duma_malloc
 *
 * A version of malloc.
 */
void * _duma_malloc(size_t size  DUMA_PARAMLIST_FL)
{
  DUMA_TLSVARS_T  * duma_tls;

  if ( _duma_g.allocList == 0 )
	_duma_init();  /* This sets DUMA_ALIGNMENT, DUMA_PROTECT_BELOW, DUMA_FILL, ... */

  duma_tls = GET_DUMA_TLSVARS();

  return _duma_allocate(0, size, duma_tls->PROTECT_BELOW,
	duma_tls->FILL, 1 /*=protectAllocList*/, EFA_MALLOC,
	DUMA_FAIL_ENV  DUMA_PARAMS_FL);
}


/* Function: _duma_calloc
 *
 * A version of calloc.
 */
void * _duma_calloc(size_t nelem, size_t elsize  DUMA_PARAMLIST_FL)
{
  DUMA_TLSVARS_T  * duma_tls;

  if ( _duma_g.allocList == 0 )
	_duma_init();  /* This sets DUMA_ALIGNMENT, DUMA_PROTECT_BELOW, DUMA_FILL, ... */

  duma_tls = GET_DUMA_TLSVARS();

  return _duma_allocate(0, nelem * elsize,
	duma_tls->PROTECT_BELOW, 0 /*=fillByte*/,
	1 /*=protectAllocList*/, EFA_CALLOC,
	DUMA_FAIL_ENV  DUMA_PARAMS_FL);
}


/* Function: _duma_free
 *
 * A version of free.
 */
void   _duma_free(void * baseAdr  DUMA_PARAMLIST_FL)
{
  if ( _duma_g.allocList == 0 )
	_duma_init();  /* This sets DUMA_ALIGNMENT, DUMA_PROTECT_BELOW, DUMA_FILL, ... */

  _duma_deallocate(baseAdr, 1 /*=protectAllocList*/, EFA_FREE  DUMA_PARAMS_FL);
}


/* Function: _duma_memalign
 *
 * A version of memalign.
 */
void * _duma_memalign(size_t alignment, size_t size  DUMA_PARAMLIST_FL)
{
  DUMA_TLSVARS_T  * duma_tls;

  if ( _duma_g.allocList == 0 )
	_duma_init();  /* This sets DUMA_ALIGNMENT, DUMA_PROTECT_BELOW, DUMA_FILL, ... */

  duma_tls = GET_DUMA_TLSVARS();

  return _duma_allocate(alignment, size, duma_tls->PROTECT_BELOW,
	duma_tls->FILL, 1 /*=protectAllocList*/, EFA_MEMALIGN,
	DUMA_FAIL_ENV  DUMA_PARAMS_FL);
}


/* Function: _duma_posix_memalign
 *
 * A version of posix_memalign.
 */
int    _duma_posix_memalign(void **memptr, size_t alignment, size_t size  DUMA_PARAMLIST_FL)
{
  DUMA_TLSVARS_T  * duma_tls;
  void * retptr;

  if ( (alignment & (alignment -1)) || alignment < sizeof(void *) )
	return EINVAL;

  if ( _duma_g.allocList == 0 )
	_duma_init();  /* This sets DUMA_ALIGNMENT, DUMA_PROTECT_BELOW, DUMA_FILL, ... */

  duma_tls = GET_DUMA_TLSVARS();

  retptr = _duma_allocate(alignment, size, duma_tls->PROTECT_BELOW,
	duma_tls->FILL, 1 /*=protectAllocList*/, EFA_POSIX_MEMALIGN,
	DUMA_FAIL_ENV  DUMA_PARAMS_FL);

  if ( retptr )
  {
	(*(char**)memptr) = (char*)retptr;
	return 0;
  }
  else
  {
	(*(char**)memptr) = NULL;
	return ENOMEM;
  }
}


/* Function: _duma_realloc
 *
 * A version of realloc that provides extra checks based on
 * information we know about HEAP.
 */
void * _duma_realloc(void * oldBuffer, size_t newSize  DUMA_PARAMLIST_FL)
{
  void * ptr;
  DUMA_TLSVARS_T  * duma_tls;

  if( _duma_g.allocList == 0 )
	_duma_init();  /* This sets DUMA_ALIGNMENT, DUMA_PROTECT_BELOW, DUMA_FILL, ... */

  duma_tls = GET_DUMA_TLSVARS();

  IF__DUMA_INIT_DONE
  DUMA_GET_SEMAPHORE();

  Page_AllowAccess(_duma_g.allocList, _duma_s.allocListSize);

  if( !oldBuffer )
  {
	ptr = _duma_allocate(0, newSize, duma_tls->PROTECT_BELOW,
	  duma_tls->FILL, 0 /*=protectAllocList*/, EFA_REALLOC,
	  DUMA_FAIL_ENV  DUMA_PARAMS_FL);
  }
  else if( newSize <= 0 )
  {
	_duma_deallocate(oldBuffer, 0 /*=protectAllocList*/, EFA_REALLOC  DUMA_PARAMS_FL);
	ptr = NULL;
  }
  else
  {
	ptr = _duma_allocate(0, newSize, duma_tls->PROTECT_BELOW,
	  -1 /*=fillByte*/, 0 /*=protectAllocList*/, EFA_REALLOC,
	  DUMA_FAIL_ENV  DUMA_PARAMS_FL);

	DUMA_ASSERT( oldBuffer );
	if( ptr )
	{
	  struct _DUMA_Slot * slot = slotForUserAddress(oldBuffer);

	  if ( slot == 0 )
		DUMA_Abort("realloc(%a, %d): address not from malloc().",
		(DUMA_ADDR)oldBuffer, (DUMA_SIZE)newSize);

	  if ( newSize > slot->userSize )
	  {
		memcpy( ptr, oldBuffer, slot->userSize );
		memset( (char*)ptr + slot->userSize, 0, newSize - slot->userSize );
	  }
	  else if ( newSize > 0 )
		memcpy(ptr, oldBuffer, newSize);

	  _duma_deallocate(oldBuffer, 0 /*=protectAllocList*/, EFA_REALLOC  DUMA_PARAMS_FL);
	}
  }

  Page_DenyAccess(_duma_g.allocList, _duma_s.allocListSize);

  IF__DUMA_INIT_DONE
  DUMA_RELEASE_SEMAPHORE(0);

  return ptr;
}


/* Function: _duma_valloc
 *
 * A version of valloc.
 */
void * _duma_valloc(size_t size  DUMA_PARAMLIST_FL)
{
  DUMA_TLSVARS_T  * duma_tls;

  if ( _duma_g.allocList == 0 )
	_duma_init();  /* This sets DUMA_ALIGNMENT, DUMA_PROTECT_BELOW, DUMA_FILL, ... */

  duma_tls = GET_DUMA_TLSVARS();

  return _duma_allocate(DUMA_PAGE_SIZE, size, duma_tls->PROTECT_BELOW,
	duma_tls->FILL, 1 /*=protectAllocList*/, EFA_VALLOC,
	DUMA_FAIL_ENV  DUMA_PARAMS_FL);
}


/* Function: _duma_strdup
 *
 * A version of strdup.
 */
char * _duma_strdup(const char * str  DUMA_PARAMLIST_FL)
{
  size_t size;
  char * dup;
  DUMA_TLSVARS_T  * duma_tls;
  unsigned i;

  if ( _duma_g.allocList == 0 )
	_duma_init();  /* This sets DUMA_ALIGNMENT, DUMA_PROTECT_BELOW, DUMA_FILL, ... */

  duma_tls = GET_DUMA_TLSVARS();

  size = 0;
  while (str[size])
	++size;

  dup = _duma_allocate(0, size +1, duma_tls->PROTECT_BELOW,
	-1 /*=fillByte*/, 1 /*=protectAllocList*/, EFA_STRDUP,
	DUMA_FAIL_ENV  DUMA_PARAMS_FL);

  if (dup)                    /* if successful */
	for (i=0; i<=size; ++i)   /* copy string */
	  dup[i] = str[i];

  return dup;
}


/* Function: _duma_memcpy
 *
 * A version of memcpy that provides extra checks based on
 * information we know about HEAP.
 *
 * Currently the only check we perform is overlapping memory
 * regions.  This should be expanded to include checking size
 * of dest to verify assumptions.
 */
void * _duma_memcpy(void *dest, const void *src, size_t size  DUMA_PARAMLIST_FL)
{
  char       * d = (char *)dest;
  const char * s = (const char *)src;
  unsigned i;

  if ( (s < d  &&  d < s + size) || (d < s  &&  s < d + size && !_duma_s.MEMCPY_OVERLAP ) )
  {
#ifndef DUMA_NO_LEAKDETECTION
	DUMA_Abort("memcpy(%a, %a, %d): memory regions overlap at %s(%i)."
	  , (DUMA_ADDR)dest, (DUMA_ADDR)src, (DUMA_SIZE)size, filename, lineno );
#else
	DUMA_Abort("memcpy(%a, %a, %d): memory regions overlap."
	  , (DUMA_ADDR)dest, (DUMA_ADDR)src, (DUMA_SIZE)size );
#endif
  }

  for (i=0; i<size; ++i)
	d[i] = s[i];

  return dest;
}


/* Function: _duma_memmove
 *
 * An implementation of memmove is provied by Duma to prevent some optimized
 * memmove implementations from calling memcpy and generate false positive overlap
 * errors.
 */
void * _duma_memmove(void *dest, const void *src, size_t size)
{
  char       * d = (char *)dest;
  const char * s = (const char *)src;

  if (d < s)
  {
	const char *end = src + size;
	while (s < end)
	  *d++ = *s++;
  }
  else
  {
	d += size;
	s += size;
	while ( s > (const char*)src )
	  *--d = *--s;
  }
  return dest;
}


/* Function: _duma_strnlen
 *
 * like strlen() but maximum return value is size
 */
size_t _duma_strnlen(const char *src, size_t size)
{
  size_t len;
  for ( len =0; len < size && src[len]; ++len )
	;
  return len;
}


/* Function: _duma_strcpy
 *
 * A version of strcpy that provides extra checks based on
 * information we know about HEAP.
 *
 * Currently the only check we perform is overlapping memory
 * regions.  This should be expanded to include checking size
 * of dest to verify assumptions.
 */
char * _duma_strcpy(char *dest, const char *src  DUMA_PARAMLIST_FL)
{
  size_t i;
  size_t size = strlen(src) +1;

  if ( src < dest  &&  dest < src + size )
  {
#ifndef DUMA_NO_LEAKDETECTION
	DUMA_Abort("strcpy(%a, %a): memory regions overlap at %s(%i)."
	  , (DUMA_ADDR)dest, (DUMA_ADDR)src, filename, lineno );
#else
	DUMA_Abort("strcpy(%a, %a): memory regions overlap."
	  , (DUMA_ADDR)dest, (DUMA_ADDR)src );
#endif
  }

  for (i=0; i<size; ++i)
	dest[i] = src[i];

  return dest;
}


/* Function: _duma_strncpy
 *
 * A version of strncpy that provides extra checks based on
 * information we know about HEAP.
 *
 * Currently the only check we perform is overlapping memory
 * regions.  This should be expanded to include checking size
 * of dest to verify assumptions.
 */
char * _duma_strncpy(char *dest, const char *src, size_t size  DUMA_PARAMLIST_FL)
{
  size_t i, srclen;

  srclen  = _duma_strnlen(src, size);

  if ( size > 0  &&
	  (  ( src < dest  &&  dest < src  + srclen )
	   ||( dest < src  &&  src  < dest + size   )
	 ))
  {
#ifndef DUMA_NO_LEAKDETECTION
	DUMA_Abort("strncpy(%a, %a, %d): memory regions overlap at %s(%i)."
	  , (DUMA_ADDR)dest, (DUMA_ADDR)src, (DUMA_SIZE)size, filename, lineno );
#else
	DUMA_Abort("strncpy(%a, %a, %d): memory regions overlap."
	  , (DUMA_ADDR)dest, (DUMA_ADDR)src, (DUMA_SIZE)size );
#endif
  }

  /* copy src to dest - up to size or zero terminator
   *   whatever happens first
   */
  for (i =0; i < size && src[i]; ++i)
	dest[i] = src[i];

  /* fill rest with '\0' character */
  for ( ; i<size; ++i)
	dest[i] = 0;

  return dest;
}


/* Function: _duma_strcat
 *
 * A version of strcat that provides extra checks based on
 * information we know about HEAP.
 *
 * Currently the only check we perform is overlapping memory
 * regions.  This should be expanded to include checking size
 * of dest to verify assumptions.
 */
char * _duma_strcat(char *dest, const char *src  DUMA_PARAMLIST_FL)
{
  unsigned i;
  size_t destlen = strlen(dest);
  size_t srcsize = strlen(src) + 1;

  if ( src < dest +destlen  &&  dest + destlen < src + srcsize )
  {
#ifndef DUMA_NO_LEAKDETECTION
	DUMA_Abort("strcat(%a, %a): memory regions overlap at %s(%i)."
	  , (DUMA_ADDR)dest, (DUMA_ADDR)src, filename, lineno );
#else
	DUMA_Abort("strcat(%a, %a): memory regions overlap."
	  , (DUMA_ADDR)dest, (DUMA_ADDR)src );
#endif
  }

  for (i=0; i<srcsize; ++i)
	dest[destlen+i] = src[i];

  return dest;
}


/* Function: _duma_strncat
 *
 * A version of strncat that provides extra checks based on
 * information we know about HEAP.
 *
 * Currently the only check we perform is overlapping memory
 * regions.  This should be expanded to include checking size
 * of dest to verify assumptions (like is size right).
 */
char * _duma_strncat(char *dest, const char *src, size_t size  DUMA_PARAMLIST_FL)
{
  unsigned i;
  size_t destlen, srclen;

  /* do nothing, when size not > 0 */
  if ( size <= 0 )
	return dest;

  /* calculate number of characters to copy from src to dest */
  destlen = strlen(dest);
  srclen  = _duma_strnlen(src, size);

  /* CHECK: Verify memory regions do not overlap */
  if ( src < (dest + destlen) && (dest + destlen) < (src + srclen + 1) )
  {
#ifndef DUMA_NO_LEAKDETECTION
	DUMA_Abort("strncat(%a, %a, %d): memory regions overlap at %s(%i)."
	  , (DUMA_ADDR)dest, (DUMA_ADDR)src, (DUMA_SIZE)size, filename, lineno );
#else
	DUMA_Abort("strncat(%a, %a, %d): memory regions overlap."
	  , (DUMA_ADDR)dest, (DUMA_ADDR)src, (DUMA_SIZE)size );
#endif
  }

  /* copy up to size characters from src to dest */
  for (i=0; i<srclen; ++i)
	dest[destlen+i] = src[i];

  /* append single '\0' character */
  dest[destlen+srclen] = 0;

  return dest;
}


/*********************************************************/


#ifndef DUMA_NO_GLOBAL_MALLOC_FREE

/*
 * define global functions for malloc(), free(), ..
 */

#ifdef _MSC_VER
/* define these functions as non-intrinsic */
#pragma function( memcpy, strcpy, strcat, memmove )
#endif


void * malloc(size_t size)
{
  return _duma_malloc(size  DUMA_PARAMS_UK);
}


void * calloc(size_t nelem, size_t elsize)
{
  return _duma_calloc(nelem, elsize  DUMA_PARAMS_UK);
}


void   free(void * address)
{
  _duma_free(address  DUMA_PARAMS_UK);
}


void * memalign(size_t alignment, size_t size)
{
  return _duma_memalign(alignment, size  DUMA_PARAMS_UK);
}


int    posix_memalign(void **memptr, size_t alignment, size_t size)
{
  return _duma_posix_memalign(memptr, alignment, size  DUMA_PARAMS_UK);
}


void * realloc(void * oldBuffer, size_t newSize)
{
  return _duma_realloc(oldBuffer, newSize  DUMA_PARAMS_UK);
}


void * valloc(size_t size)
{
  return _duma_valloc(size  DUMA_PARAMS_UK);
}


char * strdup(const char * str)
{
  return _duma_strdup(str  DUMA_PARAMS_UK);
}


void * memcpy(void *dest, const void *src, size_t size)
{
  return _duma_memcpy(dest, src, size  DUMA_PARAMS_UK);
}


void * memmove(void *dest, const void *src, size_t size)
{
  return _duma_memmove(dest, src, size);
}


char * strcpy(char *dest, const char *src)
{
  return _duma_strcpy(dest, src  DUMA_PARAMS_UK);
}


char * strncpy(char *dest, const char *src, size_t size)
{
  return _duma_strncpy(dest, src, size  DUMA_PARAMS_UK);
}


char * strcat(char *dest, const char *src)
{
  return _duma_strcat(dest, src  DUMA_PARAMS_UK);
}


char * strncat(char *dest, const char *src, size_t size)
{
  return _duma_strncat(dest, src, size  DUMA_PARAMS_UK);
}


#endif /* DUMA_NO_GLOBAL_MALLOC_FREE */


#ifndef DUMA_NO_LEAKDETECTION

/* Function DUMA_newFrame
 */
void  DUMA_newFrame(void)
{
}


/* Function DUMA_delFrame
 *
 * Will output DUMA message for all in use frames along with totals.
 * This method is called to when all memory should have been free'd by
 * the application to locate memory leaks.
 */
void  DUMA_delFrame(void)
{
  struct _DUMA_Slot * slot  = _duma_g.allocList;
  size_t        count     = _duma_s.slotCount;
  int    nonFreedTotal    = 0;
  int    nonFreedReported = 0;
  int    iExtraLeaks;

  IF__DUMA_INIT_DONE
	DUMA_GET_SEMAPHORE();

  Page_AllowAccess(_duma_g.allocList, _duma_s.allocListSize);

  for ( ; count > 0; --count, ++slot )
  {
	if ( DUMAST_IN_USE  == slot->state
	  && EFA_INT_ALLOC  != slot->allocator
#ifdef DUMA_EXPLICIT_INIT
	  && -1 !=  slot->lineno
#endif
	   )
	{

	  if ( _duma_s.REPORT_ALL_LEAKS || slot->lineno > 0 )
	  {

#if defined(DUMA_DLL_LIBRARY) || defined(DUMA_SO_LIBRARY) || defined(DUMA_DETOURS)
	  DUMA_Print("\nDUMA: ptr=0x%a size=%d type='%s' not freed\n"
				, (DUMA_ADDR)slot->userAddress, (DUMA_SIZE)slot->userSize
				, _duma_allocDesc[slot->allocator].name
				);
#else
	  DUMA_Print("\nDUMA: ptr=0x%a size=%d type='%s' alloced from %s(%i) not freed\n"
				, (DUMA_ADDR)slot->userAddress, (DUMA_SIZE)slot->userSize
				, _duma_allocDesc[slot->allocator].name
				, slot->filename, slot->lineno
				);
#endif
#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__)
	  if(DUMA_OUTPUT_STACKTRACE)
		DUMA_Print("Stacktrace of allocation:\n%s\n", slot->stacktrace);
#endif

		++nonFreedReported;
	  }

	  ++nonFreedTotal;
	}
  }

  iExtraLeaks = nonFreedTotal - nonFreedReported;

  if ( nonFreedReported )
	DUMA_Print("DUMA: Reported %i leaks. There are %i extra leaks without allocation information\n"
			  , nonFreedReported, iExtraLeaks
			  );
  else if ( nonFreedReported < nonFreedTotal )
	DUMA_Print("DUMA: Reported %i leaks. There are %i extra leaks without allocation information\n"
			  , nonFreedReported, iExtraLeaks
			  );


  Page_DenyAccess(_duma_g.allocList, _duma_s.allocListSize);

  IF__DUMA_INIT_DONE
  DUMA_RELEASE_SEMAPHORE(0);

  if (_duma_s.SHOW_ALLOC)
	DUMA_Print("\nDUMA: Processed %l allocations and %l deallocations in total.\n", _duma_s.numAllocs, _duma_s.numDeallocs);
}


/* Function: _duma_exit
 *
 * DUMA's exit function, called atexit() or with GNU C Compiler's destructor attribute.
 * This function also calls DUMA_delFrame to check for still in use memory and allert
 * the user.
 */
void
#if ( defined(DUMA_GNU_INIT_ATTR) && !defined(DUMA_PREFER_ATEXIT) )
__attribute ((destructor))
#endif
_duma_exit(void)
{
#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__)
  /* Cleanup memory owned by the stack library */
  /* wouldn't do to leak memory :) */
  StackTraceCleanup();
#endif

  DUMA_delFrame();
}


#endif /* end ifndef DUMA_NO_LEAKDETECTION */

#endif /* ifndef DUMA_NO_DUMA */
