Mark tests as not working under TARGET_JVM
[mono.git] / mono / utils / mono-dl.c
1 #include "config.h"
2 #include "mono/utils/mono-dl.h"
3
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <ctype.h>
7 #include <string.h>
8 #include <glib.h>
9
10 #ifdef PLATFORM_WIN32
11 #define SOPREFIX ""
12 static const char suffixes [][5] = {
13         ".dll"
14 };
15 #elif defined(__APPLE__)
16 #define SOPREFIX "lib"
17 static const char suffixes [][8] = {
18         ".dylib",
19         ".so",
20         ".bundle"
21 };
22 #else
23 #define SOPREFIX "lib"
24 static const char suffixes [][4] = {
25         ".so"
26 };
27 #endif
28
29 #ifdef PLATFORM_WIN32
30
31 #include <windows.h>
32 #include <psapi.h>
33
34 #define SO_HANDLE_TYPE HMODULE
35 #define LL_SO_OPEN(file,flags) w32_load_module ((file), (flags))
36 #define LL_SO_CLOSE(module) do { if (!(module)->main_module) FreeLibrary ((module)->handle); } while (0)
37 #define LL_SO_SYMBOL(module, name) w32_find_symbol ((module), (name))
38 #define LL_SO_TRFLAGS(flags) 0
39 #define LL_SO_ERROR() w32_dlerror ()
40
41 #elif defined (HAVE_DL_LOADER)
42
43 #include <dlfcn.h>
44
45 #ifndef RTLD_LAZY
46 #define RTLD_LAZY       1
47 #endif  /* RTLD_LAZY */
48
49 #define SO_HANDLE_TYPE void*
50 #define LL_SO_OPEN(file,flags) dlopen ((file), (flags))
51 #define LL_SO_CLOSE(module) dlclose ((module)->handle)
52 #define LL_SO_SYMBOL(module, name) dlsym ((module)->handle, (name))
53 #define LL_SO_TRFLAGS(flags) convert_flags ((flags))
54 #define LL_SO_ERROR() g_strdup (dlerror ())
55
56 static int
57 convert_flags (int flags)
58 {
59         int lflags = flags & MONO_DL_LOCAL? 0: RTLD_GLOBAL;
60
61         if (flags & MONO_DL_LAZY)
62                 lflags |= RTLD_LAZY;
63         else
64                 lflags |= RTLD_NOW;
65         return lflags;
66 }
67
68 #else
69 /* no dynamic loader supported */
70 #define SO_HANDLE_TYPE void*
71 #define LL_SO_OPEN(file,flags) NULL
72 #define LL_SO_CLOSE(module) 
73 #define LL_SO_SYMBOL(module, name) NULL
74 #define LL_SO_TRFLAGS(flags) (flags)
75 #define LL_SO_ERROR() g_strdup ("No support for dynamic loader")
76
77 #endif
78
79 struct _MonoDl {
80         SO_HANDLE_TYPE handle;
81         int main_module;
82 };
83
84 #ifdef PLATFORM_WIN32
85
86 static char*
87 w32_dlerror (void)
88 {
89         char* ret = NULL;
90         wchar_t* buf = NULL;
91         DWORD code = GetLastError ();
92
93         if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL,
94                 code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 0, NULL))
95         {
96                 ret = g_utf16_to_utf8 (buf, wcslen(buf), NULL, NULL, NULL);
97                 LocalFree (buf);
98         }
99         return ret;
100 }
101
102 static gpointer
103 w32_find_symbol (MonoDl *module, const gchar *symbol_name)
104 {
105         HMODULE *modules;
106         DWORD buffer_size = sizeof (HMODULE) * 1024;
107         DWORD needed, i;
108         gpointer proc = NULL;
109
110         /* get the symbol directly from the specified module */
111         if (!module->main_module)
112                 return GetProcAddress (module->handle, symbol_name);
113
114         /* get the symbol from the main module */
115         proc = GetProcAddress (module->handle, symbol_name);
116         if (proc != NULL)
117                 return proc;
118
119         /* get the symbol from the loaded DLLs */
120         modules = (HMODULE *) g_malloc (buffer_size);
121         if (modules == NULL)
122                 return NULL;
123
124         if (!EnumProcessModules (GetCurrentProcess (), modules,
125                                  buffer_size, &needed)) {
126                 g_free (modules);
127                 return NULL;
128         }
129
130         /* check whether the supplied buffer was too small, realloc, retry */
131         if (needed > buffer_size) {
132                 g_free (modules);
133
134                 buffer_size = needed;
135                 modules = (HMODULE *) g_malloc (buffer_size);
136
137                 if (modules == NULL)
138                         return NULL;
139
140                 if (!EnumProcessModules (GetCurrentProcess (), modules,
141                                          buffer_size, &needed)) {
142                         g_free (modules);
143                         return NULL;
144                 }
145         }
146
147         for (i = 0; i < needed / sizeof (HANDLE); i++) {
148                 proc = GetProcAddress (modules [i], symbol_name);
149                 if (proc != NULL) {
150                         g_free (modules);
151                         return proc;
152                 }
153         }
154
155         g_free (modules);
156         return NULL;
157 }
158
159
160 static gpointer
161 w32_load_module (const char* file, int flags)
162 {
163         gpointer hModule = NULL;
164         if (file)
165         {
166                 gunichar2* file_utf16 = g_utf8_to_utf16 (file, strlen (file), NULL, NULL, NULL);
167                 hModule = LoadLibrary (file_utf16);
168                 g_free (file_utf16);
169         }
170         else
171         {
172                 hModule = GetModuleHandle (NULL);
173         }
174         return hModule;
175 }
176 #endif
177
178 /*
179  * read a value string from line with any of the following formats:
180  * \s*=\s*'string'
181  * \s*=\s*"string"
182  * \s*=\s*non_white_space_string
183  */
184 static char*
185 read_string (char *p, FILE *file)
186 {
187         char *endp;
188         char *startp;
189         while (*p && isspace (*p))
190                 ++p;
191         if (*p == 0)
192                 return NULL;
193         if (*p == '=')
194                 p++;
195         while (*p && isspace (*p))
196                 ++p;
197         if (*p == '\'' || *p == '"') {
198                 char t = *p;
199                 p++;
200                 startp = p;
201                 endp = strchr (p, t);
202                 /* FIXME: may need to read more from file... */
203                 if (!endp)
204                         return NULL;
205                 *endp = 0;
206                 return g_memdup (startp, (endp - startp) + 1);
207         }
208         if (*p == 0)
209                 return NULL;
210         startp = p;
211         while (*p && !isspace (*p))
212                 ++p;
213         *p = 0;
214         return g_memdup (startp, (p - startp) + 1);
215 }
216
217 /*
218  * parse a libtool .la file and return the path of the file to dlopen ()
219  * handling both the installed and uninstalled cases
220  */
221 static char*
222 get_dl_name_from_libtool (const char *libtool_file)
223 {
224         FILE* file;
225         char buf [512];
226         char *line, *dlname = NULL, *libdir = NULL, *installed = NULL;
227         if (!(file = fopen (libtool_file, "r")))
228                 return NULL;
229         while ((line = fgets (buf, 512, file))) {
230                 while (*line && isspace (*line))
231                         ++line;
232                 if (*line == '#' || *line == 0)
233                         continue;
234                 if (strncmp ("dlname", line, 6) == 0) {
235                         g_free (dlname);
236                         dlname = read_string (line + 6, file);
237                 } else if (strncmp ("libdir", line, 6) == 0) {
238                         g_free (libdir);
239                         libdir = read_string (line + 6, file);
240                 } else if (strncmp ("installed", line, 9) == 0) {
241                         g_free (installed);
242                         installed = read_string (line + 9, file);
243                 }
244         }
245         fclose (file);
246         line = NULL;
247         if (installed && strcmp (installed, "no") == 0) {
248                 char *dir = g_path_get_dirname (libtool_file);
249                 if (dlname)
250                         line = g_strconcat (dir, G_DIR_SEPARATOR_S ".libs" G_DIR_SEPARATOR_S, dlname, NULL);
251                 g_free (dir);
252         } else {
253                 if (libdir && dlname)
254                         line = g_strconcat (libdir, G_DIR_SEPARATOR_S, dlname, NULL);
255         }
256         g_free (dlname);
257         g_free (libdir);
258         g_free (installed);
259         return line;
260 }
261
262 /**
263  * mono_dl_open:
264  * @name: name of file containing shared module
265  * @flags: flags
266  * @error_msg: pointer for error message on failure
267  *
268  * Load the given file @name as a shared library or dynamically loadable
269  * module. @name can be NULL to indicate loading the currently executing
270  * binary image.
271  * @flags can have the MONO_DL_LOCAL bit set to avoid exporting symbols
272  * from the module to the shared namespace. The MONO_DL_LAZY bit can be set
273  * to lazily load the symbols instead of resolving everithing at load time.
274  * @error_msg points to a string where an error message will be stored in
275  * case of failure.
276  *
277  * Returns: a MonoDl pointer on success, NULL on failure.
278  */
279 MonoDl*
280 mono_dl_open (const char *name, int flags, char **error_msg)
281 {
282         MonoDl *module;
283         void *lib;
284         int lflags = LL_SO_TRFLAGS (flags);
285
286         if (error_msg)
287                 *error_msg = NULL;
288
289         module = malloc (sizeof (MonoDl));
290         if (!module) {
291                 if (error_msg)
292                         *error_msg = g_strdup ("Out of memory");
293                 return NULL;
294         }
295         module->main_module = name == NULL? TRUE: FALSE;
296         lib = LL_SO_OPEN (name, lflags);
297         if (!lib) {
298                 char *lname;
299                 char *llname;
300                 const char *suff = ".la";
301                 const char *ext = strrchr (name, '.');
302                 if (ext && strcmp (ext, ".la") == 0)
303                         suff = "";
304                 lname = g_strconcat (name, suff, NULL);
305                 llname = get_dl_name_from_libtool (lname);
306                 g_free (lname);
307                 if (llname) {
308                         lib = LL_SO_OPEN (llname, lflags);
309                         g_free (llname);
310                 }
311                 if (!lib) {
312                         if (error_msg) {
313                                 *error_msg = LL_SO_ERROR ();
314                         }
315                         free (module);
316                         return NULL;
317                 }
318         }
319         module->handle = lib;
320         return module;
321 }
322
323 /**
324  * mono_dl_symbol:
325  * @module: a MonoDl pointer
326  * @name: symbol name
327  * @symbol: pointer for the result value
328  *
329  * Load the address of symbol @name from the given @module.
330  * The address is stored in the pointer pointed to by @symbol.
331  *
332  * Returns: NULL on success, an error message on failure
333  */
334 char*
335 mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
336 {
337         void *sym;
338
339 #if MONO_DL_NEED_USCORE
340         {
341                 char *usname = malloc (strlen (name) + 2);
342                 *usname = '_';
343                 strcpy (usname + 1, name);
344                 sym = LL_SO_SYMBOL (module, usname);
345                 free (usname);
346         }
347 #else
348         sym = LL_SO_SYMBOL (module, name);
349 #endif
350         if (sym) {
351                 if (symbol)
352                         *symbol = sym;
353                 return NULL;
354         }
355         if (symbol)
356                 *symbol = NULL;
357         return LL_SO_ERROR ();
358 }
359
360 /**
361  * mono_dl_close:
362  * @module: a MonoDl pointer
363  *
364  * Unload the given module and free the module memory.
365  *
366  * Returns: 0 on success.
367  */
368 void
369 mono_dl_close (MonoDl *module)
370 {
371         LL_SO_CLOSE (module);
372         free (module);
373 }
374
375 /**
376  * mono_dl_build_path:
377  * @directory: optional directory
378  * @name: base name of the library
379  * @iter: iterator token
380  *
381  * Given a directory name and the base name of a library, iterate
382  * over the possible file names of the library, taking into account
383  * the possible different suffixes and prefixes on the host platform.
384  *
385  * The returned file name must be freed by the caller.
386  * @iter must point to a NULL pointer the first time the function is called
387  * and then passed unchanged to the following calls.
388  * Returns: the filename or NULL at the end of the iteration
389  */
390 char*
391 mono_dl_build_path (const char *directory, const char *name, void **iter)
392 {
393         int idx;
394         const char *prefix;
395         const char *suffix;
396         int prlen;
397         char *res;
398         if (!iter)
399                 return NULL;
400         idx = GPOINTER_TO_UINT (*iter);
401         if (idx >= G_N_ELEMENTS (suffixes))
402                 return NULL;
403
404         prlen = strlen (SOPREFIX);
405         if (prlen && strncmp (name, SOPREFIX, prlen) != 0)
406                 prefix = SOPREFIX;
407         else
408                 prefix = "";
409         /* if the platform prefix is already provided, we suppose the caller knows the full name already */
410         if (prlen && strncmp (name, SOPREFIX, prlen) == 0)
411                 suffix = "";
412         else
413                 suffix = suffixes [idx];
414         if (directory && *directory)
415                 res = g_strconcat (directory, G_DIR_SEPARATOR_S, prefix, name, suffixes [idx], NULL);
416         else
417                 res = g_strconcat (prefix, name, suffixes [idx], NULL);
418         ++idx;
419         *iter = GUINT_TO_POINTER (idx);
420         return res;
421 }
422