1 /* unix_dl.c -- Dynamic loading of C modules
2    Copyright (C) 1998 John Harper <john@dcs.warwick.ac.uk>
3    $Id$
4 
5    This file is part of Jade.
6 
7    Jade is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2, or (at your option)
10    any later version.
11 
12    Jade is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with Jade; see the file COPYING.	If not, write to
19    the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
20 
21 #define _GNU_SOURCE
22 
23 /* AIX requires this to be the first thing in the file.  */
24 #include <config.h>
25 #ifdef __GNUC__
26 # define alloca __builtin_alloca
27 #else
28 # if HAVE_ALLOCA_H
29 #  include <alloca.h>
30 # else
31 #  ifdef _AIX
32  #pragma alloca
33 #  else
34 #   ifndef alloca /* predefined by HP cc +Olibcalls */
35 char *alloca ();
36 #   endif
37 #  endif
38 # endif
39 #endif
40 
41 #include "repint.h"
42 #include <assert.h>
43 #include <string.h>
44 
45 /* we define some extensions to the libtool .la file. As well as using
46    the dlname entry to find the .so file to open, we also look for:
47 
48      rep_open_globally=[yes|no]
49 
50 	whether or not to open with RTLD_GLOBAL
51 
52      rep_requires='FEATURES...'
53 
54 	FEATURES is space separated list of feature symbols.
55 	Each of which must be provided by a dl object. */
56 
57 #ifdef HAVE_DYNAMIC_LOADING
58 
59 #if defined (HAVE_DLFCN_H)
60 # include <dlfcn.h>
61 # if ! defined (RTLD_LAZY)
62 #  if defined (DL_LAZY)
63 #   define RTLD_LAZY DL_LAZY
64 #  else
65     /* from gmodule-dl.c ``The Perl sources say, RTLD_LAZY needs to be
66        defined as (1), at least for Solaris 1.'' */
67 #   define RTLD_LAZY 1
68 #  endif
69 # endif
70 # if ! defined (RTLD_GLOBAL)
71 #  if defined (DL_GLOBAL)
72 #   define RTLD_GLOBAL DL_GLOBAL
73 #  else
74 #   define RTLD_GLOBAL 0
75 #  endif
76 # endif
77 # if ! defined (RTLD_LOCAL)
78 #  if defined (DL_LOCAL)
79 #   define RTLD_LOCAL DL_LOCAL
80 #  else
81 #   define RTLD_LOCAL 0
82 #  endif
83 # endif
84 # if ! defined (RTLD_NOW)
85 #  if defined (DL_NOW)
86 #   define RTLD_NOW DL_NOW
87 #  else
88 #   define RTLD_NOW 0
89 #  endif
90 # endif
91 # if ! defined (RTLD_DEFAULT)
92 #  define RTLD_DEFAULT ((void *) 0)
93 # endif
94 # if defined (BROKEN_RTLD_GLOBAL)
95 #  undef RTLD_GLOBAL
96 #  define RTLD_GLOBAL 0
97 # endif
98 
99 #elif defined (HAVE_DL_H) || defined (HAVE_SYS_DL_H)
100 # if defined (HAVE_DL_H)
101 #  include <dl.h>
102 # else
103 #  include <sys/dl.h>
104 # endif
105 # if ! defined (BIND_IMMEDIATE)
106 #  define BIND_IMMEDIATE 0
107 # endif
108 # if ! defined (BIND_DEFERRED)
109 #  define BIND_DEFERRED 0
110 # endif
111 # if ! defined (BIND_NONFATAL)
112 #  define BIND_NONFATAL 0
113 # endif
114 # if ! defined (DYNAMIC_PATH)
115 #  define DYNAMIC_PATH 0
116 # endif
117 #endif
118 
119 struct dl_lib_info {
120     repv file_name;
121     repv feature_sym;
122     repv structure;
123     void *handle;
124     rep_bool is_rep_module;
125 };
126 
127 static int n_dl_libs, n_alloc_dl_libs;
128 static struct dl_lib_info *dl_libs;
129 
130 #if !defined (HAVE_DLOPEN) && defined (HAVE_SHL_LOAD)
131 static inline void *
dlsym(void * handle,char * sym)132 dlsym (void *handle, char *sym)
133 {
134     void *addr;
135     if (shl_findsym (&handle, sym, TYPE_UNDEFINED, &addr) == 0)
136 	return addr;
137     else
138 	return 0;
139 }
140 
141 static inline void
dlclose(void * handle)142 dlclose (void *handle)
143 {
144     shl_unload (handle);
145 }
146 #endif
147 
148 #ifndef DLSYM_NEED_USCORE
149 # define x_dlsym dlsym
150 #else
151 static void *
x_dlsym(void * handle,char * sym)152 x_dlsym (void *handle, char *sym)
153 {
154     void *ptr = 0;
155     char *tem = alloca (strlen(sym) + 2);
156     tem[0] = '_';
157     strcpy (tem + 1, sym);
158     ptr = dlsym (handle, tem);
159     return ptr;
160 }
161 #endif
162 
163 static int
find_dl(repv file)164 find_dl (repv file)
165 {
166     int i;
167 
168     assert (rep_STRINGP (file));
169 
170     for (i = 0; i < n_dl_libs; i++)
171     {
172 	assert (rep_STRINGP (dl_libs[i].file_name));
173 	if (!strcmp (rep_STR (file), rep_STR (dl_libs[i].file_name)))
174 	    return i;
175     }
176 
177     return -1;
178 }
179 
180 static int
find_dl_by_feature(repv feature)181 find_dl_by_feature(repv feature)
182 {
183     int i;
184 
185     assert (rep_STRINGP(feature));
186 
187     for (i = 0; i < n_dl_libs; i++)
188     {
189 	if (rep_SYMBOLP (dl_libs[i].feature_sym)
190 	    && strcmp (rep_STR (rep_SYM (dl_libs[i].feature_sym)->name),
191 		       rep_STR (feature)) == 0)
192 	{
193 	    return i;
194 	}
195     }
196 
197     return -1;
198 }
199 
200 static rep_bool
load_requires(char * ptr)201 load_requires (char *ptr)
202 {
203     ptr += strspn (ptr, " \t");
204     while (*ptr != 0)
205     {
206 	char *end = ptr + strcspn (ptr, " \t");
207 	repv sym = Fintern (rep_string_dupn (ptr, end - ptr), Qnil);
208 	if (Fintern_structure (sym) == rep_NULL)
209 	    return rep_FALSE;
210 	ptr = end + strspn (end, " \t");
211     }
212     return rep_TRUE;
213 }
214 
215 static void
signal_error(const char * msg)216 signal_error (const char *msg)
217 {
218     if (Qerror != 0)
219 	Fsignal (Qerror, rep_LIST_1 (rep_string_dup (msg)));
220     else
221 	fprintf (stderr, "error: %s\n", msg);
222 }
223 
224 int
rep_intern_dl_library(repv file_name)225 rep_intern_dl_library (repv file_name)
226 {
227     const char *dlname = 0;
228     rep_bool open_globally = rep_FALSE;
229     rep_bool is_rep_module = rep_TRUE;
230     int idx;
231     const char *tem;
232     int len;
233 
234     idx = find_dl (file_name);
235     if(idx >= 0)
236 	return idx;
237 
238     tem = rep_STR (file_name);
239     len = strlen (tem);
240 
241     if (len >= 3 && strcmp (tem + len - 3, ".la") == 0)
242     {
243 	/* We're trying to open a _libtool_ dl object. i.e it's a
244 	   file ending in .la that contains a dlname=FOO line
245 	   pointing to the actual DL object (in the same directory). */
246 
247 	char buf[256];
248 	FILE *fh;
249 
250 	fh = fopen(rep_STR(file_name), "r");
251 	if (fh == 0)
252 	{
253 	    rep_signal_file_error(file_name);
254 	    return -1;
255 	}
256 
257 	while (fgets(buf, sizeof(buf), fh))
258 	{
259 	    if (strncmp("dlname='", buf, sizeof("dlname='") - 1) == 0)
260 	    {
261 		char *ptr = buf + sizeof("dlname='") - 1;
262 		char *base;
263 		char *end = strchr(ptr, '\'');
264 		if (end != 0 && end > ptr)
265 		{
266 		    char *name;
267 
268 		    *end = 0;
269 		    base = strrchr(rep_STR(file_name), '/');
270 
271 		    if (base == 0)
272 		    {
273 			name = alloca (strlen (ptr) + 1);
274 			strcpy (name, ptr);
275 		    }
276 		    else
277 		    {
278 			base++;
279 			name = alloca (strlen(ptr) +
280 				       base - rep_STR(file_name) + 1);
281 			memcpy(name, rep_STR(file_name),
282 			       base - rep_STR(file_name));
283 			strcpy(name + (base - rep_STR(file_name)), ptr);
284 		    }
285 
286 		    dlname = name;
287 		}
288 	    }
289 	    else if (strncmp("rep_open_globally=", buf,
290 			     sizeof("rep_open_globally=") - 1) == 0)
291 	    {
292 		char *ptr = buf + sizeof ("rep_open_globally=") - 1;
293 		if (strncmp ("yes", ptr, 3) == 0)
294 		    open_globally = rep_TRUE;
295 	    }
296 	    else if (strncmp("rep_requires='", buf,
297 			     sizeof ("rep_requires='") - 1) == 0)
298 	    {
299 		char *ptr = buf + sizeof ("rep_requires='") - 1;
300 		char *end = strchr (ptr, '\'');
301 		if (end != 0)
302 		{
303 		    rep_GC_root gc_file_name;
304 		    rep_bool success;
305 		    char *string = alloca (end - ptr + 1);
306 		    memcpy (string, ptr, end - ptr);
307 		    string[end - ptr] = 0;
308 		    rep_PUSHGC (gc_file_name, file_name);
309 		    success = load_requires (string);
310 		    rep_POPGC;
311 		    if (!success)
312 			return -1;
313 		}
314 	    }
315 	}
316 	fclose(fh);
317     }
318     else
319     {
320 	/* not .la, assume a native library name */
321 
322 	dlname = rep_STR (file_name);
323     }
324 
325     if (dlname == NULL)
326     {
327 	char err[256];
328 #ifdef HAVE_SNPRINTF
329 	snprintf (err, sizeof (err), "Can't find dlname in %s", rep_STR (file_name));
330 #else
331 	sprintf (err, "Can't find dlname in %s", rep_STR (file_name));
332 #endif
333 	signal_error (err);
334 	return -1;
335     }
336     else
337     {
338 	void *handle;
339 	rep_bool relocate_now = rep_FALSE;
340 	struct dl_lib_info *x;
341 
342 	if (Qdl_load_reloc_now && Fsymbol_value (Qdl_load_reloc_now, Qt) != Qnil)
343 	{
344 	    relocate_now = rep_TRUE;
345 	}
346 
347 #if defined (HAVE_DLOPEN)
348 	handle = dlopen(dlname, (relocate_now ? RTLD_NOW : RTLD_LAZY)
349 			| (open_globally ? RTLD_GLOBAL : RTLD_LOCAL));
350 #elif defined (HAVE_SHL_LOAD)
351 	/* XXX how do we open these locally/globally? */
352 	handle = shl_load (dlname,
353 			   (relocate_now ? BIND_IMMEDIATE : BIND_DEFERRED)
354 			   | BIND_NONFATAL | DYNAMIC_PATH, 0L);
355 #endif
356 
357 	if(handle == NULL)
358 	{
359 	    const char *err;
360 #ifdef HAVE_DLERROR
361 	    err = dlerror();
362 #else
363 	    err = "unknown dl error";
364 #endif
365 	    if(err != 0)
366 		signal_error (err);
367 	    return -1;
368 	}
369 
370 	if (n_alloc_dl_libs == n_dl_libs)
371 	{
372 	    int new_n = MAX (n_alloc_dl_libs * 2, 32);
373 	    void *ptr;
374 
375 	    ptr = rep_realloc (dl_libs, new_n * sizeof (struct dl_lib_info));
376 	    if (ptr == NULL)
377 	    {
378 		rep_mem_error();
379 		dlclose(handle);
380 		return -1;
381 	    }
382 
383 	    dl_libs = ptr;
384 	    n_alloc_dl_libs = new_n;
385 	}
386 
387 	idx = n_dl_libs++;
388 	x = &dl_libs[idx];
389 
390 	x->file_name = file_name;
391 	x->handle = handle;
392 	x->feature_sym = Qnil;
393 	x->structure = Qnil;
394 	x->is_rep_module = is_rep_module;
395 
396 	if (is_rep_module)
397 	{
398 	    repv (*init_func)(repv);
399 
400 	    init_func = x_dlsym(handle, "rep_dl_init");
401 	    if(init_func != 0)
402 	    {
403 		repv ret;
404 
405 		ret = init_func(file_name);
406 
407 		if(Qnil != rep_NULL			/* initialising */
408 		   && (ret == rep_NULL || ret == Qnil))
409 		{
410 		    /* error. abort abort.. */
411 
412 		    --n_dl_libs;
413 		    dlclose(handle);
414 		    return -1;
415 		}
416 		else if (ret && rep_SYMBOLP(ret) && ret != Qt)
417 		    x->feature_sym = ret;
418 		else if (ret && rep_STRUCTUREP (ret))
419 		{
420 		    x->structure = ret;
421 		    ret = rep_STRUCTURE (ret)->name;
422 		    if (ret && rep_SYMBOLP (ret))
423 			x->feature_sym = ret;
424 		}
425 	    }
426 	}
427     }
428 
429     return idx;
430 }
431 
432 repv
rep_open_dl_library(repv file_name)433 rep_open_dl_library(repv file_name)
434 {
435     int idx;
436 
437     idx = rep_intern_dl_library (file_name);
438     if (idx < 0)
439 	return rep_NULL;
440 
441     if (dl_libs[idx].is_rep_module)
442     {
443 	if (dl_libs[idx].feature_sym != Qnil && dl_libs[idx].structure == Qnil)
444 	{
445 	    /* only `provide' the feature if there's no associated
446 	       structure (since we haven't actually imported it) */
447 	    Fprovide (dl_libs[idx].feature_sym);
448 	}
449 	return dl_libs[idx].structure;
450     }
451     else
452 	return Qt;
453 }
454 
455 void *
rep_lookup_dl_symbol(int idx,const char * name)456 rep_lookup_dl_symbol (int idx, const char *name)
457 {
458     void *handle;
459 
460     handle = (idx >= 0 && idx < n_dl_libs) ? dl_libs[idx].handle : RTLD_DEFAULT;
461 
462     return x_dlsym (handle, name);
463 }
464 
465 void
rep_mark_dl_data(void)466 rep_mark_dl_data(void)
467 {
468     int i;
469 
470     for (i = 0; i < n_dl_libs; i++)
471     {
472 	rep_MARKVAL(dl_libs[i].file_name);
473 	rep_MARKVAL(dl_libs[i].feature_sym);
474 	rep_MARKVAL(dl_libs[i].structure);
475     }
476 }
477 
478 void
rep_kill_dl_libraries(void)479 rep_kill_dl_libraries(void)
480 {
481     int i;
482 
483     for (i = 0; i < n_dl_libs; i++)
484     {
485 	if (dl_libs[i].is_rep_module)
486 	{
487 	    void (*exit_func) (void);
488 
489 	    exit_func = x_dlsym (dl_libs[i].handle, "rep_dl_kill");
490 	    if(exit_func != 0)
491 		(*exit_func) ();
492 	}
493 
494 #if 0
495 	/* Closing libraries is a _bad_ idea. There's no way
496 	   of knowing if any pointers to their contents exist.
497 	   For example, it's impossible to completely expunge
498 	   libgtk/libgdk, since they install an atexit () handler.. */
499 
500 	dlclose(x->handle);
501 #endif
502     }
503 
504     n_dl_libs = n_alloc_dl_libs = 0;
505     rep_free (dl_libs);
506     dl_libs = NULL;
507 }
508 
509 void *
rep_find_dl_symbol(repv feature,char * symbol)510 rep_find_dl_symbol (repv feature, char *symbol)
511 {
512     int idx;
513 
514     assert (rep_SYMBOLP (feature));
515 
516     idx = find_dl_by_feature (rep_SYM(feature)->name);
517 
518     if (idx <= 0)
519 	return NULL;
520 
521     return x_dlsym (dl_libs[idx].handle, symbol);
522 }
523 
524 /* Attempt to find the name and address of the nearest symbol before or
525    equal to PTR */
526 rep_bool
rep_find_c_symbol(void * ptr,char ** symbol_name_p,void ** symbol_addr_p)527 rep_find_c_symbol(void *ptr, char **symbol_name_p, void **symbol_addr_p)
528 {
529 #ifdef HAVE_DLADDR
530     Dl_info info;
531     if(dladdr(ptr, &info) != 0)
532     {
533 	*symbol_name_p = (char *)info.dli_sname;
534 	*symbol_addr_p = info.dli_saddr;
535 	return rep_TRUE;
536     }
537     else
538 #endif
539 	return rep_FALSE;
540 }
541 
542 #else /* HAVE_DYNAMIC_LOADING */
543 
544 rep_bool
rep_find_c_symbol(void * ptr,char ** name_p,void ** addr_p)545 rep_find_c_symbol(void *ptr, char **name_p, void **addr_p)
546 {
547     return rep_FALSE;
548 }
549 
550 #endif /* !HAVE_DYNAMIC_LOADING */
551