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 // Contains LIBC_SO definition
26 #ifdef HAVE_GNU_LIB_NAMES_H
27 #include <gnu/lib-names.h>
30 struct MonoDlFallbackHandler
{
31 MonoDlFallbackLoad load_func
;
32 MonoDlFallbackSymbol symbol_func
;
33 MonoDlFallbackClose close_func
;
37 static GSList
*fallback_handlers
;
44 * On AIX/PASE, a shared library can be contained inside of an ar format
45 * archive. Determine if the file is an ar archive or not.
48 is_library_ar_archive (char *path
)
52 lfd
= open (path
, O_RDONLY
);
54 /* don't assume it's an archive on error */
58 readret
= read (lfd
, magic
, SAIAMAG
);
60 /* check for equality with either version of header */
61 return readret
== SAIAMAG
&&
62 (memcmp (magic
, AIAMAG
, SAIAMAG
) == 0 ||
63 memcmp (magic
, AIAMAGBIG
, SAIAMAG
) == 0);
68 * read a value string from line with any of the following formats:
71 * \s*=\s*non_white_space_string
74 read_string (char *p
, FILE *file
)
78 while (*p
&& isspace (*p
))
84 while (*p
&& isspace (*p
))
86 if (*p
== '\'' || *p
== '"') {
91 /* FIXME: may need to read more from file... */
95 return (char *) g_memdup (startp
, (endp
- startp
) + 1);
100 while (*p
&& !isspace (*p
))
103 return (char *) g_memdup (startp
, (p
- startp
) + 1);
107 * parse a libtool .la file and return the path of the file to dlopen ()
108 * handling both the installed and uninstalled cases
111 get_dl_name_from_libtool (const char *libtool_file
)
115 char *line
, *dlname
= NULL
, *libdir
= NULL
, *installed
= NULL
;
116 if (!(file
= fopen (libtool_file
, "r")))
118 while ((line
= fgets (buf
, 512, file
))) {
119 while (*line
&& isspace (*line
))
121 if (*line
== '#' || *line
== 0)
123 if (strncmp ("dlname", line
, 6) == 0) {
125 dlname
= read_string (line
+ 6, file
);
126 } else if (strncmp ("libdir", line
, 6) == 0) {
128 libdir
= read_string (line
+ 6, file
);
129 } else if (strncmp ("installed", line
, 9) == 0) {
131 installed
= read_string (line
+ 9, file
);
136 if (installed
&& strcmp (installed
, "no") == 0) {
137 char *dir
= g_path_get_dirname (libtool_file
);
139 line
= g_strconcat (dir
, G_DIR_SEPARATOR_S
".libs" G_DIR_SEPARATOR_S
, dlname
, (const char*)NULL
);
142 if (libdir
&& dlname
)
143 line
= g_strconcat (libdir
, G_DIR_SEPARATOR_S
, dlname
, (const char*)NULL
);
151 #ifdef ENABLE_NETCORE
153 fix_libc_name (const char *name
)
155 if (name
!= NULL
&& strcmp (name
, "libc") == 0) {
156 // Taken from CoreCLR: https://github.com/dotnet/coreclr/blob/6b0dab793260d36e35d66c82678c63046828d01b/src/pal/src/loader/module.cpp#L568-L576
157 #if defined (HOST_DARWIN)
158 return "/usr/lib/libc.dylib";
159 #elif defined (__FreeBSD__)
161 #elif defined (LIBC_SO)
173 * \param name name of file containing shared module
175 * \param error_msg pointer for error message on failure
177 * Load the given file \p name as a shared library or dynamically loadable
178 * module. \p name can be NULL to indicate loading the currently executing
180 * \p flags can have the \c MONO_DL_LOCAL bit set to avoid exporting symbols
181 * from the module to the shared namespace. The \c MONO_DL_LAZY bit can be set
182 * to lazily load the symbols instead of resolving everything at load time.
183 * \p error_msg points to a string where an error message will be stored in
184 * case of failure. The error must be released with \c g_free.
185 * \returns a \c MonoDl pointer on success, NULL on failure.
188 mono_dl_open (const char *name
, int flags
, char **error_msg
)
192 MonoDlFallbackHandler
*dl_fallback
= NULL
;
193 int lflags
= mono_dl_convert_flags (flags
);
199 module
= (MonoDl
*) g_malloc (sizeof (MonoDl
));
202 *error_msg
= g_strdup ("Out of memory");
205 module
->main_module
= name
== NULL
? TRUE
: FALSE
;
207 #ifdef ENABLE_NETCORE
208 name
= fix_libc_name (name
);
211 // No GC safe transition because this is called early in main.c
212 lib
= mono_dl_open_file (name
, lflags
);
214 found_name
= g_strdup (name
);
218 for (node
= fallback_handlers
; node
!= NULL
; node
= node
->next
){
219 MonoDlFallbackHandler
*handler
= (MonoDlFallbackHandler
*) node
->data
;
223 lib
= handler
->load_func (name
, lflags
, error_msg
, handler
->user_data
);
224 if (error_msg
&& *error_msg
!= NULL
)
228 dl_fallback
= handler
;
229 found_name
= g_strdup (name
);
234 if (!lib
&& !dl_fallback
) {
239 /* This platform does not support dlopen */
246 ext
= strrchr (name
, '.');
247 if (ext
&& strcmp (ext
, ".la") == 0)
249 lname
= g_strconcat (name
, suff
, (const char*)NULL
);
250 llname
= get_dl_name_from_libtool (lname
);
253 lib
= mono_dl_open_file (llname
, lflags
);
255 found_name
= g_strdup (llname
);
258 * HACK: deal with AIX archive members because libtool
259 * underspecifies when using --with-aix-soname=svr4 -
260 * without this check, Mono can't find System.Native
262 * XXX: Does this also need to be in other places?
264 if (!lib
&& is_library_ar_archive (llname
)) {
265 /* try common suffix */
267 llaixname
= g_strconcat (llname
, "(shr_64.o)", (const char*)NULL
);
268 lib
= mono_dl_open_file (llaixname
, lflags
);
270 found_name
= g_strdup (llaixname
);
271 /* XXX: try another suffix like (shr.o)? */
279 *error_msg
= mono_dl_current_error_string ();
285 mono_refcount_init (module
, NULL
);
286 module
->handle
= lib
;
287 module
->dl_fallback
= dl_fallback
;
288 module
->full_name
= found_name
;
294 * \param module a MonoDl pointer
295 * \param name symbol name
296 * \param symbol pointer for the result value
297 * Load the address of symbol \p name from the given \p module.
298 * The address is stored in the pointer pointed to by \p symbol.
299 * \returns NULL on success, an error message on failure
302 mono_dl_symbol (MonoDl
*module
, const char *name
, void **symbol
)
307 if (module
->dl_fallback
) {
308 sym
= module
->dl_fallback
->symbol_func (module
->handle
, name
, &err
, module
->dl_fallback
->user_data
);
310 #if MONO_DL_NEED_USCORE
312 const size_t length
= strlen (name
);
313 char *usname
= g_new (char, length
+ 2);
315 memcpy (usname
+ 1, name
, length
+ 1);
316 sym
= mono_dl_lookup_symbol (module
, usname
);
320 sym
= mono_dl_lookup_symbol (module
, name
);
331 return (module
->dl_fallback
!= NULL
) ? err
: mono_dl_current_error_string ();
336 * \param module a \c MonoDl pointer
337 * Unload the given module and free the module memory.
338 * \returns \c 0 on success.
341 mono_dl_close (MonoDl
*module
)
343 MonoDlFallbackHandler
*dl_fallback
= module
->dl_fallback
;
346 if (dl_fallback
->close_func
!= NULL
)
347 dl_fallback
->close_func (module
->handle
, dl_fallback
->user_data
);
349 mono_dl_close_handle (module
);
351 g_free (module
->full_name
);
356 * mono_dl_build_path:
357 * \param directory optional directory
358 * \param name base name of the library
359 * \param iter iterator token
360 * Given a directory name and the base name of a library, iterate
361 * over the possible file names of the library, taking into account
362 * the possible different suffixes and prefixes on the host platform.
364 * The returned file name must be freed by the caller.
365 * \p iter must point to a NULL pointer the first time the function is called
366 * and then passed unchanged to the following calls.
367 * \returns the filename or NULL at the end of the iteration
370 mono_dl_build_path (const char *directory
, const char *name
, void **iter
)
375 gboolean need_prefix
= TRUE
, need_suffix
= TRUE
;
385 The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our
386 "bootstrap" phase in which we check the passed name verbatim and only if we fail to find
387 the dll thus named, we start appending suffixes, each time increasing idx twice (since now
388 the 0 value became special and we need to offset idx to a 0-based array index). This is
389 done to handle situations when mapped dll name is specified as libsomething.so.1 or
390 libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill
393 iteration
= GPOINTER_TO_UINT (*iter
);
400 } else if (idx
== 1) {
401 #ifdef ENABLE_NETCORE
402 /* netcore system libs have a suffix but no prefix */
405 suffix
= mono_dl_get_so_suffixes () [0];
406 suffixlen
= strlen (suffix
);
408 suffix
= mono_dl_get_so_suffixes () [idx
- 1];
409 if (suffix
[0] == '\0')
413 /* Prefix.Name.suffix */
414 suffix
= mono_dl_get_so_suffixes () [idx
- 2];
415 if (suffix
[0] == '\0')
420 prlen
= strlen (mono_dl_get_so_prefix ());
421 if (prlen
&& strncmp (name
, mono_dl_get_so_prefix (), prlen
) != 0)
422 prefix
= mono_dl_get_so_prefix ();
429 suffixlen
= strlen (suffix
);
430 if (need_suffix
&& (suffixlen
&& strstr (name
, suffix
) == (name
+ strlen (name
) - suffixlen
)))
433 if (directory
&& *directory
)
434 res
= g_strconcat (directory
, G_DIR_SEPARATOR_S
, prefix
, name
, suffix
, (const char*)NULL
);
436 res
= g_strconcat (prefix
, name
, suffix
, (const char*)NULL
);
438 *iter
= GUINT_TO_POINTER (iteration
);
442 MonoDlFallbackHandler
*
443 mono_dl_fallback_register (MonoDlFallbackLoad load_func
, MonoDlFallbackSymbol symbol_func
, MonoDlFallbackClose close_func
, void *user_data
)
445 MonoDlFallbackHandler
*handler
= NULL
;
446 if (load_func
== NULL
|| symbol_func
== NULL
)
449 handler
= g_new (MonoDlFallbackHandler
, 1);
450 handler
->load_func
= load_func
;
451 handler
->symbol_func
= symbol_func
;
452 handler
->close_func
= close_func
;
453 handler
->user_data
= user_data
;
455 fallback_handlers
= g_slist_prepend (fallback_handlers
, handler
);
462 mono_dl_fallback_unregister (MonoDlFallbackHandler
*handler
)
466 found
= g_slist_find (fallback_handlers
, handler
);
470 g_slist_remove (fallback_handlers
, handler
);
475 try_load (const char *lib_name
, char *dir
, int flags
, char **err
)
482 while ((path
= mono_dl_build_path (dir
, lib_name
, &iter
))) {
484 runtime_lib
= mono_dl_open (path
, flags
, err
);
493 mono_dl_open_runtime_lib (const char* lib_name
, int flags
, char **error_msg
)
495 MonoDl
*runtime_lib
= NULL
;
500 binl
= mono_dl_get_executable_path (buf
, sizeof (buf
));
504 char *resolvedname
, *name
;
505 char *baseparent
= NULL
;
507 resolvedname
= mono_path_resolve_symlinks (buf
);
508 base
= g_path_get_dirname (resolvedname
);
509 name
= g_strdup_printf ("%s/.libs", base
);
510 runtime_lib
= try_load (lib_name
, name
, flags
, error_msg
);
513 baseparent
= g_path_get_dirname (base
);
515 name
= g_strdup_printf ("%s/lib", baseparent
);
516 runtime_lib
= try_load (lib_name
, name
, flags
, error_msg
);
521 name
= g_strdup_printf ("%s/Libraries", baseparent
);
522 runtime_lib
= try_load (lib_name
, name
, flags
, error_msg
);
527 name
= g_strdup_printf ("%s/profiler/.libs", baseparent
);
528 runtime_lib
= try_load (lib_name
, name
, flags
, error_msg
);
532 g_free (resolvedname
);
536 runtime_lib
= try_load (lib_name
, NULL
, flags
, error_msg
);