X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=src%2Fmm%2Fboehm-gc%2Fdyn_load.c;h=b46ba0401e644c82a4dc55e2b169972ce9b4facc;hb=c83bff94e40ee0a218a39931af17814d1a42cb5c;hp=70fd0e0bc015fd40f98214138ff8fac9807e87a1;hpb=c1ce9643c18b5f133568529fdf19e21e06bc2adc;p=cacao.git diff --git a/src/mm/boehm-gc/dyn_load.c b/src/mm/boehm-gc/dyn_load.c index 70fd0e0bc..b46ba0401 100644 --- a/src/mm/boehm-gc/dyn_load.c +++ b/src/mm/boehm-gc/dyn_load.c @@ -26,9 +26,6 @@ * None of this is safe with dlclose and incremental collection. * But then not much of anything is safe in the presence of dlclose. */ - -#include "config.h" - #if (defined(__linux__) || defined(__GLIBC__) || defined(__GNU__)) \ && !defined(_GNU_SOURCE) /* Can't test LINUX, since this must be defined before other includes */ @@ -53,11 +50,6 @@ # undef GC_must_restore_redefined_dlopen # endif -/* A user-supplied routine that is called to determine if a DSO must - be scanned by the gc. */ -static int (*GC_has_static_roots)(const char *, void *, size_t); - - #if (defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE)) \ && !defined(PCR) #if !defined(SOLARISDL) && !defined(IRIX5) && \ @@ -124,7 +116,7 @@ static int (*GC_has_static_roots)(const char *, void *, size_t); #endif static struct link_map * -GC_FirstDLOpenedLinkMap() +GC_FirstDLOpenedLinkMap(void) { extern ElfW(Dyn) _DYNAMIC; ElfW(Dyn) *dp; @@ -141,7 +133,7 @@ GC_FirstDLOpenedLinkMap() if( dynStructureAddr == 0 ) { void* startupSyms = dlopen(0, RTLD_LAZY); dynStructureAddr = (ElfW(Dyn)*)dlsym(startupSyms, "_DYNAMIC"); - } + } # else dynStructureAddr = &_DYNAMIC; # endif @@ -177,7 +169,7 @@ GC_FirstDLOpenedLinkMap() # endif # ifndef USE_PROC_FOR_LIBRARIES -void GC_register_dynamic_libraries() +void GC_register_dynamic_libraries(void) { struct link_map *lm = GC_FirstDLOpenedLinkMap(); @@ -242,7 +234,37 @@ char *GC_parse_map_entry(char *buf_ptr, ptr_t *start, ptr_t *end, char *GC_get_maps(void); /* From os_dep.c */ -word GC_register_map_entries(char *maps) +/* Sort an array of HeapSects by start address. */ +/* Unfortunately at least some versions of */ +/* Linux qsort end up calling malloc by way of sysconf, and hence can't */ +/* be used in the collector. Hence we roll our own. Should be */ +/* reasonably fast if the array is already mostly sorted, as we expect */ +/* it to be. */ +static void sort_heap_sects(struct HeapSect *base, size_t number_of_elements) +{ + signed_word n = (signed_word)number_of_elements; + signed_word nsorted = 1; + signed_word i; + + while (nsorted < n) { + while (nsorted < n && + base[nsorted-1].hs_start < base[nsorted].hs_start) + ++nsorted; + if (nsorted == n) break; + GC_ASSERT(base[nsorted-1].hs_start > base[nsorted].hs_start); + i = nsorted - 1; + while (i >= 0 && base[i].hs_start > base[i+1].hs_start) { + struct HeapSect tmp = base[i]; + base[i] = base[i+1]; + base[i+1] = tmp; + --i; + } + GC_ASSERT(base[nsorted-1].hs_start < base[nsorted].hs_start); + ++nsorted; + } +} + +STATIC word GC_register_map_entries(char *maps) { char *prot; char *buf_ptr = maps; @@ -253,18 +275,11 @@ word GC_register_map_entries(char *maps) unsigned i; ptr_t datastart = (ptr_t)(DATASTART); - /* Compute heap bounds. FIXME: Should work if heap and roots are */ - /* interleaved? */ - least_ha = (ptr_t)(word)(-1); - greatest_ha = 0; - for (i = 0; i < GC_n_heap_sects; ++i) { - ptr_t sect_start = GC_heap_sects[i].hs_start; - ptr_t 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 < (ptr_t)GC_scratch_last_end_ptr) - greatest_ha = (ptr_t)GC_scratch_last_end_ptr; + GC_ASSERT(I_HOLD_LOCK()); + sort_heap_sects(GC_our_memory, GC_n_memory); + least_ha = GC_our_memory[0].hs_start; + greatest_ha = GC_our_memory[GC_n_memory-1].hs_start + + GC_our_memory[GC_n_memory-1].hs_bytes; for (;;) { buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, &prot, &maj_dev, 0); @@ -283,38 +298,66 @@ word GC_register_map_entries(char *maps) /* That can fail because the stack may disappear while */ /* we're marking. Thus the marker is, and has to be */ /* prepared to recover from segmentation faults. */ + if (GC_segment_is_thread_stack(start, end)) continue; - /* FIXME: REDIRECT_MALLOC actually works with threads on */ - /* LINUX/IA64 if we omit this check. The problem is that */ + + /* FIXME: NPTL squirrels */ + /* away pointers in pieces of the stack segment that we */ + /* don't scan. We work around this */ + /* by treating anything allocated by libpthread as */ + /* uncollectable, as we do in some other cases. */ + /* A specifically identified problem is that */ /* thread stacks contain pointers to dynamic thread */ /* vectors, which may be reused due to thread caching. */ - /* Currently they may not be marked if the thread is */ - /* still live. */ - /* For dead threads, we trace the whole stack, which is */ + /* They may not be marked if the thread is still live. */ + /* This specific instance should be addressed by */ + /* INCLUDE_LINUX_THREAD_DESCR, but that doesn't quite */ + /* seem to suffice. */ + /* We currently trace entire thread stacks, if they are */ + /* are currently cached but unused. This is */ /* very suboptimal for performance reasons. */ # endif /* We no longer exclude the main data segment. */ - if (start < least_ha && end > least_ha) { - end = least_ha; - } - if (start < greatest_ha && end > greatest_ha) { - start = greatest_ha; + if (end <= least_ha || start >= greatest_ha) { + /* The easy case; just trace entire segment */ + GC_add_roots_inner((char *)start, (char *)end, TRUE); + continue; } - if (start >= least_ha && end <= greatest_ha) continue; - GC_add_roots_inner((char *)start, (char *)end, TRUE); + /* Add sections that dont belong to us. */ + i = 0; + while (GC_our_memory[i].hs_start + GC_our_memory[i].hs_bytes + < start) + ++i; + GC_ASSERT(i < GC_n_memory); + if (GC_our_memory[i].hs_start <= start) { + start = GC_our_memory[i].hs_start + + GC_our_memory[i].hs_bytes; + ++i; + } + while (i < GC_n_memory && GC_our_memory[i].hs_start < end + && start < end) { + if ((char *)start < GC_our_memory[i].hs_start) + GC_add_roots_inner((char *)start, + GC_our_memory[i].hs_start, TRUE); + start = GC_our_memory[i].hs_start + + GC_our_memory[i].hs_bytes; + ++i; + } + if (start < end) + GC_add_roots_inner((char *)start, (char *)end, TRUE); } } return 1; } -void GC_register_dynamic_libraries() +void GC_register_dynamic_libraries(void) { if (!GC_register_map_entries(GC_get_maps())) ABORT("Failed to read /proc for library registration."); } /* We now take care of the main data segment ourselves: */ -GC_bool GC_register_main_static_data() +GC_bool GC_register_main_static_data(void) { return FALSE; } @@ -331,11 +374,48 @@ GC_bool GC_register_main_static_data() # if (defined(LINUX) || defined (__GLIBC__)) /* Are others OK here, too? */ \ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \ || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG))) - /* We have the header files for a glibc that includes dl_iterate_phdr. */ /* It may still not be available in the library on the target system. */ /* Thus we also treat it as a weak symbol. */ #define HAVE_DL_ITERATE_PHDR +#pragma weak dl_iterate_phdr +#endif + +# if (defined(FREEBSD) && __FreeBSD__ >= 7) +/* On the FreeBSD system, any target system at major version 7 shall */ +/* have dl_iterate_phdr; therefore, we need not make it weak as above. */ +#define HAVE_DL_ITERATE_PHDR +#endif + +#if defined(HAVE_DL_ITERATE_PHDR) + +# ifdef PT_GNU_RELRO + +/* Instead of registering PT_LOAD sections directly, we keep them */ +/* in a temporary list, and filter them by excluding PT_GNU_RELRO */ +/* segments. Processing PT_GNU_RELRO sections with */ +/* GC_exclude_static_roots instead would be superficially cleaner. But */ +/* it runs into trouble if a client registers an overlapping segment, */ +/* which unfortunately seems quite possible. */ + +#define MAX_LOAD_SEGS MAX_ROOT_SETS + +static struct load_segment { + ptr_t start; + ptr_t end; + /* Room for a second segment if we remove a RELRO segment */ + /* from the middle. */ + ptr_t start2; + ptr_t end2; +} load_segs[MAX_LOAD_SEGS]; + +static int n_load_segs; + +# endif /* PT_GNU_RELRO */ + +/* A user-supplied routine that is called to determine if a DSO must + be scanned by the gc. */ +static int (GC_CALLBACK * GC_has_static_roots)(const char *, void *, size_t); static int GC_register_dynlib_callback(info, size, ptr) struct dl_phdr_info * info; @@ -343,8 +423,8 @@ static int GC_register_dynlib_callback(info, size, ptr) void * ptr; { const ElfW(Phdr) * p; - char * start; - register int i; + ptr_t start, end; + int i; /* Make sure struct dl_phdr_info is at least as big as we need. */ if (size < offsetof (struct dl_phdr_info, dlpi_phnum) @@ -354,16 +434,57 @@ static int GC_register_dynlib_callback(info, size, ptr) p = info->dlpi_phdr; for( i = 0; i < (int)(info->dlpi_phnum); ((i++),(p++)) ) { switch( p->p_type ) { +# ifdef PT_GNU_RELRO + case PT_GNU_RELRO: + /* This entry is known to be constant and will eventually be remapped + read-only. However, the address range covered by this entry is + typically a subset of a previously encountered `LOAD' segment, so + we need to exclude it. */ + { + int j; + + start = ((ptr_t)(p->p_vaddr)) + info->dlpi_addr; + end = start + p->p_memsz; + for (j = n_load_segs; --j >= 0; ) { + if (start >= load_segs[j].start && start < load_segs[j].end) { + if (load_segs[j].start2 != 0) { + WARN("More than one GNU_RELRO segment per load seg\n",0); + } else { + GC_ASSERT(end <= load_segs[j].end); + /* Remove from the existing load segment */ + load_segs[j].end2 = load_segs[j].end; + load_segs[j].end = start; + load_segs[j].start2 = end; + } + break; + } + if (j == 0) WARN("Failed to find PT_GNU_RELRO segment" + " inside PT_LOAD region", 0); + } + } + + break; +# endif + case PT_LOAD: { if( !(p->p_flags & PF_W) ) break; start = ((char *)(p->p_vaddr)) + info->dlpi_addr; + end = start + p->p_memsz; 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); +# ifdef PT_GNU_RELRO + if (n_load_segs >= MAX_LOAD_SEGS) ABORT("Too many PT_LOAD segs"); + load_segs[n_load_segs].start = start; + load_segs[n_load_segs].end = end; + load_segs[n_load_segs].start2 = 0; + load_segs[n_load_segs].end2 = 0; + ++n_load_segs; +# else + GC_add_roots_inner(start, end, TRUE); +# endif /* PT_GNU_RELRO */ } break; default: @@ -377,14 +498,35 @@ static int GC_register_dynlib_callback(info, size, ptr) /* Return TRUE if we succeed, FALSE if dl_iterate_phdr wasn't there. */ -#pragma weak dl_iterate_phdr - -GC_bool GC_register_dynamic_libraries_dl_iterate_phdr() +GC_bool GC_register_dynamic_libraries_dl_iterate_phdr(void) { if (dl_iterate_phdr) { int did_something = 0; + +# ifdef PT_GNU_RELRO + static GC_bool excluded_segs = FALSE; + n_load_segs = 0; + if (!excluded_segs) { + GC_exclude_static_roots((ptr_t)load_segs, + (ptr_t)load_segs + sizeof(load_segs)); + excluded_segs = TRUE; + } +# endif dl_iterate_phdr(GC_register_dynlib_callback, &did_something); - if (!did_something) { + if (did_something) { +# ifdef PT_GNU_RELRO + size_t i; + + for (i = 0; i < n_load_segs; ++i) { + if (load_segs[i].end > load_segs[i].start) { + GC_add_roots_inner(load_segs[i].start, load_segs[i].end, TRUE); + } + if (load_segs[i].end2 > load_segs[i].start2) { + GC_add_roots_inner(load_segs[i].start2, load_segs[i].end2, TRUE); + } + } +# endif + } else { /* dl_iterate_phdr may forget the static data segment in */ /* statically linked executables. */ GC_add_roots_inner(DATASTART, (char *)(DATAEND), TRUE); @@ -400,7 +542,7 @@ GC_bool GC_register_dynamic_libraries_dl_iterate_phdr() } /* Do we need to separately register the main static data segment? */ -GC_bool GC_register_main_static_data() +GC_bool GC_register_main_static_data(void) { return (dl_iterate_phdr == 0); } @@ -442,7 +584,7 @@ GC_bool GC_register_main_static_data() extern ElfW(Dyn) _DYNAMIC[]; static struct link_map * -GC_FirstDLOpenedLinkMap() +GC_FirstDLOpenedLinkMap(void) { ElfW(Dyn) *dp; static struct link_map *cachedResult = 0; @@ -465,7 +607,7 @@ GC_FirstDLOpenedLinkMap() } -void GC_register_dynamic_libraries() +void GC_register_dynamic_libraries(void) { struct link_map *lm; @@ -520,7 +662,7 @@ void GC_register_dynamic_libraries() # define IRIX6 #endif -extern void * GC_roots_present(); +extern void * GC_roots_present(ptr_t); /* The type is a lie, since the real type doesn't make sense here, */ /* and we only test for NULL. */ @@ -528,7 +670,7 @@ extern void * GC_roots_present(); /* 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. */ -void GC_register_dynamic_libraries() +void GC_register_dynamic_libraries(void) { static int fd = -1; char buf[30]; @@ -539,7 +681,7 @@ void GC_register_dynamic_libraries() long flags; ptr_t start; ptr_t limit; - ptr_t heap_start = (ptr_t)HEAP_START; + ptr_t heap_start = HEAP_START; ptr_t heap_end = heap_start; # ifdef SOLARISDL @@ -547,7 +689,7 @@ void GC_register_dynamic_libraries() # endif /* SOLARISDL */ if (fd < 0) { - sprintf(buf, "/proc/%d", getpid()); + sprintf(buf, "/proc/%ld", (long)getpid()); /* The above generates a lint complaint, since pid_t varies. */ /* It's unclear how to improve this. */ fd = open(buf, O_RDONLY); @@ -566,7 +708,7 @@ void GC_register_dynamic_libraries() (current_sz * sizeof(prmap_t))); } if (ioctl(fd, PIOCMAP, addr_map) < 0) { - GC_err_printf("fd = %d, errno = %d, needed_sz = %d, addr_map = 0x%X\n", + GC_err_printf("fd = %d, errno = %d, needed_sz = %d, addr_map = %p\n", fd, errno, needed_sz, addr_map); ABORT("/proc PIOCMAP ioctl failed"); }; @@ -652,7 +794,9 @@ void GC_register_dynamic_libraries() extern GC_bool GC_is_heap_base (ptr_t p); # ifdef GC_WIN32_THREADS - extern void GC_get_next_stack(char *start, char **lo, char **hi); + extern void GC_get_next_stack(char *start, char * limit, char **lo, + char **hi); + void GC_cond_add_roots(char *base, char * limit) { char * curr_base = base; @@ -661,9 +805,10 @@ void GC_register_dynamic_libraries() if (base == limit) return; for(;;) { - GC_get_next_stack(curr_base, &next_stack_lo, &next_stack_hi); + GC_get_next_stack(curr_base, limit, &next_stack_lo, &next_stack_hi); if (next_stack_lo >= limit) break; - GC_add_roots_inner(curr_base, next_stack_lo, TRUE); + if (next_stack_lo > curr_base) + 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); @@ -685,14 +830,14 @@ void GC_register_dynamic_libraries() # ifdef MSWINCE /* Do we need to separately register the main static data segment? */ - GC_bool GC_register_main_static_data() + GC_bool GC_register_main_static_data(void) { return FALSE; } # else /* win32 */ extern GC_bool GC_no_win32_dlls; - GC_bool GC_register_main_static_data() + GC_bool GC_register_main_static_data(void) { return GC_no_win32_dlls; } @@ -716,7 +861,7 @@ void GC_register_dynamic_libraries() extern GC_bool GC_wnt; /* Is Windows NT derivative. */ /* Defined and set in os_dep.c. */ - void GC_register_dynamic_libraries() + void GC_register_dynamic_libraries(void) { MEMORY_BASIC_INFORMATION buf; size_t result; @@ -758,7 +903,7 @@ void GC_register_dynamic_libraries() * and predecessors. Hence we now also check for * that case. */ && (buf.Type == MEM_IMAGE || - !GC_wnt && buf.Type == MEM_PRIVATE)) { + (!GC_wnt && buf.Type == MEM_PRIVATE))) { # ifdef DEBUG_VIRTUALQUERY GC_dump_meminfo(&buf); # endif @@ -781,7 +926,7 @@ void GC_register_dynamic_libraries() #include -void GC_register_dynamic_libraries() +void GC_register_dynamic_libraries(void) { int status; ldr_process_t mypid; @@ -947,7 +1092,7 @@ void GC_register_dynamic_libraries() #pragma alloca #include #include -void GC_register_dynamic_libraries() +void GC_register_dynamic_libraries(void) { int len; char *ldibuf; @@ -1016,13 +1161,8 @@ static void GC_dyld_image_add(const struct GC_MACH_HEADER *hdr, intptr_t slide) const struct GC_MACH_SECTION *sec; if (GC_no_dls) return; for(i=0;isize == 0) continue; start = slide + sec->addr; end = start + sec->size; @@ -1044,13 +1184,8 @@ static void GC_dyld_image_remove(const struct GC_MACH_HEADER *hdr, unsigned long start,end,i; const struct GC_MACH_SECTION *sec; for(i=0;isize == 0) continue; start = slide + sec->addr; end = start + sec->size; @@ -1111,13 +1246,13 @@ void GC_init_dyld() { # endif if(!_dyld_bind_fully_image_containing_address((unsigned long*)GC_malloc)) - GC_abort("_dyld_bind_fully_image_containing_address failed"); + ABORT("_dyld_bind_fully_image_containing_address failed"); } } #define HAVE_REGISTER_MAIN_STATIC_DATA -GC_bool GC_register_main_static_data() +GC_bool GC_register_main_static_data(void) { /* Already done through dyld callbacks */ return FALSE; @@ -1133,14 +1268,14 @@ GC_bool GC_register_main_static_data() # include "th/PCR_ThCtl.h" # include "mm/PCR_MM.h" -void GC_register_dynamic_libraries() +void GC_register_dynamic_libraries(void) { /* Add new static data areas of dynamically loaded modules. */ { PCR_IL_LoadedFile * p = PCR_IL_GetLastLoadedFile(); PCR_IL_LoadedSegment * q; - /* Skip uncommited files */ + /* Skip uncommitted files */ while (p != NIL && !(p -> lf_commitPoint)) { /* The loading of this file has not yet been committed */ /* Hence its description could be inconsistent. */ @@ -1168,8 +1303,6 @@ void GC_register_dynamic_libraries() void GC_register_dynamic_libraries(){} -int GC_no_dynamic_loading; - #endif /* !PCR */ #endif /* !DYNAMIC_LOADING */ @@ -1177,16 +1310,17 @@ int GC_no_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() +GC_bool GC_register_main_static_data(void) { return TRUE; } /* Register a routine to filter dynamic library registration. */ -void -GC_register_has_static_roots_callback - (int (*callback)(const char *, void *, size_t)) { - GC_has_static_roots = callback; +GC_API void GC_CALL GC_register_has_static_roots_callback + (int (GC_CALLBACK * callback)(const char *, void *, size_t)) { +# ifdef HAVE_DL_ITERATE_PHDR + GC_has_static_roots = callback; +# endif } #endif /* HAVE_REGISTER_MAIN_STATIC_DATA */