xref: /unit/src/wasm/nxt_rt_wasmtime.c (revision 2572:9e0c72fe7acd)
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