This is the mail archive of the
systemtap@sourceware.org
mailing list for the systemtap project.
[Patch 3/5][Djprobe]Djprobe Coexist with Kprobes
- From: Masami Hiramatsu <hiramatu at sdl dot hitachi dot co dot jp>
- To: systemtap at sources dot redhat dot com
- Cc: Satoshi Oshima <soshima at redhat dot com>, Hideo Aoki <haoki at redhat dot com>, sugita at sdl dot hitachi dot co dot jp
- Date: Thu, 29 Sep 2005 21:59:38 +0900
- Subject: [Patch 3/5][Djprobe]Djprobe Coexist with Kprobes
Hi,
This patch is an i386 architecture dependent code
of djprobe.
--
Masami HIRAMATSU
2nd Research Dept.
Hitachi, Ltd., Systems Development Laboratory
E-mail: hiramatu@sdl.hitachi.co.jp
arch/i386/kernel/Makefile | 2
arch/i386/kernel/kprobes.c | 134 ++++++++++++++++++++++++++++++++++++++++
arch/i386/kernel/stub_djprobe.S | 77 ++++++++++++++++++++++
include/asm-i386/kprobes.h | 26 +++++++
4 files changed, 238 insertions(+), 1 deletion(-)
diff -Narup linux-2.6.13-mm1.djp.2/arch/i386/kernel/Makefile linux-2.6.13-mm1.djp.3/arch/i386/kernel/Makefile
--- linux-2.6.13-mm1.djp.2/arch/i386/kernel/Makefile 2005-09-05 19:11:16.000000000 +0900
+++ linux-2.6.13-mm1.djp.3/arch/i386/kernel/Makefile 2005-09-09 19:05:41.000000000 +0900
@@ -28,7 +28,7 @@ obj-$(CONFIG_X86_REBOOTFIXUPS) += reboot
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o crash.o
obj-$(CONFIG_X86_NUMAQ) += numaq.o
obj-$(CONFIG_X86_SUMMIT_NUMA) += summit.o
-obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_KPROBES) += kprobes.o stub_djprobe.o
obj-$(CONFIG_MODULES) += module.o
obj-y += sysenter.o vsyscall.o
obj-$(CONFIG_ACPI_SRAT) += srat.o
diff -Narup linux-2.6.13-mm1.djp.2/arch/i386/kernel/kprobes.c linux-2.6.13-mm1.djp.3/arch/i386/kernel/kprobes.c
--- linux-2.6.13-mm1.djp.2/arch/i386/kernel/kprobes.c 2005-09-12 20:07:55.000000000 +0900
+++ linux-2.6.13-mm1.djp.3/arch/i386/kernel/kprobes.c 2005-09-12 20:08:48.000000000 +0900
@@ -36,6 +36,7 @@
#include <asm/cacheflush.h>
#include <asm/kdebug.h>
#include <asm/desc.h>
+#include <asm/processor.h>
static struct kprobe *current_kprobe;
static unsigned long kprobe_status, kprobe_old_eflags, kprobe_saved_eflags;
@@ -558,3 +559,136 @@ int __init arch_init_kprobes(void)
{
return register_kprobe(&trampoline_p);
}
+
+/*
+ * When kernel full preemption is enabled, we can't ensure that no threads
+ * are executing the modified code. It may be stored in the stack of the
+ * threads. In this case, the djprobe interfaces are emulated by using
+ * kprobe.
+ * When kernel full preemption is disabled, threads are scheduled
+ * from only limited addresses. So it is easy to check whether the
+ * preemption can occur in the modified code.
+ */
+#ifndef CONFIG_PREEMPT
+/*
+ * On pentium series, Unsynchronized cross-modifying code
+ * operations can cause unexpected instruction execution results.
+ * So after code modified, we should synchronize it on each processor.
+ */
+static void __local_serialize_cpu(void * info)
+{
+ serialize_cpu();
+}
+
+static inline void smp_serialize_cpus(void)
+{
+ on_each_cpu(__local_serialize_cpu, NULL, 1,1);
+}
+
+/* jmp code manipulators */
+struct __arch_jmp_op {
+ char op;
+ long raddr;
+} __attribute__((packed));
+/* insert jmp code */
+static inline void __set_jmp_op(void *from, void *to)
+{
+ struct __arch_jmp_op *jop;
+ jop = (struct __arch_jmp_op *)from;
+ jop->raddr=(long)(to) - ((long)(from) + 5);
+ smp_serialize_cpus();
+ jop->op = RELATIVEJUMP_INSTRUCTION;
+}
+/* switch back to the kprobe */
+static inline void __set_breakpoint_op(void *dest, void *orig)
+{
+ struct __arch_jmp_op *jop = (struct __arch_jmp_op *)dest,
+ *jop2 = (struct __arch_jmp_op *)orig;
+
+ jop->op = BREAKPOINT_INSTRUCTION;
+ smp_serialize_cpus();
+ jop->raddr = jop2->raddr;
+}
+
+/* djprobe call back function: called from stub code */
+static void asmlinkage djprobe_callback(struct djprobe_instance * djpi,
+ struct pt_regs *regs)
+{
+ /*TODO: use list*/
+ if (djpi->djp && djpi->djp->handler)
+ djpi->djp->handler(djpi->djp, regs);
+}
+
+/*
+ * Copy post processing instructions
+ * Target instructions MUST be relocatable.
+ */
+int __kprobes arch_prepare_djprobe_instance(struct djprobe_instance *djpi,
+ unsigned long size)
+{
+ kprobe_opcode_t *stub;
+ stub = djpi->stub.insn;
+
+ /* copy arch-dep-instance from template */
+ memcpy((void*)stub, (void*)&arch_tmpl_stub_entry, ARCH_STUB_SIZE);
+
+ /* set probe information */
+ *((long*)(stub + ARCH_STUB_VAL_IDX)) = (long)djpi;
+ /* set probe function */
+ *((long*)(stub + ARCH_STUB_CALL_IDX)) = (long)djprobe_callback;
+
+ /* copy instructions into the middle of axporbe instance */
+ memcpy((void*)(stub + ARCH_STUB_INST_IDX),
+ (void*)djpi->kp.addr, size);
+ djpi->stub.size = size;
+
+ /* set returning jmp instruction at the tail of axporbe instance*/
+ __set_jmp_op(stub + ARCH_STUB_INST_IDX + size,
+ (void*)((long)djpi->kp.addr + size));
+
+ return 0;
+}
+
+/* Insert "jmp" instruction into the probing point. */
+void __kprobes arch_install_djprobe_instance(struct djprobe_instance *djpi)
+{
+ kprobe_opcode_t *stub;
+ stub = djpi->stub.insn;
+ __set_jmp_op((void*)djpi->kp.addr, (void*)stub);
+}
+
+/* Write back original instructions & kprobe */
+void __kprobes arch_uninstall_djprobe_instance(struct djprobe_instance *djpi)
+{
+ kprobe_opcode_t *stub;
+ stub = &djpi->stub.insn[ARCH_STUB_INST_IDX];
+ __set_breakpoint_op((void*)djpi->kp.addr, (void*)stub);
+ /* fixup dummy instruction */
+ djpi->kp.ainsn.insn[0] = djpi->stub.insn[ARCH_STUB_INST_IDX];
+}
+
+/* djprobe handler : switch to a bypass code */
+int __kprobes djprobe_bypass_handler(struct kprobe * kp, struct pt_regs * regs)
+{
+ struct djprobe_instance *djpi =
+ container_of(kp,struct djprobe_instance, kp);
+ kprobe_opcode_t *stub = djpi->stub.insn;
+
+ if (DJPI_EMPTY(djpi)) {
+ /* fixup dummy instruction */
+ kp->ainsn.insn[0] = djpi->stub.insn[ARCH_STUB_INST_IDX];
+ return 0;
+ } else {
+ regs->eip = (unsigned long)stub;
+ regs->eflags |= TF_MASK;
+ regs->eflags &= ~IF_MASK;
+ /*
+ * dummy return code :
+ * This code is to avoid to be changed eip value by
+ * resume_execute() of kprobes
+ */
+ kp->ainsn.insn[0] = RETURN_INSTRUCTION;
+ return 1; /* already prepared */
+ }
+}
+#endif /*!CONFIG_PREEMPT*/
diff -Narup linux-2.6.13-mm1.djp.2/arch/i386/kernel/stub_djprobe.S linux-2.6.13-mm1.djp.3/arch/i386/kernel/stub_djprobe.S
--- linux-2.6.13-mm1.djp.2/arch/i386/kernel/stub_djprobe.S 1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.13-mm1.djp.3/arch/i386/kernel/stub_djprobe.S 2005-09-09 19:05:41.000000000 +0900
@@ -0,0 +1,77 @@
+/*
+ * linux/arch/i386/stub_djprobe.S
+ *
+ * Copyright (C) HITACHI,LTD. 2005
+ * Created by Masami Hiramatsu <hiramatu@sdl.hitachi.co.jp>
+ */
+
+#include <linux/config.h>
+
+# jmp into this function from other functions.
+.global arch_tmpl_stub_entry
+arch_tmpl_stub_entry:
+ nop
+ subl $8, %esp #skip segment registers.
+ pushf
+ subl $20, %esp #skip segment registers.
+ pushl %eax
+ pushl %ebp
+ pushl %edi
+ pushl %esi
+ pushl %edx
+ pushl %ecx
+ pushl %ebx
+
+ movl %esp, %eax
+ pushl %eax
+ addl $60, %eax
+ movl %eax, 56(%esp)
+.global arch_tmpl_stub_val
+arch_tmpl_stub_val:
+ movl $0xffffffff, %eax
+ pushl %eax
+.global arch_tmpl_stub_call
+arch_tmpl_stub_call:
+ movl $0xffffffff, %eax
+ call *%eax
+ addl $8, %esp
+
+ popl %ebx
+ popl %ecx
+ popl %edx
+ popl %esi
+ popl %edi
+ popl %ebp
+ popl %eax
+ addl $20, %esp
+ popf
+ addl $8, %esp
+.global arch_tmpl_stub_inst
+arch_tmpl_stub_inst:
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+.global arch_tmpl_stub_end
+arch_tmpl_stub_end:
diff -Narup linux-2.6.13-mm1.djp.2/include/asm-i386/kprobes.h linux-2.6.13-mm1.djp.3/include/asm-i386/kprobes.h
--- linux-2.6.13-mm1.djp.2/include/asm-i386/kprobes.h 2005-09-05 19:11:16.000000000 +0900
+++ linux-2.6.13-mm1.djp.3/include/asm-i386/kprobes.h 2005-09-12 14:07:34.000000000 +0900
@@ -31,6 +31,8 @@ struct pt_regs;
typedef u8 kprobe_opcode_t;
#define BREAKPOINT_INSTRUCTION 0xcc
+#define RELATIVEJUMP_INSTRUCTION 0xe9
+#define RETURN_INSTRUCTION 0xc3
#define MAX_INSN_SIZE 16
#define MAX_STACK_SIZE 64
#define MIN_STACK_SIZE(ADDR) (((MAX_STACK_SIZE) < \
@@ -40,6 +42,30 @@ typedef u8 kprobe_opcode_t;
#define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry
#define ARCH_SUPPORTS_KRETPROBES
+#ifndef CONFIG_PREEMPT
+#define ARCH_SUPPORTS_DJPROBES
+#endif /* CONFIG_PREEMPT */
+
+/* stub template code */
+extern kprobe_opcode_t arch_tmpl_stub_entry;
+extern kprobe_opcode_t arch_tmpl_stub_val;
+extern kprobe_opcode_t arch_tmpl_stub_call;
+extern kprobe_opcode_t arch_tmpl_stub_inst;
+extern kprobe_opcode_t arch_tmpl_stub_end;
+
+#define ARCH_STUB_VAL_IDX ((long)&arch_tmpl_stub_val - (long)&arch_tmpl_stub_entry + 1)
+#define ARCH_STUB_CALL_IDX ((long)&arch_tmpl_stub_call - (long)&arch_tmpl_stub_entry + 1)
+#define ARCH_STUB_INST_IDX ((long)&arch_tmpl_stub_inst - (long)&arch_tmpl_stub_entry)
+#define ARCH_STUB_SIZE ((long)&arch_tmpl_stub_end - (long)&arch_tmpl_stub_entry)
+
+#define ARCH_STUB_INSN_MAX 20
+#define ARCH_STUB_INSN_MIN 5
+
+struct arch_djprobe_stub {
+ kprobe_opcode_t *insn;
+ int size;
+};
+#define DJPI_ARCH_SIZE(djpi) (djpi->stub.size)
void kretprobe_trampoline(void);