kit

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

start_wasm.c (8622B)


      1 /* Freestanding _start for Wasm-input native executable tests.
      2  *
      3  * Wasm frontend exports use the internal instance ABI:
      4  *   __kit_wasm_init(instance)
      5  *   test_main(instance)
      6  *
      7  * Host-import binding (kit_wasm_bind_host_imports) is intentionally not
      8  * called here: this harness is freestanding and has no link to libkit.
      9  * It binds the small canned test import set below and rejects startup if a
     10  * module declares anything else, matching Wasm instantiation semantics.
     11  */
     12 
     13 extern void __kit_wasm_init(void*);
     14 extern int test_main(void*);
     15 
     16 /* Imports metadata emitted by lang/wasm/cg.c when the module declares any
     17  * imports. Made weak so this harness still links for modules without them. */
     18 typedef struct WasmStartImportDesc {
     19   const char* module;
     20   const char* field;
     21   unsigned int kind;
     22   unsigned int desc_index;
     23   unsigned int slot_offset;
     24   unsigned int reserved;
     25 } WasmStartImportDesc;
     26 extern const WasmStartImportDesc __kit_wasm_imports[] __attribute__((weak));
     27 extern const unsigned int __kit_wasm_nimports __attribute__((weak));
     28 
     29 /* Canned test host function (also referenced by jit_runner.c, driver/run.c).
     30  * The freestanding harness inlines its own copy because it has no libc. */
     31 static int start_test_host_add(void* inst, int a, int b) {
     32   (void)inst;
     33   return a + b;
     34 }
     35 
     36 static int start_streq(const char* a, const char* b) {
     37   while (*a && *a == *b) {
     38     a++;
     39     b++;
     40   }
     41   return *a == 0 && *b == 0;
     42 }
     43 
     44 static int start_bind_canned_imports(void* instance) {
     45   if (!(&__kit_wasm_nimports)) return 1;
     46   unsigned int n = __kit_wasm_nimports;
     47   for (unsigned int i = 0; i < n; ++i) {
     48     const WasmStartImportDesc* d = &__kit_wasm_imports[i];
     49     if (d->kind == 0 && start_streq(d->module, "env") &&
     50         start_streq(d->field, "host_add")) {
     51       *(void**)((unsigned char*)instance + d->slot_offset) =
     52           (void*)(unsigned long)start_test_host_add;
     53     } else {
     54       return 0;
     55     }
     56   }
     57   return 1;
     58 }
     59 
     60 typedef struct WasmStartMemoryPrefix {
     61   unsigned char* data;
     62   unsigned long long pages;
     63   unsigned long long max_pages;
     64   unsigned int flags;
     65 } WasmStartMemoryPrefix;
     66 
     67 typedef struct WasmStartMemoryLayout {
     68   unsigned long long offset;
     69   unsigned long long min_pages;
     70   unsigned long long max_pages;
     71   unsigned int flags;
     72   unsigned int reserved;
     73 } WasmStartMemoryLayout;
     74 
     75 extern const unsigned long long __kit_wasm_instance_size
     76     __attribute__((weak));
     77 extern const unsigned int __kit_wasm_nmemories __attribute__((weak));
     78 extern const WasmStartMemoryLayout __kit_wasm_memory_layouts[]
     79     __attribute__((weak));
     80 
     81 #define WASM_START_MEMORY_PREFIX_COUNT 8u
     82 #define WASM_START_INSTANCE_SIZE (64u * 1024u)
     83 #define WASM_START_MEMORY_SIZE (16u * 1024u * 1024u)
     84 #define WASM_START_PAGE_SIZE (64ull * 1024ull)
     85 #define WASM_START_MAX_INSTANCE_SIZE (64ull * 1024ull * 1024ull)
     86 #define WASM_START_MAX_TOTAL_MEMORY_SIZE (1024ull * 1024ull * 1024ull)
     87 #define WASM_START_PROT_READ 1
     88 #define WASM_START_PROT_WRITE 2
     89 #define WASM_START_MAP_PRIVATE 2
     90 #if defined(__APPLE__)
     91 #define WASM_START_MAP_ANON 0x1000
     92 #else
     93 #define WASM_START_MAP_ANON 0x20
     94 #endif
     95 
     96 #if defined(__APPLE__)
     97 extern void exit(int) __attribute__((noreturn));
     98 extern void* mmap(void*, unsigned long, int, int, int, long);
     99 #endif
    100 
    101 __attribute__((noreturn)) static void do_exit(int code) {
    102 #if defined(__APPLE__)
    103   exit(code);
    104   __builtin_unreachable();
    105 #elif defined(__aarch64__)
    106   register long x8 __asm__("x8") = 94;
    107   register long x0 __asm__("x0") = code;
    108   __asm__ volatile("svc #0" ::"r"(x8), "r"(x0) : "memory");
    109 #elif defined(__x86_64__)
    110   register long rax __asm__("rax") = 231;
    111   register long rdi __asm__("rdi") = code;
    112   __asm__ volatile("syscall" ::"r"(rax), "r"(rdi) : "rcx", "r11", "memory");
    113 #elif defined(__riscv) && __riscv_xlen == 64
    114   register long a7 __asm__("a7") = 94;
    115   register long a0 __asm__("a0") = code;
    116   __asm__ volatile("ecall" ::"r"(a7), "r"(a0) : "memory");
    117 #else
    118 #error "start_wasm.c: unsupported architecture"
    119 #endif
    120   __builtin_unreachable();
    121 }
    122 
    123 static void* start_mmap(unsigned long size) {
    124 #if defined(__APPLE__)
    125   void* p = mmap((void*)0, size, WASM_START_PROT_READ | WASM_START_PROT_WRITE,
    126                  WASM_START_MAP_PRIVATE | WASM_START_MAP_ANON, -1, 0);
    127   if ((long)p == -1) return (void*)0;
    128   return p;
    129 #elif defined(__aarch64__)
    130   register long x8 __asm__("x8") = 222;
    131   register long x0 __asm__("x0") = 0;
    132   register long x1 __asm__("x1") = (long)size;
    133   register long x2 __asm__("x2") = WASM_START_PROT_READ | WASM_START_PROT_WRITE;
    134   register long x3 __asm__("x3") = WASM_START_MAP_PRIVATE | WASM_START_MAP_ANON;
    135   register long x4 __asm__("x4") = -1;
    136   register long x5 __asm__("x5") = 0;
    137   __asm__ volatile("svc #0"
    138                    : "+r"(x0)
    139                    : "r"(x8), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5)
    140                    : "memory");
    141   if (x0 < 0 && x0 > -4096) return (void*)0;
    142   return (void*)x0;
    143 #elif defined(__x86_64__)
    144   register long rax __asm__("rax") = 9;
    145   register long rdi __asm__("rdi") = 0;
    146   register long rsi __asm__("rsi") = (long)size;
    147   register long rdx __asm__("rdx") =
    148       WASM_START_PROT_READ | WASM_START_PROT_WRITE;
    149   register long r10 __asm__("r10") =
    150       WASM_START_MAP_PRIVATE | WASM_START_MAP_ANON;
    151   register long r8 __asm__("r8") = -1;
    152   register long r9 __asm__("r9") = 0;
    153   __asm__ volatile("syscall"
    154                    : "+r"(rax)
    155                    : "r"(rdi), "r"(rsi), "r"(rdx), "r"(r10), "r"(r8), "r"(r9)
    156                    : "rcx", "r11", "memory");
    157   if (rax < 0 && rax > -4096) return (void*)0;
    158   return (void*)rax;
    159 #elif defined(__riscv) && __riscv_xlen == 64
    160   register long a7 __asm__("a7") = 222;
    161   register long a0 __asm__("a0") = 0;
    162   register long a1 __asm__("a1") = (long)size;
    163   register long a2 __asm__("a2") = WASM_START_PROT_READ | WASM_START_PROT_WRITE;
    164   register long a3 __asm__("a3") = WASM_START_MAP_PRIVATE | WASM_START_MAP_ANON;
    165   register long a4 __asm__("a4") = -1;
    166   register long a5 __asm__("a5") = 0;
    167   __asm__ volatile("ecall"
    168                    : "+r"(a0)
    169                    : "r"(a7), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5)
    170                    : "memory");
    171   if (a0 < 0 && a0 > -4096) return (void*)0;
    172   return (void*)a0;
    173 #else
    174 #error "start_wasm.c: unsupported architecture"
    175 #endif
    176 }
    177 
    178 static int start_pages_to_bytes(unsigned long long pages,
    179                                 unsigned long long* out) {
    180   unsigned long long max = ~0ull;
    181   if (pages > max / WASM_START_PAGE_SIZE) return 0;
    182   *out = pages * WASM_START_PAGE_SIZE;
    183   return 1;
    184 }
    185 
    186 static int start_setup_dynamic_instance(void** instance_out) {
    187   void* instance;
    188   unsigned long long instance_size;
    189   unsigned long long total_memory = 0;
    190   if (!(&__kit_wasm_instance_size) || !(&__kit_wasm_nmemories))
    191     return 0;
    192   instance_size = __kit_wasm_instance_size ? __kit_wasm_instance_size : 1ull;
    193   if (instance_size > WASM_START_MAX_INSTANCE_SIZE) return -1;
    194   if (__kit_wasm_nmemories && !(&__kit_wasm_memory_layouts)) return -1;
    195   instance = start_mmap((unsigned long)instance_size);
    196   if (!instance) return -1;
    197   for (unsigned int i = 0; i < __kit_wasm_nmemories; ++i) {
    198     const WasmStartMemoryLayout* ml = &__kit_wasm_memory_layouts[i];
    199     WasmStartMemoryPrefix* rec;
    200     unsigned long long bytes;
    201     void* memory = (void*)0;
    202     if (ml->max_pages < ml->min_pages) return -1;
    203     if (ml->offset > instance_size ||
    204         instance_size - ml->offset < sizeof(WasmStartMemoryPrefix))
    205       return -1;
    206     if (!start_pages_to_bytes(ml->max_pages, &bytes)) return -1;
    207     if (bytes > WASM_START_MAX_TOTAL_MEMORY_SIZE ||
    208         total_memory > WASM_START_MAX_TOTAL_MEMORY_SIZE - bytes)
    209       return -1;
    210     total_memory += bytes;
    211     if (bytes) {
    212       memory = start_mmap((unsigned long)bytes);
    213       if (!memory) return -1;
    214     }
    215     rec = (WasmStartMemoryPrefix*)((unsigned char*)instance + ml->offset);
    216     rec->data = (unsigned char*)memory;
    217   }
    218   *instance_out = instance;
    219   return 1;
    220 }
    221 
    222 #if defined(__x86_64__)
    223 __attribute__((force_align_arg_pointer))
    224 #endif
    225 void _start(void) {
    226   void* instance = (void*)0;
    227   int dyn = start_setup_dynamic_instance(&instance);
    228   if (dyn < 0) do_exit(1);
    229   if (dyn == 0) {
    230     void* memory = start_mmap(WASM_START_MEMORY_SIZE);
    231     instance = start_mmap(WASM_START_INSTANCE_SIZE);
    232     if (!instance || !memory) do_exit(1);
    233     for (unsigned int i = 0; i < WASM_START_MEMORY_PREFIX_COUNT; ++i)
    234       ((WasmStartMemoryPrefix*)instance)[i].data =
    235           (unsigned char*)memory +
    236           i * (WASM_START_MEMORY_SIZE / WASM_START_MEMORY_PREFIX_COUNT);
    237   }
    238   if (!start_bind_canned_imports(instance)) do_exit(1);
    239   __kit_wasm_init(instance);
    240   do_exit(test_main(instance));
    241 }