Skip to content
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

Support no_main wasi binaries #1716

Closed
Yamakaky opened this issue Oct 13, 2020 · 11 comments
Closed

Support no_main wasi binaries #1716

Yamakaky opened this issue Oct 13, 2020 · 11 comments
Assignees
Labels
📦 lib-wasi About wasmer-wasi priority-high High priority issue ❓ question I've a question!
Milestone

Comments

@Yamakaky
Copy link

Yamakaky commented Oct 13, 2020

Summary

I'm working on a distributed computing framework using wasm. The computing part is compiled from rust to wasm and exposes multiple entrypoints that are executed on the remote workers. I added wasi support to be able to println from wasm and for file access in the future.

If I build the wasm with a main function, everything works fine. If I use the no_main attribute, Instance::new fails with this error : Error while importing "env"."main": unknown import. Expected Function(FunctionType { params: [I32, I32], results: [I32] }).

Current API (gets compiled to both native and wasm : https://github.com/Yamakaky/distilled/blob/master/wasm/src/main.rs
Wasi setup : https://github.com/Yamakaky/distilled/blob/master/src/lib.rs#L92-L99

Am I missing something? Is main required to properly use wasi or can I just have an empty main and never call it?

Additional details

@Yamakaky Yamakaky added the ❓ question I've a question! label Oct 13, 2020
@Rochet2
Copy link

Rochet2 commented Oct 13, 2020

Not an avid Rust user, but maybe you can use #![no_main] as seen here rust-lang/rust#60231
There might be other ways to configure the compilation not to require a main function too.

@Yamakaky
Copy link
Author

I use no_main, but wasmer + wasi fails with the error above

@MarkMcCaskey
Copy link
Contributor

@Yamakaky last I worked on that part of our WASI implementation, it was a requirement to have an empty main. This is because WASI binaries need to do setup of their filesystem and other resources on the Wasm side (done in WASI libc) before env vars, files, and other features like that can be used.

The last I looked, this set up code was called in _start and _start called your program's main. There have been talks about splitting it out for exactly the use case you described, but I'm not sure if that's happened yet...


I just compiled a WASI program with Rust 1.47.0 and I see

  (func $__wasm_call_ctors (type 0)
    call $__wasilibc_initialize_environ_eagerly
    call $__wasilibc_populate_preopens)

In the Wasm. That function is called by _start and then main is called. Unfortunately those functions are not exported by WASI... I think the idea is that runtimes can call non-exported functions for the user and perhaps that's what we want to do here. I'm not exactly sure what that would look like in Wasmer but it's something we can try.

@MarkMcCaskey MarkMcCaskey added the 📦 lib-wasi About wasmer-wasi label Oct 13, 2020
@MarkMcCaskey
Copy link
Contributor

In regards to

If I use the no_main attribute, Instance::new fails with this error : Error while importing "env"."main": unknown import. Expected Function(FunctionType { params: [I32, I32], results: [I32] }).

It sounds like that's possibly a bug in the WASI generating code. It looks like it unconditionally expects a main function and treats it as an import if it can't find one in the Wasm. Which may be intentional, actually, if the library use case isn't supported yet.

@Yamakaky
Copy link
Author

I see. So the best practice would be to call the empty main after instantiation, then call my entry points? Or do you have to be in main to call fs functions for example
What about cdylib?

@MarkMcCaskey
Copy link
Contributor

No, once the set up is done you can call these fs related things from other functions.

Wasi-libc seems to have partial support for this https://reviews.llvm.org/D81689 ; but I tried compiling some Rust with latest nightly and latest stable and neither call the fs set up code on other entrypoints. Perhaps this is the job of the runtime as it can act like a linker.

I'm not sure, this is something we'll have to look into later.

So the best practice would be to call the empty main after instantiation, then call my entry points?

I think that's the best course of action for now!

What about cdylib?

I don't know specifically, but I'd assume it's similar to no_main, if it works it may generate Wasm that doesn't do what you want.

@Yamakaky
Copy link
Author

I did some testing. stdout always works, but the only way to use fs functions is to call the empty main via _start. cdylib, no_main and not calling _start doesn't work.

@stale
Copy link

stale bot commented Oct 15, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the 🏚 stale Inactive issues or PR label Oct 15, 2021
@syrusakbary syrusakbary added priority-high High priority issue and removed 🏚 stale Inactive issues or PR labels Oct 18, 2021
@wchaudry wchaudry added this to the Wasmer Runtime 2.x milestone Oct 21, 2021
@Amanieu Amanieu self-assigned this Nov 3, 2021
@wchaudry wchaudry modified the milestones: v2.1, v2.x Nov 30, 2021
@syrusakbary
Copy link
Member

We want to work on this.
We need to:

  1. Be able to detect Wasm WASI libraries (with no main) as WASI modules (make sure the WASI check returns true for getting the WASI imports)
  2. Don't run the main function if it's not present when running a wasi module with wasmer run xyz.wasm (basically if no main function is present users should use invoke for calling a function direclty wasmer run xyz.wasm --invoke abc)

@syrusakbary
Copy link
Member

We need to reproduce this with a WASI example

@fschutt
Copy link
Contributor

fschutt commented Jul 1, 2022

I cannot reproduce this issue, the example in the original post works as intended. This issue is extremely old (> 2 years now), it may have gotten fixed in the meantime. Maybe this issue can be closed now.

$ cargo wasi build && cargo run --release --example test
[examples/test.rs:40] runner.map_reduce(&cast_then_sum, 0, &[1, 2, 3, 5]).await? = 11
[examples/test.rs:41] runner.map_reduce(&concat_str, "".to_string(),
            &["a".to_string(), "b".to_string(), "c".to_string()]).await? = "abc"
[examples/test.rs:50] runner.map(&cast_and_double, &[1, 2, 3, 5, 1, 2]).await? = [
    2,
    4,
    6,
    10,
    2,
    4,
]

I am confused by the issue title, because the example in the linked code has a main() function. When executing the test.wasm alone, the code - expectedly - fails with:

error: failed to run `../distilled/upload/test.wat.txt`
╰─▶ 1: Error while importing "env"."cust_exit": unknown import. Expected Function(FunctionType { params: [I32, I32], results: [] })

... because the cust_exit function is injected from the custom runner in the test example. The only known bug related to this is #2978 (comment) : "using --invoke deactivates WASI", i.e. on a no-main WASI example like:

(module
  (import "wasi_unstable" "args_sizes_get"
    (func $__wasi_args_sizes_get (param i32 i32) (result i32)))
  (func $_start)
  (memory 1)
  (export "memory" (memory 0))
  (export "_start" (func $_start))
)

wasm run module.wat works (does nothing, no error) while wasmer run --invoke _start module.wat fails. But this is obviously not the original problem. The only way I can reproduce a semblance of the issue is like this:

// lib.rs, build with:
// cargo +nightly build -Zbuild-std --release --target wasm32-wasi
// ar -x target/wasm32-wasi/release/project.rlib
// wasmer run ./project.8cd299b3f128130.project.2a7776b6-cgu.0.rcgu.o

#![no_std]
#![no_main]
#![feature(lang_items, core_intrinsics)]

#[panic_handler]
#[no_mangle]
pub fn panic(_info: &core::panic::PanicInfo) -> ! {
    core::intrinsics::abort();
}

#[lang = "eh_personality"]
extern "C" fn rust_eh_personality() {}

extern "C" {
    pub fn foo(ptr: *const u8);
}

... which compiles to:

(module
  (type $t0 (func (param i32)))
  (type $t1 (func))
  (import "env" "__linear_memory" (memory $env.__linear_memory 0))
  (func $rust_begin_unwind (type $t0) (param $p0 i32)
    (unreachable)
    (unreachable))
  (func $rust_eh_personality (type $t1)))

... which fails with import "env.__linear_memory" not found.

So in summary, the only problem is that --invoke disables WASI (tracked in #2978). And --invoke does not work for emscripten (unrelated to this issue). As far as I can see the original issue has been solved (running files without main functions simply returns). IMO this issue can be closed.


test.wat

@Yamakaky Yamakaky closed this as completed Jul 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
📦 lib-wasi About wasmer-wasi priority-high High priority issue ❓ question I've a question!
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants