mfp/ld.ocaml
Folders and files
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Repository files navigation
Dynamic linker/loader for OCaml.
ld.ocaml does for OCaml native code plugins (.cmxs files) what ld.so does for
shared libraries. Given a list of cmxs objects (typically only one,
corresponding to a program to execute), ld_ocaml scans the filesystem to find
the available (.cmxs) libraries, infers which are needed in order to load the
objects, and then proceeds to load them all in order.
Requirements
------------
* OCaml >= 3.11 with native Dynlink (.cmxs) support
* GNU BFD (available as binutils-dev in Debian)
* OMake for building
Building
--------
$ omake
Installing
----------
Copy the ld_ocaml executable to some directory in your PATH.
Don't rename the file (adding an extension is OK): ld_ocaml examines ARGV[0]
at runtime in order to decide how to rewrite ARGV and which cmxs files to load
(see below).
Usage
-----
ld_ocaml foo.cmxs -other --options --are --ignored
looks for .cmxs libraries and loads those required by foo.cmxs. Sys.argv is
modified so that foo.cmxs becomes Sys.argv.(0).
You can also specify multiple .cmxs files to be loaded in order. The last one
will become Sys.argv.(0).
Alternatively, if only one cmxs object (plus its dependencies) is to be
loaded, you can create a symlink to ld_ocaml in the same directory as the
.cmxs, with the same name as the .cmxs *without the extension* (it's OK to add
a different extension). For example, if you want to run hello.cmxs directly,
you can do
$ cd /dir/where/hello.cmxs/is
$ ln -s /usr/local/bin/ld_ocaml hello # hello.exe or any other extension OK
...
$ hello -other -options # assuming it's in the path
$ /dir/where/hello.cmxs/is/hello # otherwise
ld_ocaml honors these environment variables:
LD_OCAML_VERBOSE
set to 0 for no output (default), 1 for information about the libs that
will be loaded, 2 for debug info
LD_OCAML_CACHE
name of the cache file (default: $HOME/.ld.ocaml.cache)
LD_OCAML_LIBRARY_PATH
list of extra paths (separated by :) where .cmxs libraries can be found,
in addition to the standard directories. The information about these libs
will not be saved in the cache.
LD_OCAML_SYS_LIBRARY_PATH
list of paths (separated by :) where system .cmxs libraries can be
found. The information about these libs will be saved in the cache.
(defaults to (/usr/lib/ocaml, /usr/local/lib/ocaml, and their subdirs))
(Note: you should use absolute paths.)
LD_OCAML_EXTRA_SYS_LIBRARY_PATH
list of extra paths (separated by :) where system .cmxs libraries can be
found. The information about these libs will be saved in the cache.
(Note: you should use absolute paths.)
ld_ocaml caches the system DLL catalog in the file specified in LD_OCAML_CACHE
($HOME/.ld.ocaml.cache by default). Delete that file to force a system DLL
catalog rebuild when new libraries are installed.
Creating the .cmxs libraries
----------------------------
Given a .cmxa file with PIC code, you can create the cmxs easily:
ocamlopt -shared -o foo.cmxs foo.cmxa -linkall
For instance, if you want to run a program that depends on AAA batteries,
itself dependent on camomile.cmxs and nums.cmxs, you can build the required
.cmxs libs as follows (other libs, including Thread, Dynlink, Unix and Str,
are already included in ld_ocaml's runtime):
ocamlfind ocamlopt -package camomile -shared -o camomile.cmxs \
camomile.cmxa -linkall
ocamlfind ocamlopt -package num -shared -o nums.cmxs nums.cmxa -linkall
ocamlfind ocamlopt -package aaa -shared -o aaa.cmxs aaa.cmxa -linkall
Debian is already starting to distribute .cmxs files in OCaml library
packages. Presumably, all packages in Debian and other distributions will
include .cmxs files in the future, making manual cmxs generation unnecessary.
If you use OMake, you can use these rules to generate the .cmxs files for all
the OCAMLPACKS at once under libs/:
# the target used to build all the .cmxs
.PHONY: libs
# get all the packages required recursively
ALL_PACKS = \
$(shell ocamlfind query -predicates "mt,native,mt_posix" -r \
-p-format $(OCAMLPACKS) | sort -u)
# for each foo.cmxs, define a libs: libs/foo.cmxs dependency
# and a libs/foo.cmxs target
foreach(pack, $(ALL_PACKS))
ARCH = $(shell ocamlfind query -predicates "mt,native,mt_posix" \
-a-format $(pack))
DST = libs/$(pack).cmxs
libs: $(DST)
$(DST):
$(OCAMLFIND) ocamlopt -shared $(OCAMLFLAGS) -o $@ \
-package $(pack) $(ARCH) -linkall
Program compilation
-------------------
Let's suppose you want to compile a program that links against AAA Bateries.
You'd normally do
ocamlfind ocamlopt -package aaa -o hello hello.ml -linkpkg
which would link statically against AAA's libs and all its dependencies.
In order to create a cmxs that can be run with
ld_ocaml hello.cmxs
you simply need to do
ocamlfind ocamlopt -package aaa -shared -o hello.cmxs hello.ml
The difference is that we don't link against the AAA package anymore, and the
dynamic libs will be loaded as required by ld_ocaml (you'll have to make sure
the required .cmxs libs are available, though --- see the above section).
How it works
------------
.cmxs files hold information about the interfaces they import ("imports_cmi")
and the implementations they are tied to because of inlining ("imports_cmx").
This information is an OCaml string associated to the "caml_plugin_header"
symbol which corresponds to a Marshal serialization of the header (see
ld_header.ml). ld_ocaml looks for .cmxs files and builds a DLL catalog
which is later used to resolve dependencies as needed.