[ktap] [PATCH] FFI support for ktap

  • From: Yicheng Qin <qycqycqycqycqyc@xxxxxxxxx>
  • To: ktap@xxxxxxxxxxxxx, jovi.zhangwei@xxxxxxxxx
  • Date: Mon, 4 Nov 2013 01:30:57 -0500

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)


Other related posts: