Skip to content

Function signature mismatch when targetting wasm32-unknown-emscripten #954

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
curiousdannii opened this issue Feb 20, 2024 · 5 comments
Closed

Comments

@curiousdannii
Copy link

I have an exported Rust function:

#[no_mangle]
pub extern "C" fn gidispatch_get_objrock_fileref(ptr: FileRefPtr) -> DispatchRockPtr {
    ...
}

Which is called from a support C function:

extern gidispatch_rock_t gidispatch_get_objrock_fileref(void *obj);

gidispatch_rock_t gidispatch_get_objrock(void *obj, glui32 objclass) {
    switch (objclass) {
        case gidisp_Class_Fileref:
            return gidispatch_get_objrock_fileref(obj);
    }
}

Everything works fine with on x86_64-unknown-linux-gnu, but now that I'm trying to target wasm32-unknown-emscripten I'm suddenly getting an error:

wasm-ld: error: function signature mismatch: gidispatch_get_objrock_fileref
>>> defined as (i32, i32) -> void in /src/remglk/target/wasm32-unknown-emscripten/debug/libremglk_capi.a(support.o)
>>> defined as (i32) -> i32 in /src/remglk/target/wasm32-unknown-emscripten/debug/libremglk_capi.a(remglk_capi-e60aef9700ee309a.590b7ctqlq0880bk.rcgu.o)

It's like it's turned the return into a second argument. #715 looks similar, but I'm not targeting wasm32-unknown-unknown.

Any ideas how to get this working?

@curiousdannii
Copy link
Author

Well I tried compiling the C files as a normal Emscripten staticlib, and I'm still getting the same error. So this might not be a problem with cc-rs at all.

@hanna-kruppe
Copy link
Contributor

On the C side, gidispatch_rock_t is a union while the Rust side's DispatchRockPtr is a bare pointer. The union has the same size and alignment as a pointer, but scalars and aggregates are not generally interchangeable for ABI purposes. Most C ABIs have some rules for passing very small and simple aggregates the same way as a scalar, which can paper over the difference sometimes, but not all of them do it at all and in the same way. For example, the C ABI on x86-64 Linux happens to pass gidispatch_rock_t as if it was a single integer, while the Webassembly C ABI only has a carveout for "singleton" unions and structs, which doesn't include gidispatch_rock_t (so it falls into the "indirect" bucket, which explains the (i32, i32) -> void signature).

@curiousdannii
Copy link
Author

curiousdannii commented Feb 20, 2024

Thank you so much for that explanation, especially as it would've involved searching for my code to see the types involved. I had thought that using the recommended Rust way of opaque pointers would be able to handle anything C gave it, which it can when compiled to x86_64, but it's not actually the same API, so I was just lucky it worked in the x86_64 ABI. I'm going to try changing DispatchRockPtr on the Rust side to be an actual union type, or else changing it to manually use out parameters.

Edit: Well changing the Rust definition of DispatchRock to a union didn't help. I'll have to try an out parameter.

Edit 2: Out parameters seems to work. :)

@hanna-kruppe
Copy link
Contributor

That’s odd, a repr(C) union should be ABI-compatible. I’ll take a closer look later (I’ve been meaning to dive deeper into the remglk-rs code since you announced it, since I’d like to use it in a project of mine eventually) but it almost certainly won’t be a cc-rs issue.

@curiousdannii
Copy link
Author

Oh! I didn't realise you were the Hanna from the forum! Small world haha.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants