2 * ffi_call.c - foreign function calling library support for ktap
4 * This file is part of ktap by Jovi Zhangwei.
6 * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
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.
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
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.
22 #include <linux/ctype.h>
23 #include <linux/slab.h>
24 #include "../../include/ktap_types.h"
25 #include "../../include/ktap_ffi.h"
28 #include "../kp_obj.h"
30 static int ffi_type_check(ktap_state *ks, csymbol_func *csf, int idx)
36 if (idx >= csymf_arg_nr(csf))
38 arg = kp_arg(ks, idx + 1);
39 cs = csymf_arg(ks, csf, idx);
45 switch (ttypenv(arg)) {
46 case KTAP_TLIGHTUSERDATA:
47 if (type != FFI_PTR) goto error;
51 if (type != FFI_UINT8 && type != FFI_INT8
52 && type != FFI_UINT16 && type != FFI_INT16
53 && type != FFI_UINT32 && type != FFI_INT32
54 && type != FFI_UINT64 && type != FFI_INT64)
58 if (type != FFI_PTR && type != FFI_UINT8 && type != FFI_INT8)
62 if (cs != cd_csym(ks, cdvalue(arg)))
71 kp_error(ks, "Error: Cannot convert to csymbol %s for arg %d\n",
76 static csymbol *ffi_get_arg_csym(ktap_state *ks, csymbol_func *csf, int idx)
81 if (idx < csymf_arg_nr(csf))
82 return csymf_arg(ks, csf, idx);
84 arg = kp_arg(ks, idx + 1);
85 cs = id_to_csym(ks, ffi_get_csym_id(ks, "void *"));
86 switch (ttypenv(arg)) {
87 case KTAP_TLIGHTUSERDATA:
93 return cd_csym(ks, cdvalue(arg));
95 kp_error(ks, "Error: Cannot get type for arg %d\n", idx);
100 static void ffi_unpack(ktap_state *ks, csymbol_func *csf, int idx,
101 char *dst, int align)
103 StkId arg = kp_arg(ks, idx + 1);
104 csymbol *cs = ffi_get_arg_csym(ks, csf, idx);
105 ffi_type type = csym_type(cs);
106 size_t size = csym_size(ks, cs);
108 struct ktap_cdata *cd;
110 /* initialize the destination section */
111 memset(dst, 0, ALIGN(size, align));
113 switch (ttypenv(arg)) {
115 memcpy(dst, &bvalue(arg), sizeof(bool));
117 case KTAP_TLIGHTUSERDATA:
118 memcpy(dst, pvalue(arg), size);
121 memcpy(dst, &nvalue(arg), size < sizeof(ktap_number) ?
122 size : sizeof(ktap_number));
125 p = &rawtsvalue(arg)->tsv + 1;
126 memcpy(dst, &p, size);
133 kp_error(ks, "Error: Cannot copy data from void type\n");
143 memcpy(dst, &cd_int(cd), size);
146 memcpy(dst, &cd_ptr(cd), size);
149 memcpy(dst, cd_struct(cd), size);
153 kp_error(ks, "Error: internal error for csymbol %s\n",
167 #define ALIGN_STACK(v, a) ((void *)(ALIGN(((uint64_t)v), a)))
168 #define STACK_ALIGNMENT 8
169 #define REDZONE_SIZE 128
170 #define GPR_SIZE (sizeof(void *))
172 #define MAX_GPR_SIZE (MAX_GPR * GPR_SIZE)
173 #define NEWSTACK_SIZE 512
175 #define ffi_call(ks, cf, rvalue) ffi_call_x86_64(ks, cf, rvalue)
177 extern void ffi_call_assem_x86_64(void *stack, void *temp_stack,
178 void *func_addr, void *rvalue, ffi_type rtype);
180 static void ffi_call_x86_64(ktap_state *ks, csymbol_func *csf, void *rvalue)
184 int arg_bytes; /* total bytes needed for exceeded args in stack */
185 int mem_bytes; /* total bytes needed for memory storage */
186 char *stack, *stack_p, *gpr_p, *arg_p, *mem_p, *tmp_p;
192 /* New stack to call C function */
193 char space[NEWSTACK_SIZE];
195 arg_nr = kp_arg_nr(ks);
196 rsym = csymf_ret(ks, csf);
197 rtype = csym_type(rsym);
198 rsize = csym_size(ks, rsym);
199 ret_in_memory = false;
200 if (rtype == FFI_STRUCT) {
202 rvalue = kp_malloc(ks, rsize);
204 ret_in_memory = true;
206 /* much easier to always copy 16 bytes from registers */
207 rvalue = kp_malloc(ks, 16);
212 arg_bytes = mem_bytes = 0;
215 /* calculate bytes needed for stack */
216 for (i = 0; i < arg_nr; i++) {
217 csymbol *cs = ffi_get_arg_csym(ks, csf, i);
218 size_t size = csym_size(ks, cs);
219 size_t align = csym_align(ks, cs);
220 enum arg_status st = IN_REGISTER;
225 } else if (size > 16)
228 n_gpr_nr = ALIGN(size, GPR_SIZE) / GPR_SIZE;
230 if (gpr_nr + n_gpr_nr > MAX_GPR) {
232 arg_bytes += GPR_SIZE;
237 if (st == IN_STACK) {
238 arg_bytes = ALIGN(arg_bytes, align);
240 arg_bytes = ALIGN(arg_bytes, STACK_ALIGNMENT);
242 if (st == IN_MEMORY) {
243 mem_bytes = ALIGN(mem_bytes, align);
245 mem_bytes = ALIGN(mem_bytes, STACK_ALIGNMENT);
249 /* apply space to fake stack for C function call */
250 if (16 + REDZONE_SIZE + MAX_GPR_SIZE + arg_bytes +
251 mem_bytes + 6 * 8 >= NEWSTACK_SIZE) {
252 kp_error(ks, "Unable to handle that many arguments by now\n");
256 /* 128 bytes below %rsp is red zone */
257 /* stack should be 16-bytes aligned */
258 stack_p = ALIGN_STACK(stack + REDZONE_SIZE, 16);
259 /* save general purpose registers here */
261 memset(gpr_p, 0, MAX_GPR_SIZE);
262 /* save arguments in stack here */
263 arg_p = gpr_p + MAX_GPR_SIZE;
264 /* save arguments in memory here */
265 mem_p = arg_p + arg_bytes;
266 /* set additional space as temporary space */
267 tmp_p = mem_p + mem_bytes;
269 /* copy arguments here */
272 memcpy(gpr_p, &rvalue, GPR_SIZE);
276 for (i = 0; i < arg_nr; i++) {
277 csymbol *cs = ffi_get_arg_csym(ks, csf, i);
278 size_t size = csym_size(ks, cs);
279 size_t align = csym_align(ks, cs);
280 enum arg_status st = IN_REGISTER;
285 } else if (size > 16)
288 n_gpr_nr = ALIGN(size, GPR_SIZE) / GPR_SIZE;
291 mem_p = ALIGN_STACK(mem_p, align);
292 /* Tricky way about storing it above mem_p. It won't overflow
293 * because temp region can be temporarily used if necesseary. */
294 ffi_unpack(ks, csf, i, mem_p, GPR_SIZE);
295 if (gpr_nr + n_gpr_nr > MAX_GPR) {
296 if (st == IN_MEMORY) {
297 memcpy(arg_p, &mem_p, GPR_SIZE);
302 memcpy(gpr_p, mem_p, n_gpr_nr * GPR_SIZE);
303 gpr_p += n_gpr_nr * GPR_SIZE;
306 if (st == IN_STACK) {
307 arg_p = ALIGN_STACK(arg_p, align);
308 memcpy(arg_p, mem_p, size);
310 arg_p = ALIGN_STACK(arg_p, STACK_ALIGNMENT);
312 if (st == IN_MEMORY) {
314 mem_p = ALIGN_STACK(mem_p, STACK_ALIGNMENT);
318 kp_verbose_printf(ks, "Stack location: %p -redzone- %p -general purpose "
319 "register used- %p -zero- %p -stack for argument- %p"
320 " -memory for argument- %p -temp stack-\n",
321 stack, stack_p, gpr_p, stack_p + MAX_GPR_SIZE,
323 kp_verbose_printf(ks, "GPR number: %d; arg in stack: %d; "
325 gpr_nr, arg_bytes, mem_bytes);
326 kp_verbose_printf(ks, "Return: address %p type %d\n", rvalue, rtype);
327 kp_verbose_printf(ks, "Number of register used: %d\n", gpr_nr);
328 kp_verbose_printf(ks, "Start FFI call on %p\n", csf->addr);
329 ffi_call_assem_x86_64(stack_p, tmp_p, csf->addr, rvalue, rtype);
332 #else /* non-supported platform */
334 #define ffi_call(ks, cf, rvalue) ffi_call_unsupported(ks, cf, rvalue)
336 static void ffi_call_unsupported(ktap_state *ks,
337 csymbol_func *csf, void *rvalue)
339 kp_error(ks, "unsupported architecture.\n");
342 #endif /* end for platform-specific setting */
345 static int ffi_set_return(ktap_state *ks, void *rvalue, csymbol_id ret_id)
348 ffi_type type = csym_type(id_to_csym(ks, ret_id));
350 /* push return value to ktap stack */
362 set_number(ks->top, (ktap_number)rvalue);
365 cd = kp_cdata_new_ptr(ks, rvalue, ret_id);
366 set_cdata(ks->top, cd);
369 cd = kp_cdata_new_struct(ks, rvalue, ret_id);
370 set_cdata(ks->top, cd);
374 kp_error(ks, "Error: Have not support ffi_type %s\n",
375 ffi_type_name(type));
383 * Call C into function
384 * First argument should be function symbol address, argument types
386 * Left arguments should be arguments for calling the C function.
387 * Types between Ktap and C are converted automatically.
388 * Only support x86_64 function call by now
390 int kp_ffi_call(ktap_state *ks, csymbol_func *csf)
393 int expected_arg_nr, arg_nr;
397 expected_arg_nr = csymf_arg_nr(csf);
398 arg_nr = kp_arg_nr(ks);
400 /* check stack status for C call */
401 if (!csf->has_var_arg && expected_arg_nr != arg_nr) {
402 kp_error(ks, "wrong argument number %d, which should be %d\n",
403 arg_nr, expected_arg_nr);
406 if (csf->has_var_arg && expected_arg_nr > arg_nr) {
407 kp_error(ks, "argument number %d, which should be bigger than %d\n",
408 arg_nr, expected_arg_nr);
412 /* maybe useful later, leave it here first */
413 cl = clvalue(kp_arg(ks, arg_nr + 1));
415 /* check the argument types */
416 for (i = 0; i < arg_nr; i++) {
417 if (ffi_type_check(ks, csf, i) < 0)
421 /* platform-specific calling workflow */
422 ffi_call(ks, csf, &rvalue);
423 kp_verbose_printf(ks, "Finish FFI call\n");
426 return ffi_set_return(ks, rvalue, csymf_ret_id(csf));