diff --git a/arch/x86/include/asm/kprobes.h b/arch/x86/include/asm/kprobes.h
index 017f4bb..3627694 100644
--- a/arch/x86/include/asm/kprobes.h
+++ b/arch/x86/include/asm/kprobes.h
@@ -31,6 +31,7 @@
 #define RELATIVEJUMP_OPCODE 0xe9
 #define RELATIVEJUMP_SIZE 5
 #define RELATIVECALL_OPCODE 0xe8
+#define RELATIVECALL_SIZE 5
 #define RELATIVE_ADDR_SIZE 4
 #define MAX_STACK_SIZE 64
 #define MAX_OPTIMIZED_LENGTH (MAX_INSN_SIZE + RELATIVE_ADDR_SIZE)
@@ -38,8 +39,10 @@
 #ifdef __ASSEMBLY__
 
 #define KPROBE_OPCODE_SIZE     1
+#define OPT_CALL_TEMPLATE_SIZE (optprobe_call_template_end - \
+		optprobe_call_template_entry)
 #define MAX_OPTINSN_SIZE ((optprobe_template_end - optprobe_template_entry) + \
-	MAX_OPTIMIZED_LENGTH + RELATIVEJUMP_SIZE)
+	MAX_OPTIMIZED_LENGTH + RELATIVEJUMP_SIZE + OPT_CALL_TEMPLATE_SIZE)
 
 #ifdef CONFIG_EARLY_KPROBES
 # define EARLY_KPROBES_CODES_AREA					\
@@ -81,10 +84,20 @@ extern __visible kprobe_opcode_t optprobe_template_entry;
 extern __visible kprobe_opcode_t optprobe_template_val;
 extern __visible kprobe_opcode_t optprobe_template_call;
 extern __visible kprobe_opcode_t optprobe_template_end;
+
+extern __visible kprobe_opcode_t optprobe_call_template_entry;
+extern __visible kprobe_opcode_t optprobe_call_template_val_destaddr;
+extern __visible kprobe_opcode_t optprobe_call_template_val_retaddr;
+extern __visible kprobe_opcode_t optprobe_call_template_end;
+
+#define OPT_CALL_TEMPLATE_SIZE				\
+	((unsigned long)&optprobe_call_template_end -	\
+	 (unsigned long)&optprobe_call_template_entry)
 #define MAX_OPTINSN_SIZE 				\
 	(((unsigned long)&optprobe_template_end -	\
 	  (unsigned long)&optprobe_template_entry) +	\
-	 MAX_OPTIMIZED_LENGTH + RELATIVEJUMP_SIZE)
+	 MAX_OPTIMIZED_LENGTH + RELATIVEJUMP_SIZE +	\
+	 OPT_CALL_TEMPLATE_SIZE)
 
 extern const int kretprobe_blacklist_size;
 
diff --git a/arch/x86/kernel/kprobes/opt.c b/arch/x86/kernel/kprobes/opt.c
index dc5fccb..05dd06f 100644
--- a/arch/x86/kernel/kprobes/opt.c
+++ b/arch/x86/kernel/kprobes/opt.c
@@ -39,6 +39,23 @@
 
 #include "common.h"
 
+static inline bool
+is_relcall(u8 *addr)
+{
+	return (*(u8 *)(addr) == RELATIVECALL_OPCODE);
+}
+
+static inline void *
+get_relcall_target(u8 *addr)
+{
+	struct __arch_relative_insn {
+		u8 op;
+		s32 raddr;
+	} __packed *insn;
+	insn = (struct __arch_relative_insn *)addr;
+	return (void *)((unsigned long)addr + RELATIVECALL_SIZE + insn->raddr);
+}
+
 unsigned long __recover_optprobed_insn(kprobe_opcode_t *buf, unsigned long addr)
 {
 	struct optimized_kprobe *op;
@@ -89,6 +106,48 @@ static void synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val)
 }
 
 asm (
+#ifdef CONFIG_X86_64
+			".global optprobe_call_template_entry\n"
+			"optprobe_call_template_entry:"
+			"pushq %rdi\n"
+			".global optprobe_call_template_val_destaddr\n"
+			"optprobe_call_template_val_destaddr:"
+			ASM_NOP5
+			ASM_NOP5
+			"pushq %rdi\n"
+			".global optprobe_call_template_val_retaddr\n"
+			"optprobe_call_template_val_retaddr:"
+			ASM_NOP5
+			ASM_NOP5
+			"xchgq %rdi, 8(%rsp)\n"
+			"retq\n"
+#else /* CONFIG_X86_32 */
+			".global optprobe_call_template_entry\n"
+			"optprobe_call_template_entry:"
+			"push %edi\n"
+			".global optprobe_call_template_val_destaddr\n"
+			"optprobe_call_template_val_destaddr:"
+			ASM_NOP5
+			"push %edi\n"
+			".global optprobe_call_template_val_retaddr\n"
+			"optprobe_call_template_val_retaddr:"
+			ASM_NOP5
+			"xchg %edi, 4(%esp)\n"
+			"ret\n"
+#endif
+			".global optprobe_call_template_end\n"
+			"optprobe_call_template_end:\n"
+);
+
+#define __OPTCALL_TMPL_MOVE_DESTADDR_IDX \
+	((long)&optprobe_call_template_val_destaddr - (long)&optprobe_call_template_entry)
+#define __OPTCALL_TMPL_MOVE_RETADDR_IDX \
+	((long)&optprobe_call_template_val_retaddr - (long)&optprobe_call_template_entry)
+#define __OPTCALL_TMPL_END_IDX \
+	((long)&optprobe_call_template_end - (long)&optprobe_call_template_entry)
+#define OPTCALL_TMPL_SIZE	__OPTCALL_TMPL_END_IDX
+
+asm (
 			".global optprobe_template_entry\n"
 			"optprobe_template_entry:\n"
 #ifdef CONFIG_X86_64
@@ -135,6 +194,10 @@ asm (
 #define TMPL_END_IDX \
 	((long)&optprobe_template_end - (long)&optprobe_template_entry)
 
+#define TMPL_OPTCALL_MOVE_DESTADDR_IDX	(TMPL_END_IDX + __OPTCALL_TMPL_MOVE_DESTADDR_IDX)
+#define TMPL_OPTCALL_MOVE_RETADDR_IDX	(TMPL_END_IDX + __OPTCALL_TMPL_MOVE_RETADDR_IDX)
+#define TMPL_OPTCALL_END_IDX	(TMPL_END_IDX + __OPTCALL_TMPL_END_IDX)
+
 #define INT3_SIZE sizeof(kprobe_opcode_t)
 
 /* Optimized kprobe call back function: called from optinsn */
@@ -175,6 +238,12 @@ static int copy_optimized_instructions(u8 *dest, u8 *src)
 {
 	int len = 0, ret;
 
+	if (is_relcall(src)) {
+		memcpy(dest, &optprobe_call_template_entry,
+				OPTCALL_TMPL_SIZE);
+		return OPTCALL_TMPL_SIZE;
+	}
+
 	while (len < RELATIVEJUMP_SIZE) {
 		ret = __copy_instruction(dest + len, src + len);
 		if (!ret || !can_boost(dest + len))
@@ -365,9 +434,16 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op,
 	/* Set probe function call */
 	synthesize_relcall(buf + TMPL_CALL_IDX, optimized_callback);
 
-	/* Set returning jmp instruction at the tail of out-of-line buffer */
-	synthesize_reljump(buf + TMPL_END_IDX + op->optinsn.size,
-			   (u8 *)op->kp.addr + op->optinsn.size);
+	if (!is_relcall(op->kp.addr)) {
+		/* Set returning jmp instruction at the tail of out-of-line buffer */
+		synthesize_reljump(buf + TMPL_END_IDX + op->optinsn.size,
+				(u8 *)op->kp.addr + op->optinsn.size);
+	} else {
+		synthesize_set_arg1(buf + TMPL_OPTCALL_MOVE_DESTADDR_IDX,
+				(unsigned long)(get_relcall_target(op->kp.addr)));
+		synthesize_set_arg1(buf + TMPL_OPTCALL_MOVE_RETADDR_IDX,
+				(unsigned long)(op->kp.addr + RELATIVECALL_SIZE));
+	}
 
 	flush_icache_range((unsigned long) buf,
 			   (unsigned long) buf + TMPL_END_IDX +
