First set of licensing changes
[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  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10  */
11 #include "config.h"
12 #include "mono/utils/mono-dl.h"
13 #include "mono/utils/mono-embed.h"
14 #include "mono/utils/mono-path.h"
15
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <ctype.h>
19 #include <string.h>
20 #include <glib.h>
21
22 struct MonoDlFallbackHandler {
23         MonoDlFallbackLoad load_func;
24         MonoDlFallbackSymbol symbol_func;
25         MonoDlFallbackClose close_func;
26         void *user_data;
27 };
28
29 static GSList *fallback_handlers;
30
31 /*
32  * read a value string from line with any of the following formats:
33  * \s*=\s*'string'
34  * \s*=\s*"string"
35  * \s*=\s*non_white_space_string
36  */
37 static char*
38 read_string (char *p, FILE *file)
39 {
40         char *endp;
41         char *startp;
42         while (*p && isspace (*p))
43                 ++p;
44         if (*p == 0)
45                 return NULL;
46         if (*p == '=')
47                 p++;
48         while (*p && isspace (*p))
49                 ++p;
50         if (*p == '\'' || *p == '"') {
51                 char t = *p;
52                 p++;
53                 startp = p;
54                 endp = strchr (p, t);
55                 /* FIXME: may need to read more from file... */
56                 if (!endp)
57                         return NULL;
58                 *endp = 0;
59                 return (char *) g_memdup (startp, (endp - startp) + 1);
60         }
61         if (*p == 0)
62                 return NULL;
63         startp = p;
64         while (*p && !isspace (*p))
65                 ++p;
66         *p = 0;
67         return (char *) g_memdup (startp, (p - startp) + 1);
68 }
69
70 /*
71  * parse a libtool .la file and return the path of the file to dlopen ()
72  * handling both the installed and uninstalled cases
73  */
74 static char*
75 get_dl_name_from_libtool (const char *libtool_file)
76 {
77         FILE* file;
78         char buf [512];
79         char *line, *dlname = NULL, *libdir = NULL, *installed = NULL;
80         if (!(file = fopen (libtool_file, "r")))
81                 return NULL;
82         while ((line = fgets (buf, 512, file))) {
83                 while (*line && isspace (*line))
84                         ++line;
85                 if (*line == '#' || *line == 0)
86                         continue;
87                 if (strncmp ("dlname", line, 6) == 0) {
88                         g_free (dlname);
89                         dlname = read_string (line + 6, file);
90                 } else if (strncmp ("libdir", line, 6) == 0) {
91                         g_free (libdir);
92                         libdir = read_string (line + 6, file);
93                 } else if (strncmp ("installed", line, 9) == 0) {
94                         g_free (installed);
95                         installed = read_string (line + 9, file);
96                 }
97         }
98         fclose (file);
99         line = NULL;
100         if (installed && strcmp (installed, "no") == 0) {
101                 char *dir = g_path_get_dirname (libtool_file);
102                 if (dlname)
103                         line = g_strconcat (dir, G_DIR_SEPARATOR_S ".libs" G_DIR_SEPARATOR_S, dlname, NULL);
104                 g_free (dir);
105         } else {
106                 if (libdir && dlname)
107                         line = g_strconcat (libdir, G_DIR_SEPARATOR_S, dlname, NULL);
108         }
109         g_free (dlname);
110         g_free (libdir);
111         g_free (installed);
112         return line;
113 }
114
115 /**
116  * mono_dl_open:
117  * @name: name of file containing shared module
118  * @flags: flags
119  * @error_msg: pointer for error message on failure
120  *
121  * Load the given file @name as a shared library or dynamically loadable
122  * module. @name can be NULL to indicate loading the currently executing
123  * binary image.
124  * @flags can have the MONO_DL_LOCAL bit set to avoid exporting symbols
125  * from the module to the shared namespace. The MONO_DL_LAZY bit can be set
126  * to lazily load the symbols instead of resolving everithing at load time.
127  * @error_msg points to a string where an error message will be stored in
128  * case of failure.   The error must be released with g_free.
129  *
130  * Returns: a MonoDl pointer on success, NULL on failure.
131  */
132 MonoDl*
133 mono_dl_open (const char *name, int flags, char **error_msg)
134 {
135         MonoDl *module;
136         void *lib;
137         MonoDlFallbackHandler *dl_fallback = NULL;
138         int lflags = mono_dl_convert_flags (flags);
139
140         if (error_msg)
141                 *error_msg = NULL;
142
143         module = (MonoDl *) malloc (sizeof (MonoDl));
144         if (!module) {
145                 if (error_msg)
146                         *error_msg = g_strdup ("Out of memory");
147                 return NULL;
148         }
149         module->main_module = name == NULL? TRUE: FALSE;
150
151         lib = mono_dl_open_file (name, lflags);
152
153         if (!lib) {
154                 GSList *node;
155                 for (node = fallback_handlers; node != NULL; node = node->next){
156                         MonoDlFallbackHandler *handler = (MonoDlFallbackHandler *) node->data;
157                         if (error_msg)
158                                 *error_msg = NULL;
159                         
160                         lib = handler->load_func (name, lflags, error_msg, handler->user_data);
161                         if (error_msg && *error_msg != NULL)
162                                 g_free (*error_msg);
163                         
164                         if (lib != NULL){
165                                 dl_fallback = handler;
166                                 break;
167                         }
168                 }
169         }
170         if (!lib && !dl_fallback) {
171                 char *lname;
172                 char *llname;
173                 const char *suff;
174                 const char *ext;
175                 /* This platform does not support dlopen */
176                 if (name == NULL) {
177                         free (module);
178                         return NULL;
179                 }
180                 
181                 suff = ".la";
182                 ext = strrchr (name, '.');
183                 if (ext && strcmp (ext, ".la") == 0)
184                         suff = "";
185                 lname = g_strconcat (name, suff, NULL);
186                 llname = get_dl_name_from_libtool (lname);
187                 g_free (lname);
188                 if (llname) {
189                         lib = mono_dl_open_file (llname, lflags);
190                         g_free (llname);
191                 }
192                 if (!lib) {
193                         if (error_msg) {
194                                 *error_msg = mono_dl_current_error_string ();
195                         }
196                         free (module);
197                         return NULL;
198                 }
199         }
200         module->handle = lib;
201         module->dl_fallback = dl_fallback;
202         return module;
203 }
204
205 /**
206  * mono_dl_symbol:
207  * @module: a MonoDl pointer
208  * @name: symbol name
209  * @symbol: pointer for the result value
210  *
211  * Load the address of symbol @name from the given @module.
212  * The address is stored in the pointer pointed to by @symbol.
213  *
214  * Returns: NULL on success, an error message on failure
215  */
216 char*
217 mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
218 {
219         void *sym;
220         char *err = NULL;
221
222         if (module->dl_fallback) {
223                 sym = module->dl_fallback->symbol_func (module->handle, name, &err, module->dl_fallback->user_data);
224         } else {
225 #if MONO_DL_NEED_USCORE
226                 {
227                         char *usname = malloc (strlen (name) + 2);
228                         *usname = '_';
229                         strcpy (usname + 1, name);
230                         sym = mono_dl_lookup_symbol (module, usname);
231                         free (usname);
232                 }
233 #else
234                 sym = mono_dl_lookup_symbol (module, name);
235 #endif
236         }
237
238         if (sym) {
239                 if (symbol)
240                         *symbol = sym;
241                 return NULL;
242         }
243         if (symbol)
244                 *symbol = NULL;
245         return (module->dl_fallback != NULL) ? err :  mono_dl_current_error_string ();
246 }
247
248 /**
249  * mono_dl_close:
250  * @module: a MonoDl pointer
251  *
252  * Unload the given module and free the module memory.
253  *
254  * Returns: 0 on success.
255  */
256 void
257 mono_dl_close (MonoDl *module)
258 {
259         MonoDlFallbackHandler *dl_fallback = module->dl_fallback;
260         
261         if (dl_fallback){
262                 if (dl_fallback->close_func != NULL)
263                         dl_fallback->close_func (module->handle, dl_fallback->user_data);
264         } else
265                 mono_dl_close_handle (module);
266         
267         free (module);
268 }
269
270 /**
271  * mono_dl_build_path:
272  * @directory: optional directory
273  * @name: base name of the library
274  * @iter: iterator token
275  *
276  * Given a directory name and the base name of a library, iterate
277  * over the possible file names of the library, taking into account
278  * the possible different suffixes and prefixes on the host platform.
279  *
280  * The returned file name must be freed by the caller.
281  * @iter must point to a NULL pointer the first time the function is called
282  * and then passed unchanged to the following calls.
283  * Returns: the filename or NULL at the end of the iteration
284  */
285 char*
286 mono_dl_build_path (const char *directory, const char *name, void **iter)
287 {
288         int idx;
289         const char *prefix;
290         const char *suffix;
291         gboolean first_call;
292         int prlen;
293         int suffixlen;
294         char *res;
295
296         if (!iter)
297                 return NULL;
298
299         /*
300           The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our
301           "bootstrap" phase in which we check the passed name verbatim and only if we fail to find
302           the dll thus named, we start appending suffixes, each time increasing idx twice (since now
303           the 0 value became special and we need to offset idx to a 0-based array index). This is
304           done to handle situations when mapped dll name is specified as libsomething.so.1 or
305           libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill
306           here.
307          */
308         idx = GPOINTER_TO_UINT (*iter);
309         if (idx == 0) {
310                 first_call = TRUE;
311                 suffix = "";
312                 suffixlen = 0;
313         } else {
314                 idx--;
315                 if (mono_dl_get_so_suffixes () [idx][0] == '\0')
316                         return NULL;
317                 first_call = FALSE;
318                 suffix = mono_dl_get_so_suffixes () [idx];
319                 suffixlen = strlen (suffix);
320         }
321
322         prlen = strlen (mono_dl_get_so_prefix ());
323         if (prlen && strncmp (name, mono_dl_get_so_prefix (), prlen) != 0)
324                 prefix = mono_dl_get_so_prefix ();
325         else
326                 prefix = "";
327
328         if (first_call || (suffixlen && strstr (name, suffix) == (name + strlen (name) - suffixlen)))
329                 suffix = "";
330
331         if (directory && *directory)
332                 res = g_strconcat (directory, G_DIR_SEPARATOR_S, prefix, name, suffix, NULL);
333         else
334                 res = g_strconcat (prefix, name, suffix, NULL);
335         ++idx;
336         if (!first_call)
337                 idx++;
338         *iter = GUINT_TO_POINTER (idx);
339         return res;
340 }
341
342 MonoDlFallbackHandler *
343 mono_dl_fallback_register (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data)
344 {
345         MonoDlFallbackHandler *handler;
346         
347         g_return_val_if_fail (load_func != NULL, NULL);
348         g_return_val_if_fail (symbol_func != NULL, NULL);
349
350         handler = g_new (MonoDlFallbackHandler, 1);
351         handler->load_func = load_func;
352         handler->symbol_func = symbol_func;
353         handler->close_func = close_func;
354         handler->user_data = user_data;
355
356         fallback_handlers = g_slist_prepend (fallback_handlers, handler);
357         
358         return handler;
359 }
360
361 void
362 mono_dl_fallback_unregister (MonoDlFallbackHandler *handler)
363 {
364         GSList *found;
365
366         found = g_slist_find (fallback_handlers, handler);
367         if (found == NULL)
368                 return;
369
370         g_slist_remove (fallback_handlers, handler);
371         g_free (handler);
372 }
373
374 static MonoDl*
375 try_load (const char *lib_name, char *dir, int flags, char **err)
376 {
377         gpointer iter;
378         MonoDl *runtime_lib;
379         char *path;
380         iter = NULL;
381         *err = NULL;
382         while ((path = mono_dl_build_path (dir, lib_name, &iter))) {
383                 g_free (*err);
384                 runtime_lib = mono_dl_open (path, flags, err);
385                 g_free (path);
386                 if (runtime_lib)
387                         return runtime_lib;
388         }
389         return NULL;
390 }
391
392 MonoDl*
393 mono_dl_open_runtime_lib (const char* lib_name, int flags, char **error_msg)
394 {
395         MonoDl *runtime_lib = NULL;
396         char buf [4096];
397         int binl;
398         *error_msg = NULL;
399
400         binl = mono_dl_get_executable_path (buf, sizeof (buf));
401
402         if (binl != -1) {
403                 char *base;
404                 char *resolvedname, *name;
405                 buf [binl] = 0;
406                 resolvedname = mono_path_resolve_symlinks (buf);
407                 base = g_path_get_dirname (resolvedname);
408                 name = g_strdup_printf ("%s/.libs", base);
409                 runtime_lib = try_load (lib_name, name, flags, error_msg);
410                 g_free (name);
411                 if (!runtime_lib) {
412                         char *newbase = g_path_get_dirname (base);
413                         name = g_strdup_printf ("%s/lib", newbase);
414                         runtime_lib = try_load (lib_name, name, flags, error_msg);
415                         g_free (name);
416                 }
417 #ifdef __MACH__
418                 if (!runtime_lib) {
419                         char *newbase = g_path_get_dirname (base);
420                         name = g_strdup_printf ("%s/Libraries", newbase);
421                         runtime_lib = try_load (lib_name, name, flags, error_msg);
422                         g_free (name);
423                 }
424 #endif
425                 g_free (base);
426                 g_free (resolvedname);
427         }
428         if (!runtime_lib)
429                 runtime_lib = try_load (lib_name, NULL, flags, error_msg);
430
431         return runtime_lib;
432 }