[ktap] [PATCHv3 4/8] Introduce ffi call module in vm

  • From: Qingping Hou <dave2008713@xxxxxxxxx>
  • To: ktap@xxxxxxxxxxxxx
  • Date: Fri, 29 Nov 2013 15:47:18 -0500

From: Yicheng Qin <qycqycqycqycqyc@xxxxxxxxx>

kp_ffi_call function calls into C functions according to arguments
passed in ktap vm stack.

Only support x86_64 platform now, but it is rather easy to add support
for new platforms.

Signed-off-by: Yicheng Qin <qycqycqycqycqyc@xxxxxxxxx>
Signed-off-by: Qingping Hou <qingping.hou@xxxxxxxxx>
---
 interpreter/ffi/call_x86_64.S | 139 ++++++++++++++++
 interpreter/ffi/cdata.c       |  68 ++++++++
 interpreter/ffi/ffi_call.c    | 361 ++++++++++++++++++++++++++++++++++++++++++
 interpreter/ffi/ffi_symbol.c  | 172 ++++++++++++++++++++
 interpreter/ffi/ffi_util.c    |  93 +++++++++++
 5 files changed, 833 insertions(+)
 create mode 100644 interpreter/ffi/call_x86_64.S
 create mode 100644 interpreter/ffi/cdata.c
 create mode 100644 interpreter/ffi/ffi_call.c
 create mode 100644 interpreter/ffi/ffi_symbol.c
 create mode 100644 interpreter/ffi/ffi_util.c

diff --git a/interpreter/ffi/call_x86_64.S b/interpreter/ffi/call_x86_64.S
new file mode 100644
index 0000000..e109f72
--- /dev/null
+++ b/interpreter/ffi/call_x86_64.S
@@ -0,0 +1,139 @@
+/*
+ * call_x86_64.S - assembly code to call C function and handle return value
+ *
+ * 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.
+ */
+
+
+#ifdef __x86_64
+
+       .file "call_x86_64.S"
+       .text
+
+/*     ffi_call_assem_x86_64(void *stack, void *temp_stack,
+ *             void *func_addr, void *rvalue, ffi_type rftype)
+ *     @stack: base address of register values and new stack
+ *     @temp_stack: stack to store temporary values
+ *     @func_addr: Function address
+ *     @rvalue: where to put return value
+ *     @rftype: FFI type of return value
+ */
+       .align 2
+       .globl  ffi_call_assem_x86_64
+       .type   ffi_call_assem_x86_64,@function
+
+ffi_call_assem_x86_64:
+       movq    %rsp, %rax      /* save return address */
+       /* move stuffs to temp memory region(void *temp_stack) */
+       movq    %rcx, (%rsi)    /* save pointer to return value */
+       movq    %rbp, 8(%rsi)   /* save %rbp */
+       movq    %rax, 16(%rsi)  /* save %rsp */
+       movq    %r8, 24(%rsi)   /* save return_ffi_type */
+       movq    %rsi, %rbp      /* point %rbp to temp memory region */
+
+       movq    %rdx, %r11      /* move function address to %r11 */
+
+       movq    %rdi, %r10      /* set %r10 point to register region */
+       movq    (%r10), %rdi    /* load registers */
+       movq    8(%r10), %rsi
+       movq    16(%r10), %rdx
+       movq    24(%r10), %rcx
+       movq    32(%r10), %r8
+       movq    40(%r10), %r9
+       leaq    48(%r10), %rsp
+
+       callq   *%r11
+
+       movq    16(%rbp), %rsp  /* restore return address */
+
+       movq    (%rbp), %rcx    /* get pointer to return value */
+       movq    24(%rbp), %r8   /* get return_ffi_type */
+       movq    8(%rbp), %rbp   /* restore rbp */
+
+       leaq    .Lreturn_table(%rip), %r11      /* start address of 
return_table */
+       movslq  (%r11, %r8, 8), %r11    /* fetch target address from table */
+       jmpq    *%r11                   /* jump according to value in table */
+
+       .align 8
+.Lreturn_table:
+       .quad   .Lreturn_void           /* FFI_VOID */
+       .quad   .Lreturn_uint8          /* FFI_UINT8 */
+       .quad   .Lreturn_int8           /* FFI_INT8 */
+       .quad   .Lreturn_uint16         /* FFI_UINT16 */
+       .quad   .Lreturn_int16          /* FFI_INT16 */
+       .quad   .Lreturn_uint32         /* FFI_UINT32 */
+       .quad   .Lreturn_int32          /* FFI_INT32 */
+       .quad   .Lreturn_uint64         /* FFI_UINT64 */
+       .quad   .Lreturn_int64          /* FFI_INT64 */
+       .quad   .Lreturn_ptr            /* FFI_PTR */
+       .quad   .Lreturn_func           /* FFI_FUNC */
+       .quad   .Lreturn_struct         /* FFI_STRUCT */
+       .quad   .Lreturn_unknown        /* FFI_UNKNOWN */
+
+       .align 8
+.Lreturn_void:
+.Lreturn_func:
+.Lreturn_unknown:
+       retq
+       .align 8
+.Lreturn_uint8:
+       movzbq  %al, %rax
+       movq    %rax, (%rcx)
+       retq
+       .align 8
+.Lreturn_int8:
+       movsbq  %al, %rax
+       movq    %rax, (%rcx)
+       retq
+       .align 8
+.Lreturn_uint16:
+       movzwq  %ax, %rax
+       movq    %rax, (%rcx)
+       retq
+       .align 8
+.Lreturn_int16:
+       movswq  %ax, %rax
+       movq    %rax, (%rcx)
+       retq
+       .align 8
+.Lreturn_uint32:
+       movl    %eax, %eax
+       movq    %rax, (%rcx)
+       retq
+       .align 8
+.Lreturn_int32:
+       movslq  %eax, %rax
+       movq    %rax, (%rcx)
+       retq
+       .align 8
+.Lreturn_uint64:
+.Lreturn_int64:
+.Lreturn_ptr:
+       movq    %rax, (%rcx)
+       retq
+/* Struct type indicates that struct is put into at most two registers,
+ * and 16 bytes space is always available
+ */
+       .align 8
+.Lreturn_struct:
+       movq    %rax, (%rcx)
+       movq    %rdx, 8(%rcx)
+       retq
+
+#endif /* end for __x86_64 */
diff --git a/interpreter/ffi/cdata.c b/interpreter/ffi/cdata.c
new file mode 100644
index 0000000..af8b05a
--- /dev/null
+++ b/interpreter/ffi/cdata.c
@@ -0,0 +1,68 @@
+/*
+ * cdata.c - support functions for ktap_cdata
+ *
+ * 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 "../../include/ktap_types.h"
+#include "../kp_obj.h"
+#include "../kp_ffi.h"
+
+ktap_cdata *kp_cdata_new(ktap_state *ks)
+{
+       ktap_cdata *cd;
+
+       cd = &kp_newobject(ks, KTAP_TCDATA, sizeof(ktap_cdata), NULL)->cd;
+
+       return cd;
+}
+
+ktap_cdata *kp_cdata_new_ptr(ktap_state *ks, void *addr, csymbol_id id)
+{
+       ktap_cdata *cd;
+
+       cd = kp_cdata_new(ks);
+       cd_set_csym_id(cd, id);
+       cd_ptr(cd) = addr;
+
+       return cd;
+}
+
+ktap_cdata *kp_cdata_new_struct(ktap_state *ks, void *val, csymbol_id id)
+{
+       ktap_cdata *cd;
+
+       cd = kp_cdata_new(ks);
+       cd_set_csym_id(cd, id);
+       cd_struct(cd) = val;
+
+       return cd;
+}
+
+void kp_cdata_dump(ktap_state *ks, ktap_cdata *cd)
+{
+       switch (cd_type(ks, cd)) {
+       case FFI_PTR:
+               kp_printf(ks, "pointer(%p)", cd_ptr(cd));
+               break;
+       default:
+               kp_printf(ks, "unsupported cdata type %d!\n", cd_type(ks, cd));
+       }
+}
diff --git a/interpreter/ffi/ffi_call.c b/interpreter/ffi/ffi_call.c
new file mode 100644
index 0000000..4475d72
--- /dev/null
+++ b/interpreter/ffi/ffi_call.c
@@ -0,0 +1,361 @@
+/*
+ * 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/ktap_types.h"
+#include "../ktap.h"
+#include "../kp_vm.h"
+#include "../kp_obj.h"
+#include "../kp_ffi.h"
+
+static int ffi_type_check(ktap_state *ks, csymbol *cs, StkId arg, int idx)
+{
+       ffi_type type = csym_type(cs);
+
+       if (type == FFI_FUNC)
+               goto error;
+
+       switch (ttypenv(arg)) {
+       case KTAP_TLIGHTUSERDATA:
+               if (type != FFI_PTR) goto error;
+               break;
+       case KTAP_TBOOLEAN:
+       case KTAP_TNUMBER:
+               if (type != FFI_UINT8 && type != FFI_INT8
+               && type != FFI_UINT16 && type != FFI_INT16
+               && type != FFI_UINT32 && type != FFI_INT32
+               && type != FFI_UINT64 && type != FFI_INT64)
+                       goto error;
+               break;
+       case KTAP_TSTRING:
+               if (type != FFI_PTR && type != FFI_UINT8 && type != FFI_INT8)
+                       goto error;
+               break;
+       case KTAP_TCDATA:
+               if (cs != cd_csym(ks, cdvalue(arg)))
+                       goto error;
+               break;
+       default:
+               goto error;
+       }
+       return 0;
+
+ error:
+       kp_error(ks, "Error: Cannot convert to csymbol %s for arg %d\n",
+                       csym_name(cs), idx);
+       return -1;
+}
+
+
+static void ffi_unpack(ktap_state *ks, csymbol *cs, StkId arg, char *dst)
+{
+       ffi_type type = csym_type(cs);
+       size_t size = csym_size(ks, cs);
+       void *p;
+       struct ktap_cdata *cd;
+
+       switch (ttypenv(arg)) {
+       case KTAP_TBOOLEAN:
+               memset(dst, 0, size);
+               memcpy(dst, &bvalue(arg), sizeof(bool));
+               return;
+       case KTAP_TLIGHTUSERDATA:
+               memcpy(dst, pvalue(arg), size);
+               return;
+       case KTAP_TNUMBER:
+               memset(dst, 0, size);
+               memcpy(dst, &nvalue(arg), size < sizeof(ktap_number) ?
+                               size : sizeof(ktap_number));
+               return;
+       case KTAP_TSTRING:
+               p = &rawtsvalue(arg)->tsv + 1;
+               memcpy(dst, &p, size);
+               return;
+       }
+
+       cd = cdvalue(arg);
+       switch (type) {
+       case FFI_VOID:
+               kp_error(ks, "Error: Cannot copy data from void type\n");
+               return;
+       case FFI_UINT8:
+       case FFI_INT8:
+       case FFI_UINT16:
+       case FFI_INT16:
+       case FFI_UINT32:
+       case FFI_INT32:
+       case FFI_UINT64:
+       case FFI_INT64:
+               memcpy(dst, &cd_int(cd), size);
+               return;
+       case FFI_PTR:
+               memcpy(dst, &cd_ptr(cd), size);
+               return;
+       case FFI_STRUCT:
+               memcpy(dst, cd_struct(cd), size);
+               return;
+       case FFI_FUNC:
+       case FFI_UNKNOWN:
+               kp_error(ks, "Error: internal error for csymbol %s\n",
+                               csym_name(cs));
+               return;
+       }
+}
+
+#ifdef __x86_64
+
+extern void ffi_call_assem_x86_64(void *stack, void *temp_stack,
+                                       void *rvalue, void *func_addr, ffi_type 
rtype);
+
+static void ffi_call_x86_64(ktap_state *ks, csymbol_func *csf, void *rvalue)
+{
+       int i;
+       int gpr_nr;
+       int arg_bytes; /* total bytes needed for exceeded args in stack */
+       int mem_bytes; /* total bytes needed for memory storage */
+       char *stack, *stack_p, *gpr_p, *arg_p, *mem_p, *tmp_p;
+       csymbol *rsym;
+       ffi_type rtype;
+       size_t rsize;
+       bool ret_in_memory;
+
+       rsym = csymf_ret(ks, csf);
+       rtype = csym_type(rsym);
+       rsize = csym_size(ks, rsym);
+       ret_in_memory = false;
+       if (rtype == FFI_STRUCT) {
+               if (rsize > 16) {
+                       rvalue = kp_malloc(ks, rsize);
+                       rtype = FFI_VOID;
+                       ret_in_memory = true;
+               } else {
+                       /* much easier to always copy 16 bytes from registers */
+                       rvalue = kp_malloc(ks, 16);
+               }
+       }
+
+       gpr_nr = 0;
+       arg_bytes = mem_bytes = 0;
+       if (ret_in_memory)
+               gpr_nr++;
+       /* calculate bytes needed for stack */
+       for (i = 0; i < csymf_arg_nr(csf); i++) {
+               /*@TODO create api for this use case  28.11 2013 (houqp)*/
+               size_t size = csym_size(ks, csymf_arg(ks, csf, i));
+               size_t align = csym_align(ks, csymf_arg(ks, csf, i));
+               enum arg_status st = IN_REGISTER;
+               int n_gpr_nr = 0;
+               if (size > 32) {
+                       st = IN_MEMORY;
+                       n_gpr_nr = 1;
+               } else if (size > 16)
+                       st = IN_STACK;
+               else
+                       n_gpr_nr = ALIGN(size, GPR_SIZE) / GPR_SIZE;
+
+               if (gpr_nr + n_gpr_nr > MAX_GPR) {
+                       if (st == IN_MEMORY)
+                               arg_bytes += GPR_SIZE;
+                       else
+                               st = IN_STACK;
+               } else
+                       gpr_nr += n_gpr_nr;
+               if (st == IN_STACK) {
+                       arg_bytes = ALIGN(arg_bytes, align);
+                       arg_bytes += size;
+                       arg_bytes = ALIGN(arg_bytes, STACK_ALIGNMENT);
+               }
+               if (st == IN_MEMORY) {
+                       mem_bytes = ALIGN(mem_bytes, align);
+                       mem_bytes += size;
+                       mem_bytes = ALIGN(mem_bytes, STACK_ALIGNMENT);
+               }
+       }
+
+       /* apply space to fake stack for C function call */
+       stack = kp_malloc(ks, REDZONE_SIZE + MAX_GPR_SIZE + arg_bytes +
+                       mem_bytes + 6 * 8);
+       /* 128 bytes below %rsp is red zone */
+       /* stack should be 16-bytes aligned */
+       stack_p = ALIGN_STACK(stack + REDZONE_SIZE, 16);
+       /* save general purpose registers here */
+       gpr_p = stack_p;
+       memset(gpr_p, 0, MAX_GPR_SIZE);
+       /* save arguments in stack here */
+       arg_p = gpr_p + MAX_GPR_SIZE;
+       /* save arguments in memory here */
+       mem_p = arg_p + arg_bytes;
+       /* set additional space as temporary space */
+       tmp_p = mem_p + mem_bytes;
+
+       /* copy arguments here */
+       gpr_nr = 0;
+       if (ret_in_memory) {
+               memcpy(gpr_p, &rvalue, GPR_SIZE);
+               gpr_p += GPR_SIZE;
+               gpr_nr++;
+       }
+       for (i = 0; i < csymf_arg_nr(csf); i++) {
+               size_t size = csym_size(ks, csymf_arg(ks, csf, i));
+               size_t align = csym_align(ks, csymf_arg(ks, csf, i));
+               enum arg_status st = IN_REGISTER;
+               int n_gpr_nr = 0;
+               if (size > 32) {
+                       st = IN_MEMORY;
+                       n_gpr_nr = 1;
+               } else if (size > 16)
+                       st = IN_STACK;
+               else
+                       n_gpr_nr = ALIGN(size, GPR_SIZE) / GPR_SIZE;
+
+               if (st == IN_MEMORY)
+                       mem_p = ALIGN_STACK(mem_p, align);
+               /* Tricky way about storing it above mem_p. It won't overflow
+                * because temp region can be temporarily used if necesseary. */
+               ffi_unpack(ks, csymf_arg(ks, csf, i), kp_arg(ks, i+1), mem_p);
+               if (gpr_nr + n_gpr_nr > MAX_GPR) {
+                       if (st == IN_MEMORY) {
+                               memcpy(arg_p, &mem_p, GPR_SIZE);
+                               arg_p += GPR_SIZE;
+                       } else
+                               st = IN_STACK;
+               } else {
+                       memcpy(gpr_p, mem_p, n_gpr_nr * GPR_SIZE);
+                       gpr_p += n_gpr_nr * GPR_SIZE;
+                       gpr_nr += n_gpr_nr;
+               }
+               if (st == IN_STACK) {
+                       arg_p = ALIGN_STACK(arg_p, align);
+                       memcpy(arg_p, mem_p, size);
+                       arg_p += size;
+                       arg_p = ALIGN_STACK(arg_p, STACK_ALIGNMENT);
+               }
+               if (st == IN_MEMORY) {
+                       mem_p += size;
+                       mem_p = ALIGN_STACK(mem_p, STACK_ALIGNMENT);
+               }
+       }
+
+       kp_verbose_printf(ks, "Stack location: %p -redzone- %p -general purpose 
"
+                       "register used- %p -zero- %p -stack for argument- %p"
+                       " -memory for argument- %p -temp stack-\n",
+                       stack, stack_p, gpr_p, stack_p + MAX_GPR_SIZE,
+                       arg_p, mem_p);
+       kp_verbose_printf(ks, "GPR number: %d; arg in stack: %d; "
+                       "arg in mem: %d\n",
+                       gpr_nr, arg_bytes, mem_bytes);
+       kp_verbose_printf(ks, "Return: address %p type %d\n", rvalue, rtype);
+       kp_verbose_printf(ks, "Number of register used: %d\n", gpr_nr);
+       kp_verbose_printf(ks, "Start FFI call on %p\n", csf->addr);
+       ffi_call_assem_x86_64(stack_p, tmp_p, csf->addr, rvalue, rtype);
+       kp_verbose_printf(ks, "Finish FFI call\n");
+
+       kp_free(ks, stack);
+       return;
+}
+
+#else
+
+static void ffi_call_unsupported(ktap_state *ks,
+               csymbol_func *csf, void *rvalue)
+{
+       kp_error(ks, "unsupported architecture.\n");
+}
+
+#endif
+
+static int ffi_set_return(ktap_state *ks, void *rvalue, csymbol_id ret_id)
+{
+       ktap_cdata *cd;
+       ffi_type type = csym_type(id_to_csym(ks, ret_id));
+
+       /* push return value to ktap stack */
+       switch (type) {
+       case FFI_VOID:
+               return 0;
+       case FFI_UINT8:
+       case FFI_INT8:
+       case FFI_UINT16:
+       case FFI_INT16:
+       case FFI_UINT32:
+       case FFI_INT32:
+       case FFI_UINT64:
+       case FFI_INT64:
+               set_number(ks->top, (ktap_number)rvalue);
+               break;
+       case FFI_PTR:
+               cd = kp_cdata_new_ptr(ks, rvalue, ret_id);
+               set_cdata(ks->top, cd);
+               break;
+       case FFI_STRUCT:
+               cd = kp_cdata_new_struct(ks, rvalue, ret_id);
+               set_cdata(ks->top, cd);
+               break;
+       case FFI_FUNC:
+       case FFI_UNKNOWN:
+               kp_error(ks, "Error: Have not support ffi_type %s\n",
+                               ffi_type_name(type));
+               return 0;
+       }
+       incr_top(ks);
+       return 1;
+}
+
+/*
+ * 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 by now
+ */
+int kp_ffi_call(ktap_state *ks, csymbol_func *csf)
+{
+       int i, arg_nr = csf->arg_nr;
+       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, which should be %d\n",
+                               kp_arg_nr(ks), 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);
+               csymbol *atype = csymf_arg(ks, csf, i);
+               if (ffi_type_check(ks, atype, karg, i) < 0)
+                       goto out;
+       }
+
+       /* platform-specific calling workflow */
+       ffi_call(ks, csf, &rvalue);
+
+out:
+       return ffi_set_return(ks, rvalue, csymf_ret_id(csf));
+}
diff --git a/interpreter/ffi/ffi_symbol.c b/interpreter/ffi/ffi_symbol.c
new file mode 100644
index 0000000..fd32523
--- /dev/null
+++ b/interpreter/ffi/ffi_symbol.c
@@ -0,0 +1,172 @@
+/*
+ * 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_types.h"
+#include "../ktap.h"
+#include "../kp_vm.h"
+#include "../kp_obj.h"
+#include "../kp_str.h"
+#include "../kp_tab.h"
+#include "../kp_ffi.h"
+
+
+static inline csymbol *get_csym_arr(ktap_state *ks)
+{
+       return G(ks)->ffis.csym_arr;
+}
+
+static inline int get_csym_nr(ktap_state *ks)
+{
+       return G(ks)->ffis.csym_nr;
+}
+
+static inline void set_csym_arr(ktap_state *ks, csymbol *csym)
+{
+       G(ks)->ffis.csym_arr = csym;
+}
+
+static inline void set_csym_nr(ktap_state *ks, int nr)
+{
+       G(ks)->ffis.csym_nr = nr;
+}
+
+
+static inline ktap_tab *get_ffi_ctable(ktap_state *ks)
+{
+       return G(ks)->ffis.ctable;
+}
+
+static void setup_ffi_ctable(ktap_state *ks)
+{
+       ktap_value ffi_lib_name, ffi_mt;
+       ktap_tab *registry;
+       const ktap_value *gt;
+
+       gt = kp_tab_getint(hvalue(&G(ks)->registry), KTAP_RIDX_GLOBALS);
+
+       G(ks)->ffis.ctable = kp_tab_new(ks);
+
+       /* insert ffi C table to global table */
+       set_table(&ffi_mt, get_ffi_ctable(ks));
+       set_string(&ffi_lib_name, kp_tstring_new(ks, "C"));
+       registry = hvalue(gt);
+       kp_tab_setvalue(ks, registry, &ffi_lib_name, &ffi_mt);
+}
+
+
+void ffi_set_csym_arr(ktap_state *ks, int cs_nr, csymbol *new_arr)
+{
+       set_csym_nr(ks, cs_nr);
+       set_csym_arr(ks, new_arr);
+
+       if (!new_arr)
+               return;
+
+       setup_kp_ffi_symbol_table(ks);
+}
+
+inline csymbol *ffi_get_csym_by_id(ktap_state *ks, int id)
+{
+       return &(get_csym_arr(ks)[id]);
+}
+
+csymbol_id ffi_get_csym_id(ktap_state *ks, char *name)
+{
+       int i;
+
+       for (i = 0; i < get_csym_nr(ks); i++) {
+               if (!strcmp(name, csym_name(ffi_get_csym_by_id(ks, i)))) {
+                       return i;
+               }
+       }
+
+       return 0;
+}
+
+static void add_ffi_func_to_ctable(ktap_state *ks, csymbol_id id)
+{
+       ktap_value func_name, fv;
+       ktap_cdata *cd;
+       csymbol *cs;
+
+       /* push cdata to ctable */
+       set_cdata(&fv, kp_newobject(ks, KTAP_TCDATA, sizeof(ktap_cdata), NULL));
+       cd = cdvalue(&fv);
+       cd_set_csym_id(cd, id);
+
+       cs = id_to_csym(ks, id);
+       set_string(&func_name, kp_tstring_new(ks, csym_name(cs)));
+       kp_tab_setvalue(ks, get_ffi_ctable(ks), &func_name, &fv);
+}
+
+void setup_kp_ffi_symbol_table(ktap_state *ks)
+{
+       int i;
+       csymbol *cs;
+
+       setup_ffi_ctable(ks);
+
+       /* push all functions to ctable */
+       for (i = 0; i < get_csym_nr(ks); i++) {
+               cs = &get_csym_arr(ks)[i];
+               switch (cs->type) {
+               case FFI_FUNC:
+                       kp_verbose_printf(ks, "[%d] loading C function %s\n",
+                                       i, csym_name(cs));
+                       add_ffi_func_to_ctable(ks, i);
+                       kp_verbose_printf(ks, "%s loaded\n", csym_name(cs));
+                       break;
+               case FFI_STRUCT:
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+void kp_ffi_free_symbol(ktap_state *ks)
+{
+       int i;
+       csymbol_id *arg_ids;
+       csymbol *cs;
+
+       if (!get_csym_arr(ks))
+               return;
+
+       for (i = 0; i < get_csym_nr(ks); i++) {
+               cs = &get_csym_arr(ks)[i];
+               switch (csym_type(cs)) {
+               case FFI_FUNC:
+                       arg_ids = csym_func_arg_ids(cs);
+                       if (arg_ids)
+                               kp_free(ks, arg_ids);
+                       break;
+               case FFI_STRUCT:
+                       /*@TODO finish this  20.11 2013 (houqp)*/
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       kp_free(ks, get_csym_arr(ks));
+}
diff --git a/interpreter/ffi/ffi_util.c b/interpreter/ffi/ffi_util.c
new file mode 100644
index 0000000..98e9423
--- /dev/null
+++ b/interpreter/ffi/ffi_util.c
@@ -0,0 +1,93 @@
+/*
+ * ffi_util.c - utility function for ffi module
+ *
+ * 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_types.h"
+#include "../ktap.h"
+#include "../kp_vm.h"
+#include "../kp_ffi.h"
+
+size_t csym_size(ktap_state *ks, csymbol *cs)
+{
+       ffi_type type = csym_type(cs);
+       switch(type) {
+       case FFI_STRUCT:
+               if (csym_struct(cs)->align == 0)
+                       init_csym_struct(ks, csym_struct(cs));
+               return csym_struct(cs)->size;
+       default:
+               return ffi_type_size(type);
+       }
+}
+
+size_t csym_align(ktap_state *ks, csymbol *cs)
+{
+       ffi_type type = csym_type(cs);
+       switch(type) {
+       case FFI_STRUCT:
+               if (csym_struct(cs)->align == 0)
+                       init_csym_struct(ks, csym_struct(cs));
+               return csym_struct(cs)->align;
+       default:
+               return ffi_type_align(type);
+       }
+}
+
+size_t csym_struct_offset(ktap_state *ks, csymbol_struct *csst, int idx)
+{
+       int nr = csymst_mb_nr(csst);
+       size_t off = 0;
+       size_t align = 1;
+       int i;
+
+       if (idx < 0 || idx > nr)
+               return -1;
+       for (i = 0; i < idx; i++) {
+               csymbol *var_cs = csymst_mb(ks, csst, i);
+               size_t var_size = csym_size(ks, var_cs);
+               size_t var_align = csym_align(ks, var_cs);
+               off = ALIGN(off, var_align);
+               off += var_size;
+               align = align > var_align ? align : var_align;
+       }
+       off = ALIGN(off, align);
+       return off;
+}
+
+void init_csym_struct(ktap_state *ks, csymbol_struct *csst)
+{
+       int nr = csymst_mb_nr(csst);
+       size_t size = 0;
+       size_t align = 1;
+       int i;
+
+       for (i = 0; i < nr; i++) {
+               csymbol *var_cs = csymst_mb(ks, csst, i);
+               size_t var_size = csym_size(ks, var_cs);
+               size_t var_align = csym_align(ks, var_cs);
+               size = ALIGN(size, var_align);
+               size += var_size;
+               align = align > var_align ? align : var_align;
+       }
+       size = ALIGN(size, align);
+       csst->size = size;
+       csst->align = align;
+}
-- 
1.8.1.2


Other related posts:

  • » [ktap] [PATCHv3 4/8] Introduce ffi call module in vm - Qingping Hou