2010-06-21 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / utils / mono-dl.c
blob6a233c75acbafe5cee88dc4f26edbd23500f31ef
1 /*
2 * mono-dl.c: Interface to the dynamic linker
4 * Author:
5 * Mono Team (http://www.mono-project.com)
7 * Copyright 2001-2004 Ximian, Inc.
8 * Copyright 2004-2009 Novell, Inc.
9 */
10 #include "config.h"
11 #include "mono/utils/mono-dl.h"
12 #include "mono/utils/mono-embed.h"
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <ctype.h>
17 #include <string.h>
18 #include <glib.h>
20 #ifdef TARGET_WIN32
21 #define SOPREFIX ""
22 static const char suffixes [][5] = {
23 ".dll"
25 #elif defined(__APPLE__)
26 #define SOPREFIX "lib"
27 static const char suffixes [][8] = {
28 ".dylib",
29 ".so",
30 ".bundle"
32 #elif EMBEDDED_PINVOKE
33 #define SOPREFIX ""
34 static const char suffixes [][1] = {
37 #else
38 #define SOPREFIX "lib"
39 static const char suffixes [][4] = {
40 ".so"
42 #endif
44 #ifdef TARGET_WIN32
46 #include <windows.h>
47 #include <psapi.h>
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)
58 #include <dlfcn.h>
60 #ifndef RTLD_LAZY
61 #define RTLD_LAZY 1
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 ())
71 static int
72 convert_flags (int flags)
74 int lflags = flags & MONO_DL_LOCAL? 0: RTLD_GLOBAL;
76 if (flags & MONO_DL_LAZY)
77 lflags |= RTLD_LAZY;
78 else
79 lflags |= RTLD_NOW;
80 return lflags;
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);
89 char *LL_SO_ERROR();
90 #define LL_SO_TRFLAGS(flags) 0
92 #else
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")
101 #endif
103 struct _MonoDl {
104 SO_HANDLE_TYPE handle;
105 int main_module;
108 #ifdef TARGET_WIN32
110 static char*
111 w32_dlerror (void)
113 char* ret = NULL;
114 wchar_t* buf = NULL;
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);
121 LocalFree (buf);
122 } else {
123 g_assert_not_reached ();
125 return ret;
128 static gpointer
129 w32_find_symbol (MonoDl *module, const gchar *symbol_name)
131 HMODULE *modules;
132 DWORD buffer_size = sizeof (HMODULE) * 1024;
133 DWORD needed, i;
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);
142 if (proc != NULL)
143 return proc;
145 /* get the symbol from the loaded DLLs */
146 modules = (HMODULE *) g_malloc (buffer_size);
147 if (modules == NULL)
148 return NULL;
150 if (!EnumProcessModules (GetCurrentProcess (), modules,
151 buffer_size, &needed)) {
152 g_free (modules);
153 return NULL;
156 /* check whether the supplied buffer was too small, realloc, retry */
157 if (needed > buffer_size) {
158 g_free (modules);
160 buffer_size = needed;
161 modules = (HMODULE *) g_malloc (buffer_size);
163 if (modules == NULL)
164 return NULL;
166 if (!EnumProcessModules (GetCurrentProcess (), modules,
167 buffer_size, &needed)) {
168 g_free (modules);
169 return NULL;
173 for (i = 0; i < needed / sizeof (HANDLE); i++) {
174 proc = GetProcAddress (modules [i], symbol_name);
175 if (proc != NULL) {
176 g_free (modules);
177 return proc;
181 g_free (modules);
182 return NULL;
186 static gpointer
187 w32_load_module (const char* file, int flags)
189 gpointer hModule = NULL;
190 if (file) {
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);
196 if (!hModule)
197 last_error = GetLastError ();
199 SetErrorMode (last_sem);
200 g_free (file_utf16);
202 if (!hModule)
203 SetLastError (last_error);
204 } else {
205 hModule = GetModuleHandle (NULL);
207 return hModule;
209 #endif
212 * read a value string from line with any of the following formats:
213 * \s*=\s*'string'
214 * \s*=\s*"string"
215 * \s*=\s*non_white_space_string
217 static char*
218 read_string (char *p, FILE *file)
220 char *endp;
221 char *startp;
222 while (*p && isspace (*p))
223 ++p;
224 if (*p == 0)
225 return NULL;
226 if (*p == '=')
227 p++;
228 while (*p && isspace (*p))
229 ++p;
230 if (*p == '\'' || *p == '"') {
231 char t = *p;
232 p++;
233 startp = p;
234 endp = strchr (p, t);
235 /* FIXME: may need to read more from file... */
236 if (!endp)
237 return NULL;
238 *endp = 0;
239 return g_memdup (startp, (endp - startp) + 1);
241 if (*p == 0)
242 return NULL;
243 startp = p;
244 while (*p && !isspace (*p))
245 ++p;
246 *p = 0;
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
254 static char*
255 get_dl_name_from_libtool (const char *libtool_file)
257 FILE* file;
258 char buf [512];
259 char *line, *dlname = NULL, *libdir = NULL, *installed = NULL;
260 if (!(file = fopen (libtool_file, "r")))
261 return NULL;
262 while ((line = fgets (buf, 512, file))) {
263 while (*line && isspace (*line))
264 ++line;
265 if (*line == '#' || *line == 0)
266 continue;
267 if (strncmp ("dlname", line, 6) == 0) {
268 g_free (dlname);
269 dlname = read_string (line + 6, file);
270 } else if (strncmp ("libdir", line, 6) == 0) {
271 g_free (libdir);
272 libdir = read_string (line + 6, file);
273 } else if (strncmp ("installed", line, 9) == 0) {
274 g_free (installed);
275 installed = read_string (line + 9, file);
278 fclose (file);
279 line = NULL;
280 if (installed && strcmp (installed, "no") == 0) {
281 char *dir = g_path_get_dirname (libtool_file);
282 if (dlname)
283 line = g_strconcat (dir, G_DIR_SEPARATOR_S ".libs" G_DIR_SEPARATOR_S, dlname, NULL);
284 g_free (dir);
285 } else {
286 if (libdir && dlname)
287 line = g_strconcat (libdir, G_DIR_SEPARATOR_S, dlname, NULL);
289 g_free (dlname);
290 g_free (libdir);
291 g_free (installed);
292 return line;
296 * mono_dl_open:
297 * @name: name of file containing shared module
298 * @flags: flags
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
303 * binary image.
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.
312 MonoDl*
313 mono_dl_open (const char *name, int flags, char **error_msg)
315 MonoDl *module;
316 void *lib;
317 int lflags = LL_SO_TRFLAGS (flags);
319 if (error_msg)
320 *error_msg = NULL;
322 module = malloc (sizeof (MonoDl));
323 if (!module) {
324 if (error_msg)
325 *error_msg = g_strdup ("Out of memory");
326 return NULL;
328 module->main_module = name == NULL? TRUE: FALSE;
329 lib = LL_SO_OPEN (name, lflags);
330 if (!lib) {
331 char *lname;
332 char *llname;
333 const char *suff;
334 const char *ext;
335 /* This platform does not support dlopen */
336 if (name == NULL) {
337 free (module);
338 return NULL;
341 suff = ".la";
342 ext = strrchr (name, '.');
343 if (ext && strcmp (ext, ".la") == 0)
344 suff = "";
345 lname = g_strconcat (name, suff, NULL);
346 llname = get_dl_name_from_libtool (lname);
347 g_free (lname);
348 if (llname) {
349 lib = LL_SO_OPEN (llname, lflags);
350 g_free (llname);
352 if (!lib) {
353 if (error_msg) {
354 *error_msg = LL_SO_ERROR ();
356 free (module);
357 return NULL;
360 module->handle = lib;
361 return module;
365 * mono_dl_symbol:
366 * @module: a MonoDl pointer
367 * @name: symbol name
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
375 char*
376 mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
378 void *sym;
380 #if MONO_DL_NEED_USCORE
382 char *usname = malloc (strlen (name) + 2);
383 *usname = '_';
384 strcpy (usname + 1, name);
385 sym = LL_SO_SYMBOL (module, usname);
386 free (usname);
388 #else
389 sym = LL_SO_SYMBOL (module, name);
390 #endif
391 if (sym) {
392 if (symbol)
393 *symbol = sym;
394 return NULL;
396 if (symbol)
397 *symbol = NULL;
398 return LL_SO_ERROR ();
402 * mono_dl_close:
403 * @module: a MonoDl pointer
405 * Unload the given module and free the module memory.
407 * Returns: 0 on success.
409 void
410 mono_dl_close (MonoDl *module)
412 LL_SO_CLOSE (module);
413 free (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
431 char*
432 mono_dl_build_path (const char *directory, const char *name, void **iter)
434 int idx;
435 const char *prefix;
436 const char *suffix;
437 gboolean first_call;
438 int prlen;
439 int suffixlen;
440 char *res;
442 if (!iter)
443 return NULL;
446 The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our
447 "bootstrap" phase in which we check the passed name verbatim and only if we fail to find
448 the dll thus named, we start appending suffixes, each time increasing idx twice (since now
449 the 0 value became special and we need to offset idx to a 0-based array index). This is
450 done to handle situations when mapped dll name is specified as libsomething.so.1 or
451 libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill
452 here.
454 idx = GPOINTER_TO_UINT (*iter);
455 if (idx == 0) {
456 first_call = TRUE;
457 suffix = "";
458 suffixlen = 0;
459 } else {
460 idx--;
461 if (idx >= G_N_ELEMENTS (suffixes))
462 return NULL;
463 first_call = FALSE;
464 suffix = suffixes [idx];
465 suffixlen = strlen (suffix);
468 prlen = strlen (SOPREFIX);
469 if (prlen && strncmp (name, SOPREFIX, prlen) != 0)
470 prefix = SOPREFIX;
471 else
472 prefix = "";
474 if (first_call || (suffixlen && strstr (name, suffix) == (name + strlen (name) - suffixlen)))
475 suffix = "";
477 if (directory && *directory)
478 res = g_strconcat (directory, G_DIR_SEPARATOR_S, prefix, name, suffix, NULL);
479 else
480 res = g_strconcat (prefix, name, suffix, NULL);
481 ++idx;
482 if (!first_call)
483 idx++;
484 *iter = GUINT_TO_POINTER (idx);
485 return res;
488 #if EMBEDDED_PINVOKE
489 static GHashTable *mono_dls;
490 static char *ll_last_error = "";
493 * mono_dl_register_library:
494 * @name: Library name, this is the name used by the DllImport as the external library name
495 * @mappings: the mappings to register for P/Invoke.
497 * This function is only available on builds that define
498 * EMBEDDED_PINVOKE, this is available for systems that do not provide
499 * a dynamic linker but still want to use DllImport to easily invoke
500 * code from the managed side into the unmanaged world.
502 * Mappings is a pointer to the first element of an array of
503 * MonoDlMapping values. The list must be terminated with both
504 * the name and addr fields set to NULL.
506 * This is typically used like this:
507 * MonoDlMapping sample_library_mappings [] = {
508 * { "CallMe", CallMe },
509 * { NULL, NULL }
510 * };
512 * ...
513 * main ()
515 * ...
516 * mono_dl_register_library ("sample", sample_library_mappings);
517 * ...
520 * Then the C# code can use this P/Invoke signature:
522 * [DllImport ("sample")]
523 * extern static int CallMe (int f);
525 void
526 mono_dl_register_library (const char *name, MonoDlMapping *mappings)
528 if (mono_dls == NULL)
529 mono_dls = g_hash_table_new (g_str_hash, g_str_equal);
531 printf ("Inserting: 0x%p\n", mappings);
532 g_hash_table_insert (mono_dls, g_strdup (name), mappings);
535 void *
536 LL_SO_OPEN (const char *file, int flag)
538 void *mappings;
540 if (mono_dls == NULL){
541 ll_last_error = "Library not registered";
542 return NULL;
545 mappings = g_hash_table_lookup (mono_dls, file);
546 ll_last_error = mappings == NULL ? "File not registered" : "";
547 return mappings;
550 int LL_SO_CLOSE (void *handle)
552 // No-op
553 return 0;
556 void *
557 _LL_SO_SYMBOL (void *handle, const char *symbol)
559 MonoDlMapping *mappings = (MonoDlMapping *) handle;
561 for (;mappings->name; mappings++){
562 if (strcmp (symbol, mappings->name) == 0){
563 ll_last_error = "";
564 return mappings->addr;
567 ll_last_error = "Symbol not found";
568 return NULL;
571 char *
572 LL_SO_ERROR (void)
574 return g_strdup (ll_last_error);
576 #endif