Dynlink works in native mode but not in bytecode?

Dynlinking is a quite complex topic in OCaml and still unsound (as of OCaml 4.07 and below). And if you will dig further you will even find out that OCaml has 3 dynlinking facilities that differ in their behavior, the toplevel linker is different from the bytecode lifter and both are different from the native linker.

The good news is it is possible to implement a sound and robust linking facility in OCaml (even in the presence of the inherited unsoundness). We have implemented it in BAP, and other projects have their own implementations with varying flexibility and safety.

In BAP the support for dynamic linking is split into three parts,

  1. we have the bapbuild tool which is an ocamlbuild plugin that knows how to properly build plugins. It is shipped as a tool, but could be also used as a normal plugin, since it is packed into a library. Our plugins are zip-files that contain cmxs and cma so that they could be used in both three loaders, as well as meta information (the reason why meta is needed will be given below) and optional dependencies, which makes a plugin independent off the environment. The bapbuild tool is also clever in that it knows how to handle broken packages, that forget to ship cmxs or cma or even both.

  2. The loading part is implemented by the bap-plugins library. This part tracks carefully which compilation units are already linked into the process image. This is why we need the meta information in plugins, that describe which compilation units are already linked into the shared object and which are required.

  3. Finally a myocamlbuild.ml plugin is required for the host program (i.e., the program that loads plugins), since usually the host program is by itself composed of some compilation units and we should be sure that the loaded plugin doesn’t try to link into the program body any compilation unit that is already there. We don’t want to parse the ELF data structure of the host program to figure out which units are already linked since it is not portable and fragile, so we need some support from the build system. In particular we use the ocamlfind.dynlink library that stores this information in a special data structure inside the binary, and extend it to support internal modules (since a plugin and a host program could be built from the same source tree, so that ocamlfind won’t notice that they are linked).

So if you will follow carefully our implementation and ensure that you have all three ingredients in this of that form, you will be able to load your shared code using all three OCaml linkers. If stuck, don’t hesitate to ask questions. This is all not to scare you away from dynlinking. BAP wouldn’t be possible without it, for example.

2 Likes