[mono-dl] Don't do thread state transitions in mono_dl_fallback_register
[mono-project.git] / mono / utils / mono-dl.c
blobeddb0c4b3684f967da6842a8531bbadc5b1296b8
1 /**
2 * \file
3 * Interface to the dynamic linker
5 * Author:
6 * Mono Team (http://www.mono-project.com)
8 * Copyright 2001-2004 Ximian, Inc.
9 * Copyright 2004-2009 Novell, Inc.
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #include "config.h"
13 #include "mono/utils/mono-compiler.h"
14 #include "mono/utils/mono-dl.h"
15 #include "mono/utils/mono-embed.h"
16 #include "mono/utils/mono-path.h"
17 #include "mono/utils/mono-threads-api.h"
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <ctype.h>
22 #include <string.h>
23 #include <glib.h>
25 struct MonoDlFallbackHandler {
26 MonoDlFallbackLoad load_func;
27 MonoDlFallbackSymbol symbol_func;
28 MonoDlFallbackClose close_func;
29 void *user_data;
32 static GSList *fallback_handlers;
35 * read a value string from line with any of the following formats:
36 * \s*=\s*'string'
37 * \s*=\s*"string"
38 * \s*=\s*non_white_space_string
40 static char*
41 read_string (char *p, FILE *file)
43 char *endp;
44 char *startp;
45 while (*p && isspace (*p))
46 ++p;
47 if (*p == 0)
48 return NULL;
49 if (*p == '=')
50 p++;
51 while (*p && isspace (*p))
52 ++p;
53 if (*p == '\'' || *p == '"') {
54 char t = *p;
55 p++;
56 startp = p;
57 endp = strchr (p, t);
58 /* FIXME: may need to read more from file... */
59 if (!endp)
60 return NULL;
61 *endp = 0;
62 return (char *) g_memdup (startp, (endp - startp) + 1);
64 if (*p == 0)
65 return NULL;
66 startp = p;
67 while (*p && !isspace (*p))
68 ++p;
69 *p = 0;
70 return (char *) g_memdup (startp, (p - startp) + 1);
74 * parse a libtool .la file and return the path of the file to dlopen ()
75 * handling both the installed and uninstalled cases
77 static char*
78 get_dl_name_from_libtool (const char *libtool_file)
80 FILE* file;
81 char buf [512];
82 char *line, *dlname = NULL, *libdir = NULL, *installed = NULL;
83 if (!(file = fopen (libtool_file, "r")))
84 return NULL;
85 while ((line = fgets (buf, 512, file))) {
86 while (*line && isspace (*line))
87 ++line;
88 if (*line == '#' || *line == 0)
89 continue;
90 if (strncmp ("dlname", line, 6) == 0) {
91 g_free (dlname);
92 dlname = read_string (line + 6, file);
93 } else if (strncmp ("libdir", line, 6) == 0) {
94 g_free (libdir);
95 libdir = read_string (line + 6, file);
96 } else if (strncmp ("installed", line, 9) == 0) {
97 g_free (installed);
98 installed = read_string (line + 9, file);
101 fclose (file);
102 line = NULL;
103 if (installed && strcmp (installed, "no") == 0) {
104 char *dir = g_path_get_dirname (libtool_file);
105 if (dlname)
106 line = g_strconcat (dir, G_DIR_SEPARATOR_S ".libs" G_DIR_SEPARATOR_S, dlname, NULL);
107 g_free (dir);
108 } else {
109 if (libdir && dlname)
110 line = g_strconcat (libdir, G_DIR_SEPARATOR_S, dlname, NULL);
112 g_free (dlname);
113 g_free (libdir);
114 g_free (installed);
115 return line;
119 * mono_dl_open:
120 * \param name name of file containing shared module
121 * \param flags flags
122 * \param error_msg pointer for error message on failure
124 * Load the given file \p name as a shared library or dynamically loadable
125 * module. \p name can be NULL to indicate loading the currently executing
126 * binary image.
127 * \p flags can have the \c MONO_DL_LOCAL bit set to avoid exporting symbols
128 * from the module to the shared namespace. The \c MONO_DL_LAZY bit can be set
129 * to lazily load the symbols instead of resolving everithing at load time.
130 * \p error_msg points to a string where an error message will be stored in
131 * case of failure. The error must be released with \c g_free.
132 * \returns a \c MonoDl pointer on success, NULL on failure.
134 MonoDl*
135 mono_dl_open (const char *name, int flags, char **error_msg)
137 MonoDl *module;
138 void *lib;
139 MonoDlFallbackHandler *dl_fallback = NULL;
140 int lflags = mono_dl_convert_flags (flags);
142 if (error_msg)
143 *error_msg = NULL;
145 module = (MonoDl *) g_malloc (sizeof (MonoDl));
146 if (!module) {
147 if (error_msg)
148 *error_msg = g_strdup ("Out of memory");
149 return NULL;
151 module->main_module = name == NULL? TRUE: FALSE;
153 lib = mono_dl_open_file (name, lflags);
155 if (!lib) {
156 GSList *node;
157 for (node = fallback_handlers; node != NULL; node = node->next){
158 MonoDlFallbackHandler *handler = (MonoDlFallbackHandler *) node->data;
159 if (error_msg)
160 *error_msg = NULL;
162 lib = handler->load_func (name, lflags, error_msg, handler->user_data);
163 if (error_msg && *error_msg != NULL)
164 g_free (*error_msg);
166 if (lib != NULL){
167 dl_fallback = handler;
168 break;
172 if (!lib && !dl_fallback) {
173 char *lname;
174 char *llname;
175 const char *suff;
176 const char *ext;
177 /* This platform does not support dlopen */
178 if (name == NULL) {
179 g_free (module);
180 return NULL;
183 suff = ".la";
184 ext = strrchr (name, '.');
185 if (ext && strcmp (ext, ".la") == 0)
186 suff = "";
187 lname = g_strconcat (name, suff, NULL);
188 llname = get_dl_name_from_libtool (lname);
189 g_free (lname);
190 if (llname) {
191 lib = mono_dl_open_file (llname, lflags);
192 g_free (llname);
194 if (!lib) {
195 if (error_msg) {
196 *error_msg = mono_dl_current_error_string ();
198 g_free (module);
199 return NULL;
202 module->handle = lib;
203 module->dl_fallback = dl_fallback;
204 return module;
208 * mono_dl_symbol:
209 * \param module a MonoDl pointer
210 * \param name symbol name
211 * \param symbol pointer for the result value
212 * Load the address of symbol \p name from the given \p module.
213 * The address is stored in the pointer pointed to by \p symbol.
214 * \returns NULL on success, an error message on failure
216 char*
217 mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
219 void *sym;
220 char *err = NULL;
222 if (module->dl_fallback) {
223 sym = module->dl_fallback->symbol_func (module->handle, name, &err, module->dl_fallback->user_data);
224 } else {
225 #if MONO_DL_NEED_USCORE
227 char *usname = g_malloc (strlen (name) + 2);
228 *usname = '_';
229 strcpy (usname + 1, name);
230 sym = mono_dl_lookup_symbol (module, usname);
231 g_free (usname);
233 #else
234 sym = mono_dl_lookup_symbol (module, name);
235 #endif
238 if (sym) {
239 if (symbol)
240 *symbol = sym;
241 return NULL;
243 if (symbol)
244 *symbol = NULL;
245 return (module->dl_fallback != NULL) ? err : mono_dl_current_error_string ();
249 * mono_dl_close:
250 * \param module a \c MonoDl pointer
251 * Unload the given module and free the module memory.
252 * \returns \c 0 on success.
254 void
255 mono_dl_close (MonoDl *module)
257 MonoDlFallbackHandler *dl_fallback = module->dl_fallback;
259 if (dl_fallback){
260 if (dl_fallback->close_func != NULL)
261 dl_fallback->close_func (module->handle, dl_fallback->user_data);
262 } else
263 mono_dl_close_handle (module);
265 g_free (module);
269 * mono_dl_build_path:
270 * \param directory optional directory
271 * \param name base name of the library
272 * \param iter iterator token
273 * Given a directory name and the base name of a library, iterate
274 * over the possible file names of the library, taking into account
275 * the possible different suffixes and prefixes on the host platform.
277 * The returned file name must be freed by the caller.
278 * \p iter must point to a NULL pointer the first time the function is called
279 * and then passed unchanged to the following calls.
280 * \returns the filename or NULL at the end of the iteration
282 char*
283 mono_dl_build_path (const char *directory, const char *name, void **iter)
285 int idx;
286 const char *prefix;
287 const char *suffix;
288 gboolean need_prefix = TRUE, need_suffix = TRUE;
289 int prlen;
290 int suffixlen;
291 char *res;
292 int iteration;
294 if (!iter)
295 return NULL;
298 The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our
299 "bootstrap" phase in which we check the passed name verbatim and only if we fail to find
300 the dll thus named, we start appending suffixes, each time increasing idx twice (since now
301 the 0 value became special and we need to offset idx to a 0-based array index). This is
302 done to handle situations when mapped dll name is specified as libsomething.so.1 or
303 libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill
304 here.
306 iteration = GPOINTER_TO_UINT (*iter);
307 idx = iteration;
308 if (idx == 0) {
309 /* Name */
310 need_prefix = FALSE;
311 need_suffix = FALSE;
312 suffix = "";
313 } else if (idx == 1) {
314 #ifdef ENABLE_NETCORE
315 /* netcore system libs have a suffix but no prefix */
316 need_prefix = FALSE;
317 need_suffix = TRUE;
318 suffix = mono_dl_get_so_suffixes () [0];
319 suffixlen = strlen (suffix);
320 #else
321 suffix = mono_dl_get_so_suffixes () [idx - 1];
322 if (suffix [0] == '\0')
323 return NULL;
324 #endif
325 } else {
326 /* Prefix.Name.suffix */
327 suffix = mono_dl_get_so_suffixes () [idx - 2];
328 if (suffix [0] == '\0')
329 return NULL;
332 if (need_prefix) {
333 prlen = strlen (mono_dl_get_so_prefix ());
334 if (prlen && strncmp (name, mono_dl_get_so_prefix (), prlen) != 0)
335 prefix = mono_dl_get_so_prefix ();
336 else
337 prefix = "";
338 } else {
339 prefix = "";
342 suffixlen = strlen (suffix);
343 if (need_suffix && (suffixlen && strstr (name, suffix) == (name + strlen (name) - suffixlen)))
344 suffix = "";
346 if (directory && *directory)
347 res = g_strconcat (directory, G_DIR_SEPARATOR_S, prefix, name, suffix, NULL);
348 else
349 res = g_strconcat (prefix, name, suffix, NULL);
350 ++iteration;
351 *iter = GUINT_TO_POINTER (iteration);
352 return res;
355 MonoDlFallbackHandler *
356 mono_dl_fallback_register (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data)
358 MonoDlFallbackHandler *handler = NULL;
359 if (load_func == NULL || symbol_func == NULL)
360 goto leave;
362 handler = g_new (MonoDlFallbackHandler, 1);
363 handler->load_func = load_func;
364 handler->symbol_func = symbol_func;
365 handler->close_func = close_func;
366 handler->user_data = user_data;
368 fallback_handlers = g_slist_prepend (fallback_handlers, handler);
370 leave:
371 return handler;
374 void
375 mono_dl_fallback_unregister (MonoDlFallbackHandler *handler)
377 GSList *found;
379 found = g_slist_find (fallback_handlers, handler);
380 if (found == NULL)
381 return;
383 g_slist_remove (fallback_handlers, handler);
384 g_free (handler);
387 static MonoDl*
388 try_load (const char *lib_name, char *dir, int flags, char **err)
390 gpointer iter;
391 MonoDl *runtime_lib;
392 char *path;
393 iter = NULL;
394 *err = NULL;
395 while ((path = mono_dl_build_path (dir, lib_name, &iter))) {
396 g_free (*err);
397 runtime_lib = mono_dl_open (path, flags, err);
398 g_free (path);
399 if (runtime_lib)
400 return runtime_lib;
402 return NULL;
405 MonoDl*
406 mono_dl_open_runtime_lib (const char* lib_name, int flags, char **error_msg)
408 MonoDl *runtime_lib = NULL;
409 char buf [4096];
410 int binl;
411 *error_msg = NULL;
413 binl = mono_dl_get_executable_path (buf, sizeof (buf));
415 if (binl != -1) {
416 char *base;
417 char *resolvedname, *name;
418 char *baseparent = NULL;
419 buf [binl] = 0;
420 resolvedname = mono_path_resolve_symlinks (buf);
421 base = g_path_get_dirname (resolvedname);
422 name = g_strdup_printf ("%s/.libs", base);
423 runtime_lib = try_load (lib_name, name, flags, error_msg);
424 g_free (name);
425 if (!runtime_lib)
426 baseparent = g_path_get_dirname (base);
427 if (!runtime_lib) {
428 name = g_strdup_printf ("%s/lib", baseparent);
429 runtime_lib = try_load (lib_name, name, flags, error_msg);
430 g_free (name);
432 #ifdef __MACH__
433 if (!runtime_lib) {
434 name = g_strdup_printf ("%s/Libraries", baseparent);
435 runtime_lib = try_load (lib_name, name, flags, error_msg);
436 g_free (name);
438 #endif
439 if (!runtime_lib) {
440 name = g_strdup_printf ("%s/profiler/.libs", baseparent);
441 runtime_lib = try_load (lib_name, name, flags, error_msg);
442 g_free (name);
444 g_free (base);
445 g_free (resolvedname);
446 g_free (baseparent);
448 if (!runtime_lib)
449 runtime_lib = try_load (lib_name, NULL, flags, error_msg);
451 return runtime_lib;