How to generate a 32-bit binary on a 64-bit system?

I have some changes to the OCaml library code that I’ve tested with 64-bit binaries. Assuming OCaml still supports 32-bit binaries on some platforms, I’d like to test it with a 32-bit binary. How can I generate such a binary?

If relevant: my system is WSL2/Ubuntu on Windows. It should work the same as native Ubuntu for all intents and purposes. And at the moment my code is based on 4.10.


You have ocaml-variants compilers for 32bit on opam:

opam switch list-available | grep 32b

choose one and then:

opam switch create 4.**.*+32bit

It’s not completely clear from your question, but if your changes are on the compiler’s code itself then you need to setup your compiler to produce 32-bits executables.
As indicated here, the base configure command to use looks like this:

./configure CC="gcc -m32" AS="as --32" ASPP="gcc -m32 -c" --host i386-linux PARTIALLD="ld -r -melf_i386"

You can of course add other configuration options as needed.

You may need to install some ubuntu packages to get 32-bits support in gcc, the opam package suggests the gcc-multilib and g++-multilib packages.

1 Like

I recommend you to build it from Alpine-based Docker image then, with a -static switch.

Thanks to each of you for the suggestions. I thought I’d try vlaviron’s approach first.

if your changes are on the compiler’s code itself then you need to setup your compiler to produce 32-bits executables.

Yes, I need this. I’m not changing the compiler itself, but adding new library routines to Gc written in C.

After installing gcc-multilib, I can generate a 32-bit executable but I can’t run it. I don’t know if Ubuntu supports 32-bit executables or, less likely, if it is WSL2-related. (Generating a Win-32 executable wouldn’t help, BTW, because my code relies on having an ELF-format executable–I’m reading the symbol table from the executable file.)

$ gcc -m32 test.c
$ file a.out
a.out: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 3.2.0, BuildID[sha1]=5e7cf908319fd18b454f6d3b57b7caf7af664ff8, not stripped
$ ./a.out
bash: ./a.out: cannot execute binary file: Exec format error

Separately, I tried your ./configure as well as one with parameters more similar to what’s in the build script (though I couldn’t guess what prefix should be). Both of them generate a couple hundred lines of output before failing with:

cp runtime/ocamlrun boot/ocamlrun
make -C stdlib  \
  CAMLC='$(BOOT_OCAMLC) -use-prims ../runtime/primitives' all
make[2]: Entering directory '/mnt/c/proj/ocaml/stdlib'
../boot/ocamlrun ../boot/ocamlc -use-prims ../runtime/primitives -strict-sequence -absname -w +a-4-9-41-42-44-45-48 -g -warn-error A -bin-annot -nostdlib -safe-string -strict-formats  -nopervasives -c camlinternalFormatBasics.mli
sed -e "s|%%VERSION%%|`sed -e 1q ../VERSION | tr -d '\r'`|" sys.mlp >
../boot/ocamlrun: 1: ../boot/ocamlrun: Syntax error: word unexpected (expecting ")")
Makefile:217: recipe for target 'camlinternalFormatBasics.cmi' failed
make[2]: *** [camlinternalFormatBasics.cmi] Error 2
make[2]: *** Waiting for unfinished jobs....
make[2]: Leaving directory '/mnt/c/proj/ocaml/stdlib'
Makefile:347: recipe for target 'coldstart' failed
make[1]: *** [coldstart] Error 2
make[1]: Leaving directory '/mnt/c/proj/ocaml'
Makefile:476: recipe for target 'world.opt' failed
make: *** [world.opt] Error 2

I’m not sure what to try next.

@ dakk A good thought, although I expect it has the same issue described above. Since I’m adding code to OCaml itself, I need to be able to compile my changes incrementally outside of opam. Building within opam is always a full build (several minutes) while an incremental build with make is only several seconds.

If you are using WSL2, I believe you should be able to run 32-bit binaries by doing (this is from memory, not tested)

sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libc6:i386


Those 3 commands are not enough. I still see the issue. If you can find the proper incantation, I’d be grateful. Thanks.

I recently added support for i386 builds into the Docker images on both ocaml/opam2 and ocurrent/opam images. It’s slightly more involved than a conventional Docker image due to some multiarch limitations, but something like this should work to test i386 on your WSL2 machine:

  • install Docker
  • export DOCKER_CLI_EXPERIMENTAL=enabled
  • docker manifest inspect ocurrent/opam
  • note down the digest of the 386 architecture (e.g. currently sha256:6116a4d0b48b6a22c68b1e447d219eda07eb04eb109689721250dfd50cbbb74c).
  • use a Dockerfile like the below:
FROM ocurrent/opam:sha256:6116a4d0b48b6a22c68b1e447d219eda07eb04eb109689721250dfd50cbbb74c
WORKDIR /home/opam/src
COPY --chown=opam . /home/opam/src
RUN opam pin add . -n
RUN opam depext <your package>
RUN opam install -y --deps-only .
RUN opam exec -- dune build
1 Like

I had an incorrect assumption. I was unaware that I had to run a conversion step to switch from WSL1 to WSL2. (I only installed the new version of Windows a week or two ago.) This became obvious from the Docker installation instructions, since Docker requires WSL2.

In the end, I was able to run with nojb’s and vlaviron’s instructions. (I also did an apt-get upgrade afterword. That may not have been necessary.) Thanks for the help!