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