xScopedStackFrame::xScopedStackFrame(bool base_frame, bool save_base_pointer, int offset) { m_base_frame = base_frame; m_save_base_pointer = save_base_pointer; m_offset = offset; #ifdef __x86_64__ m_offset += 8; // Call stores the return address (4 bytes) // Note rbp can surely be optimized in 64 bits if (m_base_frame) { xPUSH( rbp ); xMOV( rbp, rsp ); m_offset += 8; } else if (m_save_base_pointer) { xPUSH( rbp ); m_offset += 8; } xPUSH( rbx ); xPUSH( r12 ); xPUSH( r13 ); xPUSH( r14 ); xPUSH( r15 ); m_offset += 40; #else m_offset += 4; // Call stores the return address (4 bytes) // Create a new frame if (m_base_frame) { xPUSH( ebp ); xMOV( ebp, esp ); m_offset += 4; } else if (m_save_base_pointer) { xPUSH( ebp ); m_offset += 4; } // Save the register context xPUSH( edi ); xPUSH( esi ); xPUSH( ebx ); m_offset += 12; #endif ALIGN_STACK(-(16 - m_offset % 16)); }
xScopedStackFrame::~xScopedStackFrame() { ALIGN_STACK(16 - m_offset % 16); #ifdef __x86_64__ // Restore the register context xPOP( r15 ); xPOP( r14 ); xPOP( r13 ); xPOP( r12 ); xPOP( rbx ); // Destroy the frame if (m_base_frame) { xLEAVE(); } else if (m_save_base_pointer) { xPOP( rbp ); } #else // Restore the register context xPOP( ebx ); xPOP( esi ); xPOP( edi ); // Destroy the frame if (m_base_frame) { xLEAVE(); } else if (m_save_base_pointer) { xPOP( ebp ); } #endif }
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); }