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