int cp_ctype_free() { int i; csymbol *cs; if (cts.stack) free(cts.stack); if (cs_arr) { for (i = 0; i < cs_nr; i++) { cs = &cs_arr[i]; if (csym_type(cs) == FFI_FUNC) { if (csym_func(cs)->arg_ids) free(csym_func(cs)->arg_ids); } else if (csym_type(cs) == FFI_STRUCT) { if (csym_struct(cs)->members) free(csym_struct(cs)->members); } } free(cs_arr); } if (cte_arr) { free(cte_arr); } return 0; }
ktap_cdata_t *kp_cdata_new_by_id(ktap_state_t *ks, void *val, csymbol_id id) { csymbol *cs = id_to_csym(ks, id); switch (csym_type(cs)) { case FFI_VOID: kp_error(ks, "Error: Cannot new a void type\n"); return NULL; case FFI_UINT8: case FFI_INT8: case FFI_UINT16: case FFI_INT16: case FFI_UINT32: case FFI_INT32: case FFI_UINT64: case FFI_INT64: return kp_cdata_new_number(ks, val, id); case FFI_PTR: return kp_cdata_new_ptr(ks, val, 0, id, 0); case FFI_STRUCT: case FFI_UNION: return kp_cdata_new_record(ks, val, id); case FFI_FUNC: kp_error(ks, "Error: Cannot new a function type\n"); return NULL; case FFI_UNKNOWN: default: kp_error(ks, "Error: unknown csymbol type %s\n", csym_name(cs)); return NULL; } }
/* Init its cdata type, but not its actual value */ static void kp_cdata_init(ktap_state_t *ks, ktap_val_t *val, void *addr, int len, csymbol_id id) { ffi_type type = csym_type(id_to_csym(ks, id)); switch (type) { case FFI_PTR: set_cdata(val, kp_cdata_new_ptr(ks, addr, len, id, 0)); break; case FFI_STRUCT: case FFI_UNION: set_cdata(val, kp_cdata_new_record(ks, addr, id)); break; 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 all these value into ktap_number(long) */ set_number(val, 0); break; default: set_cdata(val, kp_cdata_new(ks, id)); break; } }
static void init_builtin_type(struct cp_ctype *ct, ffi_type ftype) { csymbol cs; int cs_id; csym_type(&cs) = ftype; strncpy(csym_name(&cs), ffi_type_name(ftype), CSYM_NAME_MAX_LEN); cs_id = cp_ctype_reg_csymbol(&cs); memset(ct, 0, sizeof(*ct)); ct->ffi_cs_id = cs_id; switch (ftype) { case FFI_VOID: ct_set_type(ct, VOID_TYPE, 0); break; case FFI_UINT8: ct_set_type(ct, INT8_TYPE, 1); break; case FFI_INT8: ct_set_type(ct, INT8_TYPE, 0); break; case FFI_UINT16: ct_set_type(ct, INT16_TYPE, 1); break; case FFI_INT16: ct_set_type(ct, INT16_TYPE, 0); break; case FFI_UINT32: ct_set_type(ct, INT32_TYPE, 1); break; case FFI_INT32: ct_set_type(ct, INT32_TYPE, 0); break; case FFI_UINT64: ct_set_type(ct, INT64_TYPE, 1); break; case FFI_INT64: ct_set_type(ct, INT64_TYPE, 0); break; default: break; } ct->base_size = ffi_type_size(ftype); ct->align_mask = ffi_type_align(ftype) - 1; ct->is_defined = 1; }
static void kp_cdata_value(ktap_state_t *ks, ktap_val_t *val, void **out_addr, size_t *out_size, void **temp) { ktap_cdata_t *cd; csymbol *cs; ffi_type type; switch (ttypenv(val)) { case KTAP_TYPE_BOOLEAN: *out_addr = &bvalue(val); *out_size = sizeof(int); return; case KTAP_TYPE_LIGHTUSERDATA: *out_addr = pvalue(val); *out_size = sizeof(void *); return; case KTAP_TYPE_NUMBER: *out_addr = &nvalue(val); *out_size = sizeof(ktap_number); return; case KTAP_TYPE_STRING: *temp = (void *)svalue(val); *out_addr = temp; *out_size = sizeof(void *); return; } cd = cdvalue(val); cs = cd_csym(ks, cd); type = csym_type(cs); *out_size = csym_size(ks, cs); 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: *out_addr = &cd_int(cd); return; case FFI_PTR: *out_addr = &cd_ptr(cd); return; case FFI_STRUCT: case FFI_UNION: *out_addr = cd_record(cd); return; case FFI_FUNC: case FFI_UNKNOWN: kp_error(ks, "Error: internal error for csymbol %s\n", csym_name(cs)); return; } }
int cp_symbol_build_func(struct cp_ctype *type, const char *fname, int fn_size) { int i = 1, arg_nr, id; int *argsym_id_arr; csymbol nfcs; csymbol_func *fcs; if (cts.top == 0 || fn_size < 0 || !fname) { cp_error("invalid function definition.\n"); } argsym_id_arr = NULL; memset(&nfcs, 0, sizeof(csymbol)); csym_type(&nfcs) = FFI_FUNC; strncpy(csym_name(&nfcs), fname, fn_size); fcs = csym_func(&nfcs); fcs->has_var_arg = type->has_var_arg; /* Type needed for handling variable args handle */ if (fcs->has_var_arg && !ctype_lookup_type("void *")) cp_symbol_build_pointer(ctype_lookup_type("void")); /* Fetch start address of function */ fcs->addr = (void *)find_kernel_symbol(csym_name(&nfcs)); if (!fcs->addr) cp_error("wrong function address for %s\n", csym_name(&nfcs)); /* bottom of the stack is return type */ fcs->ret_id = ct_stack_ct(0)->ffi_cs_id; /* the rest is argument type */ if (cts.top == 1) { /* function takes no argument */ arg_nr = 0; } else { arg_nr = cts.top - 1; argsym_id_arr = malloc(arg_nr * sizeof(int)); if (!argsym_id_arr) cp_error("failed to allocate memory for function args.\n"); for (i = 0; i < arg_nr; i++) { argsym_id_arr[i] = ct_stack_ct(i+1)->ffi_cs_id; } } fcs->arg_nr = arg_nr; fcs->arg_ids = argsym_id_arr; id = cp_ctype_reg_csymbol(&nfcs); /* clear stack since we have consumed all the ctypes */ ctype_stack_reset(); return id; }
void cp_ctype_dump_stack() { int i; struct cp_ctype *ct; printf("---------------------------\n"); printf("start of ctype stack (%d) dump: \n", cts.top); for (i = 0; i < cts.top; i++) { ct = ct_stack_ct(i); printf("[%d] -> cp_ctype: %d, sym_type: %d, pointer: %d " "symbol_id: %d, name: %s\n", i, ct->type, csym_type(ct_ffi_cs(ct)), ct->pointers, ct->ffi_cs_id, ct_stack(i)->name); } }
static int ffi_set_return(ktap_state_t *ks, void *rvalue, csymbol_id ret_id) { ktap_cdata_t *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, -1, ret_id, 0); set_cdata(ks->top, cd); break; case FFI_STRUCT: case FFI_UNION: cd = kp_cdata_new_record(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; }
/* Notice: Even if the types are matched, there may exist the lost of * data in the unpack process due to precision */ int kp_cdata_type_match(ktap_state_t *ks, csymbol *cs, ktap_val_t *val) { ffi_type type; type = csym_type(cs); if (type == FFI_FUNC) goto error; switch (ttypenv(val)) { case KTAP_TYPE_LIGHTUSERDATA: if (type != FFI_PTR) goto error; break; case KTAP_TYPE_BOOLEAN: case KTAP_TYPE_NUMBER: 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_TYPE_STRING: if (type != FFI_PTR && type != FFI_UINT8 && type != FFI_INT8) goto error; break; case KTAP_TYPE_CDATA: if (cs != cd_csym(ks, cdvalue(val))) goto error; break; default: goto error; } return 0; error: return -1; }
static void ffi_call_x86_64(ktap_state_t *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; int arg_nr; csymbol *rsym; ffi_type rtype; size_t rsize; bool ret_in_memory; /* New stack to call C function */ char space[NEWSTACK_SIZE]; arg_nr = kp_arg_nr(ks); rsym = csymf_ret(ks, csf); rtype = csym_type(rsym); rsize = csym_size(ks, rsym); ret_in_memory = false; if (rtype == FFI_STRUCT || rtype == FFI_UNION) { 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 < arg_nr; i++) { csymbol *cs = ffi_get_arg_csym(ks, csf, i); size_t size = csym_size(ks, cs); size_t align = csym_align(ks, cs); 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 */ if (16 + REDZONE_SIZE + MAX_GPR_SIZE + arg_bytes + mem_bytes + 6 * 8 >= NEWSTACK_SIZE) { kp_error(ks, "Unable to handle that many arguments by now\n"); return; } stack = space; /* 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 < arg_nr; i++) { csymbol *cs = ffi_get_arg_csym(ks, csf, i); size_t size = csym_size(ks, cs); size_t align = csym_align(ks, cs); 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, mem_p, csf, i, GPR_SIZE); 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); }