wasmerio/wasmer

`wasi_env_read_stdout` in the C API returns `0` in `lib/c-api/examples/wasi.c`

Opened this issue · 3 comments

I started to play around with the Wasmer C-API and I simply wanted to run the examples in lib/c-api/examples

I successfully managed to compile the examples but the wasi.c program seems to not behave properly; here is the output I get when executing it:

$ cd lib/c-api/examples && make run
$ ./wasi
Initializing...
Setting up WASI...
Loading binary...
Compiling module...
Instantiating module...
Extracting export...
found 2 exports
Calling export...
Evaluating "function greet(name) { return JSON.stringify('Hello, ' + name); }; print(greet('World'));"
Call completed
WASI Stdout: 
Shutting down...
Done.

Why is the WASI Stdout: line not showing the Hello, World mention? It seems that the returned intptr_t is 0...

This seems like a bug, thanks for reporting it!

Possibly related: I’ve noticed that, starting in 3.2.0, the following C program shows a return value of 64 and no bytes written. (It works as expected in 3.1.0.)

#include <stdio.h>

#include <wasmer.h>

#include <inttypes.h>
#include <sys/uio.h>

const char *wat_string =
    "(module\n"

    "(import \"wasi_snapshot_preview1\" \"fd_write\" (func $fdwrite (param i32 i32 i32 i32) (result i32)))\n"

    "(memory (export \"memory\") 1)\n"

    "(func (export \"fd_write\") (param i32 i32 i32 i32) (result i32)\n"
    "    local.get 0\n"
    "    local.get 1\n"
    "    local.get 2\n"
    "    local.get 3\n"
    "    call $fdwrite\n"
    ")\n"
")";

const char *MESSAGE = "hellohello";

typedef struct {
    uint32_t iov_base;
    uint32_t iov_len;
} wasi_iovec;

// Use the last_error API to retrieve error messages
void print_wasmer_error()
{
    int error_len = wasmer_last_error_length();
    if (error_len > 0) {
      printf("Error len: `%d`\n", error_len);
      char *error_str = malloc(error_len);
      wasmer_last_error_message(error_str, error_len);
      printf("Error str: `%s`\n", error_str);
    }
}

void print_frame(wasm_frame_t* frame) {
  printf("> %p @ 0x%zx = %"PRIu32".0x%zx\n",
    wasm_frame_instance(frame),
    wasm_frame_module_offset(frame),
    wasm_frame_func_index(frame),
    wasm_frame_func_offset(frame)
  );
}

void print_trap(wasm_trap_t* trap) {
    printf("Printing message...\n");
    wasm_name_t message;
    wasm_trap_message(trap, &message);
    printf("> %s\n", message.data);

    wasm_trap_delete(trap);
}

int main() {
    printf("Wasmer version: %s\n", wasmer_version());

    //printf("WAT code:\n%s\n", wat_string);

    wasm_byte_vec_t wat;
    wasm_byte_vec_new(&wat, strlen(wat_string), wat_string);
    wasm_byte_vec_t wasm_bytes;
    wat2wasm(&wat, &wasm_bytes);
    wasm_byte_vec_delete(&wat);

    printf("Creating the store...\n");
    wasm_engine_t* engine = wasm_engine_new();
    wasm_store_t* store = wasm_store_new(engine);

    printf("Compiling module...\n");
    wasm_module_t* module = wasm_module_new(store, &wasm_bytes);

    if (!module) {
        printf("> Error compiling module!\n");
        return 1;
    }

    wasm_byte_vec_delete(&wasm_bytes);

    printf("Setting up WASI...\n");
    wasi_config_t* config = wasi_config_new("example_program");

    wasi_config_capture_stdout(config);

    wasi_env_t* wasi_env = wasi_env_new(store, config);

    if (!wasi_env) {
        printf("> Error building WASI env!\n");
        print_wasmer_error();
        return 1;
    }

    printf("Instantiating module...\n");
    wasm_extern_vec_t imports;
    bool get_imports_result = wasi_get_imports(store, wasi_env,module,&imports);

    if (!get_imports_result) {
        printf("> Error getting WASI imports!\n");
        print_wasmer_error();
        return 1;
    }

    wasm_instance_t* instance = wasm_instance_new(store, module, &imports, NULL);

  if (!instance) {
    printf("> Error instantiating module!\n");
    print_wasmer_error();
    return 1;
  }

  if (!wasi_env_initialize_instance(wasi_env, store, instance)) {
    printf("> Error initializing wasi env memory!\n");
    print_wasmer_error();
    return 1;
  }

    printf("Retrieving exports...\n");
    wasm_extern_vec_t exports;
    wasm_instance_exports(instance, &exports);

    if (exports.size == 0) {
        printf("> Error accessing exports!\n");

        return 1;
    }

    printf("exports: %lu\n", exports.size);

    printf("Retrieving the exported memory...\n");
    wasm_memory_t* memory = wasm_extern_as_memory(exports.data[0]);

    // Observed from an AssemblyScript “Hello, world!”:
    const int iovec_offset = 864;
    const int payload_offset = 35616;
    const int written_offset = 912;

    byte_t* memory_buf = wasm_memory_data(memory);
    memcpy(memory_buf + payload_offset, MESSAGE, strlen(MESSAGE));

    wasi_iovec iovec = {
        .iov_base = payload_offset,
        .iov_len = strlen(MESSAGE),
    };
    assert(sizeof(iovec) == 8);
    memcpy(memory_buf + iovec_offset, &iovec, sizeof(iovec));

    printf("Retrieving the exported function...\n");
    wasm_func_t* func = wasm_extern_as_func(exports.data[1]);

    if (func == NULL) {
        printf("> Failed to get the exported function!\n");

        return 1;
    }

    wasm_val_t args_val[] = {
        WASM_I32_VAL(1),  //fd
        WASM_I32_VAL(iovec_offset),
        WASM_I32_VAL(1),
        WASM_I32_VAL(written_offset),
    };
    wasm_val_t results_val[] = { WASM_INIT_VAL };

    wasm_val_vec_t args = WASM_ARRAY_VEC(args_val);
    wasm_val_vec_t res = WASM_ARRAY_VEC(results_val);

    printf("Calling the function...\n-----\n");
    wasm_trap_t* trapped = wasm_func_call(func, &args, &res);
    if (trapped) {
        printf("> Error calling function!\n");

        print_trap(trapped);

        return 1;
    }
    printf("\n-----\nCall completed; result=%d\n",  results_val[0].of.i32);

    uint32_t written;
    memcpy(&written, memory_buf + written_offset, sizeof(uint32_t));
    printf("Written: %"PRIu32"\n", written);

    return 0;
}