Trying to cross-compile quickjs-rs to wasm32-wasi target

I'm trying to debug this linkage error since a few days now and I cannot figure out what is going on. The lib compiles and a libquickjs.a is generated fine. I am using the wasimake tool to generate proper Makefile for wasm-wasi target. When I try to link it to a binary I get the error is:

  = note: rust-lld: error: function signature mismatch: JS_Eval
          >>> defined as (i32, i32, i32, i32, i32, i32) -> void in /home/xx/quickjs-rs/target/wasm32-wasi/debug/deps/libquickjs_sys-995f03fd80479779.4xbv5ant39qjmd1j.rcgu.o
          >>> defined as (i32, i32, i32, i32, i32) -> i64 in /home/xx/quickjs-rs/target/wasm32-wasi/debug/build/libquickjs-sys-e4acb27dbeb38a89/out/quickjs/libquickjs.a(quickjs.c.obj)

The fork where I'm trying to do this is here: GitHub - rafaelcaricio/quickjs-rs: Rust wrapper for the quickjs Javascript engine.

Since it compiles fine for the target x86_64-unknown-linux-gnu I imagine it is something related to wasm32-wasi that, for some reason, changes the signature of the functions exported by the library. But I have cross-compiled successfully the chimlib crate to wasm-wasi and ran the binary with wasmer runtime without any problems. I really have no idea where this different function signature is coming from.

I am completely lost here. I don't know what to look for. Any help would be really appreciated. Any pointers to where I should look to debug this issue?

1 Like

After a lot more debugging, I figured what seems to be going on. But I still don't fully understand why and how to fix it. Or if this is some limitation of the bindgen tool.

The mismatch signature is caused because bindgen, for some reason, does change the signature of the imported function when it is declared to return a struct. Original function/struct declaration in C:

typedef union JSValueUnion {
    int32_t int32;
    double float64;
    void *ptr;
} JSValueUnion;

typedef struct JSValue {
    JSValueUnion u;
    int64_t tag;
} JSValue;

JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
                const char *filename, int eval_flags);

Bindgen generates the following Rust code:

#[repr(C)]
#[derive(Copy, Clone)]
pub union JSValueUnion {
    pub int32: i32,
    pub float64: f64,
    pub ptr: *mut ::std::os::raw::c_void,
    _bindgen_union_align: u64,
}

#[repr(C)]
#[derive(Copy, Clone)]
pub struct JSValue {
    pub u: JSValueUnion,
    pub tag: i64,
}

extern "C" {
    pub fn JS_Eval(
        ctx: *mut JSContext,
        input: *const ::std::os::raw::c_char,
        input_len: usize,
        filename: *const ::std::os::raw::c_char,
        eval_flags: ::std::os::raw::c_int,
    ) -> JSValue;
}

That looks perfectly fine and correct. But for some reason, after compiling to object file the JS_Eval function changes the parameters signature to:

(i32, i32, i32, i32, i32, i32) -> void

Which when trying to link, the error mentioned in my original post happens.

The wrong signature is in the binary that imports the function exported correctly from the object file. For the error above I could compile and run successfully the code after changing the return type from JSValue to i64. The version that compiles fine for target wasm32-wasi is:

extern "C" {
    pub fn JS_Eval(
        ctx: *mut JSContext,
        input: *const ::std::os::raw::c_char,
        input_len: usize,
        filename: *const ::std::os::raw::c_char,
        eval_flags: ::std::os::raw::c_int,
    ) -> i64;
}

Any ideas why this is happening? If I compile to x86_64-unknwon-linux-gnu I don't need to change the code generated by bindgen.

I tried to nail down this more, on the hope to pin point the problem that I think resides in the bindgen, but I could not. I was able to use the same types in a reduced codebase and it all ran just fine. This work can be found here: GitHub - rafaelcaricio/wasm32-wasi-export-struct-sample at f5d18a29040aefe53d638f70beb5764528ffd2ba

Now I have the suspicious that there might be a bug in rustc? I would love to find exactly what causes this. If anyone is willing to help, I would greatly appreciate. My objective is to send back the support for wasm32-wasi upstream to GitHub - theduke/quickjs-rs: Rust wrapper for the quickjs Javascript engine. when I figure this out.

After some more investigation. Now everything is clear to me. I was hitting two issues here. First, I missed that there was some declarations that depends on compiler macro __x86_64__ in the C code I was compiling to WASM. It seems to me what either I have to modify the compiler command to force the macro __x86_64__ to exist, or maybe there is some compiler predefined macro for WASM target already?

Another issue is Pass small structs in parameters instead of memory · Issue #88 · WebAssembly/tool-conventions · GitHub which I am now blocked.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.