[2020-02][debugger] Bump protocol for multi threaded single step implementation ...
[mono-project.git] / mono / utils / mono-dl.c
blob8b4aedd179ac91ee91651c7cfa770d745c2933ce
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 // Contains LIBC_SO definition
26 #ifdef HAVE_GNU_LIB_NAMES_H
27 #include <gnu/lib-names.h>
28 #endif
30 struct MonoDlFallbackHandler {
31 MonoDlFallbackLoad load_func;
32 MonoDlFallbackSymbol symbol_func;
33 MonoDlFallbackClose close_func;
34 void *user_data;
37 static GSList *fallback_handlers;
39 #if defined (_AIX)
40 #include <ar.h>
41 #include <fcntl.h>
43 /**
44 * On AIX/PASE, a shared library can be contained inside of an ar format
45 * archive. Determine if the file is an ar archive or not.
47 static gboolean
48 is_library_ar_archive (char *path)
50 int lfd, readret;
51 char magic [SAIAMAG];
52 lfd = open (path, O_RDONLY);
54 /* don't assume it's an archive on error */
55 if (lfd == -1)
56 return FALSE;
58 readret = read (lfd, magic, SAIAMAG);
59 close (lfd);
60 /* check for equality with either version of header */
61 return readret == SAIAMAG &&
62 (memcmp (magic, AIAMAG, SAIAMAG) == 0 ||
63 memcmp (magic, AIAMAGBIG, SAIAMAG) == 0);
65 #endif
68 * read a value string from line with any of the following formats:
69 * \s*=\s*'string'
70 * \s*=\s*"string"
71 * \s*=\s*non_white_space_string
73 static char*
74 read_string (char *p, FILE *file)
76 char *endp;
77 char *startp;
78 while (*p && isspace (*p))
79 ++p;
80 if (*p == 0)
81 return NULL;
82 if (*p == '=')
83 p++;
84 while (*p && isspace (*p))
85 ++p;
86 if (*p == '\'' || *p == '"') {
87 char t = *p;
88 p++;
89 startp = p;
90 endp = strchr (p, t);
91 /* FIXME: may need to read more from file... */
92 if (!endp)
93 return NULL;
94 *endp = 0;
95 return (char *) g_memdup (startp, (endp - startp) + 1);
97 if (*p == 0)
98 return NULL;
99 startp = p;
100 while (*p && !isspace (*p))
101 ++p;
102 *p = 0;
103 return (char *) g_memdup (startp, (p - startp) + 1);
107 * parse a libtool .la file and return the path of the file to dlopen ()
108 * handling both the installed and uninstalled cases
110 static char*
111 get_dl_name_from_libtool (const char *libtool_file)
113 FILE* file;
114 char buf [512];
115 char *line, *dlname = NULL, *libdir = NULL, *installed = NULL;
116 if (!(file = fopen (libtool_file, "r")))
117 return NULL;
118 while ((line = fgets (buf, 512, file))) {
119 while (*line && isspace (*line))
120 ++line;
121 if (*line == '#' || *line == 0)
122 continue;
123 if (strncmp ("dlname", line, 6) == 0) {
124 g_free (dlname);
125 dlname = read_string (line + 6, file);
126 } else if (strncmp ("libdir", line, 6) == 0) {
127 g_free (libdir);
128 libdir = read_string (line + 6, file);
129 } else if (strncmp ("installed", line, 9) == 0) {
130 g_free (installed);
131 installed = read_string (line + 9, file);
134 fclose (file);
135 line = NULL;
136 if (installed && strcmp (installed, "no") == 0) {
137 char *dir = g_path_get_dirname (libtool_file);
138 if (dlname)
139 line = g_strconcat (dir, G_DIR_SEPARATOR_S ".libs" G_DIR_SEPARATOR_S, dlname, (const char*)NULL);
140 g_free (dir);
141 } else {
142 if (libdir && dlname)
143 line = g_strconcat (libdir, G_DIR_SEPARATOR_S, dlname, (const char*)NULL);
145 g_free (dlname);
146 g_free (libdir);
147 g_free (installed);
148 return line;
151 #ifdef ENABLE_NETCORE
152 static const char *
153 fix_libc_name (const char *name)
155 if (name != NULL && strcmp (name, "libc") == 0) {
156 // Taken from CoreCLR: https://github.com/dotnet/coreclr/blob/6b0dab793260d36e35d66c82678c63046828d01b/src/pal/src/loader/module.cpp#L568-L576
157 #if defined (HOST_DARWIN)
158 return "/usr/lib/libc.dylib";
159 #elif defined (__FreeBSD__)
160 return "libc.so.7";
161 #elif defined (LIBC_SO)
162 return LIBC_SO;
163 #else
164 return "libc.so";
165 #endif
167 return name;
169 #endif
172 * mono_dl_open:
173 * \param name name of file containing shared module
174 * \param flags flags
175 * \param error_msg pointer for error message on failure
177 * Load the given file \p name as a shared library or dynamically loadable
178 * module. \p name can be NULL to indicate loading the currently executing
179 * binary image.
180 * \p flags can have the \c MONO_DL_LOCAL bit set to avoid exporting symbols
181 * from the module to the shared namespace. The \c MONO_DL_LAZY bit can be set
182 * to lazily load the symbols instead of resolving everything at load time.
183 * \p error_msg points to a string where an error message will be stored in
184 * case of failure. The error must be released with \c g_free.
185 * \returns a \c MonoDl pointer on success, NULL on failure.
187 MonoDl*
188 mono_dl_open (const char *name, int flags, char **error_msg)
190 MonoDl *module;
191 void *lib;
192 MonoDlFallbackHandler *dl_fallback = NULL;
193 int lflags = mono_dl_convert_flags (flags);
194 char *found_name;
196 if (error_msg)
197 *error_msg = NULL;
199 module = (MonoDl *) g_malloc (sizeof (MonoDl));
200 if (!module) {
201 if (error_msg)
202 *error_msg = g_strdup ("Out of memory");
203 return NULL;
205 module->main_module = name == NULL? TRUE: FALSE;
207 #ifdef ENABLE_NETCORE
208 name = fix_libc_name (name);
209 #endif
211 // No GC safe transition because this is called early in main.c
212 lib = mono_dl_open_file (name, lflags);
213 if (lib)
214 found_name = g_strdup (name);
216 if (!lib) {
217 GSList *node;
218 for (node = fallback_handlers; node != NULL; node = node->next){
219 MonoDlFallbackHandler *handler = (MonoDlFallbackHandler *) node->data;
220 if (error_msg)
221 *error_msg = NULL;
223 lib = handler->load_func (name, lflags, error_msg, handler->user_data);
224 if (error_msg && *error_msg != NULL)
225 g_free (*error_msg);
227 if (lib != NULL){
228 dl_fallback = handler;
229 found_name = g_strdup (name);
230 break;
234 if (!lib && !dl_fallback) {
235 char *lname;
236 char *llname;
237 const char *suff;
238 const char *ext;
239 /* This platform does not support dlopen */
240 if (name == NULL) {
241 g_free (module);
242 return NULL;
245 suff = ".la";
246 ext = strrchr (name, '.');
247 if (ext && strcmp (ext, ".la") == 0)
248 suff = "";
249 lname = g_strconcat (name, suff, (const char*)NULL);
250 llname = get_dl_name_from_libtool (lname);
251 g_free (lname);
252 if (llname) {
253 lib = mono_dl_open_file (llname, lflags);
254 if (lib)
255 found_name = g_strdup (llname);
256 #if defined (_AIX)
258 * HACK: deal with AIX archive members because libtool
259 * underspecifies when using --with-aix-soname=svr4 -
260 * without this check, Mono can't find System.Native
261 * at build time.
262 * XXX: Does this also need to be in other places?
264 if (!lib && is_library_ar_archive (llname)) {
265 /* try common suffix */
266 char *llaixname;
267 llaixname = g_strconcat (llname, "(shr_64.o)", (const char*)NULL);
268 lib = mono_dl_open_file (llaixname, lflags);
269 if (lib)
270 found_name = g_strdup (llaixname);
271 /* XXX: try another suffix like (shr.o)? */
272 g_free (llaixname);
274 #endif
275 g_free (llname);
277 if (!lib) {
278 if (error_msg) {
279 *error_msg = mono_dl_current_error_string ();
281 g_free (module);
282 return NULL;
285 mono_refcount_init (module, NULL);
286 module->handle = lib;
287 module->dl_fallback = dl_fallback;
288 module->full_name = found_name;
289 return module;
293 * mono_dl_symbol:
294 * \param module a MonoDl pointer
295 * \param name symbol name
296 * \param symbol pointer for the result value
297 * Load the address of symbol \p name from the given \p module.
298 * The address is stored in the pointer pointed to by \p symbol.
299 * \returns NULL on success, an error message on failure
301 char*
302 mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
304 void *sym;
305 char *err = NULL;
307 if (module->dl_fallback) {
308 sym = module->dl_fallback->symbol_func (module->handle, name, &err, module->dl_fallback->user_data);
309 } else {
310 #if MONO_DL_NEED_USCORE
312 const size_t length = strlen (name);
313 char *usname = g_new (char, length + 2);
314 *usname = '_';
315 memcpy (usname + 1, name, length + 1);
316 sym = mono_dl_lookup_symbol (module, usname);
317 g_free (usname);
319 #else
320 sym = mono_dl_lookup_symbol (module, name);
321 #endif
324 if (sym) {
325 if (symbol)
326 *symbol = sym;
327 return NULL;
329 if (symbol)
330 *symbol = NULL;
331 return (module->dl_fallback != NULL) ? err : mono_dl_current_error_string ();
335 * mono_dl_close:
336 * \param module a \c MonoDl pointer
337 * Unload the given module and free the module memory.
338 * \returns \c 0 on success.
340 void
341 mono_dl_close (MonoDl *module)
343 MonoDlFallbackHandler *dl_fallback = module->dl_fallback;
345 if (dl_fallback){
346 if (dl_fallback->close_func != NULL)
347 dl_fallback->close_func (module->handle, dl_fallback->user_data);
348 } else
349 mono_dl_close_handle (module);
351 g_free (module->full_name);
352 g_free (module);
356 * mono_dl_build_path:
357 * \param directory optional directory
358 * \param name base name of the library
359 * \param iter iterator token
360 * Given a directory name and the base name of a library, iterate
361 * over the possible file names of the library, taking into account
362 * the possible different suffixes and prefixes on the host platform.
364 * The returned file name must be freed by the caller.
365 * \p iter must point to a NULL pointer the first time the function is called
366 * and then passed unchanged to the following calls.
367 * \returns the filename or NULL at the end of the iteration
369 char*
370 mono_dl_build_path (const char *directory, const char *name, void **iter)
372 int idx;
373 const char *prefix;
374 const char *suffix;
375 gboolean need_prefix = TRUE, need_suffix = TRUE;
376 int prlen;
377 int suffixlen;
378 char *res;
379 int iteration;
381 if (!iter)
382 return NULL;
385 The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our
386 "bootstrap" phase in which we check the passed name verbatim and only if we fail to find
387 the dll thus named, we start appending suffixes, each time increasing idx twice (since now
388 the 0 value became special and we need to offset idx to a 0-based array index). This is
389 done to handle situations when mapped dll name is specified as libsomething.so.1 or
390 libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill
391 here.
393 iteration = GPOINTER_TO_UINT (*iter);
394 idx = iteration;
395 if (idx == 0) {
396 /* Name */
397 need_prefix = FALSE;
398 need_suffix = FALSE;
399 suffix = "";
400 } else if (idx == 1) {
401 #ifdef ENABLE_NETCORE
402 /* netcore system libs have a suffix but no prefix */
403 need_prefix = FALSE;
404 need_suffix = TRUE;
405 suffix = mono_dl_get_so_suffixes () [0];
406 suffixlen = strlen (suffix);
407 #else
408 suffix = mono_dl_get_so_suffixes () [idx - 1];
409 if (suffix [0] == '\0')
410 return NULL;
411 #endif
412 } else {
413 /* Prefix.Name.suffix */
414 suffix = mono_dl_get_so_suffixes () [idx - 2];
415 if (suffix [0] == '\0')
416 return NULL;
419 if (need_prefix) {
420 prlen = strlen (mono_dl_get_so_prefix ());
421 if (prlen && strncmp (name, mono_dl_get_so_prefix (), prlen) != 0)
422 prefix = mono_dl_get_so_prefix ();
423 else
424 prefix = "";
425 } else {
426 prefix = "";
429 suffixlen = strlen (suffix);
430 if (need_suffix && (suffixlen && strstr (name, suffix) == (name + strlen (name) - suffixlen)))
431 suffix = "";
433 if (directory && *directory)
434 res = g_strconcat (directory, G_DIR_SEPARATOR_S, prefix, name, suffix, (const char*)NULL);
435 else
436 res = g_strconcat (prefix, name, suffix, (const char*)NULL);
437 ++iteration;
438 *iter = GUINT_TO_POINTER (iteration);
439 return res;
442 MonoDlFallbackHandler *
443 mono_dl_fallback_register (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data)
445 MonoDlFallbackHandler *handler = NULL;
446 if (load_func == NULL || symbol_func == NULL)
447 goto leave;
449 handler = g_new (MonoDlFallbackHandler, 1);
450 handler->load_func = load_func;
451 handler->symbol_func = symbol_func;
452 handler->close_func = close_func;
453 handler->user_data = user_data;
455 fallback_handlers = g_slist_prepend (fallback_handlers, handler);
457 leave:
458 return handler;
461 void
462 mono_dl_fallback_unregister (MonoDlFallbackHandler *handler)
464 GSList *found;
466 found = g_slist_find (fallback_handlers, handler);
467 if (found == NULL)
468 return;
470 g_slist_remove (fallback_handlers, handler);
471 g_free (handler);
474 static MonoDl*
475 try_load (const char *lib_name, char *dir, int flags, char **err)
477 gpointer iter;
478 MonoDl *runtime_lib;
479 char *path;
480 iter = NULL;
481 *err = NULL;
482 while ((path = mono_dl_build_path (dir, lib_name, &iter))) {
483 g_free (*err);
484 runtime_lib = mono_dl_open (path, flags, err);
485 g_free (path);
486 if (runtime_lib)
487 return runtime_lib;
489 return NULL;
492 MonoDl*
493 mono_dl_open_runtime_lib (const char* lib_name, int flags, char **error_msg)
495 MonoDl *runtime_lib = NULL;
496 char buf [4096];
497 int binl;
498 *error_msg = NULL;
500 binl = mono_dl_get_executable_path (buf, sizeof (buf));
502 if (binl != -1) {
503 char *base;
504 char *resolvedname, *name;
505 char *baseparent = NULL;
506 buf [binl] = 0;
507 resolvedname = mono_path_resolve_symlinks (buf);
508 base = g_path_get_dirname (resolvedname);
509 name = g_strdup_printf ("%s/.libs", base);
510 runtime_lib = try_load (lib_name, name, flags, error_msg);
511 g_free (name);
512 if (!runtime_lib)
513 baseparent = g_path_get_dirname (base);
514 if (!runtime_lib) {
515 name = g_strdup_printf ("%s/lib", baseparent);
516 runtime_lib = try_load (lib_name, name, flags, error_msg);
517 g_free (name);
519 #ifdef __MACH__
520 if (!runtime_lib) {
521 name = g_strdup_printf ("%s/Libraries", baseparent);
522 runtime_lib = try_load (lib_name, name, flags, error_msg);
523 g_free (name);
525 #endif
526 if (!runtime_lib) {
527 name = g_strdup_printf ("%s/profiler/.libs", baseparent);
528 runtime_lib = try_load (lib_name, name, flags, error_msg);
529 g_free (name);
531 g_free (base);
532 g_free (resolvedname);
533 g_free (baseparent);
535 if (!runtime_lib)
536 runtime_lib = try_load (lib_name, NULL, flags, error_msg);
538 return runtime_lib;