Permalink
To fix build failures.
/********************************************************************** | |
dln.c - | |
$Author$ | |
created at: Tue Jan 18 17:05:06 JST 1994 | |
Copyright (C) 1993-2007 Yukihiro Matsumoto | |
**********************************************************************/ | |
#ifdef RUBY_EXPORT | |
#include "ruby/ruby.h" | |
#define dln_notimplement rb_notimplement | |
#define dln_memerror rb_memerror | |
#define dln_exit rb_exit | |
#define dln_loaderror rb_loaderror | |
#else | |
#define dln_notimplement --->>> dln not implemented <<<--- | |
#define dln_memerror abort | |
#define dln_exit exit | |
static void dln_loaderror(const char *format, ...); | |
#endif | |
#include "dln.h" | |
#include "internal.h" | |
#ifdef HAVE_STDLIB_H | |
# include <stdlib.h> | |
#endif | |
#ifdef USE_DLN_A_OUT | |
char *dln_argv0; | |
#endif | |
#if defined(HAVE_ALLOCA_H) | |
#include <alloca.h> | |
#endif | |
#ifdef HAVE_STRING_H | |
# include <string.h> | |
#else | |
# include <strings.h> | |
#endif | |
#ifndef xmalloc | |
void *xmalloc(); | |
void *xcalloc(); | |
void *xrealloc(); | |
#endif | |
#undef free | |
#define free(x) xfree(x) | |
#include <stdio.h> | |
#if defined(_WIN32) | |
#include "missing/file.h" | |
#endif | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#ifndef S_ISDIR | |
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) | |
#endif | |
#ifdef HAVE_SYS_PARAM_H | |
# include <sys/param.h> | |
#endif | |
#ifndef MAXPATHLEN | |
# define MAXPATHLEN 1024 | |
#endif | |
#ifdef HAVE_UNISTD_H | |
# include <unistd.h> | |
#endif | |
#ifndef _WIN32 | |
char *getenv(); | |
#endif | |
#ifdef __APPLE__ | |
# if defined(HAVE_DLOPEN) | |
/* Mac OS X with dlopen (10.3 or later) */ | |
# define MACOSX_DLOPEN | |
# else | |
# define MACOSX_DYLD | |
# endif | |
#endif | |
#ifndef dln_loaderror | |
static void | |
dln_loaderror(const char *format, ...) | |
{ | |
va_list ap; | |
va_start(ap, format); | |
vfprintf(stderr, format, ap); | |
va_end(ap); | |
abort(); | |
} | |
#endif | |
#if defined(HAVE_DLOPEN) && !defined(USE_DLN_A_OUT) && !defined(_AIX) && !defined(MACOSX_DYLD) && !defined(_UNICOSMP) | |
/* dynamic load with dlopen() */ | |
# define USE_DLN_DLOPEN | |
#endif | |
#if defined(__hp9000s300) || ((defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)) && !defined(__ELF__)) || defined(NeXT) || defined(MACOSX_DYLD) | |
# define EXTERNAL_PREFIX "_" | |
#else | |
# define EXTERNAL_PREFIX "" | |
#endif | |
#define FUNCNAME_PREFIX EXTERNAL_PREFIX"Init_" | |
#if defined __CYGWIN__ || defined DOSISH | |
#define isdirsep(x) ((x) == '/' || (x) == '\\') | |
#else | |
#define isdirsep(x) ((x) == '/') | |
#endif | |
static size_t | |
init_funcname_len(const char **file) | |
{ | |
const char *p = *file, *base, *dot = NULL; | |
/* Load the file as an object one */ | |
for (base = p; *p; p++) { /* Find position of last '/' */ | |
if (*p == '.' && !dot) dot = p; | |
if (isdirsep(*p)) base = p+1, dot = NULL; | |
} | |
*file = base; | |
/* Delete suffix if it exists */ | |
return (dot ? dot : p) - base; | |
} | |
static const char funcname_prefix[sizeof(FUNCNAME_PREFIX) - 1] = FUNCNAME_PREFIX; | |
#define init_funcname(buf, file) do {\ | |
const char *base = (file);\ | |
const size_t flen = init_funcname_len(&base);\ | |
const size_t plen = sizeof(funcname_prefix);\ | |
char *const tmp = ALLOCA_N(char, plen+flen+1);\ | |
if (!tmp) {\ | |
dln_memerror();\ | |
}\ | |
memcpy(tmp, funcname_prefix, plen);\ | |
memcpy(tmp+plen, base, flen);\ | |
tmp[plen+flen] = '\0';\ | |
*(buf) = tmp;\ | |
} while (0) | |
#ifdef USE_DLN_A_OUT | |
#ifndef LIBC_NAME | |
# define LIBC_NAME "libc.a" | |
#endif | |
#ifndef DLN_DEFAULT_LIB_PATH | |
# define DLN_DEFAULT_LIB_PATH "/lib:/usr/lib:/usr/local/lib:." | |
#endif | |
#include <errno.h> | |
static int dln_errno; | |
#define DLN_ENOEXEC ENOEXEC /* Exec format error */ | |
#define DLN_ECONFL 1201 /* Symbol name conflict */ | |
#define DLN_ENOINIT 1202 /* No initializer given */ | |
#define DLN_EUNDEF 1203 /* Undefine symbol remains */ | |
#define DLN_ENOTLIB 1204 /* Not a library file */ | |
#define DLN_EBADLIB 1205 /* Malformed library file */ | |
#define DLN_EINIT 1206 /* Not initialized */ | |
static int dln_init_p = 0; | |
#include <ar.h> | |
#include <a.out.h> | |
#ifndef N_COMM | |
# define N_COMM 0x12 | |
#endif | |
#ifndef N_MAGIC | |
# define N_MAGIC(x) (x).a_magic | |
#endif | |
#define INVALID_OBJECT(h) (N_MAGIC(h) != OMAGIC) | |
#include "ruby/util.h" | |
#include "ruby/st.h" | |
static st_table *sym_tbl; | |
static st_table *undef_tbl; | |
static int load_lib(); | |
static int | |
load_header(int fd, struct exec *hdrp, long disp) | |
{ | |
int size; | |
lseek(fd, disp, 0); | |
size = read(fd, hdrp, sizeof(struct exec)); | |
if (size == -1) { | |
dln_errno = errno; | |
return -1; | |
} | |
if (size != sizeof(struct exec) || N_BADMAG(*hdrp)) { | |
dln_errno = DLN_ENOEXEC; | |
return -1; | |
} | |
return 0; | |
} | |
#if defined(sequent) | |
#define RELOC_SYMBOL(r) ((r)->r_symbolnum) | |
#define RELOC_MEMORY_SUB_P(r) ((r)->r_bsr) | |
#define RELOC_PCREL_P(r) ((r)->r_pcrel || (r)->r_bsr) | |
#define RELOC_TARGET_SIZE(r) ((r)->r_length) | |
#endif | |
/* Default macros */ | |
#ifndef RELOC_ADDRESS | |
#define RELOC_ADDRESS(r) ((r)->r_address) | |
#define RELOC_EXTERN_P(r) ((r)->r_extern) | |
#define RELOC_SYMBOL(r) ((r)->r_symbolnum) | |
#define RELOC_MEMORY_SUB_P(r) 0 | |
#define RELOC_PCREL_P(r) ((r)->r_pcrel) | |
#define RELOC_TARGET_SIZE(r) ((r)->r_length) | |
#endif | |
#if defined(__sun) && defined(__sparc) | |
/* Sparc (Sun 4) macros */ | |
# undef relocation_info | |
# define relocation_info reloc_info_sparc | |
# define R_RIGHTSHIFT(r) (reloc_r_rightshift[(r)->r_type]) | |
# define R_BITSIZE(r) (reloc_r_bitsize[(r)->r_type]) | |
# define R_LENGTH(r) (reloc_r_length[(r)->r_type]) | |
static const int reloc_r_rightshift[] = { | |
0, 0, 0, 0, 0, 0, 2, 2, 10, 0, 0, 0, 0, 0, 0, | |
}; | |
static const int reloc_r_bitsize[] = { | |
8, 16, 32, 8, 16, 32, 30, 22, 22, 22, 13, 10, 32, 32, 16, | |
}; | |
static const int reloc_r_length[] = { | |
0, 1, 2, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, | |
}; | |
# define R_PCREL(r) \ | |
((r)->r_type >= RELOC_DISP8 && (r)->r_type <= RELOC_WDISP22) | |
# define R_SYMBOL(r) ((r)->r_index) | |
#endif | |
#if defined(sequent) | |
#define R_SYMBOL(r) ((r)->r_symbolnum) | |
#define R_MEMORY_SUB(r) ((r)->r_bsr) | |
#define R_PCREL(r) ((r)->r_pcrel || (r)->r_bsr) | |
#define R_LENGTH(r) ((r)->r_length) | |
#endif | |
#ifndef R_SYMBOL | |
# define R_SYMBOL(r) ((r)->r_symbolnum) | |
# define R_MEMORY_SUB(r) 0 | |
# define R_PCREL(r) ((r)->r_pcrel) | |
# define R_LENGTH(r) ((r)->r_length) | |
#endif | |
static struct relocation_info * | |
load_reloc(int fd, struct exec *hdrp, long disp) | |
{ | |
struct relocation_info *reloc; | |
int size; | |
lseek(fd, disp + N_TXTOFF(*hdrp) + hdrp->a_text + hdrp->a_data, 0); | |
size = hdrp->a_trsize + hdrp->a_drsize; | |
reloc = (struct relocation_info*)xmalloc(size); | |
if (reloc == NULL) { | |
dln_errno = errno; | |
return NULL; | |
} | |
if (read(fd, reloc, size) != size) { | |
dln_errno = errno; | |
free(reloc); | |
return NULL; | |
} | |
return reloc; | |
} | |
static struct nlist * | |
load_sym(int fd, struct exec *hdrp, long disp) | |
{ | |
struct nlist * buffer; | |
struct nlist * sym; | |
struct nlist * end; | |
long displ; | |
int size; | |
lseek(fd, N_SYMOFF(*hdrp) + hdrp->a_syms + disp, 0); | |
if (read(fd, &size, sizeof(int)) != sizeof(int)) { | |
goto err_noexec; | |
} | |
buffer = (struct nlist*)xmalloc(hdrp->a_syms + size); | |
if (buffer == NULL) { | |
dln_errno = errno; | |
return NULL; | |
} | |
lseek(fd, disp + N_SYMOFF(*hdrp), 0); | |
if (read(fd, buffer, hdrp->a_syms + size) != hdrp->a_syms + size) { | |
free(buffer); | |
goto err_noexec; | |
} | |
sym = buffer; | |
end = sym + hdrp->a_syms / sizeof(struct nlist); | |
displ = (long)buffer + (long)(hdrp->a_syms); | |
while (sym < end) { | |
sym->n_un.n_name = (char*)sym->n_un.n_strx + displ; | |
sym++; | |
} | |
return buffer; | |
err_noexec: | |
dln_errno = DLN_ENOEXEC; | |
return NULL; | |
} | |
static st_table * | |
sym_hash(struct exec *hdrp, struct nlist *syms) | |
{ | |
st_table *tbl; | |
struct nlist *sym = syms; | |
struct nlist *end = syms + (hdrp->a_syms / sizeof(struct nlist)); | |
tbl = st_init_strtable(); | |
if (tbl == NULL) { | |
dln_errno = errno; | |
return NULL; | |
} | |
while (sym < end) { | |
st_insert(tbl, sym->n_un.n_name, sym); | |
sym++; | |
} | |
return tbl; | |
} | |
static int | |
dln_init(const char *prog) | |
{ | |
char *file, fbuf[MAXPATHLEN]; | |
int fd; | |
struct exec hdr; | |
struct nlist *syms; | |
if (dln_init_p == 1) return 0; | |
file = dln_find_exe_r(prog, NULL, fbuf, sizeof(fbuf)); | |
if (file == NULL || (fd = open(file, O_RDONLY)) < 0) { | |
dln_errno = errno; | |
return -1; | |
} | |
if (load_header(fd, &hdr, 0) == -1) return -1; | |
syms = load_sym(fd, &hdr, 0); | |
if (syms == NULL) { | |
close(fd); | |
return -1; | |
} | |
sym_tbl = sym_hash(&hdr, syms); | |
if (sym_tbl == NULL) { /* file may be start with #! */ | |
char c = '\0'; | |
char buf[MAXPATHLEN]; | |
char *p; | |
free(syms); | |
lseek(fd, 0L, 0); | |
if (read(fd, &c, 1) == -1) { | |
dln_errno = errno; | |
return -1; | |
} | |
if (c != '#') goto err_noexec; | |
if (read(fd, &c, 1) == -1) { | |
dln_errno = errno; | |
return -1; | |
} | |
if (c != '!') goto err_noexec; | |
p = buf; | |
/* skip forwarding spaces */ | |
while (read(fd, &c, 1) == 1) { | |
if (c == '\n') goto err_noexec; | |
if (c != '\t' && c != ' ') { | |
*p++ = c; | |
break; | |
} | |
} | |
/* read in command name */ | |
while (read(fd, p, 1) == 1) { | |
if (*p == '\n' || *p == '\t' || *p == ' ') break; | |
p++; | |
if (p-buf >= MAXPATHLEN) { | |
dln_errno = ENAMETOOLONG; | |
return -1; | |
} | |
} | |
*p = '\0'; | |
return dln_init(buf); | |
} | |
dln_init_p = 1; | |
undef_tbl = st_init_strtable(); | |
close(fd); | |
return 0; | |
err_noexec: | |
close(fd); | |
dln_errno = DLN_ENOEXEC; | |
return -1; | |
} | |
static long | |
load_text_data(int fd, struct exec *hdrp, int bss, long disp) | |
{ | |
int size; | |
unsigned char* addr; | |
lseek(fd, disp + N_TXTOFF(*hdrp), 0); | |
size = hdrp->a_text + hdrp->a_data; | |
if (bss == -1) size += hdrp->a_bss; | |
else if (bss > 1) size += bss; | |
addr = (unsigned char*)xmalloc(size); | |
if (addr == NULL) { | |
dln_errno = errno; | |
return 0; | |
} | |
if (read(fd, addr, size) != size) { | |
dln_errno = errno; | |
free(addr); | |
return 0; | |
} | |
if (bss == -1) { | |
memset(addr + hdrp->a_text + hdrp->a_data, 0, hdrp->a_bss); | |
} | |
else if (bss > 0) { | |
memset(addr + hdrp->a_text + hdrp->a_data, 0, bss); | |
} | |
return (long)addr; | |
} | |
static int | |
undef_print(char *key, char *value) | |
{ | |
fprintf(stderr, " %s\n", key); | |
return ST_CONTINUE; | |
} | |
static void | |
dln_print_undef(void) | |
{ | |
fprintf(stderr, " Undefined symbols:\n"); | |
st_foreach(undef_tbl, undef_print, NULL); | |
} | |
static void | |
dln_undefined(void) | |
{ | |
if (undef_tbl->num_entries > 0) { | |
fprintf(stderr, "dln: Calling undefined function\n"); | |
dln_print_undef(); | |
dln_exit(1); | |
} | |
} | |
struct undef { | |
char *name; | |
struct relocation_info reloc; | |
long base; | |
char *addr; | |
union { | |
char c; | |
short s; | |
long l; | |
} u; | |
}; | |
static st_table *reloc_tbl = NULL; | |
static void | |
link_undef(const char *name, long base, struct relocation_info *reloc) | |
{ | |
static int u_no = 0; | |
struct undef *obj; | |
char *addr = (char*)(reloc->r_address + base); | |
obj = (struct undef*)xmalloc(sizeof(struct undef)); | |
obj->name = strdup(name); | |
obj->reloc = *reloc; | |
obj->base = base; | |
switch (R_LENGTH(reloc)) { | |
case 0: /* byte */ | |
obj->u.c = *addr; | |
break; | |
case 1: /* word */ | |
obj->u.s = *(short*)addr; | |
break; | |
case 2: /* long */ | |
obj->u.l = *(long*)addr; | |
break; | |
} | |
if (reloc_tbl == NULL) { | |
reloc_tbl = st_init_numtable(); | |
} | |
st_insert(reloc_tbl, u_no++, obj); | |
} | |
struct reloc_arg { | |
const char *name; | |
long value; | |
}; | |
static int | |
reloc_undef(int no, struct undef *undef, struct reloc_arg *arg) | |
{ | |
int datum; | |
char *address; | |
#if defined(__sun) && defined(__sparc) | |
unsigned int mask = 0; | |
#endif | |
if (strcmp(arg->name, undef->name) != 0) return ST_CONTINUE; | |
address = (char*)(undef->base + undef->reloc.r_address); | |
datum = arg->value; | |
if (R_PCREL(&(undef->reloc))) datum -= undef->base; | |
#if defined(__sun) && defined(__sparc) | |
datum += undef->reloc.r_addend; | |
datum >>= R_RIGHTSHIFT(&(undef->reloc)); | |
mask = (1 << R_BITSIZE(&(undef->reloc))) - 1; | |
mask |= mask -1; | |
datum &= mask; | |
switch (R_LENGTH(&(undef->reloc))) { | |
case 0: | |
*address = undef->u.c; | |
*address &= ~mask; | |
*address |= datum; | |
break; | |
case 1: | |
*(short *)address = undef->u.s; | |
*(short *)address &= ~mask; | |
*(short *)address |= datum; | |
break; | |
case 2: | |
*(long *)address = undef->u.l; | |
*(long *)address &= ~mask; | |
*(long *)address |= datum; | |
break; | |
} | |
#else | |
switch (R_LENGTH(&(undef->reloc))) { | |
case 0: /* byte */ | |
if (R_MEMORY_SUB(&(undef->reloc))) | |
*address = datum - *address; | |
else *address = undef->u.c + datum; | |
break; | |
case 1: /* word */ | |
if (R_MEMORY_SUB(&(undef->reloc))) | |
*(short*)address = datum - *(short*)address; | |
else *(short*)address = undef->u.s + datum; | |
break; | |
case 2: /* long */ | |
if (R_MEMORY_SUB(&(undef->reloc))) | |
*(long*)address = datum - *(long*)address; | |
else *(long*)address = undef->u.l + datum; | |
break; | |
} | |
#endif | |
free(undef->name); | |
free(undef); | |
return ST_DELETE; | |
} | |
static void | |
unlink_undef(const char *name, long value) | |
{ | |
struct reloc_arg arg; | |
arg.name = name; | |
arg.value = value; | |
st_foreach(reloc_tbl, reloc_undef, &arg); | |
} | |
#ifdef N_INDR | |
struct indr_data { | |
char *name0, *name1; | |
}; | |
static int | |
reloc_repl(int no, struct undef *undef, struct indr_data *data) | |
{ | |
if (strcmp(data->name0, undef->name) == 0) { | |
free(undef->name); | |
undef->name = strdup(data->name1); | |
} | |
return ST_CONTINUE; | |
} | |
#endif | |
static int | |
load_1(int fd, long disp, const char *need_init) | |
{ | |
static const char *libc = LIBC_NAME; | |
struct exec hdr; | |
struct relocation_info *reloc = NULL; | |
long block = 0; | |
long new_common = 0; /* Length of new common */ | |
struct nlist *syms = NULL; | |
struct nlist *sym; | |
struct nlist *end; | |
int init_p = 0; | |
if (load_header(fd, &hdr, disp) == -1) return -1; | |
if (INVALID_OBJECT(hdr)) { | |
dln_errno = DLN_ENOEXEC; | |
return -1; | |
} | |
reloc = load_reloc(fd, &hdr, disp); | |
if (reloc == NULL) return -1; | |
syms = load_sym(fd, &hdr, disp); | |
if (syms == NULL) { | |
free(reloc); | |
return -1; | |
} | |
sym = syms; | |
end = syms + (hdr.a_syms / sizeof(struct nlist)); | |
while (sym < end) { | |
struct nlist *old_sym; | |
int value = sym->n_value; | |
#ifdef N_INDR | |
if (sym->n_type == (N_INDR | N_EXT)) { | |
char *key = sym->n_un.n_name; | |
if (st_lookup(sym_tbl, sym[1].n_un.n_name, &old_sym)) { | |
if (st_delete(undef_tbl, (st_data_t*)&key, NULL)) { | |
unlink_undef(key, old_sym->n_value); | |
free(key); | |
} | |
} | |
else { | |
struct indr_data data; | |
data.name0 = sym->n_un.n_name; | |
data.name1 = sym[1].n_un.n_name; | |
st_foreach(reloc_tbl, reloc_repl, &data); | |
st_insert(undef_tbl, strdup(sym[1].n_un.n_name), NULL); | |
if (st_delete(undef_tbl, (st_data_t*)&key, NULL)) { | |
free(key); | |
} | |
} | |
sym += 2; | |
continue; | |
} | |
#endif | |
if (sym->n_type == (N_UNDF | N_EXT)) { | |
if (st_lookup(sym_tbl, sym->n_un.n_name, &old_sym) == 0) { | |
old_sym = NULL; | |
} | |
if (value) { | |
if (old_sym) { | |
sym->n_type = N_EXT | N_COMM; | |
sym->n_value = old_sym->n_value; | |
} | |
else { | |
int rnd = | |
value >= sizeof(double) ? sizeof(double) - 1 | |
: value >= sizeof(long) ? sizeof(long) - 1 | |
: sizeof(short) - 1; | |
sym->n_type = N_COMM; | |
new_common += rnd; | |
new_common &= ~(long)rnd; | |
sym->n_value = new_common; | |
new_common += value; | |
} | |
} | |
else { | |
if (old_sym) { | |
sym->n_type = N_EXT | N_COMM; | |
sym->n_value = old_sym->n_value; | |
} | |
else { | |
sym->n_value = (long)dln_undefined; | |
st_insert(undef_tbl, strdup(sym->n_un.n_name), NULL); | |
} | |
} | |
} | |
sym++; | |
} | |
block = load_text_data(fd, &hdr, hdr.a_bss + new_common, disp); | |
if (block == 0) goto err_exit; | |
sym = syms; | |
while (sym < end) { | |
struct nlist *new_sym; | |
char *key; | |
switch (sym->n_type) { | |
case N_COMM: | |
sym->n_value += hdr.a_text + hdr.a_data; | |
case N_TEXT|N_EXT: | |
case N_DATA|N_EXT: | |
sym->n_value += block; | |
if (st_lookup(sym_tbl, sym->n_un.n_name, &new_sym) != 0 | |
&& new_sym->n_value != (long)dln_undefined) { | |
dln_errno = DLN_ECONFL; | |
goto err_exit; | |
} | |
key = sym->n_un.n_name; | |
if (st_delete(undef_tbl, (st_data_t*)&key, NULL) != 0) { | |
unlink_undef(key, sym->n_value); | |
free(key); | |
} | |
new_sym = (struct nlist*)xmalloc(sizeof(struct nlist)); | |
*new_sym = *sym; | |
new_sym->n_un.n_name = strdup(sym->n_un.n_name); | |
st_insert(sym_tbl, new_sym->n_un.n_name, new_sym); | |
break; | |
case N_TEXT: | |
case N_DATA: | |
sym->n_value += block; | |
break; | |
} | |
sym++; | |
} | |
/* | |
* First comes the text-relocation | |
*/ | |
{ | |
struct relocation_info * rel = reloc; | |
struct relocation_info * rel_beg = reloc + | |
(hdr.a_trsize/sizeof(struct relocation_info)); | |
struct relocation_info * rel_end = reloc + | |
(hdr.a_trsize+hdr.a_drsize)/sizeof(struct relocation_info); | |
while (rel < rel_end) { | |
char *address = (char*)(rel->r_address + block); | |
long datum = 0; | |
#if defined(__sun) && defined(__sparc) | |
unsigned int mask = 0; | |
#endif | |
if (rel >= rel_beg) | |
address += hdr.a_text; | |
if (rel->r_extern) { /* Look it up in symbol-table */ | |
sym = &(syms[R_SYMBOL(rel)]); | |
switch (sym->n_type) { | |
case N_EXT|N_UNDF: | |
link_undef(sym->n_un.n_name, block, rel); | |
case N_EXT|N_COMM: | |
case N_COMM: | |
datum = sym->n_value; | |
break; | |
default: | |
goto err_exit; | |
} | |
} /* end.. look it up */ | |
else { /* is static */ | |
switch (R_SYMBOL(rel)) { | |
case N_TEXT: | |
case N_DATA: | |
datum = block; | |
break; | |
case N_BSS: | |
datum = block + new_common; | |
break; | |
case N_ABS: | |
break; | |
} | |
} /* end .. is static */ | |
if (R_PCREL(rel)) datum -= block; | |
#if defined(__sun) && defined(__sparc) | |
datum += rel->r_addend; | |
datum >>= R_RIGHTSHIFT(rel); | |
mask = (1 << R_BITSIZE(rel)) - 1; | |
mask |= mask -1; | |
datum &= mask; | |
switch (R_LENGTH(rel)) { | |
case 0: | |
*address &= ~mask; | |
*address |= datum; | |
break; | |
case 1: | |
*(short *)address &= ~mask; | |
*(short *)address |= datum; | |
break; | |
case 2: | |
*(long *)address &= ~mask; | |
*(long *)address |= datum; | |
break; | |
} | |
#else | |
switch (R_LENGTH(rel)) { | |
case 0: /* byte */ | |
if (datum < -128 || datum > 127) goto err_exit; | |
*address += datum; | |
break; | |
case 1: /* word */ | |
*(short *)address += datum; | |
break; | |
case 2: /* long */ | |
*(long *)address += datum; | |
break; | |
} | |
#endif | |
rel++; | |
} | |
} | |
if (need_init) { | |
int len; | |
char **libs_to_be_linked = 0; | |
char *buf; | |
if (undef_tbl->num_entries > 0) { | |
if (load_lib(libc) == -1) goto err_exit; | |
} | |
init_funcname(&buf, need_init); | |
len = strlen(buf); | |
for (sym = syms; sym<end; sym++) { | |
char *name = sym->n_un.n_name; | |
if (name[0] == '_' && sym->n_value >= block) { | |
if (strcmp(name+1, "dln_libs_to_be_linked") == 0) { | |
libs_to_be_linked = (char**)sym->n_value; | |
} | |
else if (strcmp(name+1, buf) == 0) { | |
init_p = 1; | |
((int (*)())sym->n_value)(); | |
} | |
} | |
} | |
if (libs_to_be_linked && undef_tbl->num_entries > 0) { | |
while (*libs_to_be_linked) { | |
load_lib(*libs_to_be_linked); | |
libs_to_be_linked++; | |
} | |
} | |
} | |
free(reloc); | |
free(syms); | |
if (need_init) { | |
if (init_p == 0) { | |
dln_errno = DLN_ENOINIT; | |
return -1; | |
} | |
if (undef_tbl->num_entries > 0) { | |
if (load_lib(libc) == -1) goto err_exit; | |
if (undef_tbl->num_entries > 0) { | |
dln_errno = DLN_EUNDEF; | |
return -1; | |
} | |
} | |
} | |
return 0; | |
err_exit: | |
if (syms) free(syms); | |
if (reloc) free(reloc); | |
if (block) free((char*)block); | |
return -1; | |
} | |
static int target_offset; | |
static int | |
search_undef(const char *key, int value, st_table *lib_tbl) | |
{ | |
long offset; | |
if (st_lookup(lib_tbl, key, &offset) == 0) return ST_CONTINUE; | |
target_offset = offset; | |
return ST_STOP; | |
} | |
struct symdef { | |
int rb_str_index; | |
int lib_offset; | |
}; | |
const char *dln_librrb_ary_path = DLN_DEFAULT_LIB_PATH; | |
static int | |
load_lib(const char *lib) | |
{ | |
char *path, *file, fbuf[MAXPATHLEN]; | |
char *envpath = 0; | |
char armagic[SARMAG]; | |
int fd, size; | |
struct ar_hdr ahdr; | |
st_table *lib_tbl = NULL; | |
int *data, nsym; | |
struct symdef *base; | |
char *name_base; | |
if (dln_init_p == 0) { | |
dln_errno = DLN_ENOINIT; | |
return -1; | |
} | |
if (undef_tbl->num_entries == 0) return 0; | |
dln_errno = DLN_EBADLIB; | |
if (lib[0] == '-' && lib[1] == 'l') { | |
long len = strlen(lib) + 4; | |
char *p = alloca(len); | |
snprintf(p, len, "lib%s.a", lib+2); | |
lib = p; | |
} | |
/* library search path: */ | |
/* look for environment variable DLN_LIBRARY_PATH first. */ | |
/* then variable dln_librrb_ary_path. */ | |
/* if path is still NULL, use "." for path. */ | |
path = getenv("DLN_LIBRARY_PATH"); | |
if (path == NULL) path = dln_librrb_ary_path; | |
else path = envpath = strdup(path); | |
file = dln_find_file_r(lib, path, fbuf, sizeof(fbuf)); | |
if (envpath) free(envpath); | |
fd = open(file, O_RDONLY); | |
if (fd == -1) goto syserr; | |
size = read(fd, armagic, SARMAG); | |
if (size == -1) goto syserr; | |
if (size != SARMAG) { | |
dln_errno = DLN_ENOTLIB; | |
goto badlib; | |
} | |
size = read(fd, &ahdr, sizeof(ahdr)); | |
if (size == -1) goto syserr; | |
if (size != sizeof(ahdr) || sscanf(ahdr.ar_size, "%d", &size) != 1) { | |
goto badlib; | |
} | |
if (strncmp(ahdr.ar_name, "__.SYMDEF", 9) == 0) { | |
/* make hash table from __.SYMDEF */ | |
lib_tbl = st_init_strtable(); | |
data = (int*)xmalloc(size); | |
if (data == NULL) goto syserr; | |
size = read(fd, data, size); | |
nsym = *data / sizeof(struct symdef); | |
base = (struct symdef*)(data + 1); | |
name_base = (char*)(base + nsym) + sizeof(int); | |
while (nsym > 0) { | |
char *name = name_base + base->rb_str_index; | |
st_insert(lib_tbl, name, base->lib_offset + sizeof(ahdr)); | |
nsym--; | |
base++; | |
} | |
for (;;) { | |
target_offset = -1; | |
st_foreach(undef_tbl, search_undef, lib_tbl); | |
if (target_offset == -1) break; | |
if (load_1(fd, target_offset, 0) == -1) { | |
st_free_table(lib_tbl); | |
free(data); | |
goto badlib; | |
} | |
if (undef_tbl->num_entries == 0) break; | |
} | |
free(data); | |
st_free_table(lib_tbl); | |
} | |
else { | |
/* linear library, need to scan (FUTURE) */ | |
for (;;) { | |
int offset = SARMAG; | |
int found = 0; | |
struct exec hdr; | |
struct nlist *syms, *sym, *end; | |
while (undef_tbl->num_entries > 0) { | |
found = 0; | |
lseek(fd, offset, 0); | |
size = read(fd, &ahdr, sizeof(ahdr)); | |
if (size == -1) goto syserr; | |
if (size == 0) break; | |
if (size != sizeof(ahdr) | |
|| sscanf(ahdr.ar_size, "%d", &size) != 1) { | |
goto badlib; | |
} | |
offset += sizeof(ahdr); | |
if (load_header(fd, &hdr, offset) == -1) | |
goto badlib; | |
syms = load_sym(fd, &hdr, offset); | |
if (syms == NULL) goto badlib; | |
sym = syms; | |
end = syms + (hdr.a_syms / sizeof(struct nlist)); | |
while (sym < end) { | |
if (sym->n_type == N_EXT|N_TEXT | |
&& st_lookup(undef_tbl, sym->n_un.n_name, NULL)) { | |
break; | |
} | |
sym++; | |
} | |
if (sym < end) { | |
found++; | |
free(syms); | |
if (load_1(fd, offset, 0) == -1) { | |
goto badlib; | |
} | |
} | |
offset += size; | |
if (offset & 1) offset++; | |
} | |
if (found) break; | |
} | |
} | |
close(fd); | |
return 0; | |
syserr: | |
dln_errno = errno; | |
badlib: | |
if (fd >= 0) close(fd); | |
return -1; | |
} | |
static int | |
load(const char *file) | |
{ | |
int fd; | |
int result; | |
if (dln_init_p == 0) { | |
if (dln_init(dln_argv0) == -1) return -1; | |
} | |
result = strlen(file); | |
if (file[result-1] == 'a') { | |
return load_lib(file); | |
} | |
fd = open(file, O_RDONLY); | |
if (fd == -1) { | |
dln_errno = errno; | |
return -1; | |
} | |
result = load_1(fd, 0, file); | |
close(fd); | |
return result; | |
} | |
void* | |
dln_sym(const char *name) | |
{ | |
struct nlist *sym; | |
if (st_lookup(sym_tbl, name, &sym)) | |
return (void*)sym->n_value; | |
return NULL; | |
} | |
#endif /* USE_DLN_A_OUT */ | |
#ifdef USE_DLN_DLOPEN | |
# include <dlfcn.h> | |
#endif | |
#ifdef __hpux | |
#include <errno.h> | |
#include "dl.h" | |
#endif | |
#if defined(_AIX) | |
#include <ctype.h> /* for isdigit() */ | |
#include <errno.h> /* for global errno */ | |
#include <sys/ldr.h> | |
#endif | |
#ifdef NeXT | |
#if NS_TARGET_MAJOR < 4 | |
#include <mach-o/rld.h> | |
#else | |
#include <mach-o/dyld.h> | |
#ifndef NSLINKMODULE_OPTION_BINDNOW | |
#define NSLINKMODULE_OPTION_BINDNOW 1 | |
#endif | |
#endif | |
#else | |
#ifdef MACOSX_DYLD | |
#include <mach-o/dyld.h> | |
#endif | |
#endif | |
#ifdef _WIN32 | |
#include <windows.h> | |
#include <imagehlp.h> | |
#endif | |
#ifdef _WIN32 | |
static const char * | |
dln_strerror(char *message, size_t size) | |
{ | |
int error = GetLastError(); | |
char *p = message; | |
size_t len = snprintf(message, size, "%d: ", error); | |
#define format_message(sublang) FormatMessage(\ | |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, \ | |
NULL, error, MAKELANGID(LANG_NEUTRAL, (sublang)), \ | |
message + len, size - len, NULL) | |
if (format_message(SUBLANG_ENGLISH_US) == 0) | |
format_message(SUBLANG_DEFAULT); | |
for (p = message + len; *p; p++) { | |
if (*p == '\n' || *p == '\r') | |
*p = ' '; | |
} | |
return message; | |
} | |
#define dln_strerror() dln_strerror(message, sizeof message) | |
#elif ! defined _AIX | |
static const char * | |
dln_strerror(void) | |
{ | |
#ifdef USE_DLN_A_OUT | |
char *strerror(); | |
switch (dln_errno) { | |
case DLN_ECONFL: | |
return "Symbol name conflict"; | |
case DLN_ENOINIT: | |
return "No initializer given"; | |
case DLN_EUNDEF: | |
return "Unresolved symbols"; | |
case DLN_ENOTLIB: | |
return "Not a library file"; | |
case DLN_EBADLIB: | |
return "Malformed library file"; | |
case DLN_EINIT: | |
return "Not initialized"; | |
default: | |
return strerror(dln_errno); | |
} | |
#endif | |
#ifdef USE_DLN_DLOPEN | |
return (char*)dlerror(); | |
#endif | |
} | |
#endif | |
#if defined(_AIX) | |
static void | |
aix_loaderror(const char *pathname) | |
{ | |
char *message[1024], errbuf[1024]; | |
int i; | |
#define ERRBUF_APPEND(s) strlcat(errbuf, (s), sizeof(errbuf)) | |
snprintf(errbuf, sizeof(errbuf), "load failed - %s. ", pathname); | |
if (loadquery(L_GETMESSAGES, &message[0], sizeof(message)) != -1) { | |
ERRBUF_APPEND("Please issue below command for detailed reasons:\n\t"); | |
ERRBUF_APPEND("/usr/sbin/execerror ruby "); | |
for (i=0; message[i]; i++) { | |
ERRBUF_APPEND("\""); | |
ERRBUF_APPEND(message[i]); | |
ERRBUF_APPEND("\" "); | |
} | |
ERRBUF_APPEND("\n"); | |
} | |
else { | |
ERRBUF_APPEND(strerror(errno)); | |
ERRBUF_APPEND("[loadquery failed]"); | |
} | |
dln_loaderror("%s", errbuf); | |
} | |
#endif | |
#if defined _WIN32 && defined RUBY_EXPORT | |
HANDLE rb_libruby_handle(void); | |
static int | |
rb_w32_check_imported(HMODULE ext, HMODULE mine) | |
{ | |
ULONG size; | |
const IMAGE_IMPORT_DESCRIPTOR *desc; | |
desc = ImageDirectoryEntryToData(ext, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size); | |
if (!desc) return 0; | |
while (desc->Name) { | |
PIMAGE_THUNK_DATA pint = (PIMAGE_THUNK_DATA)((char *)ext + desc->Characteristics); | |
PIMAGE_THUNK_DATA piat = (PIMAGE_THUNK_DATA)((char *)ext + desc->FirstThunk); | |
for (; piat->u1.Function; piat++, pint++) { | |
static const char prefix[] = "rb_"; | |
PIMAGE_IMPORT_BY_NAME pii; | |
const char *name; | |
if (IMAGE_SNAP_BY_ORDINAL(pint->u1.Ordinal)) continue; | |
pii = (PIMAGE_IMPORT_BY_NAME)((char *)ext + (size_t)pint->u1.AddressOfData); | |
name = (const char *)pii->Name; | |
if (strncmp(name, prefix, sizeof(prefix) - 1) == 0) { | |
FARPROC addr = GetProcAddress(mine, name); | |
if (addr) return (FARPROC)piat->u1.Function == addr; | |
} | |
} | |
desc++; | |
} | |
return 1; | |
} | |
#endif | |
#if defined(DLN_NEEDS_ALT_SEPARATOR) && DLN_NEEDS_ALT_SEPARATOR | |
#define translit_separator(src) do { \ | |
char *tmp = ALLOCA_N(char, strlen(src) + 1), *p = tmp, c; \ | |
do { \ | |
*p++ = ((c = *file++) == '/') ? DLN_NEEDS_ALT_SEPARATOR : c; \ | |
} while (c); \ | |
(src) = tmp; \ | |
} while (0) | |
#else | |
#define translit_separator(str) (void)(str) | |
#endif | |
#ifdef USE_DLN_DLOPEN | |
# include "ruby/internal/stdbool.h" | |
# include "internal/warnings.h" | |
COMPILER_WARNING_PUSH | |
#if defined(__clang__) || GCC_VERSION_SINCE(4, 2, 0) | |
COMPILER_WARNING_IGNORED(-Wpedantic) | |
#endif | |
static bool | |
dln_incompatible_library_p(void *handle) | |
{ | |
void *ex = dlsym(handle, EXTERNAL_PREFIX"ruby_xmalloc"); | |
void *const fp = (void *)ruby_xmalloc; | |
return ex && ex != fp; | |
} | |
COMPILER_WARNING_POP | |
#endif | |
void* | |
dln_load(const char *file) | |
{ | |
#if (defined _WIN32 || defined USE_DLN_DLOPEN) && defined RUBY_EXPORT | |
static const char incompatible[] = "incompatible library version"; | |
#endif | |
#if !defined(_AIX) && !defined(NeXT) | |
const char *error = 0; | |
#endif | |
#if defined _WIN32 | |
HINSTANCE handle; | |
WCHAR *winfile; | |
char message[1024]; | |
void (*init_fct)(); | |
char *buf; | |
/* Load the file as an object one */ | |
init_funcname(&buf, file); | |
/* Convert the file path to wide char */ | |
winfile = rb_w32_mbstr_to_wstr(CP_UTF8, file, -1, NULL); | |
if (!winfile) { | |
dln_memerror(); | |
} | |
/* Load file */ | |
handle = LoadLibraryW(winfile); | |
free(winfile); | |
if (!handle) { | |
error = dln_strerror(); | |
goto failed; | |
} | |
#if defined _WIN32 && defined RUBY_EXPORT | |
if (!rb_w32_check_imported(handle, rb_libruby_handle())) { | |
FreeLibrary(handle); | |
error = incompatible; | |
goto failed; | |
} | |
#endif | |
if ((init_fct = (void(*)())GetProcAddress(handle, buf)) == NULL) { | |
dln_loaderror("%s - %s\n%s", dln_strerror(), buf, file); | |
} | |
/* Call the init code */ | |
(*init_fct)(); | |
return handle; | |
#else | |
#ifdef USE_DLN_A_OUT | |
if (load(file) == -1) { | |
error = dln_strerror(); | |
goto failed; | |
} | |
return 0; | |
#else | |
char *buf; | |
/* Load the file as an object one */ | |
init_funcname(&buf, file); | |
translit_separator(file); | |
#ifdef USE_DLN_DLOPEN | |
#define DLN_DEFINED | |
{ | |
void *handle; | |
void (*init_fct)(); | |
#ifndef RTLD_LAZY | |
# define RTLD_LAZY 1 | |
#endif | |
#ifdef __INTERIX | |
# undef RTLD_GLOBAL | |
#endif | |
#ifndef RTLD_GLOBAL | |
# define RTLD_GLOBAL 0 | |
#endif | |
/* Load file */ | |
if ((handle = (void*)dlopen(file, RTLD_LAZY|RTLD_GLOBAL)) == NULL) { | |
error = dln_strerror(); | |
goto failed; | |
} | |
# if defined RUBY_EXPORT | |
{ | |
if (dln_incompatible_library_p(handle)) { | |
# if defined __APPLE__ && \ | |
defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ | |
(MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11) | |
/* dlclose() segfaults */ | |
rb_fatal("%s - %s", incompatible, file); | |
# else | |
dlclose(handle); | |
error = incompatible; | |
goto failed; | |
# endif | |
} | |
} | |
# endif | |
init_fct = (void(*)())(VALUE)dlsym(handle, buf); | |
if (init_fct == NULL) { | |
const size_t errlen = strlen(error = dln_strerror()) + 1; | |
error = memcpy(ALLOCA_N(char, errlen), error, errlen); | |
dlclose(handle); | |
goto failed; | |
} | |
/* Call the init code */ | |
(*init_fct)(); | |
return handle; | |
} | |
#endif /* USE_DLN_DLOPEN */ | |
#ifdef __hpux | |
#define DLN_DEFINED | |
{ | |
shl_t lib = NULL; | |
int flags; | |
void (*init_fct)(); | |
flags = BIND_DEFERRED; | |
lib = shl_load(file, flags, 0); | |
if (lib == NULL) { | |
extern int errno; | |
dln_loaderror("%s - %s", strerror(errno), file); | |
} | |
shl_findsym(&lib, buf, TYPE_PROCEDURE, (void*)&init_fct); | |
if (init_fct == NULL) { | |
shl_findsym(&lib, buf, TYPE_UNDEFINED, (void*)&init_fct); | |
if (init_fct == NULL) { | |
errno = ENOSYM; | |
dln_loaderror("%s - %s", strerror(ENOSYM), file); | |
} | |
} | |
(*init_fct)(); | |
return (void*)lib; | |
} | |
#endif /* hpux */ | |
#if defined(_AIX) | |
#define DLN_DEFINED | |
{ | |
void (*init_fct)(); | |
init_fct = (void(*)())load((char*)file, 1, 0); | |
if (init_fct == NULL) { | |
aix_loaderror(file); | |
} | |
if (loadbind(0, (void*)dln_load, (void*)init_fct) == -1) { | |
aix_loaderror(file); | |
} | |
(*init_fct)(); | |
return (void*)init_fct; | |
} | |
#endif /* _AIX */ | |
#if defined(MACOSX_DYLD) | |
#define DLN_DEFINED | |
/*---------------------------------------------------- | |
By SHIROYAMA Takayuki Psi@fortune.nest.or.jp | |
Special Thanks... | |
Yu tomoak-i@is.aist-nara.ac.jp, | |
Mi hisho@tasihara.nest.or.jp, | |
sunshine@sunshineco.com, | |
and... Miss ARAI Akino(^^;) | |
----------------------------------------------------*/ | |
{ | |
int dyld_result; | |
NSObjectFileImage obj_file; /* handle, but not use it */ | |
/* "file" is module file name . | |
"buf" is pointer to initial function name with "_" . */ | |
void (*init_fct)(); | |
dyld_result = NSCreateObjectFileImageFromFile(file, &obj_file); | |
if (dyld_result != NSObjectFileImageSuccess) { | |
dln_loaderror("Failed to load %.200s", file); | |
} | |
NSLinkModule(obj_file, file, NSLINKMODULE_OPTION_BINDNOW); | |
/* lookup the initial function */ | |
if (!NSIsSymbolNameDefined(buf)) { | |
dln_loaderror("Failed to lookup Init function %.200s",file); | |
} | |
init_fct = NSAddressOfSymbol(NSLookupAndBindSymbol(buf)); | |
(*init_fct)(); | |
return (void*)init_fct; | |
} | |
#endif | |
#ifndef DLN_DEFINED | |
dln_notimplement(); | |
#endif | |
#endif /* USE_DLN_A_OUT */ | |
#endif | |
#if !defined(_AIX) && !defined(NeXT) | |
failed: | |
dln_loaderror("%s - %s", error, file); | |
#endif | |
return 0; /* dummy return */ | |
} |