Statically linking ocaml programs


My company servers are running a really old Linux (kernel rev 2.6.32).

On my machine, I installed ocaml-variants.4.10.0+musl+static+flambda, configure dune with the -ccopt and -static flags. However when I copy the exe to the company server, I still get this message:

./lwtTest.exe: /lib/ bad ELF interpreter: No such file or directory

Also, I can’t modify server settings, and I did try to set the LD_LIBRARY_PATH to my home folder with the lib being there.

Is there anything else I haven’t tried? Executables built by rust-musl are running just fine, but I prefer OCaml.


It looks like your resulting executable is not actually statically linked to the musl libc. You can check by running ldd <executable>, which should print not a dynamic executable.

For reference, I’m using the following lines in my dune files to create statically compiled executables:

 (ocamlopt_flags (:standard -ccopt -static))
1 Like

I tend to build the executables in an Alpine docker if I want to be very certain it is statically linked, mostly because I think -static flag have failed me before, but I might have also misremembered.

If the C compiler defaults to producing position-independent executables (PIE), you may need to call it with -no-pie in addition to -static.

Here is what I observe on Alpine Linux 3.11:

$ gcc -static hello.c
$ ldd ./a.out
	/lib/ (0xf774c000)
$ gcc -static -no-pie hello.c
$ ldd ./a.out
/lib/ ./a.out: Not a valid dynamic program

musl-gcc I am using doesn’t accept the -no-pie flag

I have
(flags -ccopt -static)

I tried your suggestion, and the result is the same.

(flags -ccopt -static)

the generated executable is claimed to be statically linked by ldd on my dev machine CentOS 7.

But the target system has the same issue

-bash: ./lwtTest.exe: /lib/ bad ELF interpreter: No such file or directory

Possibly the executable format is what’s wrong rather than whether it’s statically linked or not. A given system supports some finite number of executable formats. ELF is such a format, and it looks like your system isn’t set up to execute them.

Possibly silly question: Is the target system 32bit or 64bit?

Linux myhostname 2.6.32-754.25.1.el6.x86_64 #1 SMP x86_64 x86_64 x86_64 GNU/Linux

Maybe the kernel is way too old. That’s my guess.

file lwtTest.exe returns:
lwtTest.exe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

Interestingly ldd lwtTest.exe says:
statically linked

I think I understand what’s happening. This exe is statically linked with everything except for There needs to be a musl-x86_64.a static lib, but I don’t seem to find it.

Update: the static lib actually exists:

How do I tell ocaml.opt to link against it as opposed to ?

It’s not about linkage, it’s about the interpreter.

Think of it as the shebang for binary files.

This tells the system that it wants to be run through the /lib/ interpreter.

If that’s not there, you can’t run that binary on the system. Full stop.

There are ways to manipulate the ELF headers to accept another interpreter, but unless your system provides any musl interpreter I doubt it will work.

Best is probably to use a system in a VM that is as close as possible to what the target system is.

It is about the linkage.
On my workstation (Centos 7), a small C program compiled with musl-gcc is statically linked:

file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

file lwtTest.exe
lwtTest.exe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

and the compiled C program runs on the server

And you centos probably provides the already mentioned interpreter. Does your target system provide the interpreter as well, or any other musl interpreter that you might try to change it to using tools like patchelf?

No, it does not. I have no control over the server.

I am not speaking about changing the server, I’m speaking about controlling the deployed artifact

It took a while to get back to that issue. But you were right, patchelf did the trick.
patchelf lwtTest --set-interpreter ./ (this is the musl libc of course).

Thanks @NobbZ