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;
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
308 * case of failure. The error must be released with g_free.
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, suffix, NULL);
458 res = g_strconcat (prefix, name, suffix, 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" : "";
526 int LL_SO_CLOSE (void *handle)
533 _LL_SO_SYMBOL (void *handle, const char *symbol)
535 MonoDlMapping *mappings = (MonoDlMapping *) handle;
537 for (;mappings->name; mappings++){
538 if (strcmp (symbol, mappings->name) == 0){
540 return mappings->addr;
543 ll_last_error = "Symbol not found";
550 return g_strdup (ll_last_error);