/* * Copyright (C) 2006 Nigel Horne * * 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. * * Debug package. Don't use it in a production environment unless you have * a spare Cray. Bug reports about performance hit will be ignored unless * attached with patches that don't impact on results */ #if HAVE_CONFIG_H #include "clamav-config.h" #endif #ifdef CL_DEBUG #ifdef C_LINUX #define LINUX #define ANSI #endif /*LINTLIBRARY*/ /* * 1.1: Unix: _realloc gave bogue memcpy errors * 1.2: Unix: now includes own malloc/free * 1.3: ptr2slot added cache and sentinal as it was(is?) a bottleneck * 1.4: Unix: added isptrok(). * Creation of FILE_ID.DIZ file. * 1.5: Unix: Reduced platform dependancies. Ported to AIX Release 2. * DOS: Removed cprintf - now uses stderr. * 1.5.1: isptrok now checks for NULL pointer * 1.5.2: ptr2slot: Installed direct cache * 1.5.3: _realloc: Call clear_cache * 2.0: Now debugs more than just malloc calls * 2.1: Added checkembedded pointer and mmap stuff * 2.1.1: Check strcpy arguments are different * 2.1.2: mprotect caused mem faults in realloc * 2.1.3: fixed strdup dumps not appearing * 2.1.4: 2.1 fix now handles strdup messages better * 2.1.5: checkembeddedptr() now checks it's argument is valid * 2.2: _error: no longer repeats last message * 2.3: Added underflowcheck * Added total summary of leaks * Added call to heapchk in exit routine leaks() * 2.3.1: Reported to solaris: vsprintf format isn't const * 2.4: Added information on leaked file descriptors * 2.4.1: Need clear_cache when allocating new slot table if the * slot table has moved * 2.5: Support process watches * 2.6: added last_free_size to report needless mallocs of something * just freed * * LATER: * Stats on number of alloc/free calls */ /* * To compile on AIX 4.1.5 * cc -DAIX -DANSI -O2 -qroconst -U__STR__ -qro *.c * With gcc on SunOs: * gcc -ansi -Wshadow -Wall -Wwrite-strings -pipe -O2 -Dsun *.c * With cc on SunOs * cc -Dsun -O4 -pipe *.c * For perpos: * cc8 -O2 -c -DPERPOS *.c * With cc on Solaris * cc -DANSI -Dsolaris -fast -xO4 -fd -mc -v -Xc -xCC -xstrconst *.c * With gcc on linux * gcc -DLINUX -pipe -Wshadow -Wwrite-strings -Wall -O2 -c debug.c */ #ifdef lint #define void char #endif #ifdef solaris #ifndef sun #define sun #endif #define WATCH #endif #if defined(sun) || defined(AIX) || defined(LINUX) || defined(_HPUX_SOURCE) #ifdef MAP_ANONYMOUS #define MPROTECT /* has the mprotect system call */ #endif #endif #ifdef WATCH #undef MPROTECT #include static int watchfd = -1; #endif #include "debug.h" #ifdef UNIX static void none(const caddr_t addr, size_t size); static void ro(const caddr_t addr, size_t size); static void rw(const caddr_t addr, size_t size); static void slots_readonly(void); static void slots_rw(void); #else #define none(addr, size) #define ro(addr, size) #define rw(addr, size) #define slots_readonly() #define slots_rw() #endif #ifdef MPROTECT #include #endif /* * If MAX_STACK_DEPTH is defined we try a stack trace. */ #if defined(sun) && !defined(solaris) #define MAX_STACK_DEPTH 128 #endif #ifdef __sparc #define getpagesize() 4096 #endif #ifdef MAX_STACK_DEPTH #ifdef solaris #include #include #else #include #endif #ifdef sun #include #endif #include #endif #define NJH_DEBUG #ifndef min #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif #ifdef MAX_STACK_DEPTH /* for ultrix 0x38, 4.3 bsd 0x3d, other? */ #ifdef vax #define CRT0_ADDRESS 0x3d #endif #ifdef sun #define CRT0_ADDRESS 0x204c #endif #ifdef M_XENIX #define CRT0_ADDRESS 0xD4 /* SCO Unix */ #endif #ifdef mips /*#define CRT0_ADDRESS 0x0 /* to be filled in later */ #endif unsigned mp_root_address = CRT0_ADDRESS; #endif /*MAX_STACK_DEPTH*/ /*#undef PP2WIN*/ #if defined(M_I86MM) || defined(M_I86SM) static void *lastfreed = (void *)-1; #else static void *lastfreed = (void *)-1L; #endif static int pagesz; /* page size */ #ifndef _NFILE #define _NFILE NOFILE #endif static int fds[_NFILE]; #ifdef NJH_DEBUG #ifdef PERPOS #define size_t long #define pid_t int #define INT_MAX MAXINT #define UINT_MAX (0xFFFFFFFF) char *getenv(); #endif /* * LATER: * Replace s_ptr with a pointer to a structure that contains * long underflowcheck; * void *data; * long overflowcheck; * free will check these values as will db_heapchk and blkchk, and where * possible access to these areas will be restricted. This would add an underrun * check * ptr2slot will need modifying, as will anything that looks for 'B' */ typedef struct _slotstr { #ifdef UNDERFLOW /* meaningless code! */ char s_underflowcheck; #endif void *s_ptr; /* the allocated area */ size_t s_size; /* its size */ unsigned int s_blkno; /* program block reference number */ unsigned int s_freed:1; /* whether it's been freed yet */ const char *s_file; unsigned int s_line; #ifdef MAX_STACK_DEPTH caddr_t s_history[MAX_STACK_DEPTH]; #endif } SLOT; #define MAXSLOT 1024L #if defined(sun) && !defined(ANSI) #define atexit(f) on_exit(f) #endif #ifdef PERPOS #define atexit(f) static void cdecl leaks(); int per_exit(status) { leaks(); exit(status); } #endif static unsigned long maxslots; bool check_for_leaks; static unsigned int in_db; #ifdef MSDOS typedef char *caddr_t; static SLOT huge *slots; #else static SLOT *slots; #endif static unsigned long slotc; static unsigned int blkno; #define wsize sizeof(unsigned int) /* 386BSD stuff */ #define wmask (wsize - 1) /* 386BSD stuff */ #ifndef ANSI static void _error(); static void *_malloc(); static void *_calloc(); static void *_realloc(); static void _free(); static void cdecl leaks(); #ifdef MAX_STACK_DEPTH static void mprof(); static void st_read(); #endif static const char *symname(); #else #ifdef solaris static void _error(const char *file, unsigned int line, const caddr_t *history, char *format, ...); #else static void _error(const char *file, unsigned int line, const caddr_t *history, const char *format, ...); #endif static void *_malloc(size_t size, const char *file, unsigned int line); static void *_calloc(size_t nel, size_t size, const char *file, unsigned int line); static void *_realloc(char *ptr, size_t size, const char *file, unsigned int line); static void _free(char *ptr, const char *file, unsigned int line); static void cdecl leaks(void); #ifdef MAX_STACK_DEPTH static void mprof(SLOT *sp); static void st_read(void); static const char *symname(caddr_t pc); #endif #endif #ifndef MSDOS #undef strcmp #undef memcpy #undef malloc #undef calloc #undef realloc #undef strdup #undef strcpy #undef free #undef heapchk #undef alloca #undef memset #undef strcat #endif #ifdef MSDOS static void *real_memcpy(register void *m1, register const void *m2, register size_t n); static void *real_malloc(size_t nbytes); static void real_free(void *cp); static void *real_realloc(void *cp, size_t nbytes); #else #ifdef ANSI static void *real_memcpy(); static void *real_malloc(); static void *real_realloc(); #else static char *real_memcpy(); static char *real_malloc(); static char *real_realloc(); #endif /*ANSI*/ static void real_free(); #endif /*MSDOS*/ static void #ifdef ANSI #ifdef solaris _error(const char *file, unsigned int line, const caddr_t *history, char *format, ...) /* stdargs method */ #else _error(const char *file, unsigned int line, const caddr_t *history, const char *format, ...) /* stdargs method */ #endif #else _error(file, line, history, format, va_alist) /* varargs method */ char *file; unsigned int line; char *format; const caddr_t *history; va_dcl #endif { va_list v; char thismessage[160]; /* 2.2 */ static char lastmessage[160]; in_db++; #ifdef MSDOS if(file /*&& (_fstrcmp(file, __FILE__) != 0)*/) #else if(file /*&& (strcmp(file, __FILE__) != 0)*/) #endif fprintf(stderr, "%u of %s: ", line, file); #ifdef MAX_STACK_DEPTH else { register int i; register const char *sym; SLOT sp; register const char **fn; /* * Functions to ignore in stack tracing */ static const char *ignore_functions[] = { "db_callocchk", "db_mallocchk", "db_strdupchk", "db_setname", "calloc", "malloc", "etext", NULL }; st_read(); if(history == NULL) { mprof(&sp); history = sp.s_history; } for(i = 0; (history[i] > (caddr_t)mp_root_address) && (i < MAX_STACK_DEPTH); i++) if(sym = symname(history[i])) { if(*sym) { for(fn = ignore_functions; *fn; fn++) if(strcmp(sym, *fn) == 0) break; if(*fn) continue; fprintf(stderr, "Around %s: ", sym); /*if(strcmp(sym, "main") == 0)*/ break; } } else break; } #endif /*MAX_STACK_DEPTH*/ #if defined(__STDC__) || defined(_MSC_VER) || defined(ANSI) va_start(v, format); #else va_start(v); #endif vsprintf(thismessage, format, v); va_end(v); if(file || (strcmp(lastmessage, thismessage) != 0)) { fputs(thismessage, stderr); putc('\n', stderr); strcpy(lastmessage, thismessage); } in_db--; } #ifdef UNIX /* * Does ptr point to a valid array of at least fd bytes */ static bool isptrok(ptr, size) const void *ptr; size_t size; { static int fd; if(size == 0) return(true); if(ptr == NULL) { _error(NULL, 0, NULL, "Null pointer to %u bytes", size); return(false); } if(in_db) return(true); if(fd == 0) fd = open("/dev/null", O_WRONLY); if(fd > 0) { register int i; register const char *cptr; if(size == INT_MAX) size = 1; #if 0 if((write(fd, ptr, size) < 0) && (errno == EFAULT)) { _error(NULL, 0, NULL, "Invalid pointer to %u bytes", size); return(false); } #else cptr = (const char *)ptr; for(i = 0; i < size; i++) if((write(fd, cptr++, 1) < 0) && (errno == EFAULT)) { if(i == 0) _error(NULL, 0, NULL, "Invalid Pointer to %u bytes", size); else _error(NULL, 0, NULL, "Pointer to %u bytes only points to %u bytes", size, i); return(false); } #endif } return(true); } #else static bool isptrok(ptr, size) const void *ptr; size_t size; { if(ptr == NULL) { #ifdef M_I86LM _error(NULL, 0, NULL, "Null pointer to %lu bytes", (unsigned long)size); #else _error(NULL, 0, NULL, "Null pointer to %u bytes", (unsigned int)size); #endif return(false); } return(true); } #endif #ifdef MSDOS #define CACHE_SIZE 32 #define CACHE_SHIFT 5 #define CACHE_MASK 0x1F #else #define CACHE_SIZE 64 #define CACHE_SHIFT 6 #define CACHE_MASK 0x3F #endif /*#define CACHE_TRACE*/ #if CACHE_SIZE static struct cache { unsigned long index; bool isvalid; #ifdef M_I86LM unsigned long tag; #else unsigned int tag; #endif #ifdef CACHE_TRACE unsigned int hits; unsigned int misses; #endif } cache[CACHE_SIZE]; static void clear_cache() { register struct cache *c; for(c = cache; c < &cache[CACHE_SIZE]; c++) c->isvalid = false; } #else static void clear_cache() { } #endif /* * NJH - get an sp from a pointer */ #ifdef MSDOS static SLOT huge * #else static SLOT * #endif #ifdef ANSI ptr2slot(const void *ptr) #else ptr2slot(ptr) void *ptr; #endif { #ifdef M_I86LM register unsigned long tag; #else register unsigned int tag; #endif #ifdef MSDOS register SLOT huge *sp; #else register SLOT *sp; #endif register void *optr; #if CACHE_SIZE register struct cache *cacheelem; #endif if(slotc) { #if CACHE_SIZE /* * cache, but not if NULL - it may have been allocated * since the last call */ #ifdef M_I86LM cacheelem = &cache[(unsigned long)ptr & CACHE_MASK]; tag = (unsigned long)ptr >> CACHE_SHIFT; #else cacheelem = &cache[(unsigned int)ptr & CACHE_MASK]; tag = (unsigned int)ptr >> CACHE_SHIFT; #endif if(cacheelem->isvalid && (tag == cacheelem->tag) && slots[cacheelem->index].s_ptr) { #ifdef assert assert(slots[cacheelem->index].s_ptr == ptr); #endif #ifdef CACHE_TRACE cacheelem->hits++; #endif return(&slots[cacheelem->index]); } #ifdef CACHE_TRACE /* * Strictly speaking all times we're here counts as a miss, * but I'm really interested in looking for hot-spots, * not the initial filling of the cache */ if(cacheelem->isvalid) cacheelem->misses++; #endif sp = slots; if(sp->s_ptr == ptr) { cacheelem->tag = tag; cacheelem->index = 0L; cacheelem->isvalid = true; return(sp); } #else sp = slots; if(sp->s_ptr == ptr) return(sp); #endif /*CACHE_SIZE*/ optr = slots[0].s_ptr; slots_rw(); slots[0].s_ptr = (void *)ptr; /* post a sentinal */ /* start search at end - most likely that a match'll be there */ for(sp = &slots[slotc - 1L]; sp->s_ptr != ptr; --sp) ; slots[0].s_ptr = optr; slots_readonly(); #if CACHE_SIZE if(sp != slots) { cacheelem->tag = tag; cacheelem->index = sp - slots; cacheelem->isvalid = true; return(sp); } #else if(sp != slots) return(sp); #endif } return(NULL); } #ifdef MSDOS #ifndef PP2WIN static void huge * hmemcpy(register void huge *m1, register const void huge *m2, unsigned long l) { register unsigned char huge *dest = m1; register const unsigned char huge *source = m2; while(l--) *dest++ = *source++; return(m1); } static void huge * hmemset(register void huge *s, unsigned char ch, unsigned long l) { register unsigned char huge *t; t = s; while(l--) *t++ = (unsigned char)ch; return(s); } #endif #endif static size_t last_free_size; /* * _malloc - wrapper around malloc. Warns if unusual size given, or the * real malloc returns a NULL pointer. Returns a pointer to the * malloc'd area */ static void * #ifdef ANSI _malloc(size_t size, const char *file, unsigned int line) #else _malloc(size, file, line) size_t size; char *file; unsigned int line; #endif { #ifdef MSDOS #ifdef PP2WIN static HGLOBAL hslots; register HGLOBAL hnewslots; #endif register SLOT huge *sp; #else register SLOT *sp, *oldslots; #endif register char *ptr; register int i; static bool hasprinted; bool dofds; if(!hasprinted) { check_for_leaks = hasprinted = true; atexit(leaks); dofds = 1; } else dofds = 0; if(size == 0) { _error(file, line, NULL, "malloc: 0 bytes wanted"); /*return(NULL);*/ } else if(size == last_free_size) { in_db++; for(sp = slots; sp != &slots[slotc]; sp++) if(sp->s_freed && sp->s_file && file && (strcmp(sp->s_file, file) == 0) && (sp->s_size == last_free_size)) { _error(file, line, NULL, "malloc: %d bytes wanted which was freed at %d of %s", size, sp->s_line, sp->s_file); break; } in_db--; } #ifdef PP2WIN if((ptr = (char *)rhpaca(1, size + 1)) == (char *) NULL) { _error(file, line, NULL, "malloc: unable to malloc %u bytes", size); return(NULL); } #elif defined(M_I86LM) if((ptr = (char *)_fmalloc(size + 1)) == (char *) NULL) { _error(file, line, NULL, "malloc: unable to malloc %u bytes", size); return(NULL); } #elif defined(MSDOS) && (defined(M_I86MM) || defined(M_I86SM)) if((ptr = (char *)_nmalloc(size + 1)) == (char *) NULL) { _error(file, line, NULL, "malloc: unable to malloc %u bytes", size); return(NULL); } #elif defined(MSDOS) if((ptr = (char *)malloc(size + 1)) == (char *) NULL) { _error(file, line, NULL, "malloc: unable to malloc %u bytes", size); return(NULL); } #elif defined(MPROTECT) if(!in_db) { /* * Wasteful of memory - but it allows mprotect to work */ unsigned int alignment = (pagesz) ? pagesz : getpagesize(); size_t nsize; #define round_to(i, nearest) \ ((((i) + (nearest) - 1) / (nearest)) * (nearest)) nsize = round_to(size + 1, sizeof(int)) + alignment; ptr = real_malloc(nsize); if(ptr == (char *)NULL) { _error(file, line, NULL, "malloc: unable to malloc %u bytes", size); return(NULL); } mprotect(ptr, nsize, PROT_READ|PROT_WRITE); ptr = (char *)round_to((unsigned int)ptr, alignment); } else if((ptr = (char *)real_malloc(size + 1)) == (char *) NULL) { _error(file, line, NULL, "malloc: unable to malloc %u bytes", size); return(NULL); } else mprotect(ptr, size + 1, PROT_READ|PROT_WRITE); #else if((ptr = (char *)real_malloc(size + 1)) == (char *) NULL) { _error(file, line, NULL, "malloc: unable to malloc %u bytes", size); return(NULL); } #ifdef WATCH rw(ptr, size + 1); #endif #endif ptr[size] = 'B'; /* crude bounds check */ ro(&ptr[size], 1); if(slots == NULL) { in_db++; #ifdef MSDOS #ifdef PP2WIN hslots = GlobalAlloc(GMEM_MOVEABLE|GMEM_NODISCARD|GMEM_ZEROINIT, MAXSLOT * sizeof(SLOT)); slots = (SLOT huge *)GlobalLock(hslots); #else slots = (SLOT huge *)halloc(MAXSLOT, sizeof(SLOT)); hmemset(slots, '\0', MAXSLOT * sizeof(SLOT)); #endif #else slots = (SLOT *)real_malloc(MAXSLOT * sizeof(SLOT)); memset(slots, '\0', MAXSLOT * sizeof(SLOT)); #endif maxslots = MAXSLOT; clear_cache(); in_db--; slots_rw(); } sp = ptr2slot(ptr); if(sp == NULL) { if(slotc == maxslots - 1L) { /* maybe we can salvage something */ for(sp = slots; sp != &slots[slotc]; sp++) if(sp->s_ptr == NULL) break; /* probably realloced */ if(sp == &slots[slotc]) { /* run out of slots */ #ifdef MSDOS #ifdef PP2WIN hnewslots = GlobalAlloc(GMEM_MOVEABLE|GMEM_NODISCARD|GMEM_ZEROINIT, (maxslots + MAXSLOT) * sizeof(SLOT)); if(hnewslots == (HGLOBAL)NULL) { rhpfree(ptr); rhpw_mbox("can't increase maxslots", "Callbook on disc"); return(NULL); } sp = (SLOT huge *)GlobalLock(hnewslots); hmemcpy(sp, slots, maxslots * sizeof(SLOT)); GlobalUnlock(hslots); GlobalFree(hslots); slots = sp; hslots = hnewslots; #else /*!PP2WIN*/ if(sp = (SLOT huge *)halloc(maxslots + MAXSLOT, sizeof(SLOT))) hmemcpy(sp, slots, maxslots * sizeof(SLOT)); else { #ifdef M_I86LM _ffree(ptr); #else _nfree(ptr); #endif _error(file, line, NULL, "can't increase maxslots to %lu", maxslots); return(NULL); } hfree(slots); slots = sp; hmemset(&slots[maxslots], '\0', MAXSLOT * sizeof(SLOT)); #endif /*PP2WIN*/ maxslots += MAXSLOT; #else /*!MSDOS*/ slots_rw(); maxslots += MAXSLOT; oldslots = slots; slots = (SLOT *)real_realloc((char *)slots, maxslots * sizeof(SLOT)); slots_rw(); in_db++; memset(&slots[maxslots - MAXSLOT], '\0', MAXSLOT * sizeof(SLOT)); in_db--; #endif sp = &slots[slotc++]; /* * 2.4.1 fix */ #ifdef UNIX if(slots != oldslots) { clear_cache(); none((caddr_t)oldslots, (maxslots - MAXSLOT) * sizeof(SLOT)); } #else clear_cache(); #endif } } else sp = &slots[slotc++]; } else if(!sp->s_freed) _error(file, line, NULL, "malloc: malloc returned a non-freed pointer"); #ifdef MAX_STACK_DEPTH if(!in_db) mprof(sp); #endif if((unsigned)(sp - slots) > maxslots) fputs("sp overflow\n", stderr); slots_rw(); sp->s_size = size; sp->s_freed = false; sp->s_ptr = ptr; sp->s_blkno = blkno; sp->s_file = file; sp->s_line = line; #ifdef UNDERFLOW sp->s_underflowcheck = 'B'; #endif slots_readonly(); if(dofds) /* * Leave this to the last possible moment so that /dev/null * and /dev/zero have already been opened. That should stop us * getting bogus fd leak errors */ for(i = 0; i < _NFILE; i++) fds[i] = ((lseek(i, (off_t)0, SEEK_CUR) >= 0) || (errno != EBADF)); return(ptr); } /* * _calloc - wrapper for calloc. Calls _malloc to allocate the area, and * then sets the contents of the area to NUL bytes. Returns its address. */ static void * #ifdef ANSI _calloc(size_t nel, size_t size, const char *file, unsigned int line) #else _calloc(nel, size, file, line) size_t nel, size; char *file; unsigned int line; #endif { register size_t tot; register void *ptr; tot = nel * size; if(ptr = _malloc(tot, file, line)) { in_db++; memset(ptr, '\0', tot); in_db--; } return(ptr); } /* * _realloc - wrapper for realloc. Checks area already alloc'd and * not freed. Returns its address */ static void * #ifdef ANSI _realloc(char *ptr, size_t size, const char *file, unsigned int line) #else _realloc(ptr, size, file, line) char *ptr; size_t size; char *file; unsigned int line; #endif { register char *optr = ptr; #ifdef MSDOS register SLOT huge *sp, const huge *nsp; #ifdef PP2WIN register char *nptr; #endif #else register SLOT *sp, *nsp; #endif if(slots == NULL) _error(file, line, NULL, "realloc: called before any alloc"); sp = ptr2slot(ptr); if(sp == NULL) { _error(file, line, NULL, "realloc: realloc on unallocated area"); return(NULL); } if(sp->s_freed) { _error(file, line, NULL, "realloc: realloc on freed area (block freed at %d of %s)", sp->s_line, sp->s_file); return(NULL); } in_db++; if(ptr[sp->s_size] != 'B') /* bounds check */ _error(file, line, NULL, "realloc: overflow of %u bytes", sp->s_size); #ifdef UNDERFLOW else if(sp->s_underflowcheck != 'B') _error(file, line, NULL, "realloc: underflow of %u bytes", sp->s_size); #endif else if(sp->s_size == size) { if(sp->s_file) _error(file, line, NULL, "realloc: want same size %u bytes as %d of %s", size, sp->s_line, sp->s_file); else _error(file, line, NULL, "realloc: want same size %u bytes", size); } else if(size == 0) _error(file, line, NULL, "realloc: 0 bytes wanted"); #ifdef PP2WIN else if((nptr = rhpaca(1, size + 1)) == (char *)NULL) _error(file, line, NULL, "realloc: realloc failure %u bytes", size); #elif defined(M_I86LM) else if((ptr = _frealloc(ptr, size + 1)) == (char *)NULL) _error(file, line, NULL, "realloc: realloc failure %u bytes", size); #elif defined(MSDOS) && (defined(M_I86MM) || defined(M_I86SM)) else if((ptr = _nrealloc(ptr, size + 1)) == (char *)NULL) _error(file, line, NULL, "realloc: realloc failure %u bytes", size); #else else if((ptr = real_realloc(ptr, size + 1)) == (char *)NULL) _error(file, line, NULL, "realloc: realloc failure %u bytes", size); #endif else { if(ptr != optr) if(nsp = ptr2slot(optr)) none(optr, nsp->s_size); rw(ptr, size); #ifdef PP2WIN real_memcpy(nptr, ptr, min(sp->s_size, size)); rhpfree(ptr); /* don't call freechk here */ ptr[size - 1] = '\0'; ptr = nptr; #endif /* * If it's a completely new pointer, mark that * the old pointer is no longer used. * * If we're still using the old pointer to save * reshuffle/memcpy in real_realloc, make sure that the new * pointer is a sensible one */ slots_rw(); for(nsp = slots; nsp != &slots[slotc]; nsp++) if((nsp->s_ptr == optr) && (ptr != optr)) nsp->s_freed = true; else if((nsp->s_ptr == ptr) && (nsp != sp)) { if(!nsp->s_freed) _error(file, line, NULL, "realloc: returns pointer already in use"); nsp->s_ptr = NULL; } /* Clearing the entire cache is OTT but safe */ clear_cache(); rw(&ptr[size], 1); ptr[size] = 'B'; /* crude bounds check */ ro(&ptr[size], 1); sp->s_ptr = ptr; sp->s_size = size; sp->s_blkno = blkno; sp->s_freed = false; sp->s_file = file; sp->s_line = line; #ifdef UNDERFLOW sp->s_underflowcheck = 'B'; #endif slots_readonly(); } in_db--; return(ptr); } #ifdef sun static const SLOT * checkembeddedptr(memblock, size) const void *memblock; int size; { (void)isptrok(memblock, size); return(NULL); } #else #ifdef MSDOS static const SLOT huge * #else static const SLOT * #endif #ifdef ANSI checkembeddedptr(const void *memblock, int size) #else checkembeddedptr(memblock, size) char *memblock; int size; #endif { #ifdef ANSI const void **p; #else void **p; #endif #ifdef MSDOS register const SLOT huge *sp; #else register const SLOT *sp; #endif #ifdef sun /* Need to word align the pointer - sigh */ { unsigned long t; t = (unsigned long)memblock; if(t & wmask) { t &= ~wmask; p = (void **)t; p += sizeof(void *); size -= sizeof(void *); } else p = (void **)memblock; } #else #ifdef ANSI p = (const void **)memblock; #else p = (void **)memblock; #endif #endif if(!isptrok(p, size)) /* 2.1.5 */ return(NULL); while(size > 0) { if(*p && (sp = ptr2slot(*p))) if(!sp->s_freed) return(sp); p++; /* 2.1.5 - was p += sizeof(void *) */ size -= sizeof(void *); } return(NULL); } #endif /* 2.1.3 added const */ static const unsigned int strdupline = __LINE__; /* * _free - wrapper for free. Loop through allocated slots, until you * find the one corresponding to pointer. If none, then it's an attempt * to free an unallocated area. If it's already freed, then tell user. */ static void #ifdef ANSI _free(char *memblock, const char *file, unsigned int line) #else _free(memblock, file, line) char *memblock; char *file; unsigned int line; #endif { #ifdef MSDOS register SLOT huge *sp; register const SLOT huge *ssp; #else register SLOT *sp; register const SLOT *ssp; #endif sp = ptr2slot(memblock); in_db++; if(sp == NULL) { _error(file, line, NULL, "free: free not previously malloc'd"); in_db--; return; } if(sp->s_freed) _error(file, line, NULL, "free after previous freeing %u bytes", sp->s_size); else if(memblock[sp->s_size] != 'B') /* bounds check */ _error(file, line, NULL, "free: overflow of %u bytes allocated %u of %s", sp->s_size, sp->s_line, sp->s_file); #ifdef UNDERFLOW else if(sp->s_underflowcheck != 'B') _error(file, line, NULL, "free: underflow of %u bytes allocated %u of %s", sp->s_size, sp->s_line, sp->s_file); #endif else { /* * LATER: warn if the block to be freed contains any pointers * which haven't been freed. Warn if it does, otherwise * clear the area */ rw(memblock, sp->s_size); ssp = NULL; if(file /*&& (strcmp(file, __FILE__) != 0)*/) { /* * Find the first part of the memory that was * actually used at some time */ register char *ptr; register size_t nbytes = sp->s_size; for(ptr = memblock; nbytes && (*ptr == '\0'); ptr++) nbytes--; /* * The checkembeddedptr code produces a lot of false * positives when freeing a linked list, even * if the code is correct. * TODO: only warn on first embeddedpoiner free */ if(nbytes == 0) { if(sp->s_file) _error(file, line, NULL, "free: %u bytes (allocated %d of %s) may never have been used", sp->s_size, sp->s_line, sp->s_file); else _error(file, line, NULL, "free: %u bytes may never have been used", sp->s_size); /*} else if(ssp = checkembeddedptr(ptr, nbytes)) {*/ } else if(ssp = checkembeddedptr(memblock, sp->s_size)) { if(ssp->s_file) { if((strcmp(ssp->s_file, __FILE__) == 0) && (ssp->s_line == strdupline)) _error(file, line, NULL, "free: pointer possibly points to data which contains unfree strdup: \"%s\"", (const char *)ssp->s_ptr); else _error(file, line, NULL, "free: pointer possibly points to data which contains unfree pointer from %d of %s", ssp->s_line, ssp->s_file); } else _error(file, line, NULL, "free: pointer possibly points to data which contains unfree pointer to %u bytes", ssp->s_size); } } else if(ssp = checkembeddedptr(memblock, sp->s_size)) { if(ssp->s_file) { if((strcmp(ssp->s_file, __FILE__) == 0) && (ssp->s_line == strdupline)) _error(NULL, 0L, NULL, "free: pointer possibly points to data which contains unfree strdup: \"%s\"", (const char *)ssp->s_ptr); else _error(NULL, 0, NULL, "free: pointer possibly points to data which contains unfree pointer from %d of %s", ssp->s_line, ssp->s_file); } else _error(NULL, 0, NULL, "free: pointer possibly points to data which contains unfree pointer to %u bytes", ssp->s_size); } #ifdef PP2WIN rhpfree(memblock); #elif defined(M_I86LM) (void)_ffree(memblock); #elif defined(MSDOS) && (defined(M_I86MM) || defined(M_I86SM)) (void)_nfree(memblock); #else (void)real_free(memblock); #endif /* if(sp->s_file[0]) sp->s_file[0] = '\0'; */ if(ssp == NULL) { #ifdef MSDOS memset(memblock, '\0', sp->s_size); /* stop re-use of the area */ #else /* * Also stop access to the 'B' byte */ /*rw(&memblock[sp->s_size], 1);*/ none(memblock, sp->s_size + 1); #endif } } slots_rw(); sp->s_freed = true; sp->s_file = file; sp->s_line = line; slots_readonly(); last_free_size = sp->s_size; in_db--; } void _pascal /* NJH */ #ifdef ANSI db_heapchk(const char *file, int line) #else db_heapchk(file, line) char *file; int line; #endif { #ifdef MSDOS register SLOT huge *sp; #else register SLOT *sp; #endif #if defined(_MSC_VER) && (_MSC_VER >= 600) #ifndef PP2WIN if(_heapchk() != _HEAPOK) _error(file, line, NULL, "Possible memory corruption"); #endif #endif for(sp = slots; sp != &slots[slotc]; sp++) if((!sp->s_freed) && sp->s_file) { register const char *ptr = sp->s_ptr; if(ptr == NULL) { _error(file, line, NULL, "Unexpected NULL pointer"); return; } #ifndef MSDOS rw(&ptr[sp->s_size], 1); #endif if(!isptrok(ptr, sp->s_size)) _error(file, line, NULL, "Pointer to %u bytes from %d of %s has become invalid", sp->s_size, sp->s_line, sp->s_file); #ifdef UNDERFLOW else if((ptr[sp->s_size] != 'B') || (sp->s_underflowcheck != 'B')) #else else if(ptr[sp->s_size] != 'B') #endif _error(file, line, NULL, "Corruption of %u bytes from %d of %s", sp->s_size, sp->s_line, sp->s_file); } } /* * _blkstart - start of a program block. Increase the block reference * number by one. */ void _blkstart() { blkno++; } /* * _blkend - end of a program block. Check all areas allocated in this * block have been freed. Decrease the block number by one. */ void _blkend() { #ifdef MSDOS register SLOT huge *sp; #else register SLOT *sp; #endif if(blkno == 0) { _error(NULL, 0, NULL, "_blkend: unmatched call to _blkend"); return; } for(sp = slots; sp != &slots[slotc]; sp++) if(sp->s_blkno == blkno && !sp->s_freed) _error(NULL, 0, NULL, "_blkend: %u bytes unfreed", sp->s_size); blkno--; } /* * _blkignore - find the slot corresponding to ptr, and set its block * number to zero, to avoid _blkend picking it up when checking. */ void _blkignore(ptr) void *ptr; { #ifdef MSDOS register SLOT huge *sp; #else register SLOT *sp; #endif if(sp = ptr2slot(ptr)) sp->s_blkno = 0; else _error(NULL, 0, NULL, "_blkignore: pointer has not been allocated"); } /* * NJH: * Check a block is still valid */ bool #ifdef ANSI _blkchk(const char *ptr) #else _blkchk(ptr) char *ptr; #endif { #ifdef MSDOS register SLOT huge *sp; #else register SLOT *sp; #endif if(sp = ptr2slot(ptr)) { #ifdef UNDERFLOW if((!sp->s_freed) && ((ptr[sp->s_size] != 'B') || (sp->s_underflowcheck != 'B'))) { #else if((!sp->s_freed) && (ptr[sp->s_size] != 'B')) { #endif _error(NULL, 0, NULL, "_blkcheck: possible corruption of %u bytes", sp->s_size); return(false); } return(true); /* it's one of ours so it's safe */ } return(isptrok(ptr, (sp) ? sp->s_size : 1)); } /* what's the length of the thing this points to */ static size_t #ifdef ANSI blklen(const void *ptr, bool isstring) #else blklen(ptr, isstring) void *ptr; bool isstring; #endif { register size_t len = (_blkchk(ptr) && ptr && isstring) ? strlen(ptr) : 0; if(slots) { #ifdef MSDOS register const SLOT huge *sp; #else register const SLOT *sp; #endif if(sp = ptr2slot(ptr)) if(!sp->s_freed) { /* may be allocated elsewhere */ if(len > sp->s_size) _error(NULL, 0, NULL, "string may not be NUL terminated"); return(sp->s_size); } } if(ptr && isstring) return(len + 1); /* * It would seem sensible to return UINT_MAX here, but on some systems * size_t is a signed int (e.g. on sunos) */ return(INT_MAX); } #endif /*NJH_DEBUG*/ /* * common/debug.c: * Allocate & free memory plus checks */ void *pascal db_mallocchk(size, file, line) size_t size; const char *file; int line; { void *p = db_callocchk(1, size, file, line); if(p) { in_db++; memset(p, 0xEE, size); in_db--; } return(p); } void *pascal db_callocchk(nelem, size, file, line) /* like calloc */ size_t nelem, size; const char *file; int line; { register void *area; #ifdef MSDOS if((long)nelem * (long)size > 65535L) { #ifdef NJH_DEBUG _error(file, line, NULL, "Too big"); #else errstring = "Too big"; #endif return(NULL); } #endif lastfreed = NULL; #ifdef NJH_DEBUG if(area = _calloc(nelem, size, file, line)) return(area); #else #ifdef PP2WIN if(area = rhpaca((PP16)nelem, (PP16)size)) return(area); #else if(area = calloc(nelem, size)) return(area); #endif /*PP2WIN*/ #endif /* remove_urgent(); */ #ifdef PP2WIN if(area = rhpaca((PP16)nelem, (PP16)size)) return(area); #else #ifdef MSDOS _heapmin(); #endif #ifdef NJH_DEBUG if(area = _calloc(nelem, size, file, line)) return(area); #else if(area = calloc(nelem, size)) return(area); #endif #endif /*PP2WIN*/ #ifdef NJH_DEBUG _error(file, line, NULL, "calloc of %u bytes failed", nelem * size); #endif return((void *)NULL); } void *pascal db_reallocchk(oarea, size, file, line) /* like realloc */ void *oarea; size_t size; const char *file; int line; { register void *area; if(oarea == NULL) return(db_mallocchk(size, file, line)); lastfreed = NULL; #ifdef NJH_DEBUG if(area = _realloc(oarea, size, file, line)) return(area); #else #ifdef PP2WIN area = db_mallocchk(size, file, line); if(area == NULL) return((void *)NULL); real_memcpy(area, oarea, size); rhpfree(oarea); return(area); #else if(area = realloc(oarea, size)) return(area); #endif #endif /* remove_urgent(); */ #ifdef MSDOS _heapmin(); #endif #ifdef NJH_DEBUG if(area = _realloc(oarea, size, file, line)) return(area); _error(file, line, NULL, "realloc: No more memory"); return((void *)NULL); #else return(realloc(oarea, size)); #endif } #ifdef NJH_DEBUG #undef strdup #ifndef PERPOS char *cdecl #ifdef ANSI strdup(const char *string) #else strdup(string) char *string; #endif { /* 2.1.3 changed NULL to __FILE__ */ return(db_strdupchk(string, __FILE__, strdupline)); } #endif char *cdecl #ifdef ANSI _strdup(const char *string) #else _strdup(string) char *string; #endif { /* 2.1.3 changed NULL to __FILE__ */ return(db_strdupchk(string, __FILE__, strdupline)); } char *pascal #ifdef ANSI db_strdupchk(const char *string, const char *file, int line) /* like strdup */ #else db_strdupchk(string, file, line) /* like strdup */ char *string, *file; int line; #endif { register char *area; #ifndef PP2WIN register size_t len; #endif if(string == NULL) { _error(file, line, NULL, "Attempt to strdup NULL"); return(NULL); } if(!_blkchk(string)) return(NULL); lastfreed = NULL; #ifdef PP2WIN area = db_mallocchk(strlen(string) + 1, file, line); if(area == NULL) return((char *)NULL); return(strcpy(area, string)); #else len = strlen(string) + 1; if(area = _malloc(len, file, line)) return(real_memcpy(area, string, len)); #ifdef MSDOS _heapmin(); if(area = _malloc(len, file, line)) return(real_memcpy(area, string, len)); #endif #ifdef NJH_DEBUG _error(file, line, NULL, "strdupchk: No more memory"); #endif return((char *)NULL); #endif /*PP2WIN*/ } #ifndef DBMALLOC int cdecl #ifdef ANSI strcmp(register const char *s1, register const char *s2) #else strcmp(s1, s2) register char *s1, *s2; #endif { if(in_db == 0) { if((s1 == NULL) || (s2 == NULL)) { _error(NULL, 0, NULL, "Attempt to strcmp NULL"); return(0); } if(s1 == s2) { _error(NULL, 0, NULL, "strcmp: no affect (\"%s\")", s1); return(0); } _blkchk(s1); _blkchk(s2); } while(*s1 == *s2++) if(*s1++ == '\0') return(0); return(*(unsigned char *)s1 - *(unsigned char *)--s2); } char *cdecl #ifdef ANSI strcpy(register char *s1, register const char *s2) #else strcpy(s1, s2) register char *s1, *s2; #endif { if(in_db == 0) { register size_t sz1, sz2; if(s1 == NULL) { _error(NULL, 0, NULL, "Attempt to strcpy to NULL"); return(NULL); } if(s2 == NULL) { _error(NULL, 0, NULL, "Attempt to strcpy from NULL"); return(NULL); } if(s1 == s2) { _error(NULL, 0, NULL, "strcpy: %s (no affect)", s1); return(NULL); } sz1 = blklen(s1, false); if(sz1 != INT_MAX) { /* +1 because of the null byte */ sz2 = strlen(s2) + 1; if(sz2 > sz1) { _error(NULL, 0, NULL, "Attempt to strcpy \"%s\" on top of %u bytes", s2, sz1); return(NULL); } if(sz2 > 3 * wsize) { /* We know the length */ return(real_memcpy(s1, s2, sz2)); } } } { register char *ret = s1; while(*s1++ = *s2++) ; return(ret); } /*{ register char c; register const int off = s1 - s2 - 1; do c = *s2++; while(s1[off] = c]); return(s1); }*/ } char *cdecl #ifdef ANSI strcat(register char *s1, register const char *s2) #else strcat(s1, s2) register char *s1, *s2; #endif { register char *ret = s1; if(in_db == 0) { if((s1 == NULL) || (s2 == NULL)) { _error(NULL, 0, NULL, "Attempt to strcat NULL"); return(NULL); } _blkchk(s1); _blkchk(s2); if(strlen(s2) > strlen(s2) + blklen(s2, false)) _error(NULL, 0, NULL, "May not be enough room for strcat"); } while(*s1++) ; --s1; while(*s1++ = *s2++) ; return(ret); } #ifdef ANSI void *_pascal db_memcpy(register void *m1, register const void *m2, size_t n, const char *file, int line) #else void *_pascal db_memcpy(m1, m2, n, file, line) register char *m1, *m2; size_t n; char *file; #endif { if((n == 0) || (m1 == m2)) { /* nothing to do */ /* * On SunOs printfs generate memcpy with 0 bytes, so * this message appears from printfs in this file, hence * the need to check even if in_db - we don't want to call * real_memcpy with 0 bytes as it'll dump core */ if(!in_db) _error(file, line, NULL, "memcpy: %s (no affect)", (n == 0) ? "n == 0" : "m1 == m2"); return(m1); } if(in_db == 0) { register size_t size; if((m1 == NULL) || (m2 == NULL)) { _error(file, line, NULL, "Attempt to memcpy NULL"); return(NULL); } size = blklen(m1, false); if(n > size) { _error(file, line, NULL, "Attempt to memcpy %u on top of %u bytes", n, size); n = size; } if(!isptrok(m1, size)) return(NULL); size = blklen(m2, false); if(n > size) { _error(file, line, NULL, "Attempt to memcpy %u from %u bytes", n, size); n = size; #ifdef M_I86SM } else if(((unsigned short)m1 > (unsigned short)m2) && ((unsigned short)m1 < (unsigned short)m2 + n)) #else } else if(((unsigned long)m1 > (unsigned long)m2) && ((unsigned long)m1 < (unsigned long)m2 + n)) #endif _error(file, line, NULL, "Warning: overlapping memcpy"); if(!isptrok(m2, size)) return(NULL); } return(real_memcpy(m1, m2, n)); } #ifdef MSDOS /* can't trust sizeofs */ #ifdef ANSI void *cdecl memset(void *s, int ch, size_t n) #else char *cdecl memset(s, ch, n) void *s; int ch; size_t n; #endif { register unsigned char *t; if(in_db == 0) { register size_t size; if(s == NULL) { _error(NULL, 0, NULL, "Attempt to memset NULL"); return(NULL); } size = blklen(s, false); if(n > size) { _error(NULL, 0, NULL, "Attempt to memset %u with %u bytes", size, n); return(NULL); } } t = s; while(n--) *t++ = (unsigned char)ch; return(s); } static void * real_memcpy(register void *m1, register const void *m2, register size_t n) { register unsigned char *dest; register const unsigned char *source; dest = m1; source = m2; while(n--) *dest++ = *source++; return(m1); } #ifdef _MSC_VER /* the MSC provided fread/fwrite have a near call to memcmp */ size_t __cdecl fread(void *buffer, size_t size, size_t count, FILE *stream) { register size_t ret = 0; register size_t offset, buflen; register char *buf = (char *)buffer; register int c; buflen = blklen(buffer, false); if((count * size) > buflen) { _error(NULL, 0, NULL, "Attempt to fread %u on top of %u bytes", count * size, buflen); return(0); } while(count--) { for(offset = size; offset; offset--) { c = getc(stream); if(c == EOF) return(ret); *buf++ = (char)c; } ret++; } return(ret); } size_t __cdecl fwrite(const void *buffer, size_t size, size_t count, FILE *stream) { register size_t ret = 0; register size_t offset, buflen; register char *buf = (char *)buffer; buflen = blklen(buffer, false); if((count * size) > buflen) { _error(NULL, 0, NULL, "Attempt to fwrite %u on top of %u bytes", count * size, buflen); return(0); } while(count--) { for(offset = size; offset; offset--) { putc(*buf, stream); if(ferror(stream)) return(ret); buf++; } ret++; } return(ret); } #endif /*MSC*/ #else /*!MSDOS*/ #ifdef ANSI void *cdecl memset(void *s, int ch, size_t n) #else char *cdecl memset(s, ch, n) char *s; int ch; size_t n; #endif { register size_t size, t; register unsigned int c; register unsigned char *dst; if(in_db == 0) { if(s == NULL) { _error(NULL, 0, NULL, "Attempt to memset NULL"); return(NULL); } size = blklen(s, false); if(n > size) { _error(NULL, 0, NULL, "Attempt to memset %u with %u bytes", size, n); return(NULL); } } /*for(p = s; n--; *p++ = (unsigned char)ch) ;*/ dst = (unsigned char *)s; /* * If not enough words, just fill bytes. A length >= 2 words * guarantees that at least one of them is `complete' after * any necessary alignment. For instance: * * |-----------|-----------|-----------| * |00|01|02|03|04|05|06|07|08|09|0A|00| * ^---------------------^ * dst dst+length-1 * * but we use a minimum of 3 here since the overhead of the code * to do word writes is substantial. */ if(n < 3 * wsize) { while(n--) *dst++ = (unsigned char)ch; return(s); } if((c = (unsigned char)ch) != 0) { /* Fill the word. */ c = (c << 8) | c; /* u_int is 16 bits. */ #if UINT_MAX > 0xffff c = (c << 16) | c; /* u_int is 32 bits. */ #endif #if UINT_MAX > 0xffffffff c = (c << 32) | c; /* u_int is 64 bits. */ #endif } /* Align destination by filling in bytes. */ if((t = (int)dst & wmask) != 0) { t = wsize - t; n -= t; do *dst++ = (unsigned char)ch; while(--t != 0); } /* Fill words. Length was >= 2*words so we know t >= 1 here. */ t = n / wsize; do { *(unsigned int *)dst = c; dst += wsize; } while (--t != 0); /* Mop up trailing bytes, if any. */ t = n & wmask; /*if(t != 0) do *dst++ = (unsigned char)ch; while (--t != 0);*/ while(t--) *dst++ = (unsigned char)ch; return(s); } #ifdef ANSI static void *cdecl real_memcpy(void *m1, const void *m2, size_t n) #else static char *cdecl real_memcpy(m1, m2, n) char *m1, *m2; register size_t n; #endif { register char *dst = m1; register const char *src = m2; register int t; if ((unsigned long)dst < (unsigned long)src) { /* * Copy forward. */ t = (int)src; /* only need low bits */ if ((t | (int)dst) & wmask) { /* * Try to align operands. This cannot be done * unless the low bits match. */ if ((t ^ (int)dst) & wmask || n < wsize) t = n; else t = wsize - (t & wmask); n -= t; do *dst++ = *src++; while(--t); } /* * Copy whole words, then mop up any trailing bytes. */ t = n / wsize; /*if(t) do { *(unsigned int *)dst = *(unsigned int *)src; src += wsize; dst += wsize; } while(--t);*/ while(t--) { *(unsigned int *)dst = *(unsigned int *)src; src += wsize; dst += wsize; } t = n & wmask; /*if(t) do *dst++ = *src++; while(--t);*/ while(t--) *dst++ = *src++; } else { /* * Copy backwards. Otherwise essentially the same. * Alignment works as before, except that it takes * (t&wmask) bytes to align, not wsize-(t&wmask). */ src += n; dst += n; t = (int)src; if ((t | (int)dst) & wmask) { if ((t ^ (int)dst) & wmask || n <= wsize) t = n; else t &= wmask; n -= t; do *--dst = *--src; while(--t); } t = n / wsize; /*if(t) do { src -= wsize; dst -= wsize; *(unsigned int *)dst = *(unsigned int *)src; } while(--t);*/ while(t--) { src -= wsize; dst -= wsize; *(unsigned int *)dst = *(unsigned int *)src; } t = n & wmask; /*if(t) do *--dst = *--src; while(--t);*/ while(t--) *--dst = *--src; } return(m1); } #endif /*MSDOS*/ int cdecl #ifdef ANSI memcmp(const void *m1, const void *m2, size_t n) #else memcmp(m1, m2, n) register void *m1, *m2; size_t n; #endif { register const unsigned char *s1 = m1, *s2 = m2; if(in_db == 0) { register size_t size; if((m1 == NULL) || (m2 == NULL)) { _error(NULL, 0, NULL, "Attempt to memcmp NULL"); return(0); } if((n == 0) || (m1 == m2)) { /* nothing to do */ _error(NULL, 0, NULL, "memcmp: %s (no affect)", (n == 0) ? "n == 0" : "m1 == m2"); return(0); } size = blklen(m1, false); if(n > size) { _error(NULL, 0, NULL, "Attempt to memcmp arg1 %u for %u bytes", size, n); return(0); } size = blklen(m2, false); if(n > size) { _error(NULL, 0, NULL, "Attempt to memcmp arg2 %u for %u bytes", size, n); return(0); } } /* * We know n >= 1 because of the above test and because memcmp * isn't called from within this package, so do while is better */ do if(*s1++ != *s2++) return(*--s1 - *--s2); while(--n); return(0); } #endif /*DBMALLOC*/ #else /*!NJH_DEBUG*/ char *pascal db_strdupchk(string, file, line) /* like strdup */ const char *string, *file; { register char *area; if(string == NULL) { errstring = "Attempt to strdup NULL"; return(NULL); } lastfreed = NULL; #ifdef PP2WIN area = db_mallocchk(strlen(string) + 1, file, line); if(area == NULL) return((char *)NULL); return(strcpy(area, string)); #else if(area = strdup(string)) return(area); remove_urgent(); #ifdef MSDOS _heapmin(); #endif if(area = strdup(string)) return(area); if(errstring == (char *)NULL) errstring = MEM; return((char *)NULL); #endif /*PP2WIN*/ } #endif void pascal db_freechk(memblock, file, line) void *memblock; const char *file; int line; { if(memblock) { if(memblock == lastfreed) { _error(file, line, NULL, "Attempt to refree pointer"); return; } #ifdef NJH_DEBUG _free(memblock, file, line); #else #ifdef PP2WIN rhpfree(memblock); #else free(memblock); #endif #endif lastfreed = memblock; } else _error(file, line, NULL, "Attempt to free NULL pointer"); } #if defined(unix) || defined(_HPUX_SOURCE) || defined(UNIX) /* write around for DOS alloca: NJH call mallocchk and freechk */ /* alloca.c -- allocate automatically reclaimed memory (Mostly) portable public-domain implementation -- D A Gwyn This implementation of the PWB library alloca function, which is used to allocate space off the run-time stack so that it is automatically reclaimed upon procedure exit, was inspired by discussions with J. Q. Johnson of Cornell. J.Otto Tennant contributed the Cray support. There are some preprocessor constants that can be defined when compiling for your specific system, for improved efficiency; however, the defaults should be okay. The general concept of this implementation is to keep track of all alloca-allocated blocks, and reclaim any that are found to be deeper in the stack than the current invocation. This heuristic does not reclaim storage as soon as it becomes invalid, but it will do so eventually. As a special case, alloca(0) reclaims storage without allocating any. It is a good idea to use alloca(0) in your main control loop, etc. to force garbage collection. */ /*#ifdef HAVE_CONFIG_H*/ #if 0 #if defined (emacs) || defined (CONFIG_BROKETS) #include #else #include "config.h" #endif #endif /* If someone has defined alloca as a macro, there must be some other way alloca is supposed to work. */ #ifndef alloca #ifdef emacs #ifdef static /* actually, only want this if static is defined as "" -- this is for usg, in which emacs must undefine static in order to make unexec workable */ #ifndef STACK_DIRECTION you lose -- must know STACK_DIRECTION at compile-time #endif /* STACK_DIRECTION undefined */ #endif /* static */ #endif /* emacs */ /* If your stack is a linked list of frames, you have to provide an "address metric" ADDRESS_FUNCTION macro. */ #if defined (CRAY) && defined (CRAY_STACKSEG_END) long i00afunc (); #define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg)) #else #define ADDRESS_FUNCTION(arg) &(arg) #endif #if __STDC__ typedef void *pointer; #else typedef char *pointer; #endif #ifndef NULL #define NULL 0 #endif /* Different portions of Emacs need to call different versions of malloc. The Emacs executable needs alloca to call xmalloc, because ordinary malloc isn't protected from input signals. On the other hand, the utilities in lib-src need alloca to call malloc; some of them are very simple, and don't have an xmalloc routine. Non-Emacs programs expect this to call use xmalloc. Callers below should use malloc. */ #ifndef emacs /* #define malloc xmalloc */ #endif /* Define STACK_DIRECTION if you know the direction of stack growth for your system; otherwise it will be automatically deduced at run-time. STACK_DIRECTION > 0 => grows toward higher addresses STACK_DIRECTION < 0 => grows toward lower addresses STACK_DIRECTION = 0 => direction of growth unknown */ #ifndef STACK_DIRECTION #define STACK_DIRECTION 0 /* Direction unknown. */ #endif #if STACK_DIRECTION != 0 #define STACK_DIR STACK_DIRECTION /* Known at compile-time. */ #else /* STACK_DIRECTION == 0; need run-time code. */ static int stack_dir; /* 1 or -1 once known. */ #define STACK_DIR stack_dir static void find_stack_direction () { static char *addr = NULL; /* Address of first `dummy', once known. */ auto char dummy; /* To get stack address. */ if (addr == NULL) { /* Initial entry. */ addr = ADDRESS_FUNCTION (dummy); find_stack_direction (); /* Recurse once. */ } else { /* Second entry. */ if (ADDRESS_FUNCTION (dummy) > addr) stack_dir = 1; /* Stack grew upward. */ else stack_dir = -1; /* Stack grew downward. */ } } #endif /* STACK_DIRECTION == 0 */ /* An "alloca header" is used to: (a) chain together all alloca'ed blocks; (b) keep track of stack depth. It is very important that sizeof(header) agree with malloc alignment chunk size. The following default should work okay. */ #ifndef ALIGN_SIZE #define ALIGN_SIZE sizeof(double) #endif typedef union hdr { char align[ALIGN_SIZE]; /* To force sizeof(header). */ struct { union hdr *next; /* For chaining headers. */ char *deep; /* For stack depth measure. */ } h; } header; static header *last_alloca_header = NULL; /* -> last alloca header. */ /* Return a pointer to at least SIZE bytes of storage, which will be automatically reclaimed upon exit from the procedure that called alloca. Originally, this space was supposed to be taken from the current stack frame of the caller, but that method cannot be made to work for some implementations of C, for example under Gould's UTX/32. */ void *pascal alloca(size) size_t size; { return(db_alloca(size, NULL, 0)); } void *pascal db_alloca(size, file, line) size_t size; const char *file; int line; { auto char probe; /* Probes stack depth: */ register char *depth = ADDRESS_FUNCTION (probe); #if STACK_DIRECTION == 0 if (STACK_DIR == 0) /* Unknown growth direction. */ find_stack_direction (); #endif /* Reclaim garbage, defined as all alloca'd storage that was allocated from deeper in the stack than currently. */ { register header *hp; /* Traverses linked list. */ for (hp = last_alloca_header; hp != NULL;) if ((STACK_DIR > 0 && hp->h.deep > depth) || (STACK_DIR < 0 && hp->h.deep < depth)) { register header *np = hp->h.next; db_freechk ((pointer) hp, file, line); /* Collect garbage. */ hp = np; /* -> next header. */ } else break; /* Rest are not deeper. */ last_alloca_header = hp; /* -> last valid storage. */ } if (size == 0) return NULL; /* No allocation required. */ /* Allocate combined header + user data storage. */ { #ifdef NJH_DEBUG register pointer new = db_mallocchk (sizeof (header) + size, file, line); #else register pointer new = mallocchk (sizeof (header) + size); #endif /* Address of header. */ ((header *) new)->h.next = last_alloca_header; ((header *) new)->h.deep = depth; last_alloca_header = (header *) new; /* User storage begins just after header. */ return (pointer) ((char *) new + sizeof (header)); } } #if defined (CRAY) && defined (CRAY_STACKSEG_END) #ifdef DEBUG_I00AFUNC #include #endif #ifndef CRAY_STACK #define CRAY_STACK #ifndef CRAY2 /* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */ struct stack_control_header { long shgrow:32; /* Number of times stack has grown. */ long shaseg:32; /* Size of increments to stack. */ long shhwm:32; /* High water mark of stack. */ long shsize:32; /* Current size of stack (all segments). */ }; /* The stack segment linkage control information occurs at the high-address end of a stack segment. (The stack grows from low addresses to high addresses.) The initial part of the stack segment linkage control information is 0200 (octal) words. This provides for register storage for the routine which overflows the stack. */ struct stack_segment_linkage { long ss[0200]; /* 0200 overflow words. */ long sssize:32; /* Number of words in this segment. */ long ssbase:32; /* Offset to stack base. */ long:32; long sspseg:32; /* Offset to linkage control of previous segment of stack. */ long:32; long sstcpt:32; /* Pointer to task common address block. */ long sscsnm; /* Private control structure number for microtasking. */ long ssusr1; /* Reserved for user. */ long ssusr2; /* Reserved for user. */ long sstpid; /* Process ID for pid based multi-tasking. */ long ssgvup; /* Pointer to multitasking thread giveup. */ long sscray[7]; /* Reserved for Cray Research. */ long ssa0; long ssa1; long ssa2; long ssa3; long ssa4; long ssa5; long ssa6; long ssa7; long sss0; long sss1; long sss2; long sss3; long sss4; long sss5; long sss6; long sss7; }; #else /* CRAY2 */ /* The following structure defines the vector of words returned by the STKSTAT library routine. */ struct stk_stat { long now; /* Current total stack size. */ long maxc; /* Amount of contiguous space which would be required to satisfy the maximum stack demand to date. */ long high_water; /* Stack high-water mark. */ long overflows; /* Number of stack overflow ($STKOFEN) calls. */ long hits; /* Number of internal buffer hits. */ long extends; /* Number of block extensions. */ long stko_mallocs; /* Block allocations by $STKOFEN. */ long underflows; /* Number of stack underflow calls ($STKRETN). */ long stko_free; /* Number of deallocations by $STKRETN. */ long stkm_free; /* Number of deallocations by $STKMRET. */ long segments; /* Current number of stack segments. */ long maxs; /* Maximum number of stack segments so far. */ long pad_size; /* Stack pad size. */ long current_address; /* Current stack segment address. */ long current_size; /* Current stack segment size. This number is actually corrupted by STKSTAT to include the fifteen word trailer area. */ long initial_address; /* Address of initial segment. */ long initial_size; /* Size of initial segment. */ }; /* The following structure describes the data structure which trails any stack segment. I think that the description in 'asdef' is out of date. I only describe the parts that I am sure about. */ struct stk_trailer { long this_address; /* Address of this block. */ long this_size; /* Size of this block (does not include this trailer). */ long unknown2; long unknown3; long link; /* Address of trailer block of previous segment. */ long unknown5; long unknown6; long unknown7; long unknown8; long unknown9; long unknown10; long unknown11; long unknown12; long unknown13; long unknown14; }; #endif /* CRAY2 */ #endif /* not CRAY_STACK */ #ifdef CRAY2 /* Determine a "stack measure" for an arbitrary ADDRESS. I doubt that "lint" will like this much. */ static long i00afunc (long *address) { struct stk_stat status; struct stk_trailer *trailer; long *block, size; long result = 0; /* We want to iterate through all of the segments. The first step is to get the stack status structure. We could do this more quickly and more directly, perhaps, by referencing the $LM00 common block, but I know that this works. */ STKSTAT (&status); /* Set up the iteration. */ trailer = (struct stk_trailer *) (status.current_address + status.current_size - 15); /* There must be at least one stack segment. Therefore it is a fatal error if "trailer" is null. */ if (trailer == 0) abort (); /* Discard segments that do not contain our argument address. */ while (trailer != 0) { block = (long *) trailer->this_address; size = trailer->this_size; if (block == 0 || size == 0) abort (); trailer = (struct stk_trailer *) trailer->link; if ((block <= address) && (address < (block + size))) break; } /* Set the result to the offset in this segment and add the sizes of all predecessor segments. */ result = address - block; if (trailer == 0) { return result; } do { if (trailer->this_size <= 0) abort (); result += trailer->this_size; trailer = (struct stk_trailer *) trailer->link; } while (trailer != 0); /* We are done. Note that if you present a bogus address (one not in any segment), you will get a different number back, formed from subtracting the address of the first block. This is probably not what you want. */ return (result); } #else /* not CRAY2 */ /* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP. Determine the number of the cell within the stack, given the address of the cell. The purpose of this routine is to linearize, in some sense, stack addresses for alloca. */ static long i00afunc (long address) { long stkl = 0; long size, pseg, this_segment, stack; long result = 0; struct stack_segment_linkage *ssptr; /* Register B67 contains the address of the end of the current stack segment. If you (as a subprogram) store your registers on the stack and find that you are past the contents of B67, you have overflowed the segment. B67 also points to the stack segment linkage control area, which is what we are really interested in. */ stkl = CRAY_STACKSEG_END (); ssptr = (struct stack_segment_linkage *) stkl; /* If one subtracts 'size' from the end of the segment, one has the address of the first word of the segment. If this is not the first segment, 'pseg' will be nonzero. */ pseg = ssptr->sspseg; size = ssptr->sssize; this_segment = stkl - size; /* It is possible that calling this routine itself caused a stack overflow. Discard stack segments which do not contain the target address. */ while (!(this_segment <= address && address <= stkl)) { #ifdef DEBUG_I00AFUNC fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl); #endif if (pseg == 0) break; stkl = stkl - pseg; ssptr = (struct stack_segment_linkage *) stkl; size = ssptr->sssize; pseg = ssptr->sspseg; this_segment = stkl - size; } result = address - this_segment; /* If you subtract pseg from the current end of the stack, you get the address of the previous stack segment's end. This seems a little convoluted to me, but I'll bet you save a cycle somewhere. */ while (pseg != 0) { #ifdef DEBUG_I00AFUNC fprintf (stderr, "%011o %011o\n", pseg, size); #endif stkl = stkl - pseg; ssptr = (struct stack_segment_linkage *) stkl; size = ssptr->sssize; pseg = ssptr->sspseg; result += size; } return (result); } #endif /* not CRAY2 */ #endif /* CRAY */ #endif /* no alloca */ #endif /*UNIX*/ #undef memcpy #undef malloc #undef calloc #undef realloc #undef free #undef strdup /* * Stubs to force calls of our code which call rhpfree etc. * rhpaca and rhpfree call better windows routines and yield */ #ifdef ANSI void *cdecl memcpy(register void *m1, register const void *m2, size_t n) #else char *cdecl memcpy(m1, m2, n) register char *m1, *m2; size_t n; #endif { /*return(db_memcpy(m1, m2, n, __FILE__, __LINE__));*/ return(db_memcpy(m1, m2, n, NULL, 0)); } #ifdef ANSI void * cdecl malloc(size_t size) #else char *cdecl malloc(size) size_t size; #endif { return(db_mallocchk(size, NULL, 0)); } #ifdef ANSI void *cdecl calloc(size_t nelem, size_t size) #else char *cdecl calloc(nelem, size) size_t nelem, size; #endif { return(db_callocchk(nelem, size, NULL, 0)); } #ifdef ANSI void *cdecl realloc(void *oarea, size_t size) #else char * realloc(oarea, size) void *oarea; size_t size; #endif { return(db_reallocchk(oarea, size, NULL, 0)); } #ifndef NJH_DEBUG char * cdecl strdup(const char *string) { return(db_strdupchk(string, NULL, 0)); } #endif #ifdef ANSI void cdecl free(void *memblock) #else #ifndef sun void #endif free(memblock) char *memblock; #endif { db_freechk(memblock, NULL, 0); } #ifndef MSDOS static char *exec_name; void db_setname(progname) const char *progname; { if(exec_name) db_freechk(exec_name, NULL, 0); exec_name = db_strdupchk(progname, NULL, 0); } #endif static void cdecl #ifdef ANSI leaks(void) #else leaks() #endif { unsigned long leaks = 0L; register int i; #ifdef CACHE_TRACE register const struct cache *c; for(c = cache, i = 0; i < CACHE_SIZE; c++, i++) printf("cache %d: %d/%d\n", i, c->hits, c->misses); #endif if(check_for_leaks && slotc) { #ifdef MSDOS register SLOT const huge *sp; #else register SLOT const *sp; #endif register int count = 0; #ifdef WATCH if(watchfd >= 0) { close(watchfd); watchfd = -1; } #endif db_heapchk(NULL, 0); #ifdef UNIX db_alloca(0, __FILE__, __LINE__); #endif #ifdef MAX_STACK_DEPTH st_read(); #endif if(in_db++) /* * This can occur if a signal such as SIGINT * is caught and the handler calls exit if the * interrupt happens when in_db is set */ fputs("Warning: Unexpected check for leaks call\n", stderr); for(sp = slots; sp != &slots[slotc]; sp++) if((!sp->s_freed) && sp->s_size) { count++; leaks += sp->s_size; #ifdef MSDOS if(sp->s_file && (_fstrcmp(sp->s_file, __FILE__) == 0) && (sp->s_line == strdupline)) #else if(sp->s_file && (strcmp(sp->s_file, __FILE__) == 0) && (sp->s_line == strdupline)) #endif #ifdef MAX_STACK_DEPTH _error(NULL, 0L, sp->s_history, "unfree strdup: \"%s\"", (const char *)sp->s_ptr); #else _error(NULL, 0L, NULL, "unfree strdup: \"%s\"", (const char *)sp->s_ptr); #endif else _error(sp->s_file, sp->s_line, NULL, "unfree memory %u bytes", sp->s_size); } #ifdef MAX_STACK_DEPTH else if(exec_name && sp->s_history[0]) _error(NULL, 0, sp->s_history, "unfree memory %u bytes", sp->s_size); #endif if(leaks) fprintf(stderr, "Summary:\n\t%lu bytes in %d memory leak%c\n", leaks, count, (count == 1) ? ' ' : 's'); count = 0; for(i = 0; i < _NFILE; i++) if((lseek(i, 0, SEEK_CUR) >= 0) || (errno != EBADF)) if(!fds[i]) { #ifdef MSDOS fprintf(stderr, "leaked file descriptor %d\n", i); #else int flags = 0, ispipe; if(fcntl(i, F_GETFL, &flags) < 0) perror("fcntl"); fputs("leaked ", stderr); switch(flags & 3) { case O_RDONLY: fputs("read ", stderr); break; case O_WRONLY: fputs("write ", stderr); break; case O_RDWR: fputs("read/write ", stderr); break; } ispipe = ((lseek(i, 0, SEEK_CUR) < 0) && (errno == ESPIPE)); if(ispipe) fputs("pipe ", stderr); else if(flags & O_APPEND) fputs("append ", stderr); fprintf(stderr, "file descriptor %d ", i); #ifdef __BEOS__ putchar('\n'); #else if(ispipe) putchar('\n'); else { struct stat statb; if(fstat(i, &statb) < 0) perror("fstat"); fprintf(stderr, "to inode %d\n", (int)statb.st_ino); fprintf(stderr, "to inode %d on device %d/%d\n", (int)statb.st_ino, (int)major(statb.st_dev), (int)minor(statb.st_dev)); } #endif #endif count++; } if(count > 1) fprintf(stderr, "%d file descriptor leaks\n", count); check_for_leaks = false; in_db--; } } #ifndef MSDOS #define RCHECK /* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) // static char sccsid[] = "@(#)malloc.c 5.11 (Berkeley) 2/23/91"; #endif /* LIBC_SCCS and not lint */ /* * malloc.c (Caltech) 2/21/82 * Chris Kingsley, kingsley@cit-20. * * This is a very fast storage allocator. It allocates blocks of a small * number of different sizes, and keeps free lists of each size. Blocks that * don't exactly fit are passed up to the next larger size. In this * implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long. * This is designed for use in a virtual memory environment. */ #include /* #include #include #include */ #ifndef NULL #define NULL 0 #endif static void morecore(); static int findbucket(); /* * The overhead on a block is at least 4 bytes. When free, this space * contains a pointer to the next free block, and the bottom two bits must * be zero. When in use, the first byte is set to MAGIC, and the second * byte is the size index. The remaining bytes are for alignment. * If range checking is enabled then a second word holds the size of the * requested block, less 1, rounded up to a multiple of sizeof(RMAGIC). * The order of elements is critical: ov_magic must overlay the low order * bits of ov_next, and ov_magic can not be a valid ov_next bit pattern. */ union overhead { union overhead *ov_next; /* when free */ struct { unsigned char ovu_magic; /* magic number */ unsigned char ovu_index; /* bucket # */ #ifdef RCHECK unsigned short ovu_rmagic; /* range magic number */ unsigned int ovu_size; /* actual block size */ #endif } ovu; #define ov_magic ovu.ovu_magic #define ov_index ovu.ovu_index #define ov_rmagic ovu.ovu_rmagic #define ov_size ovu.ovu_size }; #define MAGIC 0xef /* magic # on accounting info */ #define RMAGIC 0x5555 /* magic # on range info */ #ifdef RCHECK #define RSLOP sizeof (unsigned short) #else #define RSLOP 0 #endif /* * nextf[i] is the pointer to the next free block of size 2^(i+3). The * smallest allocatable block is 8 bytes. The overhead information * precedes the data area returned to the user. */ #define NBUCKETS 30 static union overhead *nextf[NBUCKETS]; #if defined(_HPUX_SOURCE) || defined(solaris) || defined(AIX) || defined(LINUX) extern void *sbrk(); #else extern char *sbrk(); #endif static int pagebucket; /* page size bucket */ #ifdef MSTATS /* * nmalloc[i] is the difference between the number of mallocs and frees * for a given block size. */ static unsigned int nmalloc[NBUCKETS]; #include #endif #if defined(DEBUG) || defined(RCHECK) #define ASSERT(p) if (!(p)) botch("p") /* #include */ static void #ifdef ANSI botch(const char *s) #else botch(s) char *s; #endif { /* fprintf(stderr, "\r\nassertion botched: %s\r\n", s); */ _error(NULL, 0, NULL, "\r\nassertion botched: %s\r", s); (void) fflush(stderr); /* just in case user buffered it */ abort(); } #else #define ASSERT(p) #endif #if defined(MAP_ANONYMOUS) && defined(MPROTECT) static void * #ifdef ANSI grab_mem(size_t size) #else grab_mem(size) size_t size; #endif { static caddr_t startAddr; caddr_t allocation = (caddr_t) mmap( startAddr ,(int)size ,PROT_READ|PROT_WRITE ,MAP_PRIVATE|MAP_ANONYMOUS ,-1 ,0); if ( allocation == (caddr_t)-1 ) { _error(NULL, 0, NULL, "mmap failed - using sbrk"); return(sbrk(size)); } #ifndef __hpux /* * Set the "address hint" for the next mmap() so that it will abut * the mapping we just created. * * HP/UX 9.01 has a kernel bug that makes mmap() fail sometimes * when given a non-zero address hint, so we'll leave the hint set * to zero on that system. HP recently told me this is now fixed. * Someone please tell me when it is probable to assume that most * of those systems that were running 9.01 have been upgraded. */ startAddr = allocation + size; #endif return((void *)allocation); } #else static void * grab_mem(size) size_t size; { return(sbrk(size)); } #endif #ifdef ANSI static void * real_malloc(size_t nbytes) #else static char * real_malloc(nbytes) size_t nbytes; #endif { register union overhead *op; register int bucket, n; register unsigned amt; /* * First time malloc is called, setup page size and * align break pointer so all data will be page aligned. */ if (pagesz == 0) { #if defined(sun) || defined(AIX) pagesz = n = getpagesize(); #else pagesz = n = 4096; #endif op = (union overhead *)sbrk(0); n = n - sizeof (*op) - ((int)op & (n - 1)); if (n < 0) n += pagesz; if (n) { if (grab_mem(n) == (char *)-1) return (NULL); } bucket = 0; amt = 8; while (pagesz > amt) { amt <<= 1; bucket++; } pagebucket = bucket; } /* * Convert amount of memory requested into closest block size * stored in hash buckets which satisfies request. * Account for space used per block for accounting. */ if (nbytes <= (n = pagesz - sizeof (*op) - RSLOP)) { #ifndef RCHECK amt = 8; /* size of first bucket */ bucket = 0; #else amt = 16; /* size of first bucket */ bucket = 1; #endif n = -(sizeof (*op) + RSLOP); } else { amt = pagesz; bucket = pagebucket; } while (nbytes > amt + n) { amt <<= 1; if (amt == 0) return (NULL); bucket++; } /* * If nothing in hash bucket right now, * request more memory from the system. */ if ((op = nextf[bucket]) == NULL) { morecore(bucket); if ((op = nextf[bucket]) == NULL) return (NULL); } /* remove from linked list */ nextf[bucket] = op->ov_next; op->ov_magic = MAGIC; op->ov_index = (unsigned char)bucket; #ifdef MSTATS nmalloc[bucket]++; #endif #ifdef RCHECK /* * Record allocated size of block and * bound space with magic numbers. */ op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); op->ov_rmagic = RMAGIC; rw((caddr_t)(op + 1), op->ov_size + 1); *(unsigned short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; #endif return ((char *)(op + 1)); } /* * Allocate more memory to the indicated bucket. */ static void morecore(bucket) int bucket; { register union overhead *op; register int sz; /* size of desired block */ int amt; /* amount to allocate */ int nblks; /* how many blocks we get */ /* * sbrk_size <= 0 only for big, FLUFFY, requests (about * 2^30 bytes on a VAX, I think) or for a negative arg. */ sz = 1 << (bucket + 3); #ifdef DEBUG ASSERT(sz > 0); #else if (sz <= 0) return; #endif ASSERT(pagesz > 0); if (sz < pagesz) { amt = pagesz; nblks = amt / sz; } else { amt = sz + pagesz; nblks = 1; } #ifdef MPROTECT op = (union overhead *)grab_mem(amt + sz); /* 2.1.2 fix */ #else op = (union overhead *)grab_mem(amt); #endif /* no more room! */ if ((int)op == -1) return; /* * Add new memory allocated to that on * free list for this hash bucket. */ nextf[bucket] = op; while (--nblks > 0) { op->ov_next = (union overhead *)((caddr_t)op + sz); op = (union overhead *)((caddr_t)op + sz); } } static void #ifdef ANSI real_free(void *cp) #else real_free(cp) void *cp; #endif { register int size; register union overhead *op; if (cp == NULL) return; op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); #ifdef DEBUG ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */ #else if (op->ov_magic != MAGIC) return; /* sanity */ #endif #ifdef RCHECK ASSERT(op->ov_rmagic == RMAGIC); ASSERT(*(unsigned short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC); #endif size = op->ov_index; ASSERT(size < NBUCKETS); op->ov_next = nextf[size]; /* also clobbers ov_magic */ nextf[size] = op; #ifdef MSTATS nmalloc[size]--; #endif } /* * When a program attempts "storage compaction" as mentioned in the * old malloc man page, it realloc's an already freed block. Usually * this is the last block it freed; occasionally it might be farther * back. We have to search all the free lists for the block in order * to determine its bucket: 1st we make one pass thru the lists * checking only the first block in each; if that fails we search * ``realloc_srchlen'' blocks in each list for a match (the variable * is extern so the caller can modify it). If that fails we just copy * however many bytes was given to realloc() and hope it's not huge. */ #define realloc_srchlen 4 /* 4 should be plenty, -1 =>'s whole list */ #ifdef ANSI static void * real_realloc(void *cp, size_t nbytes) #else static char * real_realloc(cp, nbytes) char *cp; size_t nbytes; #endif { register unsigned int onb; register int i; union overhead *op; char *res; int was_alloced = 0; if (cp == NULL) return (real_malloc(nbytes)); op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); if (op->ov_magic == MAGIC) { was_alloced++; i = op->ov_index; } else { /* * Already free, doing "compaction". * * Search for the old block of memory on the * free list. First, check the most common * case (last element free'd), then (this failing) * the last ``realloc_srchlen'' items free'd. * If all lookups fail, then assume the size of * the memory block being realloc'd is the * largest possible (so that all "nbytes" of new * memory are copied into). Note that this could cause * a memory fault if the old area was tiny, and the moon * is gibbous. However, that is very unlikely. */ if ((i = findbucket(op, 1)) < 0 && (i = findbucket(op, realloc_srchlen)) < 0) i = NBUCKETS; } onb = 1 << (i + 3); if (onb < pagesz) onb -= sizeof (*op) + RSLOP; else onb += pagesz - sizeof (*op) - RSLOP; /* avoid the copy if same size block */ if (was_alloced) { if (i) { i = 1 << (i + 2); if (i < pagesz) i -= sizeof (*op) + RSLOP; else i += pagesz - sizeof (*op) - RSLOP; } if (nbytes <= onb && nbytes > i) { #ifdef RCHECK op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); *(unsigned short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; #endif return(cp); } else real_free(cp); } if ((res = real_malloc(nbytes)) == NULL) return (NULL); if (cp != res) /* common optimization if "compacting" */ return(real_memcpy(res, cp, (nbytes < onb) ? nbytes : onb)); return (res); } /* * Search ``srchlen'' elements of each free list for a block whose * header starts at ``freep''. If srchlen is -1 search the whole list. * Return bucket number, or -1 if not found. */ static int findbucket(freep, srchlen) union overhead *freep; int srchlen; { register int i, j; for (i = 0; i < NBUCKETS; i++) { register union overhead *p; j = 0; for (p = nextf[i]; p && j != srchlen; p = p->ov_next) { if (p == freep) return (i); j++; } } return (-1); } #ifdef MSTATS /* * mstats - print out statistics about malloc * * Prints two lines of numbers, one showing the length of the free list * for each size category, the second showing the number of mallocs - * frees for each size category. */ mstats(s) char *s; { register int i, j; register union overhead *p; int totfree = 0, totused = 0; fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s); for (i = 0; i < NBUCKETS; i++) { for (j = 0, p = nextf[i]; p; p = p->ov_next, j++) ; fprintf(stderr, " %d", j); totfree += j * (1 << (i + 3)); } fputs("\nused:\t", stderr); for (i = 0; i < NBUCKETS; i++) { fprintf(stderr, " %d", nmalloc[i]); totused += nmalloc[i] * (1 << (i + 3)); } fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n", totused, totfree); } #endif /* MSTATS */ #endif /* UNIX */ #ifdef UNIX #define tst(a,b) (*mode == 'r' ? (b) : (a)) #define RDR 0 #define WTR 1 extern FILE *fdopen(); #ifndef NOFILES_MIN #ifdef NOFILE #define NOFILES_MIN NOFILE #elif defined(_NFILE) #define NOFILES_MIN _NFILE #elif defined(OPEN_MAX) #define NOFILES_MIN OPEN_MAX #else #define NOFILES_MIN 20 #endif #endif #ifndef OPEN_MAX #define OPEN_MAX 64 #endif static pid_t popen_pid[NOFILES_MIN]; static void execshell(cmd) const char *cmd; { register const char *shell; if(strpbrk(cmd, " =?*[|&$<>") == (char *)NULL) { (void)execl(cmd, cmd, (char *)0); (void)execlp(cmd, cmd, (char *)0); } shell = getenv("SHELL"); (void) execl((shell) ? shell : "/bin/sh", "sh", "-c", cmd, (char *)0); } #ifndef PERPOS #define WAITPID #endif FILE * popen(cmd, mode) const char *cmd, *mode; { register pid_t *poptr, pid; register int myside, yourside; int stdio; int p[2]; if(!_blkchk(cmd) || !_blkchk(mode) || (*mode != 'r' && *mode != 'w')) { errno = EINVAL; return(NULL); } if(pipe(p) < 0) return(NULL); myside = tst(p[WTR], p[RDR]); yourside = tst(p[RDR], p[WTR]); #ifdef SIGCLD signal(SIGCLD, SIG_DFL); #endif switch(pid = fork()) { case 0: /* myside and yourside reverse roles in child */ /* close all pipes from other popen's */ for (poptr = popen_pid; poptr < popen_pid+NOFILES_MIN; poptr++) if(*poptr) close(poptr - popen_pid); stdio = tst(0, 1); (void) close(myside); (void) close(stdio); (void) fcntl(yourside, F_DUPFD, stdio); (void) close(yourside); setuid(getuid()); execshell(cmd); _exit(1); case -1: return(NULL); default: popen_pid[myside] = pid; (void) close(yourside); return(fdopen(myside, mode)); } } int pclose(ptr) FILE *ptr; { register int f, r; #ifdef sun int status; void (*hstat)(), (*istat)(), (*qstat)(), (*astat)(); #else int status, (*hstat)(), (*istat)(), (*qstat)(), (*astat)(); #endif register int oalarm; if(!_blkchk((char *)ptr)) return(-1); f = fileno(ptr); if(popen_pid[f] == 0) { _error(NULL, 0, NULL, "pclose with no popen"); return(-1); } (void) fclose(ptr); istat = signal(SIGINT, SIG_IGN); qstat = signal(SIGQUIT, SIG_IGN); hstat = signal(SIGHUP, SIG_IGN); astat = signal(SIGALRM, SIG_IGN); oalarm = alarm(0); #ifdef WAITPID do r = waitpid(popen_pid[f], &status, 0); while((r == -1) && errno == EINTR); #else while(((r = wait(&status)) != popen_pid[f]) && (r != -1)) ; #endif (void) signal(SIGINT, istat); (void) signal(SIGQUIT, qstat); (void) signal(SIGHUP, hstat); (void) signal(SIGALRM, astat); if(oalarm) alarm(oalarm); /* mark this pipe closed */ popen_pid[f] = 0; return((r == -1) ? -1 : status); } int system(s) const char *s; { int status; register pid_t pid; #ifdef sun register void (*cstat)(), (*istat)(), (*qstat)(); register int w; #else register int (*cstat)(), (*istat)(), (*qstat)(), w; #endif if(!_blkchk(s)) return(-1); cstat = signal(SIGCHLD, SIG_IGN); switch(pid = fork()) { case 0: setuid(getuid()); signal(SIGCHLD, cstat); execshell(s); _exit(127); case -1: return(-1); } istat = signal(SIGINT, SIG_IGN); qstat = signal(SIGQUIT, SIG_IGN); #ifdef WAITPID w = waitpid(pid, &status, 0); #else while((w = wait(&status)) != pid && w != -1) ; #endif (void) signal(SIGINT, istat); (void) signal(SIGQUIT, qstat); (void)signal(SIGCHLD, cstat); return((w == -1) ? -1 : status); } #endif /*UNIX*/ /*- * Copyright (c) 1980, 1983, 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)qsort.c 5.9 (Berkeley) 2/23/91"; #endif /* LIBC_SCCS and not lint */ /* * MTHRESH is the smallest partition for which we compare for a median * value instead of using the middle value. */ #define MTHRESH 6 /* * THRESH is the minimum number of entries in a partition for continued * partitioning. */ #define THRESH 4 static void insertion_sort(); static void quick_sort(); #ifdef ANSI /* * Nightmare definition because different Unix suppliers can't get it right */ #if defined(sun) && !defined(solaris) int #else void cdecl #endif qsort(bot, nmemb, size, compar) void *bot; size_t nmemb, size; int (cdecl *compar)(const void *, const void *); #else #ifdef _HPUX_SOURCE void qsort(bot, nmemb, size, compar) void *bot; size_t nmemb, size; int (cdecl *compar)(); #else qsort(bot, nmemb, size, compar) char *bot; int (*compar)(); #endif #endif { if (nmemb <= 1) { _error(NULL, 0, NULL, "qsort called on 0 or 1 records"); #if defined(sun) && !defined(solaris) return(0); #else return; #endif } if(blklen(bot, false) < (nmemb * size)) { _error(NULL, 0, NULL, "qsort: buffer isn't that big"); #if defined(sun) && !defined(solaris) return(0); #else return; #endif } if (nmemb >= THRESH) quick_sort(bot, nmemb, size, compar); else insertion_sort(bot, nmemb, size, compar); #if defined(sun) && !defined(solaris) return(0); #endif } /* * Swap two areas of size number of bytes. Although qsort(3) permits random * blocks of memory to be sorted, sorting pointers is almost certainly the * common case (and, were it not, could easily be made so). Regardless, it * isn't worth optimizing; the SWAP's get sped up by the cache, and pointer * arithmetic gets lost in the time required for comparison function calls. */ #define SWAP(a, b) { \ cnt = size; \ do { \ ch = *a; \ *a++ = *b; \ *b++ = ch; \ } while (--cnt); \ } /* * Knuth, Vol. 3, page 116, Algorithm Q, step b, argues that a single pass * of straight insertion sort after partitioning is complete is better than * sorting each small partition as it is created. This isn't correct in this * implementation because comparisons require at least one (and often two) * function calls and are likely to be the dominating expense of the sort. * Doing a final insertion sort does more comparisons than are necessary * because it compares the "edges" and medians of the partitions which are * known to be already sorted. * * This is also the reasoning behind selecting a small THRESH value (see * Knuth, page 122, equation 26), since the quicksort algorithm does less * comparisons than the insertion sort. */ #define SORT(bot, n) { \ if (n > 1) \ if (n == 2) { \ t1 = bot + size; \ if (compar(t1, bot) < 0) \ SWAP(t1, bot); \ } else \ insertion_sort(bot, n, size, compar); \ } static void quick_sort(bot, nmemb, size, compar) register char *bot; register int size; int nmemb, (cdecl *compar)(); { register int cnt; register unsigned char ch; register char *top, *mid, *t1, *t2; register int n1, n2; char *bsv; /* bot and nmemb must already be set. */ partition: /* find mid and top elements */ mid = bot + size * (nmemb >> 1); top = bot + (nmemb - 1) * size; /* * Find the median of the first, last and middle element (see Knuth, * Vol. 3, page 123, Eq. 28). This test order gets the equalities * right. */ if (nmemb >= MTHRESH) { n1 = compar(bot, mid); n2 = compar(mid, top); if (n1 < 0 && n2 > 0) t1 = compar(bot, top) < 0 ? top : bot; else if (n1 > 0 && n2 < 0) t1 = compar(bot, top) > 0 ? top : bot; else t1 = mid; /* if mid element not selected, swap selection there */ if (t1 != mid) { SWAP(t1, mid); mid -= size; } } /* Standard quicksort, Knuth, Vol. 3, page 116, Algorithm Q. */ #define didswap n1 #define newbot t1 #define replace t2 didswap = 0; for (bsv = bot;;) { for (; bot < mid && compar(bot, mid) <= 0; bot += size); while (top > mid) { if (compar(mid, top) <= 0) { top -= size; continue; } newbot = bot + size; /* value of bot after swap */ if (bot == mid) /* top <-> mid, mid == top */ replace = mid = top; else { /* bot <-> top */ replace = top; top -= size; } goto swap; } if (bot == mid) break; /* bot <-> mid, mid == bot */ replace = mid; newbot = mid = bot; /* value of bot after swap */ top -= size; swap: SWAP(bot, replace); bot = newbot; didswap = 1; } /* * Quicksort behaves badly in the presence of data which is already * sorted (see Knuth, Vol. 3, page 119) going from O N lg N to O N^2. * To avoid this worst case behavior, if a re-partitioning occurs * without swapping any elements, it is not further partitioned and * is insert sorted. This wins big with almost sorted data sets and * only loses if the data set is very strangely partitioned. A fix * for those data sets would be to return prematurely if the insertion * sort routine is forced to make an excessive number of swaps, and * continue the partitioning. */ if (!didswap) { insertion_sort(bsv, nmemb, size, compar); return; } /* * Re-partition or sort as necessary. Note that the mid element * itself is correctly positioned and can be ignored. */ #define nlower n1 #define nupper n2 bot = bsv; nlower = (mid - bot) / size; /* size of lower partition */ mid += size; nupper = nmemb - nlower - 1; /* size of upper partition */ /* * If must call recursively, do it on the smaller partition; this * bounds the stack to lg N entries. */ if (nlower > nupper) { if (nupper >= THRESH) quick_sort(mid, nupper, size, compar); else { SORT(mid, nupper); if (nlower < THRESH) { SORT(bot, nlower); return; } } nmemb = nlower; } else { if (nlower >= THRESH) quick_sort(bot, nlower, size, compar); else { SORT(bot, nlower); if (nupper < THRESH) { SORT(mid, nupper); return; } } bot = mid; nmemb = nupper; } goto partition; /* NOTREACHED */ } static void insertion_sort(bot, nmemb, size, compar) char *bot; register int size; int nmemb, (cdecl *compar)(); { register int cnt; register unsigned char ch; register char *s1, *s2, *t1, *t2, *top; /* * A simple insertion sort (see Knuth, Vol. 3, page 81, Algorithm * S). Insertion sort has the same worst case as most simple sorts * (O N^2). It gets used here because it is (O N) in the case of * sorted data. */ top = bot + nmemb * size; for (t1 = bot + size; t1 < top;) { for (t2 = t1; (t2 -= size) >= bot && compar(t1, t2) < 0;); if (t1 != (t2 += size)) { /* Bubble bytes up through each element. */ for (cnt = size; cnt--; ++t1) { ch = *t1; for (s1 = s2 = t1; (s2 -= size) >= t2; s1 = s2) *s1 = *s2; *s1 = ch; } } else t1 += size; } } #if 0 mprotect(addr, len, prot) caddr_t addr; { errno = EINVAL; return(-1); } #endif #ifdef MAX_STACK_DEPTH #define SHORT_CALLSTACK_SIZE 5 static int fstk_i; static int no_call_graph = 0; #if (defined(vax) || (defined(sun) && !defined(sun4))) #define get_current_fp(first_local) ((unsigned)&(first_local) + 4) #endif #if (defined(vax) || defined(sun)) #include #ifdef solaris #include #else #include #endif #define prev_fp_from_fp(fp) (unsigned)(((struct frame *)(fp))->fr_savfp) #define ret_addr_from_fp(fp) (unsigned)(((struct frame *)(fp))->fr_savpc) #endif static void mprof(sp) SLOT *sp; { unsigned first_local; /* WARNING -- This MUST be the first * local variable in this function. */ unsigned fp; unsigned ret_addr; register int i; #ifdef mips pPDR pdr; #endif fstk_i = 0; /* gather return addresses from the callstack */ #ifndef mips fp = get_current_fp(first_local); ret_addr = ret_addr_from_fp(fp); /* Step back 1 frame (to the caller of malloc) */ fp = prev_fp_from_fp(fp); ret_addr = ret_addr_from_fp(fp); i = 0; while((ret_addr > mp_root_address) && (i < MAX_STACK_DEPTH)){ if (no_call_graph && (fstk_i > SHORT_CALLSTACK_SIZE)) break; sp->s_history[i++] = (caddr_t)ret_addr; fstk_i++; fp = prev_fp_from_fp(fp); if (fp == 0) break; ret_addr = ret_addr_from_fp(fp); } #else get31(); pdr = getpdr(intloc); getsp(); fp = intloc; ret_addr = getretaddr(&fp, pdr); /* fp is changed */ /* Step back 1 frame (to the caller of malloc) */ pdr = getpdr(ret_addr); ret_addr = getretaddr(&fp, pdr); /* fp is changed */ while (ret_addr > mp_root_address) { if (no_call_graph && (fstk_i > SHORT_CALLSTACK_SIZE)) break; fpcs[fstk_i] = ret_addr; fstk_i++; pdr = getpdr(ret_addr); ret_addr = getretaddr(&fp, pdr); /* fp is updated */ } #endif } static struct sym { char sym[33]; caddr_t off; } *s; static unsigned long size_s; static void st_read() { struct exec e; register struct nlist *n, *nlist; register struct sym *sym; register int fd; register long l; if((exec_name == NULL) || (s != NULL)) return; fd = open(exec_name, 0); read(fd, &e, sizeof(e)); if(e.a_syms == 0) { /*puts("stripped");*/ close(fd); return; } in_db++; lseek(fd, N_SYMOFF(e), SEEK_SET); nlist = n = (struct nlist *)db_mallocchk(e.a_syms * sizeof(struct nlist), NULL, 0); sym = s = (struct sym *)db_mallocchk(e.a_syms * sizeof(struct sym), NULL, 0); read(fd, n, e.a_syms * sizeof(struct nlist)); size_s = 0; for(l = 0; l < e.a_syms; l++, n++) { register char *ptr; if(n->n_type&N_STAB) { /* compiled with `-g' */ if(n->n_type != N_FUN) continue; } else { if((n->n_type&N_TYPE) != N_TEXT) continue; if(!(n->n_type&N_EXT)) continue; } sym->off = (caddr_t)n->n_value; lseek(fd, N_STROFF(e) + n->n_un.n_strx, SEEK_SET); read(fd, sym->sym, sizeof(sym->sym) - 1); if(ptr = strrchr(sym->sym, ':')) *ptr = '\0'; size_s++; sym++; } #ifdef assert assert(size_s <= e.a_syms); #endif close(fd); db_freechk(nlist, NULL, 0); if(size_s == 0) { db_freechk(s, NULL, 0); s = NULL; } else s = (struct sym *)db_reallocchk(s, size_s * sizeof(struct sym), NULL, 0); in_db--; } static const char * symname(pc) caddr_t pc; { register struct sym *sym, *keep; register long l; register int diff; if(s == NULL) return(NULL); diff = 0; keep = NULL; for(sym = s, l = 0; l < size_s; l++, sym++) if((pc > sym->off) && ((pc < sym->off + diff) || (diff == 0))) { diff = pc - sym->off; keep = sym; } if(keep) return((keep->sym[0] == '_') ? &keep->sym[1] : keep->sym); return(NULL); } #endif /*MAX_STACK_DEPTH*/ #ifdef UNIX static void slots_rw(void) { rw((caddr_t)slots, maxslots * sizeof(SLOT)); } static void slots_readonly(void) { ro((caddr_t)slots, maxslots * sizeof(SLOT)); } static void ro(const caddr_t addr, size_t size) { #ifdef MPROTECT mprotect(addr, size, PROT_READ); #endif #ifdef WATCH struct { int ctl; prwatch_t p; } msg; if(watchfd < 0) { watchfd = open("/proc/self/ctl", O_WRONLY|O_EXCL); if(watchfd < 0) return; } msg.p.pr_vaddr = (uintptr_t)addr; msg.p.pr_size = size; msg.p.pr_wflags = WA_WRITE; msg.ctl = PCWATCH; if(write(watchfd, &msg, sizeof(msg)) < 0) puts("ro"); #endif } static void rw(const caddr_t addr, size_t size) { #ifdef MPROTECT mprotect(addr, size, PROT_READ|PROT_WRITE); #endif #ifdef WATCH struct { int ctl; prwatch_t p; } msg; if(watchfd < 0) return; msg.p.pr_vaddr = (uintptr_t)addr; msg.p.pr_size = size; msg.p.pr_wflags = 0; msg.ctl = PCWATCH; if(write(watchfd, &msg, sizeof(msg)) < 0) puts("rw"); #endif } static void none(const caddr_t addr, size_t size) { #ifdef MPROTECT mprotect(addr, size, PROT_NONE); #endif #ifdef WATCH struct { int ctl; prwatch_t p; } msg; if(watchfd < 0) { watchfd = open("/proc/self/ctl", O_WRONLY|O_EXCL); if(watchfd < 0) return; } msg.p.pr_vaddr = (uintptr_t)addr; msg.p.pr_size = size; msg.p.pr_wflags = WA_WRITE|WA_READ; msg.ctl = PCWATCH; if(write(watchfd, &msg, sizeof(msg)) < 0) puts("none"); #endif } #endif /*UNIX*/ #endif /*CL_DEBUG*/