Skip to content

Commit ff997c2

Browse files
authored
Update document of multi-module (#930)
1 parent 9169eff commit ff997c2

File tree

3 files changed

+110
-166
lines changed

3 files changed

+110
-166
lines changed

ci/Dockerfile

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ RUN cd /opt/emsdk \
3434
&& ./emsdk activate 2.0.26 \
3535
&& echo "source /opt/emsdk/emsdk_env.sh" >> /root/.bashrc
3636

37+
#
38+
# install clang and llvm
39+
RUN cd /tmp && wget https://apt.llvm.org/llvm.sh && chmod a+x llvm.sh
40+
RUN cd /tmp && ./llvm.sh 12
41+
42+
#
43+
# install wasi-sdk
44+
ARG WASI_SDK_VER=14
45+
RUN wget -c https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VER}/wasi-sdk-${WASI_SDK_VER}.0-linux.tar.gz -P /opt
46+
RUN tar xf /opt/wasi-sdk-${WASI_SDK_VER}.0-linux.tar.gz -C /opt \
47+
&& ln -fs /opt/was-sdk-${WASI_SDK_VER}.0 /opt/wasi-sdk
48+
RUN rm /opt/wasi-sdk-${WASI_SDK_VER}.0-linux.tar.gz
49+
3750
#
3851
#install wabt
3952
ARG WABT_VER=1.0.24
@@ -42,15 +55,6 @@ RUN tar xf /opt/wabt-${WABT_VER}-ubuntu.tar.gz -C /opt \
4255
&& ln -fs /opt/wabt-${WABT_VER} /opt/wabt
4356
RUN rm /opt/wabt-${WABT_VER}-ubuntu.tar.gz
4457

45-
#
46-
# install binaryen
47-
ARG BINARYEN_VER=version_101
48-
RUN wget -c https://github.com/WebAssembly/binaryen/releases/download/${BINARYEN_VER}/binaryen-${BINARYEN_VER}-x86_64-linux.tar.gz -P /opt
49-
RUN tar xf /opt/binaryen-${BINARYEN_VER}-x86_64-linux.tar.gz -C /opt \
50-
&& ln -fs /opt/binaryen-${BINARYEN_VER} /opt/binaryen
51-
RUN rm /opt/binaryen-${BINARYEN_VER}-x86_64-linux.tar.gz
52-
53-
5458
#
5559
# install bazelisk
5660
ARG BAZELISK_VER=1.10.1
@@ -59,8 +63,13 @@ RUN wget -c https://github.com/bazelbuild/bazelisk/releases/download/v${BAZELISK
5963
RUN chmod a+x /opt/bazelisk/bazelisk-linux-amd64 \
6064
&& ln -fs /opt/bazelisk/bazelisk-linux-amd64 /opt/bazelisk/bazel
6165

66+
#
67+
# install
68+
RUN apt update && apt install -y clang-format
69+
6270
# set path
63-
RUN echo "PATH=/opt/clang_llvm/bin:/opt/wasi-sdk/bin:/opt/wabt/bin:/opt/binaryen/bin:/opt/bazelisk:${PATH}" >> /root/.bashrc
71+
ENV PATH "$PATH:/opt/wasi-sdk/bin:/opt/wabt/bin:/opt/binaryen/bin:/opt/bazelisk"
72+
RUN echo "export PATH=/opt/wasi-sdk/bin:/opt/wabt/bin:/opt/binaryen/bin:/opt/bazelisk:${PATH}" >> /root/.bashrc
6473

6574
#
6675
# PS
@@ -72,5 +81,5 @@ RUN apt-get autoremove -y \
7281
&& rm -rf /var/lib/apt/lists/* \
7382
&& rm -rf /tmp/*
7483

75-
VOLUME workspace
76-
WORKDIR workspace
84+
VOLUME /workspace
85+
WORKDIR /workspace

ci/build_wamr.sh

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,28 @@ readonly ROOT=$(realpath "${CURRENT_PATH}/..")
88
readonly VARIANT=$(lsb_release -c | awk '{print $2}')
99

1010
docker build \
11-
--build-arg VARIANT=${VARIANT} \
12-
--memory=4G --cpu-quota=50000 \
13-
-t wamr_dev_${VARIANT}:0.1 -f "${CURRENT_PATH}"/Dockerfile "${CURRENT_PATH}" &&
14-
docker run --rm -it \
15-
--cpus=".5" \
16-
--memory=4G \
17-
--name wamr_build_env \
18-
--mount type=bind,src="${ROOT}",dst=/workspace \
19-
wamr_dev_${VARIANT}:0.1 \
20-
/bin/bash -c "\
21-
pwd \
22-
&& pushd product-mini/platforms/linux \
23-
&& rm -rf build \
24-
&& mkdir build \
25-
&& pushd build \
26-
&& cmake .. \
27-
&& make \
28-
&& popd \
29-
&& popd \
30-
&& echo 'Copying the binary ...' \
31-
&& rm -rf build_out \
32-
&& mkdir build_out \
33-
&& cp product-mini/platforms/linux/build/iwasm build_out/iwasm"
11+
--build-arg VARIANT=${VARIANT} \
12+
--memory=4G --cpu-quota=50000 \
13+
-t wamr_dev_${VARIANT}:0.1 -f "${CURRENT_PATH}"/Dockerfile "${CURRENT_PATH}" \
14+
&& docker run --rm -it \
15+
--cap-add=SYS_PTRACE \
16+
--cpus=".5" \
17+
--memory=4G \
18+
--mount type=bind,src="${ROOT}",dst=/workspace \
19+
--name wamr_build_env \
20+
--security-opt=seccomp=unconfined \
21+
wamr_dev_${VARIANT}:0.1 \
22+
/bin/bash -c "\
23+
pwd \
24+
&& pushd product-mini/platforms/linux \
25+
&& rm -rf build \
26+
&& mkdir build \
27+
&& pushd build \
28+
&& cmake .. \
29+
&& make \
30+
&& popd \
31+
&& popd \
32+
&& echo 'Copying the binary ...' \
33+
&& rm -rf build_out \
34+
&& mkdir build_out \
35+
&& cp product-mini/platforms/linux/build/iwasm build_out/iwasm"

doc/multi_module.md

Lines changed: 64 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,48 @@
1-
Multiple Modules as Dependencies
2-
=========================
1+
# Multiple Modules as Dependencies
32

4-
It is allowed that one WASM module can *import* *functions*, *globals*, *memories* and *tables* from other modules as its dependencies, and also one module can *export* those entities for other modules to *access* and may *write*.
3+
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.
54

6-
WAMR loads all dependencies recursively according to the *import section* of a module.
5+
WAMR loads all dependencies recursively according to the _import section_ of a module.
76

8-
> Currently WAMR only implements the load-time dynamic linking. Please refer to [dynamic linking](https://webassembly.org/docs/dynamic-linking/) for more details.
7+
> WAMR only implements the load-time dynamic linking. Please refer to [dynamic linking](https://webassembly.org/docs/dynamic-linking/) for more details.
8+
9+
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).
10+
11+
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_:
12+
13+
- a main module(a command) should include `_start()`
14+
- a submodule(a reactor) should include `_initialize()`
15+
- both a command and a reactor should include an exported `memory`
916

1017
## Multi-Module Related APIs
1118

1219
### Register a module
1320

14-
``` c
21+
```c
1522
bool
1623
wasm_runtime_register_module(const char *module_name,
1724
wasm_module_t module,
1825
char *error_buf,
1926
uint32_t error_buf_size);
2027
```
2128
22-
It is used to register a *module* with a *module_name* to WASM runtime, especially for the root module, which is loaded by `wasm_runtime_load()` and doesn't have a chance to tell runtime its *module name*.
29+
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_.
2330
24-
Fot all the sub modules, WAMR will get their names and load the .wasm files from the filesystem or stream, so no need to register the sub modules again.
31+
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.
2532
2633
### Find a registered module
2734
28-
``` c
35+
```c
2936
wasm_module_t
3037
wasm_runtime_find_module_registered(
3138
const char *module_name);
3239
```
3340

34-
It is used to check if a module with a given *module_name* has been registered, if yes return the module.
41+
It is used to check whether a module with a given _module_name_ has been registered before or not. Return the module if yes.
3542

3643
### Module reader and destroyer
3744

38-
``` c
45+
```c
3946
typedef bool (*module_reader)(const char *module_name,
4047
uint8_t **p_buffer,
4148
uint32_t *p_size);
@@ -48,9 +55,9 @@ wasm_runtime_set_module_reader(const module_reader reader,
4855
const module_destroyer destroyer);
4956
```
5057
51-
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 filesystem or storage issues. `module_reader` and `module_destroyer` are two callbacks called when dynamic-loading/unloading the sub modules. Developers must implement the two callbacks by themselves.
58+
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.
5259
53-
### Call function of sub module
60+
### Call function of a submodule
5461
5562
```c
5663
wasm_function_inst_t
@@ -59,87 +66,75 @@ wasm_runtime_lookup_function(wasm_module_inst_t const module_inst,
5966
const char *signature);
6067
```
6168

62-
Multi-module allows to lookup the function of sub module and call it. There are two ways to indicate the function *name*:
69+
Multi-module allows one to look up an exported function of a submodule. There are two ways to indicate the function _name_:
6370

64-
- parent function name only by default, used to lookup the function of parent module
65-
- sub module name, function name of sub module and two $ symbols, e.g. `$sub_module_name$function_name`, used to lookup function of sub module
71+
- parent function name only by default, used to look up the function of the parent module
72+
- submodule name, function name and two $ symbols, e.g. `$submodule_name$function_name`, used to lookup function of submodule
73+
- `signature` can be NULL
6674

6775
## Example
6876

69-
### WASM modules
70-
Suppose we have three C files, *mA.c*, *mB.c* and *mC.c*. Each of them has some exported functions and import some from others except mA.
77+
### Attributes in C/C++
7178

72-
Undefined symbols can be marked in the source code with the *import_name* clang attribute which means that they are expected to be undefined at static link time. Without the *import_module* clang attribute, undefined symbols will be marked from the *env* module.
79+
Suppose there are three C files, _mA.c_, _mB.c_ and _mC.c_. Each of them exports functions and imports from others except mA.
7380

74-
``` C
81+
import/export with two kinds of `__attribute__`:
82+
83+
- `__attribute__((import_module("MODULE_NAME"))) __attribute__((import_name("FUNCTION_NAME")))`. to indicate dependencies of the current module.
84+
85+
- `__attribute__((export_name("FUNCTION_NAME")))`. to expose functions.
86+
87+
```C
7588
// mA.c
76-
int A() { return 10; }
89+
__attribute__((export_name("A1"))) int
90+
A1()
91+
{
92+
return 11;
93+
}
7794
```
7895
79-
``` C
96+
```C
8097
// mB.c
81-
__attribute__((import_module("mA"))) __attribute__((import_name("A"))) extern int A();
82-
int B() { return 11; }
83-
int call_A() { return A(); }
84-
```
98+
__attribute__((import_module("mA")))
99+
__attribute__((import_name("A1"))) extern int
100+
A1();
85101
86-
``` C
87-
// mC.c
88-
__attribute__((import_module("mA"))) __attribute__((import_name("A"))) extern int A();
89-
__attribute__((import_module("mB"))) __attribute__((import_name("B"))) extern int B();
90-
int C() { return 12; }
91-
int call_A() { return A(); }
92-
int call_B() { return B(); }
102+
__attribute__((export_name("B1"))) int
103+
B1()
104+
{
105+
return 21;
106+
}
93107
```
94108

95-
By default no undefined symbols are allowed in the final binary. The flag *--allow-undefined* results in a WebAssembly import being defined for each undefined symbol. It is then up to the runtime to provide such symbols.
96-
97-
When building an executable, only the entry point (_start) and symbols with the *export_name* attribute exported by default. in addition, symbols can be exported via the linker command line using *--export*.
98-
99-
In the example, another linked command option *--export-all* is used.
100-
101-
> with more detail, please refer to [WebAssembly lld port](https://lld.llvm.org/WebAssembly.html)
109+
### Compile Options
102110

103-
Here is an example how to compile a *.c* to a *.wasm* with clang. Since there is no *start* function, we use *--no-entry* option.
111+
to generate a wasm module as a command
104112

105-
``` shell
106-
$ clang --target=wasm32 -nostdlib \
107-
-Wl,--no-entry,--allow-undefined,--export-all \
108-
-o mA.wasm mA.c
109-
$ clang --target=wasm32 -nostdlib \
110-
-Wl,--no-entry,--allow-undefined,--export-all \
111-
-o mB.wasm mB.c
112-
$ clang --target=wasm32 -nostdlib \
113-
-Wl,--no-entry,--allow-undefined,--export-all \
114-
-o mC.wasm mC.c
113+
```
114+
$ /path/to/wasi-sdk/bin/clang -o command.wasm main_module.c
115115
```
116116

117-
put *mA.wasm*, *mB.wasm* and *mC.wasm* in the directory *wasm-apps*
117+
to generate a wasm module as a reactor
118118

119-
``` shell
120-
$ # copy mA.wasm, mB.wasm and mC.wasm into wasm-apps
121-
$ tree wasm-apps/
122-
wasm-apps/
123-
├── mA.wasm
124-
├── mB.wasm
125-
└── mC.wasm
119+
```
120+
$ /path/to/wasi-sdk/bin/clang -mexec-model=reactor -o reactor.wasm submodule.c
126121
```
127122

128-
eventually, their *import relationships* will be like:
123+
In the above case, _mA_ and _mB_ are reactors(submodules), _mC_ is the command(main module). Their _import relationships_ will be like:
129124

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

132127
### libvmlib
133128

134-
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.
129+
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.
135130

136131
### code
137132

138-
After all above preparation, we can call some functions from native code with APIs
133+
After all the preparation, we can call some functions from native code with APIs
139134

140-
first, create two callbacks to load WASM module files into memory and unload them later
135+
First, create two callbacks to load WASM module files into memory and unload them later
141136

142-
``` c
137+
```c
143138
static bool
144139
module_reader_cb(const char *module_name, uint8 **p_buffer, uint32 *p_size)
145140
{
@@ -155,74 +150,12 @@ module_destroyer_cb(uint8 *buffer, uint32 size)
155150
}
156151
```
157152
158-
second, create a large buffer and tell WAMR malloc any resource only from this buffer later
153+
Second, create a large buffer and tell WAMR malloc any resource only from this buffer later.
159154
160-
``` c
161-
static char sandbox_memory_space[10 * 1024 * 1024] = { 0 };
162-
```
155+
More details
163156
164-
third, put all together
165-
166-
``` c
167-
int main()
168-
{
169-
/* all malloc() only from the given buffer */
170-
init_args.mem_alloc_type = Alloc_With_Pool;
171-
init_args.mem_alloc_option.pool.heap_buf = sandbox_memory_space;
172-
init_args.mem_alloc_option.pool.heap_size = sizeof(sandbox_memory_space);
173-
174-
/* initialize runtime environment */
175-
wasm_runtime_full_init(&init_args);
176-
177-
/* set module reader and destroyer */
178-
wasm_runtime_set_module_reader(module_reader_cb, module_destroyer_cb);
179-
180-
/* load WASM byte buffer from WASM bin file */
181-
module_reader_cb("mC", &file_buf, &file_buf_size));
182-
183-
/* load mC and let WAMR load mA and mB */
184-
module = wasm_runtime_load(file_buf, file_buf_size,
185-
error_buf, sizeof(error_buf));
186-
187-
/* instantiate the module */
188-
module_inst =
189-
wasm_runtime_instantiate(module, stack_size,
190-
heap_size, error_buf, sizeof(error_buf)));
191-
192-
193-
printf("call \"C\", it will return 0xc:i32, ===> ");
194-
wasm_application_execute_func(module_inst, "C", 0, &args[0]);
195-
printf("call \"call_B\", it will return 0xb:i32, ===> ");
196-
wasm_application_execute_func(module_inst, "call_B", 0, &args[0]);
197-
printf("call \"call_A\", it will return 0xa:i32, ===>");
198-
wasm_application_execute_func(module_inst, "call_A", 0, &args[0]);
199-
200-
/* call some functions of mB */
201-
printf("call \"mB.B\", it will return 0xb:i32, ===>");
202-
wasm_application_execute_func(module_inst, "$mB$B", 0, &args[0]);
203-
printf("call \"mB.call_A\", it will return 0xa:i32, ===>");
204-
wasm_application_execute_func(module_inst, "$mB$call_A", 0, &args[0]);
205-
206-
/* call some functions of mA */
207-
printf("call \"mA.A\", it will return 0xa:i32, ===>");
208-
wasm_application_execute_func(module_inst, "$mA$A", 0, &args[0]);
209-
210-
// ...
211-
}
157+
```c
158+
static char sandbox_memory_space[10 * 1024 * 1024] = { 0 };
212159
```
213160

214-
> please refer to [main.c](../samples/multi_modules/src/main.c)
215-
216-
The output of the main.c will like:
217-
218-
``` shell
219-
$ ./a.out
220-
221-
call "C", it will return 0xc:i32, ===> 0xc:i32
222-
call "call_B", it will return 0xb:i32, ===> 0xb:i32
223-
call "call_A", it will return 0xa:i32, ===>0xa:i32
224-
call "mB.B", it will return 0xb:i32, ===>0xb:i32
225-
call "mB.call_A", it will return 0xa:i32, ===>0xa:i32
226-
call "mA.A", it will return 0xa:i32, ===>0xa:i32
227-
228-
```
161+
Third, put all together. Please refer to [main.c](../samples/multi_modules/src/main.c)

0 commit comments

Comments
 (0)