1 /*
2  * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
3  * Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved.
4  * Copyright (c) 1997 by Silicon Graphics.  All rights reserved.
5  *
6  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
7  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
8  *
9  * Permission is hereby granted to use or copy this program
10  * for any purpose,  provided the above notices are retained on all copies.
11  * Permission to modify the code and to distribute modified code is granted,
12  * provided the above notices are retained, and a notice that the code was
13  * modified is included with the above copyright notice.
14  */
15 /* Boehm, October 9, 1995 1:16 pm PDT */
16 # include "gc_priv.h"
17 
18 /* Do we want to and know how to save the call stack at the time of	*/
19 /* an allocation?  How much space do we want to use in each object?	*/
20 
21 # define START_FLAG ((word)0xfedcedcb)
22 # define END_FLAG ((word)0xbcdecdef)
23 	/* Stored both one past the end of user object, and one before	*/
24 	/* the end of the object as seen by the allocator.		*/
25 
26 
27 /* Object header */
28 typedef struct {
29     char * oh_string;		/* object descriptor string	*/
30     word oh_int;		/* object descriptor integers	*/
31 #   ifdef SAVE_CALL_CHAIN
32       struct callinfo oh_ci[NFRAMES];
33 #   endif
34     word oh_sz;			/* Original malloc arg.		*/
35     word oh_sf;			/* start flag */
36 } oh;
37 /* The size of the above structure is assumed not to dealign things,	*/
38 /* and to be a multiple of the word length.				*/
39 
40 #define DEBUG_BYTES (sizeof (oh) + sizeof (word))
41 #undef ROUNDED_UP_WORDS
42 #define ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1) - 1)
43 
44 
45 #ifdef SAVE_CALL_CHAIN
46 #   define ADD_CALL_CHAIN(base) GC_save_callers(((oh *)(base)) -> oh_ci)
47 #   define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci)
48 #else
49 #   define ADD_CALL_CHAIN(base)
50 #   define PRINT_CALL_CHAIN(base)
51 #endif
52 
53 /* Check whether object with base pointer p has debugging info	*/
54 /* p is assumed to point to a legitimate object in our part	*/
55 /* of the heap.							*/
GC_has_debug_info(p)56 bool GC_has_debug_info(p)
57 ptr_t p;
58 {
59     register oh * ohdr = (oh *)p;
60     register ptr_t body = (ptr_t)(ohdr + 1);
61     register word sz = GC_size((ptr_t) ohdr);
62 
63     if (HBLKPTR((ptr_t)ohdr) != HBLKPTR((ptr_t)body)
64         || sz < sizeof (oh)) {
65         return(FALSE);
66     }
67     if (ohdr -> oh_sz == sz) {
68     	/* Object may have had debug info, but has been deallocated	*/
69     	return(FALSE);
70     }
71     if (ohdr -> oh_sf == (START_FLAG ^ (word)body)) return(TRUE);
72     if (((word *)ohdr)[BYTES_TO_WORDS(sz)-1] == (END_FLAG ^ (word)body)) {
73         return(TRUE);
74     }
75     return(FALSE);
76 }
77 
78 /* Store debugging info into p.  Return displaced pointer. */
79 /* Assumes we don't hold allocation lock.		   */
GC_store_debug_info(p,sz,string,integer)80 ptr_t GC_store_debug_info(p, sz, string, integer)
81 register ptr_t p;	/* base pointer */
82 word sz; 	/* bytes */
83 char * string;
84 word integer;
85 {
86     register word * result = (word *)((oh *)p + 1);
87     DCL_LOCK_STATE;
88 
89     /* There is some argument that we should dissble signals here.	*/
90     /* But that's expensive.  And this way things should only appear	*/
91     /* inconsistent while we're in the handler.				*/
92     LOCK();
93     ((oh *)p) -> oh_string = string;
94     ((oh *)p) -> oh_int = integer;
95     ((oh *)p) -> oh_sz = sz;
96     ((oh *)p) -> oh_sf = START_FLAG ^ (word)result;
97     ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] =
98          result[ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result;
99     UNLOCK();
100     return((ptr_t)result);
101 }
102 
103 /* Check the object with debugging info at p 		*/
104 /* return NIL if it's OK.  Else return clobbered	*/
105 /* address.						*/
GC_check_annotated_obj(ohdr)106 ptr_t GC_check_annotated_obj(ohdr)
107 register oh * ohdr;
108 {
109     register ptr_t body = (ptr_t)(ohdr + 1);
110     register word gc_sz = GC_size((ptr_t)ohdr);
111     if (ohdr -> oh_sz + DEBUG_BYTES > gc_sz) {
112         return((ptr_t)(&(ohdr -> oh_sz)));
113     }
114     if (ohdr -> oh_sf != (START_FLAG ^ (word)body)) {
115         return((ptr_t)(&(ohdr -> oh_sf)));
116     }
117     if (((word *)ohdr)[BYTES_TO_WORDS(gc_sz)-1] != (END_FLAG ^ (word)body)) {
118         return((ptr_t)((word *)ohdr + BYTES_TO_WORDS(gc_sz)-1));
119     }
120     if (((word *)body)[ROUNDED_UP_WORDS(ohdr -> oh_sz)]
121         != (END_FLAG ^ (word)body)) {
122         return((ptr_t)((word *)body + ROUNDED_UP_WORDS(ohdr -> oh_sz)));
123     }
124     return(0);
125 }
126 
GC_print_obj(p)127 void GC_print_obj(p)
128 ptr_t p;
129 {
130     register oh * ohdr = (oh *)GC_base(p);
131 
132     GC_err_printf1("0x%lx (", (unsigned long)ohdr + sizeof(oh));
133     GC_err_puts(ohdr -> oh_string);
134     GC_err_printf2(":%ld, sz=%ld)\n", (unsigned long)(ohdr -> oh_int),
135         			      (unsigned long)(ohdr -> oh_sz));
136     PRINT_CALL_CHAIN(ohdr);
137 }
GC_print_smashed_obj(p,clobbered_addr)138 void GC_print_smashed_obj(p, clobbered_addr)
139 ptr_t p, clobbered_addr;
140 {
141     register oh * ohdr = (oh *)GC_base(p);
142 
143     GC_err_printf2("0x%lx in object at 0x%lx(", (unsigned long)clobbered_addr,
144     					        (unsigned long)p);
145     if (clobbered_addr <= (ptr_t)(&(ohdr -> oh_sz))
146         || ohdr -> oh_string == 0) {
147         GC_err_printf1("<smashed>, appr. sz = %ld)\n",
148         	       GC_size((ptr_t)ohdr) - DEBUG_BYTES);
149     } else {
150         if (ohdr -> oh_string[0] == '\0') {
151             GC_err_puts("EMPTY(smashed?)");
152         } else {
153             GC_err_puts(ohdr -> oh_string);
154         }
155         GC_err_printf2(":%ld, sz=%ld)\n", (unsigned long)(ohdr -> oh_int),
156         			          (unsigned long)(ohdr -> oh_sz));
157         PRINT_CALL_CHAIN(ohdr);
158     }
159 }
160 
161 void GC_check_heap_proc();
162 
GC_start_debugging()163 void GC_start_debugging()
164 {
165     GC_check_heap = GC_check_heap_proc;
166     GC_debugging_started = TRUE;
167     GC_register_displacement((word)sizeof(oh));
168 }
169 
170 # if defined(__STDC__) || defined(__cplusplus)
GC_debug_register_displacement(GC_word offset)171     void GC_debug_register_displacement(GC_word offset)
172 # else
173     void GC_debug_register_displacement(offset)
174     GC_word offset;
175 # endif
176 {
177     GC_register_displacement(offset);
178     GC_register_displacement((word)sizeof(oh) + offset);
179 }
180 
181 # ifdef __STDC__
GC_debug_malloc(size_t lb,char * s,int i)182     GC_PTR GC_debug_malloc(size_t lb, char * s, int i)
183 # else
184     GC_PTR GC_debug_malloc(lb, s, i)
185     size_t lb;
186     char * s;
187     int i;
188 # endif
189 {
190     GC_PTR result = GC_malloc(lb + DEBUG_BYTES);
191 
192     if (result == 0) {
193         GC_err_printf1("GC_debug_malloc(%ld) returning NIL (",
194         	       (unsigned long) lb);
195         GC_err_puts(s);
196         GC_err_printf1(":%ld)\n", (unsigned long)i);
197         return(0);
198     }
199     if (!GC_debugging_started) {
200     	GC_start_debugging();
201     }
202     ADD_CALL_CHAIN(result);
203     return (GC_store_debug_info(result, (word)lb, s, (word)i));
204 }
205 
206 #ifdef STUBBORN_ALLOC
207 # ifdef __STDC__
GC_debug_malloc_stubborn(size_t lb,char * s,int i)208     GC_PTR GC_debug_malloc_stubborn(size_t lb, char * s, int i)
209 # else
210     GC_PTR GC_debug_malloc_stubborn(lb, s, i)
211     size_t lb;
212     char * s;
213     int i;
214 # endif
215 {
216     GC_PTR result = GC_malloc_stubborn(lb + DEBUG_BYTES);
217 
218     if (result == 0) {
219         GC_err_printf1("GC_debug_malloc(%ld) returning NIL (",
220         	       (unsigned long) lb);
221         GC_err_puts(s);
222         GC_err_printf1(":%ld)\n", (unsigned long)i);
223         return(0);
224     }
225     if (!GC_debugging_started) {
226     	GC_start_debugging();
227     }
228     ADD_CALL_CHAIN(result);
229     return (GC_store_debug_info(result, (word)lb, s, (word)i));
230 }
231 
GC_debug_change_stubborn(p)232 void GC_debug_change_stubborn(p)
233 GC_PTR p;
234 {
235     register GC_PTR q = GC_base(p);
236     register hdr * hhdr;
237 
238     if (q == 0) {
239         GC_err_printf1("Bad argument: 0x%lx to GC_debug_change_stubborn\n",
240         	       (unsigned long) p);
241         ABORT("GC_debug_change_stubborn: bad arg");
242     }
243     hhdr = HDR(q);
244     if (hhdr -> hb_obj_kind != STUBBORN) {
245         GC_err_printf1("GC_debug_change_stubborn arg not stubborn: 0x%lx\n",
246         	       (unsigned long) p);
247         ABORT("GC_debug_change_stubborn: arg not stubborn");
248     }
249     GC_change_stubborn(q);
250 }
251 
GC_debug_end_stubborn_change(p)252 void GC_debug_end_stubborn_change(p)
253 GC_PTR p;
254 {
255     register GC_PTR q = GC_base(p);
256     register hdr * hhdr;
257 
258     if (q == 0) {
259         GC_err_printf1("Bad argument: 0x%lx to GC_debug_end_stubborn_change\n",
260         	       (unsigned long) p);
261         ABORT("GC_debug_end_stubborn_change: bad arg");
262     }
263     hhdr = HDR(q);
264     if (hhdr -> hb_obj_kind != STUBBORN) {
265         GC_err_printf1("debug_end_stubborn_change arg not stubborn: 0x%lx\n",
266         	       (unsigned long) p);
267         ABORT("GC_debug_end_stubborn_change: arg not stubborn");
268     }
269     GC_end_stubborn_change(q);
270 }
271 
272 #endif /* STUBBORN_ALLOC */
273 
274 # ifdef __STDC__
GC_debug_malloc_atomic(size_t lb,char * s,int i)275     GC_PTR GC_debug_malloc_atomic(size_t lb, char * s, int i)
276 # else
277     GC_PTR GC_debug_malloc_atomic(lb, s, i)
278     size_t lb;
279     char * s;
280     int i;
281 # endif
282 {
283     GC_PTR result = GC_malloc_atomic(lb + DEBUG_BYTES);
284 
285     if (result == 0) {
286         GC_err_printf1("GC_debug_malloc_atomic(%ld) returning NIL (",
287         	      (unsigned long) lb);
288         GC_err_puts(s);
289         GC_err_printf1(":%ld)\n", (unsigned long)i);
290         return(0);
291     }
292     if (!GC_debugging_started) {
293         GC_start_debugging();
294     }
295     ADD_CALL_CHAIN(result);
296     return (GC_store_debug_info(result, (word)lb, s, (word)i));
297 }
298 
299 # ifdef __STDC__
GC_debug_malloc_uncollectable(size_t lb,char * s,int i)300     GC_PTR GC_debug_malloc_uncollectable(size_t lb, char * s, int i)
301 # else
302     GC_PTR GC_debug_malloc_uncollectable(lb, s, i)
303     size_t lb;
304     char * s;
305     int i;
306 # endif
307 {
308     GC_PTR result = GC_malloc_uncollectable(lb + DEBUG_BYTES);
309 
310     if (result == 0) {
311         GC_err_printf1("GC_debug_malloc_uncollectable(%ld) returning NIL (",
312         	      (unsigned long) lb);
313         GC_err_puts(s);
314         GC_err_printf1(":%ld)\n", (unsigned long)i);
315         return(0);
316     }
317     if (!GC_debugging_started) {
318         GC_start_debugging();
319     }
320     ADD_CALL_CHAIN(result);
321     return (GC_store_debug_info(result, (word)lb, s, (word)i));
322 }
323 
324 #ifdef ATOMIC_UNCOLLECTABLE
325 # ifdef __STDC__
GC_debug_malloc_atomic_uncollectable(size_t lb,char * s,int i)326     GC_PTR GC_debug_malloc_atomic_uncollectable(size_t lb, char * s, int i)
327 # else
328     GC_PTR GC_debug_malloc_atomic_uncollectable(lb, s, i)
329     size_t lb;
330     char * s;
331     int i;
332 # endif
333 {
334     GC_PTR result = GC_malloc_atomic_uncollectable(lb + DEBUG_BYTES);
335 
336     if (result == 0) {
337         GC_err_printf1(
338 		"GC_debug_malloc_atomic_uncollectable(%ld) returning NIL (",
339                 (unsigned long) lb);
340         GC_err_puts(s);
341         GC_err_printf1(":%ld)\n", (unsigned long)i);
342         return(0);
343     }
344     if (!GC_debugging_started) {
345         GC_start_debugging();
346     }
347     ADD_CALL_CHAIN(result);
348     return (GC_store_debug_info(result, (word)lb, s, (word)i));
349 }
350 #endif /* ATOMIC_UNCOLLECTABLE */
351 
352 # ifdef __STDC__
GC_debug_free(GC_PTR p)353     void GC_debug_free(GC_PTR p)
354 # else
355     void GC_debug_free(p)
356     GC_PTR p;
357 # endif
358 {
359     register GC_PTR base = GC_base(p);
360     register ptr_t clobbered;
361 
362     if (base == 0) {
363         GC_err_printf1("Attempt to free invalid pointer %lx\n",
364         	       (unsigned long)p);
365         if (p != 0) ABORT("free(invalid pointer)");
366     }
367     if ((ptr_t)p - (ptr_t)base != sizeof(oh)) {
368         GC_err_printf1(
369         	  "GC_debug_free called on pointer %lx wo debugging info\n",
370         	  (unsigned long)p);
371     } else {
372       clobbered = GC_check_annotated_obj((oh *)base);
373       if (clobbered != 0) {
374         if (((oh *)base) -> oh_sz == GC_size(base)) {
375             GC_err_printf0(
376                   "GC_debug_free: found previously deallocated (?) object at ");
377         } else {
378             GC_err_printf0("GC_debug_free: found smashed object at ");
379         }
380         GC_print_smashed_obj(p, clobbered);
381       }
382       /* Invalidate size */
383       ((oh *)base) -> oh_sz = GC_size(base);
384     }
385 #   ifdef FIND_LEAK
386         GC_free(base);
387 #   else
388 	{
389 	    register hdr * hhdr = HDR(p);
390 	    bool uncollectable = FALSE;
391 
392 	    if (hhdr ->  hb_obj_kind == UNCOLLECTABLE) {
393 		uncollectable = TRUE;
394 	    }
395 #	    ifdef ATOMIC_UNCOLLECTABLE
396 		if (hhdr ->  hb_obj_kind == AUNCOLLECTABLE) {
397 		    uncollectable = TRUE;
398 		}
399 #	    endif
400 	    if (uncollectable) GC_free(base);
401 	}
402 #   endif
403 }
404 
405 # ifdef __STDC__
GC_debug_realloc(GC_PTR p,size_t lb,char * s,int i)406     GC_PTR GC_debug_realloc(GC_PTR p, size_t lb, char *s, int i)
407 # else
408     GC_PTR GC_debug_realloc(p, lb, s, i)
409     GC_PTR p;
410     size_t lb;
411     char *s;
412     int i;
413 # endif
414 {
415     register GC_PTR base = GC_base(p);
416     register ptr_t clobbered;
417     register GC_PTR result;
418     register size_t copy_sz = lb;
419     register size_t old_sz;
420     register hdr * hhdr;
421 
422     if (p == 0) return(GC_debug_malloc(lb, s, i));
423     if (base == 0) {
424         GC_err_printf1(
425               "Attempt to reallocate invalid pointer %lx\n", (unsigned long)p);
426         ABORT("realloc(invalid pointer)");
427     }
428     if ((ptr_t)p - (ptr_t)base != sizeof(oh)) {
429         GC_err_printf1(
430         	"GC_debug_realloc called on pointer %lx wo debugging info\n",
431         	(unsigned long)p);
432         return(GC_realloc(p, lb));
433     }
434     hhdr = HDR(base);
435     switch (hhdr -> hb_obj_kind) {
436 #    ifdef STUBBORN_ALLOC
437       case STUBBORN:
438         result = GC_debug_malloc_stubborn(lb, s, i);
439         break;
440 #    endif
441       case NORMAL:
442         result = GC_debug_malloc(lb, s, i);
443         break;
444       case PTRFREE:
445         result = GC_debug_malloc_atomic(lb, s, i);
446         break;
447       case UNCOLLECTABLE:
448 	result = GC_debug_malloc_uncollectable(lb, s, i);
449  	break;
450 #    ifdef ATOMIC_UNCOLLECTABLE
451       case AUNCOLLECTABLE:
452 	result = GC_debug_malloc_atomic_uncollectable(lb, s, i);
453 	break;
454 #    endif
455       default:
456         GC_err_printf0("GC_debug_realloc: encountered bad kind\n");
457         ABORT("bad kind");
458     }
459     clobbered = GC_check_annotated_obj((oh *)base);
460     if (clobbered != 0) {
461         GC_err_printf0("GC_debug_realloc: found smashed object at ");
462         GC_print_smashed_obj(p, clobbered);
463     }
464     old_sz = ((oh *)base) -> oh_sz;
465     if (old_sz < copy_sz) copy_sz = old_sz;
466     if (result == 0) return(0);
467     BCOPY(p, result,  copy_sz);
468     GC_debug_free(p);
469     return(result);
470 }
471 
472 /* Check all marked objects in the given block for validity */
473 /*ARGSUSED*/
GC_check_heap_block(hbp,dummy)474 void GC_check_heap_block(hbp, dummy)
475 register struct hblk *hbp;	/* ptr to current heap block		*/
476 word dummy;
477 {
478     register struct hblkhdr * hhdr = HDR(hbp);
479     register word sz = hhdr -> hb_sz;
480     register int word_no;
481     register word *p, *plim;
482 
483     p = (word *)(hbp->hb_body);
484     word_no = HDR_WORDS;
485     if (sz > MAXOBJSZ) {
486 	plim = p;
487     } else {
488     	plim = (word *)((((word)hbp) + HBLKSIZE) - WORDS_TO_BYTES(sz));
489     }
490     /* go through all words in block */
491 	while( p <= plim ) {
492 	    if( mark_bit_from_hdr(hhdr, word_no)
493 	        && GC_has_debug_info((ptr_t)p)) {
494 	        ptr_t clobbered = GC_check_annotated_obj((oh *)p);
495 
496 	        if (clobbered != 0) {
497 	            GC_err_printf0(
498 	                "GC_check_heap_block: found smashed object at ");
499         	    GC_print_smashed_obj((ptr_t)p, clobbered);
500 	        }
501 	    }
502 	    word_no += sz;
503 	    p += sz;
504 	}
505 }
506 
507 
508 /* This assumes that all accessible objects are marked, and that	*/
509 /* I hold the allocation lock.	Normally called by collector.		*/
GC_check_heap_proc()510 void GC_check_heap_proc()
511 {
512 #   ifndef SMALL_CONFIG
513 	if (sizeof(oh) & (2 * sizeof(word) - 1) != 0) {
514 	    ABORT("Alignment problem: object header has inappropriate size\n");
515 	}
516 #   endif
517     GC_apply_to_all_blocks(GC_check_heap_block, (word)0);
518 }
519 
520 struct closure {
521     GC_finalization_proc cl_fn;
522     GC_PTR cl_data;
523 };
524 
525 # ifdef __STDC__
GC_make_closure(GC_finalization_proc fn,void * data)526     void * GC_make_closure(GC_finalization_proc fn, void * data)
527 # else
528     GC_PTR GC_make_closure(fn, data)
529     GC_finalization_proc fn;
530     GC_PTR data;
531 # endif
532 {
533     struct closure * result =
534     		(struct closure *) GC_malloc(sizeof (struct closure));
535 
536     result -> cl_fn = fn;
537     result -> cl_data = data;
538     return((GC_PTR)result);
539 }
540 
541 # ifdef __STDC__
GC_debug_invoke_finalizer(void * obj,void * data)542     void GC_debug_invoke_finalizer(void * obj, void * data)
543 # else
544     void GC_debug_invoke_finalizer(obj, data)
545     char * obj;
546     char * data;
547 # endif
548 {
549     register struct closure * cl = (struct closure *) data;
550 
551     (*(cl -> cl_fn))((GC_PTR)((char *)obj + sizeof(oh)), cl -> cl_data);
552 }
553 
554 
555 # ifdef __STDC__
GC_debug_register_finalizer(GC_PTR obj,GC_finalization_proc fn,GC_PTR cd,GC_finalization_proc * ofn,GC_PTR * ocd)556     void GC_debug_register_finalizer(GC_PTR obj, GC_finalization_proc fn,
557     				     GC_PTR cd, GC_finalization_proc *ofn,
558 				     GC_PTR *ocd)
559 # else
560     void GC_debug_register_finalizer(obj, fn, cd, ofn, ocd)
561     GC_PTR obj;
562     GC_finalization_proc fn;
563     GC_PTR cd;
564     GC_finalization_proc *ofn;
565     GC_PTR *ocd;
566 # endif
567 {
568     ptr_t base = GC_base(obj);
569     if (0 == base || (ptr_t)obj - base != sizeof(oh)) {
570         GC_err_printf1(
571 	    "GC_register_finalizer called with non-base-pointer 0x%lx\n",
572 	    obj);
573     }
574     GC_register_finalizer(base, GC_debug_invoke_finalizer,
575     			  GC_make_closure(fn,cd), ofn, ocd);
576 }
577 
578 # ifdef __STDC__
GC_debug_register_finalizer_ignore_self(GC_PTR obj,GC_finalization_proc fn,GC_PTR cd,GC_finalization_proc * ofn,GC_PTR * ocd)579     void GC_debug_register_finalizer_ignore_self
580     				    (GC_PTR obj, GC_finalization_proc fn,
581     				     GC_PTR cd, GC_finalization_proc *ofn,
582 				     GC_PTR *ocd)
583 # else
584     void GC_debug_register_finalizer_ignore_self
585     				    (obj, fn, cd, ofn, ocd)
586     GC_PTR obj;
587     GC_finalization_proc fn;
588     GC_PTR cd;
589     GC_finalization_proc *ofn;
590     GC_PTR *ocd;
591 # endif
592 {
593     ptr_t base = GC_base(obj);
594     if (0 == base || (ptr_t)obj - base != sizeof(oh)) {
595         GC_err_printf1(
596 	    "GC_register_finalizer_ignore_self called with non-base-pointer 0x%lx\n",
597 	    obj);
598     }
599     GC_register_finalizer_ignore_self(base, GC_debug_invoke_finalizer,
600     			  	      GC_make_closure(fn,cd), ofn, ocd);
601 }
602