2 * mono-dl.c: Interface to the dynamic linker
5 * Mono Team (http://www.mono-project.com)
7 * Copyright 2001-2004 Ximian, Inc.
8 * Copyright 2004-2009 Novell, Inc.
11 #include "mono/utils/mono-dl.h"
12 #include "mono/utils/mono-embed.h"
22 static const char suffixes
[][5] = {
25 #elif defined(__APPLE__)
26 #define SOPREFIX "lib"
27 static const char suffixes
[][8] = {
32 #elif EMBEDDED_PINVOKE
34 static const char suffixes
[][1] = {
38 #define SOPREFIX "lib"
39 static const char suffixes
[][4] = {
49 #define SO_HANDLE_TYPE HMODULE
50 #define LL_SO_OPEN(file,flags) w32_load_module ((file), (flags))
51 #define LL_SO_CLOSE(module) do { if (!(module)->main_module) FreeLibrary ((module)->handle); } while (0)
52 #define LL_SO_SYMBOL(module, name) w32_find_symbol ((module), (name))
53 #define LL_SO_TRFLAGS(flags) 0
54 #define LL_SO_ERROR() w32_dlerror ()
56 #elif defined (HAVE_DL_LOADER)
62 #endif /* RTLD_LAZY */
64 #define SO_HANDLE_TYPE void*
65 #define LL_SO_OPEN(file,flags) dlopen ((file), (flags))
66 #define LL_SO_CLOSE(module) dlclose ((module)->handle)
67 #define LL_SO_SYMBOL(module, name) dlsym ((module)->handle, (name))
68 #define LL_SO_TRFLAGS(flags) convert_flags ((flags))
69 #define LL_SO_ERROR() g_strdup (dlerror ())
72 convert_flags (int flags
)
74 int lflags
= flags
& MONO_DL_LOCAL
? 0: RTLD_GLOBAL
;
76 if (flags
& MONO_DL_LAZY
)
83 #elif EMBEDDED_PINVOKE
84 #define SO_HANDLE_TYPE void*
85 void *LL_SO_OPEN (const char *file
, int flags
);
86 int LL_SO_CLOSE (void *handle
);
87 #define LL_SO_SYMBOL(module,symbol) _LL_SO_SYMBOL((module)->handle, (symbol))
88 void *_LL_SO_SYMBOL (void *handle
, const char *symbol
);
90 #define LL_SO_TRFLAGS(flags) 0
93 /* no dynamic loader supported */
94 #define SO_HANDLE_TYPE void*
95 #define LL_SO_OPEN(file,flags) NULL
96 #define LL_SO_CLOSE(module)
97 #define LL_SO_SYMBOL(module, name) NULL
98 #define LL_SO_TRFLAGS(flags) (flags)
99 #define LL_SO_ERROR() g_strdup ("No support for dynamic loader")
104 SO_HANDLE_TYPE handle
;
108 #ifdef PLATFORM_WIN32
115 DWORD code
= GetLastError ();
117 if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_ALLOCATE_BUFFER
, NULL
,
118 code
, MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), (LPTSTR
)&buf
, 0, NULL
))
120 ret
= g_utf16_to_utf8 (buf
, wcslen(buf
), NULL
, NULL
, NULL
);
123 g_assert_not_reached ();
129 w32_find_symbol (MonoDl
*module
, const gchar
*symbol_name
)
132 DWORD buffer_size
= sizeof (HMODULE
) * 1024;
134 gpointer proc
= NULL
;
136 /* get the symbol directly from the specified module */
137 if (!module
->main_module
)
138 return GetProcAddress (module
->handle
, symbol_name
);
140 /* get the symbol from the main module */
141 proc
= GetProcAddress (module
->handle
, symbol_name
);
145 /* get the symbol from the loaded DLLs */
146 modules
= (HMODULE
*) g_malloc (buffer_size
);
150 if (!EnumProcessModules (GetCurrentProcess (), modules
,
151 buffer_size
, &needed
)) {
156 /* check whether the supplied buffer was too small, realloc, retry */
157 if (needed
> buffer_size
) {
160 buffer_size
= needed
;
161 modules
= (HMODULE
*) g_malloc (buffer_size
);
166 if (!EnumProcessModules (GetCurrentProcess (), modules
,
167 buffer_size
, &needed
)) {
173 for (i
= 0; i
< needed
/ sizeof (HANDLE
); i
++) {
174 proc
= GetProcAddress (modules
[i
], symbol_name
);
187 w32_load_module (const char* file
, int flags
)
189 gpointer hModule
= NULL
;
191 gunichar2
* file_utf16
= g_utf8_to_utf16 (file
, strlen (file
), NULL
, NULL
, NULL
);
192 guint last_sem
= SetErrorMode (SEM_FAILCRITICALERRORS
);
193 guint32 last_error
= 0;
195 hModule
= LoadLibrary (file_utf16
);
197 last_error
= GetLastError ();
199 SetErrorMode (last_sem
);
203 SetLastError (last_error
);
205 hModule
= GetModuleHandle (NULL
);
212 * read a value string from line with any of the following formats:
215 * \s*=\s*non_white_space_string
218 read_string (char *p
, FILE *file
)
222 while (*p
&& isspace (*p
))
228 while (*p
&& isspace (*p
))
230 if (*p
== '\'' || *p
== '"') {
234 endp
= strchr (p
, t
);
235 /* FIXME: may need to read more from file... */
239 return g_memdup (startp
, (endp
- startp
) + 1);
244 while (*p
&& !isspace (*p
))
247 return g_memdup (startp
, (p
- startp
) + 1);
251 * parse a libtool .la file and return the path of the file to dlopen ()
252 * handling both the installed and uninstalled cases
255 get_dl_name_from_libtool (const char *libtool_file
)
259 char *line
, *dlname
= NULL
, *libdir
= NULL
, *installed
= NULL
;
260 if (!(file
= fopen (libtool_file
, "r")))
262 while ((line
= fgets (buf
, 512, file
))) {
263 while (*line
&& isspace (*line
))
265 if (*line
== '#' || *line
== 0)
267 if (strncmp ("dlname", line
, 6) == 0) {
269 dlname
= read_string (line
+ 6, file
);
270 } else if (strncmp ("libdir", line
, 6) == 0) {
272 libdir
= read_string (line
+ 6, file
);
273 } else if (strncmp ("installed", line
, 9) == 0) {
275 installed
= read_string (line
+ 9, file
);
280 if (installed
&& strcmp (installed
, "no") == 0) {
281 char *dir
= g_path_get_dirname (libtool_file
);
283 line
= g_strconcat (dir
, G_DIR_SEPARATOR_S
".libs" G_DIR_SEPARATOR_S
, dlname
, NULL
);
286 if (libdir
&& dlname
)
287 line
= g_strconcat (libdir
, G_DIR_SEPARATOR_S
, dlname
, NULL
);
297 * @name: name of file containing shared module
299 * @error_msg: pointer for error message on failure
301 * Load the given file @name as a shared library or dynamically loadable
302 * module. @name can be NULL to indicate loading the currently executing
304 * @flags can have the MONO_DL_LOCAL bit set to avoid exporting symbols
305 * from the module to the shared namespace. The MONO_DL_LAZY bit can be set
306 * to lazily load the symbols instead of resolving everithing at load time.
307 * @error_msg points to a string where an error message will be stored in
310 * Returns: a MonoDl pointer on success, NULL on failure.
313 mono_dl_open (const char *name
, int flags
, char **error_msg
)
317 int lflags
= LL_SO_TRFLAGS (flags
);
322 module
= malloc (sizeof (MonoDl
));
325 *error_msg
= g_strdup ("Out of memory");
328 module
->main_module
= name
== NULL
? TRUE
: FALSE
;
329 lib
= LL_SO_OPEN (name
, lflags
);
335 /* This platform does not support dlopen */
342 ext
= strrchr (name
, '.');
343 if (ext
&& strcmp (ext
, ".la") == 0)
345 lname
= g_strconcat (name
, suff
, NULL
);
346 llname
= get_dl_name_from_libtool (lname
);
349 lib
= LL_SO_OPEN (llname
, lflags
);
354 *error_msg
= LL_SO_ERROR ();
360 module
->handle
= lib
;
366 * @module: a MonoDl pointer
368 * @symbol: pointer for the result value
370 * Load the address of symbol @name from the given @module.
371 * The address is stored in the pointer pointed to by @symbol.
373 * Returns: NULL on success, an error message on failure
376 mono_dl_symbol (MonoDl
*module
, const char *name
, void **symbol
)
380 #if MONO_DL_NEED_USCORE
382 char *usname
= malloc (strlen (name
) + 2);
384 strcpy (usname
+ 1, name
);
385 sym
= LL_SO_SYMBOL (module
, usname
);
389 sym
= LL_SO_SYMBOL (module
, name
);
398 return LL_SO_ERROR ();
403 * @module: a MonoDl pointer
405 * Unload the given module and free the module memory.
407 * Returns: 0 on success.
410 mono_dl_close (MonoDl
*module
)
412 LL_SO_CLOSE (module
);
417 * mono_dl_build_path:
418 * @directory: optional directory
419 * @name: base name of the library
420 * @iter: iterator token
422 * Given a directory name and the base name of a library, iterate
423 * over the possible file names of the library, taking into account
424 * the possible different suffixes and prefixes on the host platform.
426 * The returned file name must be freed by the caller.
427 * @iter must point to a NULL pointer the first time the function is called
428 * and then passed unchanged to the following calls.
429 * Returns: the filename or NULL at the end of the iteration
432 mono_dl_build_path (const char *directory
, const char *name
, void **iter
)
441 idx
= GPOINTER_TO_UINT (*iter
);
442 if (idx
>= G_N_ELEMENTS (suffixes
))
445 prlen
= strlen (SOPREFIX
);
446 if (prlen
&& strncmp (name
, SOPREFIX
, prlen
) != 0)
450 /* if the platform prefix is already provided, we suppose the caller knows the full name already */
451 if (prlen
&& strncmp (name
, SOPREFIX
, prlen
) == 0)
454 suffix
= suffixes
[idx
];
455 if (directory
&& *directory
)
456 res
= g_strconcat (directory
, G_DIR_SEPARATOR_S
, prefix
, name
, suffixes
[idx
], NULL
);
458 res
= g_strconcat (prefix
, name
, suffixes
[idx
], NULL
);
460 *iter
= GUINT_TO_POINTER (idx
);
465 static GHashTable
*mono_dls
;
466 static char *ll_last_error
= "";
469 * mono_dl_register_library:
470 * @name: Library name, this is the name used by the DllImport as the external library name
471 * @mappings: the mappings to register for P/Invoke.
473 * This function is only available on builds that define
474 * EMBEDDED_PINVOKE, this is available for systems that do not provide
475 * a dynamic linker but still want to use DllImport to easily invoke
476 * code from the managed side into the unmanaged world.
478 * Mappings is a pointer to the first element of an array of
479 * MonoDlMapping values. The list must be terminated with both
480 * the name and addr fields set to NULL.
482 * This is typically used like this:
483 * MonoDlMapping sample_library_mappings [] = {
484 * { "CallMe", CallMe },
492 * mono_dl_register_library ("sample", sample_library_mappings);
496 * Then the C# code can use this P/Invoke signature:
498 * [DllImport ("sample")]
499 * extern static int CallMe (int f);
502 mono_dl_register_library (const char *name
, MonoDlMapping
*mappings
)
504 if (mono_dls
== NULL
)
505 mono_dls
= g_hash_table_new (g_str_hash
, g_str_equal
);
507 printf ("Inserting: 0x%p\n", mappings
);
508 g_hash_table_insert (mono_dls
, g_strdup (name
), mappings
);
512 LL_SO_OPEN (const char *file
, int flag
)
516 if (mono_dls
== NULL
){
517 ll_last_error
= "Library not registered";
521 mappings
= g_hash_table_lookup (mono_dls
, file
);
522 ll_last_error
= mappings
== NULL
? "File not registered" : "";
523 printf ("Returning mappings=0x%p\n", mappings
);
527 int LL_SO_CLOSE (void *handle
)
534 _LL_SO_SYMBOL (void *handle
, const char *symbol
)
536 MonoDlMapping
*mappings
= (MonoDlMapping
*) handle
;
538 printf ("During lookup: 0x%p\n", handle
);
539 for (;mappings
->name
; mappings
++){
540 if (strcmp (symbol
, mappings
->name
) == 0){
542 return mappings
->addr
;
545 ll_last_error
= "Symbol not found";
552 return ll_last_error
;