host_imports.c (9859B)
1 /* Host-import binder: walks the per-module __kit_wasm_imports metadata 2 * emitted by lang/wasm/cg.c and writes resolved function pointers into the 3 * matching slots of a freshly-allocated KitWasmInstance. 4 * 5 * The metadata wire format is defined in lang/wasm/runtime_abi.h alongside 6 * the rest of the kit-instance ABI. The public-API surface is in 7 * include/kit/wasm.h. */ 8 9 #include <kit/core.h> 10 #include <kit/jit.h> 11 #include <kit/wasm.h> 12 #include <stdint.h> 13 #include <string.h> 14 15 #include "core/diag.h" 16 #include "runtime_abi.h" 17 18 /* Raw wasm WasmValType byte encoding (mirrored from src/wasm/wasm.h so this 19 * module stays free of internal wasm-core dependencies; if either side 20 * changes these, the binder/metadata pair breaks together). */ 21 enum { 22 WASM_ABI_VAL_I32 = 0x7f, 23 WASM_ABI_VAL_I64 = 0x7e, 24 WASM_ABI_VAL_F32 = 0x7d, 25 WASM_ABI_VAL_F64 = 0x7c, 26 WASM_ABI_VAL_FUNCREF = 0x70, 27 WASM_ABI_VAL_EXTERNREF = 0x6f, 28 }; 29 30 static int host_imports_streq(const char* a, const char* b) { 31 if (a == b) return 1; 32 if (!a || !b) return 0; 33 return strcmp(a, b) == 0; 34 } 35 36 static int host_imports_map_valtype(uint8_t raw, KitWasmValType* out) { 37 switch (raw) { 38 case WASM_ABI_VAL_I32: 39 *out = KIT_WASM_VAL_I32; 40 return 1; 41 case WASM_ABI_VAL_I64: 42 *out = KIT_WASM_VAL_I64; 43 return 1; 44 case WASM_ABI_VAL_F32: 45 *out = KIT_WASM_VAL_F32; 46 return 1; 47 case WASM_ABI_VAL_F64: 48 *out = KIT_WASM_VAL_F64; 49 return 1; 50 case WASM_ABI_VAL_FUNCREF: 51 *out = KIT_WASM_VAL_FUNCREF; 52 return 1; 53 case WASM_ABI_VAL_EXTERNREF: 54 *out = KIT_WASM_VAL_EXTERNREF; 55 return 1; 56 default: 57 return 0; 58 } 59 } 60 61 static void host_imports_diag_unresolved(KitCompiler* c, 62 const KitWasmImportDesc* d) { 63 const char* module = d && d->module ? d->module : "<unknown>"; 64 const char* field = d && d->field ? d->field : "<unknown>"; 65 if (host_imports_streq(module, "wasi_snapshot_preview1")) { 66 kit_ctx_diagf(c ? c->ctx : NULL, 67 "unsupported WASI Preview1 import: %s", field); 68 } else { 69 kit_ctx_diagf(c ? c->ctx : NULL, 70 "wasm host import unresolved: %s.%s", module, field); 71 } 72 } 73 74 static void host_imports_diag_unsupported_kind(KitCompiler* c, 75 const KitWasmImportDesc* d, 76 const char* kind) { 77 const char* module = d && d->module ? d->module : "<unknown>"; 78 const char* field = d && d->field ? d->field : "<unknown>"; 79 kit_ctx_diagf(c ? c->ctx : NULL, 80 "unsupported wasm host %s import: %s.%s", kind, module, field); 81 } 82 83 /* Translate an nparams/nresults raw-byte type description into the public 84 * KitWasmImportType. Returns 0 on unsupported value-type byte. The 85 * arrays are written into caller-provided storage. */ 86 static int host_imports_build_type(const KitWasmTypeDesc* src, 87 KitWasmValType* pbuf, uint32_t pcap, 88 KitWasmValType* rbuf, uint32_t rcap, 89 KitWasmImportType* out) { 90 uint32_t i; 91 if (src->nparams > pcap || src->nresults > rcap) return 0; 92 for (i = 0; i < src->nparams; ++i) 93 if (!host_imports_map_valtype(src->params[i], &pbuf[i])) return 0; 94 for (i = 0; i < src->nresults; ++i) 95 if (!host_imports_map_valtype(src->results[i], &rbuf[i])) return 0; 96 out->params = pbuf; 97 out->nparams = src->nparams; 98 out->results = rbuf; 99 out->nresults = src->nresults; 100 return 1; 101 } 102 103 /* Conservative cap on per-import param/result count for the on-stack 104 * translation buffer in kit_wasm_bind_host_imports. Wasm allows arbitrary 105 * counts in principle, but real modules cap well below this; tests use at 106 * most a few. If a module exceeds this the binder returns an error rather 107 * than silently truncating. */ 108 #define KIT_WASM_BIND_MAX_VALTYPES 32u 109 110 KIT_API KitStatus kit_wasm_get_runtime_layout(KitJit* jit, 111 KitWasmRuntimeLayout* out) { 112 const uint64_t* instance_size; 113 const uint32_t* nmemories; 114 const KitWasmMemoryLayout* memories = NULL; 115 KitWasmRuntimeLayout z = {0}; 116 if (!jit || !out) return KIT_INVALID; 117 instance_size = (const uint64_t*)kit_jit_lookup( 118 jit, KIT_SLICE_LIT("__kit_wasm_instance_size")); 119 nmemories = (const uint32_t*)kit_jit_lookup( 120 jit, KIT_SLICE_LIT("__kit_wasm_nmemories")); 121 if (!instance_size && !nmemories) return KIT_NOT_FOUND; 122 if (!instance_size || !nmemories) return KIT_MALFORMED; 123 if (*nmemories) { 124 memories = (const KitWasmMemoryLayout*)kit_jit_lookup( 125 jit, KIT_SLICE_LIT("__kit_wasm_memory_layouts")); 126 if (!memories) return KIT_MALFORMED; 127 } 128 z.instance_size = *instance_size; 129 z.memories = memories; 130 z.nmemories = *nmemories; 131 *out = z; 132 return KIT_OK; 133 } 134 135 KIT_API KitStatus kit_wasm_bind_host_imports(KitCompiler* compiler, KitJit* jit, 136 KitWasmInstance* inst, 137 const KitWasmHostImport* imports, 138 size_t nimports, 139 KitWasmResolveFn resolve, 140 void* user) { 141 const uint32_t* nimports_meta; 142 const uint32_t* nfunc_import_types_meta = NULL; 143 const KitWasmImportDesc* import_descs; 144 const KitWasmTypeDesc* type_descs = NULL; 145 const KitWasmMemoryImportDesc* memory_descs = NULL; 146 const KitWasmTableImportDesc* table_descs = NULL; 147 const KitWasmGlobalImportDesc* global_descs = NULL; 148 uint32_t n; 149 uint32_t i; 150 uint32_t nfunc_descs = 0; 151 uint32_t nmemory_descs = 0; 152 uint32_t ntable_descs = 0; 153 uint32_t nglobal_descs = 0; 154 uint32_t nfunc_import_types = 0; 155 if (!jit || !inst) return KIT_INVALID; 156 157 nimports_meta = (const uint32_t*)kit_jit_lookup( 158 jit, KIT_SLICE_LIT("__kit_wasm_nimports")); 159 if (!nimports_meta) { 160 /* Module wasn't built through kit's wasm frontend, or it has no 161 * imports at all. Nothing to bind. */ 162 return KIT_OK; 163 } 164 n = *nimports_meta; 165 if (n == 0) return KIT_OK; 166 import_descs = (const KitWasmImportDesc*)kit_jit_lookup( 167 jit, KIT_SLICE_LIT("__kit_wasm_imports")); 168 if (!import_descs) return KIT_MALFORMED; 169 170 for (i = 0; i < n; ++i) { 171 switch (import_descs[i].kind) { 172 case KIT_WASM_IMPORT_FUNC: 173 nfunc_descs++; 174 break; 175 case KIT_WASM_IMPORT_MEMORY: 176 nmemory_descs++; 177 break; 178 case KIT_WASM_IMPORT_TABLE: 179 ntable_descs++; 180 break; 181 case KIT_WASM_IMPORT_GLOBAL: 182 nglobal_descs++; 183 break; 184 default: 185 return KIT_MALFORMED; 186 } 187 } 188 if (nfunc_descs) { 189 nfunc_import_types_meta = (const uint32_t*)kit_jit_lookup( 190 jit, KIT_SLICE_LIT("__kit_wasm_nfunc_import_types")); 191 type_descs = (const KitWasmTypeDesc*)kit_jit_lookup( 192 jit, KIT_SLICE_LIT("__kit_wasm_types")); 193 if (!nfunc_import_types_meta || !type_descs) return KIT_MALFORMED; 194 nfunc_import_types = *nfunc_import_types_meta; 195 if (nfunc_import_types == 0 || nfunc_import_types > nfunc_descs) 196 return KIT_MALFORMED; 197 } 198 199 for (i = 0; i < n; ++i) { 200 const KitWasmImportDesc* d = &import_descs[i]; 201 void* fn = NULL; 202 KitWasmValType pbuf[KIT_WASM_BIND_MAX_VALTYPES]; 203 KitWasmValType rbuf[KIT_WASM_BIND_MAX_VALTYPES]; 204 KitWasmImportType type; 205 switch (d->kind) { 206 case KIT_WASM_IMPORT_FUNC: 207 if (d->desc_index >= nfunc_import_types) return KIT_MALFORMED; 208 break; 209 case KIT_WASM_IMPORT_MEMORY: 210 if (d->desc_index >= nmemory_descs) return KIT_MALFORMED; 211 if (!memory_descs) 212 memory_descs = (const KitWasmMemoryImportDesc*)kit_jit_lookup( 213 jit, KIT_SLICE_LIT("__kit_wasm_memory_import_types")); 214 if (!memory_descs) return KIT_MALFORMED; 215 host_imports_diag_unsupported_kind(compiler, d, "memory"); 216 return KIT_UNSUPPORTED; 217 case KIT_WASM_IMPORT_TABLE: 218 if (d->desc_index >= ntable_descs) return KIT_MALFORMED; 219 if (!table_descs) 220 table_descs = (const KitWasmTableImportDesc*)kit_jit_lookup( 221 jit, KIT_SLICE_LIT("__kit_wasm_table_import_types")); 222 if (!table_descs) return KIT_MALFORMED; 223 host_imports_diag_unsupported_kind(compiler, d, "table"); 224 return KIT_UNSUPPORTED; 225 case KIT_WASM_IMPORT_GLOBAL: 226 if (d->desc_index >= nglobal_descs) return KIT_MALFORMED; 227 if (!global_descs) 228 global_descs = (const KitWasmGlobalImportDesc*)kit_jit_lookup( 229 jit, KIT_SLICE_LIT("__kit_wasm_global_import_types")); 230 if (!global_descs) return KIT_MALFORMED; 231 host_imports_diag_unsupported_kind(compiler, d, "global"); 232 return KIT_UNSUPPORTED; 233 default: 234 return KIT_MALFORMED; 235 } 236 /* Static table first. */ 237 for (size_t k = 0; imports && k < nimports; ++k) { 238 if (host_imports_streq(imports[k].module, d->module) && 239 host_imports_streq(imports[k].field, d->field)) { 240 fn = imports[k].func; 241 break; 242 } 243 } 244 /* Resolver fallback. */ 245 if (!fn && resolve) { 246 const KitWasmTypeDesc* td; 247 td = &type_descs[d->desc_index]; 248 if (!host_imports_build_type(td, pbuf, KIT_WASM_BIND_MAX_VALTYPES, rbuf, 249 KIT_WASM_BIND_MAX_VALTYPES, &type)) 250 return KIT_MALFORMED; 251 fn = resolve(user, d->module, d->field, &type); 252 } 253 if (!fn) { 254 host_imports_diag_unresolved(compiler, d); 255 return KIT_NOT_FOUND; 256 } 257 /* Slot is a void* at byte offset slot_offset inside the instance struct. 258 * The KitWasmFuncImport record is { void* fn; } so the offset of the 259 * field is also the offset of the fn pointer. */ 260 *(void**)((unsigned char*)inst + d->slot_offset) = fn; 261 } 262 return KIT_OK; 263 }