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 }