[Gc] patch to support dynamic pthreads calls redirection on linux

Marat Boshernitsan maratb at cs.berkeley.edu
Wed Jan 21 18:26:19 PST 2004


I have recently run into a problem with the collector when my GC-based 
dynamic library was loaded into a GC-unaware application (Sun's JVM), 
which was spawning threads and confusing the collector.  Since we don't 
have the source code, it is not possible to statically capture all the 
relevant pthreads calls made by the JVM.  Our solution was to use 
LD_PRELOAD mecahnism on Linux to override all of the necessary 
pthreads-related calls with redirects into the garbage collector and 
then use dynamic symbol lookup to invoke the "official" library version, 
when needed.  The attached patch demonstrates how this can be done.  The 
collector needs to be compiled with (our new) GC_USE_LD_PRELOAD flag.  
Before the application is started, LD_PRELOAD env. var should be set to 
the full path of the resulting libgc.so.

It would be nice if something like this could be folded into the 
collector source base. 

Marat Boshernitsan
UC Berkeley
The Harmonia Project.
-------------- next part --------------
diff -ur gc6.3alpha4orig/gc_dlopen.c gc6.3alpha4/gc_dlopen.c
--- gc6.3alpha4orig/gc_dlopen.c	2003-05-29 08:50:09.000000000 -0700
+++ gc6.3alpha4/gc_dlopen.c	2004-01-21 13:34:44.000000000 -0800
@@ -64,6 +64,8 @@
 
 #ifdef GC_USE_LD_WRAP
   void * __wrap_dlopen(const char *path, int mode)
+#elif GC_USE_LD_PRELOAD
+  void * dlopen(const char *path, int mode)
 #else
   void * GC_dlopen(path, mode)
   GC_CONST char * path;
@@ -72,10 +74,14 @@
 {
     void * result;
     
+#ifdef GC_USE_LD_PRELOAD    
+    if (!GC_is_initialized) GC_init();
+#endif
+
 #   ifndef USE_PROC_FOR_LIBRARIES
       disable_gc_for_dlopen();
 #   endif
-#   ifdef GC_USE_LD_WRAP
+#   if defined(GC_USE_LD_WRAP) || defined(GC_USE_LD_PRELOAD)
       result = (void *)__real_dlopen(path, mode);
 #   else
       result = dlopen(path, mode);
diff -ur gc6.3alpha4orig/include/gc_pthread_redirects.h gc6.3alpha4/include/gc_pthread_redirects.h
--- gc6.3alpha4orig/include/gc_pthread_redirects.h	2003-07-21 18:45:17.000000000 -0700
+++ gc6.3alpha4/include/gc_pthread_redirects.h	2004-01-21 14:41:27.000000000 -0800
@@ -68,6 +68,7 @@
 # undef pthread_detach
 #endif
 
+#ifndef GC_USE_LD_PRELOAD
 # define pthread_create GC_pthread_create
 # define pthread_join GC_pthread_join
 # define pthread_detach GC_pthread_detach
@@ -76,6 +77,7 @@
 # define pthread_sigmask GC_pthread_sigmask
 # define dlopen GC_dlopen
 #endif
+#endif
 
 #endif /* GC_xxxxx_THREADS */
 
diff -ur gc6.3alpha4orig/pthread_support.c gc6.3alpha4/pthread_support.c
--- gc6.3alpha4orig/pthread_support.c	2004-01-01 12:04:19.000000000 -0800
+++ gc6.3alpha4/pthread_support.c	2004-01-21 14:42:47.000000000 -0800
@@ -127,6 +127,9 @@
 #ifdef GC_USE_LD_WRAP
 #   define WRAP_FUNC(f) __wrap_##f
 #   define REAL_FUNC(f) __real_##f
+#elif defined(GC_USE_LD_PRELOAD)
+#   define WRAP_FUNC(f) f
+#   define REAL_FUNC(f) __real_##f
 #else
 #   define WRAP_FUNC(f) GC_##f
 #   if !defined(GC_DGUX386_THREADS)
@@ -149,6 +152,84 @@
 #   endif
 #endif
 
+
+#ifdef GC_USE_LD_PRELOAD
+
+#if defined(__linux__) && !defined(__USE_GNU)
+#   define __USE_GNU
+#endif
+#include <dlfcn.h>
+
+typedef int (*pthread_sigmask_func)(int how, 
+				    const sigset_t *set,
+				    sigset_t *oset);
+typedef int (*pthread_join_func)(pthread_t thread, void **retval);
+typedef int (*pthread_detach_func)(pthread_t thread);
+typedef int (*pthread_create_func)(pthread_t *new_thread,
+				   const pthread_attr_t *attr,
+				   void *(*start_routine)(void *), 
+				   void *arg);
+typedef unsigned int (*sleep_func)(unsigned int seconds);
+typedef void * (*dlopen_func)(const char *path, int mode);
+
+static pthread_sigmask_func pthread_sigmask_func_ptr = NULL;
+static pthread_join_func pthread_join_func_ptr = NULL;
+static pthread_detach_func pthread_detach_func_ptr = NULL;
+static pthread_create_func pthread_create_func_ptr = NULL;
+static sleep_func sleep_func_ptr = NULL;
+static dlopen_func dlopen_func_ptr = NULL;
+
+#ifdef DEBUG_THREADS
+#define RTLD_REDIRECT_DEBUG_MSG(name) \
+    GC_printf0("Successfully captured and redirected " #name "()\n"); while(0)
+#else
+#define RTLD_REDIRECT_DEBUG_MSG(name)
+#endif
+
+#define RTLD_REDIRECT_INIT(name)			      \
+    name##_func_ptr = dlsym(RTLD_NEXT, #name);		      \
+    if (!name##_func_ptr) {				      \
+	GC_printf1("dlsym(): %s\n", dlerror());		      \
+	ABORT("Could not redirect " #name);		      \
+    }							      \
+    GC_ASSERT(name##_func_ptr != &name);		      \
+    RTLD_REDIRECT_DEBUG_MSG(name);
+
+int REAL_FUNC(pthread_sigmask)(int how, 
+			       const sigset_t *set,
+			       sigset_t *oset) {
+    return (*pthread_sigmask_func_ptr)(how, set, oset);
+}
+    
+int REAL_FUNC(pthread_join)(pthread_t thread, void **retval) {
+    return (*pthread_join_func_ptr)(thread, retval);
+}
+
+int REAL_FUNC(pthread_detach)(pthread_t thread) {
+    return (*pthread_detach_func_ptr)(thread);
+}
+
+int REAL_FUNC(pthread_create)(pthread_t *new_thread,
+			      const pthread_attr_t *attr,
+			      void *(*start_routine)(void *), 
+			      void *arg) {
+    return (*pthread_create_func_ptr)(new_thread, attr, start_routine, arg);
+}
+
+unsigned int REAL_FUNC(sleep)(unsigned int seconds) {
+    return (*sleep_func_ptr)(seconds);
+}
+
+void * REAL_FUNC(dlopen)(const char *path, int mode) {
+    // initialize dlopen here, because libgc is not linked against libdl
+    // so, we should wait until libdl is loaded by the application...
+    if (!dlopen_func_ptr) 
+	RTLD_REDIRECT_INIT(dlopen);
+    return (*dlopen_func_ptr)(path, mode);
+}
+
+#endif
+
 void GC_thr_init();
 
 static GC_bool parallel_initialized = FALSE;
@@ -852,6 +933,14 @@
 
     GC_stop_init();
 
+#ifdef GC_USE_LD_PRELOAD
+    RTLD_REDIRECT_INIT(pthread_sigmask);
+    RTLD_REDIRECT_INIT(pthread_join);
+    RTLD_REDIRECT_INIT(pthread_detach);
+    RTLD_REDIRECT_INIT(pthread_create);
+    RTLD_REDIRECT_INIT(sleep);
+#endif
+
     /* Set GC_nprocs.  */
       {
 	char * nprocs_string = GETENV("GC_NPROCS");
@@ -949,7 +1038,11 @@
 int WRAP_FUNC(pthread_sigmask)(int how, const sigset_t *set, sigset_t *oset)
 {
     sigset_t fudged_set;
-    
+
+#ifdef GC_USE_LD_PRELOAD    
+    if (!GC_is_initialized) GC_init();
+#endif
+
     if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
         fudged_set = *set;
         sigdelset(&fudged_set, SIG_SUSPEND);
@@ -1007,10 +1100,14 @@
 #endif /* GC_DGUX386_THREADS */
 
 /* A wrapper for the standard C sleep function	*/
-int WRAP_FUNC(sleep) (unsigned int seconds)
+unsigned int WRAP_FUNC(sleep) (unsigned int seconds)
 {
     int result;
 
+#ifdef GC_USE_LD_PRELOAD    
+    if (!GC_is_initialized) GC_init();
+#endif
+
     GC_start_blocking();
     result = REAL_FUNC(sleep)(seconds);
     GC_end_blocking();
@@ -1055,6 +1152,10 @@
     int result;
     GC_thread thread_gc_id;
     
+#ifdef GC_USE_LD_PRELOAD    
+    if (!GC_is_initialized) GC_init();
+#endif
+
     LOCK();
     thread_gc_id = GC_lookup_thread(thread);
     /* This is guaranteed to be the intended one, since the thread id	*/
@@ -1087,6 +1188,10 @@
     int result;
     GC_thread thread_gc_id;
     
+#ifdef GC_USE_LD_PRELOAD    
+    if (!GC_is_initialized) GC_init();
+#endif
+
     LOCK();
     thread_gc_id = GC_lookup_thread(thread);
     UNLOCK();
@@ -1193,6 +1298,10 @@
 	/* This is otherwise saved only in an area mmapped by the thread */
 	/* library, which isn't visible to the collector.		 */
  
+#ifdef GC_USE_LD_PRELOAD    
+    if (!GC_is_initialized) GC_init();
+#endif
+
     /* We resist the temptation to muck with the stack size here,	*/
     /* even if the default is unreasonably small.  That's the client's	*/
     /* responsibility.							*/


More information about the Gc mailing list