How to link without stdlib in 5.4? caml_globals_inited is missing

I want to compile a helloworld without stdlib.
In 4.14.2 I used the following command, but in 5.4 it complaints about undefined reference to `caml_globals_inited’.

ocamlopt.opt -dstartup -S hello.ml -nopervasives -verbose -ccopt -lasmrun -ccopt -lm
+ as  -o 'hello.o' 'hello.s'
+ as  -o '/tmp/camlstartupea4d27.o' 'a.out.startup.s'
+ gcc  -Wl,-E  -o 'a.out'  '-L/mnt/mand/.opam/5.4.0+flambda/lib/ocaml' -lasmrun -lm '/tmp/camlstartupea4d27.o' 'hello.o'
/usr/bin/ld: /tmp/camlstartupea4d27.o: in function `caml_program':
:(.text+0x16): undefined reference to `caml_globals_inited'
collect2: error: ld returned 1 exit status
objdump -x `opam var lib`/ocaml/libasmrun.a | grep caml_globals_inited               5.4.0+flambda
0000000000000008 g     O .bss   0000000000000008 caml_globals_inited
0000000000000007 R_X86_64_PC32     caml_globals_inited-0x0000000000000004
00000000000008e9 R_X86_64_64       caml_globals_inited

The problematic symbol should be in libasmrun.a (I think) but something goes wrong.

$ grep -r caml_globals_inited `opam var lib`/ocaml                                    
grep: /mnt/mand/.opam/5.4.0+flambda/lib/ocaml/libasmrun_shared.so: binary file matches
grep: /mnt/mand/.opam/5.4.0+flambda/lib/ocaml/libasmrund.a: binary file matches
grep: /mnt/mand/.opam/5.4.0+flambda/lib/ocaml/libasmrun.a: binary file matches
grep: /mnt/mand/.opam/5.4.0+flambda/lib/ocaml/compiler-libs/ocamloptcomp.a: binary file matches
grep: /mnt/mand/.opam/5.4.0+flambda/lib/ocaml/compiler-libs/ocamloptcomp.cma: binary file matches
grep: /mnt/mand/.opam/5.4.0+flambda/lib/ocaml/compiler-libs/cmm_helpers.cmx: binary file matches
grep: /mnt/mand/.opam/5.4.0+flambda/lib/ocaml/libasmrun_pic.a: binary file matches
/mnt/mand/.opam/5.4.0+flambda/lib/ocaml/caml/stack.h:extern intnat caml_globals_inited;
grep: /mnt/mand/.opam/5.4.0+flambda/lib/ocaml/libasmruni.a: binary file matches

The problematic symbols is defined in ./runtime/dynlink_nat.c. Should I try to compile OCaml without dynlink (or something like this) for investigation? Should it be possible?

Edit: repo: riscv_amd64_demos/00orig at gc · Kakadu/riscv_amd64_demos · GitHub

Code
type out_channel
external open_descriptor_out : int -> out_channel
  = "caml_ml_open_descriptor_out"
let stdout = open_descriptor_out 1
external flush : out_channel -> unit = "caml_ml_flush"

external format_int : string -> int -> string = "caml_format_int"
let string_of_int n = format_int "%d" n
external output_char : out_channel -> char -> unit = "caml_ml_output_char"
external unsafe_output_string : out_channel -> string -> int -> int -> unit
  = "caml_ml_output"
external string_length : string -> int = "%string_length"

let output_string oc s =
  unsafe_output_string oc s 0 (string_length s)

let print_endline s =
  output_string stdout s; output_char stdout '\n'; flush stdout

let () = print_endline (string_of_int 42)

See my repository. This might be useful for you.

Code in your repo doesn’t compile for me both on 4.14.2 and 5.4 with like errors like

File "dune", line 2, characters 7-11:
2 |  (name main)
^^^^
Command [12] exited with code 2:
$ (cd _build/default && /home/kakadu/.opam/4.14.2+flambda/bin/ocamlopt.opt -w @1..3@5..28@30..39@43@46..47@49..57@61..62@67@69-40 -strict-sequence -strict-formats -short-paths -keep-locs -nostdlib -nopervasives -ccopt '-L /home/kakadu/.opam/4.14.2+flambda/lib/ocaml' -ccopt -lm -ccopt -ldl -cclib -lasmrun -verbose -g -o main.exe stdlib.o .main.eobjs/native/dune__exe.cmx .main.eobjs/native/dune__exe__Stdlib.cmx .main.eobjs/native/dune__exe__Main.cmx)
+ as --debug-prefix-map '/mnt/work2/asp/ocaml-without-stdlib/_build/default'='/workspace_root' -o '/tmp/build_2d98a3_dune/camlstartup0a7408.o' '/tmp/build_2d98a3_dune/camlstartupcf29a7.s'
+ gcc -O2 -fno-strict-aliasing -fwrapv -pthread -Wall -Wdeclaration-after-statement -fno-common -fexcess-precision=standard -fno-tree-vrp -ffunction-sections  -Wl,-E  -o 'main.exe'   -L /home/kakadu/.opam/4.14.2+flambda/lib/ocaml -lm -ldl '/tmp/build_2d98a3_dune/camlstartup0a7408.o' '.main.eobjs/native/dune__exe__Main.o' '.main.eobjs/native/dune__exe__Stdlib.o' '.main.eobjs/native/dune__exe.o' '-lasmrun' 'stdlib.o'
/usr/bin/ld: /home/kakadu/.opam/4.14.2+flambda/lib/ocaml/libasmrun.a(major_gc.n.o): in function `caml_major_collection_slice':
/home/kakadu/.opam/4.14.2+flambda/.opam-switch/build/ocaml-variants.4.14.2+options/runtime/major_gc.c:1075:(.text.caml_major_collection_slice+0x2ba): undefined reference to `fmin'
...

I can reproduce the same error, but if I use -cclib instead of -ccopt the error disappears.
If I’m not mistaken, it’s a problem of order on the command-line: -cclib goes after the OCaml objects, while -ccopt goes before, and the linker expects files in reverse dependency order.
I’m not sure why this doesn’t cause problems for 4.14.2, it’s possible that the extra flags that OCaml used to pass to gcc made it sort the files itself, or maybe the 4.14.2 version of the startup file doesn’t depend on libasmrun.

1 Like