Hi, At first, this looks great debugging feature! :) I just concern about safety, because many kernel functions are not safely called from ktapvm context. For example, what happens if I call sched() from ktap? IMHO, this kind of intrusive feature support should be enabled by a debug kconfig, like CONFIG_KTAP_FFI which depends on CONFIG_KTAP_INTRUSIVE_MODE. And also, it looks better to have a knob to enable such kind of intrusive mode on sysctl, like /proc/sys/debug/ktap-intrusive-mode which default value is 0 (disabled). This will allow admins to choose stability or maximum debug-ability on their production/developing systems. Thank you! 2013/11/4 Yicheng Qin <qycqycqycqycqyc@xxxxxxxxx> > 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) > > > -- Masami Hiramatsu mailto:masami.hiramatsu@xxxxxxxxx