Skip to content

Accessing foreign global variables under wasm32-unknown-unknown target #60825

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
mpapierski opened this issue May 14, 2019 · 7 comments
Closed
Labels
A-linkage Area: linking into static, shared libraries and binaries C-bug Category: This is a bug. O-wasm Target: WASM (WebAssembly), http://webassembly.org/ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@mpapierski
Copy link

When trying to compile a code that uses foreign global variables under wasm32-unknown-unknown it appears that wrong code is generated. My use case is that I want to export a variable foo from the host, so Rust program compiled into wasm can use value of this variable.

I tried this code:

extern "C" {
  #[no_mangle]
  static foo: i32;
}
  

#[no_mangle]
pub extern "C" fn bar() -> i32 {
  unsafe { foo }
}

Compiled with:

rustc --target wasm32-unknown-unknown -O --crate-type=cdylib src/main.rs -o wasm_test.wasm

Produces following WASM code:

(module
  (type (;0;) (func))
  (type (;1;) (func (result i32)))
  (func $__wasm_call_ctors (type 0))
  (func $bar (type 1) (result i32)
    i32.const 0
    i32.load)
  (table (;0;) 1 1 anyfunc)
  (memory (;0;) 16)
  (global (;0;) (mut i32) (i32.const 1048576))
  (global (;1;) i32 (i32.const 1048576))
  (global (;2;) i32 (i32.const 1048576))
  (export "memory" (memory 0))
  (export "__heap_base" (global 1))
  (export "__data_end" (global 2))
  (export "bar" (func $bar)))

Rust appears to treat variable extern "C" static foo: i32; as variable with local linkage without emitting "import global" opcodes.

This should be the direct translation into C++ code. This also declares a variable with external linkage:

extern int foo;

int bar() { return foo; }

Compiled into WASM:

(module
 (import "env" "foo" (global $foo i32))
 (table 0 anyfunc)
 (memory $0 1)
 (export "memory" (memory $0))
 (export "_Z3barv" (func $_Z3barv))
 (func $_Z3barv (; 0 ;) (result i32)
  (i32.load
   (get_global $foo)
  )
 )
)

Note the (import "env" "foo" (global $foo i32)): This is what C++ compiler emits when it encounters a variable declared with an external linkage. Source

I believe current behaviour of Rust compiler is incorrect and wrong WASM code is generated for variables with external C linkage. Perhaps there is a special syntax for emitting (import "env" "foo" (global $foo i32))? Right now the only workaround seems to be using functions instead of variables to communicate between host and the wasm program which works perfectly fine. Please let me know if I missed something and there is an easy way to achieve this.

Meta

rustc --version --verbose:

rustc 1.36.0-nightly (a9ec99f 2019-05-13)

I've also tried on stable, and other versions of rust nightly.

@jonas-schievink jonas-schievink added A-linkage Area: linking into static, shared libraries and binaries C-bug Category: This is a bug. O-wasm Target: WASM (WebAssembly), http://webassembly.org/ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels May 14, 2019
@CryZe
Copy link
Contributor

CryZe commented May 14, 2019

The online tool you linked is based on LLVM 5, which is very old, so it's more likely that the code compiled by C++ there is incorrect. In fact get_global is unlikely to be the correct thing, as returning a pointer to the extern would have to yield a proper pointer, but wasm globals are not part of the linear memory's address space and thus can't produce a pointer. And in fact if you try doing exactly that, the C++ compiler there emits garbage.

@mpapierski
Copy link
Author

@CryZe Interesting. Thanks for reply.

What do you mean by proper pointer? These variables have a type in the host, so hopefully they can be represented by i32 in WASM. In other words - is current behavior of compiler by design? Although WASM spec allows this (so LLVM output seems accurate even though its old) as far as I can tell - i.e. https://github.com/WebAssembly/mutable-global/blob/master/proposals/mutable-global/Overview.md which was merged upstream I still think it may be a bug - or perhaps an incomplete implementation of codegen?

@fd
Copy link

fd commented Aug 26, 2019

I'm running into the same issue. Imported global appear to be broken currently.

@RReverser
Copy link
Contributor

Noticed same issue. @CryZe in example above there are no pointers involved. static variable should just import WebAssembly global, which on JavaScript side is represented by WebAssembly.Global interface and allows to share variables that can be mutated and read from either side.

@CryZe
Copy link
Contributor

CryZe commented Aug 30, 2019

Sure but that sounds like you want some intrinsics via std::arch and not statics?! Like what is even supposed to happen here if you take a reference and then dereference it? Unless that is supposed to be Undefined Behavior. But can we even make it Undefined Behavior? Pretty sure getting a reference and dereferencing a static is not considered UB by Rust, so it can't be made UB when compiling to wasm?! (Or can it?)

Unless Rust adopts what C++ seems to be doing, which is always treating the static like a pointer into linear memory and dereferencing it. This seems fairly reasonable too. All wasm globals will be pointers then though, never actual integers or floating point numbers. Although if they are pointers, that would mean that the environment somehow needs to "allocate" the actual variable into linear memory, which seems hard to do if you don't want to hardcode that for a specific wasm file into the environment.

@RReverser
Copy link
Contributor

@CryZe C++ behaviour seems to be the closest to correct one here. The end result has to be that extern { static ... } should always produce the same as what pub static ... currently produces, except one is an export and another is an import, but they need to be able to communicate each other when modules are loaded together on the same memory.

@alexcrichton
Copy link
Member

Custom globals are not supported by the WebAssembly target and I believe C/C++ have since been updated to reflect this as well. There's currently no plans to support this, and I'd recommend opening an issue on https://github.com/webassembly/tool-conventions to track how linear-memory languages like Rust/C/C++ will support this if desired.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-linkage Area: linking into static, shared libraries and binaries C-bug Category: This is a bug. O-wasm Target: WASM (WebAssembly), http://webassembly.org/ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

6 participants