mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 23:04:33 +00:00
Document the broken C ABI of wasm32-unknown-unknown
Inspired by discussion on https://github.com/rust-lang/rust/issues/129486 this is intended to at least document the current state of the world in a more public location than throughout a series of issues.
This commit is contained in:
parent
22572d0994
commit
992b0b3fe7
@ -195,3 +195,116 @@ conditionally compile code instead. This is notably different to the way native
|
|||||||
platforms such as x86\_64 work, and this is due to the fact that WebAssembly
|
platforms such as x86\_64 work, and this is due to the fact that WebAssembly
|
||||||
binaries must only contain code the engine understands. Native binaries work so
|
binaries must only contain code the engine understands. Native binaries work so
|
||||||
long as the CPU doesn't execute unknown code dynamically at runtime.
|
long as the CPU doesn't execute unknown code dynamically at runtime.
|
||||||
|
|
||||||
|
## Broken `extern "C"` ABI
|
||||||
|
|
||||||
|
This target has what is considered a broken `extern "C"` ABI implementation at
|
||||||
|
this time. Notably the same signature in Rust and C will compile to different
|
||||||
|
WebAssembly functions and be incompatible. This is considered a bug and it will
|
||||||
|
be fixed in a future version of Rust.
|
||||||
|
|
||||||
|
For example this Rust code:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[repr(C)]
|
||||||
|
struct MyPair {
|
||||||
|
a: u32,
|
||||||
|
b: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn take_my_pair(pair: MyPair) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn call_c() -> u32 {
|
||||||
|
take_my_pair(MyPair { a: 1, b: 2 })
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
compiles to a WebAssembly module that looks like:
|
||||||
|
|
||||||
|
```wasm
|
||||||
|
(module
|
||||||
|
(import "env" "take_my_pair" (func $take_my_pair (param i32 i32) (result i32)))
|
||||||
|
(func $call_c
|
||||||
|
i32.const 1
|
||||||
|
i32.const 2
|
||||||
|
call $take_my_pair
|
||||||
|
)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
The function when defined in C, however, looks like
|
||||||
|
|
||||||
|
```c
|
||||||
|
struct my_pair {
|
||||||
|
unsigned a;
|
||||||
|
unsigned b;
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned take_my_pair(struct my_pair pair) {
|
||||||
|
return pair.a + pair.b;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```wasm
|
||||||
|
(module
|
||||||
|
(import "env" "__linear_memory" (memory 0))
|
||||||
|
(func $take_my_pair (param i32) (result i32)
|
||||||
|
local.get 0
|
||||||
|
i32.load offset=4
|
||||||
|
local.get 0
|
||||||
|
i32.load
|
||||||
|
i32.add
|
||||||
|
)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice how Rust thinks `take_my_pair` takes two `i32` parameters but C thinks it
|
||||||
|
only takes one.
|
||||||
|
|
||||||
|
The correct definition of the `extern "C"` ABI for WebAssembly is located in the
|
||||||
|
[WebAssembly/tool-conventions](https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md)
|
||||||
|
repository. The `wasm32-unknown-unknown` target (and only this target, not other
|
||||||
|
WebAssembly targets Rust support) does not correctly follow this document.
|
||||||
|
|
||||||
|
Example issues in the Rust repository about this bug are:
|
||||||
|
|
||||||
|
* [#115666](https://github.com/rust-lang/rust/issues/115666)
|
||||||
|
* [#129486](https://github.com/rust-lang/rust/issues/129486)
|
||||||
|
|
||||||
|
This current state of the `wasm32-unknown-unknown` backend is due to an
|
||||||
|
unfortunate accident which got relied on. The `wasm-bindgen` project prior to
|
||||||
|
0.2.89 was incompatible with the "correct" definition of `extern "C"` and it was
|
||||||
|
seen as not worth the tradeoff of breaking `wasm-bindgen` historically to fix
|
||||||
|
this issue in the compiler.
|
||||||
|
|
||||||
|
Thanks to the heroic efforts of many involved in this, however, the nightly
|
||||||
|
compiler currently supports a `-Zwasm-c-abi` implemented in
|
||||||
|
[#117919](https://github.com/rust-lang/rust/pull/117919). This nightly-only flag
|
||||||
|
can be used to indicate whether the spec-defined version of `extern "C"` should
|
||||||
|
be used instead of the "legacy" version of
|
||||||
|
whatever-the-Rust-target-originally-implemented. For example using the above
|
||||||
|
code you can see (lightly edited for clarity):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ rustc +nightly -Zwasm-c-abi=spec foo.rs --target wasm32-unknown-unknown --crate-type lib --emit obj -O
|
||||||
|
$ wasm-tools print foo.o
|
||||||
|
(module
|
||||||
|
(import "env" "take_my_pair" (func $take_my_pair (param i32) (result i32)))
|
||||||
|
(func $call_c (result i32)
|
||||||
|
)
|
||||||
|
;; ...
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
which shows that the C and Rust definitions of the same function now agree like
|
||||||
|
they should.
|
||||||
|
|
||||||
|
The `-Zwasm-c-abi` compiler flag is tracked in
|
||||||
|
[#122532](https://github.com/rust-lang/rust/issues/122532) and a lint was
|
||||||
|
implemented in [#117918](https://github.com/rust-lang/rust/issues/117918) to
|
||||||
|
help warn users about the transition. The current plan is to, in the future,
|
||||||
|
switch `-Zwasm-c-api=spec` to being the default. Some time after that the
|
||||||
|
`-Zwasm-c-abi` flag and the "legacy" implementation will all be removed.
|
||||||
|
Loading…
Reference in New Issue
Block a user