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.

Thanks.

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

opam switch list-available | grep 32b

choose one and then:

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

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 > sys.ml
../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

Cheers,
Nicolás

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
3 Likes

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!

Just wanted to note that this approach works really well.

For folks wondering why Docker might choke on this Dockerfile, it should use an @ sign rather than a : character for the first part, i.e.:

FROM ocurrent/opam@sha256:6116a4d0b48b6a22c68b1e447d219eda07eb04eb109689721250dfd50cbbb74c
2 Likes