2 * mono-dl.c: Interface to the dynamic linker
5 * Mono Team (http://www.mono-project.com)
7 * Copyright 2001-2004 Ximian, Inc.
8 * Copyright 2004-2009 Novell, Inc.
11 #include "mono/utils/mono-dl.h"
12 #include "mono/utils/mono-embed.h"
22 static const char suffixes [][5] = {
25 #elif defined(__APPLE__)
26 #define SOPREFIX "lib"
27 static const char suffixes [][8] = {
33 #define SOPREFIX "lib"
34 static const char suffixes [][4] = {
44 #define SO_HANDLE_TYPE HMODULE
45 #define LL_SO_OPEN(file,flags) w32_load_module ((file), (flags))
46 #define LL_SO_CLOSE(module) do { if (!(module)->main_module) FreeLibrary ((module)->handle); } while (0)
47 #define LL_SO_SYMBOL(module, name) w32_find_symbol ((module), (name))
48 #define LL_SO_TRFLAGS(flags) 0
49 #define LL_SO_ERROR() w32_dlerror ()
51 #elif defined (HAVE_DL_LOADER)
57 #endif /* RTLD_LAZY */
59 #define SO_HANDLE_TYPE void*
60 #define LL_SO_OPEN(file,flags) dlopen ((file), (flags))
61 #define LL_SO_CLOSE(module) dlclose ((module)->handle)
62 #define LL_SO_SYMBOL(module, name) dlsym ((module)->handle, (name))
63 #define LL_SO_TRFLAGS(flags) convert_flags ((flags))
64 #define LL_SO_ERROR() g_strdup (dlerror ())
67 convert_flags (int flags)
69 int lflags = flags & MONO_DL_LOCAL? 0: RTLD_GLOBAL;
71 if (flags & MONO_DL_LAZY)
79 /* no dynamic loader supported */
80 #define SO_HANDLE_TYPE void*
81 #define LL_SO_OPEN(file,flags) NULL
82 #define LL_SO_CLOSE(module)
83 #define LL_SO_SYMBOL(module, name) NULL
84 #define LL_SO_TRFLAGS(flags) (flags)
85 #define LL_SO_ERROR() g_strdup ("No support for dynamic loader")
89 static GSList *fallback_handlers;
91 struct MonoDlFallbackHandler {
92 MonoDlFallbackLoad load_func;
93 MonoDlFallbackSymbol symbol_func;
94 MonoDlFallbackClose close_func;
99 SO_HANDLE_TYPE handle;
102 /* If not NULL, use the methods in MonoDlFallbackHandler instead of the LL_* methods */
103 MonoDlFallbackHandler *dl_fallback;
113 DWORD code = GetLastError ();
115 if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL,
116 code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buf, 0, NULL))
118 ret = g_utf16_to_utf8 (buf, wcslen(buf), NULL, NULL, NULL);
121 g_assert_not_reached ();
127 w32_find_symbol (MonoDl *module, const gchar *symbol_name)
130 DWORD buffer_size = sizeof (HMODULE) * 1024;
132 gpointer proc = NULL;
134 /* get the symbol directly from the specified module */
135 if (!module->main_module)
136 return GetProcAddress (module->handle, symbol_name);
138 /* get the symbol from the main module */
139 proc = GetProcAddress (module->handle, symbol_name);
143 /* get the symbol from the loaded DLLs */
144 modules = (HMODULE *) g_malloc (buffer_size);
148 if (!EnumProcessModules (GetCurrentProcess (), modules,
149 buffer_size, &needed)) {
154 /* check whether the supplied buffer was too small, realloc, retry */
155 if (needed > buffer_size) {
158 buffer_size = needed;
159 modules = (HMODULE *) g_malloc (buffer_size);
164 if (!EnumProcessModules (GetCurrentProcess (), modules,
165 buffer_size, &needed)) {
171 for (i = 0; i < needed / sizeof (HANDLE); i++) {
172 proc = GetProcAddress (modules [i], symbol_name);
185 w32_load_module (const char* file, int flags)
187 gpointer hModule = NULL;
189 gunichar2* file_utf16 = g_utf8_to_utf16 (file, strlen (file), NULL, NULL, NULL);
190 guint last_sem = SetErrorMode (SEM_FAILCRITICALERRORS);
191 guint32 last_error = 0;
193 hModule = LoadLibrary (file_utf16);
195 last_error = GetLastError ();
197 SetErrorMode (last_sem);
201 SetLastError (last_error);
203 hModule = GetModuleHandle (NULL);
210 * read a value string from line with any of the following formats:
213 * \s*=\s*non_white_space_string
216 read_string (char *p, FILE *file)
220 while (*p && isspace (*p))
226 while (*p && isspace (*p))
228 if (*p == '\'' || *p == '"') {
232 endp = strchr (p, t);
233 /* FIXME: may need to read more from file... */
237 return g_memdup (startp, (endp - startp) + 1);
242 while (*p && !isspace (*p))
245 return g_memdup (startp, (p - startp) + 1);
249 * parse a libtool .la file and return the path of the file to dlopen ()
250 * handling both the installed and uninstalled cases
253 get_dl_name_from_libtool (const char *libtool_file)
257 char *line, *dlname = NULL, *libdir = NULL, *installed = NULL;
258 if (!(file = fopen (libtool_file, "r")))
260 while ((line = fgets (buf, 512, file))) {
261 while (*line && isspace (*line))
263 if (*line == '#' || *line == 0)
265 if (strncmp ("dlname", line, 6) == 0) {
267 dlname = read_string (line + 6, file);
268 } else if (strncmp ("libdir", line, 6) == 0) {
270 libdir = read_string (line + 6, file);
271 } else if (strncmp ("installed", line, 9) == 0) {
273 installed = read_string (line + 9, file);
278 if (installed && strcmp (installed, "no") == 0) {
279 char *dir = g_path_get_dirname (libtool_file);
281 line = g_strconcat (dir, G_DIR_SEPARATOR_S ".libs" G_DIR_SEPARATOR_S, dlname, NULL);
284 if (libdir && dlname)
285 line = g_strconcat (libdir, G_DIR_SEPARATOR_S, dlname, NULL);
295 * @name: name of file containing shared module
297 * @error_msg: pointer for error message on failure
299 * Load the given file @name as a shared library or dynamically loadable
300 * module. @name can be NULL to indicate loading the currently executing
302 * @flags can have the MONO_DL_LOCAL bit set to avoid exporting symbols
303 * from the module to the shared namespace. The MONO_DL_LAZY bit can be set
304 * to lazily load the symbols instead of resolving everithing at load time.
305 * @error_msg points to a string where an error message will be stored in
306 * case of failure. The error must be released with g_free.
308 * Returns: a MonoDl pointer on success, NULL on failure.
311 mono_dl_open (const char *name, int flags, char **error_msg)
315 MonoDlFallbackHandler *dl_fallback = NULL;
316 int lflags = LL_SO_TRFLAGS (flags);
321 module = malloc (sizeof (MonoDl));
324 *error_msg = g_strdup ("Out of memory");
327 module->main_module = name == NULL? TRUE: FALSE;
328 lib = LL_SO_OPEN (name, lflags);
331 for (node = fallback_handlers; node != NULL; node = node->next){
332 MonoDlFallbackHandler *handler = (MonoDlFallbackHandler *) node->data;
336 lib = handler->load_func (name, lflags, error_msg, handler->user_data);
337 if (error_msg && *error_msg != NULL)
341 dl_fallback = handler;
346 if (!lib && !dl_fallback) {
351 /* This platform does not support dlopen */
358 ext = strrchr (name, '.');
359 if (ext && strcmp (ext, ".la") == 0)
361 lname = g_strconcat (name, suff, NULL);
362 llname = get_dl_name_from_libtool (lname);
365 lib = LL_SO_OPEN (llname, lflags);
370 *error_msg = LL_SO_ERROR ();
376 module->handle = lib;
377 module->dl_fallback = dl_fallback;
383 * @module: a MonoDl pointer
385 * @symbol: pointer for the result value
387 * Load the address of symbol @name from the given @module.
388 * The address is stored in the pointer pointed to by @symbol.
390 * Returns: NULL on success, an error message on failure
393 mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
398 if (module->dl_fallback) {
399 sym = module->dl_fallback->symbol_func (module->handle, name, &err, module->dl_fallback->user_data);
401 #if MONO_DL_NEED_USCORE
403 char *usname = malloc (strlen (name) + 2);
405 strcpy (usname + 1, name);
406 sym = LL_SO_SYMBOL (module, usname);
410 sym = LL_SO_SYMBOL (module, name);
421 return (module->dl_fallback != NULL) ? err : LL_SO_ERROR ();
426 * @module: a MonoDl pointer
428 * Unload the given module and free the module memory.
430 * Returns: 0 on success.
433 mono_dl_close (MonoDl *module)
435 MonoDlFallbackHandler *dl_fallback = module->dl_fallback;
438 if (dl_fallback->close_func != NULL)
439 dl_fallback->close_func (module->handle, dl_fallback->user_data);
441 LL_SO_CLOSE (module);
447 * mono_dl_build_path:
448 * @directory: optional directory
449 * @name: base name of the library
450 * @iter: iterator token
452 * Given a directory name and the base name of a library, iterate
453 * over the possible file names of the library, taking into account
454 * the possible different suffixes and prefixes on the host platform.
456 * The returned file name must be freed by the caller.
457 * @iter must point to a NULL pointer the first time the function is called
458 * and then passed unchanged to the following calls.
459 * Returns: the filename or NULL at the end of the iteration
462 mono_dl_build_path (const char *directory, const char *name, void **iter)
476 The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our
477 "bootstrap" phase in which we check the passed name verbatim and only if we fail to find
478 the dll thus named, we start appending suffixes, each time increasing idx twice (since now
479 the 0 value became special and we need to offset idx to a 0-based array index). This is
480 done to handle situations when mapped dll name is specified as libsomething.so.1 or
481 libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill
484 idx = GPOINTER_TO_UINT (*iter);
491 if (idx >= G_N_ELEMENTS (suffixes))
494 suffix = suffixes [idx];
495 suffixlen = strlen (suffix);
498 prlen = strlen (SOPREFIX);
499 if (prlen && strncmp (name, SOPREFIX, prlen) != 0)
504 if (first_call || (suffixlen && strstr (name, suffix) == (name + strlen (name) - suffixlen)))
507 if (directory && *directory)
508 res = g_strconcat (directory, G_DIR_SEPARATOR_S, prefix, name, suffix, NULL);
510 res = g_strconcat (prefix, name, suffix, NULL);
514 *iter = GUINT_TO_POINTER (idx);
518 MonoDlFallbackHandler *
519 mono_dl_fallback_register (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data)
521 MonoDlFallbackHandler *handler;
523 g_return_val_if_fail (load_func != NULL, NULL);
524 g_return_val_if_fail (symbol_func != NULL, NULL);
526 handler = g_new (MonoDlFallbackHandler, 1);
527 handler->load_func = load_func;
528 handler->symbol_func = symbol_func;
529 handler->close_func = close_func;
530 handler->user_data = user_data;
532 fallback_handlers = g_slist_prepend (fallback_handlers, handler);
538 mono_dl_fallback_unregister (MonoDlFallbackHandler *handler)
542 found = g_slist_find (fallback_handlers, handler);
546 g_slist_remove (fallback_handlers, handler);