In mono/metadata:
[mono.git] / mono / utils / mono-dl.c
blobcef47331ea3902afb20dfaedfb57298c9a2931df
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 PLATFORM_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 PLATFORM_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 PLATFORM_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.
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 int prlen;
438 char *res;
439 if (!iter)
440 return NULL;
441 idx = GPOINTER_TO_UINT (*iter);
442 if (idx >= G_N_ELEMENTS (suffixes))
443 return NULL;
445 prlen = strlen (SOPREFIX);
446 if (prlen && strncmp (name, SOPREFIX, prlen) != 0)
447 prefix = SOPREFIX;
448 else
449 prefix = "";
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)
452 suffix = "";
453 else
454 suffix = suffixes [idx];
455 if (directory && *directory)
456 res = g_strconcat (directory, G_DIR_SEPARATOR_S, prefix, name, suffixes [idx], NULL);
457 else
458 res = g_strconcat (prefix, name, suffixes [idx], NULL);
459 ++idx;
460 *iter = GUINT_TO_POINTER (idx);
461 return res;
464 #if EMBEDDED_PINVOKE
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 },
485 * { NULL, NULL }
486 * };
488 * ...
489 * main ()
491 * ...
492 * mono_dl_register_library ("sample", sample_library_mappings);
493 * ...
496 * Then the C# code can use this P/Invoke signature:
498 * [DllImport ("sample")]
499 * extern static int CallMe (int f);
501 void
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);
511 void *
512 LL_SO_OPEN (const char *file, int flag)
514 void *mappings;
516 if (mono_dls == NULL){
517 ll_last_error = "Library not registered";
518 return NULL;
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);
524 return mappings;
527 int LL_SO_CLOSE (void *handle)
529 // No-op
530 return 0;
533 void *
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){
541 ll_last_error = "";
542 return mappings->addr;
545 ll_last_error = "Symbol not found";
546 return NULL;
549 char *
550 LL_SO_ERROR (void)
552 return ll_last_error;
554 #endif