aboutsummaryrefslogtreecommitdiffstats
path: root/gc/os_dep.c
diff options
context:
space:
mode:
Diffstat (limited to 'gc/os_dep.c')
-rw-r--r--gc/os_dep.c458
1 files changed, 378 insertions, 80 deletions
diff --git a/gc/os_dep.c b/gc/os_dep.c
index cfbd837..8f677b1 100644
--- a/gc/os_dep.c
+++ b/gc/os_dep.c
@@ -84,7 +84,7 @@
# include <setjmp.h>
#endif
-#ifdef FREEBSD
+#if defined(FREEBSD) && defined(I386)
# include <machine/trap.h>
#endif
@@ -155,11 +155,11 @@
# ifdef LINUX
# pragma weak __data_start
- extern int __data_start;
+ extern int __data_start[];
# pragma weak data_start
- extern int data_start;
+ extern int data_start[];
# endif /* LINUX */
- extern int _end;
+ extern int _end[];
ptr_t GC_data_start;
@@ -169,16 +169,16 @@
# ifdef LINUX
/* Try the easy approaches first: */
- if (&__data_start != 0) {
- GC_data_start = (ptr_t)(&__data_start);
+ if ((ptr_t)__data_start != 0) {
+ GC_data_start = (ptr_t)(__data_start);
return;
}
- if (&data_start != 0) {
- GC_data_start = (ptr_t)(&data_start);
+ if ((ptr_t)data_start != 0) {
+ GC_data_start = (ptr_t)(data_start);
return;
}
# endif /* LINUX */
- GC_data_start = GC_find_limit((ptr_t)(&_end), FALSE);
+ GC_data_start = GC_find_limit((ptr_t)(_end), FALSE);
}
#endif
@@ -333,7 +333,8 @@ void GC_enable_signals(void)
# if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \
&& !defined(MSWINCE) \
- && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW)
+ && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW) \
+ && !defined(NOSYS) && !defined(ECOS)
# if defined(sigmask) && !defined(UTS4) && !defined(HURD)
/* Use the traditional BSD interface */
@@ -516,7 +517,7 @@ ptr_t GC_get_stack_base()
# undef GC_AMIGA_SB
# endif /* AMIGA */
-# if defined(NEED_FIND_LIMIT) || (defined(UNIX_LIKE) && !defined(ECOS))
+# if defined(NEED_FIND_LIMIT) || defined(UNIX_LIKE)
# ifdef __STDC__
typedef void (*handler)(int);
@@ -649,6 +650,13 @@ ptr_t GC_get_stack_base()
}
# endif
+#if defined(ECOS) || defined(NOSYS)
+ ptr_t GC_get_stack_base()
+ {
+ return STACKBOTTOM;
+ }
+#endif
+
#ifdef LINUX_STACKBOTTOM
#include <sys/types.h>
@@ -701,7 +709,16 @@ ptr_t GC_get_stack_base()
/* First try the easy way. This should work for glibc 2.2 */
if (0 != &__libc_stack_end) {
- return __libc_stack_end;
+# ifdef IA64
+ /* Some versions of glibc set the address 16 bytes too */
+ /* low while the initialization code is running. */
+ if (((word)__libc_stack_end & 0xfff) + 0x10 < 0x1000) {
+ return __libc_stack_end + 0x10;
+ } /* Otherwise it's not safe to add 16 bytes and we fall */
+ /* back to using /proc. */
+# else
+ return __libc_stack_end;
+# endif
}
f = open("/proc/self/stat", O_RDONLY);
if (f < 0 || STAT_READ(f, stat_buf, STAT_BUF_SIZE) < 2 * STAT_SKIP) {
@@ -738,20 +755,20 @@ ptr_t GC_get_stack_base()
ptr_t GC_freebsd_stack_base(void)
{
- int nm[2] = { CTL_KERN, KERN_USRSTACK}, base, len, r;
-
- len = sizeof(int);
- r = sysctl(nm, 2, &base, &len, NULL, 0);
+ int nm[2] = {CTL_KERN, KERN_USRSTACK};
+ ptr_t base;
+ size_t len = sizeof(ptr_t);
+ int r = sysctl(nm, 2, &base, &len, NULL, 0);
if (r) ABORT("Error getting stack base");
- return (ptr_t)base;
+ return base;
}
#endif /* FREEBSD_STACKBOTTOM */
#if !defined(BEOS) && !defined(AMIGA) && !defined(MSWIN32) \
- && !defined(MSWINCE) && !defined(OS2) && !defined(ECOS)
+ && !defined(MSWINCE) && !defined(OS2)
ptr_t GC_get_stack_base()
{
@@ -914,23 +931,21 @@ void GC_register_data_segments()
/* Unfortunately, we have to handle win32s very differently from NT, */
/* Since VirtualQuery has very different semantics. In particular, */
/* under win32s a VirtualQuery call on an unmapped page returns an */
- /* invalid result. Under GC_register_data_segments is a noop and */
+ /* invalid result. Under NT, GC_register_data_segments is a noop and */
/* all real work is done by GC_register_dynamic_libraries. Under */
/* win32s, we cannot find the data segments associated with dll's. */
/* We rgister the main data segment here. */
- GC_bool GC_win32s = FALSE; /* We're running under win32s. */
-
- GC_bool GC_is_win32s()
- {
- DWORD v = GetVersion();
-
- /* Check that this is not NT, and Windows major version <= 3 */
- return ((v & 0x80000000) && (v & 0xff) <= 3);
- }
+# ifdef __GCC__
+ GC_bool GC_no_win32_dlls = TRUE; /* GCC can't do SEH, so we can't use VirtualQuery */
+# else
+ GC_bool GC_no_win32_dlls = FALSE;
+# endif
void GC_init_win32()
{
- GC_win32s = GC_is_win32s();
+ /* if we're running under win32s, assume that no DLLs will be loaded */
+ DWORD v = GetVersion();
+ GC_no_win32_dlls |= ((v & 0x80000000) && (v & 0xff) <= 3);
}
/* Return the smallest address a such that VirtualQuery */
@@ -998,7 +1013,7 @@ void GC_register_data_segments()
char * base;
char * limit, * new_limit;
- if (!GC_win32s) return;
+ if (!GC_no_win32_dlls) return;
p = base = limit = GC_least_described_address(static_root);
while (p < GC_sysinfo.lpMaximumApplicationAddress) {
result = VirtualQuery(p, &buf, sizeof(buf));
@@ -1297,7 +1312,7 @@ SYSTEM_INFO GC_sysinfo;
# ifdef USE_GLOBAL_ALLOC
# define GLOBAL_ALLOC_TEST 1
# else
-# define GLOBAL_ALLOC_TEST GC_win32s
+# define GLOBAL_ALLOC_TEST GC_no_win32_dlls
# endif
word GC_n_heap_bases = 0;
@@ -1314,7 +1329,15 @@ word bytes;
result = (ptr_t) GlobalAlloc(0, bytes + HBLKSIZE);
result = (ptr_t)(((word)result + HBLKSIZE) & ~(HBLKSIZE-1));
} else {
- result = (ptr_t) VirtualAlloc(NULL, bytes,
+ /* VirtualProtect only works on regions returned by a */
+ /* single VirtualAlloc call. Thus we allocate one */
+ /* extra page, which will prevent merging of blocks */
+ /* in separate regions, and eliminate any temptation */
+ /* to call VirtualProtect on a range spanning regions. */
+ /* This wastes a small amount of memory, and risks */
+ /* increased fragmentation. But better alternatives */
+ /* would require effort. */
+ result = (ptr_t) VirtualAlloc(NULL, bytes + 1,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
}
@@ -1328,7 +1351,7 @@ word bytes;
void GC_win32_free_heap ()
{
- if (GC_win32s) {
+ if (GC_no_win32_dlls) {
while (GC_n_heap_bases > 0) {
GlobalFree (GC_heap_bases[--GC_n_heap_bases]);
GC_heap_bases[GC_n_heap_bases] = 0;
@@ -1370,6 +1393,10 @@ word bytes;
/* Reserve more pages */
word res_bytes = (bytes + GC_sysinfo.dwAllocationGranularity-1)
& ~(GC_sysinfo.dwAllocationGranularity-1);
+ /* If we ever support MPROTECT_VDB here, we will probably need to */
+ /* ensure that res_bytes is strictly > bytes, so that VirtualProtect */
+ /* never spans regions. It seems to be OK for a VirtualFree argument */
+ /* to span regions, so we should be OK for now. */
result = (ptr_t) VirtualAlloc(NULL, res_bytes,
MEM_RESERVE | MEM_TOP_DOWN,
PAGE_EXECUTE_READWRITE);
@@ -1741,11 +1768,18 @@ word n;
{
}
-/* A call hints that h is about to be written. */
-/* May speed up some dirty bit implementations. */
+/* A call that: */
+/* I) hints that [h, h+nblocks) is about to be written. */
+/* II) guarantees that protection is removed. */
+/* (I) may speed up some dirty bit implementations. */
+/* (II) may be essential if we need to ensure that */
+/* pointer-free system call buffers in the heap are */
+/* not protected. */
/*ARGSUSED*/
-void GC_write_hint(h)
+void GC_remove_protection(h, nblocks, is_ptrfree)
struct hblk *h;
+word nblocks;
+GC_bool is_ptrfree;
{
}
@@ -1761,14 +1795,18 @@ struct hblk *h;
/*
* This implementation maintains dirty bits itself by catching write
* faults and keeping track of them. We assume nobody else catches
- * SIGBUS or SIGSEGV. We assume no write faults occur in system calls
- * except as a result of a read system call. This means clients must
- * either ensure that system calls do not touch the heap, or must
- * provide their own wrappers analogous to the one for read.
+ * SIGBUS or SIGSEGV. We assume no write faults occur in system calls.
+ * This means that clients must ensure that system calls don't write
+ * to the write-protected heap. Probably the best way to do this is to
+ * ensure that system calls write at most to POINTERFREE objects in the
+ * heap, and do even that only if we are on a platform on which those
+ * are not protected. Another alternative is to wrap system calls
+ * (see example for read below), but the current implementation holds
+ * a lock across blocking calls, making it problematic for multithreaded
+ * applications.
* We assume the page size is a multiple of HBLKSIZE.
- * This implementation is currently SunOS 4.X and IRIX 5.X specific, though we
- * tried to use portable code where easily possible. It is known
- * not to work under a number of other systems.
+ * We prefer them to be the same. We avoid protecting POINTERFREE
+ * objects only if they are the same.
*/
# if !defined(MSWIN32) && !defined(MSWINCE)
@@ -1848,12 +1886,16 @@ struct hblk *h;
# endif
#endif
#if defined(LINUX)
-# include <linux/version.h>
-# if (LINUX_VERSION_CODE >= 0x20100) && !defined(M68K) || defined(ALPHA) || defined(IA64)
+# if __GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2
typedef struct sigcontext s_c;
-# else
- typedef struct sigcontext_struct s_c;
-# endif
+# else /* glibc < 2.2 */
+# include <linux/version.h>
+# if (LINUX_VERSION_CODE >= 0x20100) && !defined(M68K) || defined(ALPHA) || defined(ARM32)
+ typedef struct sigcontext s_c;
+# else
+ typedef struct sigcontext_struct s_c;
+# endif
+# endif /* glibc < 2.2 */
# if defined(ALPHA) || defined(M68K)
typedef void (* REAL_SIG_PF)(int, int, s_c *);
# else
@@ -2096,7 +2138,11 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */
# if defined(IA64) || defined(HP_PA)
void GC_write_fault_handler(int sig, siginfo_t * si, s_c * scp)
# else
- void GC_write_fault_handler(int sig, s_c sc)
+# if defined(ARM32)
+ void GC_write_fault_handler(int sig, int a2, int a3, int a4, s_c sc)
+# else
+ void GC_write_fault_handler(int sig, s_c sc)
+# endif
# endif
# endif
# define SIG_OK (sig == SIGSEGV)
@@ -2199,7 +2245,11 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */
# if defined(POWERPC)
char * addr = (char *) (sc.regs->dar);
# else
- --> architecture not supported
+# if defined(ARM32)
+ char * addr = (char *)sc.fault_address;
+# else
+ --> architecture not supported
+# endif
# endif
# endif
# endif
@@ -2319,29 +2369,33 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */
/*
* We hold the allocation lock. We expect block h to be written
- * shortly.
+ * shortly. Ensure that all pages containing any part of the n hblks
+ * starting at h are no longer protected. If is_ptrfree is false,
+ * also ensure that they will subsequently appear to be dirty.
*/
-void GC_write_hint(h)
+void GC_remove_protection(h, nblocks, is_ptrfree)
struct hblk *h;
+word nblocks;
+GC_bool is_ptrfree;
{
- register struct hblk * h_trunc;
- register unsigned i;
- register GC_bool found_clean;
+ struct hblk * h_trunc; /* Truncated to page boundary */
+ struct hblk * h_end; /* Page boundary following block end */
+ struct hblk * current;
+ GC_bool found_clean;
if (!GC_dirty_maintained) return;
h_trunc = (struct hblk *)((word)h & ~(GC_page_size-1));
+ h_end = (struct hblk *)(((word)(h + nblocks) + GC_page_size-1)
+ & ~(GC_page_size-1));
found_clean = FALSE;
- for (i = 0; i < divHBLKSZ(GC_page_size); i++) {
- register int index = PHT_HASH(h_trunc+i);
+ for (current = h_trunc; current < h_end; ++current) {
+ int index = PHT_HASH(current);
- if (!get_pht_entry_from_index(GC_dirty_pages, index)) {
- found_clean = TRUE;
+ if (!is_ptrfree || current < h || current >= h + nblocks) {
async_set_pht_entry_from_index(GC_dirty_pages, index);
}
}
- if (found_clean) {
- UNPROTECT(h_trunc, GC_page_size);
- }
+ UNPROTECT(h_trunc, (ptr_t)h_end - (ptr_t)h_trunc);
}
void GC_dirty_init()
@@ -2414,7 +2468,7 @@ void GC_dirty_init()
# else
sigaction(SIGSEGV, &act, &oldact);
# endif
-# if defined(_sigargs) || defined(HURD)
+# if defined(_sigargs) || defined(HURD) || !defined(SA_SIGINFO)
/* This is Irix 5.x, not 6.x. Irix 5.x does not have */
/* sa_sigaction. */
GC_old_segv_handler = oldact.sa_handler;
@@ -2460,18 +2514,77 @@ void GC_dirty_init()
# endif
}
+int GC_incremental_protection_needs()
+{
+ if (GC_page_size == HBLKSIZE) {
+ return GC_PROTECTS_POINTER_HEAP;
+ } else {
+ return GC_PROTECTS_POINTER_HEAP | GC_PROTECTS_PTRFREE_HEAP;
+ }
+}
+#define HAVE_INCREMENTAL_PROTECTION_NEEDS
+#define IS_PTRFREE(hhdr) ((hhdr)->hb_descr == 0)
+
+#define PAGE_ALIGNED(x) !((word)(x) & (GC_page_size - 1))
void GC_protect_heap()
{
ptr_t start;
word len;
+ struct hblk * current;
+ struct hblk * current_start; /* Start of block to be protected. */
+ struct hblk * limit;
unsigned i;
-
+ GC_bool protect_all =
+ (0 != (GC_incremental_protection_needs() & GC_PROTECTS_PTRFREE_HEAP));
for (i = 0; i < GC_n_heap_sects; i++) {
start = GC_heap_sects[i].hs_start;
len = GC_heap_sects[i].hs_bytes;
- PROTECT(start, len);
+ if (protect_all) {
+ PROTECT(start, len);
+ } else {
+ GC_ASSERT(PAGE_ALIGNED(len))
+ GC_ASSERT(PAGE_ALIGNED(start))
+ current_start = current = (struct hblk *)start;
+ limit = (struct hblk *)(start + len);
+ while (current < limit) {
+ hdr * hhdr;
+ word nhblks;
+ GC_bool is_ptrfree;
+
+ GC_ASSERT(PAGE_ALIGNED(current));
+ GET_HDR(current, hhdr);
+ if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
+ /* This can happen only if we're at the beginning of a */
+ /* heap segment, and a block spans heap segments. */
+ /* We will handle that block as part of the preceding */
+ /* segment. */
+ GC_ASSERT(current_start == current);
+ current_start = ++current;
+ continue;
+ }
+ if (HBLK_IS_FREE(hhdr)) {
+ GC_ASSERT(PAGE_ALIGNED(hhdr -> hb_sz));
+ nhblks = divHBLKSZ(hhdr -> hb_sz);
+ is_ptrfree = TRUE; /* dirty on alloc */
+ } else {
+ nhblks = OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz);
+ is_ptrfree = IS_PTRFREE(hhdr);
+ }
+ if (is_ptrfree) {
+ if (current_start < current) {
+ PROTECT(current_start, (ptr_t)current - (ptr_t)current_start);
+ }
+ current_start = (current += nhblks);
+ } else {
+ current += nhblks;
+ }
+ }
+ if (current_start < current) {
+ PROTECT(current_start, (ptr_t)current - (ptr_t)current_start);
+ }
+ }
}
}
@@ -2528,7 +2641,7 @@ word len;
register struct hblk *h;
ptr_t obj_start;
- if (!GC_incremental) return;
+ if (!GC_dirty_maintained) return;
obj_start = GC_base(addr);
if (obj_start == 0) return;
if (GC_base(addr + len - 1) != obj_start) {
@@ -2546,15 +2659,23 @@ word len;
((ptr_t)end_block - (ptr_t)start_block) + HBLKSIZE);
}
-#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(THREADS) \
- && !defined(GC_USE_LD_WRAP)
-/* Replacement for UNIX system call. */
-/* Other calls that write to the heap should be handled similarly. */
-/* Note that this doesn't work well for blocking reads: It will hold */
-/* tha allocation lock for the entur duration of the call. Multithreaded */
-/* clients should really ensure that it won't block, either by setting */
-/* the descriptor nonblocking, or by calling select or poll first, to */
-/* make sure that input is available. */
+#if 0
+
+/* We no longer wrap read by default, since that was causing too many */
+/* problems. It is preferred that the client instead avoids writing */
+/* to the write-protected heap with a system call. */
+/* This still serves as sample code if you do want to wrap system calls.*/
+
+#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(GC_USE_LD_WRAP)
+/* Replacement for UNIX system call. */
+/* Other calls that write to the heap should be handled similarly. */
+/* Note that this doesn't work well for blocking reads: It will hold */
+/* the allocation lock for the entire duration of the call. Multithreaded */
+/* clients should really ensure that it won't block, either by setting */
+/* the descriptor nonblocking, or by calling select or poll first, to */
+/* make sure that input is available. */
+/* Another, preferred alternative is to ensure that system calls never */
+/* write to the protected heap (see above). */
# if defined(__STDC__) && !defined(SUNOS4)
# include <unistd.h>
# include <sys/uio.h>
@@ -2624,6 +2745,8 @@ word len;
/* actually calls. */
#endif
+#endif /* 0 */
+
/*ARGSUSED*/
GC_bool GC_page_was_ever_dirty(h)
struct hblk *h;
@@ -2741,8 +2864,10 @@ void GC_dirty_init()
/* Ignore write hints. They don't help us here. */
/*ARGSUSED*/
-void GC_write_hint(h)
+void GC_remove_protection(h, nblocks, is_ptrfree)
struct hblk *h;
+word nblocks;
+GC_bool is_ptrfree;
{
}
@@ -2950,15 +3075,24 @@ struct hblk *h;
}
/*ARGSUSED*/
-void GC_write_hint(h)
+void GC_remove_protection(h, nblocks, is_ptrfree)
struct hblk *h;
+word nblocks;
+GC_bool is_ptrfree;
{
- PCR_VD_WriteProtectDisable(h, HBLKSIZE);
- PCR_VD_WriteProtectEnable(h, HBLKSIZE);
+ PCR_VD_WriteProtectDisable(h, nblocks*HBLKSIZE);
+ PCR_VD_WriteProtectEnable(h, nblocks*HBLKSIZE);
}
# endif /* PCR_VDB */
+# ifndef HAVE_INCREMENTAL_PROTECTION_NEEDS
+ int GC_incremental_protection_needs()
+ {
+ return GC_PROTECTS_NONE;
+ }
+# endif /* !HAVE_INCREMENTAL_PROTECTION_NEEDS */
+
/*
* Call stack save code for debugging.
* Should probably be in mach_dep.c, but that requires reorganization.
@@ -2968,6 +3102,8 @@ struct hblk *h;
/* long as the frame pointer is explicitly stored. In the case of gcc, */
/* compiler flags (e.g. -fomit-frame-pointer) determine whether it is. */
#if defined(I386) && defined(LINUX) && defined(SAVE_CALL_CHAIN)
+# include <features.h>
+
struct frame {
struct frame *fr_savfp;
long fr_savpc;
@@ -2977,6 +3113,8 @@ struct hblk *h;
#if defined(SPARC)
# if defined(LINUX)
+# include <features.h>
+
struct frame {
long fr_local[8];
long fr_arg[6];
@@ -3008,10 +3146,49 @@ struct hblk *h;
# endif
#endif /* SPARC */
-#ifdef SAVE_CALL_CHAIN
+#ifdef NEED_CALLINFO
/* Fill in the pc and argument information for up to NFRAMES of my */
/* callers. Ignore my frame and my callers frame. */
+#ifdef LINUX
+# include <features.h>
+# if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 1 || __GLIBC__ > 2
+# define HAVE_BUILTIN_BACKTRACE
+# ifdef IA64
+# define BUILTIN_BACKTRACE_BROKEN
+# endif
+# endif
+#endif
+
+#include <execinfo.h>
+#ifdef LINUX
+# include <unistd.h>
+#endif
+
+#endif /* NEED_CALLINFO */
+
+#ifdef SAVE_CALL_CHAIN
+
+#if NARGS == 0 && NFRAMES % 2 == 0 /* No padding */ \
+ && defined(HAVE_BUILTIN_BACKTRACE)
+
+void GC_save_callers (info)
+struct callinfo info[NFRAMES];
+{
+ void * tmp_info[NFRAMES + 1];
+ int npcs, i;
+# define IGNORE_FRAMES 1
+
+ /* We retrieve NFRAMES+1 pc values, but discard the first, since it */
+ /* points to our own frame. */
+ GC_ASSERT(sizeof(struct callinfo) == sizeof(void *));
+ npcs = backtrace((void **)tmp_info, NFRAMES + IGNORE_FRAMES);
+ BCOPY(tmp_info+IGNORE_FRAMES, info, (npcs - IGNORE_FRAMES) * sizeof(void *));
+ for (i = npcs - IGNORE_FRAMES; i < NFRAMES; ++i) info[i].ci_pc = 0;
+}
+
+#else /* No builtin backtrace; do it ourselves */
+
#if (defined(OPENBSD) || defined(NETBSD)) && defined(SPARC)
# define FR_SAVFP fr_fp
# define FR_SAVPC fr_pc
@@ -3058,8 +3235,129 @@ struct callinfo info[NFRAMES];
if (nframes < NFRAMES) info[nframes].ci_pc = 0;
}
+#endif /* No builtin backtrace */
+
#endif /* SAVE_CALL_CHAIN */
+#ifdef NEED_CALLINFO
+
+/* Print info to stderr. We do NOT hold the allocation lock */
+void GC_print_callers (info)
+struct callinfo info[NFRAMES];
+{
+ register int i;
+ static int reentry_count = 0;
+
+ LOCK();
+ ++reentry_count;
+ UNLOCK();
+
+# if NFRAMES == 1
+ GC_err_printf0("\tCaller at allocation:\n");
+# else
+ GC_err_printf0("\tCall chain at allocation:\n");
+# endif
+ for (i = 0; i < NFRAMES; i++) {
+ if (info[i].ci_pc == 0) break;
+# if NARGS > 0
+ {
+ int j;
+
+ GC_err_printf0("\t\targs: ");
+ for (j = 0; j < NARGS; j++) {
+ if (j != 0) GC_err_printf0(", ");
+ GC_err_printf2("%d (0x%X)", ~(info[i].ci_arg[j]),
+ ~(info[i].ci_arg[j]));
+ }
+ GC_err_printf0("\n");
+ }
+# endif
+ if (reentry_count > 1) {
+ /* We were called during an allocation during */
+ /* a previous GC_print_callers call; punt. */
+ GC_err_printf1("\t\t##PC##= 0x%lx\n", info[i].ci_pc);
+ continue;
+ }
+ {
+# ifdef LINUX
+ FILE *pipe;
+# endif
+# if defined(HAVE_BUILTIN_BACKTRACE) && \
+ !defined(BUILTIN_BACKTRACE_BROKEN)
+ char **sym_name =
+ backtrace_symbols((void **)(&(info[i].ci_pc)), 1);
+ char *name = sym_name[0];
+ GC_bool found_it = (strchr(name, '(') != 0);
+# else
+ char buf[40];
+ char *name = buf;
+ GC_bool fount_it = FALSE:
+ sprintf(buf, "##PC##= 0x%lx", info[i].ci_pc);
+# endif
+# ifdef LINUX
+ if (!found_it) {
+# define EXE_SZ 100
+ static char exe_name[EXE_SZ];
+# define CMD_SZ 200
+ char cmd_buf[CMD_SZ];
+# define RESULT_SZ 200
+ static char result_buf[RESULT_SZ];
+ size_t result_len;
+ static GC_bool found_exe_name = FALSE;
+ static GC_bool will_fail = FALSE;
+ int ret_code;
+ /* Unfortunately, this is the common case for the */
+ /* main executable. */
+ /* Try to get it via a hairy and expensive scheme. */
+ /* First we get the name of the executable: */
+ if (will_fail) goto out;
+ if (!found_exe_name) {
+ ret_code = readlink("/proc/self/exe", exe_name, EXE_SZ);
+ if (ret_code < 0 || ret_code >= EXE_SZ
+ || exe_name[0] != '/') {
+ will_fail = TRUE; /* Dont try again. */
+ goto out;
+ }
+ exe_name[ret_code] = '\0';
+ found_exe_name = TRUE;
+ }
+ /* Then we use popen to start addr2line -e <exe> <addr> */
+ /* There are faster ways to do this, but hopefully this */
+ /* isn't time critical. */
+ sprintf(cmd_buf, "/usr/bin/addr2line -e %s 0x%lx", exe_name,
+ (unsigned long)info[i].ci_pc);
+ pipe = popen(cmd_buf, "r");
+ if (pipe < 0 || fgets(result_buf, RESULT_SZ, pipe) == 0) {
+ will_fail = TRUE;
+ goto out;
+ }
+ result_len = strlen(result_buf);
+ if (result_buf[result_len - 1] == '\n') --result_len;
+ if (result_buf[0] == '?'
+ || result_buf[result_len-2] == ':'
+ && result_buf[result_len-1] == '0')
+ goto out;
+ if (result_len < RESULT_SZ - 25) {
+ /* Add in hex address */
+ sprintf(result_buf + result_len, " [0x%lx]",
+ (unsigned long)info[i].ci_pc);
+ }
+ name = result_buf;
+ pclose(pipe);
+ out:
+ }
+# endif /* LINUX */
+ GC_err_printf1("\t\t%s\n", name);
+ free(sym_name); /* May call GC_free; that's OK */
+ }
+ }
+ LOCK();
+ --reentry_count;
+ UNLOCK();
+}
+
+#endif /* NEED_CALLINFO */
+
#if defined(LINUX) && defined(__ELF__) && \
(!defined(SMALL_CONFIG) || defined(USE_PROC_FOR_LIBRARIES))
#ifdef GC_USE_LD_WRAP