Enhance XIP and add XIP document (#863)

Auto detect whether file is XIP file before loading module in posix like and
linux-sgx platforms, and if yes, mmap executable memory automatically to
run the XIP file.
Add document about XIP feature.
Enable test spec cases with XIP feature.
This commit is contained in:
Wenyong Huang 2021-12-06 17:25:10 +08:00 committed by GitHub
parent 3f808d4596
commit b490a229f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 184 additions and 24 deletions

View File

@ -29,6 +29,7 @@ iwasm VM core
- [Thread management and pthread library](./doc/pthread_library.md), ref to [sample](samples/multi-thread) - [Thread management and pthread library](./doc/pthread_library.md), ref to [sample](samples/multi-thread)
- [Linux SGX (Intel Software Guard Extension) support](./doc/linux_sgx.md) - [Linux SGX (Intel Software Guard Extension) support](./doc/linux_sgx.md)
- [Source debugging](./doc/source_debugging.md) - [Source debugging](./doc/source_debugging.md)
- [XIP (Execution In Place) support](./doc/xip.md)
### post-MVP features ### post-MVP features
- [Non-trapping float-to-int conversions](https://github.com/WebAssembly/nontrapping-float-to-int-conversions) - [Non-trapping float-to-int conversions](https://github.com/WebAssembly/nontrapping-float-to-int-conversions)

View File

@ -2190,6 +2190,8 @@ load_relocation_section(const uint8 *buf, const uint8 *buf_end,
|| !strcmp(group->section_name, ".text") || !strcmp(group->section_name, ".text")
#endif #endif
) { ) {
#if !defined(BH_PLATFORM_LINUX) && !defined(BH_PLATFORM_LINUX_SGX) \
&& !defined(BH_PLATFORM_DARWIN)
if (module->native_symbol_count > 0) { if (module->native_symbol_count > 0) {
set_error_buf(error_buf, error_buf_size, set_error_buf(error_buf, error_buf_size,
"cannot apply relocation to text section " "cannot apply relocation to text section "
@ -2197,6 +2199,7 @@ load_relocation_section(const uint8 *buf, const uint8 *buf_end,
"\"--enable-indirect-mode\" flag"); "\"--enable-indirect-mode\" flag");
goto fail; goto fail;
} }
#endif
if (!do_text_relocation(module, group, error_buf, error_buf_size)) if (!do_text_relocation(module, group, error_buf, error_buf_size))
goto fail; goto fail;
} }

View File

@ -294,6 +294,64 @@ get_package_type(const uint8 *buf, uint32 size)
return Package_Type_Unknown; return Package_Type_Unknown;
} }
#if WASM_ENABLE_AOT != 0
static uint8 *
align_ptr(const uint8 *p, uint32 b)
{
uintptr_t v = (uintptr_t)p;
uintptr_t m = b - 1;
return (uint8 *)((v + m) & ~m);
}
#define CHECK_BUF(buf, buf_end, length) \
do { \
if (buf + length < buf || buf + length > buf_end) \
return false; \
} while (0)
#define read_uint32(p, p_end, res) \
do { \
p = (uint8 *)align_ptr(p, sizeof(uint32)); \
CHECK_BUF(p, p_end, sizeof(uint32)); \
res = *(uint32 *)p; \
p += sizeof(uint32); \
} while (0)
bool
wasm_runtime_is_xip_file(const uint8 *buf, uint32 size)
{
const uint8 *p = buf, *p_end = buf + size;
uint32 section_type, sub_section_type, section_size;
if (get_package_type(buf, size) != Wasm_Module_AoT)
return false;
CHECK_BUF(p, p_end, 8);
p += 8;
while (p < p_end) {
read_uint32(p, p_end, section_type);
read_uint32(p, p_end, section_size);
CHECK_BUF(p, p_end, section_size);
if (section_type == AOT_SECTION_TYPE_CUSTOM) {
read_uint32(p, p_end, sub_section_type);
if (sub_section_type == AOT_CUSTOM_SECTION_NATIVE_SYMBOL) {
return true;
}
else {
p -= sizeof(uint32);
}
}
else if (section_type >= AOT_SECTION_TYPE_SIGANATURE) {
return false;
}
p += section_size;
}
return false;
}
#endif /* end of WASM_ENABLE_AOT */
#if (WASM_ENABLE_THREAD_MGR != 0) && (WASM_ENABLE_DEBUG_INTERP != 0) #if (WASM_ENABLE_THREAD_MGR != 0) && (WASM_ENABLE_DEBUG_INTERP != 0)
uint32 uint32
wasm_runtime_start_debug_instance(WASMExecEnv *exec_env) wasm_runtime_start_debug_instance(WASMExecEnv *exec_env)

View File

@ -406,6 +406,10 @@ wasm_runtime_destroy(void);
WASM_RUNTIME_API_EXTERN PackageType WASM_RUNTIME_API_EXTERN PackageType
get_package_type(const uint8 *buf, uint32 size); get_package_type(const uint8 *buf, uint32 size);
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN bool
wasm_runtime_is_xip_file(const uint8 *buf, uint32 size);
/* See wasm_export.h for description */ /* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN WASMModuleCommon * WASM_RUNTIME_API_EXTERN WASMModuleCommon *
wasm_runtime_load(const uint8 *buf, uint32 size, char *error_buf, wasm_runtime_load(const uint8 *buf, uint32 size, char *error_buf,

View File

@ -235,6 +235,17 @@ wasm_runtime_free(void *ptr);
WASM_RUNTIME_API_EXTERN package_type_t WASM_RUNTIME_API_EXTERN package_type_t
get_package_type(const uint8_t *buf, uint32_t size); get_package_type(const uint8_t *buf, uint32_t size);
/**
* Check whether a file is an AOT XIP (Execution In Place) file
*
* @param buf the package buffer
* @param size the package buffer size
*
* @return true if success, false otherwise
*/
WASM_RUNTIME_API_EXTERN bool
wasm_runtime_is_xip_file(const uint8_t *buf, uint32_t size);
/** /**
* It is a callback for WAMR providing by embedding to load a module file * It is a callback for WAMR providing by embedding to load a module file
* into a buffer * into a buffer

14
doc/xip.md Normal file
View File

@ -0,0 +1,14 @@
# WAMR XIP (Execution In Place) feature introduction
Some IoT devices may require to run the AOT file from flash or ROM which is read-only, so as to reduce the memory consumption, or resolve the issue that there is no executable memory available to run AOT code. In such case, the AOT code inside the AOT file shouldn't be duplicated into memory and shouldn't be modified (or patched) by the AOT relocations. To address this, WAMR implements the XIP (Execution In Place) feature, which generates the AOT relocations as few as possible:
- In the AOT code, an AOT function calls other functions with indirect mode: it doesn't call other functions directly, but looks up their pointers from the function pointer table passed by its first argument exec_env, and then calls the function pointer found. By this way the relocations to other functions are eliminated.
- Eliminate the calls to the LLVM intrinsic functions, or, replace calling them with calling runtime self implemented functions instead, e.g. the calling to `llvm.experimental.constrained.fadd.f32` is replaced by the calling to `aot_intrinsic_fadd_f32`.
The XIP file is an AOT file without (or with few) relocations to patch the AOT code (or text section). Developer can use the option `--enable-indirect-mode --disable-llvm-intrinsics` for wamrc to generate the AOT file, e.g.:
```bash
wamrc --enable-indirect-mode --disable-llvm-intrinsics -o <aot_file> <wasm_file>
```
## Known issues
There may be some relocations to the ".rodata" like sections which require to patch the AOT code. More work will be done to resolve it in the future.

View File

@ -52,6 +52,8 @@ typedef struct EnclaveModule {
uint32 wasi_env_list_size; uint32 wasi_env_list_size;
char **wasi_argv; char **wasi_argv;
uint32 wasi_argc; uint32 wasi_argc;
bool is_xip_file;
uint32 total_size_mapped;
} EnclaveModule; } EnclaveModule;
#if WASM_ENABLE_SPEC_TEST == 0 #if WASM_ENABLE_SPEC_TEST == 0
@ -127,28 +129,57 @@ handle_cmd_load_module(uint64 *args, uint32 argc)
uint32 error_buf_size = *(uint32 *)args++; uint32 error_buf_size = *(uint32 *)args++;
uint64 total_size = sizeof(EnclaveModule) + (uint64)wasm_file_size; uint64 total_size = sizeof(EnclaveModule) + (uint64)wasm_file_size;
EnclaveModule *enclave_module; EnclaveModule *enclave_module;
bool is_xip_file = false;
bh_assert(argc == 4); bh_assert(argc == 4);
if (total_size >= UINT32_MAX #if WASM_ENABLE_AOT != 0
|| !(enclave_module = is_xip_file = wasm_runtime_is_xip_file((uint8 *)wasm_file, wasm_file_size);
(EnclaveModule *)wasm_runtime_malloc((uint32)total_size))) { #endif
set_error_buf(error_buf, error_buf_size,
"WASM module load failed: " if (!is_xip_file) {
"allocate memory failed."); if (total_size >= UINT32_MAX
*(void **)args_org = NULL; || !(enclave_module = (EnclaveModule *)wasm_runtime_malloc(
return; (uint32)total_size))) {
set_error_buf(error_buf, error_buf_size,
"WASM module load failed: "
"allocate memory failed.");
*(void **)args_org = NULL;
return;
}
memset(enclave_module, 0, (uint32)total_size);
}
else {
int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC;
int map_flags = MMAP_MAP_NONE;
if (total_size >= UINT32_MAX
|| !(enclave_module = (EnclaveModule *)os_mmap(
NULL, (uint32)total_size, map_prot, map_flags))) {
set_error_buf(error_buf, error_buf_size,
"WASM module load failed: mmap memory failed.");
*(void **)args_org = NULL;
return;
}
memset(enclave_module, 0, (uint32)total_size);
enclave_module->is_xip_file = true;
enclave_module->total_size_mapped = (uint32)total_size;
} }
memset(enclave_module, 0, (uint32)total_size);
enclave_module->wasm_file = (uint8 *)enclave_module + sizeof(EnclaveModule); enclave_module->wasm_file = (uint8 *)enclave_module + sizeof(EnclaveModule);
bh_memcpy_s(enclave_module->wasm_file, wasm_file_size, wasm_file, bh_memcpy_s(enclave_module->wasm_file, wasm_file_size, wasm_file,
wasm_file_size); wasm_file_size);
if (is_xip_file) {
enclave_module->is_xip_file = true;
}
if (!(enclave_module->module = if (!(enclave_module->module =
wasm_runtime_load(enclave_module->wasm_file, wasm_file_size, wasm_runtime_load(enclave_module->wasm_file, wasm_file_size,
error_buf, error_buf_size))) { error_buf, error_buf_size))) {
wasm_runtime_free(enclave_module); if (!is_xip_file)
wasm_runtime_free(enclave_module);
else
os_munmap(enclave_module, (uint32)total_size);
*(void **)args_org = NULL; *(void **)args_org = NULL;
return; return;
} }
@ -170,7 +201,10 @@ handle_cmd_unload_module(uint64 *args, uint32 argc)
wasm_runtime_free(enclave_module->wasi_arg_buf); wasm_runtime_free(enclave_module->wasi_arg_buf);
wasm_runtime_unload(enclave_module->module); wasm_runtime_unload(enclave_module->module);
wasm_runtime_free(enclave_module); if (!enclave_module->is_xip_file)
wasm_runtime_free(enclave_module);
else
os_munmap(enclave_module, enclave_module->total_size_mapped);
LOG_VERBOSE("Unload module success.\n"); LOG_VERBOSE("Unload module success.\n");
} }

View File

@ -34,8 +34,6 @@ print_help()
printf(" --heap-size=n Set maximum heap size in bytes, default is 16 KB\n"); printf(" --heap-size=n Set maximum heap size in bytes, default is 16 KB\n");
printf(" --repl Start a very simple REPL (read-eval-print-loop) mode\n" printf(" --repl Start a very simple REPL (read-eval-print-loop) mode\n"
" that runs commands in the form of \"FUNC ARG...\"\n"); " that runs commands in the form of \"FUNC ARG...\"\n");
printf(" --xip Enable XIP (Execution In Place) mode to run AOT file\n"
" generated with \"--enable-indirect-mode\" flag\n");
#if WASM_ENABLE_LIBC_WASI != 0 #if WASM_ENABLE_LIBC_WASI != 0
printf(" --env=<env> Pass wasi environment variables with \"key=value\"\n"); printf(" --env=<env> Pass wasi environment variables with \"key=value\"\n");
printf(" to the program, for example:\n"); printf(" to the program, for example:\n");
@ -240,7 +238,7 @@ main(int argc, char *argv[])
int log_verbose_level = 2; int log_verbose_level = 2;
#endif #endif
bool is_repl_mode = false; bool is_repl_mode = false;
bool is_xip_mode = false; bool is_xip_file = false;
#if WASM_ENABLE_LIBC_WASI != 0 #if WASM_ENABLE_LIBC_WASI != 0
const char *dir_list[8] = { NULL }; const char *dir_list[8] = { NULL };
uint32 dir_list_size = 0; uint32 dir_list_size = 0;
@ -273,9 +271,6 @@ main(int argc, char *argv[])
else if (!strcmp(argv[0], "--repl")) { else if (!strcmp(argv[0], "--repl")) {
is_repl_mode = true; is_repl_mode = true;
} }
else if (!strcmp(argv[0], "--xip")) {
is_xip_mode = true;
}
else if (!strncmp(argv[0], "--stack-size=", 13)) { else if (!strncmp(argv[0], "--stack-size=", 13)) {
if (argv[0][13] == '\0') if (argv[0][13] == '\0')
return print_help(); return print_help();
@ -392,10 +387,11 @@ main(int argc, char *argv[])
(uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size))) (uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size)))
goto fail1; goto fail1;
if (is_xip_mode) { #if WASM_ENABLE_AOT != 0
if (wasm_runtime_is_xip_file(wasm_file_buf, wasm_file_size)) {
uint8 *wasm_file_mapped; uint8 *wasm_file_mapped;
int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC; int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC;
int map_flags = MMAP_MAP_NONE; int map_flags = MMAP_MAP_32BIT;
if (!(wasm_file_mapped = if (!(wasm_file_mapped =
os_mmap(NULL, (uint32)wasm_file_size, map_prot, map_flags))) { os_mmap(NULL, (uint32)wasm_file_size, map_prot, map_flags))) {
@ -408,7 +404,9 @@ main(int argc, char *argv[])
wasm_file_size); wasm_file_size);
wasm_runtime_free(wasm_file_buf); wasm_runtime_free(wasm_file_buf);
wasm_file_buf = wasm_file_mapped; wasm_file_buf = wasm_file_mapped;
is_xip_file = true;
} }
#endif
#if WASM_ENABLE_MULTI_MODULE != 0 #if WASM_ENABLE_MULTI_MODULE != 0
wasm_runtime_set_module_reader(module_reader_callback, moudle_destroyer); wasm_runtime_set_module_reader(module_reader_callback, moudle_destroyer);
@ -450,7 +448,7 @@ fail3:
fail2: fail2:
/* free the file buffer */ /* free the file buffer */
if (!is_xip_mode) if (!is_xip_file)
wasm_runtime_free(wasm_file_buf); wasm_runtime_free(wasm_file_buf);
else else
os_munmap(wasm_file_buf, wasm_file_size); os_munmap(wasm_file_buf, wasm_file_size);

View File

@ -51,6 +51,7 @@ def ignore_the_case(
multi_module_flag=False, multi_module_flag=False,
multi_thread_flag=False, multi_thread_flag=False,
simd_flag=False, simd_flag=False,
xip_flag=False,
): ):
if case_name in ["comments", "inline-module", "names"]: if case_name in ["comments", "inline-module", "names"]:
return True return True
@ -100,6 +101,7 @@ def test_case(
multi_module_flag=False, multi_module_flag=False,
multi_thread_flag=False, multi_thread_flag=False,
simd_flag=False, simd_flag=False,
xip_flag=False,
clean_up_flag=True, clean_up_flag=True,
verbose_flag=True, verbose_flag=True,
): ):
@ -114,6 +116,7 @@ def test_case(
multi_module_flag, multi_module_flag,
multi_thread_flag, multi_thread_flag,
simd_flag, simd_flag,
xip_flag,
): ):
return True return True
@ -139,6 +142,9 @@ def test_case(
if simd_flag: if simd_flag:
CMD.append("--simd") CMD.append("--simd")
if xip_flag:
CMD.append("--xip")
if not clean_up_flag: if not clean_up_flag:
CMD.append("--no_cleanup") CMD.append("--no_cleanup")
@ -195,6 +201,7 @@ def test_suite(
multi_module_flag=False, multi_module_flag=False,
multi_thread_flag=False, multi_thread_flag=False,
simd_flag=False, simd_flag=False,
xip_flag=False,
clean_up_flag=True, clean_up_flag=True,
verbose_flag=True, verbose_flag=True,
): ):
@ -217,6 +224,7 @@ def test_suite(
multi_module_flag, multi_module_flag,
multi_thread_flag, multi_thread_flag,
simd_flag, simd_flag,
xip_flag,
clean_up_flag, clean_up_flag,
verbose_flag, verbose_flag,
) )
@ -239,6 +247,7 @@ def test_suite_parallelly(
multi_module_flag=False, multi_module_flag=False,
multi_thread_flag=False, multi_thread_flag=False,
simd_flag=False, simd_flag=False,
xip_flag=False,
clean_up_flag=False, clean_up_flag=False,
verbose_flag=False, verbose_flag=False,
): ):
@ -266,6 +275,7 @@ def test_suite_parallelly(
multi_module_flag, multi_module_flag,
multi_thread_flag, multi_thread_flag,
simd_flag, simd_flag,
xip_flag,
clean_up_flag, clean_up_flag,
verbose_flag, verbose_flag,
], ],
@ -323,6 +333,13 @@ def main():
dest="simd_flag", dest="simd_flag",
help="Running with the SIMD feature", help="Running with the SIMD feature",
) )
parser.add_argument(
"-X",
action="store_true",
default=False,
dest="xip_flag",
help="Running with the XIP feature",
)
parser.add_argument( parser.add_argument(
"-t", "-t",
action="store_true", action="store_true",
@ -387,6 +404,7 @@ def main():
options.multi_module_flag, options.multi_module_flag,
options.multi_thread_flag, options.multi_thread_flag,
options.simd_flag, options.simd_flag,
options.xip_flag,
options.clean_up_flag, options.clean_up_flag,
options.verbose_flag, options.verbose_flag,
) )
@ -403,6 +421,7 @@ def main():
options.multi_module_flag, options.multi_module_flag,
options.multi_thread_flag, options.multi_thread_flag,
options.simd_flag, options.simd_flag,
options.xip_flag,
options.clean_up_flag, options.clean_up_flag,
options.verbose_flag, options.verbose_flag,
) )
@ -419,6 +438,7 @@ def main():
options.multi_module_flag, options.multi_module_flag,
options.multi_thread_flag, options.multi_thread_flag,
options.simd_flag, options.simd_flag,
options.xip_flag,
options.clean_up_flag, options.clean_up_flag,
options.verbose_flag, options.verbose_flag,
) )

View File

@ -210,6 +210,9 @@ parser.add_argument('--sgx', action='store_true',
parser.add_argument('--simd', default=False, action='store_true', parser.add_argument('--simd', default=False, action='store_true',
help="Enable SIMD") help="Enable SIMD")
parser.add_argument('--xip', default=False, action='store_true',
help="Enable XIP")
parser.add_argument('--multi-thread', default=False, action='store_true', parser.add_argument('--multi-thread', default=False, action='store_true',
help="Enable Multi-thread") help="Enable Multi-thread")
@ -942,6 +945,10 @@ def compile_wasm_to_aot(wasm_tempfile, aot_tempfile, runner, opts, r):
if not opts.simd: if not opts.simd:
cmd.append("--disable-simd") cmd.append("--disable-simd")
if opts.xip:
cmd.append("--enable-indirect-mode")
cmd.append("--disable-llvm-intrinsics")
if opts.multi_thread: if opts.multi_thread:
cmd.append("--enable-multi-thread") cmd.append("--enable-multi-thread")

View File

@ -17,9 +17,10 @@ function help()
echo "-s {suite_name} test only one suite (spec)" echo "-s {suite_name} test only one suite (spec)"
echo "-m set compile target of iwasm(x86_64\x86_32\armv7_vfp\thumbv7_vfp\riscv64_lp64d\riscv64_lp64)" echo "-m set compile target of iwasm(x86_64\x86_32\armv7_vfp\thumbv7_vfp\riscv64_lp64d\riscv64_lp64)"
echo "-t set compile type of iwasm(classic-interp\fast-interp\jit\aot)" echo "-t set compile type of iwasm(classic-interp\fast-interp\jit\aot)"
echo "-M enable the multi module feature" echo "-M enable multi module feature"
echo "-p enable multi thread feature" echo "-p enable multi thread feature"
echo "-S enable SIMD" echo "-S enable SIMD feature"
echo "-X enable XIP feature"
echo "-x test SGX" echo "-x test SGX"
echo "-b use the wabt binary release package instead of compiling from the source code" echo "-b use the wabt binary release package instead of compiling from the source code"
echo "-P run the spec test parallelly" echo "-P run the spec test parallelly"
@ -35,13 +36,14 @@ ENABLE_MULTI_MODULE=0
ENABLE_MULTI_THREAD=0 ENABLE_MULTI_THREAD=0
COLLECT_CODE_COVERAGE=0 COLLECT_CODE_COVERAGE=0
ENABLE_SIMD=0 ENABLE_SIMD=0
ENABLE_XIP=0
#unit test case arrary #unit test case arrary
TEST_CASE_ARR=() TEST_CASE_ARR=()
SGX_OPT="" SGX_OPT=""
PLATFORM=$(uname -s | tr A-Z a-z) PLATFORM=$(uname -s | tr A-Z a-z)
PARALLELISM=0 PARALLELISM=0
while getopts ":s:cabt:m:MCpSxP" opt while getopts ":s:cabt:m:MCpSXxP" opt
do do
OPT_PARSED="TRUE" OPT_PARSED="TRUE"
case $opt in case $opt in
@ -106,6 +108,10 @@ do
echo "enable SIMD feature" echo "enable SIMD feature"
ENABLE_SIMD=1 ENABLE_SIMD=1
;; ;;
X)
echo "enable XIP feature"
ENABLE_XIP=1
;;
x) x)
echo "test SGX" echo "test SGX"
SGX_OPT="--sgx" SGX_OPT="--sgx"
@ -378,7 +384,11 @@ function spec_test()
fi fi
if [[ ${ENABLE_MULTI_THREAD} == 1 ]]; then if [[ ${ENABLE_MULTI_THREAD} == 1 ]]; then
ARGS_FOR_SPEC_TEST+="-p " ARGS_FOR_SPEC_TEST+="-p "
fi
if [[ ${ENABLE_XIP} == 1 ]]; then
ARGS_FOR_SPEC_TEST+="-X "
fi fi
# require warmc only in aot mode # require warmc only in aot mode