1 /*
2 * (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil).
3 * You may copy, distribute, and use this software as long as this
4 * copyright statement is not removed.
5 */
6 #include <stdio.h>
7 #include <fcntl.h>
8 #include "malloc.h"
9 #include "tostring.h"
10
11 /*
12 * Function: malloc()
13 *
14 * Purpose: memory allocator
15 *
16 * Arguments: size - size of data area needed
17 *
18 * Returns: pointer to allocated area, or NULL if unable
19 * to allocate addtional data.
20 *
21 * Narrative:
22 *
23 */
24 #ifndef lint
25 static
26 char rcs_hdr[] = "$Id: malloc.c,v 1.2 2006-07-25 10:08:36 rt Exp $";
27 #endif
28
29 extern int malloc_checking;
30 char * malloc_data_start;
31 char * malloc_data_end;
32 struct mlist * malloc_end;
33 int malloc_errfd = 2;
34 int malloc_errno;
35 int malloc_fatal_level = M_HANDLE_CORE;
36 struct mlist malloc_start;
37 int malloc_warn_level;
38 void malloc_memset();
39
40 char *
malloc(size)41 malloc(size)
42 unsigned int size;
43 {
44 char * func = "malloc";
45 char * getenv();
46 void malloc_fatal();
47 void malloc_init();
48 void malloc_split();
49 void malloc_warning();
50 unsigned int need;
51 struct mlist * oldptr;
52 struct mlist * ptr;
53 char * sbrk();
54
55 /*
56 * If this is the first call to malloc...
57 */
58 if( malloc_data_start == (char *) 0 )
59 {
60 malloc_init();
61 }
62
63 /*
64 * If malloc chain checking is on, go do it.
65 */
66 if( malloc_checking )
67 {
68 (void) malloc_chain_check(1);
69 }
70
71 /*
72 * always make sure there is at least on extra byte in the malloc
73 * area so that we can verify that the user does not overrun the
74 * data area.
75 */
76 size++;
77
78 /*
79 * Now look for a free area of memory of size bytes...
80 */
81 oldptr = NULL;
82 for(ptr = &malloc_start; ; ptr = ptr->next)
83 {
84 /*
85 * Since the malloc chain is a forward only chain, any
86 * pointer that we get should always be positioned in
87 * memory following the previous pointer. If this is not
88 * so, we must have a corrupted chain.
89 */
90 if( ptr )
91 {
92 if( ptr<oldptr )
93 {
94 malloc_errno = M_CODE_CHAIN_BROKE;
95 malloc_fatal(func);
96 return(NULL);
97 }
98 oldptr = ptr;
99 }
100 else if( oldptr != malloc_end )
101 {
102 /*
103 * This should never happen. If it does, then
104 * we got a real problem.
105 */
106 malloc_errno = M_CODE_NO_END;
107 malloc_fatal(func);
108 return(NULL);
109 }
110
111
112 /*
113 * if this element is already in use...
114 */
115 if( ptr && ((ptr->flag & M_INUSE) != 0) )
116 {
117 continue;
118 }
119
120 /*
121 * if there isn't room for this block..
122 */
123 if( ptr && (ptr->s.size < size) )
124 {
125 continue;
126 }
127
128 /*
129 * If ptr is null, we have run out of memory and must sbrk more
130 */
131 if( ptr == NULL )
132 {
133 need = (size + M_SIZE) * (size > 10*1024 ? 1:2);
134 if( need < M_BLOCKSIZE )
135 {
136 need = M_BLOCKSIZE;
137 }
138 else if( need & (M_BLOCKSIZE-1) )
139 {
140 need &= ~(M_BLOCKSIZE-1);
141 need += M_BLOCKSIZE;
142 }
143 ptr = (struct mlist *) sbrk((int)need);
144 if( ptr == (struct mlist *) -1 )
145 {
146 malloc_errno = M_CODE_NOMORE_MEM;
147 malloc_fatal(func);
148 }
149 malloc_data_end = sbrk((int)0);
150
151 ptr->prev = oldptr;
152 ptr->next = (struct mlist *) 0;
153 ptr->s.size = need - M_SIZE;
154 ptr->flag = M_MAGIC;
155
156 oldptr->next = ptr;
157 malloc_end = ptr;
158
159
160 } /* if( ptr ==... */
161
162 /*
163 * Now ptr points to a memory location that can store
164 * this data, so lets go to work.
165 */
166
167 ptr->r_size = size; /* save requested size */
168 ptr->flag |= M_INUSE;
169
170 /*
171 * split off unneeded data area in this block, if possible...
172 */
173 malloc_split(ptr);
174
175 /*
176 * re-adjust the requested size so that it is what the user
177 * actually requested...
178 */
179
180 ptr->r_size--;
181
182 /*
183 * just to make sure that noone is misusing malloced
184 * memory without initializing it, lets set it to
185 * all '\01's. We call local_memset() because memset()
186 * may be checking for malloc'd ptrs and this isn't
187 * a malloc'd ptr yet.
188 */
189 malloc_memset(ptr->data,M_FILL,(int)ptr->s.size);
190
191 return( ptr->data);
192
193 } /* for(... */
194
195 } /* malloc(... */
196
197 /*
198 * Function: malloc_split()
199 *
200 * Purpose: to split a malloc segment if there is enough room at the
201 * end of the segment that isn't being used
202 *
203 * Arguments: ptr - pointer to segment to split
204 *
205 * Returns: nothing of any use.
206 *
207 * Narrative:
208 * get the needed size of the module
209 * round the size up to appropriat boundry
210 * calculate amount of left over space
211 * if there is enough left over space
212 * create new malloc block out of remainder
213 * if next block is free
214 * join the two blocks together
215 * fill new empty block with free space filler
216 * re-adjust pointers and size of current malloc block
217 *
218 *
219 *
220 * Mod History:
221 * 90/01/27 cpcahil Initial revision.
222 */
223 void
malloc_split(ptr)224 malloc_split(ptr)
225 struct mlist * ptr;
226 {
227 extern struct mlist * malloc_end;
228 void malloc_join();
229 int rest;
230 int size;
231 struct mlist * tptr;
232
233 size = ptr->r_size;
234
235 /*
236 * roundup size to the appropriate boundry
237 */
238
239 M_ROUNDUP(size);
240
241 /*
242 * figure out how much room is left in the array.
243 * if there is enough room, create a new mlist
244 * structure there.
245 */
246
247 if( ptr->s.size > size )
248 {
249 rest = ptr->s.size - size;
250 }
251 else
252 {
253 rest = 0;
254 }
255
256 if( rest > (M_SIZE+M_RND) )
257 {
258 tptr = (struct mlist *) (ptr->data+size);
259 tptr->prev = ptr;
260 tptr->next = ptr->next;
261 tptr->flag = M_MAGIC;
262 tptr->s.size = rest - M_SIZE;
263
264 /*
265 * If possible, join this segment with the next one
266 */
267
268 malloc_join(tptr, tptr->next,0,0);
269
270 if( tptr->next )
271 {
272 tptr->next->prev = tptr;
273 }
274
275 malloc_memset(tptr->data,M_FREE_FILL, (int)tptr->s.size);
276
277 ptr->next = tptr;
278 ptr->s.size = size;
279
280 if( malloc_end == ptr )
281 {
282 malloc_end = tptr;
283 }
284 }
285
286 } /* malloc_split(... */
287
288 /*
289 * Function: malloc_join()
290 *
291 * Purpose: to join two malloc segments together (if possible)
292 *
293 * Arguments: ptr - pointer to segment to join to.
294 * nextptr - pointer to next segment to join to ptr.
295 *
296 * Returns: nothing of any values.
297 *
298 * Narrative:
299 *
300 * Mod History:
301 * 90/01/27 cpcahil Initial revision.
302 */
303 void
malloc_join(ptr,nextptr,inuse_override,fill_flag)304 malloc_join(ptr,nextptr, inuse_override, fill_flag)
305 struct mlist * ptr;
306 struct mlist * nextptr;
307 int inuse_override;
308 int fill_flag;
309 {
310 unsigned int newsize;
311
312 if( ptr && ! (inuse_override || (ptr->flag & M_INUSE)) &&
313 nextptr && ! (nextptr->flag & M_INUSE) &&
314 ((ptr->data+ptr->s.size) == (char *) nextptr) )
315 {
316 if( malloc_end == nextptr )
317 {
318 malloc_end = ptr;
319 }
320 ptr->next = nextptr->next;
321 newsize = nextptr->s.size + M_SIZE;
322
323 /*
324 * if we are to fill and this segment is in use,
325 * fill in with M_FILL newly added space...
326 */
327
328 if(fill_flag && (ptr->flag & M_INUSE) )
329 {
330 malloc_memset(ptr->data+ptr->s.size,
331 M_FILL, (int)(nextptr->s.size + M_SIZE));
332 }
333
334 ptr->s.size += newsize;
335 if( ptr->next )
336 {
337 ptr->next->prev = ptr;
338 }
339 }
340
341 } /* malloc_join(... */
342
343
344 /*
345 * The following mess is just to ensure that the versions of these functions in
346 * the current library are included (to make sure that we don't accidentaly get
347 * the libc versions. (This is the lazy man's -u ld directive)
348 */
349
350 void free();
351 int strcmp();
352 int memcmp();
353 char * realloc();
354
355 void (*malloc_void_funcs[])() =
356 {
357 free,
358 };
359
360 int (*malloc_int_funcs[])() =
361 {
362 strcmp,
363 memcmp,
364 };
365
366 char * (*malloc_char_star_funcs[])() =
367 {
368 realloc,
369 };
370
371 /*
372 * This is malloc's own memset which is used without checking the parameters.
373 */
374
375 void
malloc_memset(ptr,byte,len)376 malloc_memset(ptr,byte,len)
377 char * ptr;
378 char byte;
379 int len;
380 {
381
382 while(len-- > 0)
383 {
384 *ptr++ = byte;
385 }
386
387 } /* malloc_memset(... */
388
389 /*
390 * Function: malloc_fatal()
391 *
392 * Purpose: to display fatal error message and take approrpriate action
393 *
394 * Arguments: funcname - name of function calling this routine
395 *
396 * Returns: nothing of any value
397 *
398 * Narrative:
399 *
400 * Notes: This routine does not make use of any libc functions to build
401 * and/or disply the error message. This is due to the fact that
402 * we are probably at a point where malloc is having a real problem
403 * and we don't want to call any function that may use malloc.
404 */
405 void
malloc_fatal(funcname)406 malloc_fatal(funcname)
407 char * funcname;
408 {
409 char errbuf[128];
410 void exit();
411 void malloc_err_handler();
412 extern char * malloc_err_strings[];
413 extern int malloc_errno;
414 extern int malloc_fatal_level;
415 char * s;
416 char * t;
417
418 s = errbuf;
419 t = "Fatal error: ";
420 while( *s = *t++)
421 {
422 s++;
423 }
424 t = funcname;
425 while( *s = *t++)
426 {
427 s++;
428 }
429
430 t = "(): ";
431 while( *s = *t++)
432 {
433 s++;
434 }
435
436 t = malloc_err_strings[malloc_errno];
437 while( *s = *t++)
438 {
439 s++;
440 }
441
442 *(s++) = '\n';
443
444 if( write(malloc_errfd,errbuf,(unsigned)(s-errbuf)) != (s-errbuf))
445 {
446 (void) write(2,"I/O error to error file\n",(unsigned)24);
447 exit(110);
448 }
449 malloc_err_handler(malloc_fatal_level);
450
451 } /* malloc_fatal(... */
452
453 /*
454 * Function: malloc_warning()
455 *
456 * Purpose: to display warning error message and take approrpriate action
457 *
458 * Arguments: funcname - name of function calling this routine
459 *
460 * Returns: nothing of any value
461 *
462 * Narrative:
463 *
464 * Notes: This routine does not make use of any libc functions to build
465 * and/or disply the error message. This is due to the fact that
466 * we are probably at a point where malloc is having a real problem
467 * and we don't want to call any function that may use malloc.
468 */
469 void
malloc_warning(funcname)470 malloc_warning(funcname)
471 char * funcname;
472 {
473 char errbuf[128];
474 void exit();
475 void malloc_err_handler();
476 extern char * malloc_err_strings[];
477 extern int malloc_errno;
478 extern int malloc_warn_level;
479 char * s;
480 char * t;
481
482 s = errbuf;
483 t = "Warning: ";
484 while( *s = *t++)
485 {
486 s++;
487 }
488 t = funcname;
489 while( *s = *t++)
490 {
491 s++;
492 }
493
494 t = "(): ";
495 while( *s = *t++)
496 {
497 s++;
498 }
499
500 t = malloc_err_strings[malloc_errno];
501 while( *s = *t++)
502 {
503 s++;
504 }
505
506 *(s++) = '\n';
507
508 if( write(malloc_errfd,errbuf,(unsigned)(s-errbuf)) != (s-errbuf))
509 {
510 (void) write(2,"I/O error to error file\n",(unsigned)24);
511 exit(110);
512 }
513
514 malloc_err_handler(malloc_warn_level);
515
516 } /* malloc_warning(... */
517
518 /*
519 * Function: malloc_err_handler()
520 *
521 * Purpose: to take the appropriate action for warning and/or fatal
522 * error conditions.
523 *
524 * Arguments: level - error handling level
525 *
526 * Returns: nothing of any value
527 *
528 * Narrative:
529 *
530 * Notes: This routine does not make use of any libc functions to build
531 * and/or disply the error message. This is due to the fact that
532 * we are probably at a point where malloc is having a real problem
533 * and we don't want to call any function that may use malloc.
534 */
535 void
malloc_err_handler(level)536 malloc_err_handler(level)
537 {
538 void exit();
539 void malloc_dump();
540 extern int malloc_errfd;
541
542 if( level & M_HANDLE_DUMP )
543 {
544 malloc_dump(malloc_errfd);
545 }
546
547 switch( level & ~M_HANDLE_DUMP )
548 {
549 /*
550 * If we are to drop a core file and exit
551 */
552 case M_HANDLE_ABORT:
553 (void) abort();
554 break;
555
556 /*
557 * If we are to exit..
558 */
559 case M_HANDLE_EXIT:
560 exit(200);
561 break;
562
563 #ifndef __MSDOS__
564 /*
565 * If we are to dump a core, but keep going on our merry way
566 */
567 case M_HANDLE_CORE:
568 {
569 int pid;
570
571 /*
572 * fork so child can abort (and dump core)
573 */
574 if( (pid = fork()) == 0 )
575 {
576 (void) write(2,"Child dumping core\n",
577 (unsigned)9);
578 (void) abort();
579 }
580
581 /*
582 * wait for child to finish dumping core
583 */
584 while( wait((int *)0) != pid)
585 {
586 }
587
588 /*
589 * Move core file to core.pid.cnt so
590 * multiple cores don't overwrite each
591 * other.
592 */
593 if( access("core",0) == 0 )
594 {
595 static int corecnt;
596 char filenam[32];
597 filenam[0] = 'c';
598 filenam[1] = 'o';
599 filenam[2] = 'r';
600 filenam[3] = 'e';
601 filenam[4] = '.';
602 (void)tostring(filenam+5,getpid(),
603 5, B_DEC, '0');
604 filenam[10] = '.';
605 (void)tostring(filenam+11,corecnt++,
606 3, B_DEC, '0');
607 filenam[14] = '\0';
608 (void) unlink(filenam);
609 if( link("core",filenam) == 0)
610 {
611 (void) unlink("core");
612 }
613 }
614 }
615 #endif
616
617
618 /*
619 * If we are to just ignore the error and keep on processing
620 */
621 case M_HANDLE_IGNORE:
622 break;
623
624 } /* switch(... */
625
626 } /* malloc_err_handler(... */
627
628