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