Linking Error with External C Code in Library

I have been getting an error when linking a project with external C code and was wondering if anyone could help me. I have come up with the simplest way to reproduce the error I can:

Here is my project structure:

main.ml

library
    stuff.ml
    stuff_external.c

and the actual file contents:

stuff.ml

external do_something : unit -> unit = "do_something"

stuff_external.c

#include <stdio.h>
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/alloc.h>

CAMLprim value do_something(value unit)
{
    printf("Doing something\n");

    return Val_unit;
}

main.ml

let _ =
    Stuff.do_something ()

The idea is that I can compile the library in the nested folder, then reference the .cmxa file in the above project while passing the -I flag for compiling projects to look for the compiled files in the library folder.

My build process looks like this:

root/library> ocamlopt -c stuff_external.c stuff.ml
root/library> ocamlopt -a stuff_external.o stuff.cmx -o library.cmxa
root/library> cd ../
root> ocamlopt -I library -c main.ml
root> ocamlopt library/library.cmxa main.cmx -o program

I get an error in the final line being:

x86_64-linux-gnu-gcc: error: stuff_external.o: No such file or directory
File "caml_startup", line 1:
Error: Error during linking
make: *** [makefile:3: build] Error 2

There are a few things to note that might be helpful.

Building a project like this works fine for me when there are no external c files.

Furthermore, if I move the .o file up to root before the final line, the compilation works, so it is as if in the linking stage the -I flag is only applying to OCaml files. I have however tried some of the other options for the c compiler flags but can’t get it to work.

Ultimately I would like for the project in the above directory to be able to find the .o file.

This command line does not actually embed stuff_external.o into library.a, it just records the name of the object file in library.cmxa so that it is automatically added to the command line during the linking step (see next paragraph).

In order for the compiler to locate stuff_external.o during this linking step you need to add -I library, so that the final command line could be

ocamlopt -I library library.cmxa main.cmx -o program

Incidentally, if you are able to, it may be a good idea to use Dune, it includes a lot of logic to make creating C stubs rather painless.

Cheers,
Nicolas

1 Like

Thank you for your response. Unfortunately that was something that I have already tried, and the same error occurs.

Right, I tried it on Windows where the interpretation of -I by the C compiler is slightly different than with gcc. A more robust solution that should work in every case (from the manual):

After the step

root/library> ocamlopt -c stuff_external.c stuff.ml

we bundle stuff_external.o into a native library by doing

root/library> ocamlmklib -o stuff_external stuff_external.o

Then in order to build library.cmxa you pass -cclib -lstuff_external instead of stuff_external.o:

root/library> ocamlopt -a -cclib -lstuff_external stuff.cmx -o library.cmxa

The final linking step is then

root> ocamlopt -I library library.cmxa main.cmx -o program

Hope that helps,

Cheers,
Nicolas

1 Like

There’s a better way to use ocamlmklib: after

root/library> ocamlopt -c stuff_external.c stuff.ml

just bundle everything together with

root/library> ocamlmklib -o mylib stuff_external.o stuff.cmxa

This will build root/libmylib.a and root/dllmylib.so, which are C static and dynamic libraries resp., and root/mylib.cmxa, which is an OCaml library that auto-links the appropriate C library.

Then:

root> ocamlopt -I library mylib.cmxa main.cmx

should do the job.

2 Likes

Thank you so much, that worked!

I should mention here for the sake of future readers that after implementing that change, I had a new error occurring:

relocation R_X86_64_PC32 against undefined symbol `caml_local_roots' can not be used when making a shared object; recompile with -fPIC

and this was fixed by recompiling with -ccopt -fPIC not just fPIC as it was an issue in the C compilation. I will leave it to more qualified people than me to explain this error.

Also with reference to the the difference in platforms on Windows and the usage of -I, I should also mention for any future readers that I am using Ubuntu through WSL.