Some random notes, influenced by reading the following
http://www.boblycat.org/~malc/scaml/index1.html
http://www.boblycat.org/~malc/scaml/index2.html
The dynlink option: make sure to add -Wl,-E to the gcc command-line
(invoked by ocamlopt to _assemble_ the code)
The natdynlink still needs caml_start_shared,
fini_shared, init_shared symbols in the file being loaded.
and shared_dyn_post_process ()...
Actually, it seems that fini_shares is located, checked that it is
found, but not used.
"init_shared" symbol in the loaded file is located and invoked.
shared_dyn_post_process (); is then invoked.
"caml_start_shared" symbol in the loaded file is located and invoked.
The symbol "init_shared" is located in the shinit.c, the start-up
file that is linked with the dynamically-loaded module:
ocaml/asmrun/shinit.c is the startup file.
It calls shared_register(&decs) and caml_init_shared ();
According to the decsription in the ChangeLog of the scaml patch,
+ (shared_init): create function caml_init_shared(pseudo):
+ map (function
+ Module module -> ()
+ | Shared library -> library.init ())
+ all_modules_shared_libraries_referenced.
it seems that caml_init_shared only matters for shared libraries (to
invoke libtrary's init function) rather than for dynamically loaded
modules.
The function shared_register (see asmrun/shared.c)
Adds the shdesc: it will be only one descriptor in the desc_roor
chain.
shared_frametable: if not null, invoke add_frametable (desc);
which adds all the frametables from shared_desc (in our case,
probably only one) at the end of shared_frametable table
(unless the frametable was already mentioned). The shared_frametable
is resized approporiately. The last slot of shared_frametable is
NULL.
But originally, shared_frametable must be null... shared_frametable
is only allocated in process_frametables(), which is invoked only from
shared_post_process(). The latter is not invoked by the dynamic
loader. Rather, the loader invokes shared_dyn_post_process ().
The latter merely does
process_segments ();
and sets "shared_new_frames = 1;"
The latter flag is only used in the predicate
shared_modules_added(). The latter is used in roots.c:
when gc routines are invoked, root.c checks is shared modules are
added. If they have been, it invokes init_frame_descriptors();
+ (oldify_local_roots): added call to shared_get_globals and code to
+ reintialize frame descriptors if new module was added.
+ (do_roots): likewise.
See ocaml/asmrun/roots.c
+#ifdef SHARED_CAML
+ long **caml_frametable = shared_get_frametable ();
+
+ if (frame_descriptors != NULL) {
+ stat_free (frame_descriptors);
+ frame_descriptors = NULL;
+ }
+#endif
CSP values can be resolved by dynamic linking?
In more detail:
introduce table : (unit array) and make it a pervasive.
translate . in
let a = something in ..
as
begin increment table_id; table.(table_id) <- Obj.magic a;
.< !Obj.magic (table.(table_id)) >.
Walid said that something like that is already being though of.
(And this is how it works in a byte compiler?)
Note that MetaOCaml draws the distinction between a CSP which is a
pervasive, and a regular CSP. The former is compiled in AST just like
Var "name" (so the compiler of AST can handle it as a native
pervasive).
Note that data in OCaml should not be referred to by a ptr, because GC
may move it. Code pointers are stable. Pervasive pointers are stable
too.
---
Dynamic linking must essentially do the same job as Ocaml static
linking does when generating /tmp/camlstartup4978fc.s
Namely, for each compiled OCaml module, need to generate tables
.L216:
addl $1, caml_globals_inited
call camlArray__entry
caml_globals:
.long camlPervasives
.long camlArray
caml_globals_map:
.ascii "..."
caml_data_segments:
.long caml_startup__data_begin
.long caml_startup__data_end
.long camlPervasives__data_begin
.long camlPervasives__data_end
.long camlArray__data_begin
.long camlArray__data_end
caml_code_segments:
.long caml_startup__code_begin
.long caml_startup__code_end
.long camlPervasives__code_begin
.long camlPervasives__code_end
.long camlArray__code_begin
.long camlArray__code_end
caml_frametable:
.long caml_startup__frametable
.long caml_system__frametable
.long camlPervasives__frametable
See make_startup_file in asmcomp/asmlink.ml
One idea is to get the compiler generate a new camlstartup.s file
(using all the existing build plus the new module to link in
dynamically), and then effectively `replace' the existing
camlstartup.s (perhaps playing with weak links, etc).
A better idea is to observe the absense of globals in the module to
dynamically load, and the absence of new exception declarations.
Requirement: no new apply, send and curry functions in the generated
fragment!
There may be a rare condition when dynamic
loading fails, if the generated code uses functions of higher arity or
does currying to a larger extent than that in the main code. This is
because currying/apply_n code is generated in the start-up file. I'd
expect this to be a rare condition that may occur only when the main
code is "too simple" (usually it is never the case because the main
code includes stdlib, and the latter is quite complex already). The
problem will manifest itself is the failure of .! (exception will be
thrown). The workaround is easy: just add to the main code some
functions of higher-arity. You don't have to execute them; they just
have to be present.
caml_frametable[] is referenced only in asmrun/roots.c
caml_code_segments, caml_data_segments
are referenced only in asmrun/startup.c
and only as far as minmax table is concerned. So, we only need
to adjust
char * caml_static_data_start, * caml_static_data_end;
char * caml_code_area_start, * caml_code_area_end;
---
Also, we may wish to unify dynamic loading. Currently, there
are three! Ocaml interfaces to exactly the same dlopen API: one in
OCaml itself, one used in off-shoring and one for metanative. Somehow
all three Ocaml interfaces are slightly different, that's why
currently we need all three of them...