Fix leak_test crash in print_callers if free() is redirected
[platform/upstream/libgc.git] / dbg_mlc.c
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  * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P.
6  * Copyright (C) 2007 Free Software Foundation, Inc
7  *
8  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
9  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
10  *
11  * Permission is hereby granted to use or copy this program
12  * for any purpose,  provided the above notices are retained on all copies.
13  * Permission to modify the code and to distribute modified code is granted,
14  * provided the above notices are retained, and a notice that the code was
15  * modified is included with the above copyright notice.
16  */
17
18 #include "private/dbg_mlc.h"
19
20 #ifndef MSWINCE
21 # include <errno.h>
22 #endif
23 #include <string.h>
24
25 #ifndef SHORT_DBG_HDRS
26   /* Check whether object with base pointer p has debugging info. */
27   /* p is assumed to point to a legitimate object in our part     */
28   /* of the heap.                                                 */
29   /* This excludes the check as to whether the back pointer is    */
30   /* odd, which is added by the GC_HAS_DEBUG_INFO macro.          */
31   /* Note that if DBG_HDRS_ALL is set, uncollectible objects      */
32   /* on free lists may not have debug information set.  Thus it's */
33   /* not always safe to return TRUE (1), even if the client does  */
34   /* its part.  Return -1 if the object with debug info has been  */
35   /* marked as deallocated.                                       */
36   GC_INNER int GC_has_other_debug_info(ptr_t p)
37   {
38     ptr_t body = (ptr_t)((oh *)p + 1);
39     word sz = GC_size(p);
40
41     if (HBLKPTR(p) != HBLKPTR((ptr_t)body)
42         || sz < DEBUG_BYTES + EXTRA_BYTES) {
43       return 0;
44     }
45     if (((oh *)p) -> oh_sf != (START_FLAG ^ (word)body)
46         && ((word *)p)[BYTES_TO_WORDS(sz)-1] != (END_FLAG ^ (word)body)) {
47       return 0;
48     }
49     if (((oh *)p)->oh_sz == sz) {
50       /* Object may have had debug info, but has been deallocated     */
51       return -1;
52     }
53     return 1;
54   }
55 #endif /* !SHORT_DBG_HDRS */
56
57 #ifdef LINT2
58   long GC_random(void)
59   {
60     static unsigned seed = 1; /* not thread-safe */
61
62     /* Linear congruential pseudo-random numbers generator.     */
63     seed = (seed * 1103515245U + 12345) & GC_RAND_MAX; /* overflow is ok */
64     return (long)seed;
65   }
66 #endif
67
68 #ifdef KEEP_BACK_PTRS
69
70 #ifdef LINT2
71 # define RANDOM() GC_random()
72 #else
73 # include <stdlib.h>
74 # define GC_RAND_MAX RAND_MAX
75
76 # if defined(__GLIBC__) || defined(SOLARIS) \
77      || defined(HPUX) || defined(IRIX5) || defined(OSF1)
78 #   define RANDOM() random()
79 # else
80 #   define RANDOM() (long)rand()
81 # endif
82 #endif /* !LINT2 */
83
84   /* Store back pointer to source in dest, if that appears to be possible. */
85   /* This is not completely safe, since we may mistakenly conclude that    */
86   /* dest has a debugging wrapper.  But the error probability is very      */
87   /* small, and this shouldn't be used in production code.                 */
88   /* We assume that dest is the real base pointer.  Source will usually    */
89   /* be a pointer to the interior of an object.                            */
90   GC_INNER void GC_store_back_pointer(ptr_t source, ptr_t dest)
91   {
92     if (GC_HAS_DEBUG_INFO(dest)) {
93       ((oh *)dest) -> oh_back_ptr = HIDE_BACK_PTR(source);
94     }
95   }
96
97   GC_INNER void GC_marked_for_finalization(ptr_t dest)
98   {
99     GC_store_back_pointer(MARKED_FOR_FINALIZATION, dest);
100   }
101
102   /* Store information about the object referencing dest in *base_p     */
103   /* and *offset_p.                                                     */
104   /*   source is root ==> *base_p = address, *offset_p = 0              */
105   /*   source is heap object ==> *base_p != 0, *offset_p = offset       */
106   /*   Returns 1 on success, 0 if source couldn't be determined.        */
107   /* Dest can be any address within a heap object.                      */
108   GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void *dest, void **base_p,
109                                                   size_t *offset_p)
110   {
111     oh * hdr = (oh *)GC_base(dest);
112     ptr_t bp;
113     ptr_t bp_base;
114
115 #   ifdef LINT2
116       /* Explicitly instruct the code analysis tool that                */
117       /* GC_get_back_ptr_info is not expected to be called with an      */
118       /* incorrect "dest" value.                                        */
119       if (!hdr) ABORT("Invalid GC_get_back_ptr_info argument");
120 #   endif
121     if (!GC_HAS_DEBUG_INFO((ptr_t) hdr)) return GC_NO_SPACE;
122     bp = GC_REVEAL_POINTER(hdr -> oh_back_ptr);
123     if (MARKED_FOR_FINALIZATION == bp) return GC_FINALIZER_REFD;
124     if (MARKED_FROM_REGISTER == bp) return GC_REFD_FROM_REG;
125     if (NOT_MARKED == bp) return GC_UNREFERENCED;
126 #   if ALIGNMENT == 1
127       /* Heuristically try to fix off by 1 errors we introduced by      */
128       /* insisting on even addresses.                                   */
129       {
130         ptr_t alternate_ptr = bp + 1;
131         ptr_t target = *(ptr_t *)bp;
132         ptr_t alternate_target = *(ptr_t *)alternate_ptr;
133
134         if ((word)alternate_target >= (word)GC_least_plausible_heap_addr
135             && (word)alternate_target <= (word)GC_greatest_plausible_heap_addr
136             && ((word)target < (word)GC_least_plausible_heap_addr
137                 || (word)target > (word)GC_greatest_plausible_heap_addr)) {
138             bp = alternate_ptr;
139         }
140       }
141 #   endif
142     bp_base = GC_base(bp);
143     if (0 == bp_base) {
144       *base_p = bp;
145       *offset_p = 0;
146       return GC_REFD_FROM_ROOT;
147     } else {
148       if (GC_HAS_DEBUG_INFO(bp_base)) bp_base += sizeof(oh);
149       *base_p = bp_base;
150       *offset_p = bp - bp_base;
151       return GC_REFD_FROM_HEAP;
152     }
153   }
154
155   /* Generate a random heap address.            */
156   /* The resulting address is in the heap, but  */
157   /* not necessarily inside a valid object.     */
158   GC_API void * GC_CALL GC_generate_random_heap_address(void)
159   {
160     size_t i;
161     word heap_offset = RANDOM();
162
163     if (GC_heapsize > GC_RAND_MAX) {
164         heap_offset *= GC_RAND_MAX;
165         heap_offset += RANDOM();
166     }
167     heap_offset %= GC_heapsize;
168         /* This doesn't yield a uniform distribution, especially if     */
169         /* e.g. RAND_MAX = 1.5* GC_heapsize.  But for typical cases,    */
170         /* it's not too bad.                                            */
171     for (i = 0;; ++i) {
172         size_t size;
173
174         if (i >= GC_n_heap_sects)
175           ABORT("GC_generate_random_heap_address: size inconsistency");
176
177         size = GC_heap_sects[i].hs_bytes;
178         if (heap_offset < size) {
179             break;
180         } else {
181             heap_offset -= size;
182         }
183     }
184     return GC_heap_sects[i].hs_start + heap_offset;
185   }
186
187   /* Generate a random address inside a valid marked heap object. */
188   GC_API void * GC_CALL GC_generate_random_valid_address(void)
189   {
190     ptr_t result;
191     ptr_t base;
192     do {
193       result = GC_generate_random_heap_address();
194       base = GC_base(result);
195     } while (base == 0 || !GC_is_marked(base));
196     return result;
197   }
198
199   /* Print back trace for p */
200   GC_API void GC_CALL GC_print_backtrace(void *p)
201   {
202     void *current = p;
203     int i;
204     GC_ref_kind source;
205     size_t offset;
206     void *base;
207
208     GC_print_heap_obj(GC_base(current));
209
210     for (i = 0; ; ++i) {
211       source = GC_get_back_ptr_info(current, &base, &offset);
212       if (GC_UNREFERENCED == source) {
213         GC_err_printf("Reference could not be found\n");
214         goto out;
215       }
216       if (GC_NO_SPACE == source) {
217         GC_err_printf("No debug info in object: Can't find reference\n");
218         goto out;
219       }
220       GC_err_printf("Reachable via %d levels of pointers from ", i);
221       switch(source) {
222         case GC_REFD_FROM_ROOT:
223           GC_err_printf("root at %p\n\n", base);
224           goto out;
225         case GC_REFD_FROM_REG:
226           GC_err_printf("root in register\n\n");
227           goto out;
228         case GC_FINALIZER_REFD:
229           GC_err_printf("list of finalizable objects\n\n");
230           goto out;
231         case GC_REFD_FROM_HEAP:
232           GC_err_printf("offset %ld in object:\n", (long)offset);
233           /* Take GC_base(base) to get real base, i.e. header. */
234           GC_print_heap_obj(GC_base(base));
235           break;
236         default:
237           GC_err_printf("INTERNAL ERROR: UNEXPECTED SOURCE!!!!\n");
238           goto out;
239       }
240       current = base;
241     }
242     out:;
243   }
244
245   /* Force a garbage collection and generate/print a backtrace  */
246   /* from a random heap address.                                */
247   GC_INNER void GC_generate_random_backtrace_no_gc(void)
248   {
249     void * current;
250     current = GC_generate_random_valid_address();
251     GC_printf("\n****Chosen address %p in object\n", current);
252     GC_print_backtrace(current);
253   }
254
255   GC_API void GC_CALL GC_generate_random_backtrace(void)
256   {
257     if (GC_try_to_collect(GC_never_stop_func) == 0) {
258       GC_err_printf("Cannot generate a backtrace: "
259                     "garbage collection is disabled!\n");
260       return;
261     }
262     GC_generate_random_backtrace_no_gc();
263   }
264
265 #endif /* KEEP_BACK_PTRS */
266
267 # define CROSSES_HBLK(p, sz) \
268         (((word)((p) + sizeof(oh) + (sz) - 1) ^ (word)(p)) >= HBLKSIZE)
269
270 /* Store debugging info into p.  Return displaced pointer.         */
271 /* This version assumes we do hold the allocation lock.            */
272 STATIC ptr_t GC_store_debug_info_inner(ptr_t p, word sz GC_ATTR_UNUSED,
273                                        const char *string, int linenum)
274 {
275     word * result = (word *)((oh *)p + 1);
276
277     GC_ASSERT(GC_size(p) >= sizeof(oh) + sz);
278     GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK(p, sz)));
279 #   ifdef KEEP_BACK_PTRS
280       ((oh *)p) -> oh_back_ptr = HIDE_BACK_PTR(NOT_MARKED);
281 #   endif
282 #   ifdef MAKE_BACK_GRAPH
283       ((oh *)p) -> oh_bg_ptr = HIDE_BACK_PTR((ptr_t)0);
284 #   endif
285     ((oh *)p) -> oh_string = string;
286     ((oh *)p) -> oh_int = (word)linenum;
287 #   ifndef SHORT_DBG_HDRS
288       ((oh *)p) -> oh_sz = sz;
289       ((oh *)p) -> oh_sf = START_FLAG ^ (word)result;
290       ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] =
291          result[SIMPLE_ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result;
292 #   endif
293     return((ptr_t)result);
294 }
295
296 GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *string,
297                                    int linenum)
298 {
299     ptr_t result;
300     DCL_LOCK_STATE;
301
302     LOCK();
303     result = GC_store_debug_info_inner(p, sz, string, linenum);
304     UNLOCK();
305     return result;
306 }
307
308 #ifndef SHORT_DBG_HDRS
309   /* Check the object with debugging info at ohdr.      */
310   /* Return NULL if it's OK.  Else return clobbered     */
311   /* address.                                           */
312   STATIC ptr_t GC_check_annotated_obj(oh *ohdr)
313   {
314     ptr_t body = (ptr_t)(ohdr + 1);
315     word gc_sz = GC_size((ptr_t)ohdr);
316     if (ohdr -> oh_sz + DEBUG_BYTES > gc_sz) {
317         return((ptr_t)(&(ohdr -> oh_sz)));
318     }
319     if (ohdr -> oh_sf != (START_FLAG ^ (word)body)) {
320         return((ptr_t)(&(ohdr -> oh_sf)));
321     }
322     if (((word *)ohdr)[BYTES_TO_WORDS(gc_sz)-1] != (END_FLAG ^ (word)body)) {
323         return((ptr_t)((word *)ohdr + BYTES_TO_WORDS(gc_sz)-1));
324     }
325     if (((word *)body)[SIMPLE_ROUNDED_UP_WORDS(ohdr -> oh_sz)]
326         != (END_FLAG ^ (word)body)) {
327         return((ptr_t)((word *)body + SIMPLE_ROUNDED_UP_WORDS(ohdr->oh_sz)));
328     }
329     return(0);
330   }
331 #endif /* !SHORT_DBG_HDRS */
332
333 STATIC GC_describe_type_fn GC_describe_type_fns[MAXOBJKINDS] = {0};
334
335 GC_API void GC_CALL GC_register_describe_type_fn(int kind,
336                                                  GC_describe_type_fn fn)
337 {
338   GC_describe_type_fns[kind] = fn;
339 }
340
341 #define GET_OH_LINENUM(ohdr) ((int)(ohdr)->oh_int)
342
343 #ifndef SHORT_DBG_HDRS
344 # define IF_NOT_SHORTDBG_HDRS(x) x
345 # define COMMA_IFNOT_SHORTDBG_HDRS(x) /* comma */, x
346 #else
347 # define IF_NOT_SHORTDBG_HDRS(x) /* empty */
348 # define COMMA_IFNOT_SHORTDBG_HDRS(x) /* empty */
349 #endif
350
351 /* Print a human-readable description of the object to stderr.          */
352 /* p points to somewhere inside an object with the debugging info.      */
353 STATIC void GC_print_obj(ptr_t p)
354 {
355     oh * ohdr = (oh *)GC_base(p);
356     ptr_t q;
357     hdr * hhdr;
358     int kind;
359     char *kind_str;
360     char buffer[GC_TYPE_DESCR_LEN + 1];
361
362     GC_ASSERT(I_DONT_HOLD_LOCK());
363 #   ifdef LINT2
364       if (!ohdr) ABORT("Invalid GC_print_obj argument");
365 #   endif
366
367     q = (ptr_t)(ohdr + 1);
368     /* Print a type description for the object whose client-visible     */
369     /* address is q.                                                    */
370     hhdr = GC_find_header(q);
371     kind = hhdr -> hb_obj_kind;
372     if (0 != GC_describe_type_fns[kind] && GC_is_marked(ohdr)) {
373         /* This should preclude free list objects except with   */
374         /* thread-local allocation.                             */
375         buffer[GC_TYPE_DESCR_LEN] = 0;
376         (GC_describe_type_fns[kind])(q, buffer);
377         GC_ASSERT(buffer[GC_TYPE_DESCR_LEN] == 0);
378         kind_str = buffer;
379     } else {
380         switch(kind) {
381           case PTRFREE:
382             kind_str = "PTRFREE";
383             break;
384           case NORMAL:
385             kind_str = "NORMAL";
386             break;
387           case UNCOLLECTABLE:
388             kind_str = "UNCOLLECTABLE";
389             break;
390 #         ifdef GC_ATOMIC_UNCOLLECTABLE
391             case AUNCOLLECTABLE:
392               kind_str = "ATOMIC_UNCOLLECTABLE";
393               break;
394 #         endif
395           case STUBBORN:
396             kind_str = "STUBBORN";
397             break;
398           default:
399             kind_str = NULL;
400                 /* The alternative is to use snprintf(buffer) but it is */
401                 /* not quite portable (see vsnprintf in misc.c).        */
402         }
403     }
404
405     if (NULL != kind_str) {
406         GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz=%lu,") " %s)\n",
407                       (void *)((ptr_t)ohdr + sizeof(oh)),
408                       ohdr->oh_string, GET_OH_LINENUM(ohdr) /*, */
409                       COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz),
410                       kind_str);
411     } else {
412         GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz=%lu,")
413                       " kind=%d descr=0x%lx)\n",
414                       (void *)((ptr_t)ohdr + sizeof(oh)),
415                       ohdr->oh_string, GET_OH_LINENUM(ohdr) /*, */
416                       COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz),
417                       kind, (unsigned long)hhdr->hb_descr);
418     }
419     PRINT_CALL_CHAIN(ohdr);
420 }
421
422 STATIC void GC_debug_print_heap_obj_proc(ptr_t p)
423 {
424     GC_ASSERT(I_DONT_HOLD_LOCK());
425     if (GC_HAS_DEBUG_INFO(p)) {
426         GC_print_obj(p);
427     } else {
428         GC_default_print_heap_obj_proc(p);
429     }
430 }
431
432 #ifndef SHORT_DBG_HDRS
433   /* Use GC_err_printf and friends to print a description of the object */
434   /* whose client-visible address is p, and which was smashed at        */
435   /* clobbered_addr.                                                    */
436   STATIC void GC_print_smashed_obj(const char *msg, ptr_t p,
437                                    ptr_t clobbered_addr)
438   {
439     oh * ohdr = (oh *)GC_base(p);
440
441     GC_ASSERT(I_DONT_HOLD_LOCK());
442 #   ifdef LINT2
443       if (!ohdr) ABORT("Invalid GC_print_smashed_obj argument");
444 #   endif
445     if ((word)clobbered_addr <= (word)(&ohdr->oh_sz)
446         || ohdr -> oh_string == 0) {
447         GC_err_printf(
448                 "%s %p in or near object at %p(<smashed>, appr. sz = %lu)\n",
449                 msg, (void *)clobbered_addr, (void *)p,
450                 (unsigned long)(GC_size((ptr_t)ohdr) - DEBUG_BYTES));
451     } else {
452         GC_err_printf("%s %p in or near object at %p (%s:%d, sz=%lu)\n",
453                 msg, (void *)clobbered_addr, (void *)p,
454                 (word)(ohdr -> oh_string) < HBLKSIZE ? "(smashed string)" :
455                 ohdr -> oh_string[0] == '\0' ? "EMPTY(smashed?)" :
456                                                 ohdr -> oh_string,
457                 GET_OH_LINENUM(ohdr), (unsigned long)(ohdr -> oh_sz));
458         PRINT_CALL_CHAIN(ohdr);
459     }
460   }
461
462   STATIC void GC_check_heap_proc (void);
463   STATIC void GC_print_all_smashed_proc (void);
464 #else
465   STATIC void GC_do_nothing(void) {}
466 #endif
467
468 STATIC void GC_start_debugging_inner(void)
469 {
470   GC_ASSERT(I_HOLD_LOCK());
471 # ifndef SHORT_DBG_HDRS
472     GC_check_heap = GC_check_heap_proc;
473     GC_print_all_smashed = GC_print_all_smashed_proc;
474 # else
475     GC_check_heap = GC_do_nothing;
476     GC_print_all_smashed = GC_do_nothing;
477 # endif
478   GC_print_heap_obj = GC_debug_print_heap_obj_proc;
479   GC_debugging_started = TRUE;
480   GC_register_displacement_inner((word)sizeof(oh));
481 }
482
483 GC_INNER void GC_start_debugging(void)
484 {
485   DCL_LOCK_STATE;
486
487   LOCK();
488   GC_start_debugging_inner();
489   UNLOCK();
490 }
491
492 size_t GC_debug_header_size = sizeof(oh);
493
494 GC_API void GC_CALL GC_debug_register_displacement(size_t offset)
495 {
496   DCL_LOCK_STATE;
497
498   LOCK();
499   GC_register_displacement_inner(offset);
500   GC_register_displacement_inner((word)sizeof(oh) + offset);
501   UNLOCK();
502 }
503
504 #ifdef GC_ADD_CALLER
505 # if defined(HAVE_DLADDR) && defined(GC_HAVE_RETURN_ADDR_PARENT)
506 #   include <dlfcn.h>
507
508     STATIC void GC_caller_func_offset(word ad, const char **symp, int *offp)
509     {
510       Dl_info caller;
511
512       if (ad && dladdr((void *)ad, &caller) && caller.dli_sname != NULL) {
513         *symp = caller.dli_sname;
514         *offp = (int)((char *)ad - (char *)caller.dli_saddr);
515       }
516       if (NULL == *symp) {
517         *symp = "unknown";
518       }
519     }
520 # else
521 #   define GC_caller_func_offset(ad, symp, offp) (void)(*(symp) = "unknown")
522 # endif
523 #endif /* GC_ADD_CALLER */
524
525 GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc(size_t lb,
526                                                      GC_EXTRA_PARAMS)
527 {
528     void * result;
529
530     /* Note that according to malloc() specification, if size is 0 then */
531     /* malloc() returns either NULL, or a unique pointer value that can */
532     /* later be successfully passed to free(). We always do the latter. */
533     result = GC_malloc(SIZET_SAT_ADD(lb, DEBUG_BYTES));
534 #   ifdef GC_ADD_CALLER
535       if (s == NULL) {
536         GC_caller_func_offset(ra, &s, &i);
537       }
538 #   endif
539     if (result == 0) {
540         GC_err_printf("GC_debug_malloc(%lu) returning NULL (%s:%d)\n",
541                       (unsigned long)lb, s, i);
542         return(0);
543     }
544     if (!GC_debugging_started) {
545         GC_start_debugging();
546     }
547     ADD_CALL_CHAIN(result, ra);
548     return (GC_store_debug_info(result, (word)lb, s, i));
549 }
550
551 GC_API GC_ATTR_MALLOC void * GC_CALL
552     GC_debug_malloc_ignore_off_page(size_t lb, GC_EXTRA_PARAMS)
553 {
554     void * result = GC_malloc_ignore_off_page(SIZET_SAT_ADD(lb, DEBUG_BYTES));
555
556     if (result == 0) {
557         GC_err_printf("GC_debug_malloc_ignore_off_page(%lu)"
558                       " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
559         return(0);
560     }
561     if (!GC_debugging_started) {
562         GC_start_debugging();
563     }
564     ADD_CALL_CHAIN(result, ra);
565     return (GC_store_debug_info(result, (word)lb, s, i));
566 }
567
568 GC_API GC_ATTR_MALLOC void * GC_CALL
569     GC_debug_malloc_atomic_ignore_off_page(size_t lb, GC_EXTRA_PARAMS)
570 {
571     void * result = GC_malloc_atomic_ignore_off_page(
572                                 SIZET_SAT_ADD(lb, DEBUG_BYTES));
573
574     if (result == 0) {
575         GC_err_printf("GC_debug_malloc_atomic_ignore_off_page(%lu)"
576                       " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
577         return(0);
578     }
579     if (!GC_debugging_started) {
580         GC_start_debugging();
581     }
582     ADD_CALL_CHAIN(result, ra);
583     return (GC_store_debug_info(result, (word)lb, s, i));
584 }
585
586 STATIC void * GC_debug_generic_malloc(size_t lb, int knd, GC_EXTRA_PARAMS)
587 {
588     void * result = GC_generic_malloc(SIZET_SAT_ADD(lb, DEBUG_BYTES), knd);
589
590     if (NULL == result) {
591         GC_err_printf(
592                 "GC_debug_generic_malloc(%lu, %d) returning NULL (%s:%d)\n",
593                 (unsigned long)lb, knd, s, i);
594         return NULL;
595     }
596     if (!GC_debugging_started) {
597         GC_start_debugging();
598     }
599     ADD_CALL_CHAIN(result, ra);
600     return GC_store_debug_info(result, (word)lb, s, i);
601 }
602
603 #ifdef DBG_HDRS_ALL
604   /* An allocation function for internal use.  Normally internally      */
605   /* allocated objects do not have debug information.  But in this      */
606   /* case, we need to make sure that all objects have debug headers.    */
607   /* We assume debugging was started in collector initialization, and   */
608   /* we already hold the GC lock.                                       */
609   GC_INNER void * GC_debug_generic_malloc_inner(size_t lb, int k)
610   {
611     void * result = GC_generic_malloc_inner(
612                                 SIZET_SAT_ADD(lb, DEBUG_BYTES), k);
613
614     if (result == 0) {
615         GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n",
616                        (unsigned long) lb);
617         return(0);
618     }
619     if (!GC_debugging_started) {
620         GC_start_debugging_inner();
621     }
622     ADD_CALL_CHAIN(result, GC_RETURN_ADDR);
623     return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", 0));
624   }
625
626   GC_INNER void * GC_debug_generic_malloc_inner_ignore_off_page(size_t lb,
627                                                                 int k)
628   {
629     void * result = GC_generic_malloc_inner_ignore_off_page(
630                                 SIZET_SAT_ADD(lb, DEBUG_BYTES), k);
631
632     if (result == 0) {
633         GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n",
634                        (unsigned long) lb);
635         return(0);
636     }
637     if (!GC_debugging_started) {
638         GC_start_debugging_inner();
639     }
640     ADD_CALL_CHAIN(result, GC_RETURN_ADDR);
641     return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", 0));
642   }
643 #endif /* DBG_HDRS_ALL */
644
645 #ifdef STUBBORN_ALLOC
646   GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_stubborn(size_t lb,
647                                                         GC_EXTRA_PARAMS)
648   {
649     void * result = GC_malloc_stubborn(SIZET_SAT_ADD(lb, DEBUG_BYTES));
650
651     if (result == 0) {
652         GC_err_printf("GC_debug_malloc_stubborn(%lu)"
653                       " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
654         return(0);
655     }
656     if (!GC_debugging_started) {
657         GC_start_debugging();
658     }
659     ADD_CALL_CHAIN(result, ra);
660     return (GC_store_debug_info(result, (word)lb, s, i));
661   }
662
663   GC_API void GC_CALL GC_debug_change_stubborn(const void *p)
664   {
665     const void * q = GC_base_C(p);
666     hdr * hhdr;
667
668     if (q == 0) {
669         ABORT_ARG1("GC_debug_change_stubborn: bad arg", ": %p", p);
670     }
671     hhdr = HDR(q);
672     if (hhdr -> hb_obj_kind != STUBBORN) {
673         ABORT_ARG1("GC_debug_change_stubborn: arg not stubborn", ": %p", p);
674     }
675     GC_change_stubborn(q);
676   }
677
678   GC_API void GC_CALL GC_debug_end_stubborn_change(const void *p)
679   {
680     const void * q = GC_base_C(p);
681     hdr * hhdr;
682
683     if (q == 0) {
684         ABORT_ARG1("GC_debug_end_stubborn_change: bad arg", ": %p", p);
685     }
686     hhdr = HDR(q);
687     if (hhdr -> hb_obj_kind != STUBBORN) {
688         ABORT_ARG1("GC_debug_end_stubborn_change: arg not stubborn",
689                    ": %p", p);
690     }
691     GC_end_stubborn_change(q);
692   }
693
694 #else /* !STUBBORN_ALLOC */
695
696   GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_stubborn(size_t lb,
697                                                         GC_EXTRA_PARAMS)
698   {
699     return GC_debug_malloc(lb, OPT_RA s, i);
700   }
701
702   GC_API void GC_CALL GC_debug_change_stubborn(
703                                 const void * p GC_ATTR_UNUSED) {}
704
705   GC_API void GC_CALL GC_debug_end_stubborn_change(
706                                 const void * p GC_ATTR_UNUSED) {}
707 #endif /* !STUBBORN_ALLOC */
708
709 GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_atomic(size_t lb,
710                                                             GC_EXTRA_PARAMS)
711 {
712     void * result = GC_malloc_atomic(SIZET_SAT_ADD(lb, DEBUG_BYTES));
713
714     if (result == 0) {
715         GC_err_printf("GC_debug_malloc_atomic(%lu) returning NULL (%s:%d)\n",
716                       (unsigned long)lb, s, i);
717         return(0);
718     }
719     if (!GC_debugging_started) {
720         GC_start_debugging();
721     }
722     ADD_CALL_CHAIN(result, ra);
723     return (GC_store_debug_info(result, (word)lb, s, i));
724 }
725
726 GC_API GC_ATTR_MALLOC char * GC_CALL GC_debug_strdup(const char *str,
727                                                      GC_EXTRA_PARAMS)
728 {
729   char *copy;
730   size_t lb;
731   if (str == NULL) {
732     if (GC_find_leak)
733       GC_err_printf("strdup(NULL) behavior is undefined\n");
734     return NULL;
735   }
736
737   lb = strlen(str) + 1;
738   copy = GC_debug_malloc_atomic(lb, OPT_RA s, i);
739   if (copy == NULL) {
740 #   ifndef MSWINCE
741       errno = ENOMEM;
742 #   endif
743     return NULL;
744   }
745   BCOPY(str, copy, lb);
746   return copy;
747 }
748
749 GC_API GC_ATTR_MALLOC char * GC_CALL GC_debug_strndup(const char *str,
750                                                 size_t size, GC_EXTRA_PARAMS)
751 {
752   char *copy;
753   size_t len = strlen(str); /* str is expected to be non-NULL  */
754   if (len > size)
755     len = size;
756   copy = GC_debug_malloc_atomic(len + 1, OPT_RA s, i);
757   if (copy == NULL) {
758 #   ifndef MSWINCE
759       errno = ENOMEM;
760 #   endif
761     return NULL;
762   }
763   BCOPY(str, copy, len);
764   copy[len] = '\0';
765   return copy;
766 }
767
768 #ifdef GC_REQUIRE_WCSDUP
769 # include <wchar.h> /* for wcslen() */
770
771   GC_API GC_ATTR_MALLOC wchar_t * GC_CALL GC_debug_wcsdup(const wchar_t *str,
772                                                           GC_EXTRA_PARAMS)
773   {
774     size_t lb = (wcslen(str) + 1) * sizeof(wchar_t);
775     wchar_t *copy = GC_debug_malloc_atomic(lb, OPT_RA s, i);
776     if (copy == NULL) {
777 #     ifndef MSWINCE
778         errno = ENOMEM;
779 #     endif
780       return NULL;
781     }
782     BCOPY(str, copy, lb);
783     return copy;
784   }
785 #endif /* GC_REQUIRE_WCSDUP */
786
787 GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_uncollectable(size_t lb,
788                                                         GC_EXTRA_PARAMS)
789 {
790     void * result = GC_malloc_uncollectable(
791                                 SIZET_SAT_ADD(lb, UNCOLLECTABLE_DEBUG_BYTES));
792
793     if (result == 0) {
794         GC_err_printf("GC_debug_malloc_uncollectable(%lu)"
795                       " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
796         return(0);
797     }
798     if (!GC_debugging_started) {
799         GC_start_debugging();
800     }
801     ADD_CALL_CHAIN(result, ra);
802     return (GC_store_debug_info(result, (word)lb, s, i));
803 }
804
805 #ifdef GC_ATOMIC_UNCOLLECTABLE
806   GC_API GC_ATTR_MALLOC void * GC_CALL
807         GC_debug_malloc_atomic_uncollectable(size_t lb, GC_EXTRA_PARAMS)
808   {
809     void * result = GC_malloc_atomic_uncollectable(
810                                 SIZET_SAT_ADD(lb, UNCOLLECTABLE_DEBUG_BYTES));
811
812     if (result == 0) {
813         GC_err_printf("GC_debug_malloc_atomic_uncollectable(%lu)"
814                       " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
815         return(0);
816     }
817     if (!GC_debugging_started) {
818         GC_start_debugging();
819     }
820     ADD_CALL_CHAIN(result, ra);
821     return (GC_store_debug_info(result, (word)lb, s, i));
822   }
823 #endif /* GC_ATOMIC_UNCOLLECTABLE */
824
825 #ifndef GC_FREED_MEM_MARKER
826 # if CPP_WORDSZ == 32
827 #   define GC_FREED_MEM_MARKER 0xdeadbeef
828 # else
829 #   define GC_FREED_MEM_MARKER GC_WORD_C(0xEFBEADDEdeadbeef)
830 # endif
831 #endif
832
833 GC_API void GC_CALL GC_debug_free(void * p)
834 {
835     ptr_t base;
836     if (0 == p) return;
837
838     base = GC_base(p);
839     if (base == 0) {
840 #     if defined(REDIRECT_MALLOC) \
841          && ((defined(NEED_CALLINFO) && defined(GC_HAVE_BUILTIN_BACKTRACE)) \
842              || defined(GC_LINUX_THREADS) || defined(GC_SOLARIS_THREADS) \
843              || defined(MSWIN32))
844         /* In some cases, we should ignore objects that do not belong   */
845         /* to the GC heap.  See the comment in GC_free.                 */
846         if (!GC_is_heap_ptr(p)) return;
847 #     endif
848       ABORT_ARG1("Invalid pointer passed to free()", ": %p", p);
849     }
850     if ((ptr_t)p - (ptr_t)base != sizeof(oh)) {
851       GC_err_printf(
852                "GC_debug_free called on pointer %p w/o debugging info\n", p);
853     } else {
854 #     ifndef SHORT_DBG_HDRS
855         ptr_t clobbered = GC_check_annotated_obj((oh *)base);
856         word sz = GC_size(base);
857         if (clobbered != 0) {
858           GC_have_errors = TRUE;
859           if (((oh *)base) -> oh_sz == sz) {
860             GC_print_smashed_obj(
861                   "GC_debug_free: found previously deallocated (?) object at",
862                   p, clobbered);
863             return; /* ignore double free */
864           } else {
865             GC_print_smashed_obj("GC_debug_free: found smashed location at",
866                                  p, clobbered);
867           }
868         }
869         /* Invalidate size (mark the object as deallocated) */
870         ((oh *)base) -> oh_sz = sz;
871 #     endif /* SHORT_DBG_HDRS */
872     }
873     if (GC_find_leak
874 #       ifndef SHORT_DBG_HDRS
875           && ((ptr_t)p - (ptr_t)base != sizeof(oh) || !GC_findleak_delay_free)
876 #       endif
877         ) {
878       GC_free(base);
879     } else {
880       hdr * hhdr = HDR(p);
881       if (hhdr -> hb_obj_kind == UNCOLLECTABLE
882 #         ifdef GC_ATOMIC_UNCOLLECTABLE
883             || hhdr -> hb_obj_kind == AUNCOLLECTABLE
884 #         endif
885           ) {
886         GC_free(base);
887       } else {
888         size_t i;
889         size_t obj_sz = BYTES_TO_WORDS(hhdr -> hb_sz - sizeof(oh));
890
891         for (i = 0; i < obj_sz; ++i)
892           ((word *)p)[i] = GC_FREED_MEM_MARKER;
893         GC_ASSERT((word *)p + i == (word *)(base + hhdr -> hb_sz));
894       }
895     } /* !GC_find_leak */
896 }
897
898 #if defined(THREADS) && defined(DBG_HDRS_ALL)
899   /* Used internally; we assume it's called correctly.    */
900   GC_INNER void GC_debug_free_inner(void * p)
901   {
902     ptr_t base = GC_base(p);
903     GC_ASSERT((ptr_t)p - (ptr_t)base == sizeof(oh));
904 #   ifdef LINT2
905       if (!base) ABORT("Invalid GC_debug_free_inner argument");
906 #   endif
907 #   ifndef SHORT_DBG_HDRS
908       /* Invalidate size */
909       ((oh *)base) -> oh_sz = GC_size(base);
910 #   endif
911     GC_free_inner(base);
912   }
913 #endif
914
915 GC_API void * GC_CALL GC_debug_realloc(void * p, size_t lb, GC_EXTRA_PARAMS)
916 {
917     void * base;
918     void * result;
919     hdr * hhdr;
920
921     if (p == 0) {
922       return GC_debug_malloc(lb, OPT_RA s, i);
923     }
924     if (0 == lb) /* and p != NULL */ {
925       GC_debug_free(p);
926       return NULL;
927     }
928
929 #   ifdef GC_ADD_CALLER
930       if (s == NULL) {
931         GC_caller_func_offset(ra, &s, &i);
932       }
933 #   endif
934     base = GC_base(p);
935     if (base == 0) {
936         ABORT_ARG1("Invalid pointer passed to realloc()", ": %p", p);
937     }
938     if ((ptr_t)p - (ptr_t)base != sizeof(oh)) {
939         GC_err_printf(
940               "GC_debug_realloc called on pointer %p w/o debugging info\n", p);
941         return(GC_realloc(p, lb));
942     }
943     hhdr = HDR(base);
944     switch (hhdr -> hb_obj_kind) {
945 #    ifdef STUBBORN_ALLOC
946       case STUBBORN:
947         result = GC_debug_malloc_stubborn(lb, OPT_RA s, i);
948         break;
949 #    endif
950       case NORMAL:
951         result = GC_debug_malloc(lb, OPT_RA s, i);
952         break;
953       case PTRFREE:
954         result = GC_debug_malloc_atomic(lb, OPT_RA s, i);
955         break;
956       case UNCOLLECTABLE:
957         result = GC_debug_malloc_uncollectable(lb, OPT_RA s, i);
958         break;
959 #    ifdef GC_ATOMIC_UNCOLLECTABLE
960       case AUNCOLLECTABLE:
961         result = GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i);
962         break;
963 #    endif
964       default:
965         result = NULL; /* initialized to prevent warning. */
966         ABORT_RET("GC_debug_realloc: encountered bad kind");
967     }
968
969     if (result != NULL) {
970       size_t old_sz;
971 #     ifdef SHORT_DBG_HDRS
972         old_sz = GC_size(base) - sizeof(oh);
973 #     else
974         old_sz = ((oh *)base) -> oh_sz;
975 #     endif
976       BCOPY(p, result, old_sz < lb ? old_sz : lb);
977       GC_debug_free(p);
978     }
979     return(result);
980 }
981
982 GC_API GC_ATTR_MALLOC void * GC_CALL
983     GC_debug_generic_or_special_malloc(size_t lb, int knd, GC_EXTRA_PARAMS)
984 {
985     switch (knd) {
986 #     ifdef STUBBORN_ALLOC
987         case STUBBORN:
988             return GC_debug_malloc_stubborn(lb, OPT_RA s, i);
989 #     endif
990         case PTRFREE:
991             return GC_debug_malloc_atomic(lb, OPT_RA s, i);
992         case NORMAL:
993             return GC_debug_malloc(lb, OPT_RA s, i);
994         case UNCOLLECTABLE:
995             return GC_debug_malloc_uncollectable(lb, OPT_RA s, i);
996 #     ifdef GC_ATOMIC_UNCOLLECTABLE
997         case AUNCOLLECTABLE:
998             return GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i);
999 #     endif
1000         default:
1001             return GC_debug_generic_malloc(lb, knd, OPT_RA s, i);
1002     }
1003 }
1004
1005 #ifndef SHORT_DBG_HDRS
1006
1007 /* List of smashed (clobbered) locations.  We defer printing these,     */
1008 /* since we can't always print them nicely with the allocation lock     */
1009 /* held.  We put them here instead of in GC_arrays, since it may be     */
1010 /* useful to be able to look at them with the debugger.                 */
1011 #ifndef MAX_SMASHED
1012 # define MAX_SMASHED 20
1013 #endif
1014 STATIC ptr_t GC_smashed[MAX_SMASHED] = {0};
1015 STATIC unsigned GC_n_smashed = 0;
1016
1017 STATIC void GC_add_smashed(ptr_t smashed)
1018 {
1019     GC_ASSERT(GC_is_marked(GC_base(smashed)));
1020     /* FIXME: Prevent adding an object while printing smashed list.     */
1021     GC_smashed[GC_n_smashed] = smashed;
1022     if (GC_n_smashed < MAX_SMASHED - 1) ++GC_n_smashed;
1023       /* In case of overflow, we keep the first MAX_SMASHED-1   */
1024       /* entries plus the last one.                             */
1025     GC_have_errors = TRUE;
1026 }
1027
1028 /* Print all objects on the list.  Clear the list.      */
1029 STATIC void GC_print_all_smashed_proc(void)
1030 {
1031     unsigned i;
1032
1033     GC_ASSERT(I_DONT_HOLD_LOCK());
1034     if (GC_n_smashed == 0) return;
1035     GC_err_printf("GC_check_heap_block: found %u smashed heap objects:\n",
1036                   GC_n_smashed);
1037     for (i = 0; i < GC_n_smashed; ++i) {
1038         ptr_t base = (ptr_t)GC_base(GC_smashed[i]);
1039
1040 #       ifdef LINT2
1041           if (!base) ABORT("Invalid GC_smashed element");
1042 #       endif
1043         GC_print_smashed_obj("", base + sizeof(oh), GC_smashed[i]);
1044         GC_smashed[i] = 0;
1045     }
1046     GC_n_smashed = 0;
1047 }
1048
1049 /* Check all marked objects in the given block for validity     */
1050 /* Avoid GC_apply_to_each_object for performance reasons.       */
1051 STATIC void GC_check_heap_block(struct hblk *hbp, word dummy GC_ATTR_UNUSED)
1052 {
1053     struct hblkhdr * hhdr = HDR(hbp);
1054     size_t sz = hhdr -> hb_sz;
1055     size_t bit_no;
1056     char *p, *plim;
1057
1058     p = hbp->hb_body;
1059     if (sz > MAXOBJBYTES) {
1060       plim = p;
1061     } else {
1062       plim = hbp->hb_body + HBLKSIZE - sz;
1063     }
1064     /* go through all words in block */
1065     for (bit_no = 0; (word)p <= (word)plim;
1066          bit_no += MARK_BIT_OFFSET(sz), p += sz) {
1067       if (mark_bit_from_hdr(hhdr, bit_no) && GC_HAS_DEBUG_INFO((ptr_t)p)) {
1068         ptr_t clobbered = GC_check_annotated_obj((oh *)p);
1069         if (clobbered != 0)
1070           GC_add_smashed(clobbered);
1071       }
1072     }
1073 }
1074
1075 /* This assumes that all accessible objects are marked, and that        */
1076 /* I hold the allocation lock.  Normally called by collector.           */
1077 STATIC void GC_check_heap_proc(void)
1078 {
1079   GC_STATIC_ASSERT((sizeof(oh) & (GRANULE_BYTES - 1)) == 0);
1080   /* FIXME: Should we check for twice that alignment?   */
1081   GC_apply_to_all_blocks(GC_check_heap_block, 0);
1082 }
1083
1084 GC_INNER GC_bool GC_check_leaked(ptr_t base)
1085 {
1086   size_t i;
1087   size_t obj_sz;
1088   word *p;
1089
1090   if (
1091 #     if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
1092         (*(word *)base & 1) != 0 &&
1093 #     endif
1094       GC_has_other_debug_info(base) >= 0)
1095     return TRUE; /* object has leaked */
1096
1097   /* Validate freed object's content. */
1098   p = (word *)(base + sizeof(oh));
1099   obj_sz = BYTES_TO_WORDS(HDR(base)->hb_sz - sizeof(oh));
1100   for (i = 0; i < obj_sz; ++i)
1101     if (p[i] != GC_FREED_MEM_MARKER) {
1102         GC_set_mark_bit(base); /* do not reclaim it in this cycle */
1103         GC_add_smashed((ptr_t)(&p[i])); /* alter-after-free detected */
1104         break; /* don't report any other smashed locations in the object */
1105     }
1106
1107   return FALSE; /* GC_debug_free() has been called */
1108 }
1109
1110 #endif /* !SHORT_DBG_HDRS */
1111
1112 #ifndef GC_NO_FINALIZATION
1113
1114 struct closure {
1115     GC_finalization_proc cl_fn;
1116     void * cl_data;
1117 };
1118
1119 STATIC void * GC_make_closure(GC_finalization_proc fn, void * data)
1120 {
1121     struct closure * result =
1122 #   ifdef DBG_HDRS_ALL
1123       (struct closure *) GC_debug_malloc(sizeof (struct closure),
1124                                          GC_EXTRAS);
1125 #   else
1126       (struct closure *) GC_malloc(sizeof (struct closure));
1127 #   endif
1128     if (result != 0) {
1129       result -> cl_fn = fn;
1130       result -> cl_data = data;
1131     }
1132     return((void *)result);
1133 }
1134
1135 /* An auxiliary fns to make finalization work correctly with displaced  */
1136 /* pointers introduced by the debugging allocators.                     */
1137 STATIC void GC_CALLBACK GC_debug_invoke_finalizer(void * obj, void * data)
1138 {
1139     struct closure * cl = (struct closure *) data;
1140     (*(cl -> cl_fn))((void *)((char *)obj + sizeof(oh)), cl -> cl_data);
1141 }
1142
1143 /* Special finalizer_proc value to detect GC_register_finalizer() failure. */
1144 #define OFN_UNSET ((GC_finalization_proc)~(signed_word)0)
1145
1146 /* Set ofn and ocd to reflect the values we got back.   */
1147 static void store_old(void *obj, GC_finalization_proc my_old_fn,
1148                       struct closure *my_old_cd, GC_finalization_proc *ofn,
1149                       void **ocd)
1150 {
1151     if (0 != my_old_fn) {
1152       if (my_old_fn == OFN_UNSET) {
1153         /* register_finalizer() failed; (*ofn) and (*ocd) are unchanged. */
1154         return;
1155       }
1156       if (my_old_fn != GC_debug_invoke_finalizer) {
1157         GC_err_printf("Debuggable object at %p had a non-debug finalizer\n",
1158                       obj);
1159         /* This should probably be fatal. */
1160       } else {
1161         if (ofn) *ofn = my_old_cd -> cl_fn;
1162         if (ocd) *ocd = my_old_cd -> cl_data;
1163       }
1164     } else {
1165       if (ofn) *ofn = 0;
1166       if (ocd) *ocd = 0;
1167     }
1168 }
1169
1170 GC_API void GC_CALL GC_debug_register_finalizer(void * obj,
1171                                         GC_finalization_proc fn,
1172                                         void * cd, GC_finalization_proc *ofn,
1173                                         void * *ocd)
1174 {
1175     GC_finalization_proc my_old_fn = OFN_UNSET;
1176     void * my_old_cd;
1177     ptr_t base = GC_base(obj);
1178     if (0 == base) {
1179         /* We won't collect it, hence finalizer wouldn't be run. */
1180         if (ocd) *ocd = 0;
1181         if (ofn) *ofn = 0;
1182         return;
1183     }
1184     if ((ptr_t)obj - base != sizeof(oh)) {
1185         GC_err_printf("GC_debug_register_finalizer called with"
1186                       " non-base-pointer %p\n", obj);
1187     }
1188     if (0 == fn) {
1189       GC_register_finalizer(base, 0, 0, &my_old_fn, &my_old_cd);
1190     } else {
1191       cd = GC_make_closure(fn, cd);
1192       if (cd == 0) return; /* out of memory */
1193       GC_register_finalizer(base, GC_debug_invoke_finalizer,
1194                             cd, &my_old_fn, &my_old_cd);
1195     }
1196     store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
1197 }
1198
1199 GC_API void GC_CALL GC_debug_register_finalizer_no_order
1200                                     (void * obj, GC_finalization_proc fn,
1201                                      void * cd, GC_finalization_proc *ofn,
1202                                      void * *ocd)
1203 {
1204     GC_finalization_proc my_old_fn = OFN_UNSET;
1205     void * my_old_cd;
1206     ptr_t base = GC_base(obj);
1207     if (0 == base) {
1208         /* We won't collect it, hence finalizer wouldn't be run. */
1209         if (ocd) *ocd = 0;
1210         if (ofn) *ofn = 0;
1211         return;
1212     }
1213     if ((ptr_t)obj - base != sizeof(oh)) {
1214         GC_err_printf("GC_debug_register_finalizer_no_order called with"
1215                       " non-base-pointer %p\n", obj);
1216     }
1217     if (0 == fn) {
1218       GC_register_finalizer_no_order(base, 0, 0, &my_old_fn, &my_old_cd);
1219     } else {
1220       cd = GC_make_closure(fn, cd);
1221       if (cd == 0) return; /* out of memory */
1222       GC_register_finalizer_no_order(base, GC_debug_invoke_finalizer,
1223                                      cd, &my_old_fn, &my_old_cd);
1224     }
1225     store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
1226 }
1227
1228 GC_API void GC_CALL GC_debug_register_finalizer_unreachable
1229                                     (void * obj, GC_finalization_proc fn,
1230                                      void * cd, GC_finalization_proc *ofn,
1231                                      void * *ocd)
1232 {
1233     GC_finalization_proc my_old_fn = OFN_UNSET;
1234     void * my_old_cd;
1235     ptr_t base = GC_base(obj);
1236     if (0 == base) {
1237         /* We won't collect it, hence finalizer wouldn't be run. */
1238         if (ocd) *ocd = 0;
1239         if (ofn) *ofn = 0;
1240         return;
1241     }
1242     if ((ptr_t)obj - base != sizeof(oh)) {
1243         GC_err_printf("GC_debug_register_finalizer_unreachable called with"
1244                       " non-base-pointer %p\n", obj);
1245     }
1246     if (0 == fn) {
1247       GC_register_finalizer_unreachable(base, 0, 0, &my_old_fn, &my_old_cd);
1248     } else {
1249       cd = GC_make_closure(fn, cd);
1250       if (cd == 0) return; /* out of memory */
1251       GC_register_finalizer_unreachable(base, GC_debug_invoke_finalizer,
1252                                         cd, &my_old_fn, &my_old_cd);
1253     }
1254     store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
1255 }
1256
1257 GC_API void GC_CALL GC_debug_register_finalizer_ignore_self
1258                                     (void * obj, GC_finalization_proc fn,
1259                                      void * cd, GC_finalization_proc *ofn,
1260                                      void * *ocd)
1261 {
1262     GC_finalization_proc my_old_fn = OFN_UNSET;
1263     void * my_old_cd;
1264     ptr_t base = GC_base(obj);
1265     if (0 == base) {
1266         /* We won't collect it, hence finalizer wouldn't be run. */
1267         if (ocd) *ocd = 0;
1268         if (ofn) *ofn = 0;
1269         return;
1270     }
1271     if ((ptr_t)obj - base != sizeof(oh)) {
1272         GC_err_printf("GC_debug_register_finalizer_ignore_self called with"
1273                       " non-base-pointer %p\n", obj);
1274     }
1275     if (0 == fn) {
1276       GC_register_finalizer_ignore_self(base, 0, 0, &my_old_fn, &my_old_cd);
1277     } else {
1278       cd = GC_make_closure(fn, cd);
1279       if (cd == 0) return; /* out of memory */
1280       GC_register_finalizer_ignore_self(base, GC_debug_invoke_finalizer,
1281                                         cd, &my_old_fn, &my_old_cd);
1282     }
1283     store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
1284 }
1285
1286 #endif /* !GC_NO_FINALIZATION */
1287
1288 GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_replacement(size_t lb)
1289 {
1290     return GC_debug_malloc(lb, GC_DBG_EXTRAS);
1291 }
1292
1293 GC_API void * GC_CALL GC_debug_realloc_replacement(void *p, size_t lb)
1294 {
1295     return GC_debug_realloc(p, lb, GC_DBG_EXTRAS);
1296 }