!defined(MSWIN32) && !defined(MSWINCE) && \
!(defined(ALPHA) && defined(OSF1)) && \
!defined(HPUX) && !(defined(LINUX) && defined(__ELF__)) && \
- !defined(RS6000) && !defined(SCO_ELF) && \
+ !defined(RS6000) && !defined(SCO_ELF) && !defined(DGUX) && \
!(defined(FREEBSD) && defined(__ELF__)) && \
- !(defined(NETBSD) && defined(__ELF__)) && !defined(HURD)
+ !(defined(NETBSD) && defined(__ELF__)) && !defined(HURD) && \
+ !defined(DARWIN)
--> We only know how to find data segments of dynamic libraries for the
--> above. Additional SVR4 variants might not be too
--> hard to add.
# define l_name lm_name
#endif
+#if defined(NETBSD)
+# include <machine/elf_machdep.h>
+# define ELFSIZE ARCH_ELFSIZE
+#endif
+
#if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \
- (defined(FREEBSD) && defined(__ELF__)) || \
+ (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \
(defined(NETBSD) && defined(__ELF__)) || defined(HURD)
# include <stddef.h>
# include <elf.h>
/* Newer versions of GNU/Linux define this macro. We
* define it similarly for any ELF systems that don't. */
# ifndef ElfW
-# if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32
-# define ElfW(type) Elf32_##type
+# if defined(FREEBSD)
+# if __ELF_WORD_SIZE == 32
+# define ElfW(type) Elf32_##type
+# else
+# define ElfW(type) Elf64_##type
+# endif
# else
-# define ElfW(type) Elf64_##type
+# ifdef NETBSD
+# if ELFSIZE == 32
+# define ElfW(type) Elf32_##type
+# else
+# define ElfW(type) Elf64_##type
+# endif
+# else
+# if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32
+# define ElfW(type) Elf32_##type
+# else
+# define ElfW(type) Elf64_##type
+# endif
+# endif
# endif
# endif
+/* An user-supplied routine that is called to dtermine if a DSO must
+ be scanned by the gc. */
+static int (*GC_has_static_roots)(const char *, void *, size_t);
+/* Register the routine. */
+void
+GC_register_has_static_roots_callback
+ (int (*callback)(const char *, void *, size_t))
+{
+ GC_has_static_roots = callback;
+}
+
#if defined(SUNOS5DL) && !defined(USE_PROC_FOR_LIBRARIES)
#ifdef LINT
# endif /* SUNOS */
#if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \
- (defined(FREEBSD) && defined(__ELF__)) || \
+ (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \
(defined(NETBSD) && defined(__ELF__)) || defined(HURD)
/* Repeatedly read until buffer is filled, or EOF is encountered */
/* Defined in os_dep.c. */
-static char *parse_map_entry(char *buf_ptr, word *start, word *end,
- char *prot_buf, unsigned int *maj_dev);
+char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
+ char *prot_buf, unsigned int *maj_dev);
+word GC_apply_to_maps(word (*fn)(char *));
+ /* From os_dep.c */
-void GC_register_dynamic_libraries()
+word GC_register_map_entries(char *maps)
{
- int f;
- int result;
char prot_buf[5];
- int maps_size;
- char maps_temp[32768];
- char *maps_buf;
- char *buf_ptr;
+ char *buf_ptr = maps;
int count;
word start, end;
- unsigned int maj_dev, min_dev;
+ unsigned int maj_dev;
word least_ha, greatest_ha;
unsigned i;
word datastart = (word)(DATASTART);
- /* Read /proc/self/maps */
- /* Note that we may not allocate, and thus can't use stdio. */
- f = open("/proc/self/maps", O_RDONLY);
- if (-1 == f) ABORT("Couldn't open /proc/self/maps");
- /* stat() doesn't work for /proc/self/maps, so we have to
- read it to find out how large it is... */
- maps_size = 0;
- do {
- result = GC_repeat_read(f, maps_temp, sizeof(maps_temp));
- if (result <= 0) ABORT("Couldn't read /proc/self/maps");
- maps_size += result;
- } while (result == sizeof(maps_temp));
-
- if (maps_size > sizeof(maps_temp)) {
- /* If larger than our buffer, close and re-read it. */
- close(f);
- f = open("/proc/self/maps", O_RDONLY);
- if (-1 == f) ABORT("Couldn't open /proc/self/maps");
- maps_buf = alloca(maps_size);
- if (NULL == maps_buf) ABORT("/proc/self/maps alloca failed");
- result = GC_repeat_read(f, maps_buf, maps_size);
- if (result <= 0) ABORT("Couldn't read /proc/self/maps");
- } else {
- /* Otherwise use the fixed size buffer */
- maps_buf = maps_temp;
- }
-
- close(f);
- maps_buf[result] = '\0';
- buf_ptr = maps_buf;
- /* Compute heap bounds. Should be done by add_to_heap? */
+ /* Compute heap bounds. FIXME: Should be done by add_to_heap? */
least_ha = (word)(-1);
greatest_ha = 0;
for (i = 0; i < GC_n_heap_sects; ++i) {
}
if (greatest_ha < (word)GC_scratch_last_end_ptr)
greatest_ha = (word)GC_scratch_last_end_ptr;
- for (;;) {
-
- buf_ptr = parse_map_entry(buf_ptr, &start, &end, prot_buf, &maj_dev);
- if (buf_ptr == NULL) return;
+ for (;;) {
+ buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, prot_buf, &maj_dev);
+ if (buf_ptr == NULL) return 1;
if (prot_buf[1] == 'w') {
/* This is a writable mapping. Add it to */
/* the root set unless it is already otherwise */
/* Stack mapping; discard */
continue;
}
- if (start <= datastart && end > datastart && maj_dev != 0) {
- /* Main data segment; discard */
- continue;
- }
# ifdef THREADS
if (GC_segment_is_thread_stack(start, end)) continue;
# endif
- /* The rest of this assumes that there is no mapping */
- /* spanning the beginning of the data segment, or extending */
- /* beyond the entire heap at both ends. */
- /* Empirically these assumptions hold. */
-
- if (start < (word)DATAEND && end > (word)DATAEND) {
- /* Rld may use space at the end of the main data */
- /* segment. Thus we add that in. */
- start = (word)DATAEND;
- }
+ /* We no longer exclude the main data segment. */
if (start < least_ha && end > least_ha) {
end = least_ha;
}
if (start >= least_ha && end <= greatest_ha) continue;
GC_add_roots_inner((char *)start, (char *)end, TRUE);
}
- }
+ }
+ return 1;
}
-//
-// parse_map_entry parses an entry from /proc/self/maps so we can
-// locate all writable data segments that belong to shared libraries.
-// The format of one of these entries and the fields we care about
-// is as follows:
-// XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537 name of mapping...\n
-// ^^^^^^^^ ^^^^^^^^ ^^^^ ^^
-// start end prot maj_dev
-// 0 9 18 32
-//
-// The parser is called with a pointer to the entry and the return value
-// is either NULL or is advanced to the next entry(the byte after the
-// trailing '\n'.)
-//
-#define OFFSET_MAP_START 0
-#define OFFSET_MAP_END 9
-#define OFFSET_MAP_PROT 18
-#define OFFSET_MAP_MAJDEV 32
-
-static char *parse_map_entry(char *buf_ptr, word *start, word *end,
- char *prot_buf, unsigned int *maj_dev)
+void GC_register_dynamic_libraries()
{
- int i;
- unsigned int val;
- char *tok;
-
- if (buf_ptr == NULL || *buf_ptr == '\0') {
- return NULL;
- }
-
- memcpy(prot_buf, buf_ptr+OFFSET_MAP_PROT, 4); // do the protections first
- prot_buf[4] = '\0';
-
- if (prot_buf[1] == 'w') { // we can skip all of this if it's not writable
-
- tok = buf_ptr;
- buf_ptr[OFFSET_MAP_START+8] = '\0';
- *start = strtoul(tok, NULL, 16);
-
- tok = buf_ptr+OFFSET_MAP_END;
- buf_ptr[OFFSET_MAP_END+8] = '\0';
- *end = strtoul(tok, NULL, 16);
-
- buf_ptr += OFFSET_MAP_MAJDEV;
- tok = buf_ptr;
- while (*buf_ptr != ':') buf_ptr++;
- *buf_ptr++ = '\0';
- *maj_dev = strtoul(tok, NULL, 16);
- }
-
- while (*buf_ptr && *buf_ptr++ != '\n');
+ if (!GC_apply_to_maps(GC_register_map_entries))
+ ABORT("Failed to read /proc for library registration.");
+}
- return buf_ptr;
+/* We now take care of the main data segment ourselves: */
+GC_bool GC_register_main_static_data()
+{
+ return FALSE;
}
+# define HAVE_REGISTER_MAIN_STATIC_DATA
+
#endif /* USE_PROC_FOR_LIBRARIES */
#if !defined(USE_PROC_FOR_LIBRARIES)
+ sizeof (info->dlpi_phnum))
return -1;
- /* Skip the first object - it is the main program. */
- if (*(int *)ptr == 0)
- {
- *(int *)ptr = 1;
- return 0;
- }
-
p = info->dlpi_phdr;
for( i = 0; i < (int)(info->dlpi_phnum); ((i++),(p++)) ) {
switch( p->p_type ) {
{
if( !(p->p_flags & PF_W) ) break;
start = ((char *)(p->p_vaddr)) + info->dlpi_addr;
+
+ if (GC_has_static_roots
+ && !GC_has_static_roots(info->dlpi_name, start, p->p_memsz))
+ break;
+
GC_add_roots_inner(start, start + p->p_memsz, TRUE);
}
break;
}
}
+ * (int *)ptr = 1; /* Signal that we were called */
return 0;
}
GC_bool GC_register_dynamic_libraries_dl_iterate_phdr()
{
- int tmp = 0;
-
if (dl_iterate_phdr) {
- dl_iterate_phdr(GC_register_dynlib_callback, &tmp);
+ int did_something = 0;
+ dl_iterate_phdr(GC_register_dynlib_callback, &did_something);
+ if (!did_something) {
+ /* dl_iterate_phdr may forget the static data segment in */
+ /* statically linked executables. */
+ GC_add_roots_inner(DATASTART, (char *)(DATAEND), TRUE);
+# if defined(DATASTART2)
+ GC_add_roots_inner(DATASTART2, (char *)(DATAEND2), TRUE);
+# endif
+ }
+
return TRUE;
} else {
return FALSE;
}
}
+/* Do we need to separately register the main static data segment? */
+GC_bool GC_register_main_static_data()
+{
+ return (dl_iterate_phdr == 0);
+}
+
+#define HAVE_REGISTER_MAIN_STATIC_DATA
+
# else /* !LINUX || version(glibc) < 2.2.4 */
/* Dynamic loading code for Linux running ELF. Somewhat tested on
#if defined(NETBSD)
# include <sys/exec_elf.h>
+/* for compatibility with 1.4.x */
+# ifndef DT_DEBUG
+# define DT_DEBUG 21
+# endif
+# ifndef PT_LOAD
+# define PT_LOAD 1
+# endif
+# ifndef PF_W
+# define PF_W 2
+# endif
#else
# include <elf.h>
#endif
GC_FirstDLOpenedLinkMap()
{
ElfW(Dyn) *dp;
- struct r_debug *r;
static struct link_map *cachedResult = 0;
if( _DYNAMIC == 0) {
if( cachedResult == 0 ) {
int tag;
for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) {
+ /* FIXME: The DT_DEBUG header is not mandated by the */
+ /* ELF spec. This code appears to be dependent on */
+ /* idiosynchracies of older GNU tool chains. If this code */
+ /* fails for you, the real problem is probably that it is */
+ /* being used at all. You should be getting the */
+ /* dl_iterate_phdr version. */
if( tag == DT_DEBUG ) {
struct link_map *lm
= ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
/* The type is a lie, since the real type doesn't make sense here, */
/* and we only test for NULL. */
+
/* We use /proc to track down all parts of the address space that are */
/* mapped by the process, and throw out regions we know we shouldn't */
/* worry about. This may also work under other SVR4 variants. */
}
for (i = 0; i < needed_sz; i++) {
flags = addr_map[i].pr_mflags;
- if ((flags & (MA_BREAK | MA_STACK | MA_PHYS)) != 0) goto irrelevant;
+ if ((flags & (MA_BREAK | MA_STACK | MA_PHYS
+ | MA_FETCHOP | MA_NOTCACHED)) != 0) goto irrelevant;
if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE))
goto irrelevant;
/* The latter test is empirically useless in very old Irix */
}
# endif
-# ifndef MSWINCE
+# ifdef MSWINCE
+ /* Do we need to separately register the main static data segment? */
+ GC_bool GC_register_main_static_data()
+ {
+ return FALSE;
+ }
+# else /* win32 */
extern GC_bool GC_no_win32_dlls;
-# endif
+
+ GC_bool GC_register_main_static_data()
+ {
+ return GC_no_win32_dlls;
+ }
+# endif /* win32 */
+
+# define HAVE_REGISTER_MAIN_STATIC_DATA
+
+ /* The frame buffer testing code is dead in this version. */
+ /* We leave it here temporarily in case the switch to just */
+ /* testing for MEM_IMAGE sections causes un expected */
+ /* problems. */
+ GC_bool GC_warn_fb = TRUE; /* Warn about traced likely */
+ /* graphics memory. */
+ GC_bool GC_disallow_ignore_fb = FALSE;
+ int GC_ignore_fb_mb; /* Ignore mappings bigger than the */
+ /* specified number of MB. */
+ GC_bool GC_ignore_fb = FALSE; /* Enable frame buffer */
+ /* checking. */
+ /* Issue warning if tracing apparent framebuffer. */
+ /* This limits us to one warning, and it's a back door to */
+ /* disable that. */
+
+ /* Should [start, start+len) be treated as a frame buffer */
+ /* and ignored? */
+ /* Unfortunately, we currently are not quite sure how to tell */
+ /* this automatically, and rely largely on user input. */
+ /* We expect that any mapping with type MEM_MAPPED (which */
+ /* apparently excludes library data sections) can be safely */
+ /* ignored. But we're too chicken to do that in this */
+ /* version. */
+ /* Based on a very limited sample, it appears that: */
+ /* - Frame buffer mappings appear as mappings of large */
+ /* length, usually a bit less than a power of two. */
+ /* - The definition of "a bit less" in the above cannot */
+ /* be made more precise. */
+ /* - Have a starting address at best 64K aligned. */
+ /* - Have type == MEM_MAPPED. */
+ static GC_bool is_frame_buffer(ptr_t start, size_t len, DWORD tp)
+ {
+ static GC_bool initialized = FALSE;
+# define MB (1024*1024)
+# define DEFAULT_FB_MB 15
+# define MIN_FB_MB 3
+
+ if (GC_disallow_ignore_fb || tp != MEM_MAPPED) return FALSE;
+ if (!initialized) {
+ char * ignore_fb_string = GETENV("GC_IGNORE_FB");
+
+ if (0 != ignore_fb_string) {
+ while (*ignore_fb_string == ' ' || *ignore_fb_string == '\t')
+ ++ignore_fb_string;
+ if (*ignore_fb_string == '\0') {
+ GC_ignore_fb_mb = DEFAULT_FB_MB;
+ } else {
+ GC_ignore_fb_mb = atoi(ignore_fb_string);
+ if (GC_ignore_fb_mb < MIN_FB_MB) {
+ WARN("Bad GC_IGNORE_FB value. Using %ld\n", DEFAULT_FB_MB);
+ GC_ignore_fb_mb = DEFAULT_FB_MB;
+ }
+ }
+ GC_ignore_fb = TRUE;
+ } else {
+ GC_ignore_fb_mb = DEFAULT_FB_MB; /* For warning */
+ }
+ initialized = TRUE;
+ }
+ if (len >= ((size_t)GC_ignore_fb_mb << 20)) {
+ if (GC_ignore_fb) {
+ return TRUE;
+ } else {
+ if (GC_warn_fb) {
+ WARN("Possible frame buffer mapping at 0x%lx: \n"
+ "\tConsider setting GC_IGNORE_FB to improve performance.\n",
+ start);
+ GC_warn_fb = FALSE;
+ }
+ return FALSE;
+ }
+ } else {
+ return FALSE;
+ }
+ }
+
+# ifdef DEBUG_VIRTUALQUERY
+ void GC_dump_meminfo(MEMORY_BASIC_INFORMATION *buf)
+ {
+ GC_printf4("BaseAddress = %lx, AllocationBase = %lx, RegionSize = %lx(%lu)\n",
+ buf -> BaseAddress, buf -> AllocationBase, buf -> RegionSize,
+ buf -> RegionSize);
+ GC_printf4("\tAllocationProtect = %lx, State = %lx, Protect = %lx, "
+ "Type = %lx\n",
+ buf -> AllocationProtect, buf -> State, buf -> Protect,
+ buf -> Type);
+ }
+# endif /* DEBUG_VIRTUALQUERY */
+
void GC_register_dynamic_libraries()
{
MEMORY_BASIC_INFORMATION buf;
if (buf.State == MEM_COMMIT
&& (protect == PAGE_EXECUTE_READWRITE
|| protect == PAGE_READWRITE)
- && !GC_is_heap_base(buf.AllocationBase)) {
+ && !GC_is_heap_base(buf.AllocationBase)
+ /* This used to check for
+ * !is_frame_buffer(p, buf.RegionSize, buf.Type)
+ * instead of just checking for MEM_IMAGE.
+ * If something breaks, change it back. */
+ && buf.Type == MEM_IMAGE) {
+# ifdef DEBUG_VIRTUALQUERY
+ GC_dump_meminfo(&buf);
+# endif
if ((char *)p != limit) {
GC_cond_add_roots(base, limit);
base = p;
len = ldi->ldinfo_next;
GC_add_roots_inner(
ldi->ldinfo_dataorg,
- (unsigned long)ldi->ldinfo_dataorg
+ (ptr_t)(unsigned long)ldi->ldinfo_dataorg
+ ldi->ldinfo_datasize,
TRUE);
ldi = len ? (struct ld_info *)((char *)ldi + len) : 0;
}
#endif /* RS6000 */
+#ifdef DARWIN
+/* __private_extern__ hack required for pre-3.4 gcc versions. */
+#ifndef __private_extern__
+# define __private_extern__ extern
+# include <mach-o/dyld.h>
+# undef __private_extern__
+#else
+# include <mach-o/dyld.h>
+#endif
+#include <mach-o/getsect.h>
+
+/*#define DARWIN_DEBUG*/
+
+const static struct {
+ const char *seg;
+ const char *sect;
+} GC_dyld_sections[] = {
+ { SEG_DATA, SECT_DATA },
+ { SEG_DATA, SECT_BSS },
+ { SEG_DATA, SECT_COMMON }
+};
+
+#ifdef DARWIN_DEBUG
+static const char *GC_dyld_name_for_hdr(struct mach_header *hdr) {
+ unsigned long i,c;
+ c = _dyld_image_count();
+ for(i=0;i<c;i++) if(_dyld_get_image_header(i) == hdr)
+ return _dyld_get_image_name(i);
+ return NULL;
+}
+#endif
+
+/* This should never be called by a thread holding the lock */
+static void GC_dyld_image_add(struct mach_header* hdr, unsigned long slide) {
+ unsigned long start,end,i;
+ const struct section *sec;
+ if (GC_no_dls) return;
+ for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
+ sec = getsectbynamefromheader(
+ hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect);
+ if(sec == NULL || sec->size == 0) continue;
+ start = slide + sec->addr;
+ end = start + sec->size;
+# ifdef DARWIN_DEBUG
+ GC_printf4("Adding section at %p-%p (%lu bytes) from image %s\n",
+ start,end,sec->size,GC_dyld_name_for_hdr(hdr));
+# endif
+ GC_add_roots((char*)start,(char*)end);
+ }
+# ifdef DARWIN_DEBUG
+ GC_print_static_roots();
+# endif
+}
+
+/* This should never be called by a thread holding the lock */
+static void GC_dyld_image_remove(struct mach_header* hdr, unsigned long slide) {
+ unsigned long start,end,i;
+ const struct section *sec;
+ for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
+ sec = getsectbynamefromheader(
+ hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect);
+ if(sec == NULL || sec->size == 0) continue;
+ start = slide + sec->addr;
+ end = start + sec->size;
+# ifdef DARWIN_DEBUG
+ GC_printf4("Removing section at %p-%p (%lu bytes) from image %s\n",
+ start,end,sec->size,GC_dyld_name_for_hdr(hdr));
+# endif
+ GC_remove_roots((char*)start,(char*)end);
+ }
+# ifdef DARWIN_DEBUG
+ GC_print_static_roots();
+# endif
+}
+
+void GC_register_dynamic_libraries() {
+ /* Currently does nothing. The callbacks are setup by GC_init_dyld()
+ The dyld library takes it from there. */
+}
+
+/* The _dyld_* functions have an internal lock so no _dyld functions
+ can be called while the world is stopped without the risk of a deadlock.
+ Because of this we MUST setup callbacks BEFORE we ever stop the world.
+ This should be called BEFORE any thread in created and WITHOUT the
+ allocation lock held. */
+
+void GC_init_dyld() {
+ static GC_bool initialized = FALSE;
+ char *bind_fully_env = NULL;
+
+ if(initialized) return;
+
+# ifdef DARWIN_DEBUG
+ GC_printf0("Registering dyld callbacks...\n");
+# endif
+
+ /* Apple's Documentation:
+ When you call _dyld_register_func_for_add_image, the dynamic linker runtime
+ calls the specified callback (func) once for each of the images that is
+ currently loaded into the program. When a new image is added to the program,
+ your callback is called again with the mach_header for the new image, and the
+ virtual memory slide amount of the new image.
+
+ This WILL properly register already linked libraries and libraries
+ linked in the future
+ */
+
+ _dyld_register_func_for_add_image(GC_dyld_image_add);
+ _dyld_register_func_for_remove_image(GC_dyld_image_remove);
+
+ /* Set this early to avoid reentrancy issues. */
+ initialized = TRUE;
+
+ bind_fully_env = getenv("DYLD_BIND_AT_LAUNCH");
+
+ if (bind_fully_env == NULL) {
+# ifdef DARWIN_DEBUG
+ GC_printf0("Forcing full bind of GC code...\n");
+# endif
+
+ if(!_dyld_bind_fully_image_containing_address((unsigned long*)GC_malloc))
+ GC_abort("_dyld_bind_fully_image_containing_address failed");
+ }
+
+}
+
+#define HAVE_REGISTER_MAIN_STATIC_DATA
+GC_bool GC_register_main_static_data()
+{
+ /* Already done through dyld callbacks */
+ return FALSE;
+}
+
+#endif /* DARWIN */
#else /* !DYNAMIC_LOADING */
int GC_no_dynamic_loading;
#endif /* !PCR */
+
#endif /* !DYNAMIC_LOADING */
+
+#ifndef HAVE_REGISTER_MAIN_STATIC_DATA
+
+/* Do we need to separately register the main static data segment? */
+GC_bool GC_register_main_static_data()
+{
+ return TRUE;
+}
+#endif /* HAVE_REGISTER_MAIN_STATIC_DATA */
+