Plasma GitLab Archive
Projects Blog Knowledge

KNOWLEDGE BASE ON CAMLCITY.ORG: ocaml

KB 002: OCaml Programs As Shared Libraries

Compile programs into .so files - by Gerd Stolpmann, 2012-11-28

Since quite some time the OCaml code generator supports the creation of position-independent code (PIC). This is e.g. required to load OCaml modules dynamically. Another use is to output OCaml programs as shared libraries.

Let's assume we have this little program:

prog.ml:
    print_endline "Hello World, OCaml";;
The main program in C:
main.c:
    #include 
    #include 
    #include 
    int main(int argc, char *argv[]) {
        printf("Hello World, C\n");
        caml_startup(argv);
        return 0;
    }
The idea here is that the main program is written in C, and initializes from there the OCaml program.

The following applies to OCaml-3.11 and higher.

Statically-linked version as documented in the manual

Let's first look at what you can also find in the manual:

Here, we create a single prog_impl.o file from prog.ml, and this object contains the whole code of the OCaml program, but omits the OCaml runtime:

$ ocamlopt -c prog.ml
$ ocamlopt -output-obj -o prog_impl.o prog.cmx
For compiling the C program we use ocamlc as driver because it adds all required options when invoking the C compiler (alternatively you can grab the options from "ocamlc -config", variable native_c_compiler):
$ ocamlc -c main.c
Linking everything together:
$ cc -o main main.o prog_impl.o -L`ocamlc -where` -lasmrun -lm -ldl
libasmrun.a is available in the OCaml standard library directory, and we have to add this directory to the search path with -L`ocamlc -where`.

Creating libprog.a: A possible simplification for the user is to put the whole OCaml code, including the runtime, into a static archive:

$ rm -rf libprog.a
$ ar rc libprog.a prog_impl.o
$ asmrun_members=$(ar t `ocamlc -where`/libasmrun.a | grep -v '__.*')
$ mkdir -p tmp_asmrun
$ ( cd tmp_asmrun && ar x `ocamlc -where`/libasmrun.a )
$ for mem in $asmrun_members; do ar rc libprog.a tmp_asmrun/$mem; done
$ ranlib libprog.a
$ rm -rf tmp_asmrun
The link command is then:
$ cc -o main main.o -L. -lprog -lm -ldl
Note that we need to add the contents of libasmrun.a to libprog.a. If we do not do this, and try to add -lasmrun instead, we run into a problem with circular references between static libraries (which is not supported by the linker).

From where did we get "-lm -ldl"? If you want to be 100% correct, take this list from the variable native_c_libraries printed by "ocamlc -config".

Dynamically-linked version

Unfortunately, this depends on your platform/CPU:
  • x86-32: A standard OCaml installation works. There is a downside, though: OCaml cannot create PIC for x86-32. This is not a blocker on x86-32, because the dynamic loader of the OS can relocate on the fly. Nevertheless, there is a price to pay, namely that the text segment cannot be put into sharable memory, i.e. the RAM consumption is higher.
  • x86-64: You need to build OCaml so that libasmrun.a is compiled as PIC - see below how to do it. Code produced by ocamlopt is normally PIC anyway unless you give -fno-PIC on the command-line. Just don't do this, and the generated code will be right.
  • ARM (since OCaml-4.00): Basically it can be made working, but there is a difficulty: The code generator for ARM disables PIC by default. Especially the standard library is not PIC, and any other 3rd-party code isn't, too. The simplest way to fix this is to change the file asmcomp/arm/arch.ml in the OCaml sources so that the variable pic_code is initialized with true. In addition to this, you need to compile libasmrun.a with PIC as for x86-64.
  • Other CPU's are not supported.

How to enable PIC for libasmrun.a: When building OCaml, you need to configure it so that PIC is enabled:

  • Linux (and probably other platforms using the GNU toolchain):
     ./configure -cc "gcc -fPIC" -aspp "gcc -c -fPIC"
    
  • MacOS X: same
  • Other platforms: Unknown
If you are using GODI, you can add configure options in godi.conf:
OCAML_CONF_ARGS=-cc "gcc -fPIC" -aspp "gcc -c -fPIC"
Note that you need to rebuild both godi-ocaml-src and godi-ocaml.

How to create libasmrun.so: Now do

$ ocamlopt -output-obj -o libprog.so prog.cmx
$ cc -o main main.o -Wl,-rpath,. -L. -lprog
Test it (Linux - on OS X use "otool -L"):
$ ldd ./main
This should print libprog.so as dependent library.

Note that it is not necessary in this case to add "-lm -ldl" to the link command, as this has already been recorded in libprog.so.

Gerd Stolpmann works as O'Caml consultant
This web site is published by Informatikbüro Gerd Stolpmann
Powered by Caml