3 * Interface to the dynamic linker
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.
13 #include "mono/utils/mono-dl.h"
14 #include "mono/utils/mono-embed.h"
15 #include "mono/utils/mono-path.h"
16 #include "mono/utils/mono-threads-api.h"
24 struct MonoDlFallbackHandler
{
25 MonoDlFallbackLoad load_func
;
26 MonoDlFallbackSymbol symbol_func
;
27 MonoDlFallbackClose close_func
;
31 static GSList
*fallback_handlers
;
34 * read a value string from line with any of the following formats:
37 * \s*=\s*non_white_space_string
40 read_string (char *p
, FILE *file
)
44 while (*p
&& isspace (*p
))
50 while (*p
&& isspace (*p
))
52 if (*p
== '\'' || *p
== '"') {
57 /* FIXME: may need to read more from file... */
61 return (char *) g_memdup (startp
, (endp
- startp
) + 1);
66 while (*p
&& !isspace (*p
))
69 return (char *) g_memdup (startp
, (p
- startp
) + 1);
73 * parse a libtool .la file and return the path of the file to dlopen ()
74 * handling both the installed and uninstalled cases
77 get_dl_name_from_libtool (const char *libtool_file
)
81 char *line
, *dlname
= NULL
, *libdir
= NULL
, *installed
= NULL
;
82 if (!(file
= fopen (libtool_file
, "r")))
84 while ((line
= fgets (buf
, 512, file
))) {
85 while (*line
&& isspace (*line
))
87 if (*line
== '#' || *line
== 0)
89 if (strncmp ("dlname", line
, 6) == 0) {
91 dlname
= read_string (line
+ 6, file
);
92 } else if (strncmp ("libdir", line
, 6) == 0) {
94 libdir
= read_string (line
+ 6, file
);
95 } else if (strncmp ("installed", line
, 9) == 0) {
97 installed
= read_string (line
+ 9, file
);
102 if (installed
&& strcmp (installed
, "no") == 0) {
103 char *dir
= g_path_get_dirname (libtool_file
);
105 line
= g_strconcat (dir
, G_DIR_SEPARATOR_S
".libs" G_DIR_SEPARATOR_S
, dlname
, NULL
);
108 if (libdir
&& dlname
)
109 line
= g_strconcat (libdir
, G_DIR_SEPARATOR_S
, dlname
, NULL
);
119 * \param name name of file containing shared module
121 * \param error_msg pointer for error message on failure
123 * Load the given file \p name as a shared library or dynamically loadable
124 * module. \p name can be NULL to indicate loading the currently executing
126 * \p flags can have the \c MONO_DL_LOCAL bit set to avoid exporting symbols
127 * from the module to the shared namespace. The \c MONO_DL_LAZY bit can be set
128 * to lazily load the symbols instead of resolving everithing at load time.
129 * \p error_msg points to a string where an error message will be stored in
130 * case of failure. The error must be released with \c g_free.
131 * \returns a \c MonoDl pointer on success, NULL on failure.
134 mono_dl_open (const char *name
, int flags
, char **error_msg
)
138 MonoDlFallbackHandler
*dl_fallback
= NULL
;
139 int lflags
= mono_dl_convert_flags (flags
);
144 module
= (MonoDl
*) g_malloc (sizeof (MonoDl
));
147 *error_msg
= g_strdup ("Out of memory");
150 module
->main_module
= name
== NULL
? TRUE
: FALSE
;
152 lib
= mono_dl_open_file (name
, lflags
);
156 for (node
= fallback_handlers
; node
!= NULL
; node
= node
->next
){
157 MonoDlFallbackHandler
*handler
= (MonoDlFallbackHandler
*) node
->data
;
161 lib
= handler
->load_func (name
, lflags
, error_msg
, handler
->user_data
);
162 if (error_msg
&& *error_msg
!= NULL
)
166 dl_fallback
= handler
;
171 if (!lib
&& !dl_fallback
) {
176 /* This platform does not support dlopen */
183 ext
= strrchr (name
, '.');
184 if (ext
&& strcmp (ext
, ".la") == 0)
186 lname
= g_strconcat (name
, suff
, NULL
);
187 llname
= get_dl_name_from_libtool (lname
);
190 lib
= mono_dl_open_file (llname
, lflags
);
195 *error_msg
= mono_dl_current_error_string ();
201 module
->handle
= lib
;
202 module
->dl_fallback
= dl_fallback
;
208 * \param module a MonoDl pointer
209 * \param name symbol name
210 * \param symbol pointer for the result value
211 * Load the address of symbol \p name from the given \p module.
212 * The address is stored in the pointer pointed to by \p symbol.
213 * \returns NULL on success, an error message on failure
216 mono_dl_symbol (MonoDl
*module
, const char *name
, void **symbol
)
221 if (module
->dl_fallback
) {
222 sym
= module
->dl_fallback
->symbol_func (module
->handle
, name
, &err
, module
->dl_fallback
->user_data
);
224 #if MONO_DL_NEED_USCORE
226 char *usname
= g_malloc (strlen (name
) + 2);
228 strcpy (usname
+ 1, name
);
229 sym
= mono_dl_lookup_symbol (module
, usname
);
233 sym
= mono_dl_lookup_symbol (module
, name
);
244 return (module
->dl_fallback
!= NULL
) ? err
: mono_dl_current_error_string ();
249 * \param module a \c MonoDl pointer
250 * Unload the given module and free the module memory.
251 * \returns \c 0 on success.
254 mono_dl_close (MonoDl
*module
)
256 MonoDlFallbackHandler
*dl_fallback
= module
->dl_fallback
;
259 if (dl_fallback
->close_func
!= NULL
)
260 dl_fallback
->close_func (module
->handle
, dl_fallback
->user_data
);
262 mono_dl_close_handle (module
);
268 * mono_dl_build_path:
269 * \param directory optional directory
270 * \param name base name of the library
271 * \param iter iterator token
272 * Given a directory name and the base name of a library, iterate
273 * over the possible file names of the library, taking into account
274 * the possible different suffixes and prefixes on the host platform.
276 * The returned file name must be freed by the caller.
277 * \p iter must point to a NULL pointer the first time the function is called
278 * and then passed unchanged to the following calls.
279 * \returns the filename or NULL at the end of the iteration
282 mono_dl_build_path (const char *directory
, const char *name
, void **iter
)
296 The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our
297 "bootstrap" phase in which we check the passed name verbatim and only if we fail to find
298 the dll thus named, we start appending suffixes, each time increasing idx twice (since now
299 the 0 value became special and we need to offset idx to a 0-based array index). This is
300 done to handle situations when mapped dll name is specified as libsomething.so.1 or
301 libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill
304 idx
= GPOINTER_TO_UINT (*iter
);
311 if (mono_dl_get_so_suffixes () [idx
][0] == '\0')
314 suffix
= mono_dl_get_so_suffixes () [idx
];
315 suffixlen
= strlen (suffix
);
318 prlen
= strlen (mono_dl_get_so_prefix ());
319 if (prlen
&& strncmp (name
, mono_dl_get_so_prefix (), prlen
) != 0)
320 prefix
= mono_dl_get_so_prefix ();
324 if (first_call
|| (suffixlen
&& strstr (name
, suffix
) == (name
+ strlen (name
) - suffixlen
)))
327 if (directory
&& *directory
)
328 res
= g_strconcat (directory
, G_DIR_SEPARATOR_S
, prefix
, name
, suffix
, NULL
);
330 res
= g_strconcat (prefix
, name
, suffix
, NULL
);
334 *iter
= GUINT_TO_POINTER (idx
);
338 MonoDlFallbackHandler
*
339 mono_dl_fallback_register (MonoDlFallbackLoad load_func
, MonoDlFallbackSymbol symbol_func
, MonoDlFallbackClose close_func
, void *user_data
)
341 MonoDlFallbackHandler
*handler
= NULL
;
342 MONO_ENTER_GC_UNSAFE
;
343 if (load_func
== NULL
|| symbol_func
== NULL
)
346 handler
= g_new (MonoDlFallbackHandler
, 1);
347 handler
->load_func
= load_func
;
348 handler
->symbol_func
= symbol_func
;
349 handler
->close_func
= close_func
;
350 handler
->user_data
= user_data
;
352 fallback_handlers
= g_slist_prepend (fallback_handlers
, handler
);
360 mono_dl_fallback_unregister (MonoDlFallbackHandler
*handler
)
364 found
= g_slist_find (fallback_handlers
, handler
);
368 g_slist_remove (fallback_handlers
, handler
);
373 try_load (const char *lib_name
, char *dir
, int flags
, char **err
)
380 while ((path
= mono_dl_build_path (dir
, lib_name
, &iter
))) {
382 runtime_lib
= mono_dl_open (path
, flags
, err
);
391 mono_dl_open_runtime_lib (const char* lib_name
, int flags
, char **error_msg
)
393 MonoDl
*runtime_lib
= NULL
;
398 binl
= mono_dl_get_executable_path (buf
, sizeof (buf
));
402 char *resolvedname
, *name
;
403 char *baseparent
= NULL
;
405 resolvedname
= mono_path_resolve_symlinks (buf
);
406 base
= g_path_get_dirname (resolvedname
);
407 name
= g_strdup_printf ("%s/.libs", base
);
408 runtime_lib
= try_load (lib_name
, name
, flags
, error_msg
);
411 baseparent
= g_path_get_dirname (base
);
413 name
= g_strdup_printf ("%s/lib", baseparent
);
414 runtime_lib
= try_load (lib_name
, name
, flags
, error_msg
);
419 name
= g_strdup_printf ("%s/Libraries", baseparent
);
420 runtime_lib
= try_load (lib_name
, name
, flags
, error_msg
);
425 name
= g_strdup_printf ("%s/profiler/.libs", baseparent
);
426 runtime_lib
= try_load (lib_name
, name
, flags
, error_msg
);
430 g_free (resolvedname
);
434 runtime_lib
= try_load (lib_name
, NULL
, flags
, error_msg
);