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

Enhance documents #25

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 134 additions & 6 deletions doc/embed_compiled_native.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Embed compiled native object file in C/C++
# Embed compiled native object file guideline

From the previous documentation on how to [compile Wasm to native](./compile_wasm_app_to_native.md), we have seen how to compile the wasm app to a native object file and link it with the auxiliary library to the native binary, both `sandbox` mode and `no-sandbox` mode, to produce the final product(executable file, shared library, or static library). In this section, we will explore the difference between `sandbox` and `no-sandbox` modes in more details and how you may use the shared library and static library(complete sample code can be found in this [directory](../samples/embed-compiled-native/))
From the previous documentation on how to [compile Wasm to native](./compile_wasm_app_to_native.md), we have seen how to compile the wasm app to a native object file, then linking it with the auxiliary library, both in `sandbox` mode and `no-sandbox` mode, to produce the final product(executable file, shared library, or static library).

In this section, we will explore the difference between `sandbox` and `no-sandbox` modes in more details, and how you may embed the static library and shared library linked from the native object file and auxiliary library within your application.

The complete sample code can be found in this [directory](../samples/embed-compiled-native/).

## Wasm app is compiled to native in sandbox mode

Expand All @@ -13,11 +17,11 @@ The sandbox mode is the default mode when compiling the wasm app to native. As i

In the sandbox mode, the native binary object file will export the API defined in `w2n_export.h`, you can use them in the host native binary to interact with the wasm app, such as `wasm_instance_create`, `wasm_get_export_apis` can be used to lookup the wasm function and then call it. You can also get exceptions, the base address and size of linear memory, host-managed heap information, etc.

And the native binary object needs to be further linked with `libc-builtin.c` to provide C standard library APIs. After that, it can mainly used in two ways:
The native binary object needs to be further linked with `libc-builtin.c` to provide C standard library APIs. After that, it can mainly used in two ways, link with source files like `wasm_application.c` and `main.c` to generate an executable file as the final product, or link without them to generate a library as the final product.

### 1. Link with source files like `wasm_application.c` and `main.c` to generate an executable file as the final product
### Choice one: Link with source files like `wasm_application.c` and `main.c` to generate an executable file as the final product

As we can see in [compile wasm applications to native binary](./compile_wasm_app_to_native.md), the `wasm2native` compiler can generate an executable file by linking with auxiliary library `libvmlib.a` which includes source files `wasm_application.c` and `main.c` .
As we can see in [compile wasm applications to native binary](./compile_wasm_app_to_native.md), the `wasm2native` compiler can generate an executable file by linking with auxiliary library `libvmlib.a` which includes source files `wasm_application.c` and `main.c`.

When linking with source files like `wasm_application.c` and `main.c`, an **executable file** is generated, and the `--invoke func` and `--repl` command line options are supported to call a function. If these two options are not used, the default is to execute the main function of the wasm app, if it exists.

Expand All @@ -36,7 +40,7 @@ webassembly> add 3 4

For more details, you can refer to the [this section](./compile_wasm_app_to_native.md#binary-executable).

### 2. If `wasm_application.c` and `main.c` are **not** linked, then normally a library(either static library or dynamic library) is generated as final product
### Choice two: If `wasm_application.c` and `main.c` are **not** linked, then normally a library(either static library or dynamic library) is generated as final product

When using this library, normally in the other source code that the native object file is linked with, use export APIs defined in `w2n_export.h` to interact with the wasm app.

Expand Down Expand Up @@ -106,3 +110,127 @@ gcc -O3 -o nosandbox_with_staticlib ../src/nosandbox_main.c -L. -lstaticnosandbo
# Example usage of executable, in the nosandbox_main.c it calls the add function in the wasm object file
./nosandbox_with_staticlib
```

## General workflow of embedding wasm object file generated library

In the previous two sections, we have seen how to use the final product of the wasm app compiled to native in sandbox mode and no-sandbox mode. Here we will explore more details of the general workflow of embedding the wasm final product library in the host native binary.

For the **no-sandbox mode** library, use the library as you would use a normal library, for example, in your wasm application you will make a library as the final product, and you can have such function:

```C
// wasm app C source
int
add(int a, int b)
{
return a + b;
}
```

Then in your host native binary, you can call this function directly.

```C
#include <stdio.h>

extern int
add(int a, int b);

int
main()
{
printf("Nosandbox main: add 3 + 4 = %d\n", add(3, 4));
}
```

So in this section, we will focus on the sandbox mode library.

Here is the more detailed process of how to use the export APIs defined in `w2n_export.h` to interact with the wasm app(in the form of a library) in the host native binary:

### Embed the wasm app library in the host native binary

First, the wasm instance needs to be created using `wasm_instance_create()`, and check whether it creates successfully using `wasm_instance_is_created()`. If it fails or at the end of the host native binary, we need to destroy the wasm instance.

```C
wasm_instance_create();
if (!wasm_instance_is_created()) {
os_printf("Create wasm instance failed.\n");
goto fail;
}

...

fail:
wasm_instance_destroy();
return ret;
```

Then you can use `wasm_get_export_apis()` and `wasm_get_export_api_num()` to get and call the wasm function by name, following similar procedures as below(a full example process can be found in `execute_func` of [wasm_application.c](wasm2native-vmlib/application/wasm_application.c)):

```C
WASMExportApi *export_apis = wasm_get_export_apis();
WASMExportApi *export_func = NULL;
void (*invoke_native)(const void *, uint32 *, uint32 *);
uint32 export_api_num = wasm_get_export_api_num();

/* Lookup exported function */
for (i = 0; i < export_api_num; i++) {
if (!strcmp(export_apis[i].func_name, name)) {
export_func = &export_apis[i];
break;
}
}

/* from the export_func->signature, parse and prepare the arguments array(argv1) */
...

/* lookup the quick aot entry, basically it will reinterpret the function ptr type that will match export_func->signature */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lookup_quick_aot_entry is provided by wasm_application.c and is static. Developer must link the libcvmlib.a, but we suppose that he doesn't have to link it.

Suggest to just convert the func_ptr to an function according the prototype, and call it directly, like:

    typedef int32 (*AOTFunc)(int32);
    AOTFunc aot_func = (AOTFunc)export_func->func_ptr;
    int32 result = aot_func(..);

invoke_native = lookup_quick_aot_entry(export_func->signature);

/* call the wasm function, use the argv1 as both parameter and return array */
invoke_native(export_func->func_ptr, argv1, argv1);

/* parse and process the return value from the return array(argv1) */
...
```

PS: during the wasm instance creation or calling the wasm function, you can use `wasm_get_exception()` and `wasm_get_exception_msg()` to check the exception and utilize the exception message.

```C
static void
check_and_print_exception()
{
int32 exce_id = wasm_get_exception();
const char*exce_msg;
if (exce_id) {
exce_msg = wasm_get_exception_msg();
os_printf("Exception: %s\n", exce_msg);
}
else {
os_printf("Exception: unknown error\n");
}
}

// check whether create wasm instance successfully
int
main(int argc, char *argv[])
{
...

if (!wasm_instance_is_created()) {
os_printf("Create wasm instance failed.\n");
check_and_print_exception();
goto fail;
}

...

if (!app_instance_func(func_name))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't call app_instance_func, it is also provided in wasm_application.c and suppose not to be linked in some scenarios.

ret = 1;

if (ret) {
check_and_print_exception();
}

...

}
```