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-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"
25 struct MonoDlFallbackHandler
{
26 MonoDlFallbackLoad load_func
;
27 MonoDlFallbackSymbol symbol_func
;
28 MonoDlFallbackClose close_func
;
32 static GSList
*fallback_handlers
;
39 * On AIX/PASE, a shared library can be contained inside of an ar format
40 * archive. Determine if the file is an ar archive or not.
43 is_library_ar_archive (char *path
)
47 lfd
= open (path
, O_RDONLY
);
49 /* don't assume it's an archive on error */
53 readret
= read (lfd
, magic
, SAIAMAG
);
55 /* check for equality with either version of header */
56 return readret
== SAIAMAG
&&
57 (memcmp (magic
, AIAMAG
, SAIAMAG
) == 0 ||
58 memcmp (magic
, AIAMAGBIG
, SAIAMAG
) == 0);
63 * read a value string from line with any of the following formats:
66 * \s*=\s*non_white_space_string
69 read_string (char *p
, FILE *file
)
73 while (*p
&& isspace (*p
))
79 while (*p
&& isspace (*p
))
81 if (*p
== '\'' || *p
== '"') {
86 /* FIXME: may need to read more from file... */
90 return (char *) g_memdup (startp
, (endp
- startp
) + 1);
95 while (*p
&& !isspace (*p
))
98 return (char *) g_memdup (startp
, (p
- startp
) + 1);
102 * parse a libtool .la file and return the path of the file to dlopen ()
103 * handling both the installed and uninstalled cases
106 get_dl_name_from_libtool (const char *libtool_file
)
110 char *line
, *dlname
= NULL
, *libdir
= NULL
, *installed
= NULL
;
111 if (!(file
= fopen (libtool_file
, "r")))
113 while ((line
= fgets (buf
, 512, file
))) {
114 while (*line
&& isspace (*line
))
116 if (*line
== '#' || *line
== 0)
118 if (strncmp ("dlname", line
, 6) == 0) {
120 dlname
= read_string (line
+ 6, file
);
121 } else if (strncmp ("libdir", line
, 6) == 0) {
123 libdir
= read_string (line
+ 6, file
);
124 } else if (strncmp ("installed", line
, 9) == 0) {
126 installed
= read_string (line
+ 9, file
);
131 if (installed
&& strcmp (installed
, "no") == 0) {
132 char *dir
= g_path_get_dirname (libtool_file
);
134 line
= g_strconcat (dir
, G_DIR_SEPARATOR_S
".libs" G_DIR_SEPARATOR_S
, dlname
, (const char*)NULL
);
137 if (libdir
&& dlname
)
138 line
= g_strconcat (libdir
, G_DIR_SEPARATOR_S
, dlname
, (const char*)NULL
);
148 * \param name name of file containing shared module
150 * \param error_msg pointer for error message on failure
152 * Load the given file \p name as a shared library or dynamically loadable
153 * module. \p name can be NULL to indicate loading the currently executing
155 * \p flags can have the \c MONO_DL_LOCAL bit set to avoid exporting symbols
156 * from the module to the shared namespace. The \c MONO_DL_LAZY bit can be set
157 * to lazily load the symbols instead of resolving everithing at load time.
158 * \p error_msg points to a string where an error message will be stored in
159 * case of failure. The error must be released with \c g_free.
160 * \returns a \c MonoDl pointer on success, NULL on failure.
163 mono_dl_open (const char *name
, int flags
, char **error_msg
)
167 MonoDlFallbackHandler
*dl_fallback
= NULL
;
168 int lflags
= mono_dl_convert_flags (flags
);
173 module
= (MonoDl
*) g_malloc (sizeof (MonoDl
));
176 *error_msg
= g_strdup ("Out of memory");
179 module
->main_module
= name
== NULL
? TRUE
: FALSE
;
181 // No GC safe transition because this is called early in main.c
182 lib
= mono_dl_open_file (name
, lflags
);
186 for (node
= fallback_handlers
; node
!= NULL
; node
= node
->next
){
187 MonoDlFallbackHandler
*handler
= (MonoDlFallbackHandler
*) node
->data
;
191 lib
= handler
->load_func (name
, lflags
, error_msg
, handler
->user_data
);
192 if (error_msg
&& *error_msg
!= NULL
)
196 dl_fallback
= handler
;
201 if (!lib
&& !dl_fallback
) {
206 /* This platform does not support dlopen */
213 ext
= strrchr (name
, '.');
214 if (ext
&& strcmp (ext
, ".la") == 0)
216 lname
= g_strconcat (name
, suff
, (const char*)NULL
);
217 llname
= get_dl_name_from_libtool (lname
);
220 lib
= mono_dl_open_file (llname
, lflags
);
223 * HACK: deal with AIX archive members because libtool
224 * underspecifies when using --with-aix-soname=svr4 -
225 * without this check, Mono can't find System.Native
227 * XXX: Does this also need to be in other places?
229 if (!lib
&& is_library_ar_archive (llname
)) {
230 /* try common suffix */
232 llaixname
= g_strconcat (llname
, "(shr_64.o)", (const char*)NULL
);
233 lib
= mono_dl_open_file (llaixname
, lflags
);
234 /* XXX: try another suffix like (shr.o)? */
242 *error_msg
= mono_dl_current_error_string ();
248 module
->handle
= lib
;
249 module
->dl_fallback
= dl_fallback
;
255 * \param module a MonoDl pointer
256 * \param name symbol name
257 * \param symbol pointer for the result value
258 * Load the address of symbol \p name from the given \p module.
259 * The address is stored in the pointer pointed to by \p symbol.
260 * \returns NULL on success, an error message on failure
263 mono_dl_symbol (MonoDl
*module
, const char *name
, void **symbol
)
268 if (module
->dl_fallback
) {
269 sym
= module
->dl_fallback
->symbol_func (module
->handle
, name
, &err
, module
->dl_fallback
->user_data
);
271 #if MONO_DL_NEED_USCORE
273 const size_t length
= strlen (name
);
274 char *usname
= g_new (char, length
+ 2);
276 memcpy (usname
+ 1, name
, length
+ 1);
277 sym
= mono_dl_lookup_symbol (module
, usname
);
281 sym
= mono_dl_lookup_symbol (module
, name
);
292 return (module
->dl_fallback
!= NULL
) ? err
: mono_dl_current_error_string ();
297 * \param module a \c MonoDl pointer
298 * Unload the given module and free the module memory.
299 * \returns \c 0 on success.
302 mono_dl_close (MonoDl
*module
)
304 MonoDlFallbackHandler
*dl_fallback
= module
->dl_fallback
;
307 if (dl_fallback
->close_func
!= NULL
)
308 dl_fallback
->close_func (module
->handle
, dl_fallback
->user_data
);
310 mono_dl_close_handle (module
);
316 * mono_dl_build_path:
317 * \param directory optional directory
318 * \param name base name of the library
319 * \param iter iterator token
320 * Given a directory name and the base name of a library, iterate
321 * over the possible file names of the library, taking into account
322 * the possible different suffixes and prefixes on the host platform.
324 * The returned file name must be freed by the caller.
325 * \p iter must point to a NULL pointer the first time the function is called
326 * and then passed unchanged to the following calls.
327 * \returns the filename or NULL at the end of the iteration
330 mono_dl_build_path (const char *directory
, const char *name
, void **iter
)
335 gboolean need_prefix
= TRUE
, need_suffix
= TRUE
;
345 The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our
346 "bootstrap" phase in which we check the passed name verbatim and only if we fail to find
347 the dll thus named, we start appending suffixes, each time increasing idx twice (since now
348 the 0 value became special and we need to offset idx to a 0-based array index). This is
349 done to handle situations when mapped dll name is specified as libsomething.so.1 or
350 libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill
353 iteration
= GPOINTER_TO_UINT (*iter
);
360 } else if (idx
== 1) {
361 #ifdef ENABLE_NETCORE
362 /* netcore system libs have a suffix but no prefix */
365 suffix
= mono_dl_get_so_suffixes () [0];
366 suffixlen
= strlen (suffix
);
368 suffix
= mono_dl_get_so_suffixes () [idx
- 1];
369 if (suffix
[0] == '\0')
373 /* Prefix.Name.suffix */
374 suffix
= mono_dl_get_so_suffixes () [idx
- 2];
375 if (suffix
[0] == '\0')
380 prlen
= strlen (mono_dl_get_so_prefix ());
381 if (prlen
&& strncmp (name
, mono_dl_get_so_prefix (), prlen
) != 0)
382 prefix
= mono_dl_get_so_prefix ();
389 suffixlen
= strlen (suffix
);
390 if (need_suffix
&& (suffixlen
&& strstr (name
, suffix
) == (name
+ strlen (name
) - suffixlen
)))
393 if (directory
&& *directory
)
394 res
= g_strconcat (directory
, G_DIR_SEPARATOR_S
, prefix
, name
, suffix
, (const char*)NULL
);
396 res
= g_strconcat (prefix
, name
, suffix
, (const char*)NULL
);
398 *iter
= GUINT_TO_POINTER (iteration
);
402 MonoDlFallbackHandler
*
403 mono_dl_fallback_register (MonoDlFallbackLoad load_func
, MonoDlFallbackSymbol symbol_func
, MonoDlFallbackClose close_func
, void *user_data
)
405 MonoDlFallbackHandler
*handler
= NULL
;
406 if (load_func
== NULL
|| symbol_func
== NULL
)
409 handler
= g_new (MonoDlFallbackHandler
, 1);
410 handler
->load_func
= load_func
;
411 handler
->symbol_func
= symbol_func
;
412 handler
->close_func
= close_func
;
413 handler
->user_data
= user_data
;
415 fallback_handlers
= g_slist_prepend (fallback_handlers
, handler
);
422 mono_dl_fallback_unregister (MonoDlFallbackHandler
*handler
)
426 found
= g_slist_find (fallback_handlers
, handler
);
430 g_slist_remove (fallback_handlers
, handler
);
435 try_load (const char *lib_name
, char *dir
, int flags
, char **err
)
442 while ((path
= mono_dl_build_path (dir
, lib_name
, &iter
))) {
444 runtime_lib
= mono_dl_open (path
, flags
, err
);
453 mono_dl_open_runtime_lib (const char* lib_name
, int flags
, char **error_msg
)
455 MonoDl
*runtime_lib
= NULL
;
460 binl
= mono_dl_get_executable_path (buf
, sizeof (buf
));
464 char *resolvedname
, *name
;
465 char *baseparent
= NULL
;
467 resolvedname
= mono_path_resolve_symlinks (buf
);
468 base
= g_path_get_dirname (resolvedname
);
469 name
= g_strdup_printf ("%s/.libs", base
);
470 runtime_lib
= try_load (lib_name
, name
, flags
, error_msg
);
473 baseparent
= g_path_get_dirname (base
);
475 name
= g_strdup_printf ("%s/lib", baseparent
);
476 runtime_lib
= try_load (lib_name
, name
, flags
, error_msg
);
481 name
= g_strdup_printf ("%s/Libraries", baseparent
);
482 runtime_lib
= try_load (lib_name
, name
, flags
, error_msg
);
487 name
= g_strdup_printf ("%s/profiler/.libs", baseparent
);
488 runtime_lib
= try_load (lib_name
, name
, flags
, error_msg
);
492 g_free (resolvedname
);
496 runtime_lib
= try_load (lib_name
, NULL
, flags
, error_msg
);