/** * \file * Interface to the dynamic linker * * Author: * Mono Team (http://www.mono-project.com) * * Copyright 2001-2004 Ximian, Inc. * Copyright 2004-2009 Novell, Inc. * Licensed under the MIT license. See LICENSE file in the project root for full license information. */ #include "config.h" #include "mono/utils/mono-dl.h" #include "mono/utils/mono-embed.h" #include "mono/utils/mono-path.h" #include #include #include #include #include struct MonoDlFallbackHandler { MonoDlFallbackLoad load_func; MonoDlFallbackSymbol symbol_func; MonoDlFallbackClose close_func; void *user_data; }; static GSList *fallback_handlers; /* * read a value string from line with any of the following formats: * \s*=\s*'string' * \s*=\s*"string" * \s*=\s*non_white_space_string */ static char* read_string (char *p, FILE *file) { char *endp; char *startp; while (*p && isspace (*p)) ++p; if (*p == 0) return NULL; if (*p == '=') p++; while (*p && isspace (*p)) ++p; if (*p == '\'' || *p == '"') { char t = *p; p++; startp = p; endp = strchr (p, t); /* FIXME: may need to read more from file... */ if (!endp) return NULL; *endp = 0; return (char *) g_memdup (startp, (endp - startp) + 1); } if (*p == 0) return NULL; startp = p; while (*p && !isspace (*p)) ++p; *p = 0; return (char *) g_memdup (startp, (p - startp) + 1); } /* * parse a libtool .la file and return the path of the file to dlopen () * handling both the installed and uninstalled cases */ static char* get_dl_name_from_libtool (const char *libtool_file) { FILE* file; char buf [512]; char *line, *dlname = NULL, *libdir = NULL, *installed = NULL; if (!(file = fopen (libtool_file, "r"))) return NULL; while ((line = fgets (buf, 512, file))) { while (*line && isspace (*line)) ++line; if (*line == '#' || *line == 0) continue; if (strncmp ("dlname", line, 6) == 0) { g_free (dlname); dlname = read_string (line + 6, file); } else if (strncmp ("libdir", line, 6) == 0) { g_free (libdir); libdir = read_string (line + 6, file); } else if (strncmp ("installed", line, 9) == 0) { g_free (installed); installed = read_string (line + 9, file); } } fclose (file); line = NULL; if (installed && strcmp (installed, "no") == 0) { char *dir = g_path_get_dirname (libtool_file); if (dlname) line = g_strconcat (dir, G_DIR_SEPARATOR_S ".libs" G_DIR_SEPARATOR_S, dlname, NULL); g_free (dir); } else { if (libdir && dlname) line = g_strconcat (libdir, G_DIR_SEPARATOR_S, dlname, NULL); } g_free (dlname); g_free (libdir); g_free (installed); return line; } /** * mono_dl_open: * \param name name of file containing shared module * \param flags flags * \param error_msg pointer for error message on failure * * Load the given file \p name as a shared library or dynamically loadable * module. \p name can be NULL to indicate loading the currently executing * binary image. * \p flags can have the \c MONO_DL_LOCAL bit set to avoid exporting symbols * from the module to the shared namespace. The \c MONO_DL_LAZY bit can be set * to lazily load the symbols instead of resolving everithing at load time. * \p error_msg points to a string where an error message will be stored in * case of failure. The error must be released with \c g_free. * \returns a \c MonoDl pointer on success, NULL on failure. */ MonoDl* mono_dl_open (const char *name, int flags, char **error_msg) { MonoDl *module; void *lib; MonoDlFallbackHandler *dl_fallback = NULL; int lflags = mono_dl_convert_flags (flags); if (error_msg) *error_msg = NULL; module = (MonoDl *) g_malloc (sizeof (MonoDl)); if (!module) { if (error_msg) *error_msg = g_strdup ("Out of memory"); return NULL; } module->main_module = name == NULL? TRUE: FALSE; lib = mono_dl_open_file (name, lflags); if (!lib) { GSList *node; for (node = fallback_handlers; node != NULL; node = node->next){ MonoDlFallbackHandler *handler = (MonoDlFallbackHandler *) node->data; if (error_msg) *error_msg = NULL; lib = handler->load_func (name, lflags, error_msg, handler->user_data); if (error_msg && *error_msg != NULL) g_free (*error_msg); if (lib != NULL){ dl_fallback = handler; break; } } } if (!lib && !dl_fallback) { char *lname; char *llname; const char *suff; const char *ext; /* This platform does not support dlopen */ if (name == NULL) { g_free (module); return NULL; } suff = ".la"; ext = strrchr (name, '.'); if (ext && strcmp (ext, ".la") == 0) suff = ""; lname = g_strconcat (name, suff, NULL); llname = get_dl_name_from_libtool (lname); g_free (lname); if (llname) { lib = mono_dl_open_file (llname, lflags); g_free (llname); } if (!lib) { if (error_msg) { *error_msg = mono_dl_current_error_string (); } g_free (module); return NULL; } } module->handle = lib; module->dl_fallback = dl_fallback; return module; } /** * mono_dl_symbol: * \param module a MonoDl pointer * \param name symbol name * \param symbol pointer for the result value * Load the address of symbol \p name from the given \p module. * The address is stored in the pointer pointed to by \p symbol. * \returns NULL on success, an error message on failure */ char* mono_dl_symbol (MonoDl *module, const char *name, void **symbol) { void *sym; char *err = NULL; if (module->dl_fallback) { sym = module->dl_fallback->symbol_func (module->handle, name, &err, module->dl_fallback->user_data); } else { #if MONO_DL_NEED_USCORE { char *usname = g_malloc (strlen (name) + 2); *usname = '_'; strcpy (usname + 1, name); sym = mono_dl_lookup_symbol (module, usname); g_free (usname); } #else sym = mono_dl_lookup_symbol (module, name); #endif } if (sym) { if (symbol) *symbol = sym; return NULL; } if (symbol) *symbol = NULL; return (module->dl_fallback != NULL) ? err : mono_dl_current_error_string (); } /** * mono_dl_close: * \param module a \c MonoDl pointer * Unload the given module and free the module memory. * \returns \c 0 on success. */ void mono_dl_close (MonoDl *module) { MonoDlFallbackHandler *dl_fallback = module->dl_fallback; if (dl_fallback){ if (dl_fallback->close_func != NULL) dl_fallback->close_func (module->handle, dl_fallback->user_data); } else mono_dl_close_handle (module); g_free (module); } /** * mono_dl_build_path: * \param directory optional directory * \param name base name of the library * \param iter iterator token * Given a directory name and the base name of a library, iterate * over the possible file names of the library, taking into account * the possible different suffixes and prefixes on the host platform. * * The returned file name must be freed by the caller. * \p iter must point to a NULL pointer the first time the function is called * and then passed unchanged to the following calls. * \returns the filename or NULL at the end of the iteration */ char* mono_dl_build_path (const char *directory, const char *name, void **iter) { int idx; const char *prefix; const char *suffix; gboolean first_call; int prlen; int suffixlen; char *res; if (!iter) return NULL; /* The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our "bootstrap" phase in which we check the passed name verbatim and only if we fail to find the dll thus named, we start appending suffixes, each time increasing idx twice (since now the 0 value became special and we need to offset idx to a 0-based array index). This is done to handle situations when mapped dll name is specified as libsomething.so.1 or libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill here. */ idx = GPOINTER_TO_UINT (*iter); if (idx == 0) { first_call = TRUE; suffix = ""; suffixlen = 0; } else { idx--; if (mono_dl_get_so_suffixes () [idx][0] == '\0') return NULL; first_call = FALSE; suffix = mono_dl_get_so_suffixes () [idx]; suffixlen = strlen (suffix); } prlen = strlen (mono_dl_get_so_prefix ()); if (prlen && strncmp (name, mono_dl_get_so_prefix (), prlen) != 0) prefix = mono_dl_get_so_prefix (); else prefix = ""; if (first_call || (suffixlen && strstr (name, suffix) == (name + strlen (name) - suffixlen))) suffix = ""; if (directory && *directory) res = g_strconcat (directory, G_DIR_SEPARATOR_S, prefix, name, suffix, NULL); else res = g_strconcat (prefix, name, suffix, NULL); ++idx; if (!first_call) idx++; *iter = GUINT_TO_POINTER (idx); return res; } MonoDlFallbackHandler * mono_dl_fallback_register (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data) { MonoDlFallbackHandler *handler; g_return_val_if_fail (load_func != NULL, NULL); g_return_val_if_fail (symbol_func != NULL, NULL); handler = g_new (MonoDlFallbackHandler, 1); handler->load_func = load_func; handler->symbol_func = symbol_func; handler->close_func = close_func; handler->user_data = user_data; fallback_handlers = g_slist_prepend (fallback_handlers, handler); return handler; } void mono_dl_fallback_unregister (MonoDlFallbackHandler *handler) { GSList *found; found = g_slist_find (fallback_handlers, handler); if (found == NULL) return; g_slist_remove (fallback_handlers, handler); g_free (handler); } static MonoDl* try_load (const char *lib_name, char *dir, int flags, char **err) { gpointer iter; MonoDl *runtime_lib; char *path; iter = NULL; *err = NULL; while ((path = mono_dl_build_path (dir, lib_name, &iter))) { g_free (*err); runtime_lib = mono_dl_open (path, flags, err); g_free (path); if (runtime_lib) return runtime_lib; } return NULL; } MonoDl* mono_dl_open_runtime_lib (const char* lib_name, int flags, char **error_msg) { MonoDl *runtime_lib = NULL; char buf [4096]; int binl; *error_msg = NULL; binl = mono_dl_get_executable_path (buf, sizeof (buf)); if (binl != -1) { char *base; char *resolvedname, *name; char *baseparent = NULL; buf [binl] = 0; resolvedname = mono_path_resolve_symlinks (buf); base = g_path_get_dirname (resolvedname); name = g_strdup_printf ("%s/.libs", base); runtime_lib = try_load (lib_name, name, flags, error_msg); g_free (name); if (!runtime_lib) baseparent = g_path_get_dirname (base); if (!runtime_lib) { name = g_strdup_printf ("%s/lib", baseparent); runtime_lib = try_load (lib_name, name, flags, error_msg); g_free (name); } #ifdef __MACH__ if (!runtime_lib) { name = g_strdup_printf ("%s/Libraries", baseparent); runtime_lib = try_load (lib_name, name, flags, error_msg); g_free (name); } #endif if (!runtime_lib) { name = g_strdup_printf ("%s/profiler/.libs", baseparent); runtime_lib = try_load (lib_name, name, flags, error_msg); g_free (name); } g_free (base); g_free (resolvedname); g_free (baseparent); } if (!runtime_lib) runtime_lib = try_load (lib_name, NULL, flags, error_msg); return runtime_lib; }