1 /*
2 * Copyright (C) Andrew Clayton
3 * Copyright (C) F5, Inc.
4 */
5
6 #include <stdio.h>
7 #include <stdbool.h>
8 #include <stdarg.h>
9
10 #include <wasm.h>
11 #include <wasi.h>
12 #include <wasmtime.h>
13
14 #include "nxt_wasm.h"
15
16
17 typedef struct nxt_wasmtime_ctx_s nxt_wasmtime_ctx_t;
18
19 struct nxt_wasmtime_ctx_s {
20 wasm_engine_t *engine;
21 wasmtime_store_t *store;
22 wasmtime_memory_t memory;
23 wasmtime_module_t *module;
24 wasmtime_linker_t *linker;
25 wasmtime_context_t *ctx;
26 };
27
28 static nxt_wasmtime_ctx_t nxt_wasmtime_ctx;
29
30
31 static void
nxt_wasmtime_err_msg(wasmtime_error_t * error,wasm_trap_t * trap,const char * fmt,...)32 nxt_wasmtime_err_msg(wasmtime_error_t *error, wasm_trap_t *trap,
33 const char *fmt, ...)
34 {
35 va_list args;
36 wasm_byte_vec_t error_message;
37
38 fprintf(stderr, "WASMTIME ERROR: ");
39 va_start(args, fmt);
40 vfprintf(stderr, fmt, args);
41 va_end(args);
42 fprintf(stderr, "\n");
43
44 if (error == NULL && trap == NULL) {
45 return;
46 }
47
48 if (error != NULL) {
49 wasmtime_error_message(error, &error_message);
50 wasmtime_error_delete(error);
51 } else {
52 wasm_trap_message(trap, &error_message);
53 wasm_trap_delete(trap);
54 }
55 fprintf(stderr, "%.*s\n", (int)error_message.size, error_message.data);
56
57 wasm_byte_vec_delete(&error_message);
58 }
59
60
61 static wasm_trap_t *
nxt_wasm_get_init_mem_size(void * env,wasmtime_caller_t * caller,const wasmtime_val_t * args,size_t nargs,wasmtime_val_t * results,size_t nresults)62 nxt_wasm_get_init_mem_size(void *env, wasmtime_caller_t *caller,
63 const wasmtime_val_t *args, size_t nargs,
64 wasmtime_val_t *results, size_t nresults)
65 {
66 results[0].of.i32 = NXT_WASM_MEM_SIZE;
67
68 return NULL;
69 }
70
71
72 static wasm_trap_t *
nxt_wasm_response_end(void * env,wasmtime_caller_t * caller,const wasmtime_val_t * args,size_t nargs,wasmtime_val_t * results,size_t nresults)73 nxt_wasm_response_end(void *env, wasmtime_caller_t *caller,
74 const wasmtime_val_t *args, size_t nargs,
75 wasmtime_val_t *results, size_t nresults)
76 {
77 nxt_wasm_do_response_end(env);
78
79 return NULL;
80 }
81
82
83 static wasm_trap_t *
nxt_wasm_send_response(void * env,wasmtime_caller_t * caller,const wasmtime_val_t * args,size_t nargs,wasmtime_val_t * results,size_t nresults)84 nxt_wasm_send_response(void *env, wasmtime_caller_t *caller,
85 const wasmtime_val_t *args, size_t nargs,
86 wasmtime_val_t *results, size_t nresults)
87 {
88 nxt_wasm_do_send_response(env, args[0].of.i32);
89
90 return NULL;
91 }
92
93
94 static wasm_trap_t *
nxt_wasm_send_headers(void * env,wasmtime_caller_t * caller,const wasmtime_val_t * args,size_t nargs,wasmtime_val_t * results,size_t nresults)95 nxt_wasm_send_headers(void *env, wasmtime_caller_t *caller,
96 const wasmtime_val_t *args, size_t nargs,
97 wasmtime_val_t *results, size_t nresults)
98 {
99 nxt_wasm_do_send_headers(env, args[0].of.i32);
100
101 return NULL;
102 }
103
104
105 static wasm_trap_t *
nxt_wasm_set_resp_status(void * env,wasmtime_caller_t * caller,const wasmtime_val_t * args,size_t nargs,wasmtime_val_t * results,size_t nresults)106 nxt_wasm_set_resp_status(void *env, wasmtime_caller_t *caller,
107 const wasmtime_val_t *args, size_t nargs,
108 wasmtime_val_t *results, size_t nresults)
109 {
110 nxt_wasm_ctx_t *ctx = env;
111
112 ctx->status = args[0].of.i32;
113
114 return NULL;
115 }
116
117
118 static void
nxt_wasmtime_execute_hook(const nxt_wasm_ctx_t * ctx,nxt_wasm_fh_t hook)119 nxt_wasmtime_execute_hook(const nxt_wasm_ctx_t *ctx, nxt_wasm_fh_t hook)
120 {
121 const char *name = ctx->fh[hook].func_name;
122 wasm_trap_t *trap = NULL;
123 wasmtime_error_t *error;
124 nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
125 const nxt_wasm_func_t *func = &ctx->fh[hook].func;
126
127 if (name == NULL) {
128 return;
129 }
130
131 error = wasmtime_func_call(rt_ctx->ctx, func, NULL, 0, NULL, 0, &trap);
132 if (error != NULL || trap != NULL) {
133 nxt_wasmtime_err_msg(error, trap, "failed to call hook function [%s]",
134 name);
135 }
136 }
137
138
139 static int
nxt_wasmtime_execute_request(const nxt_wasm_ctx_t * ctx)140 nxt_wasmtime_execute_request(const nxt_wasm_ctx_t *ctx)
141 {
142 int i = 0;
143 wasm_trap_t *trap = NULL;
144 wasmtime_val_t args[1] = { };
145 wasmtime_val_t results[1] = { };
146 wasmtime_error_t *error;
147 nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
148 const nxt_wasm_func_t *func = &ctx->fh[NXT_WASM_FH_REQUEST].func;
149
150 args[i].kind = WASMTIME_I32;
151 args[i++].of.i32 = ctx->baddr_off;
152
153 error = wasmtime_func_call(rt_ctx->ctx, func, args, i, results, 1, &trap);
154 if (error != NULL || trap != NULL) {
155 nxt_wasmtime_err_msg(error, trap,
156 "failed to call function [->wasm_request_handler]"
157 );
158 return -1;
159 }
160
161 return results[0].of.i32;
162 }
163
164
165 static void
nxt_wasmtime_set_function_imports(nxt_wasm_ctx_t * ctx)166 nxt_wasmtime_set_function_imports(nxt_wasm_ctx_t *ctx)
167 {
168 nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
169
170 static const struct {
171 const char *func_name;
172
173 wasmtime_func_callback_t func;
174 wasm_valkind_t params[1];
175 wasm_valkind_t results[1];
176
177 enum {
178 NXT_WASM_FT_0_0,
179 NXT_WASM_FT_1_0,
180 NXT_WASM_FT_0_1,
181 } ft;
182 } import_functions[] = {
183 {
184 .func_name = "nxt_wasm_get_init_mem_size",
185 .func = nxt_wasm_get_init_mem_size,
186 .results = { WASM_I32 },
187 .ft = NXT_WASM_FT_0_1
188 }, {
189 .func_name = "nxt_wasm_response_end",
190 .func = nxt_wasm_response_end,
191 .ft = NXT_WASM_FT_0_0
192 }, {
193 .func_name = "nxt_wasm_send_response",
194 .func = nxt_wasm_send_response,
195 .params = { WASM_I32 },
196 .ft = NXT_WASM_FT_1_0
197 }, {
198 .func_name = "nxt_wasm_send_headers",
199 .func = nxt_wasm_send_headers,
200 .params = { WASM_I32 },
201 .ft = NXT_WASM_FT_1_0
202 }, {
203 .func_name = "nxt_wasm_set_resp_status",
204 .func = nxt_wasm_set_resp_status,
205 .params = { WASM_I32 },
206 .ft = NXT_WASM_FT_1_0
207 },
208
209 { }
210 }, *imf;
211
212 for (imf = import_functions; imf->func_name != NULL; imf++) {
213 wasm_functype_t *func_ty;
214
215 switch (imf->ft) {
216 case NXT_WASM_FT_0_0:
217 func_ty = wasm_functype_new_0_0();
218 break;
219 case NXT_WASM_FT_1_0:
220 func_ty = wasm_functype_new_1_0(wasm_valtype_new(imf->params[0]));
221 break;
222 case NXT_WASM_FT_0_1:
223 func_ty = wasm_functype_new_0_1(wasm_valtype_new(imf->results[0]));
224 break;
225 default:
226 /* Stop GCC complaining about func_ty being used uninitialised */
227 func_ty = NULL;
228 }
229
230 wasmtime_linker_define_func(rt_ctx->linker, "env", 3,
231 imf->func_name, strlen(imf->func_name),
232 func_ty, imf->func, ctx, NULL);
233 wasm_functype_delete(func_ty);
234 }
235 }
236
237
238 static int
nxt_wasmtime_get_function_exports(nxt_wasm_ctx_t * ctx)239 nxt_wasmtime_get_function_exports(nxt_wasm_ctx_t *ctx)
240 {
241 int i;
242 nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
243
244 for (i = 0; i < NXT_WASM_FH_NR; i++) {
245 bool ok;
246 wasmtime_extern_t item;
247
248 if (ctx->fh[i].func_name == NULL) {
249 continue;
250 }
251
252 ok = wasmtime_linker_get(rt_ctx->linker, rt_ctx->ctx, "", 0,
253 ctx->fh[i].func_name,
254 strlen(ctx->fh[i].func_name), &item);
255 if (!ok) {
256 nxt_wasmtime_err_msg(NULL, NULL,
257 "couldn't get (%s) export from module",
258 ctx->fh[i].func_name);
259 return -1;
260 }
261 ctx->fh[i].func = item.of.func;
262 }
263
264 return 0;
265 }
266
267
268 static int
nxt_wasmtime_wasi_init(const nxt_wasm_ctx_t * ctx)269 nxt_wasmtime_wasi_init(const nxt_wasm_ctx_t *ctx)
270 {
271 char **dir;
272 wasi_config_t *wasi_config;
273 wasmtime_error_t *error;
274 nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
275
276 wasi_config = wasi_config_new();
277
278 wasi_config_inherit_env(wasi_config);
279 wasi_config_inherit_stdin(wasi_config);
280 wasi_config_inherit_stdout(wasi_config);
281 wasi_config_inherit_stderr(wasi_config);
282
283 for (dir = ctx->dirs; dir != NULL && *dir != NULL; dir++) {
284 wasi_config_preopen_dir(wasi_config, *dir, *dir);
285 }
286
287 error = wasmtime_context_set_wasi(rt_ctx->ctx, wasi_config);
288 if (error != NULL) {
289 nxt_wasmtime_err_msg(error, NULL, "failed to instantiate WASI");
290 return -1;
291 }
292
293 return 0;
294 }
295
296
297 static int
nxt_wasmtime_init_memory(nxt_wasm_ctx_t * ctx)298 nxt_wasmtime_init_memory(nxt_wasm_ctx_t *ctx)
299 {
300 int i = 0;
301 bool ok;
302 wasm_trap_t *trap = NULL;
303 wasmtime_val_t args[1] = { };
304 wasmtime_val_t results[1] = { };
305 wasmtime_error_t *error;
306 wasmtime_extern_t item;
307 nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
308 const nxt_wasm_func_t *func = &ctx->fh[NXT_WASM_FH_MALLOC].func;
309
310 args[i].kind = WASMTIME_I32;
311 args[i++].of.i32 = NXT_WASM_MEM_SIZE + NXT_WASM_PAGE_SIZE;
312
313 error = wasmtime_func_call(rt_ctx->ctx, func, args, i, results, 1, &trap);
314 if (error != NULL || trap != NULL) {
315 nxt_wasmtime_err_msg(error, trap,
316 "failed to call function [->wasm_malloc_handler]"
317 );
318 return -1;
319 }
320
321 ok = wasmtime_linker_get(rt_ctx->linker, rt_ctx->ctx, "", 0, "memory",
322 strlen("memory"), &item);
323 if (!ok) {
324 nxt_wasmtime_err_msg(NULL, NULL, "couldn't get 'memory' from module\n");
325 return -1;
326 }
327 rt_ctx->memory = item.of.memory;
328
329 ctx->baddr_off = results[0].of.i32;
330 ctx->baddr = wasmtime_memory_data(rt_ctx->ctx, &rt_ctx->memory);
331
332 ctx->baddr += ctx->baddr_off;
333
334 return 0;
335 }
336
337
338 static int
nxt_wasmtime_init(nxt_wasm_ctx_t * ctx)339 nxt_wasmtime_init(nxt_wasm_ctx_t *ctx)
340 {
341 int err;
342 FILE *fp;
343 size_t file_size;
344 wasm_byte_vec_t wasm;
345 wasmtime_error_t *error;
346 nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
347
348 rt_ctx->engine = wasm_engine_new();
349 rt_ctx->store = wasmtime_store_new(rt_ctx->engine, NULL, NULL);
350 rt_ctx->ctx = wasmtime_store_context(rt_ctx->store);
351
352 rt_ctx->linker = wasmtime_linker_new(rt_ctx->engine);
353 error = wasmtime_linker_define_wasi(rt_ctx->linker);
354 if (error != NULL) {
355 nxt_wasmtime_err_msg(error, NULL, "failed to link wasi");
356 return -1;
357 }
358
359 fp = fopen(ctx->module_path, "r");
360 if (!fp) {
361 nxt_wasmtime_err_msg(NULL, NULL,
362 "error opening file (%s)", ctx->module_path);
363 return -1;
364 }
365 fseek(fp, 0L, SEEK_END);
366 file_size = ftell(fp);
367 wasm_byte_vec_new_uninitialized(&wasm, file_size);
368 fseek(fp, 0L, SEEK_SET);
369 if (fread(wasm.data, file_size, 1, fp) != 1) {
370 nxt_wasmtime_err_msg(NULL, NULL, "error loading module");
371 fclose(fp);
372 return -1;
373 }
374 fclose(fp);
375
376 error = wasmtime_module_new(rt_ctx->engine, (uint8_t *)wasm.data, wasm.size,
377 &rt_ctx->module);
378 if (!rt_ctx->module) {
379 nxt_wasmtime_err_msg(error, NULL, "failed to compile module");
380 return -1;
381 }
382 wasm_byte_vec_delete(&wasm);
383
384 nxt_wasmtime_set_function_imports(ctx);
385
386 nxt_wasmtime_wasi_init(ctx);
387
388 error = wasmtime_linker_module(rt_ctx->linker, rt_ctx->ctx, "", 0,
389 rt_ctx->module);
390 if (error != NULL) {
391 nxt_wasmtime_err_msg(error, NULL, "failed to instantiate");
392 return -1;
393 }
394
395 err = nxt_wasmtime_get_function_exports(ctx);
396 if (err) {
397 return -1;
398 }
399
400 err = nxt_wasmtime_init_memory(ctx);
401 if (err) {
402 return -1;
403 }
404
405 return 0;
406 }
407
408
409 static void
nxt_wasmtime_destroy(const nxt_wasm_ctx_t * ctx)410 nxt_wasmtime_destroy(const nxt_wasm_ctx_t *ctx)
411 {
412 int i = 0;
413 wasmtime_val_t args[1] = { };
414 nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
415 const nxt_wasm_func_t *func = &ctx->fh[NXT_WASM_FH_FREE].func;
416
417 args[i].kind = WASMTIME_I32;
418 args[i++].of.i32 = ctx->baddr_off;
419
420 wasmtime_func_call(rt_ctx->ctx, func, args, i, NULL, 0, NULL);
421
422 wasmtime_module_delete(rt_ctx->module);
423 wasmtime_store_delete(rt_ctx->store);
424 wasm_engine_delete(rt_ctx->engine);
425 }
426
427
428 const nxt_wasm_operations_t nxt_wasm_ops = {
429 .init = nxt_wasmtime_init,
430 .destroy = nxt_wasmtime_destroy,
431 .exec_request = nxt_wasmtime_execute_request,
432 .exec_hook = nxt_wasmtime_execute_hook,
433 };
434