Преглед изворни кода

Implement dynamic linking section loading (#3720)

Also added build flags to enable dynamic linking.
Marcin Kolny пре 1 година
родитељ
комит
9be775e0ec

+ 3 - 0
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 ()

+ 5 - 0
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}

+ 5 - 0
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

+ 7 - 0
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

+ 15 - 0
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);

+ 16 - 0
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);

+ 307 - 0
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;
+}

+ 17 - 0
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

+ 21 - 0
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));
+}

+ 49 - 0
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

+ 12 - 0
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)

+ 4 - 0
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