From 6f3888c1f029e4990fc2bd5207d679d6ee564318 Mon Sep 17 00:00:00 2001 From: Paul Pan Date: Tue, 7 Feb 2023 23:19:28 +0800 Subject: [PATCH] feat: dl_call prototype --- main.c | 48 +++++++++++++++ tests/lib/SimpleLib.cpp | 6 ++ tests/wasm/dl.h | 1 + tests/wasm/example.c | 45 ++++++++++++-- utils/defs.h | 7 ++- wrt/dl.c | 132 ++++++++++++++++++++++++++++++---------- wrt/dl.h | 4 +- 7 files changed, 199 insertions(+), 44 deletions(-) diff --git a/main.c b/main.c index 72f6589..d70df03 100644 --- a/main.c +++ b/main.c @@ -40,9 +40,57 @@ void test_wrt() { free(buf); } +unsigned char foo(unsigned int x, float y, char z, double w) { + printf("foo: x=%u, y=%f, z=%c, w=%lf\n", x, y, z, w); + return 'P'; +} + +void test_ffi() { + ffi_cif cif; + ffi_type *arg_types[4]; + void *arg_values[4]; + ffi_status status; + + // Because the return value from foo() is smaller than sizeof(long), it + // must be passed as ffi_arg or ffi_sarg. + ffi_arg result; + + // Specify the data type of each argument. Available types are defined + // in . + arg_types[0] = &ffi_type_uint; + arg_types[1] = &ffi_type_float; + arg_types[2] = &ffi_type_schar; + arg_types[3] = &ffi_type_double; + + // Prepare the ffi_cif structure. + if ((status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 4, &ffi_type_uint8, arg_types)) != FFI_OK) { + // Handle the ffi_status error. + LOG_ERR("ffi_prep_cif failed: %d", status); + } + + // Specify the values of each argument. + unsigned int arg1 = 42; + float arg2 = 5.1f; + char arg3 = 'A'; + double arg4 = 114.1514; + + arg_values[0] = &arg1; + arg_values[1] = &arg2; + arg_values[2] = &arg3; + arg_values[3] = &arg4; + + // Invoke the function. + ffi_call(&cif, FFI_FN(foo), &result, arg_values); + + // The ffi_arg 'result' now contains the unsigned char returned from foo(), + // which can be accessed by a typecast. + printf("result is %c\n", (unsigned char)result); +} + int main() { LOG_INFO("WRT Test"); + test_ffi(); test_wrt(); return 0; diff --git a/tests/lib/SimpleLib.cpp b/tests/lib/SimpleLib.cpp index f589889..06c0e91 100644 --- a/tests/lib/SimpleLib.cpp +++ b/tests/lib/SimpleLib.cpp @@ -7,6 +7,8 @@ int fib_recurse(int n); int fib_iterate(int n); int fib_tail_call(int n); int fib_fast(int n); +int call_test(int a, long long b, float c, double d); +int call_test2(int a, long long b, int c, float d); } int fib_recurse(int n) { @@ -47,3 +49,7 @@ HIDE inline std::pair fib_fast_internal(int n) { } int fib_fast(int n) { return fib_fast_internal(n).first; } + +int call_test(int a, long long b, float c, double d) { return a + b + c + d; } + +int call_test2(int a, long long b, int c, float d) { return a + b + c + d; } diff --git a/tests/wasm/dl.h b/tests/wasm/dl.h index ffc49e3..39133b8 100644 --- a/tests/wasm/dl.h +++ b/tests/wasm/dl.h @@ -8,6 +8,7 @@ typedef enum { int dl_open(const char *filename, int flags); int dl_sym(int handle, const char *symbol, const char *signature); +Status dl_call(int symbol, ...); Status dl_close_sym(int handle, int symbol); Status dl_close(int handle); diff --git a/tests/wasm/example.c b/tests/wasm/example.c index 41fb2d4..1aad802 100644 --- a/tests/wasm/example.c +++ b/tests/wasm/example.c @@ -4,14 +4,47 @@ unsigned fib(unsigned n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); } void entry() { - // for (unsigned i = 0; i <= 35; i++) printf("fib(%2u) = %u\n", i, fib(i)); + printf("Hello WASM\n"); + + for (unsigned i = 0; i <= 5; i++) printf("fib(%u) = %u\n", i, fib(i)); int hnd = dl_open("./SimpleLib.so", 1); printf("hnd = %d\n", hnd); - int fib_iterate_sym = dl_sym(hnd, "fib_iterate", "(i)i"); - printf("fib_iterate_sym = %d\n", fib_iterate_sym); - Status status = dl_close_sym(hnd, fib_iterate_sym); - printf("close sym status = %d\n", status); - status = dl_close(hnd); + + { + int fib_iterate_sym = dl_sym(hnd, "fib_iterate", "(i)i"); + printf("fib_iterate_sym = %d\n", fib_iterate_sym); + + Status status = dl_call(fib_iterate_sym, 6); + printf("call status = %d\n", status); + + status = dl_close_sym(hnd, fib_iterate_sym); + printf("close sym status = %d\n", status); + } + + { + int call_test_sym = dl_sym(hnd, "call_test", "(ilfd)i"); + printf("call_test_sym = %d\n", call_test_sym); + + Status status = + dl_call(call_test_sym, 42, 0x12345678abcdef01, 3.1415926f, 2.718281828459045); + printf("call status = %d\n", status); + + status = dl_close_sym(hnd, call_test_sym); + printf("close sym status = %d\n", status); + } + + { + int call_test2_sym = dl_sym(hnd, "call_test2", "(ilid)i"); + printf("call_test2_sym = %d\n", call_test2_sym); + + Status status = dl_call(call_test2_sym, 42, 0x12345678abcdef01, 21, 3.1415926f); + printf("call status = %d\n", status); + + status = dl_close_sym(hnd, call_test2_sym); + printf("close sym status = %d\n", status); + } + + Status status = dl_close(hnd); printf("close lib status = %d\n", status); } diff --git a/utils/defs.h b/utils/defs.h index fc26150..ae157e1 100644 --- a/utils/defs.h +++ b/utils/defs.h @@ -9,7 +9,10 @@ typedef enum { typedef void *(*Allocator)(unsigned long); typedef void (*DeAllocator)(void *); -#define DL_MAX_HANDLES 8 -#define DL_MAX_SYMBOLS 16 +typedef char *_va_list; + +#define DL_MAX_HANDLES 8 +#define DL_MAX_SYMBOLS 16 +#define DL_SYM_TABLE_SIZE 5 #endif // WRT_DEFS_H diff --git a/wrt/dl.c b/wrt/dl.c index 8b3643f..6ceec39 100644 --- a/wrt/dl.c +++ b/wrt/dl.c @@ -29,7 +29,7 @@ Status dl_init(DLContext *ctx) { EXPORT(0, dl_open, "($i)i"); EXPORT(1, dl_sym, "(i$$)i"); - EXPORT(2, dl_call, "(i)i"); + EXPORT(2, dl_call, "(i*)i"); EXPORT(3, dl_close_sym, "(ii)i"); EXPORT(4, dl_close, "(i)i"); @@ -55,6 +55,8 @@ Status dl_free(DLContext *ctx) { } #define GET_CONTEXT \ + wasm_module_inst_t module_inst = get_module_inst(exec_env); \ + (void)module_inst; \ DLContext *ctx = wasm_runtime_get_function_attachment(exec_env); \ if (ctx == NULL) return WRT_ERROR; @@ -172,8 +174,8 @@ ffi_cif *create_ffi_cif(DLContext *ctx, const char *signature) { ch++; { // extract ret switch (*ch) { - case 'i': ret_type = &ffi_type_sint; break; - case 'l': ret_type = &ffi_type_slong; break; + case 'i': ret_type = &ffi_type_sint32; break; + case 'l': ret_type = &ffi_type_sint64; break; case 'f': ret_type = &ffi_type_float; break; case 'd': ret_type = &ffi_type_double; break; case 'p': ret_type = &ffi_type_pointer; break; // TODO: pointer to pointer @@ -231,36 +233,100 @@ int dl_sym(wasm_exec_env_t exec_env, int handle, const char *symbol, const char return WRT_ERROR; } -void dl_call(wasm_exec_env_t exec_env) { - // int sym = (int)wrt_pop(); - // if (sym < 1 || sym > DL_MAX_SYMBOLS) { - // LOG_ERR("dl_call: invalid symbol"); - // return; - // } - // - // if (dl_context.sym[sym - 1].symbol == NULL) { - // LOG_ERR("dl_call: symbol not found"); - // return; - // } - // - // ffi_cif *cif = dl_context.sym[sym - 1].cif; - // - // int arg_count = cif->nargs; - // void *args[arg_count]; - // for (int i = arg_count - 1; i >= 0; i--) { - // args[i] = (void *)wrt_pop(); - // } - // - // void *ret = NULL; - // if (cif->rtype != &ffi_type_void) { - // ret = dl_malloc(cif->rtype->size); - // } - // - // ffi_call(cif, FFI_FN(dl_context.sym[sym - 1].symbol), ret, args); - // - // if (ret != NULL) { - // wrt_push((int)ret); - // } +Status dl_call(wasm_exec_env_t exec_env, int symbol, _va_list va_args) { + if (symbol < 1 || symbol > DL_MAX_SYMBOLS) { + LOG_ERR("dl_call: invalid symbol"); + return WRT_ERROR; + } + + GET_CONTEXT + + if (ctx->sym[symbol - 1].symbol == NULL) { + LOG_ERR("dl_call: symbol not found"); + return WRT_ERROR; + } + + if (!wasm_runtime_validate_native_addr(module_inst, va_args, sizeof(uint32_t))) { + LOG_ERR("dl_call: invalid va_args"); + return WRT_ERROR; + } + + uint8_t *native_end_addr; + if (!wasm_runtime_get_native_addr_range(module_inst, (uint8_t *)va_args, NULL, + &native_end_addr)) { + LOG_ERR("dl_call: va_args out of range"); + wasm_runtime_set_exception(module_inst, "out of bounds memory access"); + return WRT_ERROR; + } + + ffi_cif *cif = ctx->sym[symbol - 1].cif; + + unsigned arg_count = cif->nargs; + void *args[arg_count]; + +#define ALIGN(n, b) (((uintptr_t)(n) + b) & (uintptr_t)~b) +#define GET_ARG(ptr, type) (*(type *)((ptr += ALIGN(sizeof(type), 3)) - ALIGN(sizeof(type), 3))) +#define CHECK_VA_ARG(ptr, type) \ + do { \ + if ((uint8_t *)ptr + ALIGN(type, 3) > native_end_addr) { \ + LOG_ERR("dl_call: va_args out of range"); \ + wasm_runtime_set_exception(module_inst, "out of bounds memory access"); \ + return WRT_ERROR; \ + } \ + } while (0) + + _va_list cur = va_args; + for (int i = 0; i < arg_count; i++) { + // TODO: check wasm abi + // only support int, long long, float, double, pointer now + // int 32, long long 32, float 64, double 64, pointer 32 + + switch (cif->arg_types[i]->type) { + case FFI_TYPE_SINT32: + case FFI_TYPE_POINTER: { + CHECK_VA_ARG(cur, int32_t); + args[i] = &GET_ARG(cur, int32_t); + LOG_DBG("dl_call: arg[%d] = %d / 0x%x", i, *(int32_t *)args[i], + *(int32_t *)args[i]); + break; + } + case FFI_TYPE_SINT64: { + cur = (_va_list)ALIGN(cur, 7); + CHECK_VA_ARG(cur, int64_t); + args[i] = &GET_ARG(cur, int64_t); + LOG_DBG("dl_call: arg[%d] = %ld / 0x%lx", i, *(int64_t *)args[i], + *(int64_t *)args[i]); + break; + } + case FFI_TYPE_FLOAT: + case FFI_TYPE_DOUBLE: { + cur = (_va_list)ALIGN(cur, 7); + CHECK_VA_ARG(cur, double); + args[i] = &GET_ARG(cur, double); + LOG_DBG("dl_call: arg[%d] = %lf", i, *(double *)args[i]); + break; + } + default: { + LOG_ERR("dl_call: unsupported type"); + return WRT_ERROR; + } + } + } + +#undef CHECK_VA_ARG +#undef GET_ARG +#undef ALIGN + + void *ret = NULL; + if (cif->rtype != &ffi_type_void) { + // TODO: return val storage, re-design dl_call + ret = ctx->mem.malloc(cif->rtype->size); + } + + ffi_call(cif, FFI_FN(ctx->sym[symbol - 1].symbol), ret, args); + + LOG_DBG("dl_call: symbol = %d, ret = 0x%x", symbol, *(unsigned *)ret); + return WRT_OK; } Status dl_close_sym(wasm_exec_env_t exec_env, int handle, int symbol) { diff --git a/wrt/dl.h b/wrt/dl.h index 8d423f8..3673751 100644 --- a/wrt/dl.h +++ b/wrt/dl.h @@ -7,8 +7,6 @@ #include "utils/defs.h" -#define DL_SYM_TABLE_SIZE 5 - typedef struct { struct { Allocator malloc; @@ -29,7 +27,7 @@ Status dl_init(DLContext *context); Status dl_free(DLContext *context); int dl_open(wasm_exec_env_t exec_env, const char *filename, int flags); int dl_sym(wasm_exec_env_t exec_env, int handle, const char *symbol, const char *signature); -void dl_call(wasm_exec_env_t exec_env); // TODO +Status dl_call(wasm_exec_env_t exec_env, int symbol, _va_list va_args); // TODO Status dl_close_sym(wasm_exec_env_t exec_env, int handle, int symbol); Status dl_close(wasm_exec_env_t exec_env, int handle);