runtime/ffi/ffi_call.c - ktap

Data types defined

Functions defined

Macros defined

Source code

  1. /*
  2. * ffi_call.c - foreign function calling library support for ktap
  3. *
  4. * This file is part of ktap by Jovi Zhangwei.
  5. *
  6. * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
  7. *
  8. * ktap is free software; you can redistribute it and/or modify it
  9. * under the terms and conditions of the GNU General Public License,
  10. * version 2, as published by the Free Software Foundation.
  11. *
  12. * ktap is distributed in the hope it will be useful, but WITHOUT
  13. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  15. * more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along with
  18. * this program; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  20. */

  21. #include <linux/ctype.h>
  22. #include <linux/slab.h>
  23. #include "../../include/ktap_types.h"
  24. #include "../../include/ktap_ffi.h"
  25. #include "../ktap.h"
  26. #include "../kp_vm.h"
  27. #include "../kp_obj.h"

  28. static int ffi_type_check(ktap_state_t *ks, csymbol_func *csf, int idx)
  29. {
  30.     StkId arg;
  31.     csymbol *cs;

  32.     if (idx >= csymf_arg_nr(csf))
  33.         return 0;
  34.     arg = kp_arg(ks, idx + 1);
  35.     cs = csymf_arg(ks, csf, idx);

  36.     if (!kp_cdata_type_match(ks, cs, arg))
  37.         return 0;
  38.     else {
  39.         kp_error(ks, "Cannot convert to csymbol %s for arg %d\n",
  40.                 csym_name(cs), idx);
  41.         return -1;
  42.     }
  43. }

  44. static csymbol *ffi_get_arg_csym(ktap_state_t *ks, csymbol_func *csf, int idx)
  45. {
  46.     StkId arg;
  47.     csymbol *cs;

  48.     if (idx < csymf_arg_nr(csf))
  49.         return csymf_arg(ks, csf, idx);

  50.     arg = kp_arg(ks, idx + 1);
  51.     cs = id_to_csym(ks, ffi_get_csym_id(ks, "void *"));
  52.     switch (ttypenv(arg)) {
  53.     case KTAP_TYPE_LIGHTUSERDATA:
  54.     case KTAP_TYPE_BOOLEAN:
  55.     case KTAP_TYPE_NUMBER:
  56.     case KTAP_TYPE_STRING:
  57.         return cs;
  58.     case KTAP_TYPE_CDATA:
  59.         return cd_csym(ks, cdvalue(arg));
  60.     default:
  61.         kp_error(ks, "Error: Cannot get type for arg %d\n", idx);
  62.         return cs;
  63.     }
  64. }

  65. static void ffi_unpack(ktap_state_t *ks, char *dst, csymbol_func *csf,
  66.         int idx, int align)
  67. {
  68.     StkId arg = kp_arg(ks, idx + 1);
  69.     csymbol *cs = ffi_get_arg_csym(ks, csf, idx);
  70.     size_t size = csym_size(ks, cs);

  71.     /* initialize the destination section */
  72.     memset(dst, 0, ALIGN(size, align));

  73.     kp_cdata_unpack(ks, dst, cs, arg);
  74. }

  75. #ifdef __x86_64

  76. enum arg_status {
  77.     IN_REGISTER,
  78.     IN_MEMORY,
  79.     IN_STACK,
  80. };

  81. #define ALIGN_STACK(v, a) ((void *)(ALIGN(((uint64_t)v), a)))
  82. #define STACK_ALIGNMENT 8
  83. #define REDZONE_SIZE 128
  84. #define GPR_SIZE (sizeof(void *))
  85. #define MAX_GPR 6
  86. #define MAX_GPR_SIZE (MAX_GPR * GPR_SIZE)
  87. #define NEWSTACK_SIZE 512

  88. #define ffi_call_arch(ks, cf, rvalue) ffi_call_x86_64(ks, cf, rvalue)

  89. extern void ffi_call_assem_x86_64(void *stack, void *temp_stack,
  90.                     void *func_addr, void *rvalue, ffi_type rtype);

  91. static void ffi_call_x86_64(ktap_state_t *ks, csymbol_func *csf, void *rvalue)
  92. {
  93.     int i;
  94.     int gpr_nr;
  95.     int arg_bytes; /* total bytes needed for exceeded args in stack */
  96.     int mem_bytes; /* total bytes needed for memory storage */
  97.     char *stack, *stack_p, *gpr_p, *arg_p, *mem_p, *tmp_p;
  98.     int arg_nr;
  99.     csymbol *rsym;
  100.     ffi_type rtype;
  101.     size_t rsize;
  102.     bool ret_in_memory;
  103.     /* New stack to call C function */
  104.     char space[NEWSTACK_SIZE];

  105.     arg_nr = kp_arg_nr(ks);
  106.     rsym = csymf_ret(ks, csf);
  107.     rtype = csym_type(rsym);
  108.     rsize = csym_size(ks, rsym);
  109.     ret_in_memory = false;
  110.     if (rtype == FFI_STRUCT || rtype == FFI_UNION) {
  111.         if (rsize > 16) {
  112.             rvalue = kp_malloc(ks, rsize);
  113.             rtype = FFI_VOID;
  114.             ret_in_memory = true;
  115.         } else {
  116.             /* much easier to always copy 16 bytes from registers */
  117.             rvalue = kp_malloc(ks, 16);
  118.         }
  119.     }

  120.     gpr_nr = 0;
  121.     arg_bytes = mem_bytes = 0;
  122.     if (ret_in_memory)
  123.         gpr_nr++;
  124.     /* calculate bytes needed for stack */
  125.     for (i = 0; i < arg_nr; i++) {
  126.         csymbol *cs = ffi_get_arg_csym(ks, csf, i);
  127.         size_t size = csym_size(ks, cs);
  128.         size_t align = csym_align(ks, cs);
  129.         enum arg_status st = IN_REGISTER;
  130.         int n_gpr_nr = 0;
  131.         if (size > 32) {
  132.             st = IN_MEMORY;
  133.             n_gpr_nr = 1;
  134.         } else if (size > 16)
  135.             st = IN_STACK;
  136.         else
  137.             n_gpr_nr = ALIGN(size, GPR_SIZE) / GPR_SIZE;

  138.         if (gpr_nr + n_gpr_nr > MAX_GPR) {
  139.             if (st == IN_MEMORY)
  140.                 arg_bytes += GPR_SIZE;
  141.             else
  142.                 st = IN_STACK;
  143.         } else
  144.             gpr_nr += n_gpr_nr;
  145.         if (st == IN_STACK) {
  146.             arg_bytes = ALIGN(arg_bytes, align);
  147.             arg_bytes += size;
  148.             arg_bytes = ALIGN(arg_bytes, STACK_ALIGNMENT);
  149.         }
  150.         if (st == IN_MEMORY) {
  151.             mem_bytes = ALIGN(mem_bytes, align);
  152.             mem_bytes += size;
  153.             mem_bytes = ALIGN(mem_bytes, STACK_ALIGNMENT);
  154.         }
  155.     }

  156.     /* apply space to fake stack for C function call */
  157.     if (16 + REDZONE_SIZE + MAX_GPR_SIZE + arg_bytes +
  158.             mem_bytes + 6 * 8 >= NEWSTACK_SIZE) {
  159.         kp_error(ks, "Unable to handle that many arguments by now\n");
  160.         return;
  161.     }
  162.     stack = space;
  163.     /* 128 bytes below %rsp is red zone */
  164.     /* stack should be 16-bytes aligned */
  165.     stack_p = ALIGN_STACK(stack + REDZONE_SIZE, 16);
  166.     /* save general purpose registers here */
  167.     gpr_p = stack_p;
  168.     memset(gpr_p, 0, MAX_GPR_SIZE);
  169.     /* save arguments in stack here */
  170.     arg_p = gpr_p + MAX_GPR_SIZE;
  171.     /* save arguments in memory here */
  172.     mem_p = arg_p + arg_bytes;
  173.     /* set additional space as temporary space */
  174.     tmp_p = mem_p + mem_bytes;

  175.     /* copy arguments here */
  176.     gpr_nr = 0;
  177.     if (ret_in_memory) {
  178.         memcpy(gpr_p, &rvalue, GPR_SIZE);
  179.         gpr_p += GPR_SIZE;
  180.         gpr_nr++;
  181.     }
  182.     for (i = 0; i < arg_nr; i++) {
  183.         csymbol *cs = ffi_get_arg_csym(ks, csf, i);
  184.         size_t size = csym_size(ks, cs);
  185.         size_t align = csym_align(ks, cs);
  186.         enum arg_status st = IN_REGISTER;
  187.         int n_gpr_nr = 0;
  188.         if (size > 32) {
  189.             st = IN_MEMORY;
  190.             n_gpr_nr = 1;
  191.         } else if (size > 16)
  192.             st = IN_STACK;
  193.         else
  194.             n_gpr_nr = ALIGN(size, GPR_SIZE) / GPR_SIZE;

  195.         if (st == IN_MEMORY)
  196.             mem_p = ALIGN_STACK(mem_p, align);
  197.         /* Tricky way about storing it above mem_p. It won't overflow
  198.          * because temp region can be temporarily used if necesseary. */
  199.         ffi_unpack(ks, mem_p, csf, i, GPR_SIZE);
  200.         if (gpr_nr + n_gpr_nr > MAX_GPR) {
  201.             if (st == IN_MEMORY) {
  202.                 memcpy(arg_p, &mem_p, GPR_SIZE);
  203.                 arg_p += GPR_SIZE;
  204.             } else
  205.                 st = IN_STACK;
  206.         } else {
  207.             memcpy(gpr_p, mem_p, n_gpr_nr * GPR_SIZE);
  208.             gpr_p += n_gpr_nr * GPR_SIZE;
  209.             gpr_nr += n_gpr_nr;
  210.         }
  211.         if (st == IN_STACK) {
  212.             arg_p = ALIGN_STACK(arg_p, align);
  213.             memcpy(arg_p, mem_p, size);
  214.             arg_p += size;
  215.             arg_p = ALIGN_STACK(arg_p, STACK_ALIGNMENT);
  216.         }
  217.         if (st == IN_MEMORY) {
  218.             mem_p += size;
  219.             mem_p = ALIGN_STACK(mem_p, STACK_ALIGNMENT);
  220.         }
  221.     }

  222.     kp_verbose_printf(ks, "Stack location: %p -redzone- %p -general purpose "
  223.             "register used- %p -zero- %p -stack for argument- %p"
  224.             " -memory for argument- %p -temp stack-\n",
  225.             stack, stack_p, gpr_p, stack_p + MAX_GPR_SIZE,
  226.             arg_p, mem_p);
  227.     kp_verbose_printf(ks, "GPR number: %d; arg in stack: %d; "
  228.             "arg in mem: %d\n",
  229.             gpr_nr, arg_bytes, mem_bytes);
  230.     kp_verbose_printf(ks, "Return: address %p type %d\n", rvalue, rtype);
  231.     kp_verbose_printf(ks, "Number of register used: %d\n", gpr_nr);
  232.     kp_verbose_printf(ks, "Start FFI call on %p\n", csf->addr);
  233.     ffi_call_assem_x86_64(stack_p, tmp_p, csf->addr, rvalue, rtype);
  234. }

  235. #else /* non-supported platform */

  236. #define ffi_call(ks, cf, rvalue) ffi_call_unsupported(ks, cf, rvalue)

  237. static void ffi_call_unsupported(ktap_state_t *ks,
  238.         csymbol_func *csf, void *rvalue)
  239. {
  240.     kp_error(ks, "unsupported architecture.\n");
  241. }

  242. #endif /* end for platform-specific setting */


  243. static int ffi_set_return(ktap_state_t *ks, void *rvalue, csymbol_id ret_id)
  244. {
  245.     ktap_cdata_t *cd;
  246.     ffi_type type = csym_type(id_to_csym(ks, ret_id));

  247.     /* push return value to ktap stack */
  248.     switch (type) {
  249.     case FFI_VOID:
  250.         return 0;
  251.     case FFI_UINT8:
  252.     case FFI_INT8:
  253.     case FFI_UINT16:
  254.     case FFI_INT16:
  255.     case FFI_UINT32:
  256.     case FFI_INT32:
  257.     case FFI_UINT64:
  258.     case FFI_INT64:
  259.         set_number(ks->top, (ktap_number)rvalue);
  260.         break;
  261.     case FFI_PTR:
  262.         cd = kp_cdata_new_ptr(ks, rvalue, -1, ret_id, 0);
  263.         set_cdata(ks->top, cd);
  264.         break;
  265.     case FFI_STRUCT:
  266.     case FFI_UNION:
  267.         cd = kp_cdata_new_record(ks, rvalue, ret_id);
  268.         set_cdata(ks->top, cd);
  269.         break;
  270.     case FFI_FUNC:
  271.     case FFI_UNKNOWN:
  272.         kp_error(ks, "Error: Have not support ffi_type %s\n",
  273.                 ffi_type_name(type));
  274.         return 0;
  275.     }
  276.     incr_top(ks);
  277.     return 1;
  278. }

  279. /*
  280. * Call C into function
  281. * First argument should be function symbol address, argument types
  282. * and return type.
  283. * Left arguments should be arguments for calling the C function.
  284. * Types between Ktap and C are converted automatically.
  285. * Only support x86_64 function call by now
  286. */
  287. int ffi_call(ktap_state_t *ks, csymbol_func *csf)
  288. {
  289.     int i;
  290.     int expected_arg_nr, arg_nr;
  291.     ktap_closure_t *cl;
  292.     void *rvalue;

  293.     expected_arg_nr = csymf_arg_nr(csf);
  294.     arg_nr = kp_arg_nr(ks);

  295.     /* check stack status for C call */
  296.     if (!csf->has_var_arg && expected_arg_nr != arg_nr) {
  297.         kp_error(ks, "wrong argument number %d, which should be %d\n",
  298.                 arg_nr, expected_arg_nr);
  299.         goto out;
  300.     }
  301.     if (csf->has_var_arg && expected_arg_nr > arg_nr) {
  302.         kp_error(ks, "argument number %d, which should be bigger than %d\n",
  303.                 arg_nr, expected_arg_nr);
  304.         goto out;
  305.     }

  306.     /* maybe useful later, leave it here first */
  307.     cl = clvalue(kp_arg(ks, arg_nr + 1));

  308.     /* check the argument types */
  309.     for (i = 0; i < arg_nr; i++) {
  310.         if (ffi_type_check(ks, csf, i) < 0)
  311.             goto out;
  312.     }

  313.     /* platform-specific calling workflow */
  314.     ffi_call_arch(ks, csf, &rvalue);
  315.     kp_verbose_printf(ks, "Finish FFI call\n");

  316. out:
  317.     return ffi_set_return(ks, rvalue, csymf_ret_id(csf));
  318. }