Today I've spent a lot of time trying to understand how Dynlink works.
I started with the following code:
main.ml:
let hello str =
print_string str ;
print_newline ()
let _ = hello "Main"
let _ =
try
Dynlink.loadfile "test.cmxs"
with Dynlink.Error(e) -> print_string (Dynlink.error_message e)
let _ = print_newline ()
main.mli:
val hello : string -> unit
test.ml
let _ = Main.hello "Test"
and then compiled everything the following way:
$ ocamlc main.mli
$ ocamlfind ocamlopt -package dynlink -linkpkg -o main main.ml
$ ocamlopt -shared -o test.cmxs test.ml
As I result I've got executable file:
$ file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
and plugin:
$ file test.cmxs
test.cmxs: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, not stripped
Unfortunately, this does not work:
$ ./main
Main
no implementation available for Main
I've spent a couple of hours trying to play with Dynlink and compiler options but failed to achieve any result until I found the following thread in fa.caml newsgroup where Xavier Leroy answers the question about exactly the same problem:
“The issue here is that when Dynlink.loadfile is called, module [...] is not yet completely evaluated, therefore it is not yet visible to dynamically-loaded code. The solution is just to split your main program in two modules [...]”
So I've created mainly.ml:
let hello str =
print_string str ;
print_newline ()
and mainly.mli:
val hello : string -> unit
Then modified main.ml:
let _ = Mainly.hello "Main"
let _ =
try
Dynlink.loadfile "test.cmxs"
with Dynlink.Error(e) -> print_string (Dynlink.error_message e)
let _ = print_newline ()
and test.ml:
let _ = Mainly.hello "test"
Compilation is similar:
$ ocamlc mainly.mli
$ ocamlfind ocamlopt -package dynlink -linkpkg -o main mainly.ml main.ml
$ ocamlopt -shared -o test.cmxs test.ml
And now it works!
$ ./main
Main
Test
So I'm happy.