[ktap] Re: [PATCH] FFI support for ktap

  • From: Masami Hiramatsu <masami.hiramatsu@xxxxxxxxx>
  • To: ktap@xxxxxxxxxxxxx
  • Date: Mon, 4 Nov 2013 17:44:04 +0900

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

Other related posts: