kit

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

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 }