Owen Taylor <[EMAIL PROTECTED]> writes:

> Rui-Xiang Guo <[EMAIL PROTECTED]> writes:
> 
> OK, I was able to reproduce it and figured out the immediate
> problem here:
> 
>  _XDynamicRegisterIMInstantiateCallback() dlopens 'ximcp.so'
>   and calls _XimRegisterIMInstantiateCallback
> 
>  _XimRegisterIMInstantiateCallback() calls lcd->methods->open_im
>   which is _XDynamicOpenIM()
> 
>  _XDynamicOpenIM() calls _XimOpenIM from ximcp.so, which fails,
>   dlcloses ximcp.so and returns control to 
>   _XimRegisterInstantiateCallback() which is in a module that
>   has been unloaded, so *boom*
> 
> The solution here is presumably to add reference counting to
> dlopen/dlclose calls in XlcDL.c; at the same time, it would make
> sense to fix the problem that someone apparently never 
> heard that cutting and pasting 30 lines of code over and
> over again was a bad idea...

OK, here's an attempt at doing this.

 a) I've done little testing on it. Xcin no longer causes
    a crash, with it. It's probably a bad idea to commit
    this without someone giving it a good look over.

 b) I'm not entirely happy with the patch since it leaks
    reference counts; as I say in a comment: 

/* We reference count dlopen() and dlclose() of modules; unfortunately,
 * since XCloseIM, XCloseOM, XlcClose aren't wrapped, but directly
 * call the close method of the object, we leak a reference count every
 * time we open then close a module. Fixing this would require
 * either creating proxy objects or hooks for close_im/close_om
 * in XLCd
 */

   While this probably isn't harmful in practice, it's
   certainly ugly. If one of these fixes isn't done, 
   it might be cleanest to add a 'permanently_loaded'
   flag to keep the reference count flag from increasing
   indefinitely.

Regards,
                                        Owen

Index: XlcDL.c
===================================================================
RCS file: /cvs/xc/lib/X11/XlcDL.c,v
retrieving revision 1.5
diff -u -p -r1.5 XlcDL.c
--- XlcDL.c	2002/01/23 16:26:41	1.5
+++ XlcDL.c	2002/01/27 18:20:48
@@ -83,6 +83,7 @@ typedef struct {
   char *im_register;
   char *im_unregister;
   int dl_release;
+  unsigned int refcount;
 #if defined(hpux)
   shl_t dl_module;
 #else
@@ -214,6 +215,7 @@ Limit the length of path to prevent stac
 	  xi18n_objects_list[lc_count].open = strdup(args[2]);
 	  xi18n_objects_list[lc_count].dl_release = XI18N_DLREL;
 	  xi18n_objects_list[lc_count].locale_name = strdup(lc_name);
+	  xi18n_objects_list[lc_count].refcount = 0;
 	  xi18n_objects_list[lc_count].dl_module = (void*)NULL;
 	  if (n == 5) {
 	    xi18n_objects_list[lc_count].im_register = strdup(args[3]);
@@ -284,6 +286,85 @@ const char *lc_dir;
     return path;
 }
 
+/* We reference count dlopen() and dlclose() of modules; unfortunately,
+ * since XCloseIM, XCloseOM, XlcClose aren't wrapped, but directly
+ * call the close method of the object, we leak a reference count every
+ * time we open then close a module. Fixing this would require
+ * either creating proxy objects or hooks for close_im/close_om
+ * in XLCd
+ */
+static Bool
+open_object (object, lc_dir)
+     XI18NObjectsList object;
+     char *lc_dir;
+{
+  char *path;
+  
+  if (object->refcount == 0) {
+      path = __lc_path(object->dl_name, lc_dir);
+#if defined(hpux)
+      object->dl_module = shl_load(path, BIND_DEFERRED, 0L);
+#else
+      object->dl_module = dlopen(path, RTLD_LAZY);
+#endif
+      Xfree(path);
+
+      if (!object->dl_module)
+	  return False;
+    }
+
+  object->refcount++;
+  return True;
+}
+
+static void *
+fetch_symbol (object, symbol)
+     XI18NObjectsList object;
+     char *symbol;
+{
+    void *result = NULL;
+#if defined(hpux)
+    int getsyms_cnt, i;
+    struct shl_symbol *symbols;
+#endif
+
+#if defined(hpux)
+    getsyms_cnt = shl_getsymbols(object->dl_module, TYPE_PROCEDURE,
+				 EXPORT_SYMBOLS, malloc, &symbols);
+
+    for(i=0; i<getsyms_cnt; i++) {
+        if(!strcmp(symbols[i].name, symbol)) {
+	    result = symbols[i].value;
+	    break;
+         }
+    }
+
+    if(getsyms_cnt > 0) {
+        free(symbols);
+    }
+#else
+    result = (XLCd(*)())try_both_dlsym(object->dl_module, symbol);
+#endif
+
+    return result;
+}
+
+static void
+close_object (object)
+     XI18NObjectsList object;
+{
+  object->refcount--;
+  if (object->refcount == 0)
+    {
+#if defined(hpux)
+        shl_unload(object->dl_module);
+#else
+        dlclose(object->dl_module);
+#endif
+        object->dl_module = NULL;
+    }
+}
+
 XLCd
 #if NeedFunctionPrototypes
 _XlcDynamicLoad(const char *lc_name)
@@ -294,14 +375,9 @@ _XlcDynamicLoad(lc_name)
 {
     XLCd lcd = (XLCd)NULL;
     XLCd (*lc_loader)() = (XLCd(*)())NULL;
-    char *path;
     int count;
     XI18NObjectsList objects_list;
     char lc_dir[BUFSIZE];
-#if defined(hpux)
-    int getsyms_cnt, i;
-    struct shl_symbol *symbols;
-#endif
 
     if (lc_name == NULL) return (XLCd)NULL;
 
@@ -315,45 +391,17 @@ _XlcDynamicLoad(lc_name)
     for (; count-- > 0; objects_list++) {
         if (objects_list->type != XLC_OBJECT ||
 	    strcmp(objects_list->locale_name, lc_name)) continue;
-	if (!objects_list->dl_module) {
-	  path = __lc_path(objects_list->dl_name, lc_dir);
-#if defined(hpux)
-	  objects_list->dl_module = shl_load(path, BIND_DEFERRED, 0L);
-#else
-	  objects_list->dl_module = dlopen(path, RTLD_LAZY);
-#endif
-	  Xfree(path);
-	  if (!objects_list->dl_module) continue;
-	}
-#if defined(hpux)
-	getsyms_cnt = shl_getsymbols(objects_list->dl_module, TYPE_PROCEDURE,
-		EXPORT_SYMBOLS, malloc, &symbols);
-
-	for(i=0; i<getsyms_cnt; i++) {
-	    if(!strcmp(symbols[i].name, objects_list->open)) {
-		lc_loader = symbols[i].value;
-		break;
-	    }
-	}
+	if (!open_object (objects_list, lc_dir))
+	    continue;
 
-	if(getsyms_cnt > 0) {
-	    free(symbols);
-	}
-#else
-	lc_loader = (XLCd(*)())try_both_dlsym(objects_list->dl_module,
-					      objects_list->open);
-#endif
+	lc_loader = (XLCd(*)())fetch_symbol (objects_list, objects_list->open);
 	if (!lc_loader) continue;
 	lcd = (*lc_loader)(lc_name);
 	if (lcd != (XLCd)NULL) {
 	    break;
 	}
-#if defined(hpux)
-	shl_unload(objects_list->dl_module);
-#else
-	dlclose(objects_list->dl_module);
-#endif
-	objects_list->dl_module = NULL;
+	
+	close_object (objects_list);
     }
     return (XLCd)lcd;
 }
@@ -371,16 +419,11 @@ char *res_name, *res_class;
 #endif
 {
   XIM im = (XIM)NULL;
-  char *path;
   char lc_dir[BUFSIZE];
   char *lc_name;
   XIM (*im_openIM)() = (XIM(*)())NULL;
   int count;
   XI18NObjectsList objects_list = xi18n_objects_list;
-#if defined(hpux)
-  int getsyms_cnt, i;
-  struct shl_symbol *symbols;
-#endif
 
   lc_name = lcd->core->name;
 
@@ -390,46 +433,18 @@ char *res_name, *res_class;
   for (; count-- > 0; objects_list++) {
     if (objects_list->type != XIM_OBJECT ||
 	strcmp(objects_list->locale_name, lc_name)) continue;
-    if (!objects_list->dl_module) {
-      path = __lc_path(objects_list->dl_name, lc_dir);
-#if defined(hpux)
-      objects_list->dl_module = shl_load(path, BIND_DEFERRED, 0L);
-#else
-      objects_list->dl_module = dlopen(path, RTLD_LAZY);
-#endif
-      Xfree(path);
-      if (!objects_list->dl_module) continue;
-    }
-#if defined(hpux)
-    getsyms_cnt = shl_getsymbols(objects_list->dl_module, TYPE_PROCEDURE,
-	EXPORT_SYMBOLS, malloc, &symbols);
 
-    for(i=0; i<getsyms_cnt; i++) {
-	if(!strcmp(symbols[i].name, objects_list->open)) {
-	    im_openIM = symbols[i].value;
-	    break;
-	}
-     }
+    if (!open_object (objects_list, lc_dir))
+        continue;
 
-    if(getsyms_cnt > 0) {
-	free(symbols);
-    }
-#else
-    im_openIM = (XIM(*)())try_both_dlsym(objects_list->dl_module,
-					 objects_list->open);
+    im_openIM = (XIM(*)())fetch_symbol(objects_list, objects_list->open);
     if (!im_openIM) continue;
-#endif
     im = (*im_openIM)(lcd, display, rdb, res_name, res_class);
     if (im != (XIM)NULL) {
-      break;
+        break;
     }
-    im_openIM = 0;
-#if defined(hpux)
-    shl_unload(objects_list->dl_module);
-#else
-    dlclose(objects_list->dl_module);
-#endif
-    objects_list->dl_module = NULL;
+    
+    close_object (objects_list);
   }
   return (XIM)im;
 }
@@ -445,7 +460,6 @@ char	*res_name, *res_class;
 XIMProc	 callback;
 XPointer	*client_data;
 {
-  char *path;
   char lc_dir[BUFSIZE];
   char *lc_name;
   Bool (*im_registerIM)() = (Bool(*)())NULL;
@@ -465,47 +479,18 @@ XPointer	*client_data;
   for (; count-- > 0; objects_list++) {
     if (objects_list->type != XIM_OBJECT ||
 	strcmp(objects_list->locale_name, lc_name)) continue;
-    if (!objects_list->dl_module) {
-      path = __lc_path(objects_list->dl_name, lc_dir);
-#if defined(hpux)
-      objects_list->dl_module = shl_load(path, BIND_DEFERRED, 0L);
-#else
-      objects_list->dl_module = dlopen(path, RTLD_LAZY);
-#endif
-      Xfree(path);
-      if (!objects_list->dl_module) continue;
-    }
-#if defined(hpux)
-    getsyms_cnt = shl_getsymbols(objects_list->dl_module, TYPE_PROCEDURE,
-	EXPORT_SYMBOLS, malloc, &symbols);
-
-    for(i=0; i<getsyms_cnt; i++) {
-	if(!strcmp(symbols[i].name, objects_list->open)) {
-	    im_registerIM = symbols[i].value;
-	    break;
-	}
-     }
 
-    if(getsyms_cnt > 0) {
-	free(symbols);
-    }
-#else
-    im_registerIM = (Bool(*)())try_both_dlsym(objects_list->dl_module,
-					      objects_list->im_register);
+    if (!open_object (objects_list, lc_dir))
+        continue;
+    im_registerIM = (Bool(*)())fetch_symbol(objects_list,
+					    objects_list->im_register);
     if (!im_registerIM) continue;
-#endif
     ret_flag = (*im_registerIM)(lcd, display, rdb,
 				res_name, res_class,
 				callback, client_data);
     if (ret_flag) break;
 
-    im_registerIM = 0;
-#if defined(hpux)
-    shl_unload(objects_list->dl_module);
-#else
-    dlclose(objects_list->dl_module);
-#endif
-    objects_list->dl_module = NULL;
+    close_object (objects_list);
   }
   return (Bool)ret_flag;
 }
@@ -521,7 +506,6 @@ char	*res_name, *res_class;
 XIMProc	 callback;
 XPointer	*client_data;
 {
-  char *path;
   char lc_dir[BUFSIZE];
   char *lc_name;
   Bool (*im_unregisterIM)() = (Bool(*)())NULL;
@@ -540,48 +524,21 @@ XPointer	*client_data;
   for (; count-- > 0; objects_list++) {
     if (objects_list->type != XIM_OBJECT ||
 	strcmp(objects_list->locale_name, lc_name)) continue;
-    if (!objects_list->dl_module) {
-      path = __lc_path(objects_list->dl_name, lc_dir);
-#if defined(hpux)
-      objects_list->dl_module = shl_load(path, BIND_DEFERRED, 0L);
-#else
-      objects_list->dl_module = dlopen(path, RTLD_LAZY);
-#endif
-      Xfree(path);
-      if (!objects_list->dl_module) continue;
-    }
-#if defined(hpux)
-    getsyms_cnt = shl_getsymbols(objects_list->dl_module, TYPE_PROCEDURE,
-	EXPORT_SYMBOLS, malloc, &symbols);
 
-    for(i=0; i<getsyms_cnt; i++) {
-	if(!strcmp(symbols[i].name, objects_list->open)) {
-	    im_unregisterIM = symbols[i].value;
-	    break;
-	}
-     }
+    if (!objects_list->refcount) /* Must already be opened */
+        continue;
 
-    if(getsyms_cnt > 0) {
-	free(symbols);
-    }
-#else
-    im_unregisterIM = (Bool(*)())try_both_dlsym(objects_list->dl_module,
-						objects_list->im_unregister);
+    im_unregisterIM = (Bool(*)())fetch_symbol(objects_list,
+					      objects_list->im_unregister);
 
     if (!im_unregisterIM) continue;
-#endif
     ret_flag = (*im_unregisterIM)(lcd, display, rdb,
 				  res_name, res_class,
 				  callback, client_data);
-    if (ret_flag) break;
-
-    im_unregisterIM = 0;
-#if defined(hpux)
-    shl_unload(objects_list->dl_module);
-#else
-    dlclose(objects_list->dl_module);
-#endif
-    objects_list->dl_module = NULL;
+    if (ret_flag) {
+        close_object (objects_list); /* opened in RegisterIMInstantiateCallback */
+	break;
+    }
   }
   return (Bool)ret_flag;
 }
@@ -617,7 +574,6 @@ char *res_class;
 {
   XOM om = (XOM)NULL;
   int count;
-  char *path;
   char lc_dir[BUFSIZE];
   char *lc_name;
   XOM (*om_openOM)() = (XOM(*)())NULL;
@@ -635,46 +591,16 @@ char *res_class;
   for (; count-- > 0; objects_list++) {
     if (objects_list->type != XOM_OBJECT ||
 	strcmp(objects_list->locale_name, lc_name)) continue;
-    if (!objects_list->dl_module) {
-      path = __lc_path(objects_list->dl_name, lc_dir);
-#if defined(hpux)
-      objects_list->dl_module = shl_load(path, BIND_DEFERRED, 0L);
-#else
-      objects_list->dl_module = dlopen(path, RTLD_LAZY);
-#endif
-      Xfree(path);
-      if (!objects_list->dl_module) continue;
-    }
-#if defined(hpux)
-    getsyms_cnt = shl_getsymbols(objects_list->dl_module, TYPE_PROCEDURE,
-	EXPORT_SYMBOLS, malloc, &symbols);
-
-    for(i=0; i<getsyms_cnt; i++) {
-	if(!strcmp(symbols[i].name, objects_list->open)) {
-	    om_openOM = symbols[i].value;
-	    break;
-	}
-     }
-
-    if(getsyms_cnt > 0) {
-	free(symbols);
-    }
-#else
-    om_openOM = (XOM(*)())try_both_dlsym(objects_list->dl_module,
-					 objects_list->open);
+    if (!open_object (objects_list, lc_dir))
+        continue;
+    
+    om_openOM = (XOM(*)())fetch_symbol(objects_list, objects_list->open);
     if (!om_openOM) continue;
-#endif
     om = (*om_openOM)(lcd, display, rdb, res_name, res_class);
     if (om != (XOM)NULL) {
-      break;
+        break;
     }
-    om_openOM = 0;
-#if defined(hpux)
-    shl_unload(objects_list->dl_module);
-#else
-    dlclose(objects_list->dl_module);
-#endif
-    objects_list->dl_module = NULL;
+    close_object(objects_list);
   }
   return (XOM)om;
 }

Reply via email to