summaryrefslogtreecommitdiffstats
path: root/src/fluent-bit/lib/wasm-micro-runtime-WAMR-1.2.2/samples/wasm-c-api-imports/README.md
blob: 9b61a6e744b9a80d718f7fc737f0d8ccbc0de9c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# How to create `imports` for wasm_instance_new() properly

It's always been asked how to create `wasm_extern_vec_t *imports` for
`wasm_instance_new()`?

```c
WASM_API_EXTERN own wasm_instance_t* wasm_instance_new(
  wasm_store_t*, const wasm_module_t*, const wasm_extern_vec_t *imports,
  own wasm_trap_t** trap
);
```

`wasm_extern_vec_t *imports` is required to match the requirement of _the
import section_ of a .wasm.

```bash
$ /opt/wabt-1.0.31/bin/wasm-objdump -j Import -x <some_example>.wasm

Section Details:

Import[27]:
 - func[0] sig=2 <pthread_mutex_lock> <- env.pthread_mutex_lock
 - func[1] sig=2 <pthread_mutex_unlock> <- env.pthread_mutex_unlock
 - func[2] sig=2 <pthread_cond_signal> <- env.pthread_cond_signal
 - func[3] sig=3 <host_log> <- env.log
 ...
 - func[11] sig=4 <__imported_wasi_snapshot_preview1_sock_bind> <- wasi_snapshot_preview1.sock_bind
 - func[12] sig=4 <__imported_wasi_snapshot_preview1_sock_connect> <- wasi_snapshot_preview1.sock_connect
 - func[13] sig=4 <__imported_wasi_snapshot_preview1_sock_listen> <- wasi_snapshot_preview1.sock_listen
 - func[14] sig=5 <__imported_wasi_snapshot_preview1_sock_open> <- wasi_snapshot_preview1.sock_open
 - func[15] sig=4 <__imported_wasi_snapshot_preview1_sock_addr_remote> <- wasi_snapshot_preview1.sock_addr_remote
 - func[16] sig=4 <__imported_wasi_snapshot_preview1_args_get> <- wasi_snapshot_preview1.args_get
 - func[17] sig=4 <__imported_wasi_snapshot_preview1_args_sizes_get> <- wasi_snapshot_preview1.args_sizes_get
 ...
```

Developers should fill in _imports_ with enough host functions and make sure
there are no linking problems during instantiation.

```bash
TODO: linking warnings
```

## A natural way

One natural answer is "to create a list which matches every item in _the import
section_" of the .wasm. Since developers can see the section details of
a .wasm by tools like _wasm-objdump_, the answer is doable. Most of the time,
if they also prepare Wasm modules, developers have full control over import
requirements, and they only need to take a look at the order of _the import
section_.

Yes, _the order_. A proper `wasm_extern_vec_t *imports` includes two things:

1. how many `wasm_extern_t`
2. and order of those

Because there is no "name information" in a `wasm_extern_t`. The only way is let
`wasm_instance_new()` to tell which item in _the import section_ of a .wasm
should match any item in `wasm_extern_vec_t *imports` is based on **_index_**.

The algorithm is quite straightforward. The first one of _the import section_ matches
`wasm_extern_vec_t *imports->data[0] `. The second one matches `wasm_extern_vec_t *imports->data[1]`.
And so on.

So the order of `wasm_extern_vec_t *imports` becomes quite a burden. It requires
developers always checking _the import section_ visually.

Until here, the natural way is still workable although involving some handy work.
Right?

## A blocker

Sorry, the situation changes a lot when driving wasm32-wasi Wasm modules with
wasm-c-api.

As you know, WASI provides _a set of crossing-platform standard libraries_ for
Wasm modules, and leaves some _interfaces_ for native platform-dependent supports.
Those _interfaces_ are those import items with the module name `wasi_snapshot_preview1`
in a Wasm module.

It seems not economical to let developers provide their version of host
implementations of the `wasi_snapshot_preview1.XXX` functions. All those support
should be packed into a common library and shared in different Wasm modules.
Like a [cargo WASI](https://github.com/bytecodealliance/cargo-wasi).

WAMR chooses to integrate the WASI support library in the runtime to reduce
developers' compilation work. It brings developers a new thing of a proper
`wasm_extern_vec_t *imports` that developers should avoid overwriting those items
of _the import section_ of a Wasm module that will be provided by the runtime. It
also not economical to code for those functions.

Using module names as a filter seems to be a simple way. But some private
additional c/c++ libraries are supported in WAMR. Those supporting will bring
more import items that don't use `wasi_snapshot_preview1` as module names but are still
covered by the WASM runtime. Like `env.pthread_`. Plus, [the native lib registeration](https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/export_native_api.md)
provides another possible way to fill in the requirement of _the import section_.

Let's take summarize. A proper `wasm_extern_vec_t *imports` should include:

1. provides all necessary host implementations for items in _the import section_
2. should not override runtime provided implementation or covered by native
   registrations. functinal or econmical.
3. keep them in a right order

## A recommendation

The recommendation is:

- use `wasm_module_imports()` to build the order
- use `wasm_importtype_is_linked()` to avoid overwriting

[wasm-c-api-imports](.) is a simple showcase of how to do that.

First, let's take a look at the Wasm module. [send_recv](./wasm/send_recv.c)
uses both standard WASI and WAMR_BUILD_LIB_PTHREAD supporting. Plus a private
native function `host_log`.

So, `wasm_extern_vec_t *imports` should only include the host implementation of
`host_log` and avoid WASI related(`wasm-c-api-imports.XXX`) and pthread related(`env.pthread_XXX`).

[Here is how to do](./host/example1.c):

- get import types with `wasm_module_imports(0)`. it contains name information

```c
  wasm_importtype_vec_t importtypes = { 0 };
  wasm_module_imports(module, &importtypes);
```

- traversal import types. The final `wasm_importvec_t *imports` should have the
  same order with `wasm_importtype_vec_t`

```c
  for (unsigned i = 0; i < importtypes.num_elems; i++)
```

- use `wasm_importtype_is_linked()` to avoid those covered by the runtime and
  registered natives. A little tip is use "wasm_extern_new_empty()" to create
  a placeholder.

```c
    /* use wasm_extern_new_empty() to create a placeholder */
    if (wasm_importtype_is_linked(importtype)) {
        externs[i] = wasm_extern_new_empty(
            store, wasm_externtype_kind(wasm_importtype_type(importtype)));
        continue;
    }
```

- use `wasm_importtype_module()` to get the module name, use `wasm_importtype_name()`
  to get the field name.

```c
      const wasm_name_t *module_name =
          wasm_importtype_module(importtypes.data[i]);
      const wasm_name_t *field_name =
          wasm_importtype_name(importtypes.data[i]);
```

- fill in `wasm_externvec_t *imports` dynamically and programmatically.

```c
      if (strncmp(module_name->data, "env", strlen("env")) == 0
          && strncmp(field_name->data, "log", strlen("log")) == 0) {
          wasm_functype_t *log_type = wasm_functype_new_2_0(
              wasm_valtype_new_i64(), wasm_valtype_new_i32());
          wasm_func_t *log_func = wasm_func_new(store, log_type, host_logs);
          wasm_functype_delete(log_type);

          externs[i] = wasm_func_as_extern(log_func);
      }
  }
```