start_wasm.c (8614B)
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 __attribute__((weak)); 76 extern const unsigned int __kit_wasm_nmemories __attribute__((weak)); 77 extern const WasmStartMemoryLayout __kit_wasm_memory_layouts[] 78 __attribute__((weak)); 79 80 #define WASM_START_MEMORY_PREFIX_COUNT 8u 81 #define WASM_START_INSTANCE_SIZE (64u * 1024u) 82 #define WASM_START_MEMORY_SIZE (16u * 1024u * 1024u) 83 #define WASM_START_PAGE_SIZE (64ull * 1024ull) 84 #define WASM_START_MAX_INSTANCE_SIZE (64ull * 1024ull * 1024ull) 85 #define WASM_START_MAX_TOTAL_MEMORY_SIZE (1024ull * 1024ull * 1024ull) 86 #define WASM_START_PROT_READ 1 87 #define WASM_START_PROT_WRITE 2 88 #define WASM_START_MAP_PRIVATE 2 89 #if defined(__APPLE__) 90 #define WASM_START_MAP_ANON 0x1000 91 #else 92 #define WASM_START_MAP_ANON 0x20 93 #endif 94 95 #if defined(__APPLE__) 96 extern void exit(int) __attribute__((noreturn)); 97 extern void* mmap(void*, unsigned long, int, int, int, long); 98 #endif 99 100 __attribute__((noreturn)) static void do_exit(int code) { 101 #if defined(__APPLE__) 102 exit(code); 103 __builtin_unreachable(); 104 #elif defined(__aarch64__) 105 register long x8 __asm__("x8") = 94; 106 register long x0 __asm__("x0") = code; 107 __asm__ volatile("svc #0" ::"r"(x8), "r"(x0) : "memory"); 108 #elif defined(__x86_64__) 109 register long rax __asm__("rax") = 231; 110 register long rdi __asm__("rdi") = code; 111 __asm__ volatile("syscall" ::"r"(rax), "r"(rdi) : "rcx", "r11", "memory"); 112 #elif defined(__riscv) && __riscv_xlen == 64 113 register long a7 __asm__("a7") = 94; 114 register long a0 __asm__("a0") = code; 115 __asm__ volatile("ecall" ::"r"(a7), "r"(a0) : "memory"); 116 #else 117 #error "start_wasm.c: unsupported architecture" 118 #endif 119 __builtin_unreachable(); 120 } 121 122 static void* start_mmap(unsigned long size) { 123 #if defined(__APPLE__) 124 void* p = mmap((void*)0, size, WASM_START_PROT_READ | WASM_START_PROT_WRITE, 125 WASM_START_MAP_PRIVATE | WASM_START_MAP_ANON, -1, 0); 126 if ((long)p == -1) return (void*)0; 127 return p; 128 #elif defined(__aarch64__) 129 register long x8 __asm__("x8") = 222; 130 register long x0 __asm__("x0") = 0; 131 register long x1 __asm__("x1") = (long)size; 132 register long x2 __asm__("x2") = WASM_START_PROT_READ | WASM_START_PROT_WRITE; 133 register long x3 __asm__("x3") = WASM_START_MAP_PRIVATE | WASM_START_MAP_ANON; 134 register long x4 __asm__("x4") = -1; 135 register long x5 __asm__("x5") = 0; 136 __asm__ volatile("svc #0" 137 : "+r"(x0) 138 : "r"(x8), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5) 139 : "memory"); 140 if (x0 < 0 && x0 > -4096) return (void*)0; 141 return (void*)x0; 142 #elif defined(__x86_64__) 143 register long rax __asm__("rax") = 9; 144 register long rdi __asm__("rdi") = 0; 145 register long rsi __asm__("rsi") = (long)size; 146 register long rdx __asm__("rdx") = 147 WASM_START_PROT_READ | WASM_START_PROT_WRITE; 148 register long r10 __asm__("r10") = 149 WASM_START_MAP_PRIVATE | WASM_START_MAP_ANON; 150 register long r8 __asm__("r8") = -1; 151 register long r9 __asm__("r9") = 0; 152 __asm__ volatile("syscall" 153 : "+r"(rax) 154 : "r"(rdi), "r"(rsi), "r"(rdx), "r"(r10), "r"(r8), "r"(r9) 155 : "rcx", "r11", "memory"); 156 if (rax < 0 && rax > -4096) return (void*)0; 157 return (void*)rax; 158 #elif defined(__riscv) && __riscv_xlen == 64 159 register long a7 __asm__("a7") = 222; 160 register long a0 __asm__("a0") = 0; 161 register long a1 __asm__("a1") = (long)size; 162 register long a2 __asm__("a2") = WASM_START_PROT_READ | WASM_START_PROT_WRITE; 163 register long a3 __asm__("a3") = WASM_START_MAP_PRIVATE | WASM_START_MAP_ANON; 164 register long a4 __asm__("a4") = -1; 165 register long a5 __asm__("a5") = 0; 166 __asm__ volatile("ecall" 167 : "+r"(a0) 168 : "r"(a7), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5) 169 : "memory"); 170 if (a0 < 0 && a0 > -4096) return (void*)0; 171 return (void*)a0; 172 #else 173 #error "start_wasm.c: unsupported architecture" 174 #endif 175 } 176 177 static int start_pages_to_bytes(unsigned long long pages, 178 unsigned long long* out) { 179 unsigned long long max = ~0ull; 180 if (pages > max / WASM_START_PAGE_SIZE) return 0; 181 *out = pages * WASM_START_PAGE_SIZE; 182 return 1; 183 } 184 185 static int start_setup_dynamic_instance(void** instance_out) { 186 void* instance; 187 unsigned long long instance_size; 188 unsigned long long total_memory = 0; 189 if (!(&__kit_wasm_instance_size) || !(&__kit_wasm_nmemories)) return 0; 190 instance_size = __kit_wasm_instance_size ? __kit_wasm_instance_size : 1ull; 191 if (instance_size > WASM_START_MAX_INSTANCE_SIZE) return -1; 192 if (__kit_wasm_nmemories && !(&__kit_wasm_memory_layouts)) return -1; 193 instance = start_mmap((unsigned long)instance_size); 194 if (!instance) return -1; 195 for (unsigned int i = 0; i < __kit_wasm_nmemories; ++i) { 196 const WasmStartMemoryLayout* ml = &__kit_wasm_memory_layouts[i]; 197 WasmStartMemoryPrefix* rec; 198 unsigned long long bytes; 199 void* memory = (void*)0; 200 if (ml->max_pages < ml->min_pages) return -1; 201 if (ml->offset > instance_size || 202 instance_size - ml->offset < sizeof(WasmStartMemoryPrefix)) 203 return -1; 204 if (!start_pages_to_bytes(ml->max_pages, &bytes)) return -1; 205 if (bytes > WASM_START_MAX_TOTAL_MEMORY_SIZE || 206 total_memory > WASM_START_MAX_TOTAL_MEMORY_SIZE - bytes) 207 return -1; 208 total_memory += bytes; 209 if (bytes) { 210 memory = start_mmap((unsigned long)bytes); 211 if (!memory) return -1; 212 } 213 rec = (WasmStartMemoryPrefix*)((unsigned char*)instance + ml->offset); 214 rec->data = (unsigned char*)memory; 215 } 216 *instance_out = instance; 217 return 1; 218 } 219 220 #if defined(__x86_64__) 221 __attribute__((force_align_arg_pointer)) 222 #endif 223 void _start(void) { 224 void* instance = (void*)0; 225 int dyn = start_setup_dynamic_instance(&instance); 226 if (dyn < 0) do_exit(1); 227 if (dyn == 0) { 228 void* memory = start_mmap(WASM_START_MEMORY_SIZE); 229 instance = start_mmap(WASM_START_INSTANCE_SIZE); 230 if (!instance || !memory) do_exit(1); 231 for (unsigned int i = 0; i < WASM_START_MEMORY_PREFIX_COUNT; ++i) 232 ((WasmStartMemoryPrefix*)instance)[i].data = 233 (unsigned char*)memory + 234 i * (WASM_START_MEMORY_SIZE / WASM_START_MEMORY_PREFIX_COUNT); 235 } 236 if (!start_bind_canned_imports(instance)) do_exit(1); 237 __kit_wasm_init(instance); 238 do_exit(test_main(instance)); 239 }