[Gc] MacOSX dynamic library patch

Andrew Begel abegel@eecs.berkeley.edu
Tue, 22 Jul 2003 01:47:36 -0700


This patches two problems with MacOSX dynamic library support in gc6.2:

1. Calling _dyld_bind_fully_image_containing_address() is a dangerous 
function on OSX because the dynamic linker will not just fully bind the 
current image (the garbage collector), but will also start loading in 
other libraries, some of which may invoke the garbage collector, and 
either call GC_init() reentrantly or even call GC_malloc too early. I 
moved this call below the assignment "initialized = TRUE" to guard 
against the GC_init_dyld() call being reentrantly called (the if 
statement at the top of the function will abort reentrant calls).

2. It's also good to avoid having to call the above dyld function if 
you don't have to, since it has such dangerous semantics. When the user 
has set DYLD_BIND_AT_LAUNCH (man dyld for info on this flag), the 
dynamic linker already has fully bound each loaded library before 
calling any functions inside, thus the call to 
_dyld_bind_fully_image_containing_address() is unnecessary (and of 
course, potentially dangerous due to the dynamic linker behavior).

These two fixes correct a reentrancy problem I was having with a set of 
libraries that used the GC but where GC_init() was not able to be 
called from the application binary itself due to design issues. My 
product is delivered as a set of libraries that plug into an existing 
application that I don't control the source for, nor does that app use 
the BW-GC.

The second patch is documentation on what you need to do when 
initializing GC from a dynamic library, and not from an executable.

Andrew



diff -u gc6.2/dyn_load.c gc6.2-mod/dyn_load.c
--- gc6.2/dyn_load.c    Fri May 30 17:54:50 2003
+++ gc6.2-mod/dyn_load.c        Tue Jul 22 00:22:49 2003
@@ -1049,32 +1057,43 @@
     allocation lock held. */

  void GC_init_dyld() {
-    static GC_bool initialized = FALSE;
-
-    if(initialized) return;
-
-#   ifdef DARWIN_DEBUG
-        GC_printf0("Forcing full bind of GC code...\n");
-#   endif
-    if(!_dyld_bind_fully_image_containing_address((unsigned 
long*)GC_malloc))
-        GC_abort("_dyld_bind_fully_image_containing_addres failed");
-
+  static GC_bool initialized = FALSE;
+  char *bind_fully_env = NULL;
+
+  if(initialized) return;
+
  #   ifdef DARWIN_DEBUG
-        GC_printf0("Registering dyld callbacks...\n");
+  GC_printf0("Registering dyld callbacks...\n");
  #   endif
-
-    /* Apple's Documentation:
-    When you call _dyld_register_func_for_add_image, the dynamic 
linker runtime
-    calls the specified callback (func) once for each of the images 
that is
-    currently loaded into the program. When a new image is added to 
the program,
-    your callback is called again with the mach_header for the new 
image, and the      virtual memory slide amount of the new image.
-
-    This WILL properly register existing and all future libraries
-    */
-
+
+  /* Apple's Documentation:
+     When you call _dyld_register_func_for_add_image, the dynamic 
linker runtime
+     calls the specified callback (func) once for each of the images 
that is
+     currently loaded into the program. When a new image is added to 
the program,
+     your callback is called again with the mach_header for the new 
image, and the
+     virtual memory slide amount of the new image.
+
+     This WILL properly register already linked libraries and libraries
+     linked in the future
+  */
+
      _dyld_register_func_for_add_image(GC_dyld_image_add);
      _dyld_register_func_for_remove_image(GC_dyld_image_remove);
+
+    /* Set this early to avoid reentrancy issues. */
      initialized = TRUE;
+
+    bind_fully_env = getenv("DYLD_BIND_AT_LAUNCH");
+
+    if (bind_fully_env == NULL) {
+#   ifdef DARWIN_DEBUG
+      GC_printf0("Forcing full bind of GC code...\n");
+#   endif
+
+      if(!_dyld_bind_fully_image_containing_address((unsigned 
long*)GC_malloc))
+        GC_abort("_dyld_bind_fully_image_containing_address failed");
+    }
+
  }

  #define HAVE_REGISTER_MAIN_STATIC_DATA


diff -u gc6.2/doc/README.darwin gc6.2-mod/doc/README.darwin
--- gc6.2/doc/README.darwin     Fri Jun 13 12:11:00 2003
+++ gc6.2-mod/doc/README.darwin Tue Jul 22 01:34:16 2003
@@ -1,4 +1,4 @@
-Darwin/MacOSX Support - May 20, 2003
+Darwin/MacOSX Support - July 22, 2003
  ====================================

  Important Usage Notes
@@ -8,6 +8,26 @@
  is necessary to properly register segments in dynamic libraries. This
  call is required even if you code does not use dynamic libraries as the
  dyld code handles registering all data segments.
+
+When your use of the garbage collector is confined to dylibs and you
+cannot call GC_init() before your libraries' static initializers have
+run and perhaps called GC_malloc(), create an initialization routine
+for each library to call GC_init():
+
+#include <gc/gc.h>
+void my_library_init() { GC_init(); }
+
+Compile this code into a my_library_init.o, and link it into your
+dylib. When you link the dylib, pass the -init argument with
+_my_library_init (e.g. gcc -dynamiclib -o my_library.dylib a.o b.o c.o
+my_library_init.o -init _my_library_init). This causes
+my_library_init() to be called before any static initializers, and
+will initialize the garbage collector properly.
+
+Note: It doesn't hurt to call GC_init() more than once, so it's best,
+if you have an application or set of libraries that all use the
+garbage collector, to create an initialization routine for each of
+them that calls GC_init(). Better safe than sorry.

  The incremental collector is still a bit flaky on darwin. It seems to
  work reliably with workarounds for a few possible bugs in place however