New test.
[mono.git] / mono / utils / mono-dl.c
1 /*
2  * mono-dl.c: Interface to the dynamic linker
3  *
4  * Author:
5  *    Mono Team (http://www.mono-project.com)
6  *
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"
13
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <ctype.h>
17 #include <string.h>
18 #include <glib.h>
19
20 #ifdef TARGET_WIN32
21 #define SOPREFIX ""
22 static const char suffixes [][5] = {
23         ".dll"
24 };
25 #elif defined(__APPLE__)
26 #define SOPREFIX "lib"
27 static const char suffixes [][8] = {
28         ".dylib",
29         ".so",
30         ".bundle"
31 };
32 #elif EMBEDDED_PINVOKE
33 #define SOPREFIX ""
34 static const char suffixes [][1] = {
35         ""
36 };
37 #else
38 #define SOPREFIX "lib"
39 static const char suffixes [][4] = {
40         ".so"
41 };
42 #endif
43
44 #ifdef TARGET_WIN32
45
46 #include <windows.h>
47 #include <psapi.h>
48
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 ()
55
56 #elif defined (HAVE_DL_LOADER)
57
58 #include <dlfcn.h>
59
60 #ifndef RTLD_LAZY
61 #define RTLD_LAZY       1
62 #endif  /* RTLD_LAZY */
63
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 ())
70
71 static int
72 convert_flags (int flags)
73 {
74         int lflags = flags & MONO_DL_LOCAL? 0: RTLD_GLOBAL;
75
76         if (flags & MONO_DL_LAZY)
77                 lflags |= RTLD_LAZY;
78         else
79                 lflags |= RTLD_NOW;
80         return lflags;
81 }
82
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
91
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")
100
101 #endif
102
103 struct _MonoDl {
104         SO_HANDLE_TYPE handle;
105         int main_module;
106 };
107
108 #ifdef TARGET_WIN32
109
110 static char*
111 w32_dlerror (void)
112 {
113         char* ret = NULL;
114         wchar_t* buf = NULL;
115         DWORD code = GetLastError ();
116
117         if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL,
118                 code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buf, 0, NULL))
119         {
120                 ret = g_utf16_to_utf8 (buf, wcslen(buf), NULL, NULL, NULL);
121                 LocalFree (buf);
122         } else {
123                 g_assert_not_reached ();
124         }
125         return ret;
126 }
127
128 static gpointer
129 w32_find_symbol (MonoDl *module, const gchar *symbol_name)
130 {
131         HMODULE *modules;
132         DWORD buffer_size = sizeof (HMODULE) * 1024;
133         DWORD needed, i;
134         gpointer proc = NULL;
135
136         /* get the symbol directly from the specified module */
137         if (!module->main_module)
138                 return GetProcAddress (module->handle, symbol_name);
139
140         /* get the symbol from the main module */
141         proc = GetProcAddress (module->handle, symbol_name);
142         if (proc != NULL)
143                 return proc;
144
145         /* get the symbol from the loaded DLLs */
146         modules = (HMODULE *) g_malloc (buffer_size);
147         if (modules == NULL)
148                 return NULL;
149
150         if (!EnumProcessModules (GetCurrentProcess (), modules,
151                                  buffer_size, &needed)) {
152                 g_free (modules);
153                 return NULL;
154         }
155
156         /* check whether the supplied buffer was too small, realloc, retry */
157         if (needed > buffer_size) {
158                 g_free (modules);
159
160                 buffer_size = needed;
161                 modules = (HMODULE *) g_malloc (buffer_size);
162
163                 if (modules == NULL)
164                         return NULL;
165
166                 if (!EnumProcessModules (GetCurrentProcess (), modules,
167                                          buffer_size, &needed)) {
168                         g_free (modules);
169                         return NULL;
170                 }
171         }
172
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;
178                 }
179         }
180
181         g_free (modules);
182         return NULL;
183 }
184
185
186 static gpointer
187 w32_load_module (const char* file, int flags)
188 {
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;
194
195                 hModule = LoadLibrary (file_utf16);
196                 if (!hModule)
197                         last_error = GetLastError ();
198
199                 SetErrorMode (last_sem);
200                 g_free (file_utf16);
201
202                 if (!hModule)
203                         SetLastError (last_error);
204         } else {
205                 hModule = GetModuleHandle (NULL);
206         }
207         return hModule;
208 }
209 #endif
210
211 /*
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
216  */
217 static char*
218 read_string (char *p, FILE *file)
219 {
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);
240         }
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);
248 }
249
250 /*
251  * parse a libtool .la file and return the path of the file to dlopen ()
252  * handling both the installed and uninstalled cases
253  */
254 static char*
255 get_dl_name_from_libtool (const char *libtool_file)
256 {
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);
276                 }
277         }
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);
288         }
289         g_free (dlname);
290         g_free (libdir);
291         g_free (installed);
292         return line;
293 }
294
295 /**
296  * mono_dl_open:
297  * @name: name of file containing shared module
298  * @flags: flags
299  * @error_msg: pointer for error message on failure
300  *
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.
309  *
310  * Returns: a MonoDl pointer on success, NULL on failure.
311  */
312 MonoDl*
313 mono_dl_open (const char *name, int flags, char **error_msg)
314 {
315         MonoDl *module;
316         void *lib;
317         int lflags = LL_SO_TRFLAGS (flags);
318
319         if (error_msg)
320                 *error_msg = NULL;
321
322         module = malloc (sizeof (MonoDl));
323         if (!module) {
324                 if (error_msg)
325                         *error_msg = g_strdup ("Out of memory");
326                 return NULL;
327         }
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;
339                 }
340                 
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);
351                 }
352                 if (!lib) {
353                         if (error_msg) {
354                                 *error_msg = LL_SO_ERROR ();
355                         }
356                         free (module);
357                         return NULL;
358                 }
359         }
360         module->handle = lib;
361         return module;
362 }
363
364 /**
365  * mono_dl_symbol:
366  * @module: a MonoDl pointer
367  * @name: symbol name
368  * @symbol: pointer for the result value
369  *
370  * Load the address of symbol @name from the given @module.
371  * The address is stored in the pointer pointed to by @symbol.
372  *
373  * Returns: NULL on success, an error message on failure
374  */
375 char*
376 mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
377 {
378         void *sym;
379
380 #if MONO_DL_NEED_USCORE
381         {
382                 char *usname = malloc (strlen (name) + 2);
383                 *usname = '_';
384                 strcpy (usname + 1, name);
385                 sym = LL_SO_SYMBOL (module, usname);
386                 free (usname);
387         }
388 #else
389         sym = LL_SO_SYMBOL (module, name);
390 #endif
391         if (sym) {
392                 if (symbol)
393                         *symbol = sym;
394                 return NULL;
395         }
396         if (symbol)
397                 *symbol = NULL;
398         return LL_SO_ERROR ();
399 }
400
401 /**
402  * mono_dl_close:
403  * @module: a MonoDl pointer
404  *
405  * Unload the given module and free the module memory.
406  *
407  * Returns: 0 on success.
408  */
409 void
410 mono_dl_close (MonoDl *module)
411 {
412         LL_SO_CLOSE (module);
413         free (module);
414 }
415
416 /**
417  * mono_dl_build_path:
418  * @directory: optional directory
419  * @name: base name of the library
420  * @iter: iterator token
421  *
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.
425  *
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
430  */
431 char*
432 mono_dl_build_path (const char *directory, const char *name, void **iter)
433 {
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;
444
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, suffix, NULL);
457         else
458                 res = g_strconcat (prefix, name, suffix, NULL);
459         ++idx;
460         *iter = GUINT_TO_POINTER (idx);
461         return res;
462 }
463
464 #if EMBEDDED_PINVOKE
465 static GHashTable *mono_dls;
466 static char *ll_last_error = "";
467
468 /**
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.
472  *
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.
477  *
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.
481  *
482  * This is typically used like this:
483  * MonoDlMapping sample_library_mappings [] = {
484  *   { "CallMe", CallMe },
485  *   { NULL, NULL }
486  * };
487  *
488  * ...
489  * main ()
490  * {
491  *    ...
492  *    mono_dl_register_library ("sample", sample_library_mappings);
493  *    ...
494  * }
495  *
496  * Then the C# code can use this P/Invoke signature:
497  *
498  *      [DllImport ("sample")]
499  *      extern static int CallMe (int f);
500  */
501 void
502 mono_dl_register_library (const char *name, MonoDlMapping *mappings)
503 {
504         if (mono_dls == NULL)
505                 mono_dls = g_hash_table_new (g_str_hash, g_str_equal);
506         
507         printf ("Inserting: 0x%p\n", mappings);
508         g_hash_table_insert (mono_dls, g_strdup (name), mappings);
509 }
510
511 void *
512 LL_SO_OPEN (const char *file, int flag)
513 {
514         void *mappings;
515         
516         if (mono_dls == NULL){
517                 ll_last_error = "Library not registered";
518                 return NULL;
519         }
520                 
521         mappings = g_hash_table_lookup (mono_dls, file);
522         ll_last_error = mappings == NULL ? "File not registered" : "";
523         return mappings;
524 }
525
526 int LL_SO_CLOSE (void *handle)
527 {
528         // No-op
529         return 0;
530 }
531
532 void *
533 _LL_SO_SYMBOL (void *handle, const char *symbol)
534 {
535         MonoDlMapping *mappings = (MonoDlMapping *) handle;
536         
537         for (;mappings->name; mappings++){
538                 if (strcmp (symbol, mappings->name) == 0){
539                         ll_last_error = "";
540                         return mappings->addr;
541                 }
542         }
543         ll_last_error = "Symbol not found";
544         return NULL;
545 }
546
547 char *
548 LL_SO_ERROR (void)
549 {
550         return g_strdup (ll_last_error);
551 }
552 #endif