kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

host_imports.c (8594B)


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