[reflection] Managed version of RuntimeType.GetFields_internal
[mono-project.git] / mono / utils / mono-dl.c
blob43116130309eec7195e24a87f8d39e10e544dbce
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 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
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"
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <ctype.h>
19 #include <string.h>
20 #include <glib.h>
22 struct MonoDlFallbackHandler {
23 MonoDlFallbackLoad load_func;
24 MonoDlFallbackSymbol symbol_func;
25 MonoDlFallbackClose close_func;
26 void *user_data;
29 static GSList *fallback_handlers;
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
37 static char*
38 read_string (char *p, FILE *file)
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);
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);
71 * parse a libtool .la file and return the path of the file to dlopen ()
72 * handling both the installed and uninstalled cases
74 static char*
75 get_dl_name_from_libtool (const char *libtool_file)
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);
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);
109 g_free (dlname);
110 g_free (libdir);
111 g_free (installed);
112 return line;
116 * mono_dl_open:
117 * @name: name of file containing shared module
118 * @flags: flags
119 * @error_msg: pointer for error message on failure
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.
130 * Returns: a MonoDl pointer on success, NULL on failure.
132 MonoDl*
133 mono_dl_open (const char *name, int flags, char **error_msg)
135 MonoDl *module;
136 void *lib;
137 MonoDlFallbackHandler *dl_fallback = NULL;
138 int lflags = mono_dl_convert_flags (flags);
140 if (error_msg)
141 *error_msg = NULL;
143 module = (MonoDl *) malloc (sizeof (MonoDl));
144 if (!module) {
145 if (error_msg)
146 *error_msg = g_strdup ("Out of memory");
147 return NULL;
149 module->main_module = name == NULL? TRUE: FALSE;
151 lib = mono_dl_open_file (name, lflags);
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;
160 lib = handler->load_func (name, lflags, error_msg, handler->user_data);
161 if (error_msg && *error_msg != NULL)
162 g_free (*error_msg);
164 if (lib != NULL){
165 dl_fallback = handler;
166 break;
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;
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);
192 if (!lib) {
193 if (error_msg) {
194 *error_msg = mono_dl_current_error_string ();
196 free (module);
197 return NULL;
200 module->handle = lib;
201 module->dl_fallback = dl_fallback;
202 return module;
206 * mono_dl_symbol:
207 * @module: a MonoDl pointer
208 * @name: symbol name
209 * @symbol: pointer for the result value
211 * Load the address of symbol @name from the given @module.
212 * The address is stored in the pointer pointed to by @symbol.
214 * Returns: NULL on success, an error message on failure
216 char*
217 mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
219 void *sym;
220 char *err = NULL;
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
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);
233 #else
234 sym = mono_dl_lookup_symbol (module, name);
235 #endif
238 if (sym) {
239 if (symbol)
240 *symbol = sym;
241 return NULL;
243 if (symbol)
244 *symbol = NULL;
245 return (module->dl_fallback != NULL) ? err : mono_dl_current_error_string ();
249 * mono_dl_close:
250 * @module: a MonoDl pointer
252 * Unload the given module and free the module memory.
254 * Returns: 0 on success.
256 void
257 mono_dl_close (MonoDl *module)
259 MonoDlFallbackHandler *dl_fallback = module->dl_fallback;
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);
267 free (module);
271 * mono_dl_build_path:
272 * @directory: optional directory
273 * @name: base name of the library
274 * @iter: iterator token
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.
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
285 char*
286 mono_dl_build_path (const char *directory, const char *name, void **iter)
288 int idx;
289 const char *prefix;
290 const char *suffix;
291 gboolean first_call;
292 int prlen;
293 int suffixlen;
294 char *res;
296 if (!iter)
297 return NULL;
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.
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);
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 = "";
328 if (first_call || (suffixlen && strstr (name, suffix) == (name + strlen (name) - suffixlen)))
329 suffix = "";
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;
342 MonoDlFallbackHandler *
343 mono_dl_fallback_register (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data)
345 MonoDlFallbackHandler *handler;
347 g_return_val_if_fail (load_func != NULL, NULL);
348 g_return_val_if_fail (symbol_func != NULL, NULL);
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;
356 fallback_handlers = g_slist_prepend (fallback_handlers, handler);
358 return handler;
361 void
362 mono_dl_fallback_unregister (MonoDlFallbackHandler *handler)
364 GSList *found;
366 found = g_slist_find (fallback_handlers, handler);
367 if (found == NULL)
368 return;
370 g_slist_remove (fallback_handlers, handler);
371 g_free (handler);
374 static MonoDl*
375 try_load (const char *lib_name, char *dir, int flags, char **err)
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;
389 return NULL;
392 MonoDl*
393 mono_dl_open_runtime_lib (const char* lib_name, int flags, char **error_msg)
395 MonoDl *runtime_lib = NULL;
396 char buf [4096];
397 int binl;
398 *error_msg = NULL;
400 binl = mono_dl_get_executable_path (buf, sizeof (buf));
402 if (binl != -1) {
403 char *base;
404 char *resolvedname, *name;
405 char *baseparent = NULL;
406 buf [binl] = 0;
407 resolvedname = mono_path_resolve_symlinks (buf);
408 base = g_path_get_dirname (resolvedname);
409 name = g_strdup_printf ("%s/.libs", base);
410 runtime_lib = try_load (lib_name, name, flags, error_msg);
411 g_free (name);
412 if (!runtime_lib)
413 baseparent = g_path_get_dirname (base);
414 if (!runtime_lib) {
415 name = g_strdup_printf ("%s/lib", baseparent);
416 runtime_lib = try_load (lib_name, name, flags, error_msg);
417 g_free (name);
419 #ifdef __MACH__
420 if (!runtime_lib) {
421 name = g_strdup_printf ("%s/Libraries", baseparent);
422 runtime_lib = try_load (lib_name, name, flags, error_msg);
423 g_free (name);
425 #endif
426 if (!runtime_lib) {
427 name = g_strdup_printf ("%s/profiler/.libs", baseparent);
428 runtime_lib = try_load (lib_name, name, flags, error_msg);
429 g_free (name);
431 g_free (base);
432 g_free (resolvedname);
433 g_free (baseparent);
435 if (!runtime_lib)
436 runtime_lib = try_load (lib_name, NULL, flags, error_msg);
438 return runtime_lib;