mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-26 07:26:15 +01:00
57a7a2b010
2000-12-22 Sebastian Wilhelmi <wilhelmi@ira.uka.de> * configure.in: Determine the suffix of the shared librarries for this system. This is done analogous to ltconfig.sh. G_MODULE_SUFFIX in glibconfig.h is set to either "sl", "dll", or (most often) "so". * tests/Makefile.am, tests/module-test.c, tests/libmoduletestplugin_a.c, tests/libmoduletestplugin_b.c: Added new testcase for gmodule. This is mostly copied from gmodule/testgmodule.c, but unlike that is is quiet. (Why BTW are some tests that verbose, not to say loquacious...) * gmodule.c: Make g_module_open more tolerant wrt to the module name. First it tries to open the module as named, if that fails, it checks, whether it is a libtool archive and parses it, if that fails it appends the systems shared library suffix (i.e. ".so") (if not already found) and tries again and if that fails it tries to append the ".la" libtool suffix (if not already found) and parses it. * gmodule.c: Lock recursive mutex during most module functions for safety. * gmodule-dl.c: Return an error from _g_module_symbol only, if dlerror says so. All other functions return an error as well, if dlerror returns NULL. * testgmodule.c: Thanks to the above change the #ifdefs have vanished. * glib/glib-sections.txt: Added G_MODULE_SUFFIX. * glib/tmpl/modules.sgml: Updated.
546 lines
13 KiB
C
546 lines
13 KiB
C
/* GMODULE - GLIB wrapper code for dynamic module loading
|
|
* Copyright (C) 1998 Tim Janik
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
/*
|
|
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
|
|
* file for a list of people on the GLib Team. See the ChangeLog
|
|
* files for a list of changes. These files are distributed with
|
|
* GLib at ftp://ftp.gtk.org/pub/gtk/.
|
|
*/
|
|
|
|
/*
|
|
* MT safe
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
#include "gmodule.h"
|
|
#include "gmoduleconf.h"
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
/* We maintain a list of modules, so we can reference count them.
|
|
* That's needed because some platforms don't support refernce counts on
|
|
* modules e.g. the shl_* implementation of HP-UX
|
|
* (http://www.stat.umn.edu/~luke/xls/projects/dlbasics/dlbasics.html).
|
|
* Also, the module for the program itself is kept seperatedly for
|
|
* faster access and because it has special semantics.
|
|
*/
|
|
|
|
|
|
/* --- structures --- */
|
|
struct _GModule
|
|
{
|
|
gchar *file_name;
|
|
gpointer handle;
|
|
guint ref_count : 31;
|
|
guint is_resident : 1;
|
|
GModuleUnload unload;
|
|
GModule *next;
|
|
};
|
|
|
|
|
|
/* --- prototypes --- */
|
|
static gpointer _g_module_open (const gchar *file_name,
|
|
gboolean bind_lazy);
|
|
static void _g_module_close (gpointer handle,
|
|
gboolean is_unref);
|
|
static gpointer _g_module_self (void);
|
|
static gpointer _g_module_symbol (gpointer handle,
|
|
const gchar *symbol_name);
|
|
static gchar* _g_module_build_path (const gchar *directory,
|
|
const gchar *module_name);
|
|
static inline void g_module_set_error (const gchar *error);
|
|
static inline GModule* g_module_find_by_handle (gpointer handle);
|
|
static inline GModule* g_module_find_by_name (const gchar *name);
|
|
|
|
|
|
/* --- variables --- */
|
|
const char *g_log_domain_gmodule = "GModule";
|
|
static GModule *modules = NULL;
|
|
static GModule *main_module = NULL;
|
|
static GStaticPrivate module_error_private = G_STATIC_PRIVATE_INIT;
|
|
|
|
|
|
/* --- inline functions --- */
|
|
static inline GModule*
|
|
g_module_find_by_handle (gpointer handle)
|
|
{
|
|
GModule *module;
|
|
GModule *retval = NULL;
|
|
|
|
if (main_module && main_module->handle == handle)
|
|
retval = main_module;
|
|
else
|
|
for (module = modules; module; module = module->next)
|
|
if (handle == module->handle)
|
|
{
|
|
retval = module;
|
|
break;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static inline GModule*
|
|
g_module_find_by_name (const gchar *name)
|
|
{
|
|
GModule *module;
|
|
GModule *retval = NULL;
|
|
|
|
for (module = modules; module; module = module->next)
|
|
if (strcmp (name, module->file_name) == 0)
|
|
{
|
|
retval = module;
|
|
break;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static inline void
|
|
g_module_set_error (const gchar *error)
|
|
{
|
|
g_static_private_set (&module_error_private, g_strdup (error), g_free);
|
|
errno = 0;
|
|
}
|
|
|
|
|
|
/* --- include platform specifc code --- */
|
|
#define SUPPORT_OR_RETURN(rv) { g_module_set_error (NULL); }
|
|
#if (G_MODULE_IMPL == G_MODULE_IMPL_DL)
|
|
#include "gmodule-dl.c"
|
|
#elif (G_MODULE_IMPL == G_MODULE_IMPL_DLD)
|
|
#include "gmodule-dld.c"
|
|
#elif (G_MODULE_IMPL == G_MODULE_IMPL_WIN32)
|
|
#include "gmodule-win32.c"
|
|
#else
|
|
#undef SUPPORT_OR_RETURN
|
|
#define SUPPORT_OR_RETURN(rv) { g_module_set_error ("dynamic modules are " \
|
|
"not supported by this system"); return rv; }
|
|
static gpointer
|
|
_g_module_open (const gchar *file_name,
|
|
gboolean bind_lazy)
|
|
{
|
|
return NULL;
|
|
}
|
|
static void
|
|
_g_module_close (gpointer handle,
|
|
gboolean is_unref)
|
|
{
|
|
}
|
|
static gpointer
|
|
_g_module_self (void)
|
|
{
|
|
return NULL;
|
|
}
|
|
static gpointer
|
|
_g_module_symbol (gpointer handle,
|
|
const gchar *symbol_name)
|
|
{
|
|
return NULL;
|
|
}
|
|
static gchar*
|
|
_g_module_build_path (const gchar *directory,
|
|
const gchar *module_name)
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif /* no implementation */
|
|
|
|
/* --- functions --- */
|
|
gboolean
|
|
g_module_supported (void)
|
|
{
|
|
SUPPORT_OR_RETURN (FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gchar*
|
|
parse_libtool_archive (const gchar* libtool_name)
|
|
{
|
|
const gint TOKEN_DLNAME = G_TOKEN_LAST + 1;
|
|
const gint TOKEN_INSTALLED = G_TOKEN_LAST + 2;
|
|
const gint TOKEN_LIBDIR = G_TOKEN_LAST + 3;
|
|
gchar *lt_dlname = NULL;
|
|
gboolean lt_installed = TRUE;
|
|
gchar *lt_libdir = NULL;
|
|
gchar *name;
|
|
GTokenType token;
|
|
GScanner *scanner;
|
|
|
|
int fd = open (libtool_name, O_RDONLY, 0);
|
|
if (fd < 0)
|
|
{
|
|
g_module_set_error ("couldn't open libtool archive");
|
|
return NULL;
|
|
}
|
|
/* search libtool's dlname specification */
|
|
scanner = g_scanner_new (NULL);
|
|
g_scanner_input_file (scanner, fd);
|
|
scanner->config->symbol_2_token = TRUE;
|
|
g_scanner_scope_add_symbol (scanner, 0, "dlname",
|
|
GUINT_TO_POINTER (TOKEN_DLNAME));
|
|
g_scanner_scope_add_symbol (scanner, 0, "installed",
|
|
GUINT_TO_POINTER (TOKEN_INSTALLED));
|
|
g_scanner_scope_add_symbol (scanner, 0, "libdir",
|
|
GUINT_TO_POINTER (TOKEN_LIBDIR));
|
|
while (!g_scanner_eof (scanner))
|
|
{
|
|
token = g_scanner_get_next_token (scanner);
|
|
if (token == TOKEN_DLNAME || token == TOKEN_INSTALLED ||
|
|
token == TOKEN_LIBDIR)
|
|
{
|
|
if (g_scanner_get_next_token (scanner) != '=' ||
|
|
g_scanner_get_next_token (scanner) !=
|
|
(token == TOKEN_INSTALLED ?
|
|
G_TOKEN_IDENTIFIER : G_TOKEN_STRING))
|
|
{
|
|
g_module_set_error ("libtool archive has unknown format");
|
|
|
|
g_free (lt_dlname);
|
|
g_free (lt_libdir);
|
|
g_scanner_destroy (scanner);
|
|
close (fd);
|
|
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
if (token == TOKEN_DLNAME)
|
|
{
|
|
g_free (lt_dlname);
|
|
lt_dlname = g_strdup (scanner->value.v_string);
|
|
}
|
|
else if (token == TOKEN_INSTALLED)
|
|
lt_installed =
|
|
strcmp (scanner->value.v_identifier, "yes") == 0;
|
|
else /* token == TOKEN_LIBDIR */
|
|
{
|
|
g_free (lt_libdir);
|
|
lt_libdir = g_strdup (scanner->value.v_string);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!lt_installed)
|
|
{
|
|
gchar *dir = g_path_get_dirname (libtool_name);
|
|
g_free (lt_libdir);
|
|
lt_libdir = g_strconcat (dir, G_DIR_SEPARATOR_S ".libs", NULL);
|
|
}
|
|
|
|
name = g_module_build_path (lt_libdir, lt_dlname);
|
|
|
|
g_free (lt_dlname);
|
|
g_free (lt_libdir);
|
|
g_scanner_destroy (scanner);
|
|
close (fd);
|
|
|
|
return name;
|
|
}
|
|
|
|
static inline gboolean
|
|
g_str_check_suffix (const gchar* string, const gchar* suffix)
|
|
{
|
|
guint string_len = strlen (string);
|
|
guint suffix_len = strlen (suffix);
|
|
|
|
return string_len >= suffix_len &&
|
|
strcmp (string + string_len - suffix_len, suffix) == 0;
|
|
}
|
|
|
|
static GStaticRecMutex g_module_global_lock = G_STATIC_REC_MUTEX_INIT;
|
|
|
|
GModule*
|
|
g_module_open (const gchar *file_name,
|
|
GModuleFlags flags)
|
|
{
|
|
GModule *module;
|
|
gpointer handle;
|
|
|
|
SUPPORT_OR_RETURN (NULL);
|
|
|
|
g_static_rec_mutex_lock (&g_module_global_lock);
|
|
if (!file_name)
|
|
{
|
|
if (!main_module)
|
|
{
|
|
handle = _g_module_self ();
|
|
if (handle)
|
|
{
|
|
main_module = g_new (GModule, 1);
|
|
main_module->file_name = NULL;
|
|
main_module->handle = handle;
|
|
main_module->ref_count = 1;
|
|
main_module->is_resident = TRUE;
|
|
main_module->unload = NULL;
|
|
main_module->next = NULL;
|
|
}
|
|
}
|
|
|
|
g_static_rec_mutex_unlock (&g_module_global_lock);
|
|
return main_module;
|
|
}
|
|
|
|
/* we first search the module list by name */
|
|
module = g_module_find_by_name (file_name);
|
|
if (module)
|
|
{
|
|
module->ref_count++;
|
|
|
|
g_static_rec_mutex_unlock (&g_module_global_lock);
|
|
return module;
|
|
}
|
|
|
|
/* First we try to open the module as provided */
|
|
handle = _g_module_open (file_name, (flags & G_MODULE_BIND_LAZY) != 0);
|
|
|
|
/* If not found, we check, if it is a libtool archive */
|
|
if (!handle && g_str_check_suffix (file_name, ".la"))
|
|
{
|
|
gchar *name = parse_libtool_archive (file_name);
|
|
if (name)
|
|
{
|
|
handle = _g_module_open (name, (flags & G_MODULE_BIND_LAZY) != 0);
|
|
g_free (name);
|
|
}
|
|
}
|
|
|
|
/* If still not found, we check, if it is a library name without suffix */
|
|
if (!handle && !g_str_check_suffix (file_name, "." G_MODULE_SUFFIX))
|
|
{
|
|
gchar *name = g_strconcat (file_name, "." G_MODULE_SUFFIX, NULL);
|
|
handle = _g_module_open (name, (flags & G_MODULE_BIND_LAZY) != 0);
|
|
g_free (name);
|
|
}
|
|
|
|
/* If still not found, we check, if it is a libtool archive name
|
|
* without suffix */
|
|
if (!handle && !g_str_check_suffix (file_name, ".la"))
|
|
{
|
|
gchar *la_name = g_strconcat (file_name, ".la", NULL);
|
|
gchar *name = parse_libtool_archive (la_name);
|
|
if (name)
|
|
{
|
|
handle = _g_module_open (name, (flags & G_MODULE_BIND_LAZY) != 0);
|
|
g_free (name);
|
|
}
|
|
g_free (la_name);
|
|
}
|
|
|
|
if (handle)
|
|
{
|
|
gchar *saved_error;
|
|
GModuleCheckInit check_init;
|
|
const gchar *check_failed = NULL;
|
|
|
|
/* search the module list by handle, since file names are not unique */
|
|
module = g_module_find_by_handle (handle);
|
|
if (module)
|
|
{
|
|
_g_module_close (module->handle, TRUE);
|
|
module->ref_count++;
|
|
g_module_set_error (NULL);
|
|
|
|
g_static_rec_mutex_unlock (&g_module_global_lock);
|
|
return module;
|
|
}
|
|
|
|
saved_error = g_strdup (g_module_error ());
|
|
g_module_set_error (NULL);
|
|
|
|
module = g_new (GModule, 1);
|
|
module->file_name = g_strdup (file_name);
|
|
module->handle = handle;
|
|
module->ref_count = 1;
|
|
module->is_resident = FALSE;
|
|
module->unload = NULL;
|
|
module->next = modules;
|
|
modules = module;
|
|
|
|
/* check initialization */
|
|
if (g_module_symbol (module, "g_module_check_init", (gpointer) &check_init))
|
|
check_failed = check_init (module);
|
|
|
|
/* we don't call unload() if the initialization check failed. */
|
|
if (!check_failed)
|
|
g_module_symbol (module, "g_module_unload", (gpointer) &module->unload);
|
|
|
|
if (check_failed)
|
|
{
|
|
gchar *error;
|
|
|
|
error = g_strconcat ("GModule initialization check failed: ", check_failed, NULL);
|
|
g_module_close (module);
|
|
module = NULL;
|
|
g_module_set_error (error);
|
|
g_free (error);
|
|
}
|
|
else
|
|
g_module_set_error (saved_error);
|
|
|
|
g_free (saved_error);
|
|
}
|
|
|
|
g_static_rec_mutex_unlock (&g_module_global_lock);
|
|
return module;
|
|
}
|
|
|
|
gboolean
|
|
g_module_close (GModule *module)
|
|
{
|
|
SUPPORT_OR_RETURN (FALSE);
|
|
|
|
g_return_val_if_fail (module != NULL, FALSE);
|
|
g_return_val_if_fail (module->ref_count > 0, FALSE);
|
|
|
|
g_static_rec_mutex_lock (&g_module_global_lock);
|
|
|
|
module->ref_count--;
|
|
|
|
if (!module->ref_count && !module->is_resident && module->unload)
|
|
{
|
|
GModuleUnload unload;
|
|
|
|
unload = module->unload;
|
|
module->unload = NULL;
|
|
unload (module);
|
|
}
|
|
|
|
if (!module->ref_count && !module->is_resident)
|
|
{
|
|
GModule *last;
|
|
GModule *node;
|
|
|
|
last = NULL;
|
|
|
|
node = modules;
|
|
while (node)
|
|
{
|
|
if (node == module)
|
|
{
|
|
if (last)
|
|
last->next = node->next;
|
|
else
|
|
modules = node->next;
|
|
break;
|
|
}
|
|
last = node;
|
|
node = last->next;
|
|
}
|
|
module->next = NULL;
|
|
|
|
_g_module_close (module->handle, FALSE);
|
|
g_free (module->file_name);
|
|
|
|
g_free (module);
|
|
}
|
|
|
|
g_static_rec_mutex_unlock (&g_module_global_lock);
|
|
return g_module_error() == NULL;
|
|
}
|
|
|
|
void
|
|
g_module_make_resident (GModule *module)
|
|
{
|
|
g_return_if_fail (module != NULL);
|
|
|
|
module->is_resident = TRUE;
|
|
}
|
|
|
|
gchar*
|
|
g_module_error (void)
|
|
{
|
|
return g_static_private_get (&module_error_private);
|
|
}
|
|
|
|
gboolean
|
|
g_module_symbol (GModule *module,
|
|
const gchar *symbol_name,
|
|
gpointer *symbol)
|
|
{
|
|
gchar *module_error;
|
|
|
|
if (symbol)
|
|
*symbol = NULL;
|
|
SUPPORT_OR_RETURN (FALSE);
|
|
|
|
g_return_val_if_fail (module != NULL, FALSE);
|
|
g_return_val_if_fail (symbol_name != NULL, FALSE);
|
|
g_return_val_if_fail (symbol != NULL, FALSE);
|
|
|
|
g_static_rec_mutex_lock (&g_module_global_lock);
|
|
|
|
#ifdef G_MODULE_NEED_USCORE
|
|
{
|
|
gchar *name;
|
|
|
|
name = g_strconcat ("_", symbol_name, NULL);
|
|
*symbol = _g_module_symbol (module->handle, name);
|
|
g_free (name);
|
|
}
|
|
#else /* !G_MODULE_NEED_USCORE */
|
|
*symbol = _g_module_symbol (module->handle, symbol_name);
|
|
#endif /* !G_MODULE_NEED_USCORE */
|
|
|
|
module_error = g_module_error ();
|
|
if (module_error)
|
|
{
|
|
gchar *error;
|
|
|
|
error = g_strconcat ("`", symbol_name, "': ", module_error, NULL);
|
|
g_module_set_error (error);
|
|
g_free (error);
|
|
*symbol = NULL;
|
|
}
|
|
|
|
g_static_rec_mutex_unlock (&g_module_global_lock);
|
|
return !module_error;
|
|
}
|
|
|
|
gchar*
|
|
g_module_name (GModule *module)
|
|
{
|
|
g_return_val_if_fail (module != NULL, NULL);
|
|
|
|
if (module == main_module)
|
|
return "main";
|
|
|
|
return module->file_name;
|
|
}
|
|
|
|
gchar*
|
|
g_module_build_path (const gchar *directory,
|
|
const gchar *module_name)
|
|
{
|
|
g_return_val_if_fail (module_name != NULL, NULL);
|
|
|
|
return _g_module_build_path (directory, module_name);
|
|
}
|