commit 9bd665b95d72f696db659675bf3c85367e1b8cf1
parent 5bd8de1ceeabdaef0abc018ea94891371381655d
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 3 Jun 2026 19:18:22 -0700
Harden wasm runtime import handling
Diffstat:
33 files changed, 1467 insertions(+), 252 deletions(-)
diff --git a/driver/cmd/run.c b/driver/cmd/run.c
@@ -6,6 +6,7 @@
#include <kit/wasm.h>
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include "cflags.h"
#include "driver.h"
@@ -696,67 +697,221 @@ typedef struct RunWasmMemoryPrefix {
uint32_t flags;
} RunWasmMemoryPrefix;
-#define RUN_WASM_INSTANCE_BYTES (64u * 1024u)
-#define RUN_WASM_MEMORY_BYTES (16u * 1024u * 1024u)
+typedef struct RunWasmInstanceAlloc {
+ uint8_t* instance;
+ uint8_t** memories;
+ uint64_t* memory_bytes;
+ uint32_t nmemories;
+ uint64_t instance_bytes;
+} RunWasmInstanceAlloc;
+
+#define RUN_WASM_MAX_INSTANCE_BYTES (64ull * 1024ull * 1024ull)
+#define RUN_WASM_MAX_TOTAL_MEMORY_BYTES (1024ull * 1024ull * 1024ull)
+
+static int run_wasm_u64_to_size(RunOptions* ro, uint64_t n, size_t* out,
+ const char* what) {
+ (void)ro;
+ if (n > (uint64_t)SIZE_MAX) {
+ driver_errf(RUN_TOOL, "wasm %s exceeds host size_t", what);
+ return 1;
+ }
+ *out = (size_t)n;
+ return 0;
+}
+
+static int run_wasm_page_bytes(RunOptions* ro, uint64_t pages, uint64_t* out) {
+ (void)ro;
+ if (pages > UINT64_MAX / (uint64_t)KIT_WASM_PAGE_SIZE) {
+ driver_errf(RUN_TOOL, "wasm memory size overflows");
+ return 1;
+ }
+ *out = pages * (uint64_t)KIT_WASM_PAGE_SIZE;
+ return 0;
+}
+
+static int run_wasm_host_add_type(const KitWasmImportType* type) {
+ return type && type->nparams == 2u && type->nresults == 1u &&
+ type->params[0] == KIT_WASM_VAL_I32 &&
+ type->params[1] == KIT_WASM_VAL_I32 &&
+ type->results[0] == KIT_WASM_VAL_I32;
+}
+
+static void* run_wasm_test_resolve(void* user, const char* module,
+ const char* field,
+ const KitWasmImportType* type) {
+ extern int32_t run_test_host_add(KitWasmInstance*, int32_t, int32_t);
+ (void)user;
+ if (!module || !field) return NULL;
+ if (driver_streq(module, "env") && driver_streq(field, "host_add") &&
+ run_wasm_host_add_type(type))
+ return (void*)(uintptr_t)run_test_host_add;
+ return NULL;
+}
+
+static void run_wasm_free_instance(RunOptions* ro,
+ RunWasmInstanceAlloc* alloc);
/* Allocate and wire a Wasm instance + linear memory and bind host imports.
* Returns 0 on success (caller owns the returned instance and memory),
* nonzero on error (already freed). Shared by the JIT and interpreter paths. */
static int run_wasm_make_instance(RunOptions* ro, KitCompiler* compiler,
- KitJit* jit, uint8_t** inst_out,
- uint8_t** mem_out) {
- uint8_t* instance =
- (uint8_t*)driver_alloc_zeroed(ro->env, RUN_WASM_INSTANCE_BYTES);
- uint8_t* memory =
- (uint8_t*)driver_alloc_zeroed(ro->env, RUN_WASM_MEMORY_BYTES);
+ KitJit* jit,
+ RunWasmInstanceAlloc* alloc_out) {
+ KitWasmRuntimeLayout layout;
+ RunWasmInstanceAlloc alloc;
+ size_t instance_size;
const KitWasmHostImport* h_imports = NULL;
size_t h_nimports = 0;
KitWasmResolveFn h_resolve = NULL;
void* h_user = NULL;
- KitWasmHostImport canned[1];
- extern int32_t run_test_host_add(KitWasmInstance*, int32_t, int32_t);
- if (!instance || !memory) {
+ uint64_t total_memory_bytes = 0;
+ KitStatus layout_st;
+ memset(&alloc, 0, sizeof alloc);
+ if (!alloc_out) return 1;
+ memset(alloc_out, 0, sizeof *alloc_out);
+ layout_st = kit_wasm_get_runtime_layout(jit, &layout);
+ if (layout_st != KIT_OK) {
+ driver_errf(RUN_TOOL, layout_st == KIT_NOT_FOUND
+ ? "wasm runtime layout metadata missing"
+ : "wasm runtime layout metadata malformed");
+ return 1;
+ }
+ alloc.instance_bytes = layout.instance_size ? layout.instance_size : 1u;
+ if (alloc.instance_bytes > RUN_WASM_MAX_INSTANCE_BYTES) {
+ driver_errf(RUN_TOOL, "wasm instance too large: %llu bytes",
+ (unsigned long long)alloc.instance_bytes);
+ return 1;
+ }
+ if (run_wasm_u64_to_size(ro, alloc.instance_bytes, &instance_size,
+ "instance") != 0)
+ return 1;
+ alloc.instance = (uint8_t*)driver_alloc_zeroed(ro->env, instance_size);
+ alloc.nmemories = layout.nmemories;
+ if (!alloc.instance) {
driver_errf(RUN_TOOL, "out of memory");
- driver_free(ro->env, memory, RUN_WASM_MEMORY_BYTES);
- driver_free(ro->env, instance, RUN_WASM_INSTANCE_BYTES);
return 1;
}
- for (uint32_t i = 0; i < 8u; ++i)
- ((RunWasmMemoryPrefix*)instance)[i].data =
- memory + i * (RUN_WASM_MEMORY_BYTES / 8u);
+ if (layout.nmemories) {
+ size_t ptr_bytes;
+ size_t size_bytes;
+ if ((uint64_t)layout.nmemories > (uint64_t)SIZE_MAX / sizeof(uint8_t*) ||
+ (uint64_t)layout.nmemories > (uint64_t)SIZE_MAX / sizeof(uint64_t)) {
+ driver_errf(RUN_TOOL, "wasm memory count too large");
+ driver_free(ro->env, alloc.instance, instance_size);
+ return 1;
+ }
+ ptr_bytes = (size_t)layout.nmemories * sizeof(uint8_t*);
+ size_bytes = (size_t)layout.nmemories * sizeof(uint64_t);
+ alloc.memories = (uint8_t**)driver_alloc_zeroed(ro->env, ptr_bytes);
+ alloc.memory_bytes = (uint64_t*)driver_alloc_zeroed(ro->env, size_bytes);
+ if (!alloc.memories || !alloc.memory_bytes) {
+ driver_errf(RUN_TOOL, "out of memory");
+ driver_free(ro->env, alloc.memory_bytes, size_bytes);
+ driver_free(ro->env, alloc.memories, ptr_bytes);
+ driver_free(ro->env, alloc.instance, instance_size);
+ return 1;
+ }
+ }
+ for (uint32_t i = 0; i < layout.nmemories; ++i) {
+ const KitWasmMemoryLayout* ml = &layout.memories[i];
+ RunWasmMemoryPrefix* rec;
+ uint64_t mem_bytes;
+ size_t mem_size;
+ if (ml->max_pages < ml->min_pages) {
+ driver_errf(RUN_TOOL, "wasm memory maximum below minimum");
+ run_wasm_free_instance(ro, &alloc);
+ return 1;
+ }
+ if (ml->offset > alloc.instance_bytes ||
+ alloc.instance_bytes - ml->offset < sizeof(RunWasmMemoryPrefix)) {
+ driver_errf(RUN_TOOL, "wasm memory record outside instance");
+ run_wasm_free_instance(ro, &alloc);
+ return 1;
+ }
+ if (run_wasm_page_bytes(ro, ml->max_pages, &mem_bytes) != 0) {
+ run_wasm_free_instance(ro, &alloc);
+ return 1;
+ }
+ if (mem_bytes > RUN_WASM_MAX_TOTAL_MEMORY_BYTES ||
+ total_memory_bytes > RUN_WASM_MAX_TOTAL_MEMORY_BYTES - mem_bytes) {
+ driver_errf(RUN_TOOL, "wasm linear memory reservation exceeds %llu bytes",
+ (unsigned long long)RUN_WASM_MAX_TOTAL_MEMORY_BYTES);
+ run_wasm_free_instance(ro, &alloc);
+ return 1;
+ }
+ total_memory_bytes += mem_bytes;
+ if (run_wasm_u64_to_size(ro, mem_bytes, &mem_size,
+ "linear memory") != 0) {
+ run_wasm_free_instance(ro, &alloc);
+ return 1;
+ }
+ if (mem_size) {
+ alloc.memories[i] = (uint8_t*)driver_alloc_zeroed(ro->env, mem_size);
+ if (!alloc.memories[i]) {
+ driver_errf(RUN_TOOL, "out of memory");
+ run_wasm_free_instance(ro, &alloc);
+ return 1;
+ }
+ }
+ alloc.memory_bytes[i] = mem_bytes;
+ rec = (RunWasmMemoryPrefix*)(alloc.instance + ml->offset);
+ rec->data = alloc.memories[i];
+ }
kit_wasm_get_host_imports(compiler, &h_imports, &h_nimports, &h_resolve,
&h_user);
if (h_nimports == 0 && !h_resolve && driver_getenv("KIT_TEST_HOST_IMPORTS")) {
- canned[0].module = "env";
- canned[0].field = "host_add";
- canned[0].func = (void*)(uintptr_t)run_test_host_add;
- h_imports = canned;
- h_nimports = 1;
+ h_resolve = run_wasm_test_resolve;
+ h_user = NULL;
}
- if (kit_wasm_bind_host_imports(compiler, jit, (KitWasmInstance*)instance,
- h_imports, h_nimports, h_resolve,
- h_user) != KIT_OK) {
- driver_errf(RUN_TOOL, "wasm host import bind failed");
- driver_free(ro->env, memory, RUN_WASM_MEMORY_BYTES);
- driver_free(ro->env, instance, RUN_WASM_INSTANCE_BYTES);
- return 1;
+ {
+ KitStatus bind_st = kit_wasm_bind_host_imports(
+ compiler, jit, (KitWasmInstance*)alloc.instance, h_imports, h_nimports,
+ h_resolve, h_user);
+ if (bind_st != KIT_OK) {
+ const char* msg = bind_st == KIT_NOT_FOUND
+ ? "wasm host import unresolved"
+ : (bind_st == KIT_UNSUPPORTED
+ ? "wasm host import kind unsupported"
+ : "wasm host import metadata malformed");
+ driver_errf(RUN_TOOL, "%s", msg);
+ run_wasm_free_instance(ro, &alloc);
+ return 1;
+ }
}
- *inst_out = instance;
- *mem_out = memory;
+ *alloc_out = alloc;
return 0;
}
-static void run_wasm_free_instance(RunOptions* ro, uint8_t* instance,
- uint8_t* memory) {
- driver_free(ro->env, memory, RUN_WASM_MEMORY_BYTES);
- driver_free(ro->env, instance, RUN_WASM_INSTANCE_BYTES);
+static void run_wasm_free_instance(RunOptions* ro,
+ RunWasmInstanceAlloc* alloc) {
+ if (!alloc) return;
+ if (alloc->memories) {
+ for (uint32_t i = 0; i < alloc->nmemories; ++i) {
+ size_t mem_size = 0;
+ if (alloc->memories[i] &&
+ run_wasm_u64_to_size(ro, alloc->memory_bytes[i], &mem_size,
+ "linear memory") == 0)
+ driver_free(ro->env, alloc->memories[i], mem_size);
+ }
+ driver_free(ro->env, alloc->memories,
+ (size_t)alloc->nmemories * sizeof(uint8_t*));
+ }
+ if (alloc->memory_bytes)
+ driver_free(ro->env, alloc->memory_bytes,
+ (size_t)alloc->nmemories * sizeof(uint64_t));
+ if (alloc->instance) {
+ size_t instance_size = 0;
+ if (run_wasm_u64_to_size(ro, alloc->instance_bytes, &instance_size,
+ "instance") == 0)
+ driver_free(ro->env, alloc->instance, instance_size);
+ }
+ memset(alloc, 0, sizeof *alloc);
}
static int run_call_wasm_entry(RunOptions* ro, KitCompiler* compiler,
KitJit* jit, void* entry, int* rc_out) {
void* init_sym = kit_jit_lookup(jit, KIT_SLICE_LIT("__kit_wasm_init"));
- uint8_t* instance;
- uint8_t* memory;
+ RunWasmInstanceAlloc alloc;
union {
void* p;
WasmInitFn fn;
@@ -766,15 +921,15 @@ static int run_call_wasm_entry(RunOptions* ro, KitCompiler* compiler,
WasmMainFn fn;
} entry_u;
if (!init_sym) return 0;
- if (run_wasm_make_instance(ro, compiler, jit, &instance, &memory) != 0) {
+ if (run_wasm_make_instance(ro, compiler, jit, &alloc) != 0) {
*rc_out = 1;
return 1;
}
init_u.p = init_sym;
entry_u.p = entry;
- init_u.fn((KitWasmInstance*)instance);
- *rc_out = entry_u.fn(instance);
- run_wasm_free_instance(ro, instance, memory);
+ init_u.fn((KitWasmInstance*)alloc.instance);
+ *rc_out = entry_u.fn(alloc.instance);
+ run_wasm_free_instance(ro, &alloc);
return 1;
}
@@ -790,8 +945,7 @@ static int run_call_wasm_entry_interp(RunOptions* ro, KitCompiler* compiler,
kit_interp_lookup(interp, KIT_SLICE_LIT("__kit_wasm_init"));
KitInterpFunc* entry_fn =
kit_interp_lookup(interp, kit_slice_cstr(ro->entry));
- uint8_t* instance;
- uint8_t* memory;
+ RunWasmInstanceAlloc alloc;
uint64_t args[1];
int64_t ret = 0;
KitInterpStatus s;
@@ -802,11 +956,11 @@ static int run_call_wasm_entry_interp(RunOptions* ro, KitCompiler* compiler,
*rc_out = 1;
return 1;
}
- if (run_wasm_make_instance(ro, compiler, jit, &instance, &memory) != 0) {
+ if (run_wasm_make_instance(ro, compiler, jit, &alloc) != 0) {
*rc_out = 1;
return 1;
}
- args[0] = (uint64_t)(uintptr_t)instance;
+ args[0] = (uint64_t)(uintptr_t)alloc.instance;
s = kit_interp_call_args(interp, init_fn, args, 1u, &ret);
if (s == KIT_INTERP_DONE)
s = kit_interp_call_args(interp, entry_fn, args, 1u, &ret);
@@ -817,7 +971,7 @@ static int run_call_wasm_entry_interp(RunOptions* ro, KitCompiler* compiler,
KIT_SLICE_ARG(kit_slice_cstr(ro->entry)));
*rc_out = 1;
}
- run_wasm_free_instance(ro, instance, memory);
+ run_wasm_free_instance(ro, &alloc);
return 1;
}
diff --git a/include/kit/wasm.h b/include/kit/wasm.h
@@ -57,14 +57,72 @@ typedef struct KitWasmImportType {
uint32_t nresults;
} KitWasmImportType;
+enum { KIT_WASM_PAGE_SIZE = 65536u };
+
+#ifndef KIT_WASM_MEMORY_FLAG_TYPES
+#define KIT_WASM_MEMORY_FLAG_TYPES
+enum {
+ KIT_WASM_MEMORY_SHARED = 1u << 0,
+ KIT_WASM_MEMORY_64 = 1u << 1,
+};
+#endif
+
+#ifndef KIT_WASM_IMPORT_KIND_TYPES
+#define KIT_WASM_IMPORT_KIND_TYPES
+typedef enum KitWasmImportKind {
+ KIT_WASM_IMPORT_FUNC = 0,
+ KIT_WASM_IMPORT_TABLE = 1,
+ KIT_WASM_IMPORT_MEMORY = 2,
+ KIT_WASM_IMPORT_GLOBAL = 3,
+} KitWasmImportKind;
+#endif
+
+typedef struct KitWasmMemoryImportType {
+ uint64_t min_pages;
+ uint64_t max_pages;
+ uint32_t flags; /* KIT_WASM_MEMORY_* */
+ uint32_t has_max; /* nonzero when the module declared an explicit maximum */
+} KitWasmMemoryImportType;
+
+typedef struct KitWasmTableImportType {
+ KitWasmValType elem_type;
+ uint32_t min;
+ uint32_t max;
+ uint32_t has_max; /* nonzero when the module declared an explicit maximum */
+} KitWasmTableImportType;
+
+typedef struct KitWasmGlobalImportType {
+ KitWasmValType type;
+ uint32_t mutable_;
+} KitWasmGlobalImportType;
+
+#ifndef KIT_WASM_RUNTIME_LAYOUT_TYPES
+#define KIT_WASM_RUNTIME_LAYOUT_TYPES
+typedef struct KitWasmMemoryLayout {
+ uint64_t offset; /* byte offset of this KitWasmMemory in the instance */
+ uint64_t min_pages; /* initial page count */
+ uint64_t max_pages; /* backing allocation must cover this page count */
+ uint32_t flags; /* KIT_WASM_MEMORY_* */
+ uint32_t reserved;
+} KitWasmMemoryLayout;
+
+typedef struct KitWasmRuntimeLayout {
+ uint64_t instance_size;
+ const KitWasmMemoryLayout* memories; /* borrowed from the JIT image */
+ uint32_t nmemories;
+} KitWasmRuntimeLayout;
+#endif
+
/* Static host-import descriptor.
*
* `func` must match the declared wasm type when called with the kit-
* instance ABI prepended:
* ret (*)(KitWasmInstance*, [wasm params...])
*
- * The binder validates (nparams, nresults) and the per-value type bytes
- * against the module's declared import type and refuses on mismatch.
+ * Static entries are raw C function pointers; the binder can match them by
+ * name but cannot prove the C signature. Use a KitWasmResolveFn when the
+ * host needs to validate imports against the declared KitWasmImportType
+ * before returning a function pointer.
*
* Strings are borrowed: they must outlive the next link / bind. */
typedef struct KitWasmHostImport {
@@ -75,8 +133,8 @@ typedef struct KitWasmHostImport {
/* Dynamic resolver. Invoked for imports not found in the static table.
* Returns a function pointer with the kit-instance ABI for the requested
- * import, or NULL to leave the slot unbound (the call site traps on
- * invocation). `type` describes the declared wasm signature. */
+ * import, or NULL to reject instantiation. `type` describes the declared wasm
+ * signature. */
typedef void* (*KitWasmResolveFn)(void* user, const char* module,
const char* field,
const KitWasmImportType* type);
@@ -84,8 +142,8 @@ typedef void* (*KitWasmResolveFn)(void* user, const char* module,
/* Configure host-import resolution for subsequent links on this compiler.
*
* `imports` is consulted first (linear scan by module+field). On miss
- * `resolve` is invoked. Either may be NULL. Unresolved imports remain
- * NULL — calling them traps via the existing import-null check.
+ * `resolve` is invoked. Either may be NULL. Unresolved imports make
+ * `kit_wasm_bind_host_imports` fail, matching Wasm instantiation semantics.
*
* The provided table and resolver pointers must outlive the next call to
* `kit_wasm_bind_host_imports` against this compiler. */
@@ -104,24 +162,37 @@ KIT_API void kit_wasm_get_host_imports(KitCompiler*,
KitWasmResolveFn* resolve_out,
void** user_out);
+/* Read the module-emitted runtime layout metadata from a JIT image. Returns
+ * KIT_NOT_FOUND when the image is not a kit-lowered Wasm module. */
+KIT_API KitStatus kit_wasm_get_runtime_layout(KitJit* jit,
+ KitWasmRuntimeLayout* out);
+
/* Resolve every declared import in the module loaded into `jit` against
* the supplied static table and/or resolver, and write the resolved
* function pointers into the per-import slots of `inst`.
*
* The function reads three module-emitted symbols out of `jit`:
* __kit_wasm_nimports uint32 count
- * __kit_wasm_imports array of {module, field, typeidx, slot_offset}
- * __kit_wasm_types array of {params, nparams, results, nresults}
+ * __kit_wasm_imports array of
+ * {module, field, kind, desc_index, slot_offset}
+ * __kit_wasm_nfunc_import_types
+ * unique function-import type-description count
+ * __kit_wasm_types unique function-import type descriptions
* If those symbols are absent (module has no imports, or was not built
* through kit's wasm frontend), the call is a no-op and returns KIT_OK.
*
- * Slots whose `(module, field)` resolves to NULL are left untouched — the
- * caller chose not to bind them, and the in-module null check will trap
- * if they are invoked.
+ * Function imports are resolved and written today. Memory, table, and global
+ * imports have emitted descriptors and reserved instance slots, but this
+ * binder returns KIT_UNSUPPORTED for those kinds until host-backed binding is
+ * implemented; runners must treat that as instantiation failure.
+ *
+ * Slots whose `(module, field)` resolves to NULL make binding fail. The
+ * in-module null checks are still present as a defensive guard against
+ * malformed or manually-built instances.
*
- * Returns KIT_MALFORMED on signature mismatch between the host import
- * and the declared wasm type, naming the offending import via the diag
- * sink configured on `compiler` (passed implicitly through the JIT). */
+ * Returns KIT_NOT_FOUND when a function import is unresolved, KIT_UNSUPPORTED
+ * for currently-unhandled import kinds, or KIT_MALFORMED if the image's import
+ * metadata is inconsistent. */
KIT_API KitStatus kit_wasm_bind_host_imports(KitCompiler* compiler, KitJit* jit,
KitWasmInstance* inst,
const KitWasmHostImport* imports,
diff --git a/lang/wasm/cg.c b/lang/wasm/cg.c
@@ -1,5 +1,6 @@
#include <kit/support/arena.h>
+#include "runtime_abi.h"
#include "wasm/wasm.h"
static KitCgTypeId wasm_cg_type(KitCompiler* c, KitCgBuiltinTypes b,
@@ -115,12 +116,12 @@ typedef struct WasmCgRuntime {
KitCgTypeId instance_ptr_ty;
uint64_t table_entry_size;
/* Byte offsets within the instance struct for each top-level slot. */
- uint64_t memory_offset[64];
- uint64_t func_import_offset[64];
- uint64_t func_ref_entry_offset[64];
- uint64_t global_offset[64];
- uint64_t table_offset[64];
- uint64_t table_entries_offset[64];
+ uint64_t* memory_offset;
+ uint64_t* func_import_offset;
+ uint64_t* func_ref_entry_offset;
+ uint64_t* global_offset;
+ uint64_t* table_offset;
+ uint64_t* table_entries_offset;
/* Byte offsets within their containing record. */
uint64_t memory_data_offset;
uint64_t memory_pages_offset;
@@ -247,6 +248,13 @@ static uint64_t wasm_cg_field_offset(KitCompiler* c, KitCgTypeId ty,
return off;
}
+static uint32_t wasm_cg_checked_add_u32(KitCompiler* c, uint32_t a,
+ uint32_t b, KitSrcLoc loc) {
+ if (UINT32_MAX - a < b)
+ wasm_error(c, loc, "wasm: module layout is too large");
+ return a + b;
+}
+
static void wasm_cg_build_runtime(KitCompiler* c, KitCgBuiltinTypes b,
const WasmModule* m, WasmCgRuntime* rt,
KitArena* arena) {
@@ -261,36 +269,81 @@ static void wasm_cg_build_runtime(KitCompiler* c, KitCgBuiltinTypes b,
* func_ref entry per func) + nglobals + 2*ntables + ndata + nelems.
* Allocate the upper bound from the arena so nfields can grow with the
* module. */
- uint32_t instance_cap = m->nmemories + m->nfuncs + m->nfuncs + m->nglobals +
- 2u * m->ntables + m->ndata + 2u * m->nelems;
+ uint32_t instance_cap = 0;
KitCgField* instance_fields =
instance_cap ? kit_arena_zarray(arena, KitCgField, instance_cap) : NULL;
uint32_t nfields = 0;
- uint32_t memory_field_idx[64];
- uint32_t func_import_field_idx[64];
- uint32_t func_ref_entry_field_idx[64];
- uint32_t global_field_idx[64];
- uint32_t table_field_idx[64];
- uint32_t table_entries_field_idx[64];
+ uint32_t* memory_field_idx = NULL;
+ uint32_t* func_import_field_idx = NULL;
+ uint32_t* func_ref_entry_field_idx = NULL;
+ uint32_t* global_field_idx = NULL;
+ uint32_t* table_field_idx = NULL;
+ uint32_t* table_entries_field_idx = NULL;
memset(rt, 0, sizeof *rt);
+
+ instance_cap =
+ wasm_cg_checked_add_u32(c, instance_cap, m->nmemories, wasm_loc(0, 0));
+ instance_cap =
+ wasm_cg_checked_add_u32(c, instance_cap, m->nfuncs, wasm_loc(0, 0));
+ instance_cap =
+ wasm_cg_checked_add_u32(c, instance_cap, m->nfuncs, wasm_loc(0, 0));
+ instance_cap =
+ wasm_cg_checked_add_u32(c, instance_cap, m->nglobals, wasm_loc(0, 0));
+ if (m->ntables > UINT32_MAX / 2u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: module layout is too large");
+ instance_cap = wasm_cg_checked_add_u32(c, instance_cap, 2u * m->ntables,
+ wasm_loc(0, 0));
+ instance_cap =
+ wasm_cg_checked_add_u32(c, instance_cap, m->ndata, wasm_loc(0, 0));
+ if (m->nelems > UINT32_MAX / 2u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: module layout is too large");
+ instance_cap = wasm_cg_checked_add_u32(c, instance_cap, 2u * m->nelems,
+ wasm_loc(0, 0));
+ instance_fields =
+ instance_cap ? kit_arena_zarray(arena, KitCgField, instance_cap) : NULL;
+
rt->memory_field =
m->nmemories ? kit_arena_zarray(arena, uint32_t, m->nmemories) : NULL;
+ rt->memory_offset =
+ m->nmemories ? kit_arena_zarray(arena, uint64_t, m->nmemories) : NULL;
rt->func_import_field =
m->nfuncs ? kit_arena_zarray(arena, uint32_t, m->nfuncs) : NULL;
+ rt->func_import_offset =
+ m->nfuncs ? kit_arena_zarray(arena, uint64_t, m->nfuncs) : NULL;
rt->func_ref_entry_field =
m->nfuncs ? kit_arena_zarray(arena, uint32_t, m->nfuncs) : NULL;
+ rt->func_ref_entry_offset =
+ m->nfuncs ? kit_arena_zarray(arena, uint64_t, m->nfuncs) : NULL;
rt->global_field =
m->nglobals ? kit_arena_zarray(arena, uint32_t, m->nglobals) : NULL;
+ rt->global_offset =
+ m->nglobals ? kit_arena_zarray(arena, uint64_t, m->nglobals) : NULL;
rt->table_field =
m->ntables ? kit_arena_zarray(arena, uint32_t, m->ntables) : NULL;
+ rt->table_offset =
+ m->ntables ? kit_arena_zarray(arena, uint64_t, m->ntables) : NULL;
rt->table_entries_field =
m->ntables ? kit_arena_zarray(arena, uint32_t, m->ntables) : NULL;
+ rt->table_entries_offset =
+ m->ntables ? kit_arena_zarray(arena, uint64_t, m->ntables) : NULL;
rt->passive_data_field =
m->ndata ? kit_arena_zarray(arena, uint32_t, m->ndata) : NULL;
rt->passive_elem_field =
m->nelems ? kit_arena_zarray(arena, uint32_t, m->nelems) : NULL;
rt->passive_elem_storage_field =
m->nelems ? kit_arena_zarray(arena, uint32_t, m->nelems) : NULL;
+ memory_field_idx =
+ m->nmemories ? kit_arena_zarray(arena, uint32_t, m->nmemories) : NULL;
+ func_import_field_idx =
+ m->nfuncs ? kit_arena_zarray(arena, uint32_t, m->nfuncs) : NULL;
+ func_ref_entry_field_idx =
+ m->nfuncs ? kit_arena_zarray(arena, uint32_t, m->nfuncs) : NULL;
+ global_field_idx =
+ m->nglobals ? kit_arena_zarray(arena, uint32_t, m->nglobals) : NULL;
+ table_field_idx =
+ m->ntables ? kit_arena_zarray(arena, uint32_t, m->ntables) : NULL;
+ table_entries_field_idx =
+ m->ntables ? kit_arena_zarray(arena, uint32_t, m->ntables) : NULL;
rt->i8_ptr_ty = kit_cg_type_ptr(c, b.id[KIT_CG_BUILTIN_I8], 0);
rt->void_ptr_ty = rt->i8_ptr_ty;
memset(memory_fields, 0, sizeof memory_fields);
@@ -647,12 +700,17 @@ static void wasm_cg_memory_check(KitCompiler* c, KitCg* cg, KitCgBuiltinTypes b,
KitCgLocal instance_local,
const WasmInsn* in) {
uint32_t width = wasm_mem_width(in->kind);
- uint64_t end = in->offset64 + width;
+ uint64_t end;
KitCgLabel ok = kit_cg_label_new(cg);
- uint32_t max_pages = (uint32_t)(m->memories[in->memidx].has_max
- ? m->memories[in->memidx].max_pages
- : m->memories[in->memidx].min_pages);
- if (end > (uint64_t)max_pages * 65536u) {
+ uint64_t max_pages = m->memories[in->memidx].has_max
+ ? m->memories[in->memidx].max_pages
+ : m->memories[in->memidx].min_pages;
+ if (in->offset64 > UINT64_MAX - (uint64_t)width) {
+ wasm_cg_trap_bounds(cg, rt);
+ return;
+ }
+ end = in->offset64 + (uint64_t)width;
+ if (max_pages > UINT64_MAX / 65536u || end > max_pages * 65536u) {
wasm_cg_trap_bounds(cg, rt);
return;
}
@@ -1030,13 +1088,10 @@ static KitCgSym wasm_cg_intern_cstr(KitCg* cg, KitCgBuiltinTypes b,
b.id[KIT_CG_BUILTIN_I8]);
}
-/* Emit the three host-import metadata symbols read by
- * kit_wasm_bind_host_imports: __kit_wasm_nimports (uint32),
- * __kit_wasm_imports (KitWasmImportDesc[]), and __kit_wasm_types
- * (KitWasmTypeDesc[]). Layout matches runtime_abi.h. When the module has
- * no function imports, only the count (=0) is emitted; the other two
- * symbols are omitted so the binder can no-op early via kit_jit_lookup
- * returning NULL. */
+/* Emit the host-import metadata symbols read by kit_wasm_bind_host_imports.
+ * Layout matches runtime_abi.h. When the module has no imports, only the
+ * count (=0) is emitted; descriptor/type arrays are omitted so the binder can
+ * no-op early via kit_jit_lookup returning NULL. */
static void wasm_cg_emit_host_import_metadata(KitCompiler* c, KitCg* cg,
KitCgBuiltinTypes b,
const WasmModule* m,
@@ -1048,18 +1103,39 @@ static void wasm_cg_emit_host_import_metadata(KitCompiler* c, KitCg* cg,
uint32_t ptr_size = (uint32_t)kit_cg_type_size(c, ptr_ty);
uint32_t ptr_align = ptr_size ? ptr_size : 8u;
uint32_t nimports = 0;
- /* The wire format of one KitWasmImportDesc on the runtime side is
- * { const char* module; const char* field; uint32_t typeidx;
- * uint32_t slot_offset; }
- * with natural alignment. Total = 2*ptr + 8. */
- uint32_t desc_size = 2u * ptr_size + 8u;
- KitCgSym nimports_sym, imports_sym, types_sym;
+ uint32_t nfunc_imports = 0;
+ uint32_t nmemory_imports = 0;
+ uint32_t ntable_imports = 0;
+ uint32_t nglobal_imports = 0;
+ uint32_t desc_size = 2u * ptr_size + 16u;
+ uint32_t type_desc_size =
+ 2u * ptr_size + 8u +
+ (ptr_align > 4u ? 2u * (ptr_align - 4u) : 0u);
+ KitCgSym nimports_sym, imports_sym;
KitCgDecl decl;
KitCgDataDefAttrs data_attrs;
uint32_t i;
- for (i = 0; i < m->nfuncs; ++i)
- if (m->funcs[i].is_import) nimports++;
+ for (i = 0; i < m->nfuncs; ++i) {
+ if (!m->funcs[i].is_import) continue;
+ nimports++;
+ nfunc_imports++;
+ }
+ for (i = 0; i < m->nmemories; ++i) {
+ if (!m->memories[i].is_import) continue;
+ nimports++;
+ nmemory_imports++;
+ }
+ for (i = 0; i < m->ntables; ++i) {
+ if (!m->tables[i].is_import) continue;
+ nimports++;
+ ntable_imports++;
+ }
+ for (i = 0; i < m->nglobals; ++i) {
+ if (!m->globals[i].is_import) continue;
+ nimports++;
+ nglobal_imports++;
+ }
/* Always emit the count symbol so the binder can read it unconditionally.
* If zero, the descriptor/type arrays are omitted. */
@@ -1086,25 +1162,45 @@ static void wasm_cg_emit_host_import_metadata(KitCompiler* c, KitCg* cg,
if (!nimports) return;
/* Walk imports once, build sym handles for module/field strings, capture
- * each import's typeidx and slot offset. Build a compact list of unique
- * typeidxs used by imports so __kit_wasm_types is no larger than
- * needed. */
+ * each import's kind-specific descriptor index and slot offset. Function
+ * import signatures are deduplicated into __kit_wasm_types; the emitted
+ * __kit_wasm_nfunc_import_types count lets the binder validate the compact
+ * descriptor indexes before reading that table. */
typedef struct WasmImportEmit {
KitCgSym module_sym;
KitCgSym field_sym;
- uint32_t local_typeidx; /* index into the emitted types array */
+ uint32_t kind;
+ uint32_t desc_index;
uint32_t slot_offset;
} WasmImportEmit;
WasmImportEmit* descs = kit_arena_zarray(arena, WasmImportEmit, nimports);
- uint32_t* type_remap =
- kit_arena_zarray(arena, uint32_t, m->ntypes ? m->ntypes : 1u);
- uint32_t* local_to_module = kit_arena_zarray(arena, uint32_t, nimports);
+ KitWasmMemoryImportDesc* memory_descs =
+ nmemory_imports ? kit_arena_zarray(arena, KitWasmMemoryImportDesc,
+ nmemory_imports)
+ : NULL;
+ KitWasmTableImportDesc* table_descs =
+ ntable_imports
+ ? kit_arena_zarray(arena, KitWasmTableImportDesc, ntable_imports)
+ : NULL;
+ KitWasmGlobalImportDesc* global_descs =
+ nglobal_imports ? kit_arena_zarray(arena, KitWasmGlobalImportDesc,
+ nglobal_imports)
+ : NULL;
+ uint32_t* type_remap = nfunc_imports
+ ? kit_arena_zarray(arena, uint32_t,
+ m->ntypes ? m->ntypes : 1u)
+ : NULL;
+ uint32_t* local_to_module =
+ nfunc_imports ? kit_arena_zarray(arena, uint32_t, nfunc_imports) : NULL;
uint32_t ntypes = 0;
uint32_t d = 0;
- for (i = 0; i < m->ntypes; ++i) type_remap[i] = UINT32_MAX;
+ uint32_t mem_d = 0;
+ uint32_t table_d = 0;
+ uint32_t global_d = 0;
+ for (i = 0; type_remap && i < m->ntypes; ++i) type_remap[i] = UINT32_MAX;
for (i = 0; i < m->nfuncs; ++i) {
const WasmFunc* f = &m->funcs[i];
- uint64_t slot_off = 0;
+ uint64_t slot_off;
uint32_t module_typeidx;
if (!f->is_import) continue;
module_typeidx = f->typeidx;
@@ -1121,12 +1217,9 @@ static void wasm_cg_emit_host_import_metadata(KitCompiler* c, KitCg* cg,
wasm_cg_intern_cstr(cg, b, f->import_module ? f->import_module : "");
descs[d].field_sym =
wasm_cg_intern_cstr(cg, b, f->import_name ? f->import_name : "");
- descs[d].local_typeidx = type_remap[module_typeidx];
- if (kit_cg_type_record_field(c, rt->instance_ty, rt->func_import_field[i],
- NULL, &slot_off) != KIT_OK) {
- wasm_error(c, f->loc, "wasm: failed to query import slot offset");
- return;
- }
+ descs[d].kind = KIT_WASM_IMPORT_FUNC;
+ descs[d].desc_index = type_remap[module_typeidx];
+ slot_off = rt->func_import_offset[i];
if (slot_off > UINT32_MAX) {
wasm_error(c, f->loc, "wasm: import slot offset exceeds u32");
return;
@@ -1134,17 +1227,88 @@ static void wasm_cg_emit_host_import_metadata(KitCompiler* c, KitCg* cg,
descs[d].slot_offset = (uint32_t)slot_off;
d++;
}
+ for (i = 0; i < m->nmemories; ++i) {
+ const WasmMemory* mem = &m->memories[i];
+ uint64_t max_pages;
+ uint32_t flags;
+ if (!mem->is_import) continue;
+ max_pages = mem->has_max ? mem->max_pages : mem->min_pages;
+ flags = (mem->shared ? KIT_WASM_MEMORY_SHARED : 0u) |
+ (mem->is64 ? KIT_WASM_MEMORY_64 : 0u);
+ descs[d].module_sym =
+ wasm_cg_intern_cstr(cg, b,
+ mem->import_module ? mem->import_module : "");
+ descs[d].field_sym =
+ wasm_cg_intern_cstr(cg, b, mem->import_name ? mem->import_name : "");
+ descs[d].kind = KIT_WASM_IMPORT_MEMORY;
+ descs[d].desc_index = mem_d;
+ if (rt->memory_offset[i] > UINT32_MAX) {
+ wasm_error(c, wasm_loc(0, 0), "wasm: memory import slot exceeds u32");
+ return;
+ }
+ descs[d].slot_offset = (uint32_t)rt->memory_offset[i];
+ memory_descs[mem_d].min_pages = mem->min_pages;
+ memory_descs[mem_d].max_pages = max_pages;
+ memory_descs[mem_d].flags = flags;
+ memory_descs[mem_d].has_max = mem->has_max ? 1u : 0u;
+ mem_d++;
+ d++;
+ }
+ for (i = 0; i < m->ntables; ++i) {
+ const WasmTable* t = &m->tables[i];
+ uint32_t max;
+ if (!t->is_import) continue;
+ max = t->has_max ? t->max : t->min;
+ descs[d].module_sym =
+ wasm_cg_intern_cstr(cg, b, t->import_module ? t->import_module : "");
+ descs[d].field_sym =
+ wasm_cg_intern_cstr(cg, b, t->import_name ? t->import_name : "");
+ descs[d].kind = KIT_WASM_IMPORT_TABLE;
+ descs[d].desc_index = table_d;
+ if (rt->table_offset[i] > UINT32_MAX) {
+ wasm_error(c, wasm_loc(0, 0), "wasm: table import slot exceeds u32");
+ return;
+ }
+ descs[d].slot_offset = (uint32_t)rt->table_offset[i];
+ table_descs[table_d].elem_type = (uint32_t)t->elem_type;
+ table_descs[table_d].min = t->min;
+ table_descs[table_d].max = max;
+ table_descs[table_d].has_max = t->has_max ? 1u : 0u;
+ table_d++;
+ d++;
+ }
+ for (i = 0; i < m->nglobals; ++i) {
+ const WasmGlobal* g = &m->globals[i];
+ if (!g->is_import) continue;
+ descs[d].module_sym =
+ wasm_cg_intern_cstr(cg, b, g->import_module ? g->import_module : "");
+ descs[d].field_sym =
+ wasm_cg_intern_cstr(cg, b, g->import_name ? g->import_name : "");
+ descs[d].kind = KIT_WASM_IMPORT_GLOBAL;
+ descs[d].desc_index = global_d;
+ if (rt->global_offset[i] > UINT32_MAX) {
+ wasm_error(c, g->loc, "wasm: global import slot exceeds u32");
+ return;
+ }
+ descs[d].slot_offset = (uint32_t)rt->global_offset[i];
+ global_descs[global_d].type = (uint32_t)g->type;
+ global_descs[global_d].mutable_ = g->mutable_ ? 1u : 0u;
+ global_d++;
+ d++;
+ }
/* __kit_wasm_types: array of KitWasmTypeDesc.
* { const u8* params; u32 nparams; const u8* results; u32 nresults; }
- * Each typeidx referenced by an import gets one entry. Param/result bytes
- * are emitted as interned const u8 arrays using raw WasmValType bytes. */
- {
+ * Each distinct imported function signature gets one entry. Param/result
+ * bytes are emitted as interned const u8 arrays using raw WasmValType
+ * bytes. */
+ if (ntypes) {
+ KitCgSym nfunc_import_types_sym;
+ KitCgSym types_sym;
KitCgSym* param_syms = kit_arena_zarray(arena, KitCgSym, ntypes);
KitCgSym* result_syms = kit_arena_zarray(arena, KitCgSym, ntypes);
- uint32_t type_desc_size = 2u * ptr_size + 8u;
KitCgTypeId types_array_ty = kit_cg_type_array(
- c, u8_ty, (uint64_t)type_desc_size * (ntypes ? ntypes : 1u));
+ c, u8_ty, (uint64_t)type_desc_size * ntypes);
for (uint32_t k = 0; k < ntypes; ++k) {
const WasmFuncType* t = &m->types[local_to_module[k]];
uint8_t* pbuf =
@@ -1164,6 +1328,25 @@ static void wasm_cg_emit_host_import_metadata(KitCompiler* c, KitCg* cg,
memset(&decl, 0, sizeof decl);
decl.kind = KIT_CG_DECL_OBJECT;
decl.linkage_name = kit_cg_c_linkage_name(
+ c, kit_sym_intern(c, KIT_SLICE_LIT("__kit_wasm_nfunc_import_types")));
+ decl.display_name = decl.linkage_name;
+ decl.type = u32_ty;
+ decl.sym.bind = KIT_SB_GLOBAL;
+ decl.as.object.flags = KIT_CG_OBJ_READONLY;
+ nfunc_import_types_sym = kit_cg_decl(cg, decl);
+ if (!nfunc_import_types_sym)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: failed to declare __kit_wasm_nfunc_import_types");
+ memset(&data_attrs, 0, sizeof data_attrs);
+ data_attrs.flags = KIT_CG_DATADEF_READONLY;
+ data_attrs.align = 4;
+ kit_cg_data_begin(cg, nfunc_import_types_sym, data_attrs);
+ kit_cg_data_int(cg, (uint64_t)ntypes, u32_ty);
+ kit_cg_data_end(cg);
+
+ memset(&decl, 0, sizeof decl);
+ decl.kind = KIT_CG_DECL_OBJECT;
+ decl.linkage_name = kit_cg_c_linkage_name(
c, kit_sym_intern(c, KIT_SLICE_LIT("__kit_wasm_types")));
decl.display_name = decl.linkage_name;
decl.type = types_array_ty;
@@ -1196,6 +1379,93 @@ static void wasm_cg_emit_host_import_metadata(KitCompiler* c, KitCg* cg,
kit_cg_data_end(cg);
}
+ if (nmemory_imports) {
+ KitCgSym memory_types_sym;
+ KitCgTypeId memory_array_ty = kit_cg_type_array(
+ c, u8_ty, (uint64_t)sizeof(KitWasmMemoryImportDesc) * nmemory_imports);
+ memset(&decl, 0, sizeof decl);
+ decl.kind = KIT_CG_DECL_OBJECT;
+ decl.linkage_name = kit_cg_c_linkage_name(
+ c, kit_sym_intern(c,
+ KIT_SLICE_LIT("__kit_wasm_memory_import_types")));
+ decl.display_name = decl.linkage_name;
+ decl.type = memory_array_ty;
+ decl.sym.bind = KIT_SB_GLOBAL;
+ decl.as.object.flags = KIT_CG_OBJ_READONLY;
+ memory_types_sym = kit_cg_decl(cg, decl);
+ if (!memory_types_sym)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: failed to declare __kit_wasm_memory_import_types");
+ memset(&data_attrs, 0, sizeof data_attrs);
+ data_attrs.flags = KIT_CG_DATADEF_READONLY;
+ data_attrs.align = 8;
+ kit_cg_data_begin(cg, memory_types_sym, data_attrs);
+ for (uint32_t k = 0; k < nmemory_imports; ++k) {
+ kit_cg_data_align(cg, 8);
+ kit_cg_data_int(cg, memory_descs[k].min_pages, b.id[KIT_CG_BUILTIN_I64]);
+ kit_cg_data_int(cg, memory_descs[k].max_pages, b.id[KIT_CG_BUILTIN_I64]);
+ kit_cg_data_int(cg, memory_descs[k].flags, u32_ty);
+ kit_cg_data_int(cg, memory_descs[k].has_max, u32_ty);
+ }
+ kit_cg_data_end(cg);
+ }
+
+ if (ntable_imports) {
+ KitCgSym table_types_sym;
+ KitCgTypeId table_array_ty = kit_cg_type_array(
+ c, u8_ty, (uint64_t)sizeof(KitWasmTableImportDesc) * ntable_imports);
+ memset(&decl, 0, sizeof decl);
+ decl.kind = KIT_CG_DECL_OBJECT;
+ decl.linkage_name = kit_cg_c_linkage_name(
+ c, kit_sym_intern(c, KIT_SLICE_LIT("__kit_wasm_table_import_types")));
+ decl.display_name = decl.linkage_name;
+ decl.type = table_array_ty;
+ decl.sym.bind = KIT_SB_GLOBAL;
+ decl.as.object.flags = KIT_CG_OBJ_READONLY;
+ table_types_sym = kit_cg_decl(cg, decl);
+ if (!table_types_sym)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: failed to declare __kit_wasm_table_import_types");
+ memset(&data_attrs, 0, sizeof data_attrs);
+ data_attrs.flags = KIT_CG_DATADEF_READONLY;
+ data_attrs.align = 4;
+ kit_cg_data_begin(cg, table_types_sym, data_attrs);
+ for (uint32_t k = 0; k < ntable_imports; ++k) {
+ kit_cg_data_int(cg, table_descs[k].elem_type, u32_ty);
+ kit_cg_data_int(cg, table_descs[k].min, u32_ty);
+ kit_cg_data_int(cg, table_descs[k].max, u32_ty);
+ kit_cg_data_int(cg, table_descs[k].has_max, u32_ty);
+ }
+ kit_cg_data_end(cg);
+ }
+
+ if (nglobal_imports) {
+ KitCgSym global_types_sym;
+ KitCgTypeId global_array_ty = kit_cg_type_array(
+ c, u8_ty, (uint64_t)sizeof(KitWasmGlobalImportDesc) * nglobal_imports);
+ memset(&decl, 0, sizeof decl);
+ decl.kind = KIT_CG_DECL_OBJECT;
+ decl.linkage_name = kit_cg_c_linkage_name(
+ c, kit_sym_intern(c, KIT_SLICE_LIT("__kit_wasm_global_import_types")));
+ decl.display_name = decl.linkage_name;
+ decl.type = global_array_ty;
+ decl.sym.bind = KIT_SB_GLOBAL;
+ decl.as.object.flags = KIT_CG_OBJ_READONLY;
+ global_types_sym = kit_cg_decl(cg, decl);
+ if (!global_types_sym)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: failed to declare __kit_wasm_global_import_types");
+ memset(&data_attrs, 0, sizeof data_attrs);
+ data_attrs.flags = KIT_CG_DATADEF_READONLY;
+ data_attrs.align = 4;
+ kit_cg_data_begin(cg, global_types_sym, data_attrs);
+ for (uint32_t k = 0; k < nglobal_imports; ++k) {
+ kit_cg_data_int(cg, global_descs[k].type, u32_ty);
+ kit_cg_data_int(cg, global_descs[k].mutable_, u32_ty);
+ }
+ kit_cg_data_end(cg);
+ }
+
/* __kit_wasm_imports: array of KitWasmImportDesc. */
{
KitCgTypeId imports_array_ty =
@@ -1220,13 +1490,111 @@ static void wasm_cg_emit_host_import_metadata(KitCompiler* c, KitCg* cg,
kit_cg_data_align(cg, ptr_align);
kit_cg_data_addr(cg, descs[k].module_sym, 0, ptr_size, 0);
kit_cg_data_addr(cg, descs[k].field_sym, 0, ptr_size, 0);
- kit_cg_data_int(cg, (uint64_t)descs[k].local_typeidx, u32_ty);
+ kit_cg_data_int(cg, (uint64_t)descs[k].kind, u32_ty);
+ kit_cg_data_int(cg, (uint64_t)descs[k].desc_index, u32_ty);
kit_cg_data_int(cg, (uint64_t)descs[k].slot_offset, u32_ty);
+ kit_cg_data_int(cg, 0, u32_ty);
if (ptr_align > 4) kit_cg_data_align(cg, ptr_align);
}
kit_cg_data_end(cg);
}
- (void)types_sym;
+}
+
+/* Emit the runtime layout symbols read by kit_wasm_get_runtime_layout:
+ *
+ * __kit_wasm_instance_size uint64
+ * __kit_wasm_nmemories uint32
+ * __kit_wasm_memory_layouts KitWasmMemoryLayout[]
+ *
+ * The memory-layout array intentionally contains only fixed-width integers, so
+ * the host reader can consume it without target pointer-size ambiguity. */
+static void wasm_cg_emit_runtime_layout_metadata(KitCompiler* c, KitCg* cg,
+ KitCgBuiltinTypes b,
+ const WasmModule* m,
+ const WasmCgRuntime* rt) {
+ KitCgTypeId u8_ty = b.id[KIT_CG_BUILTIN_I8];
+ KitCgTypeId u32_ty = b.id[KIT_CG_BUILTIN_I32];
+ KitCgTypeId u64_ty = b.id[KIT_CG_BUILTIN_I64];
+ KitCgDecl decl;
+ KitCgDataDefAttrs data_attrs;
+ KitCgSym instance_size_sym;
+ KitCgSym nmemories_sym;
+ uint64_t instance_size = kit_cg_type_size(c, rt->instance_ty);
+
+ memset(&decl, 0, sizeof decl);
+ decl.kind = KIT_CG_DECL_OBJECT;
+ decl.linkage_name = kit_cg_c_linkage_name(
+ c, kit_sym_intern(c, KIT_SLICE_LIT("__kit_wasm_instance_size")));
+ decl.display_name = decl.linkage_name;
+ decl.type = u64_ty;
+ decl.sym.bind = KIT_SB_GLOBAL;
+ decl.as.object.flags = KIT_CG_OBJ_READONLY;
+ instance_size_sym = kit_cg_decl(cg, decl);
+ if (!instance_size_sym)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: failed to declare __kit_wasm_instance_size");
+ memset(&data_attrs, 0, sizeof data_attrs);
+ data_attrs.flags = KIT_CG_DATADEF_READONLY;
+ data_attrs.align = 8;
+ kit_cg_data_begin(cg, instance_size_sym, data_attrs);
+ kit_cg_data_int(cg, instance_size, u64_ty);
+ kit_cg_data_end(cg);
+
+ memset(&decl, 0, sizeof decl);
+ decl.kind = KIT_CG_DECL_OBJECT;
+ decl.linkage_name = kit_cg_c_linkage_name(
+ c, kit_sym_intern(c, KIT_SLICE_LIT("__kit_wasm_nmemories")));
+ decl.display_name = decl.linkage_name;
+ decl.type = u32_ty;
+ decl.sym.bind = KIT_SB_GLOBAL;
+ decl.as.object.flags = KIT_CG_OBJ_READONLY;
+ nmemories_sym = kit_cg_decl(cg, decl);
+ if (!nmemories_sym)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: failed to declare __kit_wasm_nmemories");
+ memset(&data_attrs, 0, sizeof data_attrs);
+ data_attrs.flags = KIT_CG_DATADEF_READONLY;
+ data_attrs.align = 4;
+ kit_cg_data_begin(cg, nmemories_sym, data_attrs);
+ kit_cg_data_int(cg, (uint64_t)m->nmemories, u32_ty);
+ kit_cg_data_end(cg);
+
+ if (m->nmemories) {
+ const uint64_t layout_size = sizeof(KitWasmMemoryLayout);
+ KitCgTypeId layouts_array_ty =
+ kit_cg_type_array(c, u8_ty, layout_size * m->nmemories);
+ KitCgSym layouts_sym;
+ memset(&decl, 0, sizeof decl);
+ decl.kind = KIT_CG_DECL_OBJECT;
+ decl.linkage_name = kit_cg_c_linkage_name(
+ c, kit_sym_intern(c, KIT_SLICE_LIT("__kit_wasm_memory_layouts")));
+ decl.display_name = decl.linkage_name;
+ decl.type = layouts_array_ty;
+ decl.sym.bind = KIT_SB_GLOBAL;
+ decl.as.object.flags = KIT_CG_OBJ_READONLY;
+ layouts_sym = kit_cg_decl(cg, decl);
+ if (!layouts_sym)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: failed to declare __kit_wasm_memory_layouts");
+ memset(&data_attrs, 0, sizeof data_attrs);
+ data_attrs.flags = KIT_CG_DATADEF_READONLY;
+ data_attrs.align = 8;
+ kit_cg_data_begin(cg, layouts_sym, data_attrs);
+ for (uint32_t i = 0; i < m->nmemories; ++i) {
+ const WasmMemory* mem = &m->memories[i];
+ uint64_t max_pages = mem->has_max ? mem->max_pages : mem->min_pages;
+ uint32_t flags =
+ (mem->shared ? KIT_WASM_MEMORY_SHARED : 0u) |
+ (mem->is64 ? KIT_WASM_MEMORY_64 : 0u);
+ kit_cg_data_align(cg, 8);
+ kit_cg_data_int(cg, rt->memory_offset[i], u64_ty);
+ kit_cg_data_int(cg, mem->min_pages, u64_ty);
+ kit_cg_data_int(cg, max_pages, u64_ty);
+ kit_cg_data_int(cg, flags, u32_ty);
+ kit_cg_data_int(cg, 0, u32_ty);
+ }
+ kit_cg_data_end(cg);
+ }
}
/* Bounds-check that addr + n <= size. addr/n/size are all treated as i64
@@ -1609,6 +1977,51 @@ static void wasm_cg_emit_table_copy_loop(
kit_cg_label_place(cg, done);
}
+static void wasm_cg_cache_funcref_entry(KitCompiler* c, KitCg* cg,
+ KitCgBuiltinTypes b,
+ const WasmCgRuntime* rt,
+ KitCgLocal ref_local,
+ KitCgLocal fn_local,
+ KitCgLocal typeidx_local,
+ KitCgMemAccess ref_mem,
+ KitCgMemAccess i32_mem) {
+ KitCgLabel is_null = kit_cg_label_new(cg);
+ KitCgLabel done = kit_cg_label_new(cg);
+ kit_cg_push_local(cg, ref_local);
+ kit_cg_load(cg, ref_mem);
+ kit_cg_push_null(cg, rt->void_ptr_ty);
+ kit_cg_int_cmp(cg, KIT_CG_INT_EQ);
+ kit_cg_branch_true(cg, is_null);
+
+ kit_cg_push_local(cg, fn_local);
+ kit_cg_push_local(cg, ref_local);
+ kit_cg_load(cg, ref_mem);
+ kit_cg_bitcast(cg, kit_cg_type_ptr(c, rt->table_entry_ty, 0));
+ kit_cg_deref(cg, 0);
+ kit_cg_field(cg, rt->table_entry_fn_field);
+ kit_cg_load(cg, ref_mem);
+ kit_cg_store(cg, ref_mem);
+
+ kit_cg_push_local(cg, typeidx_local);
+ kit_cg_push_local(cg, ref_local);
+ kit_cg_load(cg, ref_mem);
+ kit_cg_bitcast(cg, kit_cg_type_ptr(c, rt->table_entry_ty, 0));
+ kit_cg_deref(cg, 0);
+ kit_cg_field(cg, rt->table_entry_typeidx_field);
+ kit_cg_load(cg, i32_mem);
+ kit_cg_store(cg, i32_mem);
+ kit_cg_jump(cg, done);
+
+ kit_cg_label_place(cg, is_null);
+ kit_cg_push_local(cg, fn_local);
+ kit_cg_push_null(cg, rt->void_ptr_ty);
+ kit_cg_store(cg, ref_mem);
+ kit_cg_push_local(cg, typeidx_local);
+ kit_cg_push_int(cg, 0, b.id[KIT_CG_BUILTIN_I32]);
+ kit_cg_store(cg, i32_mem);
+ kit_cg_label_place(cg, done);
+}
+
void wasm_emit_cg(KitCompiler* c, const KitCodeOptions* code_opts,
KitObjBuilder* out, const WasmModule* m) {
KitCg* cg = NULL;
@@ -1943,10 +2356,12 @@ void wasm_emit_cg(KitCompiler* c, const KitCodeOptions* code_opts,
kit_cg_ret(cg);
kit_cg_func_end(cg);
}
- /* Host-import resolution metadata: emit __kit_wasm_nimports always, and
- * __kit_wasm_imports / __kit_wasm_types when the module declares at
- * least one function import. Read by kit_wasm_bind_host_imports. */
+ /* Host-import resolution metadata: emit __kit_wasm_nimports always, plus
+ * import descriptors and kind-specific type arrays when the module declares
+ * imports. Function import signatures are deduplicated in __kit_wasm_types.
+ * Read by kit_wasm_bind_host_imports. */
wasm_cg_emit_host_import_metadata(c, cg, b, m, &rt, arena);
+ wasm_cg_emit_runtime_layout_metadata(c, cg, b, m, &rt);
for (i = 0; i < m->nfuncs; ++i) {
const WasmFunc* f = &m->funcs[i];
typedef struct WasmCgControl {
@@ -2750,27 +3165,14 @@ void wasm_emit_cg(KitCompiler* c, const KitCodeOptions* code_opts,
kit_cg_push_local(cg, val_l);
kit_cg_swap(cg);
kit_cg_store(cg, fn_mem);
- /* Cache *val (a KitWasmTableEntry) into fn/typeidx locals so the
- * fill loop writes the correct pair into each new slot. */
+ /* Cache val into fn/typeidx locals so the fill loop writes the
+ * correct pair into each new slot. A null ref grows with null slots.
+ */
KitCgLocal val_fn_l = kit_cg_local(cg, rt.void_ptr_ty, attrs);
KitCgLocal val_typeidx_l =
kit_cg_local(cg, b.id[KIT_CG_BUILTIN_I32], attrs);
- kit_cg_push_local(cg, val_fn_l);
- kit_cg_push_local(cg, val_l);
- kit_cg_load(cg, fn_mem);
- kit_cg_bitcast(cg, kit_cg_type_ptr(c, rt.table_entry_ty, 0));
- kit_cg_deref(cg, 0);
- kit_cg_field(cg, rt.table_entry_fn_field);
- kit_cg_load(cg, fn_mem);
- kit_cg_store(cg, fn_mem);
- kit_cg_push_local(cg, val_typeidx_l);
- kit_cg_push_local(cg, val_l);
- kit_cg_load(cg, fn_mem);
- kit_cg_bitcast(cg, kit_cg_type_ptr(c, rt.table_entry_ty, 0));
- kit_cg_deref(cg, 0);
- kit_cg_field(cg, rt.table_entry_typeidx_field);
- kit_cg_load(cg, i32_mem);
- kit_cg_store(cg, i32_mem);
+ wasm_cg_cache_funcref_entry(c, cg, b, &rt, val_l, val_fn_l,
+ val_typeidx_l, fn_mem, i32_mem);
kit_cg_push_local(cg, old_len_l);
wasm_cg_push_table_lvalue(cg, &rt, instance_local, tableidx);
kit_cg_field(cg, rt.table_len_field);
@@ -2889,25 +3291,10 @@ void wasm_emit_cg(KitCompiler* c, const KitCodeOptions* code_opts,
kit_cg_push_local(cg, dst_idx_l);
kit_cg_swap(cg);
kit_cg_store(cg, i32_mem);
- /* Cache *val into (val_fn, val_typeidx). val points at a
- * KitWasmTableEntry which has fn (void*) at offset 0 and
- * typeidx (i32) at offset sizeof(void*). */
- kit_cg_push_local(cg, val_fn_l);
- kit_cg_push_local(cg, val_l);
- kit_cg_load(cg, fn_mem);
- kit_cg_bitcast(cg, kit_cg_type_ptr(c, rt.table_entry_ty, 0));
- kit_cg_deref(cg, 0);
- kit_cg_field(cg, rt.table_entry_fn_field);
- kit_cg_load(cg, fn_mem);
- kit_cg_store(cg, fn_mem);
- kit_cg_push_local(cg, val_typeidx_l);
- kit_cg_push_local(cg, val_l);
- kit_cg_load(cg, fn_mem);
- kit_cg_bitcast(cg, kit_cg_type_ptr(c, rt.table_entry_ty, 0));
- kit_cg_deref(cg, 0);
- kit_cg_field(cg, rt.table_entry_typeidx_field);
- kit_cg_load(cg, i32_mem);
- kit_cg_store(cg, i32_mem);
+ /* Cache val into (val_fn, val_typeidx). Null refs fill with null
+ * entries and are checked later by call_indirect/call_ref. */
+ wasm_cg_cache_funcref_entry(c, cg, b, &rt, val_l, val_fn_l,
+ val_typeidx_l, fn_mem, i32_mem);
/* Bounds check: dst_idx + n <= len. */
kit_cg_push_local(cg, len_l);
wasm_cg_push_table_lvalue(cg, &rt, instance_local, tableidx);
diff --git a/lang/wasm/host_imports.c b/lang/wasm/host_imports.c
@@ -84,6 +84,31 @@ static int host_imports_build_type(const KitWasmTypeDesc* src,
* than silently truncating. */
#define KIT_WASM_BIND_MAX_VALTYPES 32u
+KIT_API KitStatus kit_wasm_get_runtime_layout(KitJit* jit,
+ KitWasmRuntimeLayout* out) {
+ const uint64_t* instance_size;
+ const uint32_t* nmemories;
+ const KitWasmMemoryLayout* memories = NULL;
+ KitWasmRuntimeLayout z = {0};
+ if (!jit || !out) return KIT_INVALID;
+ instance_size = (const uint64_t*)kit_jit_lookup(
+ jit, KIT_SLICE_LIT("__kit_wasm_instance_size"));
+ nmemories = (const uint32_t*)kit_jit_lookup(
+ jit, KIT_SLICE_LIT("__kit_wasm_nmemories"));
+ if (!instance_size && !nmemories) return KIT_NOT_FOUND;
+ if (!instance_size || !nmemories) return KIT_MALFORMED;
+ if (*nmemories) {
+ memories = (const KitWasmMemoryLayout*)kit_jit_lookup(
+ jit, KIT_SLICE_LIT("__kit_wasm_memory_layouts"));
+ if (!memories) return KIT_MALFORMED;
+ }
+ z.instance_size = *instance_size;
+ z.memories = memories;
+ z.nmemories = *nmemories;
+ *out = z;
+ return KIT_OK;
+}
+
KIT_API KitStatus kit_wasm_bind_host_imports(KitCompiler* compiler, KitJit* jit,
KitWasmInstance* inst,
const KitWasmHostImport* imports,
@@ -91,10 +116,19 @@ KIT_API KitStatus kit_wasm_bind_host_imports(KitCompiler* compiler, KitJit* jit,
KitWasmResolveFn resolve,
void* user) {
const uint32_t* nimports_meta;
+ const uint32_t* nfunc_import_types_meta = NULL;
const KitWasmImportDesc* import_descs;
- const KitWasmTypeDesc* type_descs;
+ const KitWasmTypeDesc* type_descs = NULL;
+ const KitWasmMemoryImportDesc* memory_descs = NULL;
+ const KitWasmTableImportDesc* table_descs = NULL;
+ const KitWasmGlobalImportDesc* global_descs = NULL;
uint32_t n;
uint32_t i;
+ uint32_t nfunc_descs = 0;
+ uint32_t nmemory_descs = 0;
+ uint32_t ntable_descs = 0;
+ uint32_t nglobal_descs = 0;
+ uint32_t nfunc_import_types = 0;
(void)compiler;
if (!jit || !inst) return KIT_INVALID;
@@ -109,9 +143,36 @@ KIT_API KitStatus kit_wasm_bind_host_imports(KitCompiler* compiler, KitJit* jit,
if (n == 0) return KIT_OK;
import_descs = (const KitWasmImportDesc*)kit_jit_lookup(
jit, KIT_SLICE_LIT("__kit_wasm_imports"));
- type_descs = (const KitWasmTypeDesc*)kit_jit_lookup(
- jit, KIT_SLICE_LIT("__kit_wasm_types"));
- if (!import_descs || !type_descs) return KIT_MALFORMED;
+ if (!import_descs) return KIT_MALFORMED;
+
+ for (i = 0; i < n; ++i) {
+ switch (import_descs[i].kind) {
+ case KIT_WASM_IMPORT_FUNC:
+ nfunc_descs++;
+ break;
+ case KIT_WASM_IMPORT_MEMORY:
+ nmemory_descs++;
+ break;
+ case KIT_WASM_IMPORT_TABLE:
+ ntable_descs++;
+ break;
+ case KIT_WASM_IMPORT_GLOBAL:
+ nglobal_descs++;
+ break;
+ default:
+ return KIT_MALFORMED;
+ }
+ }
+ if (nfunc_descs) {
+ nfunc_import_types_meta = (const uint32_t*)kit_jit_lookup(
+ jit, KIT_SLICE_LIT("__kit_wasm_nfunc_import_types"));
+ type_descs = (const KitWasmTypeDesc*)kit_jit_lookup(
+ jit, KIT_SLICE_LIT("__kit_wasm_types"));
+ if (!nfunc_import_types_meta || !type_descs) return KIT_MALFORMED;
+ nfunc_import_types = *nfunc_import_types_meta;
+ if (nfunc_import_types == 0 || nfunc_import_types > nfunc_descs)
+ return KIT_MALFORMED;
+ }
for (i = 0; i < n; ++i) {
const KitWasmImportDesc* d = &import_descs[i];
@@ -119,8 +180,36 @@ KIT_API KitStatus kit_wasm_bind_host_imports(KitCompiler* compiler, KitJit* jit,
KitWasmValType pbuf[KIT_WASM_BIND_MAX_VALTYPES];
KitWasmValType rbuf[KIT_WASM_BIND_MAX_VALTYPES];
KitWasmImportType type;
+ switch (d->kind) {
+ case KIT_WASM_IMPORT_FUNC:
+ if (d->desc_index >= nfunc_import_types) return KIT_MALFORMED;
+ break;
+ case KIT_WASM_IMPORT_MEMORY:
+ if (d->desc_index >= nmemory_descs) return KIT_MALFORMED;
+ if (!memory_descs)
+ memory_descs = (const KitWasmMemoryImportDesc*)kit_jit_lookup(
+ jit, KIT_SLICE_LIT("__kit_wasm_memory_import_types"));
+ if (!memory_descs) return KIT_MALFORMED;
+ return KIT_UNSUPPORTED;
+ case KIT_WASM_IMPORT_TABLE:
+ if (d->desc_index >= ntable_descs) return KIT_MALFORMED;
+ if (!table_descs)
+ table_descs = (const KitWasmTableImportDesc*)kit_jit_lookup(
+ jit, KIT_SLICE_LIT("__kit_wasm_table_import_types"));
+ if (!table_descs) return KIT_MALFORMED;
+ return KIT_UNSUPPORTED;
+ case KIT_WASM_IMPORT_GLOBAL:
+ if (d->desc_index >= nglobal_descs) return KIT_MALFORMED;
+ if (!global_descs)
+ global_descs = (const KitWasmGlobalImportDesc*)kit_jit_lookup(
+ jit, KIT_SLICE_LIT("__kit_wasm_global_import_types"));
+ if (!global_descs) return KIT_MALFORMED;
+ return KIT_UNSUPPORTED;
+ default:
+ return KIT_MALFORMED;
+ }
/* Static table first. */
- for (size_t k = 0; k < nimports; ++k) {
+ for (size_t k = 0; imports && k < nimports; ++k) {
if (host_imports_streq(imports[k].module, d->module) &&
host_imports_streq(imports[k].field, d->field)) {
fn = imports[k].func;
@@ -129,13 +218,14 @@ KIT_API KitStatus kit_wasm_bind_host_imports(KitCompiler* compiler, KitJit* jit,
}
/* Resolver fallback. */
if (!fn && resolve) {
- const KitWasmTypeDesc* td = &type_descs[d->typeidx];
+ const KitWasmTypeDesc* td;
+ td = &type_descs[d->desc_index];
if (!host_imports_build_type(td, pbuf, KIT_WASM_BIND_MAX_VALTYPES, rbuf,
KIT_WASM_BIND_MAX_VALTYPES, &type))
return KIT_MALFORMED;
fn = resolve(user, d->module, d->field, &type);
}
- if (!fn) continue; /* Unresolved: leave slot null; runtime traps on call. */
+ if (!fn) return KIT_NOT_FOUND;
/* Slot is a void* at byte offset slot_offset inside the instance struct.
* The KitWasmFuncImport record is { void* fn; } so the offset of the
* field is also the offset of the fn pointer. */
diff --git a/lang/wasm/runtime_abi.h b/lang/wasm/runtime_abi.h
@@ -10,10 +10,40 @@ typedef struct KitWasmMemory {
uint32_t flags;
} KitWasmMemory;
+#ifndef KIT_WASM_MEMORY_FLAG_TYPES
+#define KIT_WASM_MEMORY_FLAG_TYPES
enum {
KIT_WASM_MEMORY_SHARED = 1u << 0,
KIT_WASM_MEMORY_64 = 1u << 1,
};
+#endif
+
+#ifndef KIT_WASM_IMPORT_KIND_TYPES
+#define KIT_WASM_IMPORT_KIND_TYPES
+typedef enum KitWasmImportKind {
+ KIT_WASM_IMPORT_FUNC = 0,
+ KIT_WASM_IMPORT_TABLE = 1,
+ KIT_WASM_IMPORT_MEMORY = 2,
+ KIT_WASM_IMPORT_GLOBAL = 3,
+} KitWasmImportKind;
+#endif
+
+#ifndef KIT_WASM_RUNTIME_LAYOUT_TYPES
+#define KIT_WASM_RUNTIME_LAYOUT_TYPES
+typedef struct KitWasmMemoryLayout {
+ uint64_t offset;
+ uint64_t min_pages;
+ uint64_t max_pages;
+ uint32_t flags;
+ uint32_t reserved;
+} KitWasmMemoryLayout;
+
+typedef struct KitWasmRuntimeLayout {
+ uint64_t instance_size;
+ const KitWasmMemoryLayout* memories;
+ uint32_t nmemories;
+} KitWasmRuntimeLayout;
+#endif
typedef struct KitWasmFuncImport {
void* fn;
@@ -62,19 +92,25 @@ typedef void (*KitWasmInitFn)(KitWasmInstance*);
/* ---- Host-import metadata (subagent C) ----
*
- * Each module that the wasm frontend lowers emits three readonly symbols
- * alongside __kit_wasm_init so a runtime can resolve imports by
- * (module, field) without needing the source WasmModule struct:
+ * Each module that the wasm frontend lowers emits readonly symbols alongside
+ * __kit_wasm_init so a runtime can resolve imports by (module, field) without
+ * needing the source WasmModule struct:
*
* __kit_wasm_imports : KitWasmImportDesc[] (one per declared
- * function import; if
- * the module has none,
- * the symbol is absent)
+ * import; if the module has
+ * none, the symbol is
+ * absent)
* __kit_wasm_nimports : uint32_t (array length)
- * __kit_wasm_types : KitWasmTypeDesc[] (every WasmFuncType
- * referenced by an
- * import; absent when
- * nimports == 0)
+ * __kit_wasm_nfunc_import_types : uint32_t (unique function import
+ * signature count)
+ * __kit_wasm_types : KitWasmTypeDesc[] (unique WasmFuncType
+ * signatures referenced by
+ * imported functions;
+ * absent when no functions
+ * are imported)
+ * __kit_wasm_memory_import_types : KitWasmMemoryImportDesc[]
+ * __kit_wasm_table_import_types : KitWasmTableImportDesc[]
+ * __kit_wasm_global_import_types : KitWasmGlobalImportDesc[]
*
* The wire format below is the C ABI the JIT/AOT image exposes. Param/result
* bytes use the raw wasm WasmValType encoding (I32=0x7f, I64=0x7e, F32=0x7d,
@@ -82,11 +118,12 @@ typedef void (*KitWasmInitFn)(KitWasmInstance*);
* the public KitWasmValType enum when invoking host resolvers. */
typedef struct KitWasmImportDesc {
- const char* module; /* image-owned, NUL-terminated */
- const char* field; /* image-owned, NUL-terminated */
- uint32_t typeidx; /* index into __kit_wasm_types */
- uint32_t slot_offset; /* byte offset of the void* slot inside the
- KitWasmInstance for this import */
+ const char* module; /* image-owned, NUL-terminated */
+ const char* field; /* image-owned, NUL-terminated */
+ uint32_t kind; /* KitWasmImportKind */
+ uint32_t desc_index; /* index into the kind-specific descriptor array */
+ uint32_t slot_offset; /* byte offset of the instance slot for this import */
+ uint32_t reserved;
} KitWasmImportDesc;
typedef struct KitWasmTypeDesc {
@@ -96,4 +133,23 @@ typedef struct KitWasmTypeDesc {
uint32_t nresults;
} KitWasmTypeDesc;
+typedef struct KitWasmMemoryImportDesc {
+ uint64_t min_pages;
+ uint64_t max_pages;
+ uint32_t flags; /* KIT_WASM_MEMORY_* */
+ uint32_t has_max; /* nonzero when the import declared an explicit maximum */
+} KitWasmMemoryImportDesc;
+
+typedef struct KitWasmTableImportDesc {
+ uint32_t elem_type; /* raw WasmValType byte */
+ uint32_t min;
+ uint32_t max;
+ uint32_t has_max; /* nonzero when the import declared an explicit maximum */
+} KitWasmTableImportDesc;
+
+typedef struct KitWasmGlobalImportDesc {
+ uint32_t type; /* raw WasmValType byte */
+ uint32_t mutable_;
+} KitWasmGlobalImportDesc;
+
#endif
diff --git a/src/wasm/decode.c b/src/wasm/decode.c
@@ -1054,7 +1054,6 @@ void wasm_decode_binary(KitCompiler* c, const KitSlice* input,
}
if (id == 1) {
uint32_t i, count = bin_uleb(&r);
- if (count > 64u) wasm_error(c, wasm_loc(0, 0), "wasm: too many types");
for (i = 0; i < count; ++i) {
WasmFuncType* t = wasm_add_type(c, out);
uint32_t j, nparam, nresult;
@@ -1135,8 +1134,6 @@ void wasm_decode_binary(KitCompiler* c, const KitSlice* input,
}
} else if (id == 3) {
uint32_t i, count = bin_uleb(&r);
- if (count > 64u)
- wasm_error(c, wasm_loc(0, 0), "wasm: too many functions");
for (i = 0; i < count; ++i) {
uint32_t typeidx = bin_uleb(&r);
WasmFunc* f;
diff --git a/src/wasm/validate.c b/src/wasm/validate.c
@@ -28,6 +28,29 @@ static WasmValType wasm_global_init_type(const WasmInsn* in) {
}
}
+static uint64_t wasm_memory_initial_bytes(KitCompiler* c,
+ const WasmMemory* mem) {
+ if (mem->min_pages > UINT64_MAX / 65536u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: memory minimum overflows");
+ return mem->min_pages * 65536u;
+}
+
+static uint32_t wasm_mem_align_log2(uint32_t width) {
+ uint32_t lg = 0;
+ while (width > 1u) {
+ width >>= 1u;
+ lg++;
+ }
+ return lg;
+}
+
+static void wasm_validate_memarg(KitCompiler* c, const WasmInsn* in,
+ const char* what) {
+ uint32_t max_align = wasm_mem_align_log2(wasm_mem_width(in->kind));
+ if (in->align > max_align)
+ wasm_error(c, in->loc, "wasm: bad %s alignment", what);
+}
+
static void wasm_stack_push(KitCompiler* c, WasmValStack* s, WasmValType vt) {
if (s->depth >= 256u) wasm_error(c, s->loc, "wasm: operand stack too deep");
s->vals[s->depth++] = vt;
@@ -138,14 +161,37 @@ void wasm_validate(WasmModule* m, KitCompiler* c) {
wasm_error(c, wasm_loc(0, 0),
"wasm: start function must have no params or results");
}
+ for (i = 0; i < m->ndata; ++i) {
+ const WasmDataSegment* d = &m->data[i];
+ uint64_t memory_bytes;
+ uint64_t offset;
+ if (d->mode != WASM_SEG_ACTIVE) continue;
+ if (d->memidx >= m->nmemories)
+ wasm_error(c, wasm_loc(0, 0), "wasm: data memory index out of range");
+ if (d->offset < 0)
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad data offset");
+ memory_bytes = wasm_memory_initial_bytes(c, &m->memories[d->memidx]);
+ offset = (uint64_t)d->offset;
+ if (offset > memory_bytes || d->nbytes > memory_bytes - offset)
+ wasm_error(c, wasm_loc(0, 0), "wasm: data segment out of range");
+ }
for (i = 0; i < m->nelems; ++i) {
- uint32_t table_min;
- if (m->elems[i].tableidx >= m->ntables)
- wasm_error(c, wasm_loc(0, 0), "wasm: element table index out of range");
- table_min = m->tables[m->elems[i].tableidx].min;
- if (m->elems[i].offset < 0 ||
- (uint64_t)m->elems[i].offset + m->elems[i].nfuncs > table_min)
- wasm_error(c, wasm_loc(0, 0), "wasm: element segment out of range");
+ if (m->elems[i].elem_type != WASM_VAL_FUNCREF)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: unsupported element segment type");
+ if (m->elems[i].mode == WASM_SEG_ACTIVE) {
+ uint32_t table_min;
+ uint64_t offset;
+ if (m->elems[i].tableidx >= m->ntables)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: element table index out of range");
+ table_min = m->tables[m->elems[i].tableidx].min;
+ if (m->elems[i].offset < 0)
+ wasm_error(c, wasm_loc(0, 0), "wasm: element segment out of range");
+ offset = (uint64_t)m->elems[i].offset;
+ if (offset > table_min || m->elems[i].nfuncs > table_min - offset)
+ wasm_error(c, wasm_loc(0, 0), "wasm: element segment out of range");
+ }
for (j = 0; j < m->elems[i].nfuncs; ++j)
if (m->elems[i].funcs[j] >= m->nfuncs)
wasm_error(c, wasm_loc(0, 0),
@@ -479,8 +525,7 @@ void wasm_validate_func(KitCompiler* c, WasmModule* m, WasmFunc* f) {
if (!m->memories[in->memidx].shared)
wasm_error(c, wasm_loc(0, 0),
"wasm: atomic load requires shared memory");
- if (in->align && in->align > wasm_mem_width(in->kind))
- wasm_error(c, wasm_loc(0, 0), "wasm: bad atomic alignment");
+ wasm_validate_memarg(c, in, "atomic");
wasm_stack_pop(
c, &stack, control, ncontrol,
m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32,
@@ -501,8 +546,7 @@ void wasm_validate_func(KitCompiler* c, WasmModule* m, WasmFunc* f) {
if (!m->memories[in->memidx].shared)
wasm_error(c, wasm_loc(0, 0),
"wasm: atomic store requires shared memory");
- if (in->align && in->align > wasm_mem_width(in->kind))
- wasm_error(c, wasm_loc(0, 0), "wasm: bad atomic alignment");
+ wasm_validate_memarg(c, in, "atomic");
wasm_stack_pop(c, &stack, control, ncontrol,
wasm_atomic_value_type(in->kind), "atomic store");
wasm_stack_pop(
@@ -529,6 +573,7 @@ void wasm_validate_func(KitCompiler* c, WasmModule* m, WasmFunc* f) {
if (!m->memories[in->memidx].shared)
wasm_error(c, wasm_loc(0, 0),
"wasm: atomic rmw requires shared memory");
+ wasm_validate_memarg(c, in, "atomic");
wasm_stack_pop(c, &stack, control, ncontrol,
wasm_atomic_value_type(in->kind), "atomic rmw");
wasm_stack_pop(
@@ -547,6 +592,7 @@ void wasm_validate_func(KitCompiler* c, WasmModule* m, WasmFunc* f) {
if (!m->memories[in->memidx].shared)
wasm_error(c, wasm_loc(0, 0),
"wasm: atomic cmpxchg requires shared memory");
+ wasm_validate_memarg(c, in, "atomic");
wasm_stack_pop(c, &stack, control, ncontrol,
wasm_atomic_value_type(in->kind), "atomic cmpxchg");
wasm_stack_pop(c, &stack, control, ncontrol,
@@ -566,6 +612,7 @@ void wasm_validate_func(KitCompiler* c, WasmModule* m, WasmFunc* f) {
if (!m->memories[in->memidx].shared)
wasm_error(c, wasm_loc(0, 0),
"wasm: atomic wait requires shared memory");
+ wasm_validate_memarg(c, in, "atomic");
wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I64,
"atomic wait timeout");
wasm_stack_pop(c, &stack, control, ncontrol,
@@ -585,6 +632,7 @@ void wasm_validate_func(KitCompiler* c, WasmModule* m, WasmFunc* f) {
if (!m->memories[in->memidx].shared)
wasm_error(c, wasm_loc(0, 0),
"wasm: atomic notify requires shared memory");
+ wasm_validate_memarg(c, in, "atomic");
wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32,
"atomic notify count");
wasm_stack_pop(
@@ -609,6 +657,7 @@ void wasm_validate_func(KitCompiler* c, WasmModule* m, WasmFunc* f) {
case WASM_INSN_I64_LOAD32_U:
if (in->memidx >= m->nmemories)
wasm_error(c, wasm_loc(0, 0), "wasm: load without memory");
+ wasm_validate_memarg(c, in, "load");
wasm_stack_pop(
c, &stack, control, ncontrol,
m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32,
@@ -626,6 +675,7 @@ void wasm_validate_func(KitCompiler* c, WasmModule* m, WasmFunc* f) {
case WASM_INSN_I64_STORE32:
if (in->memidx >= m->nmemories)
wasm_error(c, wasm_loc(0, 0), "wasm: store without memory");
+ wasm_validate_memarg(c, in, "store");
wasm_stack_pop(c, &stack, control, ncontrol,
wasm_store_value_type(in->kind), "store");
wasm_stack_pop(
@@ -675,6 +725,9 @@ void wasm_validate_func(KitCompiler* c, WasmModule* m, WasmFunc* f) {
if (in->imm < 0 || (uint64_t)in->imm >= m->ndata)
wasm_error(c, wasm_loc(0, 0),
"wasm: memory.init data index out of range");
+ if (m->data[in->imm].mode != WASM_SEG_PASSIVE)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: memory.init requires passive data segment");
if (in->memidx >= m->nmemories)
wasm_error(c, wasm_loc(0, 0),
"wasm: memory.init memory index out of range");
@@ -692,6 +745,9 @@ void wasm_validate_func(KitCompiler* c, WasmModule* m, WasmFunc* f) {
if (in->imm < 0 || (uint64_t)in->imm >= m->ndata)
wasm_error(c, wasm_loc(0, 0),
"wasm: data.drop data index out of range");
+ if (m->data[in->imm].mode != WASM_SEG_PASSIVE)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: data.drop requires passive data segment");
break;
case WASM_INSN_TABLE_COPY:
wasm_require_feature(c, m, WASM_FEATURE_BULK_MEMORY, "bulk memory",
@@ -713,6 +769,9 @@ void wasm_validate_func(KitCompiler* c, WasmModule* m, WasmFunc* f) {
if (in->imm < 0 || (uint64_t)in->imm >= m->nelems)
wasm_error(c, wasm_loc(0, 0),
"wasm: table.init elem index out of range");
+ if (m->elems[in->imm].mode != WASM_SEG_PASSIVE)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: table.init requires passive element segment");
if (in->aux_idx >= m->ntables)
wasm_error(c, wasm_loc(0, 0),
"wasm: table.init table index out of range");
@@ -729,6 +788,9 @@ void wasm_validate_func(KitCompiler* c, WasmModule* m, WasmFunc* f) {
if (in->imm < 0 || (uint64_t)in->imm >= m->nelems)
wasm_error(c, wasm_loc(0, 0),
"wasm: elem.drop elem index out of range");
+ if (m->elems[in->imm].mode != WASM_SEG_PASSIVE)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: elem.drop requires passive element segment");
break;
case WASM_INSN_TABLE_SIZE:
wasm_require_feature(c, m, WASM_FEATURE_BULK_MEMORY, "bulk memory",
diff --git a/src/wasm/wat.c b/src/wasm/wat.c
@@ -492,6 +492,19 @@ static int wat_parse_u32_atom(WasmTok t, uint32_t* out) {
return 1;
}
+static int wat_parse_mem_align_log2(WasmTok t, uint32_t* out) {
+ uint32_t bytes;
+ uint32_t lg = 0;
+ if (!wat_parse_u32_atom(t, &bytes) || bytes == 0u) return 0;
+ if (bytes & (bytes - 1u)) return 0;
+ while (bytes > 1u) {
+ bytes >>= 1u;
+ lg++;
+ }
+ *out = lg;
+ return 1;
+}
+
static int wat_parse_u64_atom(WasmTok t, uint64_t* out) {
uint64_t v = 0;
size_t i = 0;
@@ -542,7 +555,7 @@ static void wat_parse_mem_attrs(WatParser* p, uint32_t* align, uint64_t* offset,
val = p->tok;
val.p += 6;
val.len -= 6;
- if (!wat_parse_u32_atom(val, align))
+ if (!wat_parse_mem_align_log2(val, align))
wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
"wasm wat: bad memory alignment");
wat_next(p);
diff --git a/test/link/harness/jit_runner.c b/test/link/harness/jit_runner.c
@@ -423,9 +423,157 @@ typedef struct WasmRunnerMemoryPrefix {
uint32_t flags;
} WasmRunnerMemoryPrefix;
+typedef struct WasmRunnerInstanceAlloc {
+ uint8_t* instance;
+ uint8_t** memories;
+ uint32_t nmemories;
+} WasmRunnerInstanceAlloc;
+
typedef void (*WasmRunnerInitFn)(void*);
typedef int (*WasmRunnerMainFn)(void*);
+#define WASM_RUNNER_MAX_INSTANCE_BYTES (64ull * 1024ull * 1024ull)
+#define WASM_RUNNER_MAX_TOTAL_MEMORY_BYTES (1024ull * 1024ull * 1024ull)
+
+static int wasm_runner_u64_to_size(uint64_t n, size_t* out,
+ const char* what) {
+ if (n > (uint64_t)SIZE_MAX) {
+ fprintf(stderr, "jit-runner: wasm %s exceeds host size_t\n", what);
+ return 1;
+ }
+ *out = (size_t)n;
+ return 0;
+}
+
+static int wasm_runner_page_bytes(uint64_t pages, uint64_t* out) {
+ if (pages > UINT64_MAX / (uint64_t)KIT_WASM_PAGE_SIZE) {
+ fprintf(stderr, "jit-runner: wasm memory size overflows\n");
+ return 1;
+ }
+ *out = pages * (uint64_t)KIT_WASM_PAGE_SIZE;
+ return 0;
+}
+
+static void wasm_runner_free_instance(WasmRunnerInstanceAlloc* alloc) {
+ if (!alloc) return;
+ if (alloc->memories) {
+ for (uint32_t i = 0; i < alloc->nmemories; ++i) free(alloc->memories[i]);
+ free(alloc->memories);
+ }
+ free(alloc->instance);
+ memset(alloc, 0, sizeof *alloc);
+}
+
+static int wasm_runner_make_instance(KitJit* jit,
+ WasmRunnerInstanceAlloc* out) {
+ KitWasmRuntimeLayout layout;
+ WasmRunnerInstanceAlloc alloc;
+ uint64_t instance_bytes;
+ uint64_t total_memory_bytes = 0;
+ size_t instance_size;
+ KitStatus st;
+ memset(&alloc, 0, sizeof alloc);
+ if (!out) return 1;
+ memset(out, 0, sizeof *out);
+ st = kit_wasm_get_runtime_layout(jit, &layout);
+ if (st != KIT_OK) {
+ fprintf(stderr, "jit-runner: wasm runtime layout metadata %s\n",
+ st == KIT_NOT_FOUND ? "missing" : "malformed");
+ return 1;
+ }
+ instance_bytes = layout.instance_size ? layout.instance_size : 1u;
+ if (instance_bytes > WASM_RUNNER_MAX_INSTANCE_BYTES) {
+ fprintf(stderr, "jit-runner: wasm instance too large: %llu bytes\n",
+ (unsigned long long)instance_bytes);
+ return 1;
+ }
+ if (wasm_runner_u64_to_size(instance_bytes, &instance_size, "instance") != 0)
+ return 1;
+ alloc.instance = (uint8_t*)calloc(1, instance_size);
+ alloc.nmemories = layout.nmemories;
+ if (!alloc.instance) {
+ fprintf(stderr, "jit-runner: out of memory\n");
+ return 1;
+ }
+ if (layout.nmemories) {
+ if ((uint64_t)layout.nmemories > (uint64_t)SIZE_MAX / sizeof(uint8_t*)) {
+ fprintf(stderr, "jit-runner: wasm memory count too large\n");
+ wasm_runner_free_instance(&alloc);
+ return 1;
+ }
+ alloc.memories = (uint8_t**)calloc(layout.nmemories, sizeof(uint8_t*));
+ if (!alloc.memories) {
+ fprintf(stderr, "jit-runner: out of memory\n");
+ wasm_runner_free_instance(&alloc);
+ return 1;
+ }
+ }
+ for (uint32_t i = 0; i < layout.nmemories; ++i) {
+ const KitWasmMemoryLayout* ml = &layout.memories[i];
+ uint64_t mem_bytes;
+ size_t mem_size;
+ WasmRunnerMemoryPrefix* rec;
+ if (ml->max_pages < ml->min_pages) {
+ fprintf(stderr, "jit-runner: wasm memory maximum below minimum\n");
+ wasm_runner_free_instance(&alloc);
+ return 1;
+ }
+ if (ml->offset > instance_bytes ||
+ instance_bytes - ml->offset < sizeof(WasmRunnerMemoryPrefix)) {
+ fprintf(stderr, "jit-runner: wasm memory record outside instance\n");
+ wasm_runner_free_instance(&alloc);
+ return 1;
+ }
+ if (wasm_runner_page_bytes(ml->max_pages, &mem_bytes) != 0) {
+ wasm_runner_free_instance(&alloc);
+ return 1;
+ }
+ if (mem_bytes > WASM_RUNNER_MAX_TOTAL_MEMORY_BYTES ||
+ total_memory_bytes > WASM_RUNNER_MAX_TOTAL_MEMORY_BYTES - mem_bytes) {
+ fprintf(stderr,
+ "jit-runner: wasm linear memory reservation exceeds %llu bytes\n",
+ (unsigned long long)WASM_RUNNER_MAX_TOTAL_MEMORY_BYTES);
+ wasm_runner_free_instance(&alloc);
+ return 1;
+ }
+ total_memory_bytes += mem_bytes;
+ if (wasm_runner_u64_to_size(mem_bytes, &mem_size, "linear memory") != 0) {
+ wasm_runner_free_instance(&alloc);
+ return 1;
+ }
+ if (mem_size) {
+ alloc.memories[i] = (uint8_t*)calloc(1, mem_size);
+ if (!alloc.memories[i]) {
+ fprintf(stderr, "jit-runner: out of memory\n");
+ wasm_runner_free_instance(&alloc);
+ return 1;
+ }
+ }
+ rec = (WasmRunnerMemoryPrefix*)(alloc.instance + ml->offset);
+ rec->data = alloc.memories[i];
+ }
+ *out = alloc;
+ return 0;
+}
+
+static int wasm_runner_host_add_type(const KitWasmImportType* type) {
+ return type && type->nparams == 2u && type->nresults == 1u &&
+ type->params[0] == KIT_WASM_VAL_I32 &&
+ type->params[1] == KIT_WASM_VAL_I32 &&
+ type->results[0] == KIT_WASM_VAL_I32;
+}
+
+static void* wasm_runner_resolve_import(void* user, const char* module,
+ const char* field,
+ const KitWasmImportType* type) {
+ (void)user;
+ if (!module || !field) return NULL;
+ if (strcmp(module, "env") == 0 && strcmp(field, "host_add") == 0 &&
+ wasm_runner_host_add_type(type))
+ return (void*)(uintptr_t)test_host_add;
+ return NULL;
+}
+
int main(int argc, char** argv) {
{
long ps = sysconf(_SC_PAGESIZE);
@@ -657,48 +805,33 @@ int main(int argc, char** argv) {
int result;
if (wasm_init && entry) {
- uint8_t* instance = calloc(1, 64u * 1024u);
- uint8_t* memory = calloc(1, 16u * 1024u * 1024u);
- if (!instance || !memory) {
- free(memory);
- free(instance);
+ WasmRunnerInstanceAlloc alloc;
+ if (wasm_runner_make_instance(jit, &alloc) != 0) {
kit_jit_run_dtors(jit);
kit_jit_free(jit);
free_compiler_target(c, kt);
return 1;
}
- for (uint32_t i = 0; i < 8u; ++i)
- ((WasmRunnerMemoryPrefix*)instance)[i].data =
- memory + i * (16u * 1024u * 1024u / 8u);
- /* Provide a small set of canned host imports for test fixtures. The
- * binder is a no-op if the module doesn't declare these names. */
- extern int32_t test_host_add(KitWasmInstance*, int32_t, int32_t);
- static const KitWasmHostImport test_imports[] = {
- {"env", "host_add", (void*)0},
- };
- /* Patch the function pointer at runtime so the static initializer
- * stays simple — clang under -Werror dislikes function-to-void* casts
- * in const initializers. */
- KitWasmHostImport
- runtime_imports[sizeof(test_imports) / sizeof(test_imports[0])];
- for (size_t k = 0; k < sizeof(test_imports) / sizeof(test_imports[0]); ++k)
- runtime_imports[k] = test_imports[k];
- runtime_imports[0].func = (void*)(uintptr_t)test_host_add;
- if (kit_wasm_bind_host_imports(
- c, jit, (KitWasmInstance*)instance, runtime_imports,
- sizeof(runtime_imports) / sizeof(runtime_imports[0]), NULL,
- NULL) != KIT_OK) {
- free(memory);
- free(instance);
- kit_jit_run_dtors(jit);
- kit_jit_free(jit);
- free_compiler_target(c, kt);
- return 1;
+ {
+ KitStatus bind_st = kit_wasm_bind_host_imports(
+ c, jit, (KitWasmInstance*)alloc.instance, NULL, 0,
+ wasm_runner_resolve_import, NULL);
+ if (bind_st != KIT_OK) {
+ fprintf(stderr, "jit-runner: wasm host import %s\n",
+ bind_st == KIT_NOT_FOUND
+ ? "unresolved"
+ : (bind_st == KIT_UNSUPPORTED ? "kind unsupported"
+ : "bind failed"));
+ wasm_runner_free_instance(&alloc);
+ kit_jit_run_dtors(jit);
+ kit_jit_free(jit);
+ free_compiler_target(c, kt);
+ return 1;
+ }
}
- ((WasmRunnerInitFn)wasm_init)(instance);
- result = ((WasmRunnerMainFn)entry)(instance);
- free(memory);
- free(instance);
+ ((WasmRunnerInitFn)wasm_init)(alloc.instance);
+ result = ((WasmRunnerMainFn)entry)(alloc.instance);
+ wasm_runner_free_instance(&alloc);
} else if (fn) {
#if defined(__aarch64__) || defined(__arm64__)
result = call_with_aarch64_tls(fn, tls_block);
diff --git a/test/wasm/cases/host_import_dedup_sig.expect b/test/wasm/cases/host_import_dedup_sig.expect
@@ -0,0 +1 @@
+14
diff --git a/test/wasm/cases/host_import_dedup_sig.wat b/test/wasm/cases/host_import_dedup_sig.wat
@@ -0,0 +1,12 @@
+(module
+ (type $binop (func (param i32 i32) (result i32)))
+ (import "env" "host_add" (func $add_a (type $binop)))
+ (import "env" "host_add" (func $add_b (type $binop)))
+ (func (export "test_main") (result i32)
+ i32.const 2
+ i32.const 3
+ call $add_a
+ i32.const 4
+ i32.const 5
+ call $add_b
+ i32.add))
diff --git a/test/wasm/cases/import_slot_unused.wat b/test/wasm/cases/import_slot_unused.wat
@@ -1,4 +1,4 @@
(module
- (import "host" "unused" (func $unused (param i32) (result i32)))
+ (import "env" "host_add" (func $unused (param i32 i32) (result i32)))
(func (export "test_main") (result i32)
i32.const 42))
diff --git a/test/wasm/cases/many_funcs_70.expect b/test/wasm/cases/many_funcs_70.expect
@@ -0,0 +1 @@
+42
diff --git a/test/wasm/cases/many_funcs_70.wat b/test/wasm/cases/many_funcs_70.wat
@@ -0,0 +1,75 @@
+(module
+ ;; Exercises module-indexed codegen layout tables past the old fixed
+ ;; 64-entry offset arrays.
+ (func $f0 (result i32) i32.const 0)
+ (func $f1 (result i32) i32.const 1)
+ (func $f2 (result i32) i32.const 2)
+ (func $f3 (result i32) i32.const 3)
+ (func $f4 (result i32) i32.const 4)
+ (func $f5 (result i32) i32.const 5)
+ (func $f6 (result i32) i32.const 6)
+ (func $f7 (result i32) i32.const 7)
+ (func $f8 (result i32) i32.const 8)
+ (func $f9 (result i32) i32.const 9)
+ (func $f10 (result i32) i32.const 10)
+ (func $f11 (result i32) i32.const 11)
+ (func $f12 (result i32) i32.const 12)
+ (func $f13 (result i32) i32.const 13)
+ (func $f14 (result i32) i32.const 14)
+ (func $f15 (result i32) i32.const 15)
+ (func $f16 (result i32) i32.const 16)
+ (func $f17 (result i32) i32.const 17)
+ (func $f18 (result i32) i32.const 18)
+ (func $f19 (result i32) i32.const 19)
+ (func $f20 (result i32) i32.const 20)
+ (func $f21 (result i32) i32.const 21)
+ (func $f22 (result i32) i32.const 22)
+ (func $f23 (result i32) i32.const 23)
+ (func $f24 (result i32) i32.const 24)
+ (func $f25 (result i32) i32.const 25)
+ (func $f26 (result i32) i32.const 26)
+ (func $f27 (result i32) i32.const 27)
+ (func $f28 (result i32) i32.const 28)
+ (func $f29 (result i32) i32.const 29)
+ (func $f30 (result i32) i32.const 30)
+ (func $f31 (result i32) i32.const 31)
+ (func $f32 (result i32) i32.const 32)
+ (func $f33 (result i32) i32.const 33)
+ (func $f34 (result i32) i32.const 34)
+ (func $f35 (result i32) i32.const 35)
+ (func $f36 (result i32) i32.const 36)
+ (func $f37 (result i32) i32.const 37)
+ (func $f38 (result i32) i32.const 38)
+ (func $f39 (result i32) i32.const 39)
+ (func $f40 (result i32) i32.const 40)
+ (func $f41 (result i32) i32.const 41)
+ (func $f42 (result i32) i32.const 42)
+ (func $f43 (result i32) i32.const 43)
+ (func $f44 (result i32) i32.const 44)
+ (func $f45 (result i32) i32.const 45)
+ (func $f46 (result i32) i32.const 46)
+ (func $f47 (result i32) i32.const 47)
+ (func $f48 (result i32) i32.const 48)
+ (func $f49 (result i32) i32.const 49)
+ (func $f50 (result i32) i32.const 50)
+ (func $f51 (result i32) i32.const 51)
+ (func $f52 (result i32) i32.const 52)
+ (func $f53 (result i32) i32.const 53)
+ (func $f54 (result i32) i32.const 54)
+ (func $f55 (result i32) i32.const 55)
+ (func $f56 (result i32) i32.const 56)
+ (func $f57 (result i32) i32.const 57)
+ (func $f58 (result i32) i32.const 58)
+ (func $f59 (result i32) i32.const 59)
+ (func $f60 (result i32) i32.const 60)
+ (func $f61 (result i32) i32.const 61)
+ (func $f62 (result i32) i32.const 62)
+ (func $f63 (result i32) i32.const 63)
+ (func $f64 (result i32) i32.const 64)
+ (func $f65 (result i32) i32.const 65)
+ (func $f66 (result i32) i32.const 66)
+ (func $f67 (result i32) i32.const 67)
+ (func $f68 (result i32) i32.const 68)
+ (func $f69 (result i32) i32.const 42)
+ (func (export "test_main") (result i32)
+ call $f69))
diff --git a/test/wasm/cases/memory_grow_large.expect b/test/wasm/cases/memory_grow_large.expect
@@ -0,0 +1 @@
+42
diff --git a/test/wasm/cases/memory_grow_large.wat b/test/wasm/cases/memory_grow_large.wat
@@ -0,0 +1,14 @@
+(module
+ ;; Old kit run allocated a fixed 16 MiB memory slab, then let memory.grow
+ ;; increase the logical page count past that backing store. A valid access in
+ ;; the newly-grown range must remain in-bounds for the host allocation.
+ (memory 1 300)
+ (func (export "test_main") (result i32)
+ i32.const 299
+ memory.grow
+ drop
+ i32.const 19595264
+ i32.const 42
+ i32.store
+ i32.const 19595264
+ i32.load))
diff --git a/test/wasm/cases/passive_elem_no_table.expect b/test/wasm/cases/passive_elem_no_table.expect
@@ -0,0 +1 @@
+42
diff --git a/test/wasm/cases/passive_elem_no_table.wat b/test/wasm/cases/passive_elem_no_table.wat
@@ -0,0 +1,6 @@
+(module
+ (func $target (result i32)
+ i32.const 7)
+ (elem $unused func $target)
+ (func (export "test_main") (result i32)
+ i32.const 42))
diff --git a/test/wasm/cases/table_fill_null.expect b/test/wasm/cases/table_fill_null.expect
@@ -0,0 +1 @@
+42
diff --git a/test/wasm/cases/table_fill_null.wat b/test/wasm/cases/table_fill_null.wat
@@ -0,0 +1,8 @@
+(module
+ (table 1 4 funcref)
+ (func (export "test_main") (result i32)
+ i32.const 0
+ ref.null func
+ i32.const 1
+ table.fill
+ i32.const 42))
diff --git a/test/wasm/cases/table_grow_null.expect b/test/wasm/cases/table_grow_null.expect
@@ -0,0 +1 @@
+42
diff --git a/test/wasm/cases/table_grow_null.wat b/test/wasm/cases/table_grow_null.wat
@@ -0,0 +1,10 @@
+(module
+ (table 0 4 funcref)
+ (func (export "test_main") (result i32)
+ ref.null func
+ i32.const 2
+ table.grow
+ drop
+ table.size
+ i32.const 40
+ i32.add))
diff --git a/test/wasm/err/data_segment_oob.wat b/test/wasm/err/data_segment_oob.wat
@@ -0,0 +1,3 @@
+(module
+ (memory 1)
+ (data (i32.const 65536) "\01"))
diff --git a/test/wasm/err/memory_bad_align.wat b/test/wasm/err/memory_bad_align.wat
@@ -0,0 +1,5 @@
+(module
+ (memory 1)
+ (func (export "test_main") (result i32)
+ i32.const 0
+ i32.load align=8))
diff --git a/test/wasm/err/memory_init_active.wat b/test/wasm/err/memory_init_active.wat
@@ -0,0 +1,8 @@
+(module
+ (memory 1)
+ (data $seg (i32.const 0) "\01")
+ (func (export "test_main")
+ i32.const 0
+ i32.const 0
+ i32.const 0
+ memory.init $seg))
diff --git a/test/wasm/err/table_init_active.wat b/test/wasm/err/table_init_active.wat
@@ -0,0 +1,11 @@
+(module
+ (type $ret_i32 (func (result i32)))
+ (func $target (type $ret_i32)
+ i32.const 42)
+ (table 1 funcref)
+ (elem $seg (i32.const 0) func $target)
+ (func (export "test_main")
+ i32.const 0
+ i32.const 0
+ i32.const 0
+ table.init $seg))
diff --git a/test/wasm/harness/start_wasm.c b/test/wasm/harness/start_wasm.c
@@ -6,10 +6,8 @@
*
* Host-import binding (kit_wasm_bind_host_imports) is intentionally not
* called here: this harness is freestanding and has no link to libkit.
- * Modules executed through this path that declare imports leave the slots
- * NULL; invoking such an import traps via the in-module null check. Real
- * embedders should use the JIT runner or `kit run`, both of which
- * resolve imports before calling __kit_wasm_init.
+ * It binds the small canned test import set below and rejects startup if a
+ * module declares anything else, matching Wasm instantiation semantics.
*/
extern void __kit_wasm_init(void*);
@@ -20,8 +18,10 @@ extern int test_main(void*);
typedef struct WasmStartImportDesc {
const char* module;
const char* field;
- unsigned int typeidx;
+ unsigned int kind;
+ unsigned int desc_index;
unsigned int slot_offset;
+ unsigned int reserved;
} WasmStartImportDesc;
extern const WasmStartImportDesc __kit_wasm_imports[] __attribute__((weak));
extern const unsigned int __kit_wasm_nimports __attribute__((weak));
@@ -41,16 +41,20 @@ static int start_streq(const char* a, const char* b) {
return *a == 0 && *b == 0;
}
-static void start_bind_canned_imports(void* instance) {
- if (!(&__kit_wasm_nimports)) return;
+static int start_bind_canned_imports(void* instance) {
+ if (!(&__kit_wasm_nimports)) return 1;
unsigned int n = __kit_wasm_nimports;
for (unsigned int i = 0; i < n; ++i) {
const WasmStartImportDesc* d = &__kit_wasm_imports[i];
- if (start_streq(d->module, "env") && start_streq(d->field, "host_add")) {
+ if (d->kind == 0 && start_streq(d->module, "env") &&
+ start_streq(d->field, "host_add")) {
*(void**)((unsigned char*)instance + d->slot_offset) =
(void*)(unsigned long)start_test_host_add;
+ } else {
+ return 0;
}
}
+ return 1;
}
typedef struct WasmStartMemoryPrefix {
@@ -60,9 +64,26 @@ typedef struct WasmStartMemoryPrefix {
unsigned int flags;
} WasmStartMemoryPrefix;
+typedef struct WasmStartMemoryLayout {
+ unsigned long long offset;
+ unsigned long long min_pages;
+ unsigned long long max_pages;
+ unsigned int flags;
+ unsigned int reserved;
+} WasmStartMemoryLayout;
+
+extern const unsigned long long __kit_wasm_instance_size
+ __attribute__((weak));
+extern const unsigned int __kit_wasm_nmemories __attribute__((weak));
+extern const WasmStartMemoryLayout __kit_wasm_memory_layouts[]
+ __attribute__((weak));
+
#define WASM_START_MEMORY_PREFIX_COUNT 8u
#define WASM_START_INSTANCE_SIZE (64u * 1024u)
#define WASM_START_MEMORY_SIZE (16u * 1024u * 1024u)
+#define WASM_START_PAGE_SIZE (64ull * 1024ull)
+#define WASM_START_MAX_INSTANCE_SIZE (64ull * 1024ull * 1024ull)
+#define WASM_START_MAX_TOTAL_MEMORY_SIZE (1024ull * 1024ull * 1024ull)
#define WASM_START_PROT_READ 1
#define WASM_START_PROT_WRITE 2
#define WASM_START_MAP_PRIVATE 2
@@ -154,18 +175,67 @@ static void* start_mmap(unsigned long size) {
#endif
}
+static int start_pages_to_bytes(unsigned long long pages,
+ unsigned long long* out) {
+ unsigned long long max = ~0ull;
+ if (pages > max / WASM_START_PAGE_SIZE) return 0;
+ *out = pages * WASM_START_PAGE_SIZE;
+ return 1;
+}
+
+static int start_setup_dynamic_instance(void** instance_out) {
+ void* instance;
+ unsigned long long instance_size;
+ unsigned long long total_memory = 0;
+ if (!(&__kit_wasm_instance_size) || !(&__kit_wasm_nmemories))
+ return 0;
+ instance_size = __kit_wasm_instance_size ? __kit_wasm_instance_size : 1ull;
+ if (instance_size > WASM_START_MAX_INSTANCE_SIZE) return -1;
+ if (__kit_wasm_nmemories && !(&__kit_wasm_memory_layouts)) return -1;
+ instance = start_mmap((unsigned long)instance_size);
+ if (!instance) return -1;
+ for (unsigned int i = 0; i < __kit_wasm_nmemories; ++i) {
+ const WasmStartMemoryLayout* ml = &__kit_wasm_memory_layouts[i];
+ WasmStartMemoryPrefix* rec;
+ unsigned long long bytes;
+ void* memory = (void*)0;
+ if (ml->max_pages < ml->min_pages) return -1;
+ if (ml->offset > instance_size ||
+ instance_size - ml->offset < sizeof(WasmStartMemoryPrefix))
+ return -1;
+ if (!start_pages_to_bytes(ml->max_pages, &bytes)) return -1;
+ if (bytes > WASM_START_MAX_TOTAL_MEMORY_SIZE ||
+ total_memory > WASM_START_MAX_TOTAL_MEMORY_SIZE - bytes)
+ return -1;
+ total_memory += bytes;
+ if (bytes) {
+ memory = start_mmap((unsigned long)bytes);
+ if (!memory) return -1;
+ }
+ rec = (WasmStartMemoryPrefix*)((unsigned char*)instance + ml->offset);
+ rec->data = (unsigned char*)memory;
+ }
+ *instance_out = instance;
+ return 1;
+}
+
#if defined(__x86_64__)
__attribute__((force_align_arg_pointer))
#endif
void _start(void) {
- void* instance = start_mmap(WASM_START_INSTANCE_SIZE);
- void* memory = start_mmap(WASM_START_MEMORY_SIZE);
- if (!instance || !memory) do_exit(1);
- for (unsigned int i = 0; i < WASM_START_MEMORY_PREFIX_COUNT; ++i)
- ((WasmStartMemoryPrefix*)instance)[i].data =
- (unsigned char*)memory +
- i * (WASM_START_MEMORY_SIZE / WASM_START_MEMORY_PREFIX_COUNT);
- start_bind_canned_imports(instance);
+ void* instance = (void*)0;
+ int dyn = start_setup_dynamic_instance(&instance);
+ if (dyn < 0) do_exit(1);
+ if (dyn == 0) {
+ void* memory = start_mmap(WASM_START_MEMORY_SIZE);
+ instance = start_mmap(WASM_START_INSTANCE_SIZE);
+ if (!instance || !memory) do_exit(1);
+ for (unsigned int i = 0; i < WASM_START_MEMORY_PREFIX_COUNT; ++i)
+ ((WasmStartMemoryPrefix*)instance)[i].data =
+ (unsigned char*)memory +
+ i * (WASM_START_MEMORY_SIZE / WASM_START_MEMORY_PREFIX_COUNT);
+ }
+ if (!start_bind_canned_imports(instance)) do_exit(1);
__kit_wasm_init(instance);
do_exit(test_main(instance));
}
diff --git a/test/wasm/run.sh b/test/wasm/run.sh
@@ -130,19 +130,25 @@ static int32_t test_host_add(void *inst, int32_t a, int32_t b) {
typedef struct {
const char *module;
const char *field;
- unsigned int typeidx;
+ unsigned int kind;
+ unsigned int desc_index;
unsigned int slot_offset;
+ unsigned int reserved;
} WasmImportDesc;
__attribute__((weak)) const unsigned int __kit_wasm_nimports = 0;
-__attribute__((weak)) const WasmImportDesc __kit_wasm_imports[1] = {{0, 0, 0, 0}};
+__attribute__((weak)) const WasmImportDesc __kit_wasm_imports[1] = {{0, 0, 0, 0, 0, 0}};
-static void bind_canned_imports(void *instance) {
+static int bind_canned_imports(void *instance) {
for (unsigned int i = 0; i < __kit_wasm_nimports; ++i) {
const WasmImportDesc *d = &__kit_wasm_imports[i];
- if (strcmp(d->module, "env") == 0 && strcmp(d->field, "host_add") == 0)
+ if (d->kind == 0 && strcmp(d->module, "env") == 0 &&
+ strcmp(d->field, "host_add") == 0)
*(void **)((unsigned char *)instance + d->slot_offset) =
(void *)(uintptr_t)test_host_add;
+ else
+ return 0;
}
+ return 1;
}
typedef struct { unsigned char *data; unsigned long long pages;
@@ -159,7 +165,7 @@ int main(void) {
memory + i * (WASM_START_MEMORY_SIZE / WASM_START_MEMORY_PREFIX_COUNT);
/* After the memory prefix (which can overlap import slots for memory-less
* modules), before init (which consumes the bound slots). */
- bind_canned_imports(instance);
+ if (!bind_canned_imports(instance)) return 1;
__kit_wasm_init(instance);
return (int)test_main(instance);
}
diff --git a/test/wasm/trap/host_import_unbound.wat b/test/wasm/trap/host_import_unbound.wat
@@ -1,6 +1,6 @@
(module
- ;; Declares an import the test harness does not bind. Calling it must
- ;; trap cleanly (not segfault) at invocation time.
+ ;; Declares an import the test harness does not bind. Instantiation must
+ ;; fail cleanly (not run with a dangling or NULL import slot).
(import "env" "host_unbound" (func $host_unbound (param i32) (result i32)))
(func (export "test_main") (result i32)
i32.const 0
diff --git a/test/wasm/trap/import_global_unbound.wat b/test/wasm/trap/import_global_unbound.wat
@@ -0,0 +1,4 @@
+(module
+ (import "env" "global" (global i32))
+ (func (export "test_main") (result i32)
+ global.get 0))
diff --git a/test/wasm/trap/import_memory_unbound.wat b/test/wasm/trap/import_memory_unbound.wat
@@ -0,0 +1,4 @@
+(module
+ (import "env" "memory" (memory 1))
+ (func (export "test_main") (result i32)
+ i32.const 0))
diff --git a/test/wasm/trap/import_table_unbound.wat b/test/wasm/trap/import_table_unbound.wat
@@ -0,0 +1,4 @@
+(module
+ (import "env" "table" (table 1 funcref))
+ (func (export "test_main") (result i32)
+ i32.const 0))
diff --git a/test/wasm/trap/memory_offset_overflow.wat b/test/wasm/trap/memory_offset_overflow.wat
@@ -0,0 +1,6 @@
+(module
+ (memory 1)
+ (data (i32.const 0) "\2a\00\00\00")
+ (func (export "test_main") (result i32)
+ i32.const 1
+ i32.load offset=18446744073709551615))