1/**********************************************************************
2
3  dln.c -
4
5  $Author: nobu $
6  created at: Tue Jan 18 17:05:06 JST 1994
7
8  Copyright (C) 1993-2007 Yukihiro Matsumoto
9
10**********************************************************************/
11
12#ifdef RUBY_EXPORT
13#include "ruby/ruby.h"
14#define dln_notimplement rb_notimplement
15#define dln_memerror rb_memerror
16#define dln_exit rb_exit
17#define dln_loaderror rb_loaderror
18#else
19#define dln_notimplement --->>> dln not implemented <<<---
20#define dln_memerror abort
21#define dln_exit exit
22static void dln_loaderror(const char *format, ...);
23#endif
24#include "dln.h"
25
26#ifdef HAVE_STDLIB_H
27# include <stdlib.h>
28#endif
29
30#ifdef USE_DLN_A_OUT
31char *dln_argv0;
32#endif
33
34#if defined(HAVE_ALLOCA_H)
35#include <alloca.h>
36#endif
37
38#ifdef HAVE_STRING_H
39# include <string.h>
40#else
41# include <strings.h>
42#endif
43
44#ifndef xmalloc
45void *xmalloc();
46void *xcalloc();
47void *xrealloc();
48#endif
49
50#define free(x) xfree(x)
51
52#include <stdio.h>
53#if defined(_WIN32)
54#include "missing/file.h"
55#endif
56#include <sys/types.h>
57#include <sys/stat.h>
58
59#ifndef S_ISDIR
60#   define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
61#endif
62
63#ifdef HAVE_SYS_PARAM_H
64# include <sys/param.h>
65#endif
66#ifndef MAXPATHLEN
67# define MAXPATHLEN 1024
68#endif
69
70#ifdef HAVE_UNISTD_H
71# include <unistd.h>
72#endif
73
74#ifndef _WIN32
75char *getenv();
76#endif
77
78#ifdef __APPLE__
79# if defined(HAVE_DLOPEN)
80   /* Mac OS X with dlopen (10.3 or later) */
81#  define MACOSX_DLOPEN
82# else
83#  define MACOSX_DYLD
84# endif
85#endif
86
87#if defined(__BEOS__) || defined(__HAIKU__)
88# include <image.h>
89#endif
90
91#ifndef dln_loaderror
92static void
93dln_loaderror(const char *format, ...)
94{
95    va_list ap;
96    va_start(ap, format);
97    vfprintf(stderr, format, ap);
98    va_end(ap);
99    abort();
100}
101#endif
102
103#if defined(HAVE_DLOPEN) && !defined(USE_DLN_A_OUT) && !defined(_AIX) && !defined(MACOSX_DYLD) && !defined(_UNICOSMP)
104/* dynamic load with dlopen() */
105# define USE_DLN_DLOPEN
106#endif
107
108#ifndef FUNCNAME_PATTERN
109# if defined(__hp9000s300) || ((defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)) && !defined(__ELF__)) || defined(__BORLANDC__) || defined(NeXT) || defined(__WATCOMC__) || defined(MACOSX_DYLD)
110#  define FUNCNAME_PREFIX "_Init_"
111# else
112#  define FUNCNAME_PREFIX "Init_"
113# endif
114#endif
115
116#if defined __CYGWIN__ || defined DOSISH
117#define isdirsep(x) ((x) == '/' || (x) == '\\')
118#else
119#define isdirsep(x) ((x) == '/')
120#endif
121
122static size_t
123init_funcname_len(const char **file)
124{
125    const char *p = *file, *base, *dot = NULL;
126
127    /* Load the file as an object one */
128    for (base = p; *p; p++) { /* Find position of last '/' */
129	if (*p == '.' && !dot) dot = p;
130	if (isdirsep(*p)) base = p+1, dot = NULL;
131    }
132    *file = base;
133    /* Delete suffix if it exists */
134    return (dot ? dot : p) - base;
135}
136
137static const char funcname_prefix[sizeof(FUNCNAME_PREFIX) - 1] = FUNCNAME_PREFIX;
138
139#define init_funcname(buf, file) do {\
140    const char *base = (file);\
141    const size_t flen = init_funcname_len(&base);\
142    const size_t plen = sizeof(funcname_prefix);\
143    char *const tmp = ALLOCA_N(char, plen+flen+1);\
144    if (!tmp) {\
145	dln_memerror();\
146    }\
147    memcpy(tmp, funcname_prefix, plen);\
148    memcpy(tmp+plen, base, flen);\
149    tmp[plen+flen] = '\0';\
150    *(buf) = tmp;\
151} while (0)
152
153#ifdef USE_DLN_A_OUT
154
155#ifndef LIBC_NAME
156# define LIBC_NAME "libc.a"
157#endif
158
159#ifndef DLN_DEFAULT_LIB_PATH
160#  define DLN_DEFAULT_LIB_PATH "/lib:/usr/lib:/usr/local/lib:."
161#endif
162
163#include <errno.h>
164
165static int dln_errno;
166
167#define DLN_ENOEXEC	ENOEXEC	/* Exec format error */
168#define DLN_ECONFL	1201	/* Symbol name conflict */
169#define DLN_ENOINIT	1202	/* No initializer given */
170#define DLN_EUNDEF	1203	/* Undefine symbol remains */
171#define DLN_ENOTLIB	1204	/* Not a library file */
172#define DLN_EBADLIB	1205	/* Malformed library file */
173#define DLN_EINIT	1206	/* Not initialized */
174
175static int dln_init_p = 0;
176
177#include <ar.h>
178#include <a.out.h>
179#ifndef N_COMM
180# define N_COMM 0x12
181#endif
182#ifndef N_MAGIC
183# define N_MAGIC(x) (x).a_magic
184#endif
185
186#define INVALID_OBJECT(h) (N_MAGIC(h) != OMAGIC)
187
188#include "ruby/util.h"
189#include "ruby/st.h"
190
191static st_table *sym_tbl;
192static st_table *undef_tbl;
193
194static int load_lib();
195
196static int
197load_header(int fd, struct exec *hdrp, long disp)
198{
199    int size;
200
201    lseek(fd, disp, 0);
202    size = read(fd, hdrp, sizeof(struct exec));
203    if (size == -1) {
204	dln_errno = errno;
205	return -1;
206    }
207    if (size != sizeof(struct exec) || N_BADMAG(*hdrp)) {
208	dln_errno = DLN_ENOEXEC;
209	return -1;
210    }
211    return 0;
212}
213
214#if defined(sequent)
215#define RELOC_SYMBOL(r)			((r)->r_symbolnum)
216#define RELOC_MEMORY_SUB_P(r)		((r)->r_bsr)
217#define RELOC_PCREL_P(r)		((r)->r_pcrel || (r)->r_bsr)
218#define RELOC_TARGET_SIZE(r)		((r)->r_length)
219#endif
220
221/* Default macros */
222#ifndef RELOC_ADDRESS
223#define RELOC_ADDRESS(r)		((r)->r_address)
224#define RELOC_EXTERN_P(r)		((r)->r_extern)
225#define RELOC_SYMBOL(r)			((r)->r_symbolnum)
226#define RELOC_MEMORY_SUB_P(r)		0
227#define RELOC_PCREL_P(r)		((r)->r_pcrel)
228#define RELOC_TARGET_SIZE(r)		((r)->r_length)
229#endif
230
231#if defined(__sun) && defined(__sparc)
232/* Sparc (Sun 4) macros */
233#  undef relocation_info
234#  define relocation_info reloc_info_sparc
235#  define R_RIGHTSHIFT(r)	(reloc_r_rightshift[(r)->r_type])
236#  define R_BITSIZE(r) 		(reloc_r_bitsize[(r)->r_type])
237#  define R_LENGTH(r)		(reloc_r_length[(r)->r_type])
238static int reloc_r_rightshift[] = {
239  0, 0, 0, 0, 0, 0, 2, 2, 10, 0, 0, 0, 0, 0, 0,
240};
241static int reloc_r_bitsize[] = {
242  8, 16, 32, 8, 16, 32, 30, 22, 22, 22, 13, 10, 32, 32, 16,
243};
244static int reloc_r_length[] = {
245  0, 1, 2, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
246};
247#  define R_PCREL(r) \
248    ((r)->r_type >= RELOC_DISP8 && (r)->r_type <= RELOC_WDISP22)
249#  define R_SYMBOL(r) ((r)->r_index)
250#endif
251
252#if defined(sequent)
253#define R_SYMBOL(r)		((r)->r_symbolnum)
254#define R_MEMORY_SUB(r)		((r)->r_bsr)
255#define R_PCREL(r)		((r)->r_pcrel || (r)->r_bsr)
256#define R_LENGTH(r)		((r)->r_length)
257#endif
258
259#ifndef R_SYMBOL
260#  define R_SYMBOL(r) 		((r)->r_symbolnum)
261#  define R_MEMORY_SUB(r)	0
262#  define R_PCREL(r)  		((r)->r_pcrel)
263#  define R_LENGTH(r) 		((r)->r_length)
264#endif
265
266static struct relocation_info *
267load_reloc(int fd, struct exec *hdrp, long disp)
268{
269    struct relocation_info *reloc;
270    int size;
271
272    lseek(fd, disp + N_TXTOFF(*hdrp) + hdrp->a_text + hdrp->a_data, 0);
273    size = hdrp->a_trsize + hdrp->a_drsize;
274    reloc = (struct relocation_info*)xmalloc(size);
275    if (reloc == NULL) {
276	dln_errno = errno;
277	return NULL;
278    }
279
280    if (read(fd, reloc, size) !=  size) {
281	dln_errno = errno;
282	free(reloc);
283	return NULL;
284    }
285
286    return reloc;
287}
288
289static struct nlist *
290load_sym(int fd, struct exec *hdrp, long disp)
291{
292    struct nlist * buffer;
293    struct nlist * sym;
294    struct nlist * end;
295    long displ;
296    int size;
297
298    lseek(fd, N_SYMOFF(*hdrp) + hdrp->a_syms + disp, 0);
299    if (read(fd, &size, sizeof(int)) != sizeof(int)) {
300	goto err_noexec;
301    }
302
303    buffer = (struct nlist*)xmalloc(hdrp->a_syms + size);
304    if (buffer == NULL) {
305	dln_errno = errno;
306	return NULL;
307    }
308
309    lseek(fd, disp + N_SYMOFF(*hdrp), 0);
310    if (read(fd, buffer, hdrp->a_syms + size) != hdrp->a_syms + size) {
311	free(buffer);
312	goto err_noexec;
313    }
314
315    sym = buffer;
316    end = sym + hdrp->a_syms / sizeof(struct nlist);
317    displ = (long)buffer + (long)(hdrp->a_syms);
318
319    while (sym < end) {
320	sym->n_un.n_name = (char*)sym->n_un.n_strx + displ;
321	sym++;
322    }
323    return buffer;
324
325  err_noexec:
326    dln_errno = DLN_ENOEXEC;
327    return NULL;
328}
329
330static st_table *
331sym_hash(struct exec *hdrp, struct nlist *syms)
332{
333    st_table *tbl;
334    struct nlist *sym = syms;
335    struct nlist *end = syms + (hdrp->a_syms / sizeof(struct nlist));
336
337    tbl = st_init_strtable();
338    if (tbl == NULL) {
339	dln_errno = errno;
340	return NULL;
341    }
342
343    while (sym < end) {
344	st_insert(tbl, sym->n_un.n_name, sym);
345	sym++;
346    }
347    return tbl;
348}
349
350static int
351dln_init(const char *prog)
352{
353    char *file, fbuf[MAXPATHLEN];
354    int fd;
355    struct exec hdr;
356    struct nlist *syms;
357
358    if (dln_init_p == 1) return 0;
359
360    file = dln_find_exe_r(prog, NULL, fbuf, sizeof(fbuf));
361    if (file == NULL || (fd = open(file, O_RDONLY)) < 0) {
362	dln_errno = errno;
363	return -1;
364    }
365
366    if (load_header(fd, &hdr, 0) == -1) return -1;
367    syms = load_sym(fd, &hdr, 0);
368    if (syms == NULL) {
369	close(fd);
370	return -1;
371    }
372    sym_tbl = sym_hash(&hdr, syms);
373    if (sym_tbl == NULL) {	/* file may be start with #! */
374	char c = '\0';
375	char buf[MAXPATHLEN];
376	char *p;
377
378	free(syms);
379	lseek(fd, 0L, 0);
380	if (read(fd, &c, 1) == -1) {
381	    dln_errno = errno;
382	    return -1;
383	}
384	if (c != '#') goto err_noexec;
385	if (read(fd, &c, 1) == -1) {
386	    dln_errno = errno;
387	    return -1;
388	}
389	if (c != '!') goto err_noexec;
390
391	p = buf;
392	/* skip forwarding spaces */
393	while (read(fd, &c, 1) == 1) {
394	    if (c == '\n') goto err_noexec;
395	    if (c != '\t' && c != ' ') {
396		*p++ = c;
397		break;
398	    }
399	}
400	/* read in command name */
401	while (read(fd, p, 1) == 1) {
402	    if (*p == '\n' || *p == '\t' || *p == ' ') break;
403	    p++;
404	    if (p-buf >= MAXPATHLEN) {
405		dln_errno = ENAMETOOLONG;
406		return -1;
407	    }
408	}
409	*p = '\0';
410
411	return dln_init(buf);
412    }
413    dln_init_p = 1;
414    undef_tbl = st_init_strtable();
415    close(fd);
416    return 0;
417
418  err_noexec:
419    close(fd);
420    dln_errno = DLN_ENOEXEC;
421    return -1;
422}
423
424static long
425load_text_data(int fd, struct exec *hdrp, int bss, long disp)
426{
427    int size;
428    unsigned char* addr;
429
430    lseek(fd, disp + N_TXTOFF(*hdrp), 0);
431    size = hdrp->a_text + hdrp->a_data;
432
433    if (bss == -1) size += hdrp->a_bss;
434    else if (bss > 1) size += bss;
435
436    addr = (unsigned char*)xmalloc(size);
437    if (addr == NULL) {
438	dln_errno = errno;
439	return 0;
440    }
441
442    if (read(fd, addr, size) !=  size) {
443	dln_errno = errno;
444	free(addr);
445	return 0;
446    }
447
448    if (bss == -1) {
449	memset(addr +  hdrp->a_text + hdrp->a_data, 0, hdrp->a_bss);
450    }
451    else if (bss > 0) {
452	memset(addr +  hdrp->a_text + hdrp->a_data, 0, bss);
453    }
454
455    return (long)addr;
456}
457
458static int
459undef_print(char *key, char *value)
460{
461    fprintf(stderr, "  %s\n", key);
462    return ST_CONTINUE;
463}
464
465static void
466dln_print_undef(void)
467{
468    fprintf(stderr, " Undefined symbols:\n");
469    st_foreach(undef_tbl, undef_print, NULL);
470}
471
472static void
473dln_undefined(void)
474{
475    if (undef_tbl->num_entries > 0) {
476	fprintf(stderr, "dln: Calling undefined function\n");
477	dln_print_undef();
478	dln_exit(1);
479    }
480}
481
482struct undef {
483    char *name;
484    struct relocation_info reloc;
485    long base;
486    char *addr;
487    union {
488	char c;
489	short s;
490	long l;
491    } u;
492};
493
494static st_table *reloc_tbl = NULL;
495static void
496link_undef(const char *name, long base, struct relocation_info *reloc)
497{
498    static int u_no = 0;
499    struct undef *obj;
500    char *addr = (char*)(reloc->r_address + base);
501
502    obj = (struct undef*)xmalloc(sizeof(struct undef));
503    obj->name = strdup(name);
504    obj->reloc = *reloc;
505    obj->base = base;
506    switch (R_LENGTH(reloc)) {
507      case 0:		/* byte */
508	obj->u.c = *addr;
509	break;
510      case 1:		/* word */
511	obj->u.s = *(short*)addr;
512	break;
513      case 2:		/* long */
514	obj->u.l = *(long*)addr;
515	break;
516    }
517    if (reloc_tbl == NULL) {
518	reloc_tbl = st_init_numtable();
519    }
520    st_insert(reloc_tbl, u_no++, obj);
521}
522
523struct reloc_arg {
524    const char *name;
525    long value;
526};
527
528static int
529reloc_undef(int no, struct undef *undef, struct reloc_arg *arg)
530{
531    int datum;
532    char *address;
533#if defined(__sun) && defined(__sparc)
534    unsigned int mask = 0;
535#endif
536
537    if (strcmp(arg->name, undef->name) != 0) return ST_CONTINUE;
538    address = (char*)(undef->base + undef->reloc.r_address);
539    datum = arg->value;
540
541    if (R_PCREL(&(undef->reloc))) datum -= undef->base;
542#if defined(__sun) && defined(__sparc)
543    datum += undef->reloc.r_addend;
544    datum >>= R_RIGHTSHIFT(&(undef->reloc));
545    mask = (1 << R_BITSIZE(&(undef->reloc))) - 1;
546    mask |= mask -1;
547    datum &= mask;
548    switch (R_LENGTH(&(undef->reloc))) {
549      case 0:
550	*address = undef->u.c;
551	*address &= ~mask;
552	*address |= datum;
553	break;
554      case 1:
555	*(short *)address = undef->u.s;
556	*(short *)address &= ~mask;
557	*(short *)address |= datum;
558	break;
559      case 2:
560	*(long *)address = undef->u.l;
561	*(long *)address &= ~mask;
562	*(long *)address |= datum;
563	break;
564    }
565#else
566    switch (R_LENGTH(&(undef->reloc))) {
567      case 0:		/* byte */
568	if (R_MEMORY_SUB(&(undef->reloc)))
569	    *address = datum - *address;
570	else *address = undef->u.c + datum;
571	break;
572      case 1:		/* word */
573	if (R_MEMORY_SUB(&(undef->reloc)))
574	    *(short*)address = datum - *(short*)address;
575	else *(short*)address = undef->u.s + datum;
576	break;
577      case 2:		/* long */
578	if (R_MEMORY_SUB(&(undef->reloc)))
579	    *(long*)address = datum - *(long*)address;
580	else *(long*)address = undef->u.l + datum;
581	break;
582    }
583#endif
584    free(undef->name);
585    free(undef);
586    return ST_DELETE;
587}
588
589static void
590unlink_undef(const char *name, long value)
591{
592    struct reloc_arg arg;
593
594    arg.name = name;
595    arg.value = value;
596    st_foreach(reloc_tbl, reloc_undef, &arg);
597}
598
599#ifdef N_INDR
600struct indr_data {
601    char *name0, *name1;
602};
603
604static int
605reloc_repl(int no, struct undef *undef, struct indr_data *data)
606{
607    if (strcmp(data->name0, undef->name) == 0) {
608	free(undef->name);
609	undef->name = strdup(data->name1);
610    }
611    return ST_CONTINUE;
612}
613#endif
614
615static int
616load_1(int fd, long disp, const char *need_init)
617{
618    static const char *libc = LIBC_NAME;
619    struct exec hdr;
620    struct relocation_info *reloc = NULL;
621    long block = 0;
622    long new_common = 0; /* Length of new common */
623    struct nlist *syms = NULL;
624    struct nlist *sym;
625    struct nlist *end;
626    int init_p = 0;
627
628    if (load_header(fd, &hdr, disp) == -1) return -1;
629    if (INVALID_OBJECT(hdr)) {
630	dln_errno = DLN_ENOEXEC;
631	return -1;
632    }
633    reloc = load_reloc(fd, &hdr, disp);
634    if (reloc == NULL) return -1;
635
636    syms = load_sym(fd, &hdr, disp);
637    if (syms == NULL) {
638	free(reloc);
639	return -1;
640    }
641
642    sym = syms;
643    end = syms + (hdr.a_syms / sizeof(struct nlist));
644    while (sym < end) {
645	struct nlist *old_sym;
646	int value = sym->n_value;
647
648#ifdef N_INDR
649	if (sym->n_type == (N_INDR | N_EXT)) {
650	    char *key = sym->n_un.n_name;
651
652	    if (st_lookup(sym_tbl, sym[1].n_un.n_name, &old_sym)) {
653		if (st_delete(undef_tbl, (st_data_t*)&key, NULL)) {
654		    unlink_undef(key, old_sym->n_value);
655		    free(key);
656		}
657	    }
658	    else {
659		struct indr_data data;
660
661		data.name0 = sym->n_un.n_name;
662		data.name1 = sym[1].n_un.n_name;
663		st_foreach(reloc_tbl, reloc_repl, &data);
664
665		st_insert(undef_tbl, strdup(sym[1].n_un.n_name), NULL);
666		if (st_delete(undef_tbl, (st_data_t*)&key, NULL)) {
667		    free(key);
668		}
669	    }
670	    sym += 2;
671	    continue;
672	}
673#endif
674	if (sym->n_type == (N_UNDF | N_EXT)) {
675	    if (st_lookup(sym_tbl, sym->n_un.n_name, &old_sym) == 0) {
676		old_sym = NULL;
677	    }
678
679	    if (value) {
680		if (old_sym) {
681		    sym->n_type = N_EXT | N_COMM;
682		    sym->n_value = old_sym->n_value;
683		}
684		else {
685		    int rnd =
686			value >= sizeof(double) ? sizeof(double) - 1
687			    : value >= sizeof(long) ? sizeof(long) - 1
688				: sizeof(short) - 1;
689
690		    sym->n_type = N_COMM;
691		    new_common += rnd;
692		    new_common &= ~(long)rnd;
693		    sym->n_value = new_common;
694		    new_common += value;
695		}
696	    }
697	    else {
698		if (old_sym) {
699		    sym->n_type = N_EXT | N_COMM;
700		    sym->n_value = old_sym->n_value;
701		}
702		else {
703		    sym->n_value = (long)dln_undefined;
704		    st_insert(undef_tbl, strdup(sym->n_un.n_name), NULL);
705		}
706	    }
707	}
708	sym++;
709    }
710
711    block = load_text_data(fd, &hdr, hdr.a_bss + new_common, disp);
712    if (block == 0) goto err_exit;
713
714    sym = syms;
715    while (sym < end) {
716	struct nlist *new_sym;
717	char *key;
718
719	switch (sym->n_type) {
720	  case N_COMM:
721	    sym->n_value += hdr.a_text + hdr.a_data;
722	  case N_TEXT|N_EXT:
723	  case N_DATA|N_EXT:
724
725	    sym->n_value += block;
726
727	    if (st_lookup(sym_tbl, sym->n_un.n_name, &new_sym) != 0
728		&& new_sym->n_value != (long)dln_undefined) {
729		dln_errno = DLN_ECONFL;
730		goto err_exit;
731	    }
732
733	    key = sym->n_un.n_name;
734	    if (st_delete(undef_tbl, (st_data_t*)&key, NULL) != 0) {
735		unlink_undef(key, sym->n_value);
736		free(key);
737	    }
738
739	    new_sym = (struct nlist*)xmalloc(sizeof(struct nlist));
740	    *new_sym = *sym;
741	    new_sym->n_un.n_name = strdup(sym->n_un.n_name);
742	    st_insert(sym_tbl, new_sym->n_un.n_name, new_sym);
743	    break;
744
745	  case N_TEXT:
746	  case N_DATA:
747	    sym->n_value += block;
748	    break;
749	}
750	sym++;
751    }
752
753    /*
754     * First comes the text-relocation
755     */
756    {
757	struct relocation_info * rel = reloc;
758	struct relocation_info * rel_beg = reloc +
759	    (hdr.a_trsize/sizeof(struct relocation_info));
760	struct relocation_info * rel_end = reloc +
761	    (hdr.a_trsize+hdr.a_drsize)/sizeof(struct relocation_info);
762
763	while (rel < rel_end) {
764	    char *address = (char*)(rel->r_address + block);
765	    long datum = 0;
766#if defined(__sun) && defined(__sparc)
767	    unsigned int mask = 0;
768#endif
769
770	    if (rel >= rel_beg)
771		address += hdr.a_text;
772
773	    if (rel->r_extern) { /* Look it up in symbol-table */
774		sym = &(syms[R_SYMBOL(rel)]);
775		switch (sym->n_type) {
776		  case N_EXT|N_UNDF:
777		    link_undef(sym->n_un.n_name, block, rel);
778		  case N_EXT|N_COMM:
779		  case N_COMM:
780		    datum = sym->n_value;
781		    break;
782		  default:
783		    goto err_exit;
784		}
785	    } /* end.. look it up */
786	    else { /* is static */
787		switch (R_SYMBOL(rel)) {
788		  case N_TEXT:
789		  case N_DATA:
790		    datum = block;
791		    break;
792		  case N_BSS:
793		    datum = block +  new_common;
794		    break;
795		  case N_ABS:
796		    break;
797		}
798	    } /* end .. is static */
799	    if (R_PCREL(rel)) datum -= block;
800
801#if defined(__sun) && defined(__sparc)
802	    datum += rel->r_addend;
803	    datum >>= R_RIGHTSHIFT(rel);
804	    mask = (1 << R_BITSIZE(rel)) - 1;
805	    mask |= mask -1;
806	    datum &= mask;
807
808	    switch (R_LENGTH(rel)) {
809	      case 0:
810		*address &= ~mask;
811		*address |= datum;
812		break;
813	      case 1:
814		*(short *)address &= ~mask;
815		*(short *)address |= datum;
816		break;
817	      case 2:
818		*(long *)address &= ~mask;
819		*(long *)address |= datum;
820		break;
821	    }
822#else
823	    switch (R_LENGTH(rel)) {
824	      case 0:		/* byte */
825		if (datum < -128 || datum > 127) goto err_exit;
826		*address += datum;
827		break;
828	      case 1:		/* word */
829		*(short *)address += datum;
830		break;
831	      case 2:		/* long */
832		*(long *)address += datum;
833		break;
834	    }
835#endif
836	    rel++;
837	}
838    }
839
840    if (need_init) {
841	int len;
842	char **libs_to_be_linked = 0;
843	char *buf;
844
845	if (undef_tbl->num_entries > 0) {
846	    if (load_lib(libc) == -1) goto err_exit;
847	}
848
849	init_funcname(&buf, need_init);
850	len = strlen(buf);
851
852	for (sym = syms; sym<end; sym++) {
853	    char *name = sym->n_un.n_name;
854	    if (name[0] == '_' && sym->n_value >= block) {
855		if (strcmp(name+1, "dln_libs_to_be_linked") == 0) {
856		    libs_to_be_linked = (char**)sym->n_value;
857		}
858		else if (strcmp(name+1, buf) == 0) {
859		    init_p = 1;
860		    ((int (*)())sym->n_value)();
861		}
862	    }
863	}
864	if (libs_to_be_linked && undef_tbl->num_entries > 0) {
865	    while (*libs_to_be_linked) {
866		load_lib(*libs_to_be_linked);
867		libs_to_be_linked++;
868	    }
869	}
870    }
871    free(reloc);
872    free(syms);
873    if (need_init) {
874	if (init_p == 0) {
875	    dln_errno = DLN_ENOINIT;
876	    return -1;
877	}
878	if (undef_tbl->num_entries > 0) {
879	    if (load_lib(libc) == -1) goto err_exit;
880	    if (undef_tbl->num_entries > 0) {
881		dln_errno = DLN_EUNDEF;
882		return -1;
883	    }
884	}
885    }
886    return 0;
887
888  err_exit:
889    if (syms) free(syms);
890    if (reloc) free(reloc);
891    if (block) free((char*)block);
892    return -1;
893}
894
895static int target_offset;
896static int
897search_undef(const char *key, int value, st_table *lib_tbl)
898{
899    long offset;
900
901    if (st_lookup(lib_tbl, key, &offset) == 0) return ST_CONTINUE;
902    target_offset = offset;
903    return ST_STOP;
904}
905
906struct symdef {
907    int rb_str_index;
908    int lib_offset;
909};
910
911const char *dln_librrb_ary_path = DLN_DEFAULT_LIB_PATH;
912
913static int
914load_lib(const char *lib)
915{
916    char *path, *file, fbuf[MAXPATHLEN];
917    char *envpath = 0;
918    char armagic[SARMAG];
919    int fd, size;
920    struct ar_hdr ahdr;
921    st_table *lib_tbl = NULL;
922    int *data, nsym;
923    struct symdef *base;
924    char *name_base;
925
926    if (dln_init_p == 0) {
927	dln_errno = DLN_ENOINIT;
928	return -1;
929    }
930
931    if (undef_tbl->num_entries == 0) return 0;
932    dln_errno = DLN_EBADLIB;
933
934    if (lib[0] == '-' && lib[1] == 'l') {
935	long len = strlen(lib) + 4;
936	char *p = alloca(len);
937	snprintf(p, len, "lib%s.a", lib+2);
938	lib = p;
939    }
940
941    /* library search path: */
942    /* look for environment variable DLN_LIBRARY_PATH first. */
943    /* then variable dln_librrb_ary_path. */
944    /* if path is still NULL, use "." for path. */
945    path = getenv("DLN_LIBRARY_PATH");
946    if (path == NULL) path = dln_librrb_ary_path;
947    else path = envpath = strdup(path);
948
949    file = dln_find_file_r(lib, path, fbuf, sizeof(fbuf));
950    if (envpath) free(envpath);
951    fd = open(file, O_RDONLY);
952    if (fd == -1) goto syserr;
953    size = read(fd, armagic, SARMAG);
954    if (size == -1) goto syserr;
955
956    if (size != SARMAG) {
957	dln_errno = DLN_ENOTLIB;
958	goto badlib;
959    }
960    size = read(fd, &ahdr, sizeof(ahdr));
961    if (size == -1) goto syserr;
962    if (size != sizeof(ahdr) || sscanf(ahdr.ar_size, "%d", &size) != 1) {
963	goto badlib;
964    }
965
966    if (strncmp(ahdr.ar_name, "__.SYMDEF", 9) == 0) {
967	/* make hash table from __.SYMDEF */
968
969	lib_tbl = st_init_strtable();
970	data = (int*)xmalloc(size);
971	if (data == NULL) goto syserr;
972	size = read(fd, data, size);
973	nsym = *data / sizeof(struct symdef);
974	base = (struct symdef*)(data + 1);
975	name_base = (char*)(base + nsym) + sizeof(int);
976	while (nsym > 0) {
977	    char *name = name_base + base->rb_str_index;
978
979	    st_insert(lib_tbl, name, base->lib_offset + sizeof(ahdr));
980	    nsym--;
981	    base++;
982	}
983	for (;;) {
984	    target_offset = -1;
985	    st_foreach(undef_tbl, search_undef, lib_tbl);
986	    if (target_offset == -1) break;
987	    if (load_1(fd, target_offset, 0) == -1) {
988		st_free_table(lib_tbl);
989		free(data);
990		goto badlib;
991	    }
992	    if (undef_tbl->num_entries == 0) break;
993	}
994	free(data);
995	st_free_table(lib_tbl);
996    }
997    else {
998	/* linear library, need to scan (FUTURE) */
999
1000	for (;;) {
1001	    int offset = SARMAG;
1002	    int found = 0;
1003	    struct exec hdr;
1004	    struct nlist *syms, *sym, *end;
1005
1006	    while (undef_tbl->num_entries > 0) {
1007		found = 0;
1008		lseek(fd, offset, 0);
1009		size = read(fd, &ahdr, sizeof(ahdr));
1010		if (size == -1) goto syserr;
1011		if (size == 0) break;
1012		if (size != sizeof(ahdr)
1013		    || sscanf(ahdr.ar_size, "%d", &size) != 1) {
1014		    goto badlib;
1015		}
1016		offset += sizeof(ahdr);
1017		if (load_header(fd, &hdr, offset) == -1)
1018		    goto badlib;
1019		syms = load_sym(fd, &hdr, offset);
1020		if (syms == NULL) goto badlib;
1021		sym = syms;
1022		end = syms + (hdr.a_syms / sizeof(struct nlist));
1023		while (sym < end) {
1024		    if (sym->n_type == N_EXT|N_TEXT
1025			&& st_lookup(undef_tbl, sym->n_un.n_name, NULL)) {
1026			break;
1027		    }
1028		    sym++;
1029		}
1030		if (sym < end) {
1031		    found++;
1032		    free(syms);
1033		    if (load_1(fd, offset, 0) == -1) {
1034			goto badlib;
1035		    }
1036		}
1037		offset += size;
1038		if (offset & 1) offset++;
1039	    }
1040	    if (found) break;
1041	}
1042    }
1043    close(fd);
1044    return 0;
1045
1046  syserr:
1047    dln_errno = errno;
1048  badlib:
1049    if (fd >= 0) close(fd);
1050    return -1;
1051}
1052
1053static int
1054load(const char *file)
1055{
1056    int fd;
1057    int result;
1058
1059    if (dln_init_p == 0) {
1060	if (dln_init(dln_argv0) == -1) return -1;
1061    }
1062    result = strlen(file);
1063    if (file[result-1] == 'a') {
1064	return load_lib(file);
1065    }
1066
1067    fd = open(file, O_RDONLY);
1068    if (fd == -1) {
1069	dln_errno = errno;
1070	return -1;
1071    }
1072    result = load_1(fd, 0, file);
1073    close(fd);
1074
1075    return result;
1076}
1077
1078void*
1079dln_sym(const char *name)
1080{
1081    struct nlist *sym;
1082
1083    if (st_lookup(sym_tbl, name, &sym))
1084	return (void*)sym->n_value;
1085    return NULL;
1086}
1087
1088#endif /* USE_DLN_A_OUT */
1089
1090#ifdef USE_DLN_DLOPEN
1091# include <dlfcn.h>
1092#endif
1093
1094#ifdef __hpux
1095#include <errno.h>
1096#include "dl.h"
1097#endif
1098
1099#if defined(_AIX)
1100#include <ctype.h>	/* for isdigit()	*/
1101#include <errno.h>	/* for global errno	*/
1102#include <sys/ldr.h>
1103#endif
1104
1105#ifdef NeXT
1106#if NS_TARGET_MAJOR < 4
1107#include <mach-o/rld.h>
1108#else
1109#include <mach-o/dyld.h>
1110#ifndef NSLINKMODULE_OPTION_BINDNOW
1111#define NSLINKMODULE_OPTION_BINDNOW 1
1112#endif
1113#endif
1114#else
1115#ifdef MACOSX_DYLD
1116#include <mach-o/dyld.h>
1117#endif
1118#endif
1119
1120#if defined _WIN32 && !defined __CYGWIN__
1121#include <windows.h>
1122#include <imagehlp.h>
1123#endif
1124
1125#if defined _WIN32 && !defined __CYGWIN__
1126static const char *
1127dln_strerror(char *message, size_t size)
1128{
1129    int error = GetLastError();
1130    char *p = message;
1131    size_t len = snprintf(message, size, "%d: ", error);
1132
1133#define format_message(sublang) FormatMessage(\
1134	FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,	\
1135	NULL, error, MAKELANGID(LANG_NEUTRAL, (sublang)),		\
1136	message + len, size - len, NULL)
1137    if (format_message(SUBLANG_ENGLISH_US) == 0)
1138	format_message(SUBLANG_DEFAULT);
1139    for (p = message + len; *p; p++) {
1140	if (*p == '\n' || *p == '\r')
1141	    *p = ' ';
1142    }
1143    return message;
1144}
1145#define dln_strerror() dln_strerror(message, sizeof message)
1146#elif ! defined _AIX
1147static const char *
1148dln_strerror(void)
1149{
1150#ifdef USE_DLN_A_OUT
1151    char *strerror();
1152
1153    switch (dln_errno) {
1154      case DLN_ECONFL:
1155	return "Symbol name conflict";
1156      case DLN_ENOINIT:
1157	return "No initializer given";
1158      case DLN_EUNDEF:
1159	return "Unresolved symbols";
1160      case DLN_ENOTLIB:
1161	return "Not a library file";
1162      case DLN_EBADLIB:
1163	return "Malformed library file";
1164      case DLN_EINIT:
1165	return "Not initialized";
1166      default:
1167	return strerror(dln_errno);
1168    }
1169#endif
1170
1171#ifdef USE_DLN_DLOPEN
1172    return (char*)dlerror();
1173#endif
1174}
1175#endif
1176
1177#if defined(_AIX) && ! defined(_IA64)
1178static void
1179aix_loaderror(const char *pathname)
1180{
1181  char *message[1024], errbuf[1024];
1182  int i;
1183#define ERRBUF_APPEND(s) strncat(errbuf, (s), sizeof(errbuf)-strlen(errbuf)-1)
1184  snprintf(errbuf, sizeof(errbuf), "load failed - %s. ", pathname);
1185
1186  if (loadquery(L_GETMESSAGES, &message[0], sizeof(message)) != -1) {
1187    ERRBUF_APPEND("Please issue below command for detailed reasons:\n\t");
1188    ERRBUF_APPEND("/usr/sbin/execerror ruby ");
1189    for (i=0; message[i]; i++) {
1190      ERRBUF_APPEND("\"");
1191      ERRBUF_APPEND(message[i]);
1192      ERRBUF_APPEND("\" ");
1193    }
1194    ERRBUF_APPEND("\n");
1195  } else {
1196    ERRBUF_APPEND(strerror(errno));
1197    ERRBUF_APPEND("[loadquery failed]");
1198  }
1199  dln_loaderror("%s", errbuf);
1200}
1201#endif
1202
1203#if defined _WIN32 && defined RUBY_EXPORT
1204HANDLE rb_libruby_handle(void);
1205
1206static int
1207rb_w32_check_imported(HMODULE ext, HMODULE mine)
1208{
1209    ULONG size;
1210    const IMAGE_IMPORT_DESCRIPTOR *desc;
1211
1212    desc = ImageDirectoryEntryToData(ext, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size);
1213    if (!desc) return 0;
1214    while (desc->Name) {
1215	PIMAGE_THUNK_DATA pint = (PIMAGE_THUNK_DATA)((char *)ext + desc->Characteristics);
1216	PIMAGE_THUNK_DATA piat = (PIMAGE_THUNK_DATA)((char *)ext + desc->FirstThunk);
1217	for (; piat->u1.Function; piat++, pint++) {
1218	    static const char prefix[] = "rb_";
1219	    PIMAGE_IMPORT_BY_NAME pii;
1220	    const char *name;
1221
1222	    if (IMAGE_SNAP_BY_ORDINAL(pint->u1.Ordinal)) continue;
1223	    pii = (PIMAGE_IMPORT_BY_NAME)((char *)ext + (size_t)pint->u1.AddressOfData);
1224	    name = (const char *)pii->Name;
1225	    if (strncmp(name, prefix, sizeof(prefix) - 1) == 0) {
1226		FARPROC addr = GetProcAddress(mine, name);
1227		if (addr) return (FARPROC)piat->u1.Function == addr;
1228	    }
1229	}
1230	desc++;
1231    }
1232    return 1;
1233}
1234#endif
1235
1236#if defined(DLN_NEEDS_ALT_SEPARATOR) && DLN_NEEDS_ALT_SEPARATOR
1237#define translit_separator(src) do { \
1238	char *tmp = ALLOCA_N(char, strlen(src) + 1), *p = tmp, c; \
1239	do { \
1240	    *p++ = ((c = *file++) == '/') ? DLN_NEEDS_ALT_SEPARATOR : c; \
1241	} while (c); \
1242	(src) = tmp; \
1243    } while (0)
1244#else
1245#define translit_separator(str) (void)(str)
1246#endif
1247
1248void*
1249dln_load(const char *file)
1250{
1251#if !defined(_AIX) && !defined(NeXT)
1252    const char *error = 0;
1253#define DLN_ERROR() (error = dln_strerror(), strcpy(ALLOCA_N(char, strlen(error) + 1), error))
1254#endif
1255
1256#if defined _WIN32 && !defined __CYGWIN__
1257    HINSTANCE handle;
1258    char winfile[MAXPATHLEN];
1259    char message[1024];
1260    void (*init_fct)();
1261    char *buf;
1262
1263    if (strlen(file) >= MAXPATHLEN) dln_loaderror("filename too long");
1264
1265    /* Load the file as an object one */
1266    init_funcname(&buf, file);
1267
1268    strlcpy(winfile, file, sizeof(winfile));
1269
1270    /* Load file */
1271    if ((handle = LoadLibrary(winfile)) == NULL) {
1272	error = dln_strerror();
1273	goto failed;
1274    }
1275
1276#if defined _WIN32 && defined RUBY_EXPORT
1277    if (!rb_w32_check_imported(handle, rb_libruby_handle())) {
1278	FreeLibrary(handle);
1279	error = "incompatible library version";
1280	goto failed;
1281    }
1282#endif
1283
1284    if ((init_fct = (void(*)())GetProcAddress(handle, buf)) == NULL) {
1285	dln_loaderror("%s - %s\n%s", dln_strerror(), buf, file);
1286    }
1287
1288    /* Call the init code */
1289    (*init_fct)();
1290    return handle;
1291#else
1292#ifdef USE_DLN_A_OUT
1293    if (load(file) == -1) {
1294	error = dln_strerror();
1295	goto failed;
1296    }
1297    return 0;
1298#else
1299
1300    char *buf;
1301    /* Load the file as an object one */
1302    init_funcname(&buf, file);
1303    translit_separator(file);
1304
1305#ifdef USE_DLN_DLOPEN
1306#define DLN_DEFINED
1307    {
1308	void *handle;
1309	void (*init_fct)();
1310
1311#ifndef RTLD_LAZY
1312# define RTLD_LAZY 1
1313#endif
1314#ifdef __INTERIX
1315# undef RTLD_GLOBAL
1316#endif
1317#ifndef RTLD_GLOBAL
1318# define RTLD_GLOBAL 0
1319#endif
1320
1321#ifdef __native_client__
1322	char* p, *orig;
1323        if (file[0] == '.' && file[1] == '/') file+=2;
1324	orig = strdup(file);
1325	for (p = file; *p; ++p) {
1326	    if (*p == '/') *p = '_';
1327	}
1328#endif
1329	/* Load file */
1330	if ((handle = (void*)dlopen(file, RTLD_LAZY|RTLD_GLOBAL)) == NULL) {
1331#ifdef __native_client__
1332            free(orig);
1333#endif
1334	    error = dln_strerror();
1335	    goto failed;
1336	}
1337
1338	init_fct = (void(*)())(VALUE)dlsym(handle, buf);
1339#ifdef __native_client__
1340	strcpy(file, orig);
1341	free(orig);
1342#endif
1343#if defined __SYMBIAN32__
1344	if (init_fct == NULL) {
1345	    init_fct = (void(*)())dlsym(handle, "1"); /* Some Symbian versions do not support symbol table in DLL, ordinal numbers only */
1346	}
1347#endif
1348	if (init_fct == NULL) {
1349	    error = DLN_ERROR();
1350	    dlclose(handle);
1351	    goto failed;
1352	}
1353	/* Call the init code */
1354	(*init_fct)();
1355
1356	return handle;
1357    }
1358#endif /* USE_DLN_DLOPEN */
1359
1360#ifdef __hpux
1361#define DLN_DEFINED
1362    {
1363	shl_t lib = NULL;
1364	int flags;
1365	void (*init_fct)();
1366
1367	flags = BIND_DEFERRED;
1368	lib = shl_load(file, flags, 0);
1369	if (lib == NULL) {
1370	    extern int errno;
1371	    dln_loaderror("%s - %s", strerror(errno), file);
1372	}
1373	shl_findsym(&lib, buf, TYPE_PROCEDURE, (void*)&init_fct);
1374	if (init_fct == NULL) {
1375	    shl_findsym(&lib, buf, TYPE_UNDEFINED, (void*)&init_fct);
1376	    if (init_fct == NULL) {
1377		errno = ENOSYM;
1378		dln_loaderror("%s - %s", strerror(ENOSYM), file);
1379	    }
1380	}
1381	(*init_fct)();
1382	return (void*)lib;
1383    }
1384#endif /* hpux */
1385
1386#if defined(_AIX) && ! defined(_IA64)
1387#define DLN_DEFINED
1388    {
1389	void (*init_fct)();
1390
1391	init_fct = (void(*)())load((char*)file, 1, 0);
1392	if (init_fct == NULL) {
1393	    aix_loaderror(file);
1394	}
1395	if (loadbind(0, (void*)dln_load, (void*)init_fct) == -1) {
1396	    aix_loaderror(file);
1397	}
1398	(*init_fct)();
1399	return (void*)init_fct;
1400    }
1401#endif /* _AIX */
1402
1403#if defined(MACOSX_DYLD)
1404#define DLN_DEFINED
1405/*----------------------------------------------------
1406   By SHIROYAMA Takayuki Psi@fortune.nest.or.jp
1407
1408   Special Thanks...
1409    Yu tomoak-i@is.aist-nara.ac.jp,
1410    Mi hisho@tasihara.nest.or.jp,
1411    sunshine@sunshineco.com,
1412    and... Miss ARAI Akino(^^;)
1413 ----------------------------------------------------*/
1414    {
1415	int dyld_result;
1416	NSObjectFileImage obj_file; /* handle, but not use it */
1417	/* "file" is module file name .
1418	   "buf" is pointer to initial function name with "_" . */
1419
1420	void (*init_fct)();
1421
1422
1423	dyld_result = NSCreateObjectFileImageFromFile(file, &obj_file);
1424
1425	if (dyld_result != NSObjectFileImageSuccess) {
1426	    dln_loaderror("Failed to load %.200s", file);
1427	}
1428
1429	NSLinkModule(obj_file, file, NSLINKMODULE_OPTION_BINDNOW);
1430
1431	/* lookup the initial function */
1432	if (!NSIsSymbolNameDefined(buf)) {
1433	    dln_loaderror("Failed to lookup Init function %.200s",file);
1434	}
1435	init_fct = NSAddressOfSymbol(NSLookupAndBindSymbol(buf));
1436	(*init_fct)();
1437
1438	return (void*)init_fct;
1439    }
1440#endif
1441
1442#if defined(__BEOS__) || defined(__HAIKU__)
1443# define DLN_DEFINED
1444    {
1445      status_t err_stat;  /* BeOS error status code */
1446      image_id img_id;    /* extension module unique id */
1447      void (*init_fct)(); /* initialize function for extension module */
1448
1449      /* load extension module */
1450      img_id = load_add_on(file);
1451      if (img_id <= 0) {
1452	dln_loaderror("Failed to load add_on %.200s error_code=%x",
1453	  file, img_id);
1454      }
1455
1456      /* find symbol for module initialize function. */
1457      /* The Be Book KernelKit Images section described to use
1458	 B_SYMBOL_TYPE_TEXT for symbol of function, not
1459	 B_SYMBOL_TYPE_CODE. Why ? */
1460      /* strcat(init_fct_symname, "__Fv"); */  /* parameter nothing. */
1461      /* "__Fv" dont need! The Be Book Bug ? */
1462      err_stat = get_image_symbol(img_id, buf,
1463				  B_SYMBOL_TYPE_TEXT, (void **)&init_fct);
1464
1465      if (err_stat != B_NO_ERROR) {
1466	char real_name[MAXPATHLEN];
1467
1468	strlcpy(real_name, buf, MAXPATHLEN);
1469	strlcat(real_name, "__Fv", MAXPATHLEN);
1470        err_stat = get_image_symbol(img_id, real_name,
1471				    B_SYMBOL_TYPE_TEXT, (void **)&init_fct);
1472      }
1473
1474      if ((B_BAD_IMAGE_ID == err_stat) || (B_BAD_INDEX == err_stat)) {
1475	unload_add_on(img_id);
1476	dln_loaderror("Failed to lookup Init function %.200s", file);
1477      }
1478      else if (B_NO_ERROR != err_stat) {
1479	char errmsg[] = "Internal of BeOS version. %.200s (symbol_name = %s)";
1480	unload_add_on(img_id);
1481	dln_loaderror(errmsg, strerror(err_stat), buf);
1482      }
1483
1484      /* call module initialize function. */
1485      (*init_fct)();
1486      return (void*)img_id;
1487    }
1488#endif /* __BEOS__ || __HAIKU__ */
1489
1490#ifndef DLN_DEFINED
1491    dln_notimplement();
1492#endif
1493
1494#endif /* USE_DLN_A_OUT */
1495#endif
1496#if !defined(_AIX) && !defined(NeXT)
1497  failed:
1498    dln_loaderror("%s - %s", error, file);
1499#endif
1500
1501    return 0;			/* dummy return */
1502}
1503