summaryrefslogtreecommitdiffstats
path: root/src/fluent-bit/lib/wasm-micro-runtime-WAMR-1.2.2/doc/multi_module.md
blob: 1c22863f95beea9b0d0299334422fe251325bf8b (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
# Multiple Modules as Dependencies

A WASM module can _import_ _functions_, _globals_, _memories_ and _tables_ from other modules as dependencies. A module can also _export_ those entities for other modules like a library.

WAMR loads all dependencies recursively according to the _import section_ of a module.

> WAMR only implements the load-time dynamic linking. Please refer to [dynamic linking](https://webassembly.org/docs/dynamic-linking/) for more details.

WAMR follows [WASI Command/Reactor Model](https://github.com/WebAssembly/WASI/blob/main/design/application-abi.md#current-unstable-abi). The WASI model separates modules into commands and reactors. A Command is the main module that requires exports of reactors(submodules).

if `WASM_ENABLE_LIBC_WASI` is enabled, any module imports a WASI APIs, like `(import "wasi_snapshot_preview1" "XXX")`, should follow restrictions of the _WASI application ABI_:

- a main module(a command) should include `_start()`
- a submodule(a reactor) should include `_initialize()`
- both a command and a reactor should include an exported `memory`

## Multi-Module Related APIs

### Register a module

```c
bool
wasm_runtime_register_module(const char *module_name,
                             wasm_module_t module,
                             char *error_buf,
                             uint32_t error_buf_size);
```

It is used to register a _module_ with a _module_name_ to WASM runtime, especially for the main module, which is loaded by `wasm_runtime_load()` and doesn't have a chance to tell runtime its _module name_.

WAMR will get submodules' names(according to the _import section_ of the main module) and load .wasm files from the filesystem or stream and then register them internally.

### Find a registered module

```c
wasm_module_t
wasm_runtime_find_module_registered(
    const char *module_name);
```

It is used to check whether a module with a given _module_name_ has been registered before or not. Return the module if yes.

### Module reader and destroyer

```c
typedef bool (*module_reader)(const char *module_name,
                              uint8_t **p_buffer,
                              uint32_t *p_size);

typedef void (*module_destroyer)(uint8_t *buffer,
                                 uint32_t size);

void
wasm_runtime_set_module_reader(const module_reader reader,
                               const module_destroyer destroyer);
```

WAMR hopes that the native host or embedding environment loads/unloads the module WASM files by themselves and only passes runtime the binary content without worrying about filesystem or storage issues. `module_reader` and `module_destroyer` are two callbacks called when dynamic-loading/unloading submodules. Developers must implement the two callbacks by themselves.

### Call function of a submodule

```c
wasm_function_inst_t
wasm_runtime_lookup_function(wasm_module_inst_t const module_inst,
                             const char *name,
                             const char *signature);
```

Multi-module allows one to look up an exported function of a submodule. There are two ways to indicate the function _name_:

- parent function name only by default, used to look up the function of the parent module
- submodule name, function name and two $ symbols, e.g. `$submodule_name$function_name`, used to lookup function of submodule
- `signature` can be NULL

## Example

### Attributes in C/C++

Suppose there are three C files, _mA.c_, _mB.c_ and _mC.c_. Each of them exports functions and imports from others except mA.

import/export with two kinds of `__attribute__`:

- `__attribute__((import_module("MODULE_NAME"))) __attribute__((import_name("FUNCTION_NAME")))`. to indicate dependencies of the current module.

- `__attribute__((export_name("FUNCTION_NAME")))`. to expose functions.

```C
// mA.c
__attribute__((export_name("A1"))) int
A1()
{
    return 11;
}
```

```C
// mB.c
__attribute__((import_module("mA")))
__attribute__((import_name("A1"))) extern int
A1();

__attribute__((export_name("B1"))) int
B1()
{
    return 21;
}
```

### Compile Options

to generate a wasm module as a command

```
$ /path/to/wasi-sdk/bin/clang -o command.wasm main_module.c
```

to generate a wasm module as a reactor

```
$ /path/to/wasi-sdk/bin/clang -mexec-model=reactor -o reactor.wasm submodule.c
```

In the above case, _mA_ and _mB_ are reactors(submodules), _mC_ is the command(main module). Their _import relationships_ will be like:

![import relationships](./pics/multi_module_pic1.png)

### libvmlib

We need to enable _WAMR_BUILD_MULTI_MODULE_ option when building WAMR vmlib. Please ref to [Build WAMR core](./build_wamr.md) for a thoughtful guide.

### code

After all the preparation, we can call some functions from native code with APIs

First, create two callbacks to load WASM module files into memory and unload them later

```c
static bool
module_reader_cb(const char *module_name, uint8 **p_buffer, uint32 *p_size)
{
  // ...
  *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_path, p_size);
  // ...
}

static void
module_destroyer_cb(uint8 *buffer, uint32 size)
{
  BH_FREE(buffer);
}
```

Second, create a large buffer and tell WAMR malloc any resource only from this buffer later.

More details

```c
static char sandbox_memory_space[10 * 1024 * 1024] = { 0 };
```

Third, put all together. Please refer to [main.c](../samples/multi-module/src/main.c)