EXTRA_DIST =
## more items will be succesively added below
-lib_LTLIBRARIES = libgc.la
+lib_LTLIBRARIES = libgc.la @addlibs@
+
+EXTRA_LTLIBRARIES = libgccpp.la
include_HEADERS = include/gc.h include/gc_local_alloc.h \
include/gc_pthread_redirects.h include/gc_config_macros.h \
EXTRA_libgc_la_SOURCES = alpha_mach_dep.S \
mips_sgi_mach_dep.S mips_ultrix_mach_dep.s powerpc_macosx_mach_dep.s \
rs6000_mach_dep.s sparc_mach_dep.S sparc_netbsd_mach_dep.s \
- sparc_sunos4_mach_dep.s ia64_save_regs_in_stack.s gc_cpp.cc
+ sparc_sunos4_mach_dep.s ia64_save_regs_in_stack.s
+
+libgccpp_la_SOURCES = gc_cpp.cc
+libgccpp_la_LIBADD = $(THREADLIBS)
+libgccpp_la_LDFLAGS = -version-info 1:2:0
EXTRA_DIST += alpha_mach_dep.S mips_sgi_mach_dep.S sparc_mach_dep.S
gctest_SOURCES = tests/test.c
gctest_LDADD = ./libgc.la $(THREADLIBS) $(EXTRA_TEST_LIBS)
test_cpp_SOURCES = tests/test_cpp.cc
-test_cpp_LDADD = ./libgc.la $(THREADLIBS) $(EXTRA_TEST_LIBS)
-TESTS_ENVIRONMENT = LD_LIBRARY_PATH=../../gcc
+test_cpp_LDADD = ./libgc.la ./libgccpp.la $(THREADLIBS) $(EXTRA_TEST_LIBS)
+
TESTS = gctest @addtests@
+test_cpp$(EXEEXT): test_cpp.o
+ $(LIBTOOL) --mode=link $(CXX) $(AM_CFLAGS) $(MY_CFLAGS) \
+ test_cpp.o $(test_cpp_LDADD) $(LDFLAGS) -o $@
+
## FIXME: relies on internal code generated by automake.
all_objs = @addobjs@ $(libgc_la_OBJECTS)
$(all_objs) : include/private/gcconfig.h include/private/gc_priv.h \
THREADLIBS = @THREADLIBS@
VERSION = @VERSION@
addincludes = @addincludes@
+addlibs = @addlibs@
addobjs = @addobjs@
addtests = @addtests@
am__include = @am__include@
cord/de_win.h cord/de_win.RC\
libtool.m4
-lib_LTLIBRARIES = libgc.la
+lib_LTLIBRARIES = libgc.la @addlibs@
+
+EXTRA_LTLIBRARIES = libgccpp.la
include_HEADERS = include/gc.h include/gc_local_alloc.h \
include/gc_pthread_redirects.h include/gc_config_macros.h \
EXTRA_libgc_la_SOURCES = alpha_mach_dep.S \
mips_sgi_mach_dep.S mips_ultrix_mach_dep.s powerpc_macosx_mach_dep.s \
rs6000_mach_dep.s sparc_mach_dep.S sparc_netbsd_mach_dep.s \
- sparc_sunos4_mach_dep.s ia64_save_regs_in_stack.s gc_cpp.cc
+ sparc_sunos4_mach_dep.s ia64_save_regs_in_stack.s
+
+libgccpp_la_SOURCES = gc_cpp.cc
+libgccpp_la_LIBADD = $(THREADLIBS)
+libgccpp_la_LDFLAGS = -version-info 1:2:0
AM_CXXFLAGS = @GC_CFLAGS@
AM_CFLAGS = @GC_CFLAGS@
gctest_SOURCES = tests/test.c
gctest_LDADD = ./libgc.la $(THREADLIBS) $(EXTRA_TEST_LIBS)
test_cpp_SOURCES = tests/test_cpp.cc
-test_cpp_LDADD = ./libgc.la $(THREADLIBS) $(EXTRA_TEST_LIBS)
-TESTS_ENVIRONMENT = LD_LIBRARY_PATH=../../gcc
+test_cpp_LDADD = ./libgc.la ./libgccpp.la $(THREADLIBS) $(EXTRA_TEST_LIBS)
+
TESTS = gctest @addtests@
all_objs = @addobjs@ $(libgc_la_OBJECTS)
reclaim.lo solaris_pthreads.lo solaris_threads.lo specific.lo \
stubborn.lo typd_mlc.lo backgraph.lo win32_threads.lo
libgc_la_OBJECTS = $(am_libgc_la_OBJECTS)
+libgccpp_la_DEPENDENCIES =
+am_libgccpp_la_OBJECTS = gc_cpp.lo
+libgccpp_la_OBJECTS = $(am_libgccpp_la_OBJECTS)
EXTRA_PROGRAMS = test_cpp$(EXEEXT)
check_PROGRAMS = gctest$(EXEEXT) @addtests@
am_gctest_OBJECTS = test.$(OBJEXT)
gctest_LDFLAGS =
am_test_cpp_OBJECTS = test_cpp.$(OBJEXT)
test_cpp_OBJECTS = $(am_test_cpp_OBJECTS)
-test_cpp_DEPENDENCIES = ./libgc.la
+test_cpp_DEPENDENCIES = ./libgc.la ./libgccpp.la
test_cpp_LDFLAGS =
SCRIPTS = $(dist_noinst_SCRIPTS)
CXXLINK = $(LIBTOOL) --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
DIST_SOURCES = $(libgc_la_SOURCES) $(EXTRA_libgc_la_SOURCES) \
- $(gctest_SOURCES) $(test_cpp_SOURCES)
+ $(libgccpp_la_SOURCES) $(gctest_SOURCES) $(test_cpp_SOURCES)
HEADERS = $(dist_noinst_HEADERS) $(include_HEADERS)
aclocal.m4 config.guess config.sub configure configure.in \
depcomp install-sh ltconfig ltmain.sh missing mkinstalldirs
DIST_SUBDIRS = $(SUBDIRS)
-SOURCES = $(libgc_la_SOURCES) $(EXTRA_libgc_la_SOURCES) $(gctest_SOURCES) $(test_cpp_SOURCES)
+SOURCES = $(libgc_la_SOURCES) $(EXTRA_libgc_la_SOURCES) $(libgccpp_la_SOURCES) $(gctest_SOURCES) $(test_cpp_SOURCES)
all: all-recursive
rm -f "$${dir}/so_locations"; \
done
libgc.la: $(libgc_la_OBJECTS) $(libgc_la_DEPENDENCIES)
- $(CXXLINK) -rpath $(libdir) $(libgc_la_LDFLAGS) $(libgc_la_OBJECTS) $(libgc_la_LIBADD) $(LIBS)
+ $(LINK) -rpath $(libdir) $(libgc_la_LDFLAGS) $(libgc_la_OBJECTS) $(libgc_la_LIBADD) $(LIBS)
+libgccpp.la: $(libgccpp_la_OBJECTS) $(libgccpp_la_DEPENDENCIES)
+ $(CXXLINK) $(libgccpp_la_LDFLAGS) $(libgccpp_la_OBJECTS) $(libgccpp_la_LIBADD) $(LIBS)
clean-checkPROGRAMS:
@list='$(check_PROGRAMS)'; for p in $$list; do \
@rm -f gctest$(EXEEXT)
$(LINK) $(gctest_LDFLAGS) $(gctest_OBJECTS) $(gctest_LDADD) $(LIBS)
test_cpp.$(OBJEXT): tests/test_cpp.cc
-test_cpp$(EXEEXT): $(test_cpp_OBJECTS) $(test_cpp_DEPENDENCIES)
- @rm -f test_cpp$(EXEEXT)
- $(CXXLINK) $(test_cpp_LDFLAGS) $(test_cpp_OBJECTS) $(test_cpp_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT) core *.core
# Using $< in the above seems to fail with the HP/UX on Itanium make.
test_cpp.o: $(srcdir)/tests/test_cpp.cc
$(COMPILE) -c $(srcdir)/tests/test_cpp.cc
+
+test_cpp$(EXEEXT): test_cpp.o
+ $(LIBTOOL) --mode=link $(CXX) $(AM_CFLAGS) $(MY_CFLAGS) \
+ test_cpp.o $(test_cpp_LDADD) $(LDFLAGS) -o $@
$(all_objs) : include/private/gcconfig.h include/private/gc_priv.h \
include/private/gc_hdrs.h include/gc.h include/gc_gcj.h \
include/gc_pthread_redirects.h include/gc_config_macros.h \
GC_bool GC_need_full_gc = FALSE;
/* Need full GC do to heap growth. */
+#ifdef THREADS
+ GC_bool GC_world_stopped = FALSE;
+# define IF_THREADS(x) x
+#else
+# define IF_THREADS(x)
+#endif
+
word GC_used_heap_size_after_full = 0;
char * GC_copyright[] =
GC_cond_register_dynamic_libraries();
# endif
STOP_WORLD();
+ IF_THREADS(GC_world_stopped = TRUE);
# ifdef CONDPRINT
if (GC_print_stats) {
GC_printf1("--> Marking for collection %lu ",
}
# endif
GC_deficit = i; /* Give the mutator a chance. */
+ IF_THREADS(GC_world_stopped = FALSE);
START_WORLD();
return(FALSE);
}
(*GC_check_heap)();
}
+ IF_THREADS(GC_world_stopped = FALSE);
START_WORLD();
# ifdef PRINTTIMES
GET_TIME(current_time);
#! /bin/sh
# From configure.in Revision: 1.2 .
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.53 for gc 6.2alpha4.
+# Generated by GNU Autoconf 2.53 for gc 6.2alpha5.
#
# Report bugs to <Hans.Boehm@hp.com>.
#
# Identity of this package.
PACKAGE_NAME='gc'
PACKAGE_TARNAME='gc'
-PACKAGE_VERSION='6.2alpha4'
-PACKAGE_STRING='gc 6.2alpha4'
+PACKAGE_VERSION='6.2alpha5'
+PACKAGE_STRING='gc 6.2alpha5'
PACKAGE_BUGREPORT='Hans.Boehm@hp.com'
ac_unique_file="gcj_mlc.c"
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures gc 6.2alpha4 to adapt to many kinds of systems.
+\`configure' configures gc 6.2alpha5 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of gc 6.2alpha4:";;
+ short | recursive ) echo "Configuration of gc 6.2alpha5:";;
esac
cat <<\_ACEOF
(and sometimes confusing) to the casual installer
--enable-threads=TYPE choose threading package
--enable-parallel-mark parallelize marking and free list construction
- --enable-cplusplus include C++ support in GC library and include directory
+ --enable-cplusplus install C++ support
--enable-shared=PKGS build shared libraries default=yes
--enable-static=PKGS build static libraries default=yes
--enable-fast-install=PKGS optimize for fast installation default=yes
test -n "$ac_init_help" && exit 0
if $ac_init_version; then
cat <<\_ACEOF
-gc configure 6.2alpha4
+gc configure 6.2alpha5
generated by GNU Autoconf 2.53
Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by gc $as_me 6.2alpha4, which was
+It was created by gc $as_me 6.2alpha5, which was
generated by GNU Autoconf 2.53. Invocation command line was
$ $0 $@
# Define the identity of the package.
PACKAGE=gc
- VERSION=6.2alpha4
+ VERSION=6.2alpha5
cat >>confdefs.h <<_ACEOF
esac
+# I'm not too familiar with autoconf, is this the best way to do this? -Brian
+case "$host" in
+ *-*-darwin*) ;;
+ *)
echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6
EXTRA_TEST_LIBS="$EXTRA_TEST_LIBS -ldl"
fi
+ ;;
+esac
+
target_all=libgc.la
fi;
addobjs=
+addlibs=
addincludes=
addtests=
CXXINCLUDES=
esac
if test "${enable_cplusplus}" = yes; then
- addobjs="$addobjs gc_cpp.lo"
+ addlibs="$addlibs libgccpp.la"
addincludes="$addincludes include/gc_cpp.h include/gc_allocator.h"
addtests="$addtests test_cpp"
fi
+
# Check whether --enable-static or --disable-static was given.
if test "${enable_static+set}" = set; then
enableval="$enable_static"
case $host in
*-*-irix6*)
# Find out which ABI we are using.
- echo '#line 5338 "configure"' > conftest.$ac_ext
+ echo '#line 5347 "configure"' > conftest.$ac_ext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5
ac_status=$?
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -o out/conftest2.$ac_objext"
compiler_c_o=no
-if { (eval echo configure:5874: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>out/conftest.err; } && test -s out/conftest2.$ac_objext; then
+if { (eval echo configure:5883: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>out/conftest.err; } && test -s out/conftest2.$ac_objext; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
if test -s out/conftest.err; then
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
-#line 7667 "configure"
+#line 7676 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
-#line 7765 "configure"
+#line 7774 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
} >&5
cat >&5 <<_CSEOF
-This file was extended by gc $as_me 6.2alpha4, which was
+This file was extended by gc $as_me 6.2alpha5, which was
generated by GNU Autoconf 2.53. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
-gc config.status 6.2alpha4
+gc config.status 6.2alpha5
configured by $0, generated by GNU Autoconf 2.53,
with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
s,@CXXINCLUDES@,$CXXINCLUDES,;t t
s,@addobjs@,$addobjs,;t t
s,@addincludes@,$addincludes,;t t
+s,@addlibs@,$addlibs,;t t
s,@addtests@,$addtests,;t t
s,@LN_S@,$LN_S,;t t
s,@ECHO@,$ECHO,;t t
# Initialization
# ==============
-AC_INIT(gc,6.2alpha4,Hans.Boehm@hp.com)
+AC_INIT(gc,6.2alpha5,Hans.Boehm@hp.com)
## version must conform to [0-9]+[.][0-9]+(alpha[0-9]+)?
AC_CONFIG_SRCDIR(gcj_mlc.c)
AC_CANONICAL_TARGET
)
AC_ARG_ENABLE(cplusplus,
-[ --enable-cplusplus include C++ support in GC library and include directory],
+[ --enable-cplusplus install C++ support],
)
INCLUDES=-I${srcdir}/include
)
addobjs=
+addlibs=
addincludes=
addtests=
CXXINCLUDES=
esac
if test "${enable_cplusplus}" = yes; then
- addobjs="$addobjs gc_cpp.lo"
+ addlibs="$addlibs libgccpp.la"
addincludes="$addincludes include/gc_cpp.h include/gc_allocator.h"
addtests="$addtests test_cpp"
fi
addobjs="$addobjs $machdep"
AC_SUBST(addobjs)
AC_SUBST(addincludes)
+AC_SUBST(addlibs)
AC_SUBST(addtests)
AC_PROG_LIBTOOL
result->len = result_len;
result->left = x;
result->right = y;
- if (depth > MAX_DEPTH) {
+ if (depth >= MAX_DEPTH) {
return(CORD_balance((CORD)result));
} else {
return((CORD) result);
result->len = result_len;
result->left = x;
result->right = y;
- return((CORD) result);
+ if (depth >= MAX_DEPTH) {
+ return(CORD_balance((CORD)result));
+ } else {
+ return((CORD) result);
+ }
}
}
void GC_check_heap_proc()
{
# ifndef SMALL_CONFIG
- if (sizeof(oh) & (2 * sizeof(word) - 1) != 0) {
- ABORT("Alignment problem: object header has inappropriate size\n");
- }
+# ifdef ALIGN_DOUBLE
+ GC_STATIC_ASSERT((sizeof(oh) & (2 * sizeof(word) - 1)) == 0);
+# else
+ GC_STATIC_ASSERT((sizeof(oh) & (sizeof(word) - 1)) == 0);
+# endif
# endif
GC_apply_to_all_blocks(GC_check_heap_block, (word)0);
}
THREADLIBS = @THREADLIBS@
VERSION = @VERSION@
addincludes = @addincludes@
+addlibs = @addlibs@
addobjs = @addobjs@
addtests = @addtests@
am__include = @am__include@
collector. (If you are concerned about such things, I recommend you look
at the notice in config.guess or ltmain.sh.)
-This is version 6.2alpha3 of a conservative garbage collector for C and C++.
+This is version 6.2alpha5 of a conservative garbage collector for C and C++.
You might find a more recent version of this at
when it should have called the lower case version, since it was
explicitly computing a base pointer.
+Since 6.2alpha4:
+ - GC_invoke_finalizers could, under rare conditions, set
+ GC_finalizer_mem_freed to an essentially random value. This could
+ possibly cause unbounded heap growth for long-running applications
+ under some conditions. (The bug was introduced in 6.1alpha5, and
+ is not in gcc3.3. Thanks to Ben Hutchings for finding it.)
+ - Attempted to sanitize the various DLL macros. GC_USE_DLL disappeared.
+ GC_DLL is used instead. All internal tests are now on GC_DLL.
+ README.macros is now more precise about the intended meaning.
+ - Include DllMain in the multithreaded win32 version only if the
+ collector is actually built as a dll. (Thanks to Mohan Embar for
+ a version of the patch.)
+ - Hide the cygwin threadAttach/Detach functions. They were violating our
+ namespace rules.
+ - Fixed an assertion in GC_check_heap_proc. Added GC_STATIC_ASSERT.
+ (Thanks again to Ben Hutchings.)
+ - Removed some obsolete definitions for Linux/PowerPC in gcconfig.h.
+ - CORD_cat was not rebalancing unbalanced trees in some cases, violating
+ a CORD invariant. Also tweaked the rebalancing rule for
+ CORD_cat_char_star. (Thanks to Alexandr Petrosian for the bug report
+ and patch.)
+ - Added hand-coded structured exception handling support to mark.c.
+ This should enable support of dynamic libraries under win32 with
+ gcc-compiled code. (Thanks to Ranjit Mathew for the patch.)
+ Turned on dynamic library scanning for win32/gcc.
+ - Removed some remnants of read wrapping. (Thanks to Kenneth Schalk.)
+ GC_USE_LD_WRAP ws probably broken in recent versions.
+ - The build could fail on some platforms since gcconfig.h could include
+ declarations mentioning ptr_t, which was not defined, e.g. when if_mach
+ was built. (Thanks to Yann Dirson for pointing this out.) Also
+ cleaned up tests for GC_PRIVATE_H in gcconfig.h a bit.
+ - The GC_LOOP_ON_ABORT environment variable interfered with incremental
+ collection, since the write fault handler was erroneously overridden.
+ Handlers are now set up in the correct order.
+ - It used to be possible to call GC_mark_thread_local_free_lists() while
+ the world was not stopped during an incremental GC. This was not safe.
+ Fortunately, it was also unnecessary. Added GC_world_stopped flag
+ to avoid it. (This caused occasional crashes in GC_set_fl_marks
+ with thread local allocation and incremental GC. This probably happened
+ primarily on old, slow multiprocessors.)
+ - Allowed overriding of MAX_THREADS in win32_threads.c from the build
+ command line. (Patch from Yannis Bres.)
+ - Taught the IA64/linux code to determine the register backing store base from
+ /proc/self/maps after checking the __libc symbol, but before guessing.
+ (__libc symbols are on the endangered list, and the guess is likely to not
+ always be right for 2.6 kernels.) Restructured the code to read and parse
+ /proc/self/maps so it only exists in one place (all platforms).
+ - The -DUSE_PROC_FOR_LIBRARIES code was broken on Linux. It claimed that it
+ also registered the main data segment, but didn't actually do so. (I don't
+ think anyone actually uses this configuration, but ...)
+ - Made another attempt to get --enablecplusplus to do the right thing.
+ Since there are unavoidable problems with C programs linking against a
+ dynamic library that includes C++ code, I separated out the c++ code into
+ libgccpp.
+
+
To do:
- MacOSX thread support still appears to be a bit unreliable.
- A dynamic libgc.so references dlopen unconditionally, but doesn't link
__declspec(dllexport) needs to be added to declarations
to support the case in which the collector is in a dll.
-GC_DLL User-settable macro that forces the effect of _DLL.
+GC_DLL User-settable macro that forces the effect of _DLL. Set
+ by gc.h if _DLL is defined and GC_NOT_DLL is undefined.
+ This is the macro that is tested internally to determine
+ whether the GC is in its own dynamic library. May need
+ to be set by clients before including gc.h. Note that
+ inside the GC implementation it indicates that the
+ collector is in its own dynamic library, should export
+ its symbols, etc. But in clients it indicates that the
+ GC resides in a different DLL, its entry points should
+ be referenced accordingly, and precautions may need to
+ be taken to properly deal with statically allocated
+ variables in the main program. Used only for MS Windows.
GC_NOT_DLL User-settable macro that overrides _DLL, e.g. if dynamic
libraries are used, but the collector is in a static library.
All programs using gc should be compiled with 4-byte alignment.
For further explanations on this see comments about Borland.
-If gc compiled as dll, the macro ``GC_DLL'' should be defined before
+If the gc is compiled as dll, the macro ``GC_DLL'' should be defined before
including "gc.h" (for example, with -DGC_DLL compiler option). It's
important, otherwise resulting programs will not run.
/* 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 */
# 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;
+}
+
+void GC_register_dynamic_libraries()
+{
+ if (!GC_apply_to_maps(GC_register_map_entries))
+ ABORT("Failed to read /proc for library registration.");
}
/* We now take care of the main data segment ourselves: */
}
# define HAVE_REGISTER_MAIN_STATIC_DATA
-//
-// 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
-//
-// For 64 bit ABIs:
-// 0 17 34 56
-//
-// 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'.)
-//
-#if CPP_WORDSZ == 32
-# define OFFSET_MAP_START 0
-# define OFFSET_MAP_END 9
-# define OFFSET_MAP_PROT 18
-# define OFFSET_MAP_MAJDEV 32
-# define ADDR_WIDTH 8
-#endif
-
-#if CPP_WORDSZ == 64
-# define OFFSET_MAP_START 0
-# define OFFSET_MAP_END 17
-# define OFFSET_MAP_PROT 34
-# define OFFSET_MAP_MAJDEV 56
-# define ADDR_WIDTH 16
-#endif
-
-static char *parse_map_entry(char *buf_ptr, word *start, word *end,
- char *prot_buf, unsigned int *maj_dev)
-{
- int i;
- 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+ADDR_WIDTH] = '\0';
- *start = strtoul(tok, NULL, 16);
-
- tok = buf_ptr+OFFSET_MAP_END;
- buf_ptr[OFFSET_MAP_END+ADDR_WIDTH] = '\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;
-}
#endif /* USE_PROC_FOR_LIBRARIES */
struct finalizable_object * curr_fo;
int count = 0;
word mem_freed_before;
- GC_bool first_time = TRUE;
DCL_LOCK_STATE;
while (GC_finalize_now != 0) {
DISABLE_SIGNALS();
LOCK();
# endif
- if (first_time) {
+ if (count == 0) {
mem_freed_before = GC_mem_freed;
- first_time = FALSE;
}
curr_fo = GC_finalize_now;
# ifdef THREADS
GC_free((GC_PTR)curr_fo);
# endif
}
- if (mem_freed_before != GC_mem_freed) {
+ if (count != 0 && mem_freed_before != GC_mem_freed) {
LOCK();
GC_finalizer_mem_freed += (GC_mem_freed - mem_freed_before);
UNLOCK();
THREADLIBS = @THREADLIBS@
VERSION = @VERSION@
addincludes = @addincludes@
+addlibs = @addlibs@
addobjs = @addobjs@
addtests = @addtests@
am__include = @am__include@
/* Add a displacement to the set of those considered valid by the */
/* collector. GC_register_displacement(n) means that if p was returned */
/* by GC_malloc, then (char *)p + n will be considered to be a valid */
-/* pointer to n. N must be small and less than the size of p. */
+/* pointer to p. N must be small and less than the size of p. */
/* (All pointers to the interior of objects from the stack are */
/* considered valid in any case. This applies to heap objects and */
/* static data.) */
/* Preferably, this should be called before any other GC procedures. */
/* Calling it later adds to the probability of excess memory */
/* retention. */
-/* This is a no-op if the collector was compiled with recognition of */
+/* This is a no-op if the collector has recognition of */
/* arbitrary interior pointers enabled, which is now the default. */
GC_API void GC_register_displacement GC_PROTO((GC_word n));
* and does then use DllMain to keep track of thread creations. But new code
* should be built to call GC_CreateThread.
*/
- HANDLE WINAPI GC_CreateThread(
+ GC_API HANDLE GC_CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );
# define GC_INIT() { extern end, etext; \
GC_noop(&end, &etext); }
#else
-# if defined(__CYGWIN32__) && defined(GC_USE_DLL) || defined (_AIX)
+# if defined(__CYGWIN32__) && defined(GC_DLL) || defined (_AIX)
/*
* Similarly gnu-win32 DLLs need explicit initialization from
* the main program, as does AIX.
typedef long ptrdiff_t; /* ptrdiff_t is not defined */
# endif
-#if defined(__MINGW32__) && defined(_DLL) && !defined(GC_NOT_DLL)
+#if defined(_DLL) && !defined(GC_NOT_DLL) && !defined(GC_DLL)
+# define GC_DLL
+#endif
+
+#if defined(__MINGW32__) && defined(GC_DLL)
# ifdef GC_BUILD
# define GC_API __declspec(dllexport)
# else
# endif
#endif
-#if (defined(__DMC__) || defined(_MSC_VER)) \
- && (defined(_DLL) && !defined(GC_NOT_DLL) \
- || defined(GC_DLL))
+#if (defined(__DMC__) || defined(_MSC_VER)) && defined(GC_DLL)
# ifdef GC_BUILD
# define GC_API extern __declspec(dllexport)
# else
{
char result;
__asm__ __volatile__("lock; cmpxchgl %2, %0; setz %1"
- : "=m"(*(addr)), "=r"(result)
- : "r" (new_val), "0"(*(addr)), "a"(old) : "memory");
+ : "+m"(*(addr)), "=r"(result)
+ : "r" (new_val), "a"(old) : "memory");
return (GC_bool) result;
}
# endif /* !GENERIC_COMPARE_AND_SWAP */
/* OFFSET_TOO_BIG if the value j would be too */
/* large to fit in the entry. (Note that the */
/* size of these entries matters, both for */
- /* space consumption and for cache utilization. */
+ /* space consumption and for cache utilization.) */
# define OFFSET_TOO_BIG 0xfe
# define OBJ_INVALID 0xff
# define MAP_ENTRY(map, bytes) (map)[bytes]
extern long GC_large_alloc_warn_suppressed;
/* Number of warnings suppressed so far. */
+#ifdef THREADS
+ extern GC_bool GC_world_stopped;
+#endif
+
/* Operations */
# ifndef abs
# define abs(x) ((x) < 0? (-(x)) : (x))
# define GC_ASSERT(expr)
# endif
+/* Check a compile time assertion at compile time. The error */
+/* message for failure is a bit baroque, but ... */
+# define GC_STATIC_ASSERT(expr) sizeof(char[(expr)? 1 : -1])
+
# if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
/* We need additional synchronization facilities from the thread */
/* support. We believe these are less performance critical */
# define GCCONFIG_H
+# ifndef GC_PRIVATE_H
+ /* Fake ptr_t declaration, just to avoid compilation errors. */
+ /* This avoids many instances if "ifndef GC_PRIVATE_H" below. */
+ typedef struct GC_undefined_struct * ptr_t;
+# endif
+
/* Machine dependent parameters. Some tuning parameters can be found */
/* near the top of gc_private.h. */
/* executable on a 64 bit kernel. */
# define LINUX_STACKBOTTOM
# define DYNAMIC_LOADING
-# undef STACK_GRAN
-# define STACK_GRAN 0x10000000
- /* Stack usually starts at 0x80000000 */
# define SEARCH_FOR_DATA_START
extern int _end[];
# define DATAEND (_end)
/* platforms as well, though it should be avoided in win32. */
# endif /* LINUX */
-# if defined(SEARCH_FOR_DATA_START) && defined(GC_PRIVATE_H)
+# if defined(SEARCH_FOR_DATA_START)
extern ptr_t GC_data_start;
# define DATASTART GC_data_start
# endif
+ GC_page_size-1)
# else
# ifdef MSWIN32
-# ifdef GC_PRIVATE_H
- extern ptr_t GC_win32_get_mem();
-# endif
+ extern ptr_t GC_win32_get_mem();
# define GET_MEM(bytes) (struct hblk *)GC_win32_get_mem(bytes)
# else
# ifdef MACOS
# endif
# else
# ifdef MSWINCE
-# ifdef GC_PRIVATE_H
- extern ptr_t GC_wince_get_mem();
-# endif
+ extern ptr_t GC_wince_get_mem();
# define GET_MEM(bytes) (struct hblk *)GC_wince_get_mem(bytes)
# else
# if defined(AMIGA) && defined(GC_AMIGA_FASTALLOC)
GC_amiga_get_mem((size_t)bytes + GC_page_size) \
+ GC_page_size-1)
# else
-# ifdef GC_PRIVATE_H
- extern ptr_t GC_unix_get_mem();
-# endif
+ extern ptr_t GC_unix_get_mem();
# define GET_MEM(bytes) (struct hblk *)GC_unix_get_mem(bytes)
# endif
# endif
void GC_restart_handler(int sig)
{
+ pthread_t my_thread = pthread_self();
GC_thread me;
if (sig != SIG_THR_RESTART) ABORT("Bad signal in suspend_handler");
/* of a thread which holds the allocation lock in order */
/* to stop the world. Thus concurrent modification of the */
/* data structure is impossible. */
- me = GC_lookup_thread(pthread_self());
+ me = GC_lookup_thread(my_thread);
me->signal = SIG_THR_RESTART;
/*
/* Caller holds allocation lock. */
void GC_stop_world()
{
- register int i;
- register int n_live_threads;
+ int i;
+ int n_live_threads;
+ int code;
/* Make sure all free list construction has stopped before we start. */
/* No new construction can start, since free list construction is */
for (i = 0; i < n_live_threads; i++) {
# ifdef GC_MACOSX_THREADS
if (KERN_SUCCESS != semaphore_wait(GC_suspend_ack_sem))
- ABORT("semaphore_wait in handler failed");
+ ABORT("semaphore_wait for handler failed");
# else
- if (0 != sem_wait(&GC_suspend_ack_sem))
- ABORT("sem_wait in handler failed");
+ if (0 != (code = sem_wait(&GC_suspend_ack_sem))) {
+ GC_err_printf1("Sem_wait returned %ld\n", (unsigned long)code);
+ ABORT("sem_wait for handler failed");
+ }
# endif
}
# ifdef PARALLEL_MARK
/* appears to be buggy in many cases. */
/* We look for lines "cpu<n>" in /proc/stat. */
# define STAT_BUF_SIZE 4096
-# if defined(GC_USE_LD_WRAP)
-# define STAT_READ __real_read
-# else
-# define STAT_READ read
-# endif
+# define STAT_READ read
+ /* If read is wrapped, this may need to be redefined to call */
+ /* the real one. */
char stat_buf[STAT_BUF_SIZE];
int f;
char c;
# include <stdio.h>
# include "private/gc_pmark.h"
+#if defined(MSWIN32) && defined(__GNUC__)
+# include <excpt.h>
+#endif
+
/* We put this here to minimize the risk of inlining. */
/*VARARGS*/
#ifdef __WATCOMC__
/* remains valid until all marking is complete. */
/* A zero value indicates that it's OK to miss some */
/* register values. */
-GC_bool GC_mark_some(cold_gc_frame)
-ptr_t cold_gc_frame;
+/* We hold the allocation lock. In the case of */
+/* incremental collection, the world may not be stopped.*/
+#ifdef MSWIN32
+ /* For win32, this is called after we establish a structured */
+ /* exception handler, in case Windows unmaps one of our root */
+ /* segments. See below. In either case, we acquire the */
+ /* allocator lock long before we get here. */
+ GC_bool GC_mark_some_inner(cold_gc_frame)
+ ptr_t cold_gc_frame;
+#else
+ GC_bool GC_mark_some(cold_gc_frame)
+ ptr_t cold_gc_frame;
+#endif
{
-#if defined(MSWIN32) && !defined(__GNUC__)
- /* Windows 98 appears to asynchronously create and remove writable */
- /* memory mappings, for reasons we haven't yet understood. Since */
- /* we look for writable regions to determine the root set, we may */
- /* try to mark from an address range that disappeared since we */
- /* started the collection. Thus we have to recover from faults here. */
- /* This code does not appear to be necessary for Windows 95/NT/2000. */
- /* Note that this code should never generate an incremental GC write */
- /* fault. */
- __try {
-#endif /* defined(MSWIN32) && !defined(__GNUC__) */
switch(GC_mark_state) {
case MS_NONE:
return(FALSE);
ABORT("GC_mark_some: bad state");
return(FALSE);
}
-#if defined(MSWIN32) && !defined(__GNUC__)
- } __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
- EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
-# ifdef CONDPRINT
- if (GC_print_stats) {
- GC_printf0("Caught ACCESS_VIOLATION in marker. "
- "Memory mapping disappeared.\n");
+}
+
+
+#ifdef MSWIN32
+
+# ifdef __GNUC__
+
+ typedef struct {
+ EXCEPTION_REGISTRATION ex_reg;
+ void *alt_path;
+ } ext_ex_regn;
+
+
+ static EXCEPTION_DISPOSITION mark_ex_handler(
+ struct _EXCEPTION_RECORD *ex_rec,
+ void *est_frame,
+ struct _CONTEXT *context,
+ void *disp_ctxt)
+ {
+ if (ex_rec->ExceptionCode == STATUS_ACCESS_VIOLATION) {
+ ext_ex_regn *xer = (ext_ex_regn *)est_frame;
+
+ /* Unwind from the inner function assuming the standard */
+ /* function prologue. */
+ /* Assumes code has not been compiled with */
+ /* -fomit-frame-pointer. */
+ context->Esp = context->Ebp;
+ context->Ebp = *((DWORD *)context->Esp);
+ context->Esp = context->Esp - 8;
+
+ /* Resume execution at the "real" handler within the */
+ /* wrapper function. */
+ context->Eip = (DWORD )(xer->alt_path);
+
+ return ExceptionContinueExecution;
+
+ } else {
+ return ExceptionContinueSearch;
+ }
+ }
+# endif /* __GNUC__ */
+
+
+ GC_bool GC_mark_some(cold_gc_frame)
+ ptr_t cold_gc_frame;
+ {
+ GC_bool ret_val;
+
+# ifndef __GNUC__
+ /* Windows 98 appears to asynchronously create and remove */
+ /* writable memory mappings, for reasons we haven't yet */
+ /* understood. Since we look for writable regions to */
+ /* determine the root set, we may try to mark from an */
+ /* address range that disappeared since we started the */
+ /* collection. Thus we have to recover from faults here. */
+ /* This code does not appear to be necessary for Windows */
+ /* 95/NT/2000. Note that this code should never generate */
+ /* an incremental GC write fault. */
+
+ __try {
+
+# else /* __GNUC__ */
+
+ /* Manually install an exception handler since GCC does */
+ /* not yet support Structured Exception Handling (SEH) on */
+ /* Win32. */
+
+ ext_ex_regn er;
+
+ er.alt_path = &&handle_ex;
+ er.ex_reg.handler = mark_ex_handler;
+ asm volatile ("movl %%fs:0, %0" : "=r" (er.ex_reg.prev));
+ asm volatile ("movl %0, %%fs:0" : : "r" (&er));
+
+# endif /* __GNUC__ */
+
+ ret_val = GC_mark_some_inner(cold_gc_frame);
+
+# ifndef __GNUC__
+
+ } __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
+ EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
+
+# else /* __GNUC__ */
+
+ /* Prevent GCC from considering the following code unreachable */
+ /* and thus eliminating it. */
+ if (er.alt_path != 0)
+ goto rm_handler;
+
+handle_ex:
+ /* Execution resumes from here on an access violation. */
+
+# endif /* __GNUC__ */
+
+# ifdef CONDPRINT
+ if (GC_print_stats) {
+ GC_printf0("Caught ACCESS_VIOLATION in marker. "
+ "Memory mapping disappeared.\n");
+ }
+# endif /* CONDPRINT */
+
+ /* We have bad roots on the stack. Discard mark stack. */
+ /* Rescan from marked objects. Redetermine roots. */
+ GC_invalidate_mark_state();
+ scan_ptr = 0;
+
+ ret_val = FALSE;
+
+# ifndef __GNUC__
+
}
-# endif /* CONDPRINT */
- /* We have bad roots on the stack. Discard mark stack. */
- /* Rescan from marked objects. Redetermine roots. */
- GC_invalidate_mark_state();
- scan_ptr = 0;
- return FALSE;
+
+# else /* __GNUC__ */
+
+rm_handler:
+ /* Uninstall the exception handler */
+ asm volatile ("mov %0, %%fs:0" : : "r" (er.ex_reg.prev));
+
+# endif /* __GNUC__ */
+
+ return ret_val;
}
-#endif /* defined(MSWIN32) && !defined(__GNUC__) */
-}
+#endif /* MSWIN32 */
GC_bool GC_mark_stack_empty()
/* Mark thread local free lists, even if their mark */
/* descriptor excludes the link field. */
+ /* If the world is not stopped, this is unsafe. It is */
+ /* also unnecessary, since we will do this again with the */
+ /* world stopped. */
# ifdef THREAD_LOCAL_ALLOC
- GC_mark_thread_local_free_lists();
+ if (GC_world_stopped) GC_mark_thread_local_free_lists();
# endif
/*
# if defined(GC_WIN32_THREADS)
# if defined(GC_PTHREADS)
pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER;
-# elif !defined(GC_NOT_DLL) && (defined(_DLL) || defined(GC_DLL))
+# elif defined(GC_DLL)
__declspec(dllexport) CRITICAL_SECTION GC_allocate_ml;
# else
CRITICAL_SECTION GC_allocate_ml;
extern void GC_setpagesize();
+
+#ifdef MSWIN32
+extern GC_bool GC_no_win32_dlls;
+#else
+# define GC_no_win32_dlls FALSE
+#endif
+
+void GC_exit_check GC_PROTO((void))
+{
+ GC_gcollect();
+}
+
+#ifdef SEARCH_FOR_DATA_START
+ extern void GC_init_linux_data_start GC_PROTO((void));
+#endif
+
#ifdef UNIX_LIKE
extern void GC_set_and_save_fault_handler GC_PROTO((void (*handler)(int)));
GC_err_printf1("Caught signal %d: looping in handler\n", sig);
for(;;);
}
-#endif
-#ifdef MSWIN32
-extern GC_bool GC_no_win32_dlls;
-#else
-# define GC_no_win32_dlls FALSE
-#endif
+static GC_bool installed_looping_handler = FALSE;
-void GC_exit_check GC_PROTO((void))
+void maybe_install_looping_handler()
{
- GC_gcollect();
+ /* Install looping handler before the write fault handler, so we */
+ /* handle write faults correctly. */
+ if (!installed_looping_handler && 0 != GETENV("GC_LOOP_ON_ABORT")) {
+ GC_set_and_save_fault_handler(looping_handler);
+ installed_looping_handler = TRUE;
+ }
}
-#ifdef SEARCH_FOR_DATA_START
- extern void GC_init_linux_data_start GC_PROTO((void));
+#else /* !UNIX_LIKE */
+
+# define maybe_install_looping_handler()
+
#endif
void GC_init_inner()
}
}
}
-# ifdef UNIX_LIKE
- if (0 != GETENV("GC_LOOP_ON_ABORT")) {
- GC_set_and_save_fault_handler(looping_handler);
- }
-# endif
+ maybe_install_looping_handler();
/* Adjust normal object descriptor for extra allocation. */
if (ALIGNMENT > GC_DS_TAGS && EXTRA_BYTES != 0) {
GC_obj_kinds[NORMAL].ok_descriptor = ((word)(-ALIGNMENT) | GC_DS_LENGTH);
# endif
}
# endif
- GC_ASSERT(sizeof (ptr_t) == sizeof(word));
- GC_ASSERT(sizeof (signed_word) == sizeof(word));
- GC_ASSERT(sizeof (struct hblk) == HBLKSIZE);
+ GC_STATIC_ASSERT(sizeof (ptr_t) == sizeof(word));
+ GC_STATIC_ASSERT(sizeof (signed_word) == sizeof(word));
+ GC_STATIC_ASSERT(sizeof (struct hblk) == HBLKSIZE);
# ifndef THREADS
# if defined(STACK_GROWS_UP) && defined(STACK_GROWS_DOWN)
ABORT(
if (GC_incremental) goto out;
GC_setpagesize();
if (GC_no_win32_dlls) goto out;
-# ifndef GC_SOLARIS_THREADS
- GC_dirty_init();
+# ifndef GC_SOLARIS_THREADS
+ maybe_install_looping_handler(); /* Before write fault handler! */
+ GC_dirty_init();
# endif
if (!GC_is_initialized) {
GC_init_inner();
# define OPT_PROT_EXEC 0
#endif
+#if defined(LINUX) && \
+ (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64) || !defined(SMALL_CONFIG))
+
+/* We need to parse /proc/self/maps, either to find dynamic libraries, */
+/* and/or to find the register backing store base (IA64). Do it once */
+/* here. */
+
+#define READ read
+
+/* Repeatedly perform a read call until the buffer is filled or */
+/* we encounter EOF. */
+ssize_t GC_repeat_read(int fd, char *buf, size_t count)
+{
+ ssize_t num_read = 0;
+ ssize_t result;
+
+ while (num_read < count) {
+ result = READ(fd, buf + num_read, count - num_read);
+ if (result < 0) return result;
+ if (result == 0) break;
+ num_read += result;
+ }
+ return num_read;
+}
+
+/*
+ * Apply fn to a buffer containing the contents of /proc/self/maps.
+ * Return the result of fn or, if we failed, 0.
+ */
+
+word GC_apply_to_maps(word (*fn)(char *))
+{
+ int f;
+ int result;
+ int maps_size;
+ char maps_temp[32768];
+ char *maps_buf;
+
+ /* 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) return 0;
+ /* 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) return 0;
+ 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) return 0;
+ maps_buf = alloca(maps_size);
+ if (NULL == maps_buf) return 0;
+ result = GC_repeat_read(f, maps_buf, maps_size);
+ if (result <= 0) return 0;
+ } else {
+ /* Otherwise use the fixed size buffer */
+ maps_buf = maps_temp;
+ }
+
+ close(f);
+ maps_buf[result] = '\0';
+
+ /* Apply fn to result. */
+ return fn(maps_buf);
+}
+
+#endif /* Need GC_apply_to_maps */
+
+#if defined(LINUX) && (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64))
+//
+// GC_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
+//
+// For 64 bit ABIs:
+// 0 17 34 56
+//
+// 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'.)
+//
+#if CPP_WORDSZ == 32
+# define OFFSET_MAP_START 0
+# define OFFSET_MAP_END 9
+# define OFFSET_MAP_PROT 18
+# define OFFSET_MAP_MAJDEV 32
+# define ADDR_WIDTH 8
+#endif
+
+#if CPP_WORDSZ == 64
+# define OFFSET_MAP_START 0
+# define OFFSET_MAP_END 17
+# define OFFSET_MAP_PROT 34
+# define OFFSET_MAP_MAJDEV 56
+# define ADDR_WIDTH 16
+#endif
+
+/*
+ * Assign various fields of the first line in buf_ptr to *start, *end,
+ * *prot_buf and *maj_dev. Only *prot_buf may be set for unwritable maps.
+ */
+char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
+ char *prot_buf, unsigned int *maj_dev)
+{
+ int i;
+ 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+ADDR_WIDTH] = '\0';
+ *start = strtoul(tok, NULL, 16);
+
+ tok = buf_ptr+OFFSET_MAP_END;
+ buf_ptr[OFFSET_MAP_END+ADDR_WIDTH] = '\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;
+}
+
+#endif /* Need to parse /proc/self/maps. */
+
#if defined(SEARCH_FOR_DATA_START)
/* The I386 case can be handled without a search. The Alpha case */
/* used to be handled differently as well, but the rules changed */
extern ptr_t __libc_stack_end;
# ifdef IA64
+ /* Try to read the backing store base from /proc/self/maps. */
+ /* We look for the writable mapping with a 0 major device, */
+ /* which is as close to our frame as possible, but below it.*/
+ static word backing_store_base_from_maps(char *maps)
+ {
+ char prot_buf[5];
+ char *buf_ptr = maps;
+ word start, end;
+ unsigned int maj_dev;
+ word current_best = 0;
+ word dummy;
+
+ for (;;) {
+ buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, prot_buf, &maj_dev);
+ if (buf_ptr == NULL) return current_best;
+ if (prot_buf[1] == 'w' && maj_dev == 0) {
+ if (end < (word)(&dummy) && start > current_best) current_best = start;
+ }
+ }
+ return current_best;
+ }
+
+ static word backing_store_base_from_proc(void)
+ {
+ return GC_apply_to_maps(backing_store_base_from_maps);
+ }
+
# pragma weak __libc_ia64_register_backing_store_base
extern ptr_t __libc_ia64_register_backing_store_base;
/* Hence we check for both nonzero address and value. */
return __libc_ia64_register_backing_store_base;
} else {
- word result = (word)GC_stackbottom - BACKING_STORE_DISPLACEMENT;
- result += BACKING_STORE_ALIGNMENT - 1;
- result &= ~(BACKING_STORE_ALIGNMENT - 1);
+ word result = backing_store_base_from_proc();
+ if (0 == result) {
+ /* Use dumb heuristics. Works only for default configuration. */
+ result = (word)GC_stackbottom - BACKING_STORE_DISPLACEMENT;
+ result += BACKING_STORE_ALIGNMENT - 1;
+ result &= ~(BACKING_STORE_ALIGNMENT - 1);
+ /* Verify that it's at least readable. If not, we goofed. */
+ GC_noop1(*(word *)result);
+ }
return (ptr_t)result;
}
}
/* using direct I/O system calls in order to avoid calling malloc */
/* in case REDIRECT_MALLOC is defined. */
# define STAT_BUF_SIZE 4096
-# if defined(GC_USE_LD_WRAP)
-# define STAT_READ __real_read
-# else
-# define STAT_READ read
-# endif
+# define STAT_READ read
+ /* Should probably call the real read, if read is wrapped. */
char stat_buf[STAT_BUF_SIZE];
int f;
char c;
/* all real work is done by GC_register_dynamic_libraries. Under */
/* win32s, we cannot find the data segments associated with dll's. */
/* We register the main data segment here. */
-# 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
+ /* This used to be set for gcc, to avoid dealing with */
+ /* the structured exception handling issues. But we now have */
+ /* assembly code to do that right. */
void GC_init_win32()
{
sigaction(SIGSEGV, 0, &oldact);
sigaction(SIGSEGV, &act, 0);
# else
- sigaction(SIGSEGV, &act, &oldact);
+ {
+ int res = sigaction(SIGSEGV, &act, &oldact);
+ if (res != 0) ABORT("Sigaction failed");
+ }
# endif
# if defined(_sigargs) || defined(HURD) || !defined(SA_SIGINFO)
/* This is Irix 5.x, not 6.x. Irix 5.x does not have */
{
}
-# else /* !MPROTECT_VDB */
-
-# ifdef GC_USE_LD_WRAP
- ssize_t __wrap_read(int fd, void *buf, size_t nbyte)
- { return __real_read(fd, buf, nbyte); }
-# endif
-
# endif /* MPROTECT_VDB */
# ifdef PROC_VDB
}
name = result_buf;
pclose(pipe);
- out:
+ out:;
}
# endif /* LINUX */
GC_err_printf1("\t\t%s\n", name);
#endif /* NEED_CALLINFO */
-#if defined(LINUX) && defined(__ELF__) && \
- (!defined(SMALL_CONFIG) || defined(USE_PROC_FOR_LIBRARIES))
-#ifdef GC_USE_LD_WRAP
-# define READ __real_read
-#else
-# define READ read
-#endif
-
-
-/* Repeatedly perform a read call until the buffer is filled or */
-/* we encounter EOF. */
-ssize_t GC_repeat_read(int fd, char *buf, size_t count)
-{
- ssize_t num_read = 0;
- ssize_t result;
-
- while (num_read < count) {
- result = READ(fd, buf + num_read, count - num_read);
- if (result < 0) return result;
- if (result == 0) break;
- num_read += result;
- }
- return num_read;
-}
-#endif /* LINUX && ... */
#if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG)
/* Dump /proc/self/maps to GC_stderr, to enable looking up names for
addresses in FIND_LEAK output. */
+static word dump_maps(char *maps)
+{
+ GC_err_write(maps, strlen(maps));
+ return 1;
+}
+
void GC_print_address_map()
{
- int f;
- int result;
- char maps_temp[32768];
GC_err_printf0("---------- Begin address map ----------\n");
- f = open("/proc/self/maps", O_RDONLY);
- if (-1 == f) ABORT("Couldn't open /proc/self/maps");
- do {
- result = GC_repeat_read(f, maps_temp, sizeof(maps_temp));
- if (result <= 0) ABORT("Couldn't read /proc/self/maps");
- GC_err_write(maps_temp, result);
- } while (result == sizeof(maps_temp));
- close(f);
+ GC_apply_to_maps(dump_maps);
GC_err_printf0("---------- End address map ----------\n");
}
return(p);
}
sz = WORDS_TO_BYTES(hhdr -> hb_sz);
- if (sz > WORDS_TO_BYTES(MAXOBJSZ)) {
+ if (sz > MAXOBJBYTES) {
base = (ptr_t)HBLKPTR(p);
limit = base + sz;
if ((ptr_t)p >= limit) {
pdispl = HBLKDISPL(p);
map_entry = MAP_ENTRY((hhdr -> hb_map), pdispl);
if (map_entry == OBJ_INVALID
- || sz > MAXOBJSZ && (ptr_t)p >= (ptr_t)h + sz) {
+ || sz > MAXOBJBYTES && (ptr_t)p >= (ptr_t)h + sz) {
goto fail;
}
return(p);
* Clear *flp.
* This must be done before dropping a list of free gcj-style objects,
* since may otherwise end up with dangling "descriptor" pointers.
- * It may help for other pointer-containg objects.
+ * It may help for other pointer-containing objects.
*/
void GC_clear_fl_links(flp)
ptr_t *flp;
# endif
# ifdef GC_WIN32_THREADS
-# ifndef MSWINCE
- /* FIXME - Why is this here? */
-# include <process.h>
-# define GC_CreateThread(a,b,c,d,e,f) ((HANDLE) _beginthreadex(a,b,c,d,e,f))
-# endif
static CRITICAL_SECTION incr_cs;
# endif
#ifdef THREADS
# if defined(GC_WIN32_THREADS) && !defined(CYGWIN32)
- unsigned __stdcall tiny_reverse_test(void * arg)
+ DWORD __stdcall tiny_reverse_test(void * arg)
# else
void * tiny_reverse_test(void * arg)
# endif
# elif defined(GC_WIN32_THREADS)
void fork_a_thread()
{
- unsigned thread_id;
+ DWORD thread_id;
HANDLE h;
h = GC_CreateThread(NULL, 0, tiny_reverse_test, 0, 0, &thread_id);
if (h == (HANDLE)NULL) {
FAIL;
}
finalized_count++;
+ t -> level = -1; /* detect duplicate finalization immediately */
# ifdef PCR
PCR_ThCrSec_ExitSys();
# endif
#if defined(GC_WIN32_THREADS) && !defined(CYGWIN32)
-unsigned __stdcall thr_run_one_test(void *arg)
+DWORD __stdcall thr_run_one_test(void *arg)
{
run_one_test();
return 0;
return ret;
}
-unsigned __stdcall thr_window(void *arg)
+DWORD __stdcall thr_window(void *arg)
{
WNDCLASS win_class = {
CS_NOCLOSE,
# ifdef MSWINCE
HANDLE win_thr_h;
# endif
- unsigned thread_id;
+ DWORD thread_id;
# if 0
GC_enable_incremental();
# endif
+ GC_init();
InitializeCriticalSection(&incr_cs);
(void) GC_set_warn_proc(warn_proc);
# ifdef MSWINCE
int main()
{
# if defined(GC_USE_LD_WRAP)
- printf("-Wl,--wrap -Wl,read -Wl,--wrap -Wl,dlopen "
+ printf("-Wl,--wrap -Wl,dlopen "
"-Wl,--wrap -Wl,pthread_create -Wl,--wrap -Wl,pthread_join "
"-Wl,--wrap -Wl,pthread_detach "
"-Wl,--wrap -Wl,pthread_sigmask -Wl,--wrap -Wl,sleep\n");
/* it to keep the old-style build process working. */
#define GC_TMP_VERSION_MAJOR 6
#define GC_TMP_VERSION_MINOR 2
-#define GC_TMP_ALPHA_VERSION 4
+#define GC_TMP_ALPHA_VERSION 5
#if defined(GC_VERSION_MAJOR)
# if GC_TMP_VERSION_MAJOR != GC_VERSION_MAJOR || \
#endif
-
-#if 0
-#define STRICT
-#include <windows.h>
+#ifndef MAX_THREADS
+# define MAX_THREADS 64
#endif
-#define MAX_THREADS 64
-
struct thread_entry {
LONG in_use;
DWORD id;
if (*lo < start) *lo = start;
}
-#if !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL))
+#if !defined(MSWINCE) && defined(GC_DLL)
-HANDLE WINAPI GC_CreateThread(
+/* We register threads from DllMain */
+
+GC_API HANDLE GC_CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
lpParameter, dwCreationFlags, lpThreadId);
}
-#else /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
+#else /* defined(MSWINCE) || !defined(GC_DLL)) */
+
+/* We have no DllMain to take care of new threads. Thus we */
+/* must properly intercept thread creation. */
typedef struct {
HANDLE child_ready_h, parent_ready_h;
LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
-/* threadAttach/threadDetach routines used by both CYGWIN and DLL implementation,
- since both recieve explicit notification on thread creation/destruction
+/* threadAttach/threadDetach routines used by both CYGWIN and DLL
+ * implementation, since both recieve explicit notification on thread
+ * creation/destruction.
*/
-void threadAttach() {
+static void threadAttach() {
int i;
/* It appears to be unsafe to acquire a lock here, since this */
/* code is apparently not preeemptible on some systems. */
while (GC_please_stop) Sleep(20);
}
-void threadDetach(DWORD thread_id) {
+static void threadDetach(DWORD thread_id) {
int i;
LOCK();
int i;
# if DEBUG_CYGWIN_THREADS
- GC_printf2("thread 0x%x(0x%x) called pthread_exit().\n",(int)pthread_self(),GetCurrentThreadId());
+ GC_printf2("thread 0x%x(0x%x) called pthread_exit().\n",
+ (int)pthread_self(),GetCurrentThreadId());
# endif
LOCK();
int GC_pthread_detach(pthread_t thread) {
return pthread_detach(thread);
}
-#else
+#else /* !CYGWIN32 */
/*
* We avoid acquiring locks here, since this doesn't seem to be preemptable.
* Pontus Rydin suggests wrapping the thread start routine instead.
*/
+#ifdef GC_DLL
BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
{
switch (reason) {
}
return TRUE;
}
-#endif /* CYGWIN32 */
+#endif /* GC_DLL */
+#endif /* !CYGWIN32 */
# endif /* !MSWINCE */