/* - Wed Oct 17 16:43:30 2001 by Malcy - started */
/* Slightly modified to accept the entryname_v in ml_natdynload */
/* $Id: ndl.c,v 1.3 2005/09/18 21:58:00 oleg Exp $ */
#include <dlfcn.h>
#include <fail.h>
#include <misc.h>
#include <alloc.h>
#include <memory.h>
#include <custom.h>
#include <mlvalues.h>
#include <callback.h>
/* for debugging */
#include <stdio.h>
#if defined(DYN_DEBUG) && DYN_DEBUG
#define DBGPRINT fprintf
#else
#define DBGPRINT (void)
#endif
/* in new roots.c */
extern long ** caml_dyn_frametable;
void init_frame_descriptors(void);
/* Operations on DYNlimits
This code is to operate on data/code limits in a dynamically
loaded code.
This code generalizes the simplistic handling of data/code
limits in ../asmrun/startup.c
We put the code here for convenience.
*/
struct DYNlimits caml_static_data_limits =
{0,0,(struct DYNlimit *)0};
struct DYNlimits caml_code_area_limits = /* not dealt with yet */
{0,0,(struct DYNlimit *)0};
/* Allocate that many entries, initially */
#define DYNlimits_init_alloc 10
static void debug_print_DYNlimits(const struct DYNlimits * dynlimits,
const char * title)
{
#if defined(DYN_DEBUG) && DYN_DEBUG
const struct DYNlimit * const dl_end = dynlimits->limit + dynlimits->used;
const struct DYNlimit * dl = dynlimits->limit;
DBGPRINT (stderr,"\n%s: Dynlimits %p; size %d used %d limits %p\n",
title,dynlimits,dynlimits->size,dynlimits->used,dynlimits->limit);
for(;dl != dl_end; dl++)
DBGPRINT (stderr," [%p,%p]\n",dl->begin,dl->end);
#endif
}
/* check to see if the address is within limits */
int within_DYNlimitsP(const char * addr,
const struct DYNlimits * dynlimits)
{
const struct DYNlimit * const dl_end = dynlimits->limit + dynlimits->used;
const struct DYNlimit * dl = dynlimits->limit;
debug_print_DYNlimits(dynlimits,"within_DYNlimitsP");
DBGPRINT (stderr,"addr %p\n",addr);
for(;dl != dl_end; dl++)
if( addr >= dl->begin && addr < dl->end )
return 1;
DBGPRINT (stderr,"not found addr %p\n",addr);
return 0;
}
static void add_to_dynlimits(struct DYNlimits * dynlimits,
const void * begin, const void * end)
{
if( dynlimits->size == 0 )
{ /* Initial allocation */
dynlimits->limit = caml_stat_alloc(sizeof(struct DYNlimit) *
DYNlimits_init_alloc);
dynlimits->used = 0;
dynlimits->size = DYNlimits_init_alloc;
}
else if( dynlimits->size == dynlimits->used )
{ /* Reallocate */
const int new_size = 2 * dynlimits->size;
dynlimits->limit = caml_stat_resize(dynlimits->limit,
sizeof(struct DYNlimit) *
new_size);
dynlimits->size = new_size;
}
{
struct DYNlimit * dl = dynlimits->limit + dynlimits->used;
dynlimits->used++;
dl->begin = begin;
dl->end = end;
}
debug_print_DYNlimits(dynlimits,"add_to_dynlimits");
}
/*
handle is the DL handle
entryname is the name of the entry, a string like
"camlTest_dyn1__entry"
We locate in the shared object represented by [handle]
the symbol whose name is [entryname] with "entry" suffix replaced by
[new_suffix]
*/
static void * locate_suffixed_symbol(void * handle,
const char * entryname,
const char * new_suffix)
{
const char entry_suffix [] = "entry";
char new_name[256];
char *p;
if ( strlen(entryname) + strlen(new_suffix) >= sizeof(new_name)-2 )
caml_fatal_error_arg("ndl.c: entryname `%s' is too long\n",entryname);
strncpy(new_name,entryname,sizeof(new_name)-2);
new_name[sizeof(new_name)-1] = '\0';
p = new_name + strlen(new_name) - (sizeof(entry_suffix)-1);
if( strcmp(p,entry_suffix) != 0 )
caml_fatal_error_arg(
"ndl.c: wrong entryname `%s', should end in `entry'\n",
entryname);
strcpy(p,new_suffix);
return dlsym (handle, new_name);
}
static struct custom_operations dso_handle_ops = {
"dsohandle",
NULL,
custom_compare_default,
custom_hash_default,
custom_serialize_default,
custom_deserialize_default
};
value dso_handle_new (void *handle)
{
value wrapper = alloc_custom (&dso_handle_ops, sizeof (void *), 0, 1);
*((void **)Data_custom_val (wrapper)) = handle;
return wrapper;
}
extern void shared_dyn_post_process (void);
static value * natdynlink_exn = NULL;
#define Link_error_Tag 0
#define Init_entry_not_found_Tag 1
#define Caml_entry_not_found_Tag 2
#define Fini_entry_not_found_Tag 3
#define Success_Tag 0
#define Entry_Exn_Tag 1
static void fail (const char *err, int tag) Noreturn;
static void fail (const char *err, int tag)
{
value res = alloc_small (1, tag);
Store_field (res, 0, copy_string (err));
if (NULL == natdynlink_exn)
natdynlink_exn = caml_named_value ("Natdynlink.Error");
raise_with_arg (*natdynlink_exn, res);
}
static void * check_null(void * handle, void * addr)
{
if (NULL == addr) {
const char *err = dlerror ();
dlclose (handle);
fail (err, Caml_entry_not_found_Tag);
}
return addr;
}
value ml_natdynload (value private, value filename_v,
value entryname_v) /* ML */
{
CAMLparam3 (private, filename_v, entryname_v);
CAMLlocal3 (result, err, tup);
value (*caml_shared_entry) (void);
void (*caml_shared_fini) (void);
void (*caml_shared_init) (void);
void * frametable;
char *filename = String_val (filename_v);
char *entryname = String_val (entryname_v);
void *handle;
handle = (private == Val_true)
? dlopen (filename, RTLD_NOW)
: dlopen (filename, RTLD_NOW | RTLD_GLOBAL);
if (NULL == handle)
fail (dlerror (), Link_error_Tag);
caml_shared_entry = check_null(handle, dlsym (handle, entryname));
DBGPRINT (stderr,"\nentry name %s: %p\n",entryname,caml_shared_entry);
frametable = check_null(handle,
locate_suffixed_symbol(handle,entryname,"frametable"));
/* TODO: checks for duplicates: check frametable against existing
entries in caml_dyn_frametable.
This is not too critical though...
*/
if( caml_dyn_frametable == NULL )
{
/* Allocate a new caml_dyn_frametable */
caml_dyn_frametable = caml_stat_alloc(2 * sizeof(caml_dyn_frametable[0]));
caml_dyn_frametable[0] = frametable;
caml_dyn_frametable[1] = NULL;
}
else
{
/* reallocate the caml_dyn_frametable */
int count = 0;
for (count = 0; caml_dyn_frametable[count] != 0; count++)
;
caml_dyn_frametable = caml_stat_resize(caml_dyn_frametable,
(count+1+1) *
sizeof(caml_dyn_frametable[0]));
caml_dyn_frametable[count] = frametable;
caml_dyn_frametable[count+1] = NULL;
}
init_frame_descriptors();
add_to_dynlimits(&caml_static_data_limits,
check_null(handle,
locate_suffixed_symbol(handle,entryname,
"data_begin")),
check_null(handle,
locate_suffixed_symbol(handle,entryname,
"data_end")));
#if 0
caml_shared_fini = check_null(handle, dlsym (handle, "fini_shared"));
caml_shared_init = check_null(handle, dlsym (handle, "init_shared"));
caml_shared_init ();
shared_dyn_post_process ();
#endif
/* I cannot call caml_shared_entry directly, as in
err = caml_shared_entry ();
Becasue the shared entry is a CAML program and needs a properly
set up frame. So, I need to invoke it as a call-back into OCaml
from C.
So, I need to use caml_callback_exn. However, the latter
needs a closure as its first argument. But all I have is
the code pointer. The examination of asmrun/i386.S shows however
that a mere reference cell suffices. The second argument to
caml_callback_exn is an argument to the closure. We use 0 --
no argument is needed. That doesn't affect the stack,
see again asmrun/i386.S
*/
err = caml_callback_exn((value)(&caml_shared_entry), 0);
/* TODO: change the of natdynlink so I could return the value
of the plusgin directly. It woulbe be unit anyway
A better invocation: make a tail call to caml_shared_entry.
In that case, there will be no need to mess with setting up
a frame
*/
if (Is_exception_result (err)) {
tup = alloc_tuple (2);
Store_field (tup, 0, dso_handle_new (handle));
Store_field (tup, 1, Extract_exception (err));
result = alloc_small (1, Entry_Exn_Tag);
Field (result, 0) = tup;
} else {
result = alloc_small (1, Success_Tag);
Store_field (result, 0, err);
#if 0
Store_field (result, 0, dso_handle_new (handle));
#endif
}
CAMLreturn (result);
}
syntax highlighted by Code2HTML, v. 0.9.1