js_of_ocaml icon indicating copy to clipboard operation
js_of_ocaml copied to clipboard

Wamr Loader/GC giving a type mismatch failure

Open nickbetteridge opened this issue 8 months ago • 21 comments

I'm too sure whether the issue I'm having is with the ocaml wasm compiler or with the bytecodealliance's runtime (wamr).

I've wrote a simple ocaml program:

let _ = Printf.printf "Hello")

compiled it with:

ocamlfind ocamlc -package js_of_ocaml -linkpkg -o hello.byte hello.ml

generated the wasm:

wasm_of_ocaml hello.byte

After building the wamr runtime with the following flags ...

-DWAMR_BUILD_TAIL_CALL=1
-DWAMR_BUILD_EXCE_HANDLING=1
-DWAMR_BUILD_GC=1

... I was able to run a simple wasm file generated from C.

When attempting to run the ocaml wasm file I got the following error:

WASM module load failed: type mismatch: expect (ref ht) but got other

So, could anyone suggest possible ways for me to narrow down the cause of the error?

nickbetteridge avatar Apr 10 '25 15:04 nickbetteridge

I think I fixed this issue in https://github.com/bytecodealliance/wasm-micro-runtime/pull/4075.

But the implementation of the GC proposal in WAMR is partial and not sufficient to run programs produced by wasm_of_ocaml. I have just reopened an issue: https://github.com/bytecodealliance/wasm-micro-runtime/issues/4190

vouillon avatar Apr 10 '25 17:04 vouillon

Ah, sorry - you're right, I was using an older commit.

However, I rebuilt and this time got a unhandled SIGSEGV, si_addr: 0x575800000002 - it does seem to be fragile

What's the best way of debugging this? Perhaps I should just raise a new issue on bytecodealliance/wasm-micro-runtime?

nickbetteridge avatar Apr 11 '25 07:04 nickbetteridge

You can get a backtrace by running it with gdb:

gdb --args ~/sources/wasm-micro-runtime/product-mini/platforms/linux/build/iwasm hello.assets/code*.wasm

It seems it fails because the Wasm module is not a WASI module, and then crashes while trying to unload the module. You can raise an issue on bytecodealliance/wasm-micro-runtime, indeed. By the way, if you want to produce WASI modules (which do not depend on some JavaScript helper code), you should use https://github.com/ocsigen/js_of_ocaml/pull/1831 and compile using the --enable wasi option.

0x000055555567bb68 in destroy_init_expr_data_recursive (module=0x55555574d340, 
    data=0x555500000005)
    at /home/jerome/sources/wasm-micro-runtime/core/iwasm/interpreter/wasm_loader.c:509
509	    wasm_type = module->types[struct_init_values->type_idx];
(gdb) bt
#0  0x000055555567bb68 in destroy_init_expr_data_recursive (
    module=0x55555574d340, data=0x555500000005)
    at /home/jerome/sources/wasm-micro-runtime/core/iwasm/interpreter/wasm_loader.c:509
#1  0x000055555567bd7a in destroy_init_expr_data_recursive (
    module=0x55555574d340, data=0x55555577f790)
    at /home/jerome/sources/wasm-micro-runtime/core/iwasm/interpreter/wasm_loader.c:552
#2  0x000055555567bc72 in destroy_init_expr_data_recursive (
    module=0x55555574d340, data=0x55555577f970)
    at /home/jerome/sources/wasm-micro-runtime/core/iwasm/interpreter/wasm_loader.c:535
#3  0x00005555556a5f7d in destroy_init_expr (module=<optimized out>, 
    expr=<optimized out>)
    at /home/jerome/sources/wasm-micro-runtime/core/iwasm/interpreter/wasm_loader.c:647
#4  destroy_init_expr (module=<optimized out>, expr=<optimized out>)
    at /home/jerome/sources/wasm-micro-runtime/core/iwasm/interpreter/wasm_loader.c:642
#5  wasm_loader_unload (module=0x55555574d340)
    at /home/jerome/sources/wasm-micro-runtime/core/iwasm/interpreter/wasm_loader.c:6889
#6  0x00005555556a69ac in wasm_loader_load (buf=buf@entry=0x7ffff7e56010 "", 
    size=size@entry=167172, args=args@entry=0x7fffffffd580, 
    error_buf=error_buf@entry=0x7fffffffd8f0 "WASM module load failed: a module with WASI apis must export memory by default", 
    error_buf_size=error_buf_size@entry=128)
    at /home/jerome/sources/wasm-micro-runtime/core/iwasm/interpreter/wasm_loader.c:6794

vouillon avatar Apr 11 '25 08:04 vouillon

Thanks for the above! Yes, after building the wasi branch, array as a field in struct object is definitely a show stopper - I'll add frustration to 4190. Thanks again for your help

nickbetteridge avatar Apr 11 '25 09:04 nickbetteridge

I think I fixed this issue in bytecodealliance/wasm-micro-runtime#4075.

But the implementation of the GC proposal in WAMR is partial and not sufficient to run programs produced by wasm_of_ocaml. I have just reopened an issue: bytecodealliance/wasm-micro-runtime#4190

I'm going to spend some time on this soon - any suggestions on how to proceed? - ie do I need to attempt to implement the whole gc spec for ocaml on wamr or do I just need a subset?

nickbetteridge avatar May 09 '25 11:05 nickbetteridge

The GC spec is already mostly implemented. I think there are only two things missing, both related to the initialization of global variables.

First, WAMR does not support nesting arrays inside structures in constant expressions:

(type $bytes (array (mut i8)))
(type $struct (struct (field (ref $bytes))))
(global $s (ref $struct)
   (struct.new $struct (array.new_fixed $bytes 1 (i32.const 48))))

This should not be too hard to fix by refactoring a bit core/iwasm/interpreter/wasm_runtime.c.

The other issue is that, with the GC proposal, global.get is a constant instruction and can access preceding (immutable) global definitions:

(type $bytes (array (mut i8)))
(type $block (array (ref eq)))
(global $a (ref $bytes) (array.new_fixed $bytes 1 (i32.const 48)))
(global $b (ref $block) (array.new_fixed $block 1 (global.get $a)))

Given the way the initialization of global variables is performed in WAMR, that might be complicated to implement. But as a temporary workaround, I could add a flag to wasm_of_ocaml so that it does not generate this kind of code.

vouillon avatar May 09 '25 13:05 vouillon

Thanks for the above! I'll get onto it as soon as I possibly can. Adding a flag to wasm_of_ocaml would be useful - certainly in the early stages of getting wamr/ocaml to work.

nickbetteridge avatar May 09 '25 14:05 nickbetteridge

I've been doing some work on this in [https://github.com/nickbetteridge/wasm-micro-runtime]

When I compile a simple ocaml file with wasm_of_ocaml and run it with iwasm, I get a WASM module load failed: a module with WASI apis must export memory by default error - is there any way I can run wasm_of_ocaml to include the export or perhaps this is something I need to include in the ocaml file?

nickbetteridge avatar Aug 06 '25 08:08 nickbetteridge

When I compile a simple ocaml file with wasm_of_ocaml and run it with iwasm, I get a WASM module load failed: a module with WASI apis must export memory by default error - is there any way I can run wasm_of_ocaml to include the export or perhaps this is something I need to include in the ocaml file?

Are you sure you used the wasi branch and compiled your program with the --enable wasi flag?

vouillon avatar Aug 19 '25 14:08 vouillon

Yes, used —enable wasi

On Tue, 19 Aug 2025 at 16:58, Jérôme Vouillon @.***> wrote:

vouillon left a comment (ocsigen/js_of_ocaml#1929) https://github.com/ocsigen/js_of_ocaml/issues/1929#issuecomment-3201110317

When I compile a simple ocaml file with wasm_of_ocaml and run it with iwasm, I get a WASM module load failed: a module with WASI apis must export memory by default error - is there any way I can run wasm_of_ocaml to include the export or perhaps this is something I need to include in the ocaml file?

Are you sure you used the wasi branch and compiled your program with the --enable wasi flag?

— Reply to this email directly, view it on GitHub https://github.com/ocsigen/js_of_ocaml/issues/1929#issuecomment-3201110317, or unsubscribe https://github.com/notifications/unsubscribe-auth/AC3HXJLPDWXGIZ4O2CVGONL3OM3RRAVCNFSM6AAAAAB2377W2WVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTEMBRGEYTAMZRG4 . You are receiving this because you authored the thread.Message ID: @.***>

nickbetteridge avatar Aug 19 '25 16:08 nickbetteridge

I have tried with an empty OCaml program. Your branch of Wamr was compiled with

cmake .. -DWAMR_BUILD_GC=1 -DWAMR_BUILD_EXCE_HANDLING=1 -DWAMR_BUILD_TAIL_CALL=1 -DWAMR_BUILD_FAST_INTERP=0

When compiling with wasm_of_ocaml --enable wasi /tmp/a.out, I get the following errors:

$ ~/sources/wasm-micro-runtime/product-mini/platforms/linux/build/iwasm /tmp/a.assets/code.wasm
[...]
[14:17:06:425 - 7B5C41ABE780]: Invalid heap object pointer detected: 0x5d86fffffff0 (heap: 0x7b5c41a9d2cc-0x7b5c41abd00c)
[14:17:06:425 - 7B5C41ABE780]: warning: failed to allocate memory for gc object
Exception: create array object failed

With just wasm_of_ocaml /tmp/a.out, I get the following errors:

$ ~/sources/wasm-micro-runtime/product-mini/platforms/linux/build/iwasm /tmp/a.assets/code-4179a667264f1fc2c2b4.wasm 
WASM module load failed: a module with WASI apis must export memory by default

vouillon avatar Aug 20 '25 09:08 vouillon

Ah - that's what happens when you're in a rush to get down to the South of France and copy the wrong tree onto a small 10 year-old laptop!

Anyhow - a quick question - I'm trying to get your tests to run and the ones that are failing (with null references) are the ones depending on io/stdlib runtime - ie printf. I've '--enable wasi' so I'm not too sure if I also need to also either '--link' or '--link-wasm' as well? As I'm not currently using dune with using wasm_of_ocaml (compilation_mode), what is the build-chain that I'm missing?

nickbetteridge avatar Sep 01 '25 16:09 nickbetteridge

No, you don't have to do anything special.

vouillon avatar Sep 04 '25 15:09 vouillon

I'm having an issue with string_of_int.

let () =
       print_endline "Testing Option value extraction";
       let x = Some 42 in
       print_endline "Option created";
       match x with
       | Some n ->
           print_endline "Matched Some";
           print_endline ("Value: " ^ (string_of_int n))
       | None ->
           print_endline "No value"

The crash occurs right after "Matched Some" is printed. The issue happens when OCaml tries to extract the integer value from the Some wrapper and use it in string_of_int.

I'm having to use Claude here for debugging this, and I'm not too sure if what it understands is correct, and what it proposes is the best way forward.

The Issue: The object 0x56910000002a contains the value 0x2a (which is 42
  in hex), but it's NOT a proper i31 object. When OCaml tries to cast it to
  i31 type for use in string_of_int, the cast fails because WAMR doesn't
  recognize it as a valid i31 reference.

  The Analysis:
  - 0x56910000002a - This looks like a heap object pointer with the value 42
   (0x2a) in the lower bits
  - Expected: An i31-encoded reference like 0x55 (which we saw working in
  earlier tests)
  - Actual: A heap object that contains the value 42 but is not itself an
  i31 reference

  This confirms our hypothesis: OCaml stores the integer 42 inside a GC 
  object wrapper (probably as a struct field), and when it extracts the
  value for use in string_of_int, it tries to cast the wrapper object to i31
   type instead of extracting the contained value first.

  The solution requires either:
  1. Fix WAMR's i31 cast validation to handle wrapped integers correctly, OR

  2. Fix WAMR's struct.get operations to properly extract i31 values from GC
   object fields

What are your thoughts on this?

nickbetteridge avatar Sep 10 '25 07:09 nickbetteridge

Another quick question - do you know of any work-in-progress, which might be publicly released, which relates to WIT [1]. This seems like the obvious way forward with interfacing with the real world. If nothing is happening on this front then I'll put something together - I could build this into js_of_ocaml or, alternatively, make it a separate library?

[1] https://component-model.bytecodealliance.org/design/wit.html

nickbetteridge avatar Sep 10 '25 09:09 nickbetteridge

I'm having an issue with string_of_int.

How can I reproduce this?

vouillon avatar Sep 12 '25 13:09 vouillon

I'm not aware of any WIT-related work. It probably makes more sense to make it a separate library.

vouillon avatar Sep 12 '25 13:09 vouillon

I'm having an issue with string_of_int.

How can I reproduce this?

git clone https://github.com/nickbetteridge/wasm-micro-runtime

git clone https://github.com/nickbetteridge/wasm-micro-runtime-ocaml-test

# cd wasm-micro-runtime-ocaml-test

Edit the Makefile to set WAMR_DIR to the appropriate path.

# make build-wamr

# make test_option_extract_value.wasm

make run-test_option_extract_value

There are similar issues with other tests, such as test_pattern_match_debug, with externref and casting being the bad actors

nickbetteridge avatar Sep 17 '25 15:09 nickbetteridge

I31 values in globals are not correctly initialized:

diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c
index addd0fc7..bae2a85b 100644
--- a/core/iwasm/interpreter/wasm_loader.c
+++ b/core/iwasm/interpreter/wasm_loader.c
@@ -1455,6 +1455,7 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                                                   error_buf, error_buf_size)) {
                             goto fail;
                         }
+                        cur_value.gc_obj =  (WASMObjectRef) wasm_i31_obj_new(cur_value.i32);
 
                         wasm_set_refheaptype_common(&cur_ref_type.ref_ht_common,
                                                     false, HEAP_TYPE_I31);

There is still something wrong with the test since it outputs Value: 85 instead of Value: 42. I don't really see why since the interpreter seems to do the right thing for the instruction WASM_OP_I31_GET_S.

For test_pattern_match_debug, it seems one get an incorrect pointer, since no externref is expected there.

vouillon avatar Sep 18 '25 14:09 vouillon

Actually, the fix above is incorrect. The issue is here: https://github.com/nickbetteridge/wasm-micro-runtime/blob/7ed8696a7f09053891ad0f70adec1969338269a3/core/iwasm/interpreter/wasm_runtime.c#L1193-L1194

            wasm_array_obj_set_elem(array_obj, elem_idx,
                                    &init_values->elem_data[elem_idx]);

The contents of init_values should be first converted into the appropriate Wasm object when the element has a reference type.

And here as well: https://github.com/nickbetteridge/wasm-micro-runtime/blob/7ed8696a7f09053891ad0f70adec1969338269a3/core/iwasm/interpreter/wasm_runtime.c#L3263C44-L3265

vouillon avatar Sep 18 '25 14:09 vouillon

Well, I gave up, in the end, with wamr and switched to using wasmtime - I'm getting relatively small runtimes which are acceptable for the controllers, and to ease the flow I wrote a wit library. I'd like to release the wit library quite soon but I'm being held up by the wasi runtime PR - just the convenience of not requiring users to build a branch etc. Do you have any idea when the PR is likely to be merged? Also, thanks again for all the help you provided in trying to get wamr to run

nickbetteridge avatar Nov 10 '25 18:11 nickbetteridge