/* * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil). * You may copy, distribute, and use this software as long as this * copyright statement is not removed. */ #include #include #include "malloc.h" #include "tostring.h" /* * Function: malloc() * * Purpose: memory allocator * * Arguments: size - size of data area needed * * Returns: pointer to allocated area, or NULL if unable * to allocate addtional data. * * Narrative: * */ #ifndef lint static char rcs_hdr[] = "$Id: malloc.c,v 1.1 1992/01/24 03:29:05 dvadura Exp $"; #endif extern int malloc_checking; char * malloc_data_start; char * malloc_data_end; struct mlist * malloc_end; int malloc_errfd = 2; int malloc_errno; int malloc_fatal_level = M_HANDLE_CORE; struct mlist malloc_start; int malloc_warn_level; void malloc_memset(); char * malloc(size) unsigned int size; { char * func = "malloc"; char * getenv(); void malloc_fatal(); void malloc_init(); void malloc_split(); void malloc_warning(); unsigned int need; struct mlist * oldptr; struct mlist * ptr; char * sbrk(); /* * If this is the first call to malloc... */ if( malloc_data_start == (char *) 0 ) { malloc_init(); } /* * If malloc chain checking is on, go do it. */ if( malloc_checking ) { (void) malloc_chain_check(1); } /* * always make sure there is at least on extra byte in the malloc * area so that we can verify that the user does not overrun the * data area. */ size++; /* * Now look for a free area of memory of size bytes... */ oldptr = NULL; for(ptr = &malloc_start; ; ptr = ptr->next) { /* * Since the malloc chain is a forward only chain, any * pointer that we get should always be positioned in * memory following the previous pointer. If this is not * so, we must have a corrupted chain. */ if( ptr ) { if( IsLess(ptr,oldptr) ) { malloc_errno = M_CODE_CHAIN_BROKE; malloc_fatal(func); return(NULL); } oldptr = ptr; } else if( oldptr != malloc_end ) { /* * This should never happen. If it does, then * we got a real problem. */ malloc_errno = M_CODE_NO_END; malloc_fatal(func); return(NULL); } /* * if this element is already in use... */ if( ptr && ((ptr->flag & M_INUSE) != 0) ) { continue; } /* * if there isn't room for this block.. */ if( ptr && (ptr->s.size < size) ) { continue; } /* * If ptr is null, we have run out of memory and must sbrk more */ if( ptr == NULL ) { need = (size + M_SIZE) * (size > 10*1024 ? 1:2); if( need < M_BLOCKSIZE ) { need = M_BLOCKSIZE; } else if( need & (M_BLOCKSIZE-1) ) { need &= ~(M_BLOCKSIZE-1); need += M_BLOCKSIZE; } ptr = (struct mlist *) sbrk((int)need); if( ptr == (struct mlist *) -1 ) { malloc_errno = M_CODE_NOMORE_MEM; malloc_fatal(func); } malloc_data_end = sbrk((int)0); ptr->prev = oldptr; ptr->next = (struct mlist *) 0; ptr->s.size = need - M_SIZE; ptr->flag = M_MAGIC; oldptr->next = ptr; malloc_end = ptr; } /* if( ptr ==... */ /* * Now ptr points to a memory location that can store * this data, so lets go to work. */ ptr->r_size = size; /* save requested size */ ptr->flag |= M_INUSE; /* * split off unneeded data area in this block, if possible... */ malloc_split(ptr); /* * re-adjust the requested size so that it is what the user * actually requested... */ ptr->r_size--; /* * just to make sure that noone is misusing malloced * memory without initializing it, lets set it to * all '\01's. We call local_memset() because memset() * may be checking for malloc'd ptrs and this isn't * a malloc'd ptr yet. */ malloc_memset(ptr->data,M_FILL,(int)ptr->s.size); return( ptr->data); } /* for(... */ } /* malloc(... */ /* * Function: malloc_split() * * Purpose: to split a malloc segment if there is enough room at the * end of the segment that isn't being used * * Arguments: ptr - pointer to segment to split * * Returns: nothing of any use. * * Narrative: * get the needed size of the module * round the size up to appropriat boundry * calculate amount of left over space * if there is enough left over space * create new malloc block out of remainder * if next block is free * join the two blocks together * fill new empty block with free space filler * re-adjust pointers and size of current malloc block * * * * Mod History: * 90/01/27 cpcahil Initial revision. */ void malloc_split(ptr) struct mlist * ptr; { extern struct mlist * malloc_end; void malloc_join(); int rest; int size; struct mlist * tptr; size = ptr->r_size; /* * roundup size to the appropriate boundry */ M_ROUNDUP(size); /* * figure out how much room is left in the array. * if there is enough room, create a new mlist * structure there. */ if( ptr->s.size > size ) { rest = ptr->s.size - size; } else { rest = 0; } if( rest > (M_SIZE+M_RND) ) { tptr = (struct mlist *) (ptr->data+size); tptr->prev = ptr; tptr->next = ptr->next; tptr->flag = M_MAGIC; tptr->s.size = rest - M_SIZE; /* * If possible, join this segment with the next one */ malloc_join(tptr, tptr->next,0,0); if( tptr->next ) { tptr->next->prev = tptr; } malloc_memset(tptr->data,M_FREE_FILL, (int)tptr->s.size); ptr->next = tptr; ptr->s.size = size; if( malloc_end == ptr ) { malloc_end = tptr; } } } /* malloc_split(... */ /* * Function: malloc_join() * * Purpose: to join two malloc segments together (if possible) * * Arguments: ptr - pointer to segment to join to. * nextptr - pointer to next segment to join to ptr. * * Returns: nothing of any values. * * Narrative: * * Mod History: * 90/01/27 cpcahil Initial revision. */ void malloc_join(ptr,nextptr, inuse_override, fill_flag) struct mlist * ptr; struct mlist * nextptr; int inuse_override; int fill_flag; { unsigned int newsize; if( ptr && ! (inuse_override || (ptr->flag & M_INUSE)) && nextptr && ! (nextptr->flag & M_INUSE) && ((ptr->data+ptr->s.size) == (char *) nextptr) ) { if( malloc_end == nextptr ) { malloc_end = ptr; } ptr->next = nextptr->next; newsize = nextptr->s.size + M_SIZE; /* * if we are to fill and this segment is in use, * fill in with M_FILL newly added space... */ if(fill_flag && (ptr->flag & M_INUSE) ) { malloc_memset(ptr->data+ptr->s.size, M_FILL, (int)(nextptr->s.size + M_SIZE)); } ptr->s.size += newsize; if( ptr->next ) { ptr->next->prev = ptr; } } } /* malloc_join(... */ /* * The following mess is just to ensure that the versions of these functions in * the current library are included (to make sure that we don't accidentaly get * the libc versions. (This is the lazy man's -u ld directive) */ void free(); int strcmp(); int memcmp(); char * realloc(); void (*malloc_void_funcs[])() = { free, }; int (*malloc_int_funcs[])() = { strcmp, memcmp, }; char * (*malloc_char_star_funcs[])() = { realloc, }; /* * This is malloc's own memset which is used without checking the parameters. */ void malloc_memset(ptr,byte,len) char * ptr; char byte; int len; { while(len-- > 0) { *ptr++ = byte; } } /* malloc_memset(... */ /* * Function: malloc_fatal() * * Purpose: to display fatal error message and take approrpriate action * * Arguments: funcname - name of function calling this routine * * Returns: nothing of any value * * Narrative: * * Notes: This routine does not make use of any libc functions to build * and/or disply the error message. This is due to the fact that * we are probably at a point where malloc is having a real problem * and we don't want to call any function that may use malloc. */ void malloc_fatal(funcname) char * funcname; { char errbuf[128]; void exit(); void malloc_err_handler(); extern char * malloc_err_strings[]; extern int malloc_errno; extern int malloc_fatal_level; char * s; char * t; s = errbuf; t = "Fatal error: "; while( *s = *t++) { s++; } t = funcname; while( *s = *t++) { s++; } t = "(): "; while( *s = *t++) { s++; } t = malloc_err_strings[malloc_errno]; while( *s = *t++) { s++; } *(s++) = '\n'; if( write(malloc_errfd,errbuf,(unsigned)(s-errbuf)) != (s-errbuf)) { (void) write(2,"I/O error to error file\n",(unsigned)24); exit(110); } malloc_err_handler(malloc_fatal_level); } /* malloc_fatal(... */ /* * Function: malloc_warning() * * Purpose: to display warning error message and take approrpriate action * * Arguments: funcname - name of function calling this routine * * Returns: nothing of any value * * Narrative: * * Notes: This routine does not make use of any libc functions to build * and/or disply the error message. This is due to the fact that * we are probably at a point where malloc is having a real problem * and we don't want to call any function that may use malloc. */ void malloc_warning(funcname) char * funcname; { char errbuf[128]; void exit(); void malloc_err_handler(); extern char * malloc_err_strings[]; extern int malloc_errno; extern int malloc_warn_level; char * s; char * t; s = errbuf; t = "Warning: "; while( *s = *t++) { s++; } t = funcname; while( *s = *t++) { s++; } t = "(): "; while( *s = *t++) { s++; } t = malloc_err_strings[malloc_errno]; while( *s = *t++) { s++; } *(s++) = '\n'; if( write(malloc_errfd,errbuf,(unsigned)(s-errbuf)) != (s-errbuf)) { (void) write(2,"I/O error to error file\n",(unsigned)24); exit(110); } malloc_err_handler(malloc_warn_level); } /* malloc_warning(... */ /* * Function: malloc_err_handler() * * Purpose: to take the appropriate action for warning and/or fatal * error conditions. * * Arguments: level - error handling level * * Returns: nothing of any value * * Narrative: * * Notes: This routine does not make use of any libc functions to build * and/or disply the error message. This is due to the fact that * we are probably at a point where malloc is having a real problem * and we don't want to call any function that may use malloc. */ void malloc_err_handler(level) { void exit(); void malloc_dump(); extern int malloc_errfd; if( level & M_HANDLE_DUMP ) { malloc_dump(malloc_errfd); } switch( level & ~M_HANDLE_DUMP ) { /* * If we are to drop a core file and exit */ case M_HANDLE_ABORT: (void) abort(); break; /* * If we are to exit.. */ case M_HANDLE_EXIT: exit(200); break; #ifndef __MSDOS__ /* * If we are to dump a core, but keep going on our merry way */ case M_HANDLE_CORE: { int pid; /* * fork so child can abort (and dump core) */ if( (pid = fork()) == 0 ) { (void) write(2,"Child dumping core\n", (unsigned)9); (void) abort(); } /* * wait for child to finish dumping core */ while( wait((int *)0) != pid) { } /* * Move core file to core.pid.cnt so * multiple cores don't overwrite each * other. */ if( access("core",0) == 0 ) { static int corecnt; char filenam[32]; filenam[0] = 'c'; filenam[1] = 'o'; filenam[2] = 'r'; filenam[3] = 'e'; filenam[4] = '.'; (void)tostring(filenam+5,getpid(), 5, B_DEC, '0'); filenam[10] = '.'; (void)tostring(filenam+11,corecnt++, 3, B_DEC, '0'); filenam[14] = '\0'; (void) unlink(filenam); if( link("core",filenam) == 0) { (void) unlink("core"); } } } #endif /* * If we are to just ignore the error and keep on processing */ case M_HANDLE_IGNORE: break; } /* switch(... */ } /* malloc_err_handler(... */ /* * $Log: malloc.c,v $ * Revision 1.1 1992/01/24 03:29:05 dvadura * dmake Version 3.8, Initial revision * * Revision 1.6 90/05/11 00:13:09 cpcahil * added copyright statment * * Revision 1.5 90/02/25 11:01:18 cpcahil * added support for malloc chain checking. * * Revision 1.4 90/02/24 21:50:21 cpcahil * lots of lint fixes * * Revision 1.3 90/02/24 14:51:18 cpcahil * 1. changed malloc_fatal and malloc_warn to use malloc_errno and be passed * the function name as a parameter. * 2. Added several function headers. * 3. Changed uses of malloc_fatal/warning to conform to new usage. * * Revision 1.2 90/02/23 18:05:23 cpcahil * fixed open of error log to use append mode. * * Revision 1.1 90/02/22 23:17:43 cpcahil * Initial revision * */