Cygwin linking error for ocaml called from C example

Hello and may your day be nice.
TLDR; could not compile (link) example from ocaml manual for embedding ocaml into c program. (no ctypes)

My setup: opam with default cygwin configuration and ocaml 5.3 on Windows 10.
Some details from ocamlc -config:

  • version: 5.3.0
  • c_compiler: x86_64-w64-mingw32-gcc

Ocaml code used in code.ml:

let rec fib n = if n < 2 then 1 else fib(n-1) + fib(n-2)
let _ = Callback.register "fib" fib

C code in main.c:

#include <wchar.h>
#include <stdio.h>

#include "caml/misc.h"
#include "caml/mlvalues.h"
#include "caml/sys.h"
#include "caml/osdeps.h"
#include <caml/callback.h>
#include <windows.h>

int fib(int n)
{
  static const value * fib_closure = NULL;
  if (fib_closure == NULL) fib_closure = caml_named_value("fib");
  return Int_val(caml_callback(*fib_closure, Val_int(n)));
}

int main() {
  // fake argv for now since I don't know how to convert windows arguments to argv
  char_os argv0[] = { (unsigned short int)'x', 0 };
  char_os** argv = malloc(sizeof(char_os*) * 2);
  argv[0] = argv0;
  argv[1] = NULL; // ocaml probably uses a null terminator for its argv since there's no argc?
  caml_main(argv);
  int result = fib(10);
  return 0;
}

With build commands like:

ocamlopt -output-obj caml.ml -o caml_with_extras.o
ar r caml_final.a D:/opam/default/lib/ocaml/libasmrun.a caml_with_extras.o 
gcc -I D:/opam/default/lib/ocaml -o main.exe main.c caml_final.a	

Errors look suspiciously similar:

  • undefined reference to `__imp_caml_named_value’
  • undefined reference to `__imp_caml_callback’
  • undefined reference to `__imp_caml_main’

From my understanding this _impl prefix situation is some gcc behavior to (here my understanding is limited) link to DLLs which can be found related to __declspec(dllimport).

Last time similar issue was described in topic from 2023.

I was doing out of curiosity so maybe something is missing from my current setup.
Cheers!

On Windows, you must use flexlink to link your final executable. Also, you can use -output-complete-obj instead of -output-obj to include the runtime code in the resulting object (avoiding the need for your ar invocation). Something like:

# use -output-complete-obj to also include the runtime in the resulting object
ocamlopt -output-complete-obj caml.ml -o caml_with_extras.o
# compile only, do not yet link
gcc -I D:/opam/default/lib/ocaml -c main.c
# now link
flexlink -exe -chain mingw64 -o main.exe -LD:/opam/default/lib/ocaml/flexdll -LD:/opam/default/lib/ocaml main.o caml_with_extras.o -lpthread -lws2_32 -lole32 -luuid -lversion -lshlwapi -lsynchronization -l:libpthread.a -lgcc_eh

To get the exact list of support libraries needed to link the final executable you can look at ocamlopt -config (the native_c_libraries field).

Cheers,
Nicolas

Short reply: you sir are amazing and with your response I linked final execuatable in no time. May your day be great and programming activites pleasant.

I modified C code to include simple printing and this is main.c:

#include <wchar.h>
#include <stdio.h>

#include "caml/misc.h"
#include "caml/mlvalues.h"
#include "caml/sys.h"
#include "caml/osdeps.h"
#include <caml/callback.h>
#include <windows.h>

int fib(int n)
{
  static const value * fib_closure = NULL;
  if (fib_closure == NULL) fib_closure = caml_named_value("fib");
  return Int_val(caml_callback(*fib_closure, Val_int(n)));
}

int main() {
  // fake argv for now since I don't know how to convert windows arguments to argv
  char_os argv0[] = { (unsigned short int)'x', 0 };
  char_os** argv = malloc(sizeof(char_os*) * 2);
  argv[0] = argv0;
  argv[1] = NULL; // ocaml probably uses a null terminator for its argv since there's no argc?
  caml_main(argv);
  int result = fib(10);
  printf("%d\n", result);
  return 0;
}

And these instructions are used for compiling and linking (-l:libpthread.a -lgcc_eh are not necessary for my c code):

ocamlopt -output-complete-obj caml.ml -o caml_with_extras.o
gcc -I D:/opam/default/lib/ocaml -c main.c
flexlink -exe -chain mingw64 -o main.exe -LD:/opam/default/lib/ocaml/flexdll -LD:/opam/default/lib/ocaml main.o caml_with_extras.o -lpthread -lws2_32 -lole32 -luuid -lversion -lshlwapi -lsynchronization

After this I could execute main.exe and get 89 as a result from fib function computed in ocaml.

1 Like