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"
13 #include "mono/utils/mono-path.h"
23 static const char suffixes [][5] = {
26 #elif defined(__APPLE__)
27 #define SOPREFIX "lib"
28 static const char suffixes [][8] = {
34 #define SOPREFIX "lib"
35 static const char suffixes [][4] = {
45 #define SO_HANDLE_TYPE HMODULE
46 #define LL_SO_OPEN(file,flags) w32_load_module ((file), (flags))
47 #define LL_SO_CLOSE(module) do { if (!(module)->main_module) FreeLibrary ((module)->handle); } while (0)
48 #define LL_SO_SYMBOL(module, name) w32_find_symbol ((module), (name))
49 #define LL_SO_TRFLAGS(flags) 0
50 #define LL_SO_ERROR() w32_dlerror ()
52 #elif defined (HAVE_DL_LOADER)
58 #include <mach-o/dyld.h>
64 #endif /* RTLD_LAZY */
66 #define SO_HANDLE_TYPE void*
67 #define LL_SO_OPEN(file,flags) dlopen ((file), (flags))
68 #define LL_SO_CLOSE(module) dlclose ((module)->handle)
69 #define LL_SO_SYMBOL(module, name) dlsym ((module)->handle, (name))
70 #define LL_SO_TRFLAGS(flags) convert_flags ((flags))
71 #define LL_SO_ERROR() g_strdup (dlerror ())
74 convert_flags (int flags)
76 int lflags = flags & MONO_DL_LOCAL? 0: RTLD_GLOBAL;
78 if (flags & MONO_DL_LAZY)
86 /* no dynamic loader supported */
87 #define SO_HANDLE_TYPE void*
88 #define LL_SO_OPEN(file,flags) NULL
89 #define LL_SO_CLOSE(module)
90 #define LL_SO_SYMBOL(module, name) NULL
91 #define LL_SO_TRFLAGS(flags) (flags)
92 #define LL_SO_ERROR() g_strdup ("No support for dynamic loader")
96 static GSList *fallback_handlers;
98 struct MonoDlFallbackHandler {
99 MonoDlFallbackLoad load_func;
100 MonoDlFallbackSymbol symbol_func;
101 MonoDlFallbackClose close_func;
106 SO_HANDLE_TYPE handle;
109 /* If not NULL, use the methods in MonoDlFallbackHandler instead of the LL_* methods */
110 MonoDlFallbackHandler *dl_fallback;
120 DWORD code = GetLastError ();
122 if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
123 code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buf, 0, NULL))
125 ret = g_utf16_to_utf8 (buf, wcslen(buf), NULL, NULL, NULL);
128 g_assert_not_reached ();
134 w32_find_symbol (MonoDl *module, const gchar *symbol_name)
137 DWORD buffer_size = sizeof (HMODULE) * 1024;
139 gpointer proc = NULL;
141 /* get the symbol directly from the specified module */
142 if (!module->main_module)
143 return GetProcAddress (module->handle, symbol_name);
145 /* get the symbol from the main module */
146 proc = GetProcAddress (module->handle, symbol_name);
150 /* get the symbol from the loaded DLLs */
151 modules = (HMODULE *) g_malloc (buffer_size);
155 if (!EnumProcessModules (GetCurrentProcess (), modules,
156 buffer_size, &needed)) {
161 /* check whether the supplied buffer was too small, realloc, retry */
162 if (needed > buffer_size) {
165 buffer_size = needed;
166 modules = (HMODULE *) g_malloc (buffer_size);
171 if (!EnumProcessModules (GetCurrentProcess (), modules,
172 buffer_size, &needed)) {
178 for (i = 0; i < needed / sizeof (HANDLE); i++) {
179 proc = GetProcAddress (modules [i], symbol_name);
192 w32_load_module (const char* file, int flags)
194 gpointer hModule = NULL;
196 gunichar2* file_utf16 = g_utf8_to_utf16 (file, strlen (file), NULL, NULL, NULL);
197 guint last_sem = SetErrorMode (SEM_FAILCRITICALERRORS);
198 guint32 last_error = 0;
200 hModule = LoadLibrary (file_utf16);
202 last_error = GetLastError ();
204 SetErrorMode (last_sem);
208 SetLastError (last_error);
210 hModule = GetModuleHandle (NULL);
217 * read a value string from line with any of the following formats:
220 * \s*=\s*non_white_space_string
223 read_string (char *p, FILE *file)
227 while (*p && isspace (*p))
233 while (*p && isspace (*p))
235 if (*p == '\'' || *p == '"') {
239 endp = strchr (p, t);
240 /* FIXME: may need to read more from file... */
244 return g_memdup (startp, (endp - startp) + 1);
249 while (*p && !isspace (*p))
252 return g_memdup (startp, (p - startp) + 1);
256 * parse a libtool .la file and return the path of the file to dlopen ()
257 * handling both the installed and uninstalled cases
260 get_dl_name_from_libtool (const char *libtool_file)
264 char *line, *dlname = NULL, *libdir = NULL, *installed = NULL;
265 if (!(file = fopen (libtool_file, "r")))
267 while ((line = fgets (buf, 512, file))) {
268 while (*line && isspace (*line))
270 if (*line == '#' || *line == 0)
272 if (strncmp ("dlname", line, 6) == 0) {
274 dlname = read_string (line + 6, file);
275 } else if (strncmp ("libdir", line, 6) == 0) {
277 libdir = read_string (line + 6, file);
278 } else if (strncmp ("installed", line, 9) == 0) {
280 installed = read_string (line + 9, file);
285 if (installed && strcmp (installed, "no") == 0) {
286 char *dir = g_path_get_dirname (libtool_file);
288 line = g_strconcat (dir, G_DIR_SEPARATOR_S ".libs" G_DIR_SEPARATOR_S, dlname, NULL);
291 if (libdir && dlname)
292 line = g_strconcat (libdir, G_DIR_SEPARATOR_S, dlname, NULL);
302 * @name: name of file containing shared module
304 * @error_msg: pointer for error message on failure
306 * Load the given file @name as a shared library or dynamically loadable
307 * module. @name can be NULL to indicate loading the currently executing
309 * @flags can have the MONO_DL_LOCAL bit set to avoid exporting symbols
310 * from the module to the shared namespace. The MONO_DL_LAZY bit can be set
311 * to lazily load the symbols instead of resolving everithing at load time.
312 * @error_msg points to a string where an error message will be stored in
313 * case of failure. The error must be released with g_free.
315 * Returns: a MonoDl pointer on success, NULL on failure.
318 mono_dl_open (const char *name, int flags, char **error_msg)
322 MonoDlFallbackHandler *dl_fallback = NULL;
323 int lflags = LL_SO_TRFLAGS (flags);
328 module = malloc (sizeof (MonoDl));
331 *error_msg = g_strdup ("Out of memory");
334 module->main_module = name == NULL? TRUE: FALSE;
336 #ifdef PLATFORM_ANDROID
337 /* Bionic doesn't support NULL filenames */
341 lib = LL_SO_OPEN (name, lflags);
343 lib = LL_SO_OPEN (name, lflags);
347 for (node = fallback_handlers; node != NULL; node = node->next){
348 MonoDlFallbackHandler *handler = (MonoDlFallbackHandler *) node->data;
352 lib = handler->load_func (name, lflags, error_msg, handler->user_data);
353 if (error_msg && *error_msg != NULL)
357 dl_fallback = handler;
362 if (!lib && !dl_fallback) {
367 /* This platform does not support dlopen */
374 ext = strrchr (name, '.');
375 if (ext && strcmp (ext, ".la") == 0)
377 lname = g_strconcat (name, suff, NULL);
378 llname = get_dl_name_from_libtool (lname);
381 lib = LL_SO_OPEN (llname, lflags);
386 *error_msg = LL_SO_ERROR ();
392 module->handle = lib;
393 module->dl_fallback = dl_fallback;
399 * @module: a MonoDl pointer
401 * @symbol: pointer for the result value
403 * Load the address of symbol @name from the given @module.
404 * The address is stored in the pointer pointed to by @symbol.
406 * Returns: NULL on success, an error message on failure
409 mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
414 if (module->dl_fallback) {
415 sym = module->dl_fallback->symbol_func (module->handle, name, &err, module->dl_fallback->user_data);
417 #if MONO_DL_NEED_USCORE
419 char *usname = malloc (strlen (name) + 2);
421 strcpy (usname + 1, name);
422 sym = LL_SO_SYMBOL (module, usname);
426 sym = LL_SO_SYMBOL (module, name);
437 return (module->dl_fallback != NULL) ? err : LL_SO_ERROR ();
442 * @module: a MonoDl pointer
444 * Unload the given module and free the module memory.
446 * Returns: 0 on success.
449 mono_dl_close (MonoDl *module)
451 MonoDlFallbackHandler *dl_fallback = module->dl_fallback;
454 if (dl_fallback->close_func != NULL)
455 dl_fallback->close_func (module->handle, dl_fallback->user_data);
457 LL_SO_CLOSE (module);
463 * mono_dl_build_path:
464 * @directory: optional directory
465 * @name: base name of the library
466 * @iter: iterator token
468 * Given a directory name and the base name of a library, iterate
469 * over the possible file names of the library, taking into account
470 * the possible different suffixes and prefixes on the host platform.
472 * The returned file name must be freed by the caller.
473 * @iter must point to a NULL pointer the first time the function is called
474 * and then passed unchanged to the following calls.
475 * Returns: the filename or NULL at the end of the iteration
478 mono_dl_build_path (const char *directory, const char *name, void **iter)
492 The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our
493 "bootstrap" phase in which we check the passed name verbatim and only if we fail to find
494 the dll thus named, we start appending suffixes, each time increasing idx twice (since now
495 the 0 value became special and we need to offset idx to a 0-based array index). This is
496 done to handle situations when mapped dll name is specified as libsomething.so.1 or
497 libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill
500 idx = GPOINTER_TO_UINT (*iter);
507 if (idx >= G_N_ELEMENTS (suffixes))
510 suffix = suffixes [idx];
511 suffixlen = strlen (suffix);
514 prlen = strlen (SOPREFIX);
515 if (prlen && strncmp (name, SOPREFIX, prlen) != 0)
520 if (first_call || (suffixlen && strstr (name, suffix) == (name + strlen (name) - suffixlen)))
523 if (directory && *directory)
524 res = g_strconcat (directory, G_DIR_SEPARATOR_S, prefix, name, suffix, NULL);
526 res = g_strconcat (prefix, name, suffix, NULL);
530 *iter = GUINT_TO_POINTER (idx);
534 MonoDlFallbackHandler *
535 mono_dl_fallback_register (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data)
537 MonoDlFallbackHandler *handler;
539 g_return_val_if_fail (load_func != NULL, NULL);
540 g_return_val_if_fail (symbol_func != NULL, NULL);
542 handler = g_new (MonoDlFallbackHandler, 1);
543 handler->load_func = load_func;
544 handler->symbol_func = symbol_func;
545 handler->close_func = close_func;
546 handler->user_data = user_data;
548 fallback_handlers = g_slist_prepend (fallback_handlers, handler);
554 mono_dl_fallback_unregister (MonoDlFallbackHandler *handler)
558 found = g_slist_find (fallback_handlers, handler);
562 g_slist_remove (fallback_handlers, handler);
567 #if defined (HAVE_DL_LOADER)
570 try_load (const char *lib_name, char *dir, int flags, char **err)
577 while ((path = mono_dl_build_path (dir, lib_name, &iter))) {
579 runtime_lib = mono_dl_open (path, flags, err);
588 mono_dl_open_runtime_lib (const char* lib_name, int flags, char **error_msg)
590 MonoDl *runtime_lib = NULL;
593 binl = readlink ("/proc/self/exe", buf, sizeof (buf)-1);
598 uint32_t bsize = sizeof (buf);
599 if (_NSGetExecutablePath (buf, &bsize) == 0) {
606 char *resolvedname, *name;
608 resolvedname = mono_path_resolve_symlinks (buf);
609 base = g_path_get_dirname (resolvedname);
610 name = g_strdup_printf ("%s/.libs", base);
611 runtime_lib = try_load (lib_name, name, flags, error_msg);
614 char *newbase = g_path_get_dirname (base);
615 name = g_strdup_printf ("%s/lib", newbase);
616 runtime_lib = try_load (lib_name, name, flags, error_msg);
621 char *newbase = g_path_get_dirname (base);
622 name = g_strdup_printf ("%s/Libraries", newbase);
623 runtime_lib = try_load (lib_name, name, flags, error_msg);
628 g_free (resolvedname);
631 runtime_lib = try_load (lib_name, NULL, flags, error_msg);
639 mono_dl_open_runtime_lib (const char* lib_name, int flags, char **error_msg)