This patch is the first version of basic FFI support for ktap. A brief overview of current design: * user define needed C symbols in ktap script (functions, structs, etc) * C symbols get compiled into chunk and passed into ktap vm * ktap vm generates ktap_ctype according to symbol information from chunks and populates the global "C" table * when ktap script calls "C.foo()", ktap vm detects that it's a FFI function, and does following: - sets up the hardware stack - calls into the C function - put back return value to ktap stack * continue executing next instructions Implementation of function call module is under interpreter/ffi/, including argument check, type conversion, stack setup and return value handling. Noted that in this POC, C symbol definitions are not compiled into ktap chunk yet. Because we are still working on a C header parser. Currently, we work around it by defining C symbols in ktap table and populate global "C" table in runtime. That way we can focus on the FFI call implementaion. This will be fixed in the third milestone. Support: - x86_64 machine - int, long, longlong and void type - kernel functions that are defined in C table in script - implicit type conversion between ctypes and ktap types Test: ``` New test script is under test/ffi/ ``` Plan: https://github.com/ktap/ktap/wiki/FFI-Support Signed-off-by: Yicheng Qin <qycqycqycqycqyc@xxxxxxxxx> Signed-off-by: Qingping Hou <qingping.hou@xxxxxxxxx> --- Makefile | 8 +- include/ffi.h | 117 ++++++++++++++++++ include/ktap.h | 6 + include/ktap_types.h | 23 +++- interpreter/ffi/ffi_call.c | 282 +++++++++++++++++++++++++++++++++++++++++++ interpreter/ffi/ffi_symbol.c | 195 ++++++++++++++++++++++++++++++ interpreter/ffi/ffi_type.c | 43 +++++++ interpreter/library/ffi.c | 40 ++++++ interpreter/table.c | 17 ++- interpreter/vm.c | 27 +++++ test/ffi/Makefile | 13 ++ test/ffi/cdef.kp | 40 ++++++ test/ffi/ktest.c | 32 +++++ 13 files changed, 839 insertions(+), 4 deletions(-) create mode 100644 include/ffi.h create mode 100644 interpreter/ffi/ffi_call.c create mode 100644 interpreter/ffi/ffi_symbol.c create mode 100644 interpreter/ffi/ffi_type.c create mode 100644 interpreter/library/ffi.c create mode 100644 test/ffi/Makefile create mode 100644 test/ffi/cdef.kp create mode 100644 test/ffi/ktest.c diff --git a/Makefile b/Makefile index 372b41a..717c57b 100644 --- a/Makefile +++ b/Makefile @@ -11,14 +11,18 @@ INC = include INTP = interpreter LIBDIR = $(INTP)/library +FFIDIR = $(INTP)/ffi LIB_OBJS += $(LIBDIR)/baselib.o $(LIBDIR)/kdebug.o $(LIBDIR)/timer.o \ - $(LIBDIR)/ansilib.o + $(LIBDIR)/ansilib.o $(LIBDIR)/ffi.o + +FFI_OBJS += $(FFIDIR)/ffi_call.o $(FFIDIR)/ffi_type.o $(FFIDIR)/ffi_symbol.o \ + $(FFIDIR)/call_x86_64.o INTP_OBJS += $(INTP)/ktap.o $(INTP)/loader.o $(INTP)/object.o \ $(INTP)/tstring.o $(INTP)/table.o $(INTP)/vm.o \ $(INTP)/opcode.o $(INTP)/strfmt.o $(INTP)/transport.o \ - $(LIB_OBJS) + $(LIB_OBJS) $(FFI_OBJS) obj-m += ktapvm.o ktapvm-y := $(INTP_OBJS) diff --git a/include/ffi.h b/include/ffi.h new file mode 100644 index 0000000..4c03b5b --- /dev/null +++ b/include/ffi.h @@ -0,0 +1,117 @@ +#ifndef __KTAP_FFI_H__ +#define __KTAP_FFI_H__ + +#include <stddef.h> + +//@TODO add struct in cp2 25.10 2013 (houqp) +typedef enum { + /* 0 - 4 */ + KTAP_CDVOID, + KTAP_CDCHAR, + KTAP_CDUCHAR, + KTAP_CDUSHORT, + KTAP_CDSHORT, + /* 5 - 10 */ + KTAP_CDUINT, + KTAP_CDINT, + KTAP_CDULONG, + KTAP_CDLONG, + KTAP_CDULLONG, + KTAP_CDLLONG, + /* 11 - 13 */ + KTAP_CDPTR, + KTAP_CDFUNC, + KTAP_CDUNKNOWN, +} cdata_type; + +#define NUM_CDATAS ((int)KTAP_CDUNKNOWN) + + +typedef struct csymbol_func { + void *addr; + int arg_nr; + cdata_type *arg_types; + cdata_type ret_type; + int bytes; +} csymbol_func; + +/* + * used for symbol array; + */ +typedef struct csymbol { + const char *name; + cdata_type type; + union { + csymbol_func f; + } u; +} csymbol; + + + +typedef struct cdata_int { + int val; +} cdata_int ; + +typedef struct cdata_func { + csymbol *proto; +} cdata_func; + + +#define csym_type(s) ((s)->type) +#define csym_name(s) ((s)->name) +#define csym_getf(s) ((s)->u.f) + +#define cd_type(cd) ((cd)->type) +#define cd_getf(cd) ((cd)->u.f) +#define cd_getfsym(cd) (cd_getf(cd).proto) +#define cd_setfsym(cd, sym) (cd_getfsym(cd) = (sym)) + + + +#ifdef __KERNEL__ +/* ffi type used in ffi module */ +typedef enum { + FFI_VOID, + FFI_UINT8, + FFI_INT8, + FFI_UINT16, + FFI_INT16, + FFI_UINT32, + FFI_INT32, + FFI_UINT64, + FFI_INT64, + FFI_POINTER, + FFI_UNKNOWN, +} ffi_type; +#define NUM_FFI_TYPE ((int)FFI_UNKNOWN) + +typedef struct { + size_t size; + size_t alignment; + const char *name; +} ffi_mode; +extern const ffi_mode const ffi_type_modes[]; + +#define ffi_type_size(t) (ffi_type_modes[t].size) +#define ffi_type_alignment(t) (ffi_type_modes[t].alignment) +#define ffi_type_name(t) (ffi_type_modes[t].name) + +#ifdef __x86_64 + +#define ALIGN_STACK(v, a) ((void *)(ALIGN(((uint64_t)v), a))) +#define STACK_ALIGNMENT 8 +#define GPR_SIZE (sizeof(uint64_t)) +#define MAX_GPR 6 +#define MAX_GPR_SIZE (MAX_GPR * GPR_SIZE) + +#define ffi_call(ks, cf, rvalue) ffi_call_x86_64(ks, cf, rvalue) + +#else /* non-supported platform */ + +#define ffi_call(ks, cf, rvalue) ffi_call_unsupported(ks, cf, rvalue) + +#endif /* end for platform-specific setting */ + +#endif /* for __KERNEL__ */ + +#endif diff --git a/include/ktap.h b/include/ktap.h index 5f408cf..4967a5e 100644 --- a/include/ktap.h +++ b/include/ktap.h @@ -54,11 +54,17 @@ void kp_optimize_code(ktap_state *ks, int level, ktap_proto *f); void kp_register_lib(ktap_state *ks, const char *libname, const ktap_Reg *funcs); void *kp_percpu_data(int type); +void kp_ffi_call(ktap_state *ks, csymbol_func *cf); +void kp_ffi_free_symbol(ktap_state *ks); +void setup_kp_ffi_symbol_table(ktap_state *ks); +void ffi_load_sym(ktap_state *ks); + void kp_init_baselib(ktap_state *ks); void kp_init_oslib(ktap_state *ks); void kp_init_kdebuglib(ktap_state *ks); void kp_init_timerlib(ktap_state *ks); void kp_init_ansilib(ktap_state *ks); +void kp_init_ffilib(ktap_state *ks); int kp_probe_init(ktap_state *ks); void kp_probe_exit(ktap_state *ks); diff --git a/include/ktap_types.h b/include/ktap_types.h index 1dae9d8..3c5eb57 100644 --- a/include/ktap_types.h +++ b/include/ktap_types.h @@ -14,6 +14,7 @@ typedef char u8; #include <stdio.h> #include <string.h> #endif +#include "ffi.h" typedef struct ktap_parm { char *trunk; /* __user */ @@ -250,6 +251,16 @@ typedef struct ktap_stringtable { int size; } ktap_stringtable; +typedef struct ktap_cdata { + CommonHeader; + /* type can be struct, int, function, etc.. */ + int type; + union { + cdata_func f; + cdata_int i; + } u; +} ktap_cdata; + typedef struct ktap_stats { int mem_allocated; int nr_mem_allocate; @@ -329,6 +340,7 @@ union ktap_gcobject { struct ktap_upval uv; struct ktap_state th; /* thread */ struct ktap_btrace bt; /* backtrace object */ + struct ktap_cdata cd; }; #define gch(o) (&(o)->gch) @@ -363,11 +375,11 @@ union ktap_gcobject { #define KTAP_TTHREAD 7 #define KTAP_TPROTO 8 #define KTAP_TUPVAL 9 - #define KTAP_TEVENT 10 #define KTAP_TBTRACE 11 #define KTAP_TPTABLE 12 #define KTAP_TSTATDATA 13 +#define KTAP_CDATA 14 /* * type number is ok so far, but it may collide later between * 16+ and | (1 << 4), so be careful on this. @@ -404,6 +416,7 @@ union ktap_gcobject { #define fvalue(o) (val_(o).f) #define evalue(o) (val_(o).p) #define btvalue(o) (&val_(o).gc->bt) +#define cdvalue(o) (&val_(o).gc->cd) #define is_nil(o) ((o)->type == KTAP_TNIL) #define is_boolean(o) ((o)->type == KTAP_TBOOLEAN) @@ -417,6 +430,7 @@ union ktap_gcobject { #define is_event(o) ((o)->type == KTAP_TEVENT) #define is_btrace(o) ((o)->type == KTAP_TBTRACE) #define is_needclone(o) is_btrace(o) +#define is_cdata(o) ((o)->type == KTAP_CDATA) #define set_nil(obj) \ @@ -463,6 +477,10 @@ union ktap_gcobject { { ktap_value *io = (obj); \ val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_TBTRACE); } +#define set_cdata(obj, x) \ + { ktap_value *io=(obj); \ + val_(io).gc = (ktap_gcobject *)(x); settype(io, KTAP_CDATA); } + #define set_obj(obj1, obj2) \ { const ktap_value *io2 = (obj2); ktap_value *io1 = (obj1); \ io1->val = io2->val; io1->type = io2->type; } @@ -509,6 +527,9 @@ void kp_table_resize(ktap_state *ks, ktap_table *t, int nasize, int nhsize); void kp_table_resizearray(ktap_state *ks, ktap_table *t, int nasize); void kp_table_free(ktap_state *ks, ktap_table *t); int kp_table_length(ktap_state *ks, ktap_table *t); +int kp_table_sizenode(ktap_state *ks, ktap_table *t); +ktap_value *kp_table_node_gkey(ktap_tnode *n); +ktap_value *kp_table_node_gval(ktap_tnode *n); void kp_table_dump(ktap_state *ks, ktap_table *t); void kp_table_clear(ktap_state *ks, ktap_table *t); void kp_table_histogram(ktap_state *ks, ktap_table *t); diff --git a/interpreter/ffi/ffi_call.c b/interpreter/ffi/ffi_call.c new file mode 100644 index 0000000..c46a22e --- /dev/null +++ b/interpreter/ffi/ffi_call.c @@ -0,0 +1,282 @@ +/* + * ffi_call.c - foreign function calling library support for ktap + * + * This file is part of ktap by zhangwei(Jovi). + * + * Copyright (C) 2012-2013 zhangwei(Jovi) <jovi.zhangwei@xxxxxxxxx>. + * See the COPYRIGHT file at the top-level directory of this distribution. + * + * ktap is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * ktap is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/ctype.h> +#include <linux/slab.h> +#include "../../include/ffi.h" +#include "../../include/ktap.h" + +static ffi_type cdata_to_ffi_type(cdata_type type) +{ + /*@TODO size should be architecture dependent, need to have different + * version for different arch in the future (houqp)*/ + switch(type) { + case KTAP_CDVOID: + return FFI_VOID; + case KTAP_CDUCHAR: + return FFI_UINT8; + case KTAP_CDCHAR: + return FFI_INT8; + case KTAP_CDUSHORT: + return FFI_UINT16; + case KTAP_CDSHORT: + return FFI_INT16; + case KTAP_CDUINT: + return FFI_UINT32; + case KTAP_CDINT: + return FFI_INT32; + case KTAP_CDULONG: + case KTAP_CDULLONG: + return FFI_UINT64; + case KTAP_CDLONG: + case KTAP_CDLLONG: + return FFI_INT64; + case KTAP_CDPTR: + case KTAP_CDFUNC: + return FFI_POINTER; + case KTAP_CDUNKNOWN: + break; + } + + /* NEVER reach here, silence compiler */ + return -1; +} + +static int cdata_type_check(ktap_state *ks, cdata_type type, StkId arg, int idx) +{ + switch (ttypenv(arg)) { + case KTAP_TLIGHTUSERDATA: + if (type != KTAP_CDPTR) goto error; + break; + case KTAP_TBOOLEAN: + case KTAP_TNUMBER: + if (type != KTAP_CDCHAR && type != KTAP_CDUCHAR + && type != KTAP_CDUSHORT && type != KTAP_CDSHORT + && type != KTAP_CDUINT && type != KTAP_CDINT + && type != KTAP_CDULONG && type != KTAP_CDLONG + && type != KTAP_CDULLONG && type != KTAP_CDLLONG) + goto error; + break; + case KTAP_TSTRING: + if (type != KTAP_CDPTR) goto error; + break; + default: + if (type != cdvalue(arg)->type) goto error; + } + return 0; + + error: + kp_error(ks, "Error: Cannot convert to cdata_type %s for arg %d\n", + ffi_type_name(cdata_to_ffi_type(type)), idx); + return -1; +} + +static void unpack_cdata(ktap_state *ks, cdata_type type, + StkId arg, char *dst) +{ + size_t size = ffi_type_size(cdata_to_ffi_type(type)); + void *p; + + switch (ttypenv(arg)) { + case KTAP_TBOOLEAN: + memcpy(dst, &bvalue(arg), size); + return; + case KTAP_TLIGHTUSERDATA: + memcpy(dst, pvalue(arg), size); + return; + case KTAP_TNUMBER: + memcpy(dst, &nvalue(arg), size); + return; + case KTAP_TSTRING: + p = &rawtsvalue(arg)->tsv + 1; + memcpy(dst, &p, size); + return; + } + + switch (type) { + case KTAP_CDVOID: + kp_error(ks, "Error: Cannot copy data from void type\n"); + return; + case KTAP_CDCHAR: + case KTAP_CDUCHAR: + case KTAP_CDUSHORT: + case KTAP_CDSHORT: + case KTAP_CDUINT: + case KTAP_CDINT: + case KTAP_CDULONG: + case KTAP_CDLONG: + case KTAP_CDULLONG: + case KTAP_CDLLONG: + memcpy(dst, &cdvalue(arg)->u.i.val, size); + break; + case KTAP_CDPTR: + case KTAP_CDFUNC: + case KTAP_CDUNKNOWN: + kp_error(ks, "Error: Unsupport for cdata_type %s\n", + ffi_type_name(cdata_to_ffi_type(type))); + } + + return; +} + +extern void ffi_call_assem_x86_64(void *stack, void *temp_stack, + void *rvalue, void *func_addr); + +static void ffi_call_x86_64(ktap_state *ks, csymbol_func *cf, void *rvalue) +{ + int i; + int gpr_nr; + int bytes = 0; /* total bytes needed for arguments */ + char *stack, *stack_p, *arg_p, *gpr_p, *tmp_p; + + /* calculate bytes needed for stack */ + /* Currently, one argument can be put in a 64-bit register always */ + gpr_nr = 0; + for (i = 0; i < cf->arg_nr; i++) { + cdata_type actype = cf->arg_types[i]; + ffi_type aftype = cdata_to_ffi_type(actype); + if (gpr_nr < MAX_GPR) + gpr_nr++; + else { + bytes = ALIGN(bytes, ffi_type_alignment(aftype)); + bytes += ffi_type_size(aftype); + bytes = ALIGN(bytes, STACK_ALIGNMENT); + } + } + + /* apply space to fake stack for C function call */ + stack = kp_malloc(ks, MAX_GPR_SIZE + bytes + 6 * 8 + 128); + /* 128 bytes below %rsp is red zone */ + stack_p = stack + 128; + /* stack should be 16-bytes aligned */ + stack_p = ALIGN_STACK(stack_p, 16); + /* save general purpose registers here */ + gpr_p = stack_p; + memset(gpr_p, 0, MAX_GPR_SIZE); + /* save arguments here */ + arg_p = gpr_p + MAX_GPR_SIZE; + /* set additional space as temporary space */ + tmp_p = arg_p + bytes; + + /* copy arguments here */ + gpr_nr = 0; + for (i = 0; i < cf->arg_nr; i++) { + cdata_type actype = cf->arg_types[i]; + ffi_type aftype = cdata_to_ffi_type(actype); + unpack_cdata(ks, actype, kp_arg(ks, i+1), tmp_p); + if (gpr_nr < MAX_GPR) { + memcpy(gpr_p, tmp_p, ffi_type_size(aftype)); + gpr_p += GPR_SIZE; + gpr_nr++; + } else { + arg_p = ALIGN_STACK(arg_p, ffi_type_alignment(aftype)); + memcpy(arg_p, tmp_p, ffi_type_size(aftype)); + arg_p += ffi_type_size(aftype); + arg_p = ALIGN_STACK(arg_p, STACK_ALIGNMENT); + } + } + + kp_verbose_printf(ks, "Number of register used: %d\n", gpr_nr); + kp_verbose_printf(ks, "Stack location: %p %p %p %p\n", stack_p, gpr_p, arg_p, tmp_p); + kp_verbose_printf(ks, "Address for return value: %p\n", rvalue); + kp_verbose_printf(ks, "Function address: %p\n", cf->addr); + ffi_call_assem_x86_64(stack_p, tmp_p, rvalue, cf->addr); + kp_verbose_printf(ks, "Finish FFI call\n"); + + kp_free(ks, stack); + return; +} + +static void ffi_call_unsupported(ktap_state *ks, csymbol_func *cf, void *rvalue) +{ + kp_error(ks, "not supported architecture.\n"); +} + +static void ffi_set_return(ktap_state *ks, void *rvalue, cdata_type ret_type) +{ + /* push return value to ktap stack */ + switch (ret_type) { + case KTAP_CDVOID: + return; + case KTAP_CDSHORT: + case KTAP_CDINT: + case KTAP_CDLONG: + case KTAP_CDLLONG: + case KTAP_CDUSHORT: + case KTAP_CDCHAR: + case KTAP_CDUCHAR: + case KTAP_CDUINT: + case KTAP_CDULONG: + case KTAP_CDULLONG: + set_number(ks->top, (ktap_number)rvalue); + break; + case KTAP_CDPTR: + case KTAP_CDFUNC: + /*@TODO handle pointer case in cp2 25.10 2013 (houqp)*/ + break; + case KTAP_CDUNKNOWN: + /*@TODO handle unknown type 25.10 2013 (houqp)*/ + break; + } + incr_top(ks); +} + +/* + * Call C into function + * First argument should be function symbol address, argument types + * and return type. + * Left arguments should be arguments for calling the C function. + * Types between Ktap and C are converted automatically. + * Only support x86_64 function call for now + * Return value is handled carelessly now + */ +void kp_ffi_call(ktap_state *ks, csymbol_func *cf) +{ + int i, arg_nr = cf->arg_nr; + cdata_type *arg_types = cf->arg_types; + ktap_closure *cl; + void *rvalue; + + /* check stack status for C call */ + if (arg_nr != kp_arg_nr(ks)) { + kp_error(ks, "wrong argument number %d\n", arg_nr); + goto out; + } + + /* maybe useful later, leave it here first */ + cl = clvalue(kp_arg(ks, arg_nr + 1)); + + /* check the argument types */ + for (i = 0; i < arg_nr; i++) { + StkId karg = kp_arg(ks, i + 1); + cdata_type atype = arg_types[i]; + if (cdata_type_check(ks, atype, karg, i) < 0) + goto out; + } + + /* platform-specific calling workflow */ + ffi_call(ks, cf, &rvalue); + +out: + ffi_set_return(ks, rvalue, cf->ret_type); + return; +} diff --git a/interpreter/ffi/ffi_symbol.c b/interpreter/ffi/ffi_symbol.c new file mode 100644 index 0000000..3d2ead9 --- /dev/null +++ b/interpreter/ffi/ffi_symbol.c @@ -0,0 +1,195 @@ +/* + * ffi_symbol.c - ktapvm kernel module ffi symbol submodule + * + * This file is part of ktap by Jovi Zhangwei. + * + * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@xxxxxxxxx>. + * + * ktap is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * ktap is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +#include "../../include/ktap.h" +#include "../../include/ktap_types.h" + +static int sym_cnt; /* number of loaded symbols */ +static ktap_table *ffi_table; +static ktap_table *ffi_cdef_table; +static csymbol *ffi_syms; + + +static void add_ffi_func_to_ctable(ktap_state *ks, csymbol *fsym) +{ + ktap_value func_name, fv; + ktap_cdata *cd; + + set_cdata(&fv, kp_newobject(ks, KTAP_CDATA, sizeof(ktap_cdata), NULL)); + cd = cdvalue(&fv); + cd_type(cd) = KTAP_CDFUNC; + cd_setfsym(cd, fsym); + + set_string(&func_name, kp_tstring_new(ks, csym_name(fsym))); + kp_table_setvalue(ks, ffi_table, &func_name, &fv); +} + +static inline int is_valid_cfunc_def(const ktap_table *sym_def) +{ + return (sym_def->sizearray >= 1); +} + +int register_ffi_func(ktap_state *ks, const char *name, void *faddr, + ktap_table *sym_def, csymbol *cs) +{ + int i, arraysize, arg_nr = 0; + cdata_type *argtype_arr; + + argtype_arr = NULL; + /* ok, we found a function definition */ + kp_verbose_printf(ks, "registering C function: %s\n", name); + csym_type(cs) = KTAP_CDFUNC; + /* set function name */ + csym_name(cs) = name; + /* set function address */ + kp_verbose_printf(ks, "function address %p\n", faddr); + csym_getf(cs).addr = faddr; + /* set function prototype */ + /* first array element stores return type */ + csym_getf(cs).ret_type = nvalue(&sym_def->array[0]); + /* populate argument types */ + arraysize = sym_def->sizearray; + if (arraysize > 1) { + /* get table array size */ + for (i = 1; i < arraysize; i++) + if (is_nil(&sym_def->array[i])) + break; + /* since first element is return type, real argument number + * should be arraysize -1 */ + arg_nr = i - 1; + argtype_arr = kp_malloc(ks, arg_nr*sizeof(cdata_type)); + for (i = 0; i < arg_nr; i++) { + /* argument type starts from second element */ + argtype_arr[i] = nvalue(&sym_def->array[i+1]); + } + } else if (arraysize == 1) { + arg_nr = 0; + } + csym_getf(cs).arg_types = argtype_arr; + csym_getf(cs).arg_nr = arg_nr; + + add_ffi_func_to_ctable(ks, cs); + + return 0; +} + +void ffi_load_sym(ktap_state *ks) +{ + int i, re, node_sz; + ktap_table *sym_def; + ktap_value key_type, key_addr; + const ktap_value *sym_type, *addr; + csymbol *cs; + + set_string(&key_type, kp_tstring_new(ks, "type")); + set_string(&key_addr, kp_tstring_new(ks, "addr")); + + node_sz = kp_table_sizenode(ks, ffi_cdef_table); + if (node_sz <= 0) + return; + sym_cnt = 0; + + ffi_syms = kp_malloc(ks, node_sz*sizeof(csymbol)); + memset(ffi_syms, 0, node_sz*sizeof(csymbol)); + + /* iterate through each key(symbol) */ + for (i = 0; i < node_sz; i++) { + ktap_tnode *n = &ffi_cdef_table->node[i]; + + if (is_nil(kp_table_node_gkey(n))) + continue; + + sym_def = hvalue(kp_table_node_gval(n)); + sym_type = kp_table_get(sym_def, &key_type); + /* symbol type must be a number */ + if (!is_number(sym_type)) + continue; + + cs = &ffi_syms[sym_cnt]; + switch (nvalue(sym_type)) { + case KTAP_CDFUNC: + if (!is_valid_cfunc_def(sym_def)) { + kp_verbose_printf(ks, + "invalid function definition!\n"); + continue; + } + + addr = kp_table_get(sym_def, &key_addr); + re = register_ffi_func(ks, + svalue(kp_table_node_gkey(n)), + (void *)nvalue(addr), sym_def, cs); + if (re < 0) + continue; + break; + default: + kp_verbose_printf(ks, "unsupported C symbol type!\n"); + continue; + break; + } + + sym_cnt++; + } +} + +void setup_kp_ffi_symbol_table(ktap_state *ks) +{ + ktap_table *registry; + ktap_value ffi_lib_name, ffi_mt, cdef_name, cdef_mt; + const ktap_value *gt = kp_table_getint(hvalue(&G(ks)->registry), + KTAP_RIDX_GLOBALS); + + ffi_table = kp_table_new(ks); + ffi_cdef_table = kp_table_new(ks); + + /* insert cdef table to ffi table */ + set_table(&cdef_mt, ffi_cdef_table); + set_string(&cdef_name, kp_tstring_new(ks, "cdef")); + kp_table_setvalue(ks, ffi_table, &cdef_name, &cdef_mt); + + /* insert ffi table to global table */ + set_table(&ffi_mt, ffi_table); + set_string(&ffi_lib_name, kp_tstring_new(ks, "C")); + registry = hvalue(gt); + kp_table_setvalue(ks, registry, &ffi_lib_name, &ffi_mt); +} + +void kp_ffi_free_symbol(ktap_state *ks) +{ + int i; + csymbol *cs; + + for (i = 0; i < sym_cnt; i++) { + cs = &ffi_syms[i]; + switch (csym_type(cs)) { + case KTAP_CDFUNC: + if (csym_getf(cs).arg_types) { + kp_free(ks, csym_getf(cs).arg_types); + } + break; + default: + continue; + } + } + + if (ffi_syms) + kp_free(ks, ffi_syms); +} diff --git a/interpreter/ffi/ffi_type.c b/interpreter/ffi/ffi_type.c new file mode 100644 index 0000000..6b1f8f2 --- /dev/null +++ b/interpreter/ffi/ffi_type.c @@ -0,0 +1,43 @@ +#include "../../include/ffi.h" +#include <linux/types.h> + +#define CTYPE_MODE_HELPER(name, type) \ +struct _##name##_align { \ + type t1; \ + char c; \ + type t2; \ +}; + +#define CTYPE_MODE(name) \ +{ \ + offsetof(struct _##name##_align, c), \ + offsetof(struct _##name##_align, t2) - \ + offsetof(struct _##name##_align, c), \ + "##name" \ +} + +#define CTYPE_MODE_NAME(name) _##name##_mode + +/* ffi_ctype_mode should be corresponded to ffi_ctype */ +CTYPE_MODE_HELPER(uint8, uint8_t); +CTYPE_MODE_HELPER(int8, int8_t); +CTYPE_MODE_HELPER(uint16, uint16_t); +CTYPE_MODE_HELPER(int16, int16_t); +CTYPE_MODE_HELPER(uint32, uint32_t); +CTYPE_MODE_HELPER(int32, int32_t); +CTYPE_MODE_HELPER(uint64, uint64_t); +CTYPE_MODE_HELPER(int64, int64_t); +CTYPE_MODE_HELPER(pointer, void*); + +const ffi_mode ffi_type_modes[NUM_FFI_TYPE] = { + {0, 1, "void"}, + CTYPE_MODE(uint8), + CTYPE_MODE(int8), + CTYPE_MODE(uint16), + CTYPE_MODE(int16), + CTYPE_MODE(uint32), + CTYPE_MODE(int32), + CTYPE_MODE(uint64), + CTYPE_MODE(int64), + CTYPE_MODE(pointer), +}; diff --git a/interpreter/library/ffi.c b/interpreter/library/ffi.c new file mode 100644 index 0000000..47c4419 --- /dev/null +++ b/interpreter/library/ffi.c @@ -0,0 +1,40 @@ +/* + * ffi.c - ktapvm kernel module ffi library + * + * This file is part of ktap by Jovi Zhangwei. + * + * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@xxxxxxxxx>. + * + * ktap is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * ktap is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "../../include/ktap.h" +#include "../../include/ktap_types.h" + +static int kp_ffi_load_sym(ktap_state *ks) +{ + ffi_load_sym(ks); + return 0; +} + +static const ktap_Reg ffi_funcs[] = { + {"load_sym", kp_ffi_load_sym}, + {NULL} +}; + +void kp_init_ffilib(ktap_state *ks) +{ + setup_kp_ffi_symbol_table(ks); + kp_register_lib(ks, "ffi", ffi_funcs); +} diff --git a/interpreter/table.c b/interpreter/table.c index 071965b..36859a0 100644 --- a/interpreter/table.c +++ b/interpreter/table.c @@ -952,11 +952,26 @@ int kp_table_length(ktap_state *ks, ktap_table *t) len++; } - + kp_table_unlock(t); return len; } +int kp_table_sizenode(ktap_state *ks, ktap_table *t) +{ + return sizenode(t); +} + +ktap_value *kp_table_node_gkey(ktap_tnode *n) +{ + return gkey(n); +} + +ktap_value *kp_table_node_gval(ktap_tnode *n) +{ + return gval(n); +} + void kp_table_free(ktap_state *ks, ktap_table *t) { if (t->sizearray > 0) { diff --git a/interpreter/vm.c b/interpreter/vm.c index dc090a1..d976c0b 100644 --- a/interpreter/vm.c +++ b/interpreter/vm.c @@ -296,6 +296,8 @@ static int precall(ktap_state *ks, StkId func, int nresults) { ktap_cfunction f; ktap_callinfo *ci; + ktap_cdata *cf; + csymbol *cs; ktap_proto *p; StkId base; ptrdiff_t funcr = savestack(ks, func); @@ -339,6 +341,29 @@ static int precall(ktap_state *ks, StkId func, int nresults) ci->callstatus = CIST_KTAP; ks->top = ci->top; return 0; + case KTAP_CDATA: + cf = cdvalue(func); + if (cf->type != KTAP_CDFUNC) + kp_error(ks, "ctype is not a c funcion\n"); + + if (checkstack(ks, KTAP_MIN_RESERVED_STACK_SIZE)) + return 1; + + cs = cd_getfsym(cf); + kp_verbose_printf(ks, "calling function [%s] with address %p\n", + csym_name(cs), csym_getf(cs).addr); + + ci = next_ci(ks); + ci->nresults = nresults; + ci->func = restorestack(ks, funcr); + ci->top = ks->top + KTAP_MIN_RESERVED_STACK_SIZE; + ci->callstatus = 0; + + kp_ffi_call(ks, &csym_getf(cs)); + kp_verbose_printf(ks, "returned from ffi call...\n"); + n = (csym_getf(cs).ret_type == KTAP_CDVOID) ? 0 : 1; + poscall(ks, ks->top - n); + return 1; default: kp_error(ks, "attempt to call nil function\n"); } @@ -1277,6 +1302,7 @@ void kp_final_exit(ktap_state *ks) kp_probe_exit(ks); /* free all resources got by ktap */ + kp_ffi_free_symbol(ks); kp_tstring_freeall(ks); kp_free_all_gcobject(ks); cfunction_cache_exit(ks); @@ -1374,6 +1400,7 @@ ktap_state *kp_newstate(ktap_parm *parm, struct dentry *dir) kp_init_kdebuglib(ks); kp_init_timerlib(ks); kp_init_ansilib(ks); + kp_init_ffilib(ks); if (alloc_kp_percpu_data()) goto out; diff --git a/test/ffi/Makefile b/test/ffi/Makefile new file mode 100644 index 0000000..5e2eaea --- /dev/null +++ b/test/ffi/Makefile @@ -0,0 +1,13 @@ +obj-m += ktest.o + +all: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +load: + insmod ktest.ko + +unload: + rmmod ktest + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean diff --git a/test/ffi/cdef.kp b/test/ffi/cdef.kp new file mode 100644 index 0000000..27597be --- /dev/null +++ b/test/ffi/cdef.kp @@ -0,0 +1,40 @@ +# this need to be synchronized with C header +KTAP_CDVOID = 0 +KTAP_CDCHAR = 1 +KTAP_CDUCHAR = 2 +KTAP_CDUSHORT = 3 +KTAP_CDSHORT = 4 +KTAP_CDUINT = 5 +KTAP_CDINT = 6 +KTAP_CDULONG = 7 +KTAP_CDLONG = 8 +KTAP_CDULLONG = 9 +KTAP_CDLLONG = 10 +KTAP_CDPTR = 11 +KTAP_CDFUNC = 12 +KTAP_CDUNKNOWN = 13 + + +C.cdef["sched_clock"] = {} +C.cdef["sched_clock"]["addr"] = `sched_clock` +C.cdef["sched_clock"]["type"] = KTAP_CDFUNC +C.cdef["sched_clock"][1] = KTAP_CDULLONG + +#C.cdef["ktest_int_int"] = {} +#C.cdef["ktest_int_int"]["type"] = KTAP_CDFUNC +#C.cdef["ktest_int_int"]["addr"] = `ktest_int_int` +#C.cdef["ktest_int_int"][1] = KTAP_CDINT +#C.cdef["ktest_int_int"][2] = KTAP_CDINT +#C.cdef["ktest_int_int"][3] = KTAP_CDINT + +ffi.load_sym() + +# end of cdef header + + +print("start of user script...") + +print("[*] try a registered C function...") +#ret = C.ktest_int_int(1337, 1337) +ret = C.sched_clock() +print("C function returned, value: ", ret) diff --git a/test/ffi/ktest.c b/test/ffi/ktest.c new file mode 100644 index 0000000..917c8e5 --- /dev/null +++ b/test/ffi/ktest.c @@ -0,0 +1,32 @@ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> + +static int __init ktest_init(void) +{ + printk("Hello World!\n"); + return 0; +} + +int ktest_void() +{ + return 1377; +} + +int ktest_int(int x) +{ + return x + 1337; +} + +int ktest_int_int(int x, int y) +{ + return x + y + 1337; +} + +static void __exit ktest_exit(void) +{ + printk("Goodbye!\n"); +} + +module_init(ktest_init); +module_exit(ktest_exit); -- 1.8.3.4 (Apple Git-47)