/* $Id: electric.c 2798 2015-09-19 20:35:03Z bird $ */ /** @file * A simple electric heap implementation. */ /* * Copyright (c) 2007-2010 knut st. osmundsen * * This file is part of kBuild. * * kBuild 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 3 of the License, or * (at your option) any later version. * * kBuild 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 kBuild. If not, see * */ #ifdef ELECTRIC_HEAP # ifdef WINDOWS32 # include # else # include # include # include # endif # include # include # include # define FREED_ENTRIES 512 static struct { void *ptr; unsigned aligned; } freed[FREED_ENTRIES]; static unsigned freed_head = 0; static unsigned freed_tail = 0; static void fatal_error (const char *msg) { #ifdef _MSC_VER fprintf (stderr, "electric heap error: %s\n", msg); __debugbreak (); #else fprintf (stderr, "electric heap error: %s (errno=%d)\n", msg, errno); __asm__ ("int3"); /* not portable... */ #endif abort (); exit (1); } static void free_it (void *ptr, unsigned aligned) { # ifdef WINDOWS32 if (!VirtualFree (ptr, 0, MEM_RELEASE)) fatal_error ("VirtualFree failed"); # else if (munmap(ptr, aligned)) fatal_error ("munmap failed"); # endif } /* Return 1 if something was freed, 0 otherwise. */ static int free_up_some (void) { if (freed_tail == freed_head) return 0; free_it (freed[freed_tail].ptr, freed[freed_tail].aligned); freed[freed_tail].ptr = NULL; freed[freed_tail].aligned = 0; freed_tail = (freed_tail + 1) % FREED_ENTRIES; return 1; } static unsigned *get_hdr (void *ptr) { if (((uintptr_t)ptr & 0xfff) < sizeof(unsigned)) return (unsigned *)(((uintptr_t)ptr - 0x1000) & ~0xfff); return (unsigned *)((uintptr_t)ptr & ~0xfff); } void xfree (void *ptr) { unsigned int size, aligned; unsigned *hdr; # ifdef WINDOWS32 DWORD fFlags = PAGE_NOACCESS; # endif if (!ptr) return; hdr = get_hdr (ptr); size = *hdr; aligned = (size + 0x1fff + sizeof(unsigned)) & ~0xfff; # ifdef WINDOWS32 if (!VirtualProtect (hdr, aligned - 0x1000, fFlags, &fFlags)) fatal_error ("failed to protect freed memory"); # else if (mprotect(hdr, aligned - 0x1000, PROT_NONE)) fatal_error ("failed to protect freed memory"); # endif freed[freed_head].ptr = hdr; freed[freed_head].aligned = aligned; if (((freed_head + 1) % FREED_ENTRIES) == freed_tail) free_up_some(); freed_head = (freed_head + 1) % FREED_ENTRIES; } void * xmalloc (unsigned int size) { /* Make sure we don't allocate 0, for pre-ANSI libraries. */ unsigned int aligned = (size + 0x1fff + sizeof(unsigned)) & ~0xfff; unsigned *hdr; unsigned i; for (i = 0; i < FREED_ENTRIES; i++) { # ifdef WINDOWS32 DWORD fFlags = PAGE_NOACCESS; hdr = VirtualAlloc(NULL, aligned, MEM_COMMIT, PAGE_READWRITE); if (hdr && !VirtualProtect((char *)hdr + aligned - 0x1000, 0x1000, fFlags, &fFlags)) fatal_error ("failed to set guard page protection"); # else hdr = mmap(NULL, aligned, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (hdr == MAP_FAILED) hdr = 0; if (hdr && mprotect((char *)hdr + aligned - 0x1000, 0x1000, PROT_NONE)) fatal_error ("failed to set guard page protection"); # endif if (hdr) break; if (!free_up_some ()) break; } if (hdr == 0) fatal_error ("virtual memory exhausted"); *hdr = size; # if 0 return hdr + 1; # else return (char *)hdr + aligned - 0x1000 - size; # endif } void * xcalloc (unsigned size) { void *result; result = xmalloc (size); return memset (result, 0, size); } void * xrealloc (void *ptr, unsigned int size) { void *result; result = xmalloc (size); if (ptr) { unsigned *hdr = get_hdr (ptr); unsigned int oldsize = *hdr; memcpy (result, ptr, oldsize >= size ? size : oldsize); xfree (ptr); } return result; } char * xstrdup (const char *ptr) { if (ptr) { size_t size = strlen (ptr) + 1; char *result = xmalloc (size); return memcpy (result, ptr, size); } return NULL; } # ifdef __GNUC__ void * xmalloc_size_t (size_t size) { return xmalloc(size); } void * xcalloc_size_t (size_t size, size_t items) { return xcalloc(size * items); } void * xrealloc_size_t (void *ptr, size_t size) { return xrealloc(ptr, size); } # endif /* __GNUC__ */ #else /* !ELECTRIC_HEAP */ extern void electric_heap_keep_ansi_c_quiet (void); #endif /* !ELECTRIC_HEAP */