feat: now gets a functional dl_call
1. update examples 2. implements dl_call 3. add documentations
This commit is contained in:
parent
a471519ff7
commit
5c31d4e7ba
@ -1,15 +1,16 @@
|
|||||||
|
#include <iostream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#define HIDE __attribute__((visibility("hidden")))
|
#define HIDE __attribute__((visibility("hidden")))
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
int fib_recurse(int n);
|
int fib_recurse(int n);
|
||||||
int fib_iterate(int n);
|
int fib_iterate(int n);
|
||||||
int fib_tail_call(int n);
|
int fib_tail_call(int n);
|
||||||
int fib_fast(int n);
|
int fib_fast(int n);
|
||||||
int call_test(int a, long long b, float c, double d);
|
float call_test(int a, long long b, float c, double d);
|
||||||
int call_test2(int a, long long b, int c, float d);
|
double call_test2(int a, long long b, int c, float d);
|
||||||
int my_cpy(char *dest, char *src, int len);
|
int my_cpy(char *dest, char *src, int len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int fib_recurse(int n) {
|
int fib_recurse(int n) {
|
||||||
@ -51,15 +52,28 @@ HIDE inline std::pair<int, int> fib_fast_internal(int n) {
|
|||||||
|
|
||||||
int fib_fast(int n) { return fib_fast_internal(n).first; }
|
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; }
|
float call_test(int a, long long b, float c, double d) {
|
||||||
|
float ans = a + b + c + d;
|
||||||
|
std::cout << "SimpleLib.cpp: call_test(" << a << ", " << b << ", " << c << ", " << d
|
||||||
|
<< ") = " << ans << std::endl;
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
int call_test2(int a, long long b, int c, float d) { return a + b + c + d; }
|
double call_test2(int a, long long b, int c, float d) {
|
||||||
|
double ans = a + b + c + d;
|
||||||
|
std::cout << "SimpleLib.cpp: call_test2(" << a << ", " << b << ", " << c << ", " << d
|
||||||
|
<< ") = " << ans << std::endl;
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
int my_cpy(char *dest, char *src, int len) {
|
int my_cpy(char *dest, char *src, int len) {
|
||||||
|
printf("SimpleLib.cpp: my_cpy(dest=%p, src=%p)\n", dest, src);
|
||||||
char seed = 0x42;
|
char seed = 0x42;
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
dest[i] = src[i];
|
dest[i] = src[i];
|
||||||
seed ^= src[i];
|
seed ^= src[i];
|
||||||
}
|
}
|
||||||
|
std::cout << "SimpleLib.cpp: my_cpy(\"" << dest << "\", \"" << src << "\", " << len
|
||||||
|
<< ") = " << (int)seed << std::endl;
|
||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
#ifndef WRT_DL_H
|
#ifndef WRT_DL_H
|
||||||
#define WRT_DL_H
|
#define WRT_DL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
WRT_OK = 0,
|
WRT_OK = 0,
|
||||||
WRT_ERROR = -1,
|
WRT_ERROR = -1,
|
||||||
} Status;
|
} Status;
|
||||||
|
|
||||||
int dl_open(const char *filename, int flags);
|
int dl_open(const char *filename, int flags);
|
||||||
int dl_sym(int handle, const char *symbol, const char *signature);
|
int dl_sym(int handle, const char *symbol, const char *signature);
|
||||||
void *dl_call(int symbol, ...);
|
int64_t dl_call(int symbol, ...);
|
||||||
Status dl_close_sym(int handle, int symbol);
|
Status dl_close_sym(int symbol);
|
||||||
Status dl_close(int handle);
|
Status dl_close(int handle);
|
||||||
|
|
||||||
#endif // WRT_DL_H
|
#endif // WRT_DL_H
|
||||||
|
@ -4,62 +4,49 @@
|
|||||||
unsigned fib(unsigned n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); }
|
unsigned fib(unsigned n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); }
|
||||||
|
|
||||||
void dl_test() {
|
void dl_test() {
|
||||||
int hnd, fib_iterate_sym, call_test_sym, call_test2_sym, my_cpy_sym;
|
int hnd, fib_iterate_sym, call_test_sym, call_test2_sym, my_cpy_sym;
|
||||||
Status status;
|
int64_t result;
|
||||||
void *result;
|
|
||||||
|
|
||||||
{ // Load library
|
{ // Load library
|
||||||
hnd = dl_open("./SimpleLib.so", 1);
|
hnd = dl_open("./SimpleLib.so", 1);
|
||||||
printf("hnd = %d\n", hnd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // Load symbols
|
{ // Load symbols
|
||||||
fib_iterate_sym = dl_sym(hnd, "fib_iterate", "(i)i");
|
fib_iterate_sym = dl_sym(hnd, "fib_iterate", "(i)i");
|
||||||
printf("fib_iterate_sym = %d\n", fib_iterate_sym);
|
call_test_sym = dl_sym(hnd, "call_test", "(ilfd)f");
|
||||||
|
call_test2_sym = dl_sym(hnd, "call_test2", "(ilid)d");
|
||||||
call_test_sym = dl_sym(hnd, "call_test", "(ilfd)i");
|
my_cpy_sym = dl_sym(hnd, "my_cpy", "(ppi)i");
|
||||||
printf("call_test_sym = %d\n", call_test_sym);
|
|
||||||
|
|
||||||
call_test2_sym = dl_sym(hnd, "call_test2", "(ilid)i");
|
|
||||||
printf("call_test2_sym = %d\n", call_test2_sym);
|
|
||||||
|
|
||||||
my_cpy_sym = dl_sym(hnd, "my_cpy", "(ppi)i");
|
|
||||||
printf("my_cpy_sym = %d\n", my_cpy_sym);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // Call function
|
{ // Call function
|
||||||
result = dl_call(fib_iterate_sym, 6);
|
result = dl_call(fib_iterate_sym, 6);
|
||||||
printf("fib_iterate(6) = %d\n", (int)result);
|
printf("fib_iterate(6) = %lld\n", result);
|
||||||
|
|
||||||
result = dl_call(call_test_sym, 42, 0x12345678abcdef01, 3.1415926f, 2.718281828459045);
|
float pi = 3.1415926f;
|
||||||
printf("call_test_sym = %d\n", (int)result);
|
double e = 2.718281828459045;
|
||||||
|
long long ll = 0x12345678abcdef01;
|
||||||
|
|
||||||
result = dl_call(call_test2_sym, 42, 0x12345678abcdef01, 21, 3.1415926f);
|
result = dl_call(call_test_sym, 42, ll, pi, e);
|
||||||
printf("call_test2_sym = %d\n", (int)result);
|
printf("call_test(...) = %f\n", (float)result);
|
||||||
|
|
||||||
|
result = dl_call(call_test2_sym, 42, 21, 10, pi);
|
||||||
|
printf("call_test2(...) = %lf\n", (double)result);
|
||||||
|
|
||||||
volatile char src[16] = "Hello, World!";
|
volatile char src[16] = "Hello, World!";
|
||||||
volatile char dst[16] = "++++++++++++++++";
|
volatile char dst[16] = "+++++++++++++";
|
||||||
result = dl_call(my_cpy_sym, dst, src, 16);
|
result = dl_call(my_cpy_sym, dst, 16, src, 16, 16);
|
||||||
printf("my_cpy = %d, src = %s , dst = %s\n", (int)result, src, dst);
|
printf("my_cpy(...) = %lld, src = %s , dst = %s\n", result, src, dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // Close symbols
|
{ // Close symbols
|
||||||
status = dl_close_sym(hnd, fib_iterate_sym);
|
dl_close_sym(fib_iterate_sym);
|
||||||
printf("close sym status = %d\n", status);
|
dl_close_sym(call_test_sym);
|
||||||
|
dl_close_sym(call_test2_sym);
|
||||||
status = dl_close_sym(hnd, call_test_sym);
|
dl_close_sym(my_cpy_sym);
|
||||||
printf("close sym status = %d\n", status);
|
|
||||||
|
|
||||||
status = dl_close_sym(hnd, call_test2_sym);
|
|
||||||
printf("close sym status = %d\n", status);
|
|
||||||
|
|
||||||
status = dl_close_sym(hnd, my_cpy_sym);
|
|
||||||
printf("close sym status = %d\n", status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // Close library
|
{ // Close library
|
||||||
status = dl_close(hnd);
|
dl_close(hnd);
|
||||||
printf("close lib status = %d\n", status);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,12 +67,12 @@ void evil_test() {
|
|||||||
// 3. valid symbol, valid args
|
// 3. valid symbol, valid args
|
||||||
dl_call(my_cpy_sym, (void *)evil_test, (void *)evil_test, 1);
|
dl_call(my_cpy_sym, (void *)evil_test, (void *)evil_test, 1);
|
||||||
|
|
||||||
dl_close_sym(hnd, my_cpy_sym);
|
dl_close_sym(my_cpy_sym);
|
||||||
dl_close(hnd);
|
dl_close(hnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void entry() {
|
void entry() {
|
||||||
printf("Hello WASM\n");
|
printf("Hello WASM\n");
|
||||||
dl_test();
|
dl_test();
|
||||||
evil_test();
|
// evil_test();
|
||||||
}
|
}
|
||||||
|
130
utils/tinymap.c
130
utils/tinymap.c
@ -1,130 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "utils/log.h"
|
|
||||||
#include "wrt/tinymap.h"
|
|
||||||
|
|
||||||
#define FOR(i) for (unsigned i = map->head[hash]; i; i = map->node[i].next)
|
|
||||||
|
|
||||||
static inline unsigned map_hash(int key) { return (key % BUK_SIZE + BUK_SIZE) % BUK_SIZE; }
|
|
||||||
|
|
||||||
void map_init(Map *map) {
|
|
||||||
map->count = 0;
|
|
||||||
memset(map->head, 0, sizeof(map->head));
|
|
||||||
memset(map->node, 0, sizeof(map->node));
|
|
||||||
}
|
|
||||||
|
|
||||||
void *map_get(Map *map, int key) {
|
|
||||||
if (key == 0) return NULL;
|
|
||||||
|
|
||||||
unsigned hash = map_hash(key);
|
|
||||||
FOR(i) {
|
|
||||||
if (map->node[i].key == key) return map->node[i].value;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool map_set(Map *map, int key, void *value) {
|
|
||||||
if (key == 0) return false;
|
|
||||||
|
|
||||||
unsigned hash = map_hash(key);
|
|
||||||
FOR(i) {
|
|
||||||
if (map->node[i].key == key) {
|
|
||||||
LOG_DBG("map_set: upd key=%d, val=0x%llx, pos=%d", key, (unsigned long long)value, i);
|
|
||||||
map->node[i].value = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map->count >= NOD_SIZE) {
|
|
||||||
LOG_DBG("map_set: nod full, compact");
|
|
||||||
map_compact(map);
|
|
||||||
if (map->count == NOD_SIZE) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
map->node[++map->count] = (MapNode){key, value, map->head[hash]};
|
|
||||||
map->head[hash] = map->count;
|
|
||||||
LOG_DBG("map_set: ins key=%d, val=0x%llx, pos=%d", key, (unsigned long long)value, map->count);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void map_delete(Map *map, int key) {
|
|
||||||
if (key == 0) return;
|
|
||||||
|
|
||||||
unsigned hash = map_hash(key);
|
|
||||||
FOR(i) {
|
|
||||||
if (map->node[i].key == key) {
|
|
||||||
LOG_DBG("map_delete: del key=%d, pos=%d", key, i);
|
|
||||||
map->node[i].key = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void map_compact(Map *map) {
|
|
||||||
LOG_DBG("map_compact: before count=%d", map->count);
|
|
||||||
|
|
||||||
{
|
|
||||||
// compact map->node
|
|
||||||
// i = last empty node, j = current scanning node
|
|
||||||
// node[0] is undefined
|
|
||||||
unsigned i = 1, j = 2;
|
|
||||||
for (; j <= NOD_SIZE;) {
|
|
||||||
if (map->node[i].key) {
|
|
||||||
i++, j++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map->node[j].key) {
|
|
||||||
LOG_DBG("map_compact: move node[%d] to node[%d]", j, i);
|
|
||||||
|
|
||||||
map->node[i] = map->node[j];
|
|
||||||
map->node[j].key = 0;
|
|
||||||
|
|
||||||
i++, j++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
|
|
||||||
map->count = i - 1;
|
|
||||||
|
|
||||||
LOG_DBG("map_compact: after count=%d", map->count);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// rebuild map->head
|
|
||||||
for (unsigned i = 0; i <= NOD_SIZE; i++) map->node[i].next = 0;
|
|
||||||
memset(map->head, 0, sizeof(map->head));
|
|
||||||
for (unsigned i = 1; i <= NOD_SIZE; i++) {
|
|
||||||
if (!map->node[i].key) break;
|
|
||||||
|
|
||||||
unsigned hash = map_hash(map->node[i].key);
|
|
||||||
|
|
||||||
if (map->head[hash] == 0) {
|
|
||||||
map->head[hash] = i;
|
|
||||||
} else {
|
|
||||||
unsigned j = map->head[hash];
|
|
||||||
map->head[hash] = i;
|
|
||||||
map->node[i].next = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void map_dbg(Map *map) {
|
|
||||||
for (int hash = 0; hash < BUK_SIZE; hash++) {
|
|
||||||
printf("[+] BUK[%2d]:\n", hash);
|
|
||||||
int spaces = 5;
|
|
||||||
FOR(i) {
|
|
||||||
for (int j = 0; j < spaces; j++) putchar(' ');
|
|
||||||
printf("└─ NOD[" __COLOR_GREEN "%2d" __COLOR_RESET "]: key=" __COLOR_YELLOW
|
|
||||||
"%d" __COLOR_RESET ", val=" __COLOR_CYAN "0x%llx" __COLOR_RESET
|
|
||||||
", nxt=" __COLOR_RED "%d" __COLOR_RESET "\n",
|
|
||||||
i, map->node[i].key, (unsigned long long)map->node[i].value, map->node[i].next);
|
|
||||||
spaces += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
#ifndef WRT_TINYMAP_H
|
|
||||||
#define WRT_TINYMAP_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#ifndef BUK_SIZE
|
|
||||||
#define BUK_SIZE 8
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NOD_SIZE
|
|
||||||
#define NOD_SIZE 32
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int key;
|
|
||||||
void *value;
|
|
||||||
unsigned next;
|
|
||||||
} MapNode;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
MapNode node[NOD_SIZE + 1];
|
|
||||||
unsigned head[BUK_SIZE];
|
|
||||||
unsigned count;
|
|
||||||
} Map;
|
|
||||||
|
|
||||||
void map_init(Map *map);
|
|
||||||
void *map_get(Map *map, int key);
|
|
||||||
bool map_set(Map *map, int key, void *value);
|
|
||||||
void map_delete(Map *map, int key);
|
|
||||||
void map_compact(Map *map);
|
|
||||||
|
|
||||||
#endif // WRT_TINYMAP_H
|
|
140
wrt/lib/dl.c
140
wrt/lib/dl.c
@ -1,11 +1,25 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
#include <alloca.h>
|
||||||
|
|
||||||
#include "utils/log.h"
|
#include "utils/log.h"
|
||||||
#include "wrt/lib/dl.h"
|
#include "wrt/lib/dl.h"
|
||||||
|
|
||||||
#warning "TODO: Permission Check"
|
#warning "TODO: Permission Check"
|
||||||
|
|
||||||
|
#define LOG_AND_QUIT(msg, ret) \
|
||||||
|
do { \
|
||||||
|
LOG_ERR(msg); \
|
||||||
|
wasm_runtime_set_exception(module_inst, msg); \
|
||||||
|
return ret; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define GET_CONTEXT(ret) \
|
||||||
|
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) LOG_AND_QUIT("dl_get_context: NULL", ret);
|
||||||
|
|
||||||
Status dl_init(DLContext *ctx) {
|
Status dl_init(DLContext *ctx) {
|
||||||
memset(ctx->hnd, 0, sizeof(ctx->hnd));
|
memset(ctx->hnd, 0, sizeof(ctx->hnd));
|
||||||
memset(ctx->sym, 0, sizeof(ctx->sym));
|
memset(ctx->sym, 0, sizeof(ctx->sym));
|
||||||
@ -26,8 +40,8 @@ Status dl_init(DLContext *ctx) {
|
|||||||
|
|
||||||
EXPORT(0, dl_open, "($i)i");
|
EXPORT(0, dl_open, "($i)i");
|
||||||
EXPORT(1, dl_sym, "(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(3, dl_close_sym, "(i)i");
|
||||||
EXPORT(4, dl_close, "(i)i");
|
EXPORT(4, dl_close, "(i)i");
|
||||||
|
|
||||||
#undef EXPORT
|
#undef EXPORT
|
||||||
@ -51,12 +65,6 @@ Status dl_free(DLContext *ctx) {
|
|||||||
return WRT_OK;
|
return WRT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define GET_CONTEXT(ret) \
|
|
||||||
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 ret;
|
|
||||||
|
|
||||||
int dl_open(wasm_exec_env_t exec_env, const char *filename, int flags) {
|
int dl_open(wasm_exec_env_t exec_env, const char *filename, int flags) {
|
||||||
if (filename == NULL) return WRT_ERROR;
|
if (filename == NULL) return WRT_ERROR;
|
||||||
if (filename[0] == '\0') return WRT_ERROR;
|
if (filename[0] == '\0') return WRT_ERROR;
|
||||||
@ -82,9 +90,10 @@ int dl_open(wasm_exec_env_t exec_env, const char *filename, int flags) {
|
|||||||
return WRT_ERROR;
|
return WRT_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: integrate it into create_ffi_cif
|
||||||
bool signature_check(const char *signature, int *arg_count, bool *has_ret) {
|
bool signature_check(const char *signature, int *arg_count, bool *has_ret) {
|
||||||
const static char allowed_chars[] = {'i', 'l', 'f', 'd', 'p'};
|
const char allowed_chars[] = {'i', 'l', 'f', 'd', 'p'};
|
||||||
const static int allowed_chars_len = sizeof(allowed_chars) / sizeof(char);
|
const int allowed_chars_len = sizeof(allowed_chars) / sizeof(char);
|
||||||
|
|
||||||
// tmpl: '(' ch* ')' ch?
|
// tmpl: '(' ch* ')' ch?
|
||||||
|
|
||||||
@ -230,50 +239,47 @@ int dl_sym(wasm_exec_env_t exec_env, int handle, const char *symbol, const char
|
|||||||
return WRT_ERROR;
|
return WRT_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *dl_call(wasm_exec_env_t exec_env, int symbol, _va_list va_args) {
|
int64_t dl_call(wasm_exec_env_t exec_env, int symbol, _va_list va_args) {
|
||||||
GET_CONTEXT(NULL)
|
GET_CONTEXT(WRT_ERROR)
|
||||||
|
|
||||||
#define LOG_AND_QUIT(msg) \
|
|
||||||
do { \
|
|
||||||
LOG_ERR(msg); \
|
|
||||||
wasm_runtime_set_exception(module_inst, msg); \
|
|
||||||
return NULL; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
if (symbol < 1 || symbol > DL_MAX_SYMBOLS) {
|
if (symbol < 1 || symbol > DL_MAX_SYMBOLS) {
|
||||||
LOG_AND_QUIT("dl_call: invalid symbol");
|
LOG_AND_QUIT("dl_call: invalid symbol", WRT_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->sym[symbol - 1].symbol == NULL) {
|
if (ctx->sym[symbol - 1].symbol == NULL) {
|
||||||
LOG_AND_QUIT("dl_call: symbol not found");
|
LOG_AND_QUIT("dl_call: symbol not found", WRT_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wasm_runtime_validate_native_addr(module_inst, va_args, sizeof(uint32_t))) {
|
if (!wasm_runtime_validate_native_addr(module_inst, va_args, sizeof(uint32_t))) {
|
||||||
LOG_AND_QUIT("dl_call: invalid va_args");
|
LOG_AND_QUIT("dl_call: invalid va_args", WRT_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *native_end_addr;
|
uint8_t *native_end_addr;
|
||||||
if (!wasm_runtime_get_native_addr_range(module_inst, (uint8_t *)va_args, NULL,
|
if (!wasm_runtime_get_native_addr_range(module_inst, (uint8_t *)va_args, NULL,
|
||||||
&native_end_addr)) {
|
&native_end_addr)) {
|
||||||
LOG_AND_QUIT("dl_call: va_args out of bounds");
|
LOG_AND_QUIT("dl_call: va_args out of bounds", WRT_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi_cif *cif = ctx->sym[symbol - 1].cif;
|
ffi_cif *cif = ctx->sym[symbol - 1].cif;
|
||||||
|
unsigned arg_count = cif->nargs, ext_ptr_count = 0, ext_float_count = 0;
|
||||||
unsigned arg_count = cif->nargs;
|
for (int i = 0; i < arg_count; i++) {
|
||||||
void *args[arg_count];
|
if (cif->arg_types[i]->type == FFI_TYPE_POINTER) ext_ptr_count++;
|
||||||
void *tmp_buffer[arg_count]; // TODO: tmp_buffer only used for ptr args, which wastes memory
|
if (cif->arg_types[i]->type == FFI_TYPE_FLOAT) ext_float_count++;
|
||||||
|
}
|
||||||
|
|
||||||
#define ALIGN(n, b) (((uintptr_t)(n) + b) & (uintptr_t)~b)
|
#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 GET_ARG(ptr, type) (*(type *)((ptr += ALIGN(sizeof(type), 3)) - ALIGN(sizeof(type), 3)))
|
||||||
#define CHECK_VA_ARG(ptr, type) \
|
#define CHECK_VA_ARG(ptr, type) \
|
||||||
do { \
|
do { \
|
||||||
if ((uint8_t *)ptr + ALIGN(type, 3) > native_end_addr) { \
|
if ((uint8_t *)ptr + ALIGN(type, 3) > native_end_addr) { \
|
||||||
LOG_AND_QUIT("dl_call: func args out of bounds"); \
|
LOG_AND_QUIT("dl_call: func args out of bounds", WRT_ERROR); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
_va_list cur = va_args;
|
void **args = alloca(sizeof(void *) * arg_count);
|
||||||
|
void **ext_ptrs = alloca(sizeof(void *) * ext_ptr_count);
|
||||||
|
float *ext_floats = alloca(sizeof(float *) * ext_float_count);
|
||||||
|
_va_list cur = va_args;
|
||||||
for (int i = 0; i < arg_count; i++) {
|
for (int i = 0; i < arg_count; i++) {
|
||||||
// TODO: check wasm abi
|
// TODO: check wasm abi
|
||||||
// only support int, long long, float, double, pointer now
|
// only support int, long long, float, double, pointer now
|
||||||
@ -288,6 +294,8 @@ void *dl_call(wasm_exec_env_t exec_env, int symbol, _va_list va_args) {
|
|||||||
case FFI_TYPE_POINTER: { // 32 bit
|
case FFI_TYPE_POINTER: { // 32 bit
|
||||||
CHECK_VA_ARG(cur, int32_t);
|
CHECK_VA_ARG(cur, int32_t);
|
||||||
uint32_t ptr = GET_ARG(cur, int32_t);
|
uint32_t ptr = GET_ARG(cur, int32_t);
|
||||||
|
CHECK_VA_ARG(cur, int32_t);
|
||||||
|
uint32_t len = GET_ARG(cur, int32_t);
|
||||||
|
|
||||||
/* TODO: Security Warning !!
|
/* TODO: Security Warning !!
|
||||||
* - 3rd-party libraries could access native memory directly, which means a buffer
|
* - 3rd-party libraries could access native memory directly, which means a buffer
|
||||||
@ -298,12 +306,14 @@ void *dl_call(wasm_exec_env_t exec_env, int symbol, _va_list va_args) {
|
|||||||
* force memory alignment)
|
* force memory alignment)
|
||||||
* - On embedded devices: maybe disable this feature? or embedded native symbols?
|
* - On embedded devices: maybe disable this feature? or embedded native symbols?
|
||||||
*/
|
*/
|
||||||
if (!wasm_runtime_validate_app_addr(module_inst, ptr, 0)) {
|
if (!wasm_runtime_validate_app_addr(module_inst, ptr, len)) {
|
||||||
LOG_AND_QUIT("dl_call: invalid ptr");
|
LOG_AND_QUIT("dl_call: invalid ptr", WRT_ERROR);
|
||||||
}
|
}
|
||||||
tmp_buffer[i] = wasm_runtime_addr_app_to_native(module_inst, ptr);
|
*ext_ptrs = wasm_runtime_addr_app_to_native(module_inst, ptr);
|
||||||
args[i] = &tmp_buffer[i];
|
args[i] = ext_ptrs;
|
||||||
LOG_WARN("dl_call: ptr arg[%d] = 0x%lx", i, (uintptr_t)tmp_buffer[i]);
|
LOG_WARN("dl_call: ptr arg[%d] = 0x%lx, buf = %p", i, (uintptr_t)*ext_ptrs,
|
||||||
|
ext_ptrs);
|
||||||
|
ext_ptrs++;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -313,53 +323,55 @@ void *dl_call(wasm_exec_env_t exec_env, int symbol, _va_list va_args) {
|
|||||||
args[i] = &GET_ARG(cur, int64_t);
|
args[i] = &GET_ARG(cur, int64_t);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FFI_TYPE_FLOAT:
|
case FFI_TYPE_FLOAT: { // 64 bit in WASM
|
||||||
case FFI_TYPE_DOUBLE: { // 64 bit: FLOAT and DOUBLE are the same in WASM
|
cur = (_va_list)ALIGN(cur, 7);
|
||||||
|
CHECK_VA_ARG(cur, double);
|
||||||
|
*ext_floats = (float)GET_ARG(cur, double);
|
||||||
|
args[i] = ext_floats;
|
||||||
|
ext_floats++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FFI_TYPE_DOUBLE: { // 64 bit
|
||||||
cur = (_va_list)ALIGN(cur, 7);
|
cur = (_va_list)ALIGN(cur, 7);
|
||||||
CHECK_VA_ARG(cur, double);
|
CHECK_VA_ARG(cur, double);
|
||||||
args[i] = &GET_ARG(cur, double);
|
args[i] = &GET_ARG(cur, double);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
LOG_AND_QUIT("dl_call: unsupported type");
|
LOG_AND_QUIT("dl_call: unsupported type", WRT_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef CHECK_VA_ARG
|
||||||
|
#undef GET_ARG
|
||||||
|
#undef ALIGN
|
||||||
|
|
||||||
void *ret = NULL;
|
void *ret = NULL;
|
||||||
if (cif->rtype->type != FFI_TYPE_VOID) {
|
if (cif->rtype->type != FFI_TYPE_VOID) {
|
||||||
unsigned size = cif->rtype->size;
|
unsigned size = cif->rtype->size;
|
||||||
if (cif->rtype->type == FFI_TYPE_FLOAT) size = (&ffi_type_double)->size;
|
if (cif->rtype->type == FFI_TYPE_FLOAT) size = (&ffi_type_double)->size;
|
||||||
ret = ctx->mem.malloc(size);
|
ret = alloca(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned short not_implemented[4] = {FFI_TYPE_POINTER, FFI_TYPE_SINT64, FFI_TYPE_DOUBLE,
|
|
||||||
FFI_TYPE_FLOAT};
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
if (cif->rtype->type == not_implemented[i]) {
|
|
||||||
/* TODO:
|
|
||||||
* - FFI_TYPE_POINTER: copy the data, but how to free?(make a linked list?), how to
|
|
||||||
* determine the size?
|
|
||||||
* - Others: 64 bit, how to return? change signature to return int64? which suits ptr
|
|
||||||
* :)
|
|
||||||
*/
|
|
||||||
LOG_AND_QUIT("dl_call: unsupported return type (pointer)");
|
|
||||||
}
|
|
||||||
|
|
||||||
ffi_call(cif, FFI_FN(ctx->sym[symbol - 1].symbol), ret, args);
|
ffi_call(cif, FFI_FN(ctx->sym[symbol - 1].symbol), ret, args);
|
||||||
|
|
||||||
LOG_DBG("dl_call: symbol = %d, ret = 0x%x", symbol, *(unsigned *)ret);
|
int64_t ret_val;
|
||||||
|
switch (cif->rtype->type) {
|
||||||
|
case FFI_TYPE_SINT32: ret_val = *(int32_t *)ret; break;
|
||||||
|
case FFI_TYPE_SINT64: ret_val = *(int64_t *)ret; break;
|
||||||
|
case FFI_TYPE_FLOAT: ret_val = (int64_t)(double)(*(float *)ret); break;
|
||||||
|
case FFI_TYPE_DOUBLE: ret_val = (int64_t)(*(double *)ret); break;
|
||||||
|
case FFI_TYPE_VOID: ret_val = 0; break;
|
||||||
|
default: LOG_AND_QUIT("dl_call: unsupported type", WRT_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
return (void *)0x42;
|
LOG_DBG("dl_call: symbol = %d, ret = 0x%lx", symbol, ret_val);
|
||||||
|
|
||||||
#undef CHECK_VA_ARG
|
return ret_val;
|
||||||
#undef GET_ARG
|
|
||||||
#undef ALIGN
|
|
||||||
#undef LOG_AND_QUIT
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Status dl_close_sym(wasm_exec_env_t exec_env, int handle, int symbol) {
|
Status dl_close_sym(wasm_exec_env_t exec_env, int symbol) {
|
||||||
if (handle < 1 || handle > DL_MAX_HANDLES) return WRT_ERROR;
|
|
||||||
if (symbol < 1 || symbol > DL_MAX_SYMBOLS) return WRT_ERROR;
|
if (symbol < 1 || symbol > DL_MAX_SYMBOLS) return WRT_ERROR;
|
||||||
|
|
||||||
GET_CONTEXT(WRT_ERROR)
|
GET_CONTEXT(WRT_ERROR)
|
||||||
|
64
wrt/lib/dl.h
64
wrt/lib/dl.h
@ -23,12 +23,68 @@ typedef struct {
|
|||||||
} sym[DL_MAX_SYMBOLS];
|
} sym[DL_MAX_SYMBOLS];
|
||||||
} DLContext;
|
} DLContext;
|
||||||
|
|
||||||
|
/// Init DL environment
|
||||||
|
/// \param ctx DLContext to be initialized, ctx->mem.malloc and ctx->mem.free must be set before
|
||||||
|
/// \return WRT_OK, unless malloc failed
|
||||||
Status dl_init(DLContext *context);
|
Status dl_init(DLContext *context);
|
||||||
|
|
||||||
|
/// Free DL environment
|
||||||
|
/// \param ctx DLContext to be freed
|
||||||
|
/// \return always WRT_OK
|
||||||
Status dl_free(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);
|
/// Open a dynamic library
|
||||||
void *dl_call(wasm_exec_env_t exec_env, int symbol, _va_list va_args);
|
/// \param exec_env WAMR execution environment
|
||||||
Status dl_close_sym(wasm_exec_env_t exec_env, int handle, int symbol);
|
/// \param filename dynamic library file name
|
||||||
|
/// \param flags dlopen flags (man dlopen)
|
||||||
|
/// \return handle of the opened dynamic library, or WRT_ERROR if failed
|
||||||
|
int dl_open(wasm_exec_env_t exec_env, const char *filename, int flags);
|
||||||
|
|
||||||
|
/// Get symbol from a dynamic library
|
||||||
|
/// \param exec_env WAMR execution environment
|
||||||
|
/// \param handle handle of the dynamic library, returned by dl_open
|
||||||
|
/// \param symbol symbol name
|
||||||
|
/// \param signature function signature: \n
|
||||||
|
/// signature grammar: \n
|
||||||
|
/// -# `type_ret ::= 'i' | 'l' | 'f' | 'd'` \n
|
||||||
|
/// -# `type_arg ::= type_ret | 'p'` \n
|
||||||
|
/// -# `signature ::= '(' type_arg* ')' type_ret?` \n
|
||||||
|
/// 'i' for int(sint32), 'l' for long(sint64), 'f' for float, 'd' for double, 'p' for pointer \n
|
||||||
|
/// Examples: \n
|
||||||
|
/// -# `()` for function: `void func(void)` \n
|
||||||
|
/// -# `(i)` for function: `void func(int)` \n
|
||||||
|
/// -# `(ip)f` for function: `float func(int, void*)` \n
|
||||||
|
/// Several Notes: \n
|
||||||
|
/// -# Pointer type: \n
|
||||||
|
/// -# A int must be followed after the pointer indicating the length of the pointer \n
|
||||||
|
/// -# For example: \n
|
||||||
|
/// + if a native function is defined as `void func(int *p, int *q)` \n
|
||||||
|
/// + then, in WASM environment, the function should be defined as `void func(int *p, int
|
||||||
|
/// p_len, int *q, int q_len)` \n
|
||||||
|
/// -# We do not guarantee the same behavior when there are multiple pointers intervene with each
|
||||||
|
/// other \n
|
||||||
|
/// -# We do not support return value of type pointer, since we don't know how the data length \n
|
||||||
|
/// -# More types will be supported in the future \n
|
||||||
|
/// \return native function's output, or terminated if failed
|
||||||
|
int dl_sym(wasm_exec_env_t exec_env, int handle, const char *symbol, const char *signature);
|
||||||
|
|
||||||
|
/// Call a native function
|
||||||
|
/// \param exec_env WASM execution environment
|
||||||
|
/// \param symbol symbol handle, returned by dl_sym
|
||||||
|
/// \param va_args arguments, in WASM runtime, variadic arguments are pushed into stack, just like a
|
||||||
|
/// array \return function's return value
|
||||||
|
int64_t dl_call(wasm_exec_env_t exec_env, int symbol, _va_list va_args);
|
||||||
|
|
||||||
|
/// Destroy a symbol, it will free the cif structure
|
||||||
|
/// \param exec_env WAMR execution environment
|
||||||
|
/// \param symbol symbol handle, returned by dl_sym
|
||||||
|
/// \return WRT_OK, unless symbol is invalid
|
||||||
|
Status dl_close_sym(wasm_exec_env_t exec_env, int symbol);
|
||||||
|
|
||||||
|
/// Close a dynamic library
|
||||||
|
/// \param exec_env WASM execution environment
|
||||||
|
/// \param handle handle of the dynamic library, returned by dl_open
|
||||||
|
/// \return WRT_OK, unless handle is invalid or dlclose failed
|
||||||
Status dl_close(wasm_exec_env_t exec_env, int handle);
|
Status dl_close(wasm_exec_env_t exec_env, int handle);
|
||||||
|
|
||||||
#endif // WRT_DL_H
|
#endif // WRT_DL_H
|
||||||
|
Reference in New Issue
Block a user