summaryrefslogtreecommitdiff
path: root/dyn_load.c
diff options
context:
space:
mode:
Diffstat (limited to 'dyn_load.c')
-rw-r--r--dyn_load.c416
1 files changed, 280 insertions, 136 deletions
diff --git a/dyn_load.c b/dyn_load.c
index 74aacd7..5a86cf6 100644
--- a/dyn_load.c
+++ b/dyn_load.c
@@ -26,15 +26,15 @@
* None of this is safe with dlclose and incremental collection.
* But then not much of anything is safe in the presence of dlclose.
*/
-#ifndef MACOS
+#if !defined(MACOS) && !defined(_WIN32_WCE)
# include <sys/types.h>
#endif
-#include "gc_priv.h"
+#include "private/gc_priv.h"
/* BTL: avoid circular redefinition of dlopen if SOLARIS_THREADS defined */
# if (defined(LINUX_THREADS) || defined(SOLARIS_THREADS) \
|| defined(HPUX_THREADS) || defined(IRIX_THREADS)) && defined(dlopen) \
- && !defined(USE_LD_WRAP)
+ && !defined(GC_USE_LD_WRAP)
/* To support threads in Solaris, gc.h interposes on dlopen by */
/* defining "dlopen" to be "GC_dlopen", which is implemented below. */
/* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the */
@@ -46,11 +46,14 @@
# undef GC_must_restore_redefined_dlopen
# endif
-#if (defined(DYNAMIC_LOADING) || defined(MSWIN32)) && !defined(PCR)
+#if (defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE)) \
+ && !defined(PCR)
#if !defined(SUNOS4) && !defined(SUNOS5DL) && !defined(IRIX5) && \
- !defined(MSWIN32) && !(defined(ALPHA) && defined(OSF1)) && \
+ !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(NETBSD) && defined(__ELF__)) && !defined(HURD)
--> We only know how to find data segments of dynamic libraries for the
--> above. Additional SVR4 variants might not be too
--> hard to add.
@@ -121,6 +124,11 @@ GC_FirstDLOpenedLinkMap()
#endif /* SUNOS5DL ... */
+/* BTL: added to fix circular dlopen definition if SOLARIS_THREADS defined */
+# if defined(GC_must_restore_redefined_dlopen)
+# define dlopen GC_dlopen
+# endif
+
#if defined(SUNOS4) && !defined(USE_PROC_FOR_LIBRARIES)
#ifdef LINT
@@ -161,69 +169,6 @@ static ptr_t GC_first_common()
#endif /* SUNOS4 ... */
-# if defined(LINUX_THREADS) || defined(SOLARIS_THREADS) \
- || defined(HPUX_THREADS) || defined(IRIX_THREADS)
- /* Make sure we're not in the middle of a collection, and make */
- /* sure we don't start any. Returns previous value of GC_dont_gc. */
- /* This is invoked prior to a dlopen call to avoid synchronization */
- /* issues. We can't just acquire the allocation lock, since startup */
- /* code in dlopen may try to allocate. */
- /* This solution risks heap growth in the presence of many dlopen */
- /* calls in either a multithreaded environment, or if the library */
- /* initialization code allocates substantial amounts of GC'ed memory. */
- /* But I don't know of a better solution. */
- /* This can still deadlock if the client explicitly starts a GC */
- /* during the dlopen. He shouldn't do that. */
- static GC_bool disable_gc_for_dlopen()
- {
- GC_bool result;
- LOCK();
- result = GC_dont_gc;
- while (GC_incremental && GC_collection_in_progress()) {
- GC_collect_a_little_inner(1000);
- }
- GC_dont_gc = TRUE;
- UNLOCK();
- return(result);
- }
-
- /* Redefine dlopen to guarantee mutual exclusion with */
- /* GC_register_dynamic_libraries. */
- /* Should probably happen for other operating systems, too. */
-
-#include <dlfcn.h>
-
-#ifdef USE_LD_WRAP
- void * __wrap_dlopen(const char *path, int mode)
-#else
- void * GC_dlopen(path, mode)
- GC_CONST char * path;
- int mode;
-#endif
-{
- void * result;
- GC_bool dont_gc_save;
-
-# ifndef USE_PROC_FOR_LIBRARIES
- dont_gc_save = disable_gc_for_dlopen();
-# endif
-# ifdef USE_LD_WRAP
- result = __real_dlopen(path, mode);
-# else
- result = dlopen(path, mode);
-# endif
-# ifndef USE_PROC_FOR_LIBRARIES
- GC_dont_gc = dont_gc_save;
-# endif
- return(result);
-}
-# endif /* SOLARIS_THREADS */
-
-/* BTL: added to fix circular dlopen definition if SOLARIS_THREADS defined */
-# if defined(GC_must_restore_redefined_dlopen)
-# define dlopen GC_dlopen
-# endif
-
# if defined(SUNOS4) || defined(SUNOS5DL)
/* Add dynamic library data sections to the root set. */
# if !defined(PCR) && !defined(SOLARIS_THREADS) && defined(THREADS)
@@ -297,14 +242,193 @@ void GC_register_dynamic_libraries()
# endif /* !USE_PROC ... */
# endif /* SUNOS */
-#if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF)
+#if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \
+ (defined(NETBSD) && defined(__ELF__)) || defined(HURD)
+
+
+#ifdef USE_PROC_FOR_LIBRARIES
+
+#include <string.h>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define MAPS_BUF_SIZE (32*1024)
+
+extern ssize_t GC_repeat_read(int fd, char *buf, size_t count);
+ /* 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);
+
+void GC_register_dynamic_libraries()
+{
+ int f;
+ int result;
+ char prot_buf[5];
+ int maps_size;
+ char maps_temp[32768];
+ char *maps_buf;
+ char *buf_ptr;
+ int count;
+ word start, end;
+ unsigned int maj_dev, min_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? */
+ least_ha = (word)(-1);
+ greatest_ha = 0;
+ for (i = 0; i < GC_n_heap_sects; ++i) {
+ word sect_start = (word)GC_heap_sects[i].hs_start;
+ word sect_end = sect_start + GC_heap_sects[i].hs_bytes;
+ if (sect_start < least_ha) least_ha = sect_start;
+ if (sect_end > greatest_ha) greatest_ha = sect_end;
+ }
+ 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;
+
+ if (prot_buf[1] == 'w') {
+ /* This is a writable mapping. Add it to */
+ /* the root set unless it is already otherwise */
+ /* accounted for. */
+ if (start <= (word)GC_stackbottom && end >= (word)GC_stackbottom) {
+ /* 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;
+ }
+ if (start < least_ha && end > least_ha) {
+ end = least_ha;
+ }
+ if (start < greatest_ha && end > greatest_ha) {
+ start = greatest_ha;
+ }
+ if (start >= least_ha && end <= greatest_ha) continue;
+ GC_add_roots_inner((char *)start, (char *)end, TRUE);
+ }
+ }
+}
+
+//
+// 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)
+{
+ 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');
+
+ return buf_ptr;
+}
+
+#else /* !USE_PROC_FOR_LIBRARIES */
/* Dynamic loading code for Linux running ELF. Somewhat tested on
* Linux/x86, untested but hopefully should work on Linux/Alpha.
* This code was derived from the Solaris/ELF support. Thanks to
* whatever kind soul wrote that. - Patrick Bridges */
-#include <elf.h>
+#if defined(NETBSD)
+# include <sys/exec_elf.h>
+#else
+# include <elf.h>
+#endif
#include <link.h>
/* Newer versions of Linux/Alpha and Linux/x86 define this macro. We
@@ -379,23 +503,26 @@ void GC_register_dynamic_libraries()
}
}
-#endif
+#endif /* !USE_PROC_FOR_LIBRARIES */
+
+#endif /* LINUX */
-#if defined(IRIX5) || defined(USE_PROC_FOR_LIBRARIES)
+#if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX))
#include <sys/procfs.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <elf.h>
#include <errno.h>
+#include <signal.h> /* Only for the following test. */
+#ifndef _sigargs
+# define IRIX6
+#endif
extern void * GC_roots_present();
/* The type is a lie, since the real type doesn't make sense here, */
/* and we only test for NULL. */
-#ifndef GC_scratch_last_end_ptr /* Never an extern any more? */
-extern ptr_t GC_scratch_last_end_ptr; /* End of GC_scratch_alloc arena */
-#endif
/* 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 */
@@ -452,7 +579,8 @@ void GC_register_dynamic_libraries()
if ((flags & (MA_BREAK | MA_STACK | MA_PHYS)) != 0) goto irrelevant;
if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE))
goto irrelevant;
- /* The latter test is empirically useless. Other than the */
+ /* The latter test is empirically useless in very old Irix */
+ /* versions. Other than the */
/* main data and stack segments, everything appears to be */
/* mapped readable, writable, executable, and shared(!!). */
/* This makes no sense to me. - HB */
@@ -465,7 +593,11 @@ void GC_register_dynamic_libraries()
# endif /* MMAP_STACKS */
limit = start + addr_map[i].pr_size;
- if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) {
+ /* The following seemed to be necessary for very old versions */
+ /* of Irix, but it has been reported to discard relevant */
+ /* segments under Irix 6.5. */
+# ifndef IRIX6
+ if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) {
/* Discard text segments, i.e. 0-offset mappings against */
/* executable files which appear to have ELF headers. */
caddr_t arg;
@@ -492,7 +624,8 @@ void GC_register_dynamic_libraries()
goto irrelevant;
}
}
- }
+ }
+# endif /* !IRIX6 */
GC_add_roots_inner(start, limit, TRUE);
irrelevant: ;
}
@@ -504,7 +637,7 @@ void GC_register_dynamic_libraries()
# endif /* USE_PROC || IRIX5 */
-# ifdef MSWIN32
+# if defined(MSWIN32) || defined(MSWINCE)
# define WIN32_LEAN_AND_MEAN
# define NOSERVICE
@@ -513,86 +646,97 @@ void GC_register_dynamic_libraries()
/* We traverse the entire address space and register all segments */
/* that could possibly have been written to. */
- DWORD GC_allocation_granularity;
extern GC_bool GC_is_heap_base (ptr_t p);
# ifdef WIN32_THREADS
extern void GC_get_next_stack(char *start, char **lo, char **hi);
-# endif
-
- void GC_cond_add_roots(char *base, char * limit)
- {
- char dummy;
- char * stack_top
- = (char *) ((word)(&dummy) & ~(GC_allocation_granularity-1));
- if (base == limit) return;
-# ifdef WIN32_THREADS
+ void GC_cond_add_roots(char *base, char * limit)
{
- char * curr_base = base;
- char * next_stack_lo;
- char * next_stack_hi;
-
- for(;;) {
- GC_get_next_stack(curr_base, &next_stack_lo, &next_stack_hi);
- if (next_stack_lo >= limit) break;
- GC_add_roots_inner(curr_base, next_stack_lo, TRUE);
- curr_base = next_stack_hi;
- }
- if (curr_base < limit) GC_add_roots_inner(curr_base, limit, TRUE);
+ char * curr_base = base;
+ char * next_stack_lo;
+ char * next_stack_hi;
+
+ if (base == limit) return;
+ for(;;) {
+ GC_get_next_stack(curr_base, &next_stack_lo, &next_stack_hi);
+ if (next_stack_lo >= limit) break;
+ GC_add_roots_inner(curr_base, next_stack_lo, TRUE);
+ curr_base = next_stack_hi;
+ }
+ if (curr_base < limit) GC_add_roots_inner(curr_base, limit, TRUE);
}
-# else
- if (limit > stack_top && base < GC_stackbottom) {
- /* Part of the stack; ignore it. */
- return;
- }
- GC_add_roots_inner(base, limit, TRUE);
-# endif
- }
-
+# else
+ void GC_cond_add_roots(char *base, char * limit)
+ {
+ char dummy;
+ char * stack_top
+ = (char *) ((word)(&dummy) & ~(GC_sysinfo.dwAllocationGranularity-1));
+ if (base == limit) return;
+ if (limit > stack_top && base < GC_stackbottom) {
+ /* Part of the stack; ignore it. */
+ return;
+ }
+ GC_add_roots_inner(base, limit, TRUE);
+ }
+# endif
+
+# ifndef MSWINCE
extern GC_bool GC_win32s;
+# endif
void GC_register_dynamic_libraries()
{
MEMORY_BASIC_INFORMATION buf;
- SYSTEM_INFO sysinfo;
DWORD result;
DWORD protect;
LPVOID p;
char * base;
char * limit, * new_limit;
-
- if (GC_win32s) return;
- GetSystemInfo(&sysinfo);
- base = limit = p = sysinfo.lpMinimumApplicationAddress;
- GC_allocation_granularity = sysinfo.dwAllocationGranularity;
- while (p < sysinfo.lpMaximumApplicationAddress) {
+
+# ifdef MSWIN32
+ if (GC_win32s) return;
+# endif
+ base = limit = p = GC_sysinfo.lpMinimumApplicationAddress;
+# if defined(MSWINCE) && !defined(_WIN32_WCE_EMULATION)
+ /* Only the first 32 MB of address space belongs to the current process */
+ while (p < (LPVOID)0x02000000) {
result = VirtualQuery(p, &buf, sizeof(buf));
- if (result != sizeof(buf)) {
- ABORT("Weird VirtualQuery result");
- }
- new_limit = (char *)p + buf.RegionSize;
- protect = buf.Protect;
- if (buf.State == MEM_COMMIT
- && (protect == PAGE_EXECUTE_READWRITE
- || protect == PAGE_READWRITE)
- && !GC_is_heap_base(buf.AllocationBase)) {
- if ((char *)p == limit) {
- limit = new_limit;
- } else {
- GC_cond_add_roots(base, limit);
- base = p;
- limit = new_limit;
- }
- }
+ if (result == 0) {
+ /* Page is free; advance to the next possible allocation base */
+ new_limit = (char *)
+ (((DWORD) p + GC_sysinfo.dwAllocationGranularity)
+ & ~(GC_sysinfo.dwAllocationGranularity-1));
+ } else
+# else
+ while (p < GC_sysinfo.lpMaximumApplicationAddress) {
+ result = VirtualQuery(p, &buf, sizeof(buf));
+# endif
+ {
+ if (result != sizeof(buf)) {
+ ABORT("Weird VirtualQuery result");
+ }
+ new_limit = (char *)p + buf.RegionSize;
+ protect = buf.Protect;
+ if (buf.State == MEM_COMMIT
+ && (protect == PAGE_EXECUTE_READWRITE
+ || protect == PAGE_READWRITE)
+ && !GC_is_heap_base(buf.AllocationBase)) {
+ if ((char *)p != limit) {
+ GC_cond_add_roots(base, limit);
+ base = p;
+ }
+ limit = new_limit;
+ }
+ }
if (p > (LPVOID)new_limit /* overflow */) break;
p = (LPVOID)new_limit;
}
GC_cond_add_roots(base, limit);
}
-#endif /* MSWIN32 */
-
+#endif /* MSWIN32 || MSWINCE */
+
#if defined(ALPHA) && defined(OSF1)
#include <loader.h>