mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 20:00:19 +02:00
data. (dynl_smob): Use it. * dynl.c (scm_dynamic_link): Moved allocating of the memory for the dynamic object data below the linking of the object to avoid memory leak when the linking code throws an error. Now the code leaks a whole dynamically linked library when must_malloc throws, but that should be much less likely.
470 lines
12 KiB
C
470 lines
12 KiB
C
/* dynl.c - dynamic linking
|
|
*
|
|
* Copyright (C) 1990-1997 Free Software Foundation, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this software; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
|
* Boston, MA 02111-1307 USA
|
|
*
|
|
* As a special exception, the Free Software Foundation gives permission
|
|
* for additional uses of the text contained in its release of GUILE.
|
|
*
|
|
* The exception is that, if you link the GUILE library with other files
|
|
* to produce an executable, this does not by itself cause the
|
|
* resulting executable to be covered by the GNU General Public License.
|
|
* Your use of that executable is in no way restricted on account of
|
|
* linking the GUILE library code into it.
|
|
*
|
|
* This exception does not however invalidate any other reasons why
|
|
* the executable file might be covered by the GNU General Public License.
|
|
*
|
|
* This exception applies only to the code released by the
|
|
* Free Software Foundation under the name GUILE. If you copy
|
|
* code from other Free Software Foundation releases into a copy of
|
|
* GUILE, as the General Public License permits, the exception does
|
|
* not apply to the code that you add in this way. To avoid misleading
|
|
* anyone as to the status of such modified files, you must delete
|
|
* this exception notice from them.
|
|
*
|
|
* If you write modifications of your own for GUILE, it is your choice
|
|
* whether to permit this exception to apply to your modifications.
|
|
* If you do not wish that, delete this exception notice. */
|
|
|
|
/* "dynl.c" dynamically link&load object files.
|
|
Author: Aubrey Jaffer
|
|
Modified for libguile by Marius Vollmer */
|
|
|
|
/* XXX - This is only here to drag in a definition of __eprintf. This
|
|
is needed for proper operation of dynamic linking. The real
|
|
solution would probably be a shared libgcc. */
|
|
|
|
#undef NDEBUG
|
|
#include <assert.h>
|
|
|
|
static void
|
|
maybe_drag_in_eprintf ()
|
|
{
|
|
assert (!maybe_drag_in_eprintf);
|
|
}
|
|
|
|
#include <stdio.h>
|
|
#include "_scm.h"
|
|
#include "dynl.h"
|
|
#include "genio.h"
|
|
#include "smob.h"
|
|
|
|
/* Converting a list of SCM strings into a argv-style array. You must
|
|
have ints disabled for the whole lifetime of the created argv (from
|
|
before MAKE_ARGV_FROM_STRINGLIST until after
|
|
MUST_FREE_ARGV). Atleast this is was the documentation for
|
|
MAKARGVFROMSTRS says, it isn't really used that way.
|
|
|
|
This code probably belongs into strings.c */
|
|
|
|
static char **scm_make_argv_from_stringlist SCM_P ((SCM args, int *argcp,
|
|
char *subr, int argn));
|
|
|
|
static char **
|
|
scm_make_argv_from_stringlist (args, argcp, subr, argn)
|
|
SCM args;
|
|
int *argcp;
|
|
char *subr;
|
|
int argn;
|
|
{
|
|
char **argv;
|
|
int argc, i;
|
|
|
|
argc = scm_ilength(args);
|
|
argv = (char **) scm_must_malloc ((1L+argc)*sizeof(char *), subr);
|
|
for(i = 0; SCM_NNULLP (args); args = SCM_CDR (args), i++) {
|
|
size_t len;
|
|
char *dst, *src;
|
|
SCM str = SCM_CAR (args);
|
|
|
|
SCM_ASSERT (SCM_NIMP (str) && SCM_ROSTRINGP (str), str, argn, subr);
|
|
len = 1 + SCM_ROLENGTH (str);
|
|
dst = (char *) scm_must_malloc ((long)len, subr);
|
|
src = SCM_ROCHARS (str);
|
|
while (len--)
|
|
dst[len] = src[len];
|
|
argv[i] = dst;
|
|
}
|
|
|
|
if (argcp)
|
|
*argcp = argc;
|
|
argv[argc] = 0;
|
|
return argv;
|
|
}
|
|
|
|
static void scm_must_free_argv SCM_P ((char **argv));
|
|
|
|
static void
|
|
scm_must_free_argv(argv)
|
|
char **argv;
|
|
{
|
|
char **av = argv;
|
|
while(!(*av))
|
|
free(*(av++));
|
|
free(argv);
|
|
}
|
|
|
|
/* Coerce an arbitrary readonly-string into a zero-terminated string.
|
|
*/
|
|
|
|
static SCM scm_coerce_rostring SCM_P ((SCM rostr, char *subr, int argn));
|
|
|
|
static SCM
|
|
scm_coerce_rostring (rostr, subr, argn)
|
|
SCM rostr;
|
|
char *subr;
|
|
int argn;
|
|
{
|
|
SCM_ASSERT (SCM_NIMP (rostr) && SCM_ROSTRINGP (rostr), rostr, argn, subr);
|
|
if (SCM_SUBSTRP (rostr))
|
|
rostr = scm_makfromstr (SCM_ROCHARS (rostr), SCM_ROLENGTH (rostr), 0);
|
|
return rostr;
|
|
}
|
|
|
|
/* Module registry
|
|
*/
|
|
|
|
/* We can't use SCM objects here. One should be able to call
|
|
SCM_REGISTER_MODULE from a C++ constructor for a static
|
|
object. This happens before main and thus before libguile is
|
|
initialized. */
|
|
|
|
struct moddata {
|
|
struct moddata *link;
|
|
char *module_name;
|
|
void *init_func;
|
|
};
|
|
|
|
static struct moddata *registered_mods = NULL;
|
|
|
|
void
|
|
scm_register_module_xxx (module_name, init_func)
|
|
char *module_name;
|
|
void *init_func;
|
|
{
|
|
struct moddata *md;
|
|
|
|
/* XXX - should we (and can we) DEFER_INTS here? */
|
|
|
|
for (md = registered_mods; md; md = md->link)
|
|
if (!strcmp (md->module_name, module_name)) {
|
|
md->init_func = init_func;
|
|
return;
|
|
}
|
|
|
|
md = (struct moddata *)malloc (sizeof (struct moddata));
|
|
if (md == NULL) {
|
|
fprintf (stderr,
|
|
"guile: can't register module (%s): not enough memory",
|
|
module_name);
|
|
return;
|
|
}
|
|
|
|
md->module_name = module_name;
|
|
md->init_func = init_func;
|
|
md->link = registered_mods;
|
|
registered_mods = md;
|
|
}
|
|
|
|
SCM_PROC (s_registered_modules, "c-registered-modules", 0, 0, 0, scm_registered_modules);
|
|
|
|
SCM
|
|
scm_registered_modules ()
|
|
{
|
|
SCM res;
|
|
struct moddata *md;
|
|
|
|
res = SCM_EOL;
|
|
for (md = registered_mods; md; md = md->link)
|
|
res = scm_cons (scm_cons (scm_makfrom0str (md->module_name),
|
|
scm_ulong2num ((unsigned long) md->init_func)),
|
|
res);
|
|
return res;
|
|
}
|
|
|
|
SCM_PROC (s_clear_registered_modules, "c-clear-registered-modules", 0, 0, 0, scm_clear_registered_modules);
|
|
|
|
SCM
|
|
scm_clear_registered_modules ()
|
|
{
|
|
struct moddata *md1, *md2;
|
|
|
|
SCM_DEFER_INTS;
|
|
|
|
for (md1 = registered_mods; md1; md1 = md2) {
|
|
md2 = md1->link;
|
|
free (md1);
|
|
}
|
|
registered_mods = NULL;
|
|
|
|
SCM_ALLOW_INTS;
|
|
return SCM_UNSPECIFIED;
|
|
}
|
|
|
|
/* Dispatch to the system dependent files
|
|
*
|
|
* They define some static functions. These functions are called with
|
|
* deferred interrupts. When they want to throw errors, they are
|
|
* expected to insert a SCM_ALLOW_INTS before doing the throw. It
|
|
* might work to throw an error while interrupts are deferred (because
|
|
* they will be unconditionally allowed the next time a SCM_ALLOW_INTS
|
|
* is executed, SCM_DEFER_INTS and SCM_ALLOW_INTS do not nest).
|
|
*/
|
|
|
|
static void sysdep_dynl_init SCM_P ((void));
|
|
static void *sysdep_dynl_link SCM_P ((char *filename, char *subr));
|
|
static void sysdep_dynl_unlink SCM_P ((void *handle, char *subr));
|
|
static void *sysdep_dynl_func SCM_P ((char *symbol, void *handle, char *subr));
|
|
|
|
#ifdef HAVE_LIBDL
|
|
#include "dynl-dl.c"
|
|
#else
|
|
#ifdef HAVE_SHL_LOAD
|
|
#include "dynl-shl.c"
|
|
#else
|
|
#ifdef HAVE_DLD
|
|
#include "dynl-dld.c"
|
|
#else
|
|
|
|
/* no dynamic linking available, throw errors. */
|
|
|
|
static void
|
|
sysdep_dynl_init ()
|
|
{
|
|
}
|
|
|
|
static void
|
|
no_dynl_error (subr)
|
|
char *subr;
|
|
{
|
|
SCM_ALLOW_INTS;
|
|
scm_misc_error (subr, "dynamic linking not available", SCM_EOL);
|
|
}
|
|
|
|
static void *
|
|
sysdep_dynl_link (filename, subr)
|
|
char *filename;
|
|
char *subr;
|
|
{
|
|
no_dynl_error (subr);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
sysdep_dynl_unlink (handle, subr)
|
|
void *handle;
|
|
char *subr;
|
|
{
|
|
no_dynl_error (subr);
|
|
}
|
|
|
|
static void *
|
|
sysdep_dynl_func (symbol, handle, subr)
|
|
char *symbol;
|
|
void *handle;
|
|
char *subr;
|
|
{
|
|
no_dynl_error (subr);
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
int scm_tc16_dynamic_obj;
|
|
|
|
struct dynl_obj {
|
|
SCM filename;
|
|
void *handle;
|
|
};
|
|
|
|
static SCM mark_dynl_obj SCM_P ((SCM ptr));
|
|
static SCM
|
|
mark_dynl_obj (ptr)
|
|
SCM ptr;
|
|
{
|
|
struct dynl_obj *d = (struct dynl_obj *)SCM_CDR (ptr);
|
|
SCM_SETGC8MARK (ptr);
|
|
return d->filename;
|
|
}
|
|
|
|
static scm_sizet free_dynl_obj SCM_P ((SCM ptr));
|
|
static scm_sizet
|
|
free_dynl_obj (ptr)
|
|
SCM ptr;
|
|
{
|
|
scm_must_free ((char *)SCM_CDR (ptr));
|
|
return sizeof (struct dynl_obj);
|
|
}
|
|
|
|
static int print_dynl_obj SCM_P ((SCM exp, SCM port, scm_print_state *pstate));
|
|
static int
|
|
print_dynl_obj (exp, port, pstate)
|
|
SCM exp;
|
|
SCM port;
|
|
scm_print_state *pstate;
|
|
{
|
|
struct dynl_obj *d = (struct dynl_obj *)SCM_CDR (exp);
|
|
scm_gen_puts (scm_regular_string, "#<dynamic-object ", port);
|
|
scm_iprin1 (d->filename, port, pstate);
|
|
if (d->handle == NULL)
|
|
scm_gen_puts (scm_regular_string, " (unlinked)", port);
|
|
scm_gen_putc ('>', port);
|
|
return 1;
|
|
}
|
|
|
|
static scm_smobfuns dynl_obj_smob = {
|
|
mark_dynl_obj,
|
|
free_dynl_obj,
|
|
print_dynl_obj
|
|
};
|
|
|
|
SCM_PROC (s_dynamic_link, "dynamic-link", 1, 0, 0, scm_dynamic_link);
|
|
|
|
SCM
|
|
scm_dynamic_link (fname)
|
|
SCM fname;
|
|
{
|
|
SCM z;
|
|
void *handle;
|
|
struct dynl_obj *d;
|
|
|
|
fname = scm_coerce_rostring (fname, s_dynamic_link, SCM_ARG1);
|
|
|
|
SCM_DEFER_INTS;
|
|
handle = sysdep_dynl_link (SCM_CHARS (fname), s_dynamic_link);
|
|
|
|
d = (struct dynl_obj *)scm_must_malloc (sizeof (struct dynl_obj),
|
|
s_dynamic_link);
|
|
d->filename = fname;
|
|
d->handle = handle;
|
|
|
|
SCM_NEWCELL (z);
|
|
SCM_SETCHARS (z, d);
|
|
SCM_SETCAR (z, scm_tc16_dynamic_obj);
|
|
SCM_ALLOW_INTS;
|
|
|
|
return z;
|
|
}
|
|
|
|
static struct dynl_obj *get_dynl_obj SCM_P ((SCM obj, char *subr, int argn));
|
|
static struct dynl_obj *
|
|
get_dynl_obj (dobj, subr, argn)
|
|
SCM dobj;
|
|
char *subr;
|
|
int argn;
|
|
{
|
|
struct dynl_obj *d;
|
|
SCM_ASSERT (SCM_NIMP (dobj) && SCM_CAR (dobj) == scm_tc16_dynamic_obj,
|
|
dobj, argn, subr);
|
|
d = (struct dynl_obj *)SCM_CDR (dobj);
|
|
SCM_ASSERT (d->handle != NULL, dobj, argn, subr);
|
|
return d;
|
|
}
|
|
|
|
SCM_PROC (s_dynamic_object_p, "dynamic-object?", 1, 0, 0, scm_dynamic_object_p);
|
|
|
|
SCM
|
|
scm_dynamic_object_p (SCM obj)
|
|
{
|
|
return (SCM_NIMP (obj) && SCM_CAR (obj) == scm_tc16_dynamic_obj)?
|
|
SCM_BOOL_T : SCM_BOOL_F;
|
|
}
|
|
|
|
SCM_PROC (s_dynamic_unlink, "dynamic-unlink", 1, 0, 0, scm_dynamic_unlink);
|
|
|
|
SCM
|
|
scm_dynamic_unlink (dobj)
|
|
SCM dobj;
|
|
{
|
|
struct dynl_obj *d = get_dynl_obj (dobj, s_dynamic_unlink, SCM_ARG1);
|
|
SCM_DEFER_INTS;
|
|
sysdep_dynl_unlink (d->handle, s_dynamic_unlink);
|
|
d->handle = NULL;
|
|
SCM_ALLOW_INTS;
|
|
return SCM_UNSPECIFIED;
|
|
}
|
|
|
|
SCM_PROC (s_dynamic_func, "dynamic-func", 2, 0, 0, scm_dynamic_func);
|
|
|
|
SCM
|
|
scm_dynamic_func (SCM symb, SCM dobj)
|
|
{
|
|
struct dynl_obj *d;
|
|
void (*func) ();
|
|
|
|
symb = scm_coerce_rostring (symb, s_dynamic_func, SCM_ARG1);
|
|
d = get_dynl_obj (dobj, s_dynamic_func, SCM_ARG2);
|
|
|
|
SCM_DEFER_INTS;
|
|
func = (void (*) ()) sysdep_dynl_func (SCM_CHARS (symb), d->handle,
|
|
s_dynamic_func);
|
|
SCM_ALLOW_INTS;
|
|
|
|
return scm_ulong2num ((unsigned long)func);
|
|
}
|
|
|
|
SCM_PROC (s_dynamic_call, "dynamic-call", 2, 0, 0, scm_dynamic_call);
|
|
|
|
SCM
|
|
scm_dynamic_call (SCM func, SCM dobj)
|
|
{
|
|
void (*fptr)();
|
|
|
|
if (SCM_NIMP (func) && SCM_ROSTRINGP (func))
|
|
func = scm_dynamic_func (func, dobj);
|
|
fptr = (void (*)()) scm_num2ulong (func, (char *)SCM_ARG1, s_dynamic_call);
|
|
SCM_DEFER_INTS;
|
|
fptr ();
|
|
SCM_ALLOW_INTS;
|
|
return SCM_UNSPECIFIED;
|
|
}
|
|
|
|
SCM_PROC (s_dynamic_args_call, "dynamic-args-call", 3, 0, 0, scm_dynamic_args_call);
|
|
|
|
SCM
|
|
scm_dynamic_args_call (func, dobj, args)
|
|
SCM func, dobj, args;
|
|
{
|
|
int (*fptr) (int argc, char **argv);
|
|
int result, argc;
|
|
char **argv;
|
|
|
|
if (SCM_NIMP (func) && SCM_ROSTRINGP (func))
|
|
func = scm_dynamic_func (func, dobj);
|
|
|
|
fptr = (int (*)(int, char **)) scm_num2ulong (func, (char *)SCM_ARG1,
|
|
s_dynamic_args_call);
|
|
SCM_DEFER_INTS;
|
|
argv = scm_make_argv_from_stringlist (args, &argc, s_dynamic_args_call,
|
|
SCM_ARG3);
|
|
result = (*fptr) (argc, argv);
|
|
scm_must_free_argv (argv);
|
|
SCM_ALLOW_INTS;
|
|
|
|
return SCM_MAKINUM(0L+result);
|
|
}
|
|
|
|
void
|
|
scm_init_dynamic_linking ()
|
|
{
|
|
scm_tc16_dynamic_obj = scm_newsmob (&dynl_obj_smob);
|
|
sysdep_dynl_init ();
|
|
#include "dynl.x"
|
|
}
|