From 9be775e0ecb6d3283e1fe9f71e5011be646f98c9 Mon Sep 17 00:00:00 2001 From: Marcin Kolny Date: Tue, 10 Sep 2024 09:52:06 +0100 Subject: [PATCH] Implement dynamic linking section loading (#3720) Also added build flags to enable dynamic linking. --- build-scripts/config_common.cmake | 3 + build-scripts/runtime_lib.cmake | 5 + core/config.h | 5 + core/iwasm/interpreter/wasm.h | 7 + core/iwasm/interpreter/wasm_loader.c | 15 + core/iwasm/interpreter/wasm_mini_loader.c | 16 + .../lib-dynlink/dynlink_section_loader.c | 307 ++++++++++++++++++ .../lib-dynlink/dynlink_section_loader.h | 17 + .../libraries/lib-dynlink/dynlink_types.c | 21 ++ .../libraries/lib-dynlink/dynlink_types.h | 49 +++ .../libraries/lib-dynlink/lib_dynlink.cmake | 12 + doc/build_wamr.md | 4 + 12 files changed, 461 insertions(+) create mode 100644 core/iwasm/libraries/lib-dynlink/dynlink_section_loader.c create mode 100644 core/iwasm/libraries/lib-dynlink/dynlink_section_loader.h create mode 100644 core/iwasm/libraries/lib-dynlink/dynlink_types.c create mode 100644 core/iwasm/libraries/lib-dynlink/dynlink_types.h create mode 100644 core/iwasm/libraries/lib-dynlink/lib_dynlink.cmake diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index 12fc06bd7..8bbf3c7e9 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -282,6 +282,9 @@ endif () if (WAMR_BUILD_LIB_WASI_THREADS EQUAL 1) message (" Lib wasi-threads enabled") endif () +if (WAMR_BUILD_DYNAMIC_LINKING EQUAL 1) + message (" Dynamic linking enabled") +endif () if (WAMR_BUILD_LIBC_EMCC EQUAL 1) message (" Libc emcc enabled") endif () diff --git a/build-scripts/runtime_lib.cmake b/build-scripts/runtime_lib.cmake index 3ab0cff4f..109cea6d7 100644 --- a/build-scripts/runtime_lib.cmake +++ b/build-scripts/runtime_lib.cmake @@ -119,6 +119,10 @@ if (WAMR_BUILD_LIB_WASI_THREADS EQUAL 1) set (WAMR_BUILD_SHARED_MEMORY 1) endif () +if (WAMR_BUILD_DYNAMIC_LINKING EQUAL 1) + include (${IWASM_DIR}/libraries/lib-dynlink/lib_dynlink.cmake) +endif () + if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) set (WAMR_BUILD_THREAD_MGR 1) include (${IWASM_DIR}/libraries/debug-engine/debug_engine.cmake) @@ -189,6 +193,7 @@ set (source_all ${IWASM_GC_SOURCE} ${LIB_WASI_THREADS_SOURCE} ${LIB_PTHREAD_SOURCE} + ${LIB_DYNLINK_SOURCE} ${THREAD_MGR_SOURCE} ${LIBC_EMCC_SOURCE} ${LIB_RATS_SOURCE} diff --git a/core/config.h b/core/config.h index a25eb543e..061dc101b 100644 --- a/core/config.h +++ b/core/config.h @@ -292,6 +292,11 @@ #define WASM_ENABLE_MULTI_MODULE 0 #endif +/* Support a dynamic linking */ +#ifndef WASM_ENABLE_DYNAMIC_LINKING +#define WASM_ENABLE_DYNAMIC_LINKING 0 +#endif + /* Enable wasm mini loader or not */ #ifndef WASM_ENABLE_MINI_LOADER #define WASM_ENABLE_MINI_LOADER 0 diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index e043465d4..10f8dad13 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/core/iwasm/interpreter/wasm.h @@ -12,6 +12,9 @@ #if WASM_ENABLE_GC != 0 #include "gc_export.h" #endif +#if WASM_ENABLE_DYNAMIC_LINKING != 0 +#include "dynlink_types.h" +#endif #ifdef __cplusplus extern "C" { @@ -1008,6 +1011,10 @@ struct WASMModule { WASMCustomSection *custom_section_list; #endif +#if WASM_ENABLE_DYNAMIC_LINKING != 0 + DynLinkSections dynlink_sections; +#endif + #if WASM_ENABLE_FAST_JIT != 0 /** * func pointers of Fast JITed (un-imported) functions diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index 3a21b1fc6..2096731a1 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -26,6 +26,9 @@ #if WASM_ENABLE_JIT != 0 #include "../compilation/aot_llvm.h" #endif +#if WASM_ENABLE_DYNAMIC_LINKING != 0 +#include "dynlink_section_loader.h" +#endif #ifndef TRACE_WASM_LOADER #define TRACE_WASM_LOADER 0 @@ -5236,6 +5239,14 @@ load_user_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, } #endif +#if WASM_ENABLE_DYNAMIC_LINKING != 0 + if (!dynlink_try_load_dylink0_section(p, p_end, name_len, module, + is_load_from_file_buf, error_buf, + error_buf_size)) { + return false; + } +#endif + #if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 { WASMCustomSection *section = @@ -6937,6 +6948,10 @@ wasm_loader_unload(WASMModule *module) wasm_runtime_destroy_custom_sections(module->custom_section_list); #endif +#if WASM_ENABLE_DYNAMIC_LINKING != 0 + dynlink_sections_deinit(&module->dynlink_sections); +#endif + #if WASM_ENABLE_FAST_JIT != 0 if (module->fast_jit_func_ptrs) { wasm_runtime_free(module->fast_jit_func_ptrs); diff --git a/core/iwasm/interpreter/wasm_mini_loader.c b/core/iwasm/interpreter/wasm_mini_loader.c index 968eaf009..085f9c151 100644 --- a/core/iwasm/interpreter/wasm_mini_loader.c +++ b/core/iwasm/interpreter/wasm_mini_loader.c @@ -19,6 +19,9 @@ #if WASM_ENABLE_JIT != 0 #include "../compilation/aot_llvm.h" #endif +#if WASM_ENABLE_DYNAMIC_LINKING != 0 +#include "dynlink_section_loader.h" +#endif /* Read a value of given type from the address pointed to by the given pointer and increase the pointer to the position just after the @@ -2009,6 +2012,15 @@ load_user_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, } } #endif + +#if WASM_ENABLE_DYNAMIC_LINKING != 0 + if (!dynlink_try_load_dylink0_section(p, p_end, name_len, module, + is_load_from_file_buf, error_buf, + error_buf_size)) { + return false; + } +#endif + LOG_VERBOSE("Load custom section success.\n"); (void)name_len; return true; @@ -3371,6 +3383,10 @@ wasm_loader_unload(WASMModule *module) } #endif +#if WASM_ENABLE_DYNAMIC_LINKING != 0 + dynlink_sections_deinit(&module->dynlink_sections); +#endif + #if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ && WASM_ENABLE_LAZY_JIT != 0 os_mutex_destroy(&module->instance_list_lock); diff --git a/core/iwasm/libraries/lib-dynlink/dynlink_section_loader.c b/core/iwasm/libraries/lib-dynlink/dynlink_section_loader.c new file mode 100644 index 000000000..f4ce5da52 --- /dev/null +++ b/core/iwasm/libraries/lib-dynlink/dynlink_section_loader.c @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "dynlink_section_loader.h" +#include "wasm_runtime.h" +#include "wasm_loader_common.h" + +#define SECTION_NAME "dylink.0" +#define SECTION_NAME_LEN (sizeof(SECTION_NAME) - 1) + +enum DynLinkSubSectionType { + WASM_DYLINK_MEM_INFO = 1, + WASM_DYLINK_NEEDED, + WASM_DYLINK_EXPORT_INFO, + WASM_DYLINK_IMPORT_INFO, +}; + +typedef struct { + uint8 *ptr; + const uint8 *ptr_end; + WASMModule *module; + bool is_load_from_file_buf; + char *error_buf; + uint32 error_buf_size; +} DynLinkLoaderCtx; + +#define read_leb_uint32(p, p_end, res) \ + do { \ + uint64 res64; \ + if (!read_leb((uint8 **)&p, p_end, 32, false, &res64, ctx->error_buf, \ + ctx->error_buf_size)) \ + goto fail; \ + res = (uint32)res64; \ + } while (0) + +static void +set_error_buf(const DynLinkLoaderCtx *ctx, const char *string) +{ + if (ctx->error_buf != NULL) { + snprintf(ctx->error_buf, ctx->error_buf_size, + "Loading dynlink.0 section failed: %s", string); + } +} + +static bool +check_buf(const uint8 *buf, const uint8 *buf_end, uint32 length, + const DynLinkLoaderCtx *ctx) +{ + if ((uintptr_t)buf + length < (uintptr_t)buf + || (uintptr_t)buf + length > (uintptr_t)buf_end) { + set_error_buf(ctx, "unexpected end of section or function"); + return false; + } + return true; +} + +static char * +load_string(DynLinkLoaderCtx *ctx) +{ + uint32 length; + char *ret = NULL; + bh_assert(ctx); + + read_leb_uint32(ctx->ptr, ctx->ptr_end, length); + if (!check_buf(ctx->ptr, ctx->ptr_end, length, ctx)) { + goto fail; + } + + ret = wasm_const_str_list_insert(ctx->ptr, length, ctx->module, + ctx->is_load_from_file_buf, ctx->error_buf, + ctx->error_buf_size); + + if (ret != NULL) { + ctx->ptr += length; + } + +fail: + return ret; +} + +static bool +load_mem_info_section(DynLinkLoaderCtx *ctx) +{ + DynLinkSectionMemInfo *mem_info = &ctx->module->dynlink_sections.mem_info; + + bh_assert(ctx); + + read_leb_uint32(ctx->ptr, ctx->ptr_end, mem_info->memory_size); + read_leb_uint32(ctx->ptr, ctx->ptr_end, mem_info->memory_alignment); + read_leb_uint32(ctx->ptr, ctx->ptr_end, mem_info->table_size); + read_leb_uint32(ctx->ptr, ctx->ptr_end, mem_info->table_alignment); + + LOG_VERBOSE("Load dylink memory section success:"); + LOG_VERBOSE(" memory size: %" PRIu32, mem_info->memory_size); + LOG_VERBOSE(" memory alignment: %" PRIu32, mem_info->memory_alignment); + LOG_VERBOSE(" table size: %" PRIu32, mem_info->table_size); + LOG_VERBOSE(" table alignment: %" PRIu32, mem_info->table_alignment); + + return true; + +fail: + return false; +} + +static bool +load_needed_section(DynLinkLoaderCtx *ctx) +{ + DynLinkSectionNeeded *needed = &ctx->module->dynlink_sections.needed; + uint32 i; + + bh_assert(ctx); + + read_leb_uint32(ctx->ptr, ctx->ptr_end, needed->count); + + needed->entries = + wasm_runtime_malloc(sizeof(*needed->entries) * needed->count); + if (needed->entries == NULL) { + set_error_buf(ctx, "out of memory"); + return false; + } + + for (i = 0; i < needed->count; i++) { + if (!(needed->entries[i] = load_string(ctx))) { + goto fail; + } + LOG_VERBOSE("Load dylink needed entry success: %s", needed->entries[i]); + } + + LOG_VERBOSE("Load dylink needed section success."); + + return true; + +fail: + if (needed->entries != NULL) { + wasm_runtime_free(needed->entries); + } + needed->entries = NULL; + + return false; +} + +static bool +load_export_info_section(DynLinkLoaderCtx *ctx) +{ + DynLinkSectionExportInfo *export_info = + &ctx->module->dynlink_sections.export_info; + uint32 i; + + bh_assert(ctx); + + read_leb_uint32(ctx->ptr, ctx->ptr_end, export_info->count); + + export_info->entries = + wasm_runtime_malloc(sizeof(*export_info->entries) * export_info->count); + + if (export_info->entries == NULL) { + set_error_buf(ctx, "out of memory"); + return false; + } + + for (i = 0; i < export_info->count; i++) { + if (!(export_info->entries[i].name = load_string(ctx))) { + goto fail; + } + read_leb_uint32(ctx->ptr, ctx->ptr_end, export_info->entries[i].flags); + + LOG_VERBOSE("Load dylink export entry success: %s %" PRIu32, + export_info->entries[i].name, + export_info->entries[i].flags); + } + + LOG_VERBOSE("Load dylink export section success."); + + return true; +fail: + if (export_info->entries != NULL) { + wasm_runtime_free(export_info->entries); + export_info->entries = NULL; + } + + return false; +} + +static bool +load_import_info_section(DynLinkLoaderCtx *ctx) +{ + DynLinkSectionImportInfo *import_info = + &ctx->module->dynlink_sections.import_info; + + bh_assert(ctx); + + if (!(import_info->module = load_string(ctx))) { + goto fail; + } + + if (!(import_info->field = load_string(ctx))) { + goto fail; + } + + read_leb_uint32(ctx->ptr, ctx->ptr_end, import_info->flags); + + LOG_VERBOSE("Load dylink import section success: %s %s %" PRIu32, + import_info->module, import_info->field, import_info->flags); + + return true; + +fail: + return false; +} + +static bool +dynlink_load_subsections(DynLinkLoaderCtx *ctx) +{ + while (ctx->ptr < ctx->ptr_end) { + uint8 type = *ctx->ptr++; + uint32 subsection_size; + + read_leb_uint32(ctx->ptr, ctx->ptr_end, subsection_size); + + if (!check_buf(ctx->ptr, ctx->ptr_end, subsection_size, ctx)) { + return false; + } + + switch (type) { + case WASM_DYLINK_MEM_INFO: + if (!load_mem_info_section(ctx)) { + return false; + } + break; + case WASM_DYLINK_NEEDED: + if (!load_needed_section(ctx)) { + return false; + } + break; + case WASM_DYLINK_EXPORT_INFO: + if (!load_export_info_section(ctx)) { + return false; + } + break; + case WASM_DYLINK_IMPORT_INFO: + if (!load_import_info_section(ctx)) { + return false; + } + break; + default: + set_error_buf(ctx, "unknown subsection type"); + return false; + } + } + + return true; + +fail: + return false; +} + +bool +dynlink_load_dylink0_section(const uint8 *buf, const uint8 *buf_end, + WASMModule *module, bool is_load_from_file_buf, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + + DynLinkLoaderCtx ctx = { + .ptr = (uint8 *)p, + .ptr_end = p_end, + .module = module, + .is_load_from_file_buf = is_load_from_file_buf, + .error_buf = error_buf, + .error_buf_size = error_buf_size, + }; + + if (p >= p_end) { + set_error_buf(&ctx, "unexpected end"); + return false; + } + + memset(&module->dynlink_sections, 0, sizeof(module->dynlink_sections)); + + return dynlink_load_subsections(&ctx); +} + +static bool +dynlink_is_dylink0_section(const uint8 *buf, uint32 section_name_len) +{ + return section_name_len == SECTION_NAME_LEN + && memcmp(buf, SECTION_NAME, SECTION_NAME_LEN) == 0; +} + +bool +dynlink_try_load_dylink0_section(const uint8 *buf, const uint8 *buf_end, + uint32 section_name_len, WASMModule *module, + bool is_load_from_file_buf, char *error_buf, + uint32 error_buf_size) +{ + + if (dynlink_is_dylink0_section(buf, section_name_len)) { + buf += section_name_len; + return dynlink_load_dylink0_section(buf, buf_end, module, + is_load_from_file_buf, error_buf, + error_buf_size); + } + + return true; +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-dynlink/dynlink_section_loader.h b/core/iwasm/libraries/lib-dynlink/dynlink_section_loader.h new file mode 100644 index 000000000..71272bcac --- /dev/null +++ b/core/iwasm/libraries/lib-dynlink/dynlink_section_loader.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _DYNLINK_SECTION_LOADER_H +#define _DYNLINK_SECTION_LOADER_H + +#include "wasm.h" +#include "dynlink_types.h" + +bool +dynlink_try_load_dylink0_section(const uint8 *buf, const uint8 *buf_end, + uint32 section_name_len, WASMModule *module, + bool is_load_from_file_buf, char *error_buf, + uint32 error_buf_size); +#endif \ No newline at end of file diff --git a/core/iwasm/libraries/lib-dynlink/dynlink_types.c b/core/iwasm/libraries/lib-dynlink/dynlink_types.c new file mode 100644 index 000000000..02cd1477c --- /dev/null +++ b/core/iwasm/libraries/lib-dynlink/dynlink_types.c @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include "dynlink_types.h" +#include "bh_assert.h" + +void +dynlink_sections_deinit(DynLinkSections *sections) +{ + bh_assert(sections); + + if (sections->needed.entries) { + wasm_runtime_free(sections->needed.entries); + } + if (sections->export_info.entries) { + wasm_runtime_free(sections->export_info.entries); + } + + memset(sections, 0, sizeof(*sections)); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-dynlink/dynlink_types.h b/core/iwasm/libraries/lib-dynlink/dynlink_types.h new file mode 100644 index 000000000..051a474f1 --- /dev/null +++ b/core/iwasm/libraries/lib-dynlink/dynlink_types.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _DYNLINK_TYPES_H +#define _DYNLINK_TYPES_H + +#include "platform_common.h" + +typedef struct { + uint32 memory_size; + uint32 memory_alignment; + uint32 table_size; + uint32 table_alignment; +} DynLinkSectionMemInfo; + +typedef struct { + uint32 count; + char **entries; +} DynLinkSectionNeeded; + +typedef struct { + char *name; + uint32 flags; +} DynLinkSectionExportInfoEntry; + +typedef struct { + uint32 count; + DynLinkSectionExportInfoEntry *entries; +} DynLinkSectionExportInfo; + +typedef struct { + char *module; + char *field; + uint32 flags; +} DynLinkSectionImportInfo; + +typedef struct { + DynLinkSectionMemInfo mem_info; + DynLinkSectionNeeded needed; + DynLinkSectionExportInfo export_info; + DynLinkSectionImportInfo import_info; +} DynLinkSections; + +void +dynlink_sections_deinit(DynLinkSections *sections); + +#endif \ No newline at end of file diff --git a/core/iwasm/libraries/lib-dynlink/lib_dynlink.cmake b/core/iwasm/libraries/lib-dynlink/lib_dynlink.cmake new file mode 100644 index 000000000..78f57e220 --- /dev/null +++ b/core/iwasm/libraries/lib-dynlink/lib_dynlink.cmake @@ -0,0 +1,12 @@ +# Copyright (C) 2024 Amazon.com Inc. or its affiliates. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (LIB_DYNLINK_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_DYNAMIC_LINKING=1) + +include_directories(${LIB_DYNLINK_DIR}) + +set (LIB_DYNLINK_SOURCE + ${LIB_DYNLINK_DIR}/dynlink_section_loader.c + ${LIB_DYNLINK_DIR}/dynlink_types.c) diff --git a/doc/build_wamr.md b/doc/build_wamr.md index 2adb17f6a..73d22cfd8 100644 --- a/doc/build_wamr.md +++ b/doc/build_wamr.md @@ -64,6 +64,10 @@ cmake -DWAMR_BUILD_PLATFORM=linux -DWAMR_BUILD_TARGET=ARM - **WAMR_BUILD_MULTI_MODULE**=1/0, default to disable if not set > Note: See [Multiple Modules as Dependencies](./multi_module.md) for more details. +#### **Enable Dynamic Linking feature** + +- **WAMR_BUILD_DYNAMIC_LINKING**=1/0, build the [Dynamic Linking](https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md) support, default to disable if not set + #### **Enable WASM mini loader** - **WAMR_BUILD_MINI_LOADER**=1/0, default to disable if not set