Use expression-bodied members in more places (dotnet/coreclr#26500)
[mono-project.git] / mono / utils / mono-dl.c
blob8023730f69a00e62a0439d54dc1b3c5842476591
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-compiler.h"
14 #include "mono/utils/mono-dl.h"
15 #include "mono/utils/mono-embed.h"
16 #include "mono/utils/mono-path.h"
17 #include "mono/utils/mono-threads-api.h"
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <ctype.h>
22 #include <string.h>
23 #include <glib.h>
25 struct MonoDlFallbackHandler {
26 MonoDlFallbackLoad load_func;
27 MonoDlFallbackSymbol symbol_func;
28 MonoDlFallbackClose close_func;
29 void *user_data;
32 static GSList *fallback_handlers;
34 #if defined (_AIX)
35 #include <ar.h>
36 #include <fcntl.h>
38 /**
39 * On AIX/PASE, a shared library can be contained inside of an ar format
40 * archive. Determine if the file is an ar archive or not.
42 static gboolean
43 is_library_ar_archive (char *path)
45 int lfd, readret;
46 char magic [SAIAMAG];
47 lfd = open (path, O_RDONLY);
49 /* don't assume it's an archive on error */
50 if (lfd == -1)
51 return FALSE;
53 readret = read (lfd, magic, SAIAMAG);
54 close (lfd);
55 /* check for equality with either version of header */
56 return readret == SAIAMAG &&
57 (memcmp (magic, AIAMAG, SAIAMAG) == 0 ||
58 memcmp (magic, AIAMAGBIG, SAIAMAG) == 0);
60 #endif
63 * read a value string from line with any of the following formats:
64 * \s*=\s*'string'
65 * \s*=\s*"string"
66 * \s*=\s*non_white_space_string
68 static char*
69 read_string (char *p, FILE *file)
71 char *endp;
72 char *startp;
73 while (*p && isspace (*p))
74 ++p;
75 if (*p == 0)
76 return NULL;
77 if (*p == '=')
78 p++;
79 while (*p && isspace (*p))
80 ++p;
81 if (*p == '\'' || *p == '"') {
82 char t = *p;
83 p++;
84 startp = p;
85 endp = strchr (p, t);
86 /* FIXME: may need to read more from file... */
87 if (!endp)
88 return NULL;
89 *endp = 0;
90 return (char *) g_memdup (startp, (endp - startp) + 1);
92 if (*p == 0)
93 return NULL;
94 startp = p;
95 while (*p && !isspace (*p))
96 ++p;
97 *p = 0;
98 return (char *) g_memdup (startp, (p - startp) + 1);
102 * parse a libtool .la file and return the path of the file to dlopen ()
103 * handling both the installed and uninstalled cases
105 static char*
106 get_dl_name_from_libtool (const char *libtool_file)
108 FILE* file;
109 char buf [512];
110 char *line, *dlname = NULL, *libdir = NULL, *installed = NULL;
111 if (!(file = fopen (libtool_file, "r")))
112 return NULL;
113 while ((line = fgets (buf, 512, file))) {
114 while (*line && isspace (*line))
115 ++line;
116 if (*line == '#' || *line == 0)
117 continue;
118 if (strncmp ("dlname", line, 6) == 0) {
119 g_free (dlname);
120 dlname = read_string (line + 6, file);
121 } else if (strncmp ("libdir", line, 6) == 0) {
122 g_free (libdir);
123 libdir = read_string (line + 6, file);
124 } else if (strncmp ("installed", line, 9) == 0) {
125 g_free (installed);
126 installed = read_string (line + 9, file);
129 fclose (file);
130 line = NULL;
131 if (installed && strcmp (installed, "no") == 0) {
132 char *dir = g_path_get_dirname (libtool_file);
133 if (dlname)
134 line = g_strconcat (dir, G_DIR_SEPARATOR_S ".libs" G_DIR_SEPARATOR_S, dlname, (const char*)NULL);
135 g_free (dir);
136 } else {
137 if (libdir && dlname)
138 line = g_strconcat (libdir, G_DIR_SEPARATOR_S, dlname, (const char*)NULL);
140 g_free (dlname);
141 g_free (libdir);
142 g_free (installed);
143 return line;
147 * mono_dl_open:
148 * \param name name of file containing shared module
149 * \param flags flags
150 * \param error_msg pointer for error message on failure
152 * Load the given file \p name as a shared library or dynamically loadable
153 * module. \p name can be NULL to indicate loading the currently executing
154 * binary image.
155 * \p flags can have the \c MONO_DL_LOCAL bit set to avoid exporting symbols
156 * from the module to the shared namespace. The \c MONO_DL_LAZY bit can be set
157 * to lazily load the symbols instead of resolving everithing at load time.
158 * \p error_msg points to a string where an error message will be stored in
159 * case of failure. The error must be released with \c g_free.
160 * \returns a \c MonoDl pointer on success, NULL on failure.
162 MonoDl*
163 mono_dl_open (const char *name, int flags, char **error_msg)
165 MonoDl *module;
166 void *lib;
167 MonoDlFallbackHandler *dl_fallback = NULL;
168 int lflags = mono_dl_convert_flags (flags);
170 if (error_msg)
171 *error_msg = NULL;
173 module = (MonoDl *) g_malloc (sizeof (MonoDl));
174 if (!module) {
175 if (error_msg)
176 *error_msg = g_strdup ("Out of memory");
177 return NULL;
179 module->main_module = name == NULL? TRUE: FALSE;
181 // No GC safe transition because this is called early in main.c
182 lib = mono_dl_open_file (name, lflags);
184 if (!lib) {
185 GSList *node;
186 for (node = fallback_handlers; node != NULL; node = node->next){
187 MonoDlFallbackHandler *handler = (MonoDlFallbackHandler *) node->data;
188 if (error_msg)
189 *error_msg = NULL;
191 lib = handler->load_func (name, lflags, error_msg, handler->user_data);
192 if (error_msg && *error_msg != NULL)
193 g_free (*error_msg);
195 if (lib != NULL){
196 dl_fallback = handler;
197 break;
201 if (!lib && !dl_fallback) {
202 char *lname;
203 char *llname;
204 const char *suff;
205 const char *ext;
206 /* This platform does not support dlopen */
207 if (name == NULL) {
208 g_free (module);
209 return NULL;
212 suff = ".la";
213 ext = strrchr (name, '.');
214 if (ext && strcmp (ext, ".la") == 0)
215 suff = "";
216 lname = g_strconcat (name, suff, (const char*)NULL);
217 llname = get_dl_name_from_libtool (lname);
218 g_free (lname);
219 if (llname) {
220 lib = mono_dl_open_file (llname, lflags);
221 #if defined (_AIX)
223 * HACK: deal with AIX archive members because libtool
224 * underspecifies when using --with-aix-soname=svr4 -
225 * without this check, Mono can't find System.Native
226 * at build time.
227 * XXX: Does this also need to be in other places?
229 if (!lib && is_library_ar_archive (llname)) {
230 /* try common suffix */
231 char *llaixname;
232 llaixname = g_strconcat (llname, "(shr_64.o)", (const char*)NULL);
233 lib = mono_dl_open_file (llaixname, lflags);
234 /* XXX: try another suffix like (shr.o)? */
235 g_free (llaixname);
237 #endif
238 g_free (llname);
240 if (!lib) {
241 if (error_msg) {
242 *error_msg = mono_dl_current_error_string ();
244 g_free (module);
245 return NULL;
248 module->handle = lib;
249 module->dl_fallback = dl_fallback;
250 return module;
254 * mono_dl_symbol:
255 * \param module a MonoDl pointer
256 * \param name symbol name
257 * \param symbol pointer for the result value
258 * Load the address of symbol \p name from the given \p module.
259 * The address is stored in the pointer pointed to by \p symbol.
260 * \returns NULL on success, an error message on failure
262 char*
263 mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
265 void *sym;
266 char *err = NULL;
268 if (module->dl_fallback) {
269 sym = module->dl_fallback->symbol_func (module->handle, name, &err, module->dl_fallback->user_data);
270 } else {
271 #if MONO_DL_NEED_USCORE
273 const size_t length = strlen (name);
274 char *usname = g_new (char, length + 2);
275 *usname = '_';
276 memcpy (usname + 1, name, length + 1);
277 sym = mono_dl_lookup_symbol (module, usname);
278 g_free (usname);
280 #else
281 sym = mono_dl_lookup_symbol (module, name);
282 #endif
285 if (sym) {
286 if (symbol)
287 *symbol = sym;
288 return NULL;
290 if (symbol)
291 *symbol = NULL;
292 return (module->dl_fallback != NULL) ? err : mono_dl_current_error_string ();
296 * mono_dl_close:
297 * \param module a \c MonoDl pointer
298 * Unload the given module and free the module memory.
299 * \returns \c 0 on success.
301 void
302 mono_dl_close (MonoDl *module)
304 MonoDlFallbackHandler *dl_fallback = module->dl_fallback;
306 if (dl_fallback){
307 if (dl_fallback->close_func != NULL)
308 dl_fallback->close_func (module->handle, dl_fallback->user_data);
309 } else
310 mono_dl_close_handle (module);
312 g_free (module);
316 * mono_dl_build_path:
317 * \param directory optional directory
318 * \param name base name of the library
319 * \param iter iterator token
320 * Given a directory name and the base name of a library, iterate
321 * over the possible file names of the library, taking into account
322 * the possible different suffixes and prefixes on the host platform.
324 * The returned file name must be freed by the caller.
325 * \p iter must point to a NULL pointer the first time the function is called
326 * and then passed unchanged to the following calls.
327 * \returns the filename or NULL at the end of the iteration
329 char*
330 mono_dl_build_path (const char *directory, const char *name, void **iter)
332 int idx;
333 const char *prefix;
334 const char *suffix;
335 gboolean need_prefix = TRUE, need_suffix = TRUE;
336 int prlen;
337 int suffixlen;
338 char *res;
339 int iteration;
341 if (!iter)
342 return NULL;
345 The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our
346 "bootstrap" phase in which we check the passed name verbatim and only if we fail to find
347 the dll thus named, we start appending suffixes, each time increasing idx twice (since now
348 the 0 value became special and we need to offset idx to a 0-based array index). This is
349 done to handle situations when mapped dll name is specified as libsomething.so.1 or
350 libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill
351 here.
353 iteration = GPOINTER_TO_UINT (*iter);
354 idx = iteration;
355 if (idx == 0) {
356 /* Name */
357 need_prefix = FALSE;
358 need_suffix = FALSE;
359 suffix = "";
360 } else if (idx == 1) {
361 #ifdef ENABLE_NETCORE
362 /* netcore system libs have a suffix but no prefix */
363 need_prefix = FALSE;
364 need_suffix = TRUE;
365 suffix = mono_dl_get_so_suffixes () [0];
366 suffixlen = strlen (suffix);
367 #else
368 suffix = mono_dl_get_so_suffixes () [idx - 1];
369 if (suffix [0] == '\0')
370 return NULL;
371 #endif
372 } else {
373 /* Prefix.Name.suffix */
374 suffix = mono_dl_get_so_suffixes () [idx - 2];
375 if (suffix [0] == '\0')
376 return NULL;
379 if (need_prefix) {
380 prlen = strlen (mono_dl_get_so_prefix ());
381 if (prlen && strncmp (name, mono_dl_get_so_prefix (), prlen) != 0)
382 prefix = mono_dl_get_so_prefix ();
383 else
384 prefix = "";
385 } else {
386 prefix = "";
389 suffixlen = strlen (suffix);
390 if (need_suffix && (suffixlen && strstr (name, suffix) == (name + strlen (name) - suffixlen)))
391 suffix = "";
393 if (directory && *directory)
394 res = g_strconcat (directory, G_DIR_SEPARATOR_S, prefix, name, suffix, (const char*)NULL);
395 else
396 res = g_strconcat (prefix, name, suffix, (const char*)NULL);
397 ++iteration;
398 *iter = GUINT_TO_POINTER (iteration);
399 return res;
402 MonoDlFallbackHandler *
403 mono_dl_fallback_register (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data)
405 MonoDlFallbackHandler *handler = NULL;
406 if (load_func == NULL || symbol_func == NULL)
407 goto leave;
409 handler = g_new (MonoDlFallbackHandler, 1);
410 handler->load_func = load_func;
411 handler->symbol_func = symbol_func;
412 handler->close_func = close_func;
413 handler->user_data = user_data;
415 fallback_handlers = g_slist_prepend (fallback_handlers, handler);
417 leave:
418 return handler;
421 void
422 mono_dl_fallback_unregister (MonoDlFallbackHandler *handler)
424 GSList *found;
426 found = g_slist_find (fallback_handlers, handler);
427 if (found == NULL)
428 return;
430 g_slist_remove (fallback_handlers, handler);
431 g_free (handler);
434 static MonoDl*
435 try_load (const char *lib_name, char *dir, int flags, char **err)
437 gpointer iter;
438 MonoDl *runtime_lib;
439 char *path;
440 iter = NULL;
441 *err = NULL;
442 while ((path = mono_dl_build_path (dir, lib_name, &iter))) {
443 g_free (*err);
444 runtime_lib = mono_dl_open (path, flags, err);
445 g_free (path);
446 if (runtime_lib)
447 return runtime_lib;
449 return NULL;
452 MonoDl*
453 mono_dl_open_runtime_lib (const char* lib_name, int flags, char **error_msg)
455 MonoDl *runtime_lib = NULL;
456 char buf [4096];
457 int binl;
458 *error_msg = NULL;
460 binl = mono_dl_get_executable_path (buf, sizeof (buf));
462 if (binl != -1) {
463 char *base;
464 char *resolvedname, *name;
465 char *baseparent = NULL;
466 buf [binl] = 0;
467 resolvedname = mono_path_resolve_symlinks (buf);
468 base = g_path_get_dirname (resolvedname);
469 name = g_strdup_printf ("%s/.libs", base);
470 runtime_lib = try_load (lib_name, name, flags, error_msg);
471 g_free (name);
472 if (!runtime_lib)
473 baseparent = g_path_get_dirname (base);
474 if (!runtime_lib) {
475 name = g_strdup_printf ("%s/lib", baseparent);
476 runtime_lib = try_load (lib_name, name, flags, error_msg);
477 g_free (name);
479 #ifdef __MACH__
480 if (!runtime_lib) {
481 name = g_strdup_printf ("%s/Libraries", baseparent);
482 runtime_lib = try_load (lib_name, name, flags, error_msg);
483 g_free (name);
485 #endif
486 if (!runtime_lib) {
487 name = g_strdup_printf ("%s/profiler/.libs", baseparent);
488 runtime_lib = try_load (lib_name, name, flags, error_msg);
489 g_free (name);
491 g_free (base);
492 g_free (resolvedname);
493 g_free (baseparent);
495 if (!runtime_lib)
496 runtime_lib = try_load (lib_name, NULL, flags, error_msg);
498 return runtime_lib;