Skip to contentSkip to conversation

PATCH Kprobes for x86_64- 2.6.9-final

PATCH In review Prasanna S Panchamukhi 3 participants 12 messages Oct 28, 2004 – Oct 30, 2004 kprobes

lore
Share & tools
lore
#0 lore raw

cover PATCH Kprobes for x86_64- 2.6.9-final

Hi,

Below are the Kprobes patches ported to x86_64 architecture. I have updated these patches with suggestions from Andi Kleen. Thanks Andi for reviewing and providing your feedback. These patches can be applied over 2.6.9-final. Please see the description in the individual patches.

Please review and provide your feedback.

[1/3] kprobes-arch-i386-changes.patch
[2/3] kprobes-x86_64-port-2.6.9-final.patch
[3/3] kprobes-arch-sparc64-changes.patch

Thanks
Prasanna

Signature · 5 lines
Prasanna S Panchamukhi
Linux Technology Center
India Software Labs, IBM Bangalore
Ph: 91-80-25044636
<[email protected]>
#1 lore raw

[1/3] kprobes-arch-i386-changes.patch
Couple of routine's are modified to support kprobes porting to other architectures.

---

---

 linux-2.6.9-final-prasanna/arch/i386/kernel/kprobes.c |   23 ++++++++++--------
 linux-2.6.9-final-prasanna/include/asm-i386/kprobes.h |    7 +++++
 linux-2.6.9-final-prasanna/include/linux/kprobes.h    |    5 ++-
 linux-2.6.9-final-prasanna/kernel/kprobes.c           |    6 +++-
 4 files changed, 29 insertions(+), 12 deletions(-)
arch/i386/kernel/kprobes.c renamed from arch/i386/kernel/kprobes.c~kprobes-arch-i386-changes +14 -9
diff -puN arch/i386/kernel/kprobes.c~kprobes-arch-i386-changes arch/i386/kernel/kprobes.c
@@ -58,9 +58,14 @@ static inline int is_IF_modifier(kprobe_
 	return 0;
 }
 
-void arch_prepare_kprobe(struct kprobe *p)
+int arch_prepare_kprobe(struct kprobe *p)
+{
+	memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
+	return 0;
+}
+
+void arch_remove_kprobe(struct kprobe *p)
 {
-	memcpy(p->insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
 }
 
 static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs)
@@ -73,7 +78,7 @@ static inline void prepare_singlestep(st
 {
 	regs->eflags |= TF_MASK;
 	regs->eflags &= ~IF_MASK;
-	regs->eip = (unsigned long)&p->insn;
+	regs->eip = (unsigned long)&p->ainsn.insn;
 }
 
 /*
@@ -153,7 +158,7 @@ static inline int kprobe_handler(struct 
  * instruction.  To avoid the SMP problems that can occur when we
  * temporarily put back the original opcode to single-step, we
  * single-stepped a copy of the instruction.  The address of this
- * copy is p->insn.
+ * copy is p->ainsn.insn.
  *
  * This function prepares to return from the post-single-step
  * interrupt.  We have to fix up the stack as follows:
@@ -173,10 +178,10 @@ static void resume_execution(struct kpro
 {
 	unsigned long *tos = (unsigned long *)&regs->esp;
 	unsigned long next_eip = 0;
-	unsigned long copy_eip = (unsigned long)&p->insn;
+	unsigned long copy_eip = (unsigned long)&p->ainsn.insn;
 	unsigned long orig_eip = (unsigned long)p->addr;
 
-	switch (p->insn[0]) {
+	switch (p->ainsn.insn[0]) {
 	case 0x9c:		/* pushfl */
 		*tos &= ~(TF_MASK | IF_MASK);
 		*tos |= kprobe_old_eflags;
@@ -185,13 +190,13 @@ static void resume_execution(struct kpro
 		*tos = orig_eip + (*tos - copy_eip);
 		break;
 	case 0xff:
-		if ((p->insn[1] & 0x30) == 0x10) {
+		if ((p->ainsn.insn[1] & 0x30) == 0x10) {
 			/* call absolute, indirect */
 			/* Fix return addr; eip is correct. */
 			next_eip = regs->eip;
 			*tos = orig_eip + (*tos - copy_eip);
-		} else if (((p->insn[1] & 0x31) == 0x20) ||	/* jmp near, absolute indirect */
-			   ((p->insn[1] & 0x31) == 0x21)) {	/* jmp far, absolute indirect */
+		} else if (((p->ainsn.insn[1] & 0x31) == 0x20) ||	/* jmp near, absolute indirect */
+			   ((p->ainsn.insn[1] & 0x31) == 0x21)) {	/* jmp far, absolute indirect */
 			/* eip is correct. */
 			next_eip = regs->eip;
 		}
include/asm-i386/kprobes.h renamed from include/asm-i386/kprobes.h~kprobes-arch-i386-changes +7 -0
diff -puN include/asm-i386/kprobes.h~kprobes-arch-i386-changes include/asm-i386/kprobes.h
@@ -38,6 +38,13 @@ typedef u8 kprobe_opcode_t;
 	? (MAX_STACK_SIZE) \
 	: (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))
 
+/* Architecture specific copy of original instruction*/
+struct arch_specific_insn {
+	/* copy of the original instruction */
+	kprobe_opcode_t insn[MAX_INSN_SIZE];
+};
+
+
 /* trap3/1 are intr gates for kprobes.  So, restore the status of IF,
  * if necessary, before executing the original int3/1 (trap) handler.
  */
include/linux/kprobes.h renamed from include/linux/kprobes.h~kprobes-arch-i386-changes +3 -2
diff -puN include/linux/kprobes.h~kprobes-arch-i386-changes include/linux/kprobes.h
@@ -64,7 +64,7 @@ struct kprobe {
 	kprobe_opcode_t opcode;
 
 	/* copy of the original instruction */
-	kprobe_opcode_t insn[MAX_INSN_SIZE];
+	struct arch_specific_insn ainsn;
 };
 
 /*
@@ -94,7 +94,8 @@ static inline int kprobe_running(void)
 	return kprobe_cpu == smp_processor_id();
 }
 
-extern void arch_prepare_kprobe(struct kprobe *p);
+extern int arch_prepare_kprobe(struct kprobe *p);
+extern void arch_remove_kprobe(struct kprobe *p);
 extern void show_registers(struct pt_regs *regs);
 
 /* Get the kprobe at this addr (if any).  Must have called lock_kprobes */
kernel/kprobes.c renamed from kernel/kprobes.c~kprobes-arch-i386-changes +5 -1
diff -puN kernel/kprobes.c~kprobes-arch-i386-changes kernel/kprobes.c
@@ -84,10 +84,13 @@ int register_kprobe(struct kprobe *p)
 		ret = -EEXIST;
 		goto out;
 	}
+
+	if ((ret = arch_prepare_kprobe(p)) != 0) {
+		goto out;
+	}
 	hlist_add_head(&p->hlist,
 		       &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]);
 
-	arch_prepare_kprobe(p);
 	p->opcode = *p->addr;
 	*p->addr = BREAKPOINT_INSTRUCTION;
 	flush_icache_range((unsigned long) p->addr,
@@ -101,6 +104,7 @@ void unregister_kprobe(struct kprobe *p)
 {
 	unsigned long flags;
 	spin_lock_irqsave(&kprobe_lock, flags);
+	arch_remove_kprobe(p);
 	*p->addr = p->opcode;
 	hlist_del(&p->hlist);
 	flush_icache_range((unsigned long) p->addr,

_

Signature · 5 lines
Prasanna S Panchamukhi
Linux Technology Center
India Software Labs, IBM Bangalore
Ph: 91-80-25044636
<[email protected]>
#2 lore raw

[2/3] kprobes-x86_64-port-2.6.9-final.patch

Adopted from i386 architecture.

Kprobes:

Helps developers to trap at almost any kernel code address, specifying a handler routine to be invoked when the breakpoint is hit. Useful for analysing the Linux kernel by collecting debugging information non-disruptively. Employs single-stepping out-of-line to avoid probe misses on SMP and may be especially useful in aiding debugging elusive races and problems on live systems. More elaborate dynamic tracing tools can be built over the kprobes interface.

Sample usage:

	To place a probe on __blockdev_direct_IO:
	static int probe_handler(struct kprobe *p, struct pt_regs *)
	{
		... whatever ...
	}
	struct kprobe kp = {
		.addr = __blockdev_direct_IO,
		.pre_handler = probe_handler
	};
	register_kprobe(&kp);

Jprobes:

A special kprobe type which can be placed on function entry points, and employs a simple mirroring principle to allow seamless access to the arguments of a function being probed. The probe handler routine should have the same prototype as the function being probed.

The way it works is that when the probe is hit, the breakpoint handler simply irets to the probe handler's rip while retaining register and stack state corresponding to the function entry. After it is done, the probe handler calls jprobe_return() which traps again to restore processor state and switch back to the probed function. Linus noted correctly at KS that we need to be careful as gcc assumes that the callee owns arguments. We save and restore enough stack bytes to cover argument space.

Sample Usage:

	static int jip_queue_xmit(struct sk_buff *skb, int ipfragok)
	{
		... whatever ...
		jprobe_return();
		return 0;
	}

	struct jprobe jp = {
		{.addr = (kprobe_opcode_t *) ip_queue_xmit},
		.entry = (kprobe_opcode_t *) jip_queue_xmit
	};
	register_jprobe(&jp);

---

---

---

 linux-2.6.9-final-prasanna/arch/x86_64/Kconfig.debug        |   10 
 linux-2.6.9-final-prasanna/arch/x86_64/kernel/Makefile      |    1 
 linux-2.6.9-final-prasanna/arch/x86_64/kernel/kprobes.c     |  480 ++++++++++++
 linux-2.6.9-final-prasanna/arch/x86_64/kernel/traps.c       |   30 
 linux-2.6.9-final-prasanna/arch/x86_64/kernel/x8664_ksyms.c |    2 
 linux-2.6.9-final-prasanna/arch/x86_64/mm/fault.c           |    7 
 linux-2.6.9-final-prasanna/include/asm-x86_64/kdebug.h      |    3 
 linux-2.6.9-final-prasanna/include/asm-x86_64/kprobes.h     |   62 +
 8 files changed, 591 insertions(+), 4 deletions(-)
arch/x86_64/Kconfig.debug renamed from arch/x86_64/Kconfig.debug~kprobes-x86_64-port-2.6.9-final +10 -0
diff -puN arch/x86_64/Kconfig.debug~kprobes-x86_64-port-2.6.9-final arch/x86_64/Kconfig.debug
@@ -18,6 +18,16 @@ config INIT_DEBUG
 	  Fill __init and __initdata at the end of boot. This helps debugging
 	  illegal uses of __init and __initdata after initialization.
 
+config KPROBES
+	bool "Kprobes"
+	depends on DEBUG_KERNEL
+	help
+	  Kprobes allows you to trap at almost any kernel address and
+	  execute a callback function.  register_kprobe() establishes
+	  a probepoint and specifies the callback.  Kprobes is useful
+	  for kernel debugging, non-intrusive instrumentation and testing.
+	  If in doubt, say "N".
+
 config SCHEDSTATS
 	bool "Collect scheduler statistics"
 	depends on DEBUG_KERNEL && PROC_FS
arch/x86_64/kernel/kprobes.c new file +480 -0

480 lines not on this page · raw message

arch/x86_64/kernel/Makefile renamed from arch/x86_64/kernel/Makefile~kprobes-x86_64-port-2.6.9-final +1 -0
diff -puN arch/x86_64/kernel/Makefile~kprobes-x86_64-port-2.6.9-final arch/x86_64/kernel/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_EARLY_PRINTK)	+= early_prin
 obj-$(CONFIG_GART_IOMMU)	+= pci-gart.o aperture.o
 obj-$(CONFIG_DUMMY_IOMMU)	+= pci-nommu.o pci-dma.o
 obj-$(CONFIG_SWIOTLB)		+= swiotlb.o
+obj-$(CONFIG_KPROBES)		+= kprobes.o
 
 obj-$(CONFIG_MODULES)		+= module.o
 
arch/x86_64/kernel/traps.c renamed from arch/x86_64/kernel/traps.c~kprobes-x86_64-port-2.6.9-final +27 -3
diff -puN arch/x86_64/kernel/traps.c~kprobes-x86_64-port-2.6.9-final arch/x86_64/kernel/traps.c
@@ -46,6 +46,7 @@
 
 #include <linux/irq.h>
 
+
 extern struct gate_struct idt_table[256]; 
 
 asmlinkage void divide_error(void);
@@ -72,6 +73,17 @@ asmlinkage void spurious_interrupt_bug(v
 asmlinkage void call_debug(void);
 
 struct notifier_block *die_chain;
+static spinlock_t die_notifier_lock = SPIN_LOCK_UNLOCKED;
+
+int register_die_notifier(struct notifier_block *nb)
+{
+	int err = 0;
+	unsigned long flags;
+	spin_lock_irqsave(&die_notifier_lock, flags);
+	err = notifier_chain_register(&die_chain, nb);
+	spin_unlock_irqrestore(&die_notifier_lock, flags);
+	return err;
+}
 
 static inline void conditional_sti(struct pt_regs *regs)
 {
@@ -458,7 +470,6 @@ asmlinkage void do_##name(struct pt_regs
 }
 
 DO_ERROR_INFO( 0, SIGFPE,  "divide error", divide_error, FPE_INTDIV, regs->rip)
-DO_ERROR( 3, SIGTRAP, "int3", int3);
 DO_ERROR( 4, SIGSEGV, "overflow", overflow)
 DO_ERROR( 5, SIGSEGV, "bounds", bounds)
 DO_ERROR_INFO( 6, SIGILL,  "invalid operand", invalid_op, ILL_ILLOPN, regs->rip)
@@ -601,6 +612,15 @@ asmlinkage void default_do_nmi(struct pt
 	inb(0x71);		/* dummy */
 }
 
+asmlinkage void do_int3(struct pt_regs * regs, long error_code)
+{
+	if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP) == NOTIFY_STOP) {
+		return;
+	}
+	do_trap(3, SIGTRAP, "int3", regs, error_code, NULL);
+	return;
+}
+
 /* runs on IST stack. */
 asmlinkage void *do_debug(struct pt_regs * regs, unsigned long error_code)
 {
@@ -630,6 +650,10 @@ asmlinkage void *do_debug(struct pt_regs
 
 	asm("movq %%db6,%0" : "=r" (condition));
 
+	if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code,
+						SIGTRAP) == NOTIFY_STOP) {
+		return regs;
+	}
 	conditional_sti(regs);
 
 	/* Mask out spurious debug traps due to lazy DR7 setting */
@@ -862,8 +886,8 @@ void __init trap_init(void)
 	set_intr_gate(0,&divide_error);
 	set_intr_gate_ist(1,&debug,DEBUG_STACK);
 	set_intr_gate_ist(2,&nmi,NMI_STACK);
-	set_system_gate(3,&int3);	/* int3-5 can be called from all */
-	set_system_gate(4,&overflow);
+	set_intr_gate(3,&int3);
+	set_system_gate(4,&overflow);	/* int4-5 can be called from all */
 	set_system_gate(5,&bounds);
 	set_intr_gate(6,&invalid_op);
 	set_intr_gate(7,&device_not_available);
arch/x86_64/kernel/x8664_ksyms.c renamed from arch/x86_64/kernel/x8664_ksyms.c~kprobes-x86_64-port-2.6.9-final +2 -0
diff -puN arch/x86_64/kernel/x8664_ksyms.c~kprobes-x86_64-port-2.6.9-final arch/x86_64/kernel/x8664_ksyms.c
@@ -32,6 +32,7 @@
 #include <asm/unistd.h>
 #include <asm/delay.h>
 #include <asm/tlbflush.h>
+#include <asm/kdebug.h>
 
 extern spinlock_t rtc_lock;
 
@@ -197,6 +198,7 @@ EXPORT_SYMBOL(atomic_dec_and_lock);
 #endif
 
 EXPORT_SYMBOL(die_chain);
+EXPORT_SYMBOL(register_die_notifier);
 
 #ifdef CONFIG_SMP
 EXPORT_SYMBOL(cpu_sibling_map);
arch/x86_64/mm/fault.c renamed from arch/x86_64/mm/fault.c~kprobes-x86_64-port-2.6.9-final +7 -0
diff -puN arch/x86_64/mm/fault.c~kprobes-x86_64-port-2.6.9-final arch/x86_64/mm/fault.c
@@ -23,6 +23,7 @@
 #include <linux/vt_kern.h>		/* For unblank_screen() */
 #include <linux/compiler.h>
 #include <linux/module.h>
+#include <linux/kprobes.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -32,6 +33,7 @@
 #include <asm/proto.h>
 #include <asm/kdebug.h>
 #include <asm-generic/sections.h>
+#include <asm/kdebug.h>
 
 void bust_spinlocks(int yes)
 {
@@ -268,6 +270,11 @@ asmlinkage void do_page_fault(struct pt_
 
 	/* get the address */
 	__asm__("movq %%cr2,%0":"=r" (address));
+	if (kprobe_running()) {
+		if (notify_die(DIE_PAGE_FAULT, "page fault", regs, error_code, 14,
+						SIGSEGV) == NOTIFY_STOP)
+			return;
+	}
 
 	if (likely(regs->eflags & X86_EFLAGS_IF))
 		local_irq_enable();
include/asm-x86_64/kdebug.h renamed from include/asm-x86_64/kdebug.h~kprobes-x86_64-port-2.6.9-final +2 -1
diff -puN include/asm-x86_64/kdebug.h~kprobes-x86_64-port-2.6.9-final include/asm-x86_64/kdebug.h
@@ -16,8 +16,8 @@ struct die_args { 
 /* Note - you should never unregister because that can race with NMIs.
    If you really want to do it first unregister - then synchronize_kernel - then free. 
   */
+int register_die_notifier(struct notifier_block *nb);
 extern struct notifier_block *die_chain;
-
 /* Grossly misnamed. */
 enum die_val { 
 	DIE_OOPS = 1,
@@ -32,6 +32,7 @@ enum die_val { 
 	DIE_GPF,
 	DIE_CALL,
 	DIE_NMI_IPI,
+	DIE_PAGE_FAULT,
 }; 
 	
 static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err,int trap, int sig)
include/asm-x86_64/kprobes.h new file +62 -0

62 lines not on this page · raw message

_

Signature · 5 lines
Prasanna S Panchamukhi
Linux Technology Center
India Software Labs, IBM Bangalore
Ph: 91-80-25044636
<[email protected]>
#3 lore raw

[3/3] kprobes-arch-sparc64-changes.patch

Minor changes required to port kprobes for x86_64.

---

 linux-2.6.9-final-prasanna/arch/sparc64/kernel/kprobes.c |   28 ++++++++++-----
 linux-2.6.9-final-prasanna/include/asm-sparc64/kprobes.h |    6 +++
 2 files changed, 25 insertions(+), 9 deletions(-)
arch/sparc64/kernel/kprobes.c renamed from arch/sparc64/kernel/kprobes.c~kprobes-arch-sparc64-changes +19 -9
diff -puN arch/sparc64/kernel/kprobes.c~kprobes-arch-sparc64-changes arch/sparc64/kernel/kprobes.c
@@ -15,7 +15,7 @@
  * traps.  The top-level scheme is similar to that used
  * in the x86 kprobes implementation.
  *
- * In the kprobe->insn[] array we store the original
+ * At kprobe->insn we allocate enough bytes and then store the original
  * instruction at index zero and a break instruction at
  * index one.
  *
@@ -38,10 +38,20 @@
  * - Mark that we are no longer actively in a kprobe.
  */
 
-void arch_prepare_kprobe(struct kprobe *p)
+int arch_prepare_kprobe(struct kprobe *p)
+{
+	p->ainsn.insn = (kprobe_opcode_t *) kmalloc(MAX_INSN_SIZE, GFP_ATOMIC);
+	if (!p->ainsn.insn) {
+		return -ENOMEM;
+	}
+
+	p->ainsn.insn[0] = *p->addr;
+	p->ainsn.insn[1] = BREAKPOINT_INSTRUCTION_2;
+	return 0;
+}
+
+void arch_remove_kprobe(struct kprobe *p)
 {
-	p->insn[0] = *p->addr;
-	p->insn[1] = BREAKPOINT_INSTRUCTION_2;
 }
 
 /* kprobe_status settings */
@@ -59,8 +69,8 @@ static inline void prepare_singlestep(st
 	current_kprobe_orig_tstate_pil = (regs->tstate & TSTATE_PIL);
 	regs->tstate |= TSTATE_PIL;
 
-	regs->tpc = (unsigned long) &p->insn[0];
-	regs->tnpc = (unsigned long) &p->insn[1];
+	regs->tpc = (unsigned long) &p->ainsn.insn[0];
+	regs->tnpc = (unsigned long) &p->ainsn.insn[1];
 }
 
 static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs)
@@ -199,19 +209,19 @@ static void retpc_fixup(struct pt_regs *
  * instruction.  To avoid the SMP problems that can occur when we
  * temporarily put back the original opcode to single-step, we
  * single-stepped a copy of the instruction.  The address of this
- * copy is p->insn.
+ * copy is p->ainsn.insn.
  *
  * This function prepares to return from the post-single-step
  * breakpoint trap.
  */
 static void resume_execution(struct kprobe *p, struct pt_regs *regs)
 {
-	u32 insn = p->insn[0];
+	u32 insn = p->ainsn.insn[0];
 
 	regs->tpc = current_kprobe_orig_tnpc;
 	regs->tnpc = relbranch_fixup(insn,
 				     (unsigned long) p->addr,
-				     (unsigned long) &p->insn[0],
+				     (unsigned long) &p->ainsn.insn[0],
 				     regs->tnpc);
 	retpc_fixup(regs, insn, (unsigned long) p->addr);
 
include/asm-sparc64/kprobes.h renamed from include/asm-sparc64/kprobes.h~kprobes-arch-sparc64-changes +6 -0
diff -puN include/asm-sparc64/kprobes.h~kprobes-arch-sparc64-changes include/asm-sparc64/kprobes.h
@@ -10,6 +10,12 @@ typedef u32 kprobe_opcode_t;
 #define BREAKPOINT_INSTRUCTION_2 0x91d02071 /* ta 0x71 */
 #define MAX_INSN_SIZE 2
 
+/* Architecture specific copy of original instruction*/
+struct arch_specific_insn {
+	/* copy of the original instruction */
+	kprobe_opcode_t insn[MAX_INSN_SIZE];
+};
+
 #ifdef CONFIG_KPROBES
 extern int kprobe_exceptions_notify(struct notifier_block *self,
 				    unsigned long val, void *data);

_

Signature · 5 lines
Prasanna S Panchamukhi
Linux Technology Center
India Software Labs, IBM Bangalore
Ph: 91-80-25044636
<[email protected]>
#4 lore raw

Re: [patch 3/3] kprobes : Minor changes for sparc64

Minor changes required to port kprobes for x86_64. -arch_prepare_kprobe() returns an integer.
-added arch_remove_kprobe().
-changes to access copy of original instruction, since kprobe_opcode_t insn[MAX_INSN_SIZE] is moved from struct kprobe to a struct arch_specific_insn.

---

 linux-2.6.9-final-prasanna/arch/sparc64/kernel/kprobes.c |   29 ++++++++-------
 linux-2.6.9-final-prasanna/include/asm-sparc64/kprobes.h |    6 +++
 2 files changed, 23 insertions(+), 12 deletions(-)
arch/sparc64/kernel/kprobes.c renamed from arch/sparc64/kernel/kprobes.c~kprobes-arch-sparc64-changes +17 -12
diff -puN arch/sparc64/kernel/kprobes.c~kprobes-arch-sparc64-changes arch/sparc64/kernel/kprobes.c
@@ -15,7 +15,7 @@
  * traps.  The top-level scheme is similar to that used
  * in the x86 kprobes implementation.
  *
- * In the kprobe->insn[] array we store the original
+ * In the kprobe->ainsn.insn[] array we store the original
  * instruction at index zero and a break instruction at
  * index one.
  *
@@ -24,12 +24,12 @@
  * - Remember "regs->tnpc" and interrupt level stored in
  *   "regs->tstate" so we can restore them later
  * - Disable PIL interrupts
- * - Set regs->tpc to point to kprobe->insn[0]
- * - Set regs->tnpc to point to kprobe->insn[1]
+ * - Set regs->tpc to point to kprobe->ainsn.insn[0]
+ * - Set regs->tnpc to point to kprobe->ainsn.insn[1]
  * - Mark that we are actively in a kprobe
  *
  * At this point we wait for the second breakpoint at
- * kprobe->insn[1] to hit.  When it does we:
+ * kprobe->ainsn.insn[1] to hit.  When it does we:
  * - Run the post-handler
  * - Set regs->tpc to "remembered" regs->tnpc stored above,
  *   restore the PIL interrupt level in "regs->tstate" as well
@@ -38,10 +38,15 @@
  * - Mark that we are no longer actively in a kprobe.
  */
 
-void arch_prepare_kprobe(struct kprobe *p)
+int arch_prepare_kprobe(struct kprobe *p)
+{
+	p->ainsn.insn[0] = *p->addr;
+	p->ainsn.insn[1] = BREAKPOINT_INSTRUCTION_2;
+	return 0;
+}
+
+void arch_remove_kprobe(struct kprobe *p)
 {
-	p->insn[0] = *p->addr;
-	p->insn[1] = BREAKPOINT_INSTRUCTION_2;
 }
 
 /* kprobe_status settings */
@@ -59,8 +64,8 @@ static inline void prepare_singlestep(st
 	current_kprobe_orig_tstate_pil = (regs->tstate & TSTATE_PIL);
 	regs->tstate |= TSTATE_PIL;
 
-	regs->tpc = (unsigned long) &p->insn[0];
-	regs->tnpc = (unsigned long) &p->insn[1];
+	regs->tpc = (unsigned long) &p->ainsn.insn[0];
+	regs->tnpc = (unsigned long) &p->ainsn.insn[1];
 }
 
 static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs)
@@ -199,19 +204,19 @@ static void retpc_fixup(struct pt_regs *
  * instruction.  To avoid the SMP problems that can occur when we
  * temporarily put back the original opcode to single-step, we
  * single-stepped a copy of the instruction.  The address of this
- * copy is p->insn.
+ * copy is p->ainsn.insn.
  *
  * This function prepares to return from the post-single-step
  * breakpoint trap.
  */
 static void resume_execution(struct kprobe *p, struct pt_regs *regs)
 {
-	u32 insn = p->insn[0];
+	u32 insn = p->ainsn.insn[0];
 
 	regs->tpc = current_kprobe_orig_tnpc;
 	regs->tnpc = relbranch_fixup(insn,
 				     (unsigned long) p->addr,
-				     (unsigned long) &p->insn[0],
+				     (unsigned long) &p->ainsn.insn[0],
 				     regs->tnpc);
 	retpc_fixup(regs, insn, (unsigned long) p->addr);
 
include/asm-sparc64/kprobes.h renamed from include/asm-sparc64/kprobes.h~kprobes-arch-sparc64-changes +6 -0
diff -puN include/asm-sparc64/kprobes.h~kprobes-arch-sparc64-changes include/asm-sparc64/kprobes.h
@@ -10,6 +10,12 @@ typedef u32 kprobe_opcode_t;
 #define BREAKPOINT_INSTRUCTION_2 0x91d02071 /* ta 0x71 */
 #define MAX_INSN_SIZE 2
 
+/* Architecture specific copy of original instruction*/
+struct arch_specific_insn {
+	/* copy of the original instruction */
+	kprobe_opcode_t insn[MAX_INSN_SIZE];
+};
+
 #ifdef CONFIG_KPROBES
 extern int kprobe_exceptions_notify(struct notifier_block *self,
 				    unsigned long val, void *data);

_

Signature · 6 lines
Thanks & Regards
Prasanna S Panchamukhi
Linux Technology Center
India Software Labs, IBM Bangalore
Ph: 91-80-25044636
<[email protected]>
#5 lore raw

Re: [patch 2/3] kprobes : kprobes ported to x86_64

In reply to Prasanna S Panchamukhi

Adopted from i386 architecture.

Kprobes:

Helps developers to trap at almost any kernel code address, specifying a handler routine to be invoked when the breakpoint is hit. Useful for analysing the Linux kernel by collecting debugging information non-disruptively. Employs single-stepping out-of-line to avoid probe misses on SMP and may be especially useful in aiding debugging elusive races and problems on live systems. More elaborate dynamic tracing tools can be built over the kprobes interface.

Sample usage:

	To place a probe on __blockdev_direct_IO:
	static int probe_handler(struct kprobe *p, struct pt_regs *)
	{
		... whatever ...
	}
	struct kprobe kp = {
		.addr = __blockdev_direct_IO,
		.pre_handler = probe_handler
	};
	register_kprobe(&kp);

Jprobes:

A special kprobe type which can be placed on function entry points, and employs a simple mirroring principle to allow seamless access to the arguments of a function being probed. The probe handler routine should have the same prototype as the function being probed.

The way it works is that when the probe is hit, the breakpoint handler simply irets to the probe handler's rip while retaining register and stack state corresponding to the function entry. After it is done, the probe handler calls jprobe_return() which traps again to restore processor state and switch back to the probed function. Linus noted correctly at KS that we need to be careful as gcc assumes that the callee owns arguments. We save and restore enough stack bytes to cover argument space.

Sample Usage:

	static int jip_queue_xmit(struct sk_buff *skb, int ipfragok)
	{
		... whatever ...
		jprobe_return();
		return 0;
	}

	struct jprobe jp = {
		{.addr = (kprobe_opcode_t *) ip_queue_xmit},
		.entry = (kprobe_opcode_t *) jip_queue_xmit
	};
	register_jprobe(&jp);

---

---

---

 linux-2.6.9-final-prasanna/arch/x86_64/Kconfig.debug        |   10 
 linux-2.6.9-final-prasanna/arch/x86_64/kernel/Makefile      |    1 
 linux-2.6.9-final-prasanna/arch/x86_64/kernel/kprobes.c     |  481 ++++++++++++
 linux-2.6.9-final-prasanna/arch/x86_64/kernel/traps.c       |   30 
 linux-2.6.9-final-prasanna/arch/x86_64/kernel/x8664_ksyms.c |    2 
 linux-2.6.9-final-prasanna/arch/x86_64/mm/fault.c           |    5 
 linux-2.6.9-final-prasanna/include/asm-x86_64/kdebug.h      |    3 
 linux-2.6.9-final-prasanna/include/asm-x86_64/kprobes.h     |   61 +
 8 files changed, 589 insertions(+), 4 deletions(-)
arch/x86_64/Kconfig.debug renamed from arch/x86_64/Kconfig.debug~kprobes-x86_64-port-2.6.9-final +10 -0
diff -puN arch/x86_64/Kconfig.debug~kprobes-x86_64-port-2.6.9-final arch/x86_64/Kconfig.debug
@@ -18,6 +18,16 @@ config INIT_DEBUG
 	  Fill __init and __initdata at the end of boot. This helps debugging
 	  illegal uses of __init and __initdata after initialization.
 
+config KPROBES
+	bool "Kprobes"
+	depends on DEBUG_KERNEL
+	help
+	  Kprobes allows you to trap at almost any kernel address and
+	  execute a callback function.  register_kprobe() establishes
+	  a probepoint and specifies the callback.  Kprobes is useful
+	  for kernel debugging, non-intrusive instrumentation and testing.
+	  If in doubt, say "N".
+
 config SCHEDSTATS
 	bool "Collect scheduler statistics"
 	depends on DEBUG_KERNEL && PROC_FS
arch/x86_64/kernel/kprobes.c new file +481 -0

481 lines not on this page · raw message

arch/x86_64/kernel/Makefile renamed from arch/x86_64/kernel/Makefile~kprobes-x86_64-port-2.6.9-final +1 -0
diff -puN arch/x86_64/kernel/Makefile~kprobes-x86_64-port-2.6.9-final arch/x86_64/kernel/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_EARLY_PRINTK)	+= early_prin
 obj-$(CONFIG_GART_IOMMU)	+= pci-gart.o aperture.o
 obj-$(CONFIG_DUMMY_IOMMU)	+= pci-nommu.o pci-dma.o
 obj-$(CONFIG_SWIOTLB)		+= swiotlb.o
+obj-$(CONFIG_KPROBES)		+= kprobes.o
 
 obj-$(CONFIG_MODULES)		+= module.o
 
arch/x86_64/kernel/traps.c renamed from arch/x86_64/kernel/traps.c~kprobes-x86_64-port-2.6.9-final +27 -3
diff -puN arch/x86_64/kernel/traps.c~kprobes-x86_64-port-2.6.9-final arch/x86_64/kernel/traps.c
@@ -46,6 +46,7 @@
 
 #include <linux/irq.h>
 
+
 extern struct gate_struct idt_table[256]; 
 
 asmlinkage void divide_error(void);
@@ -72,6 +73,17 @@ asmlinkage void spurious_interrupt_bug(v
 asmlinkage void call_debug(void);
 
 struct notifier_block *die_chain;
+static spinlock_t die_notifier_lock = SPIN_LOCK_UNLOCKED;
+
+int register_die_notifier(struct notifier_block *nb)
+{
+	int err = 0;
+	unsigned long flags;
+	spin_lock_irqsave(&die_notifier_lock, flags);
+	err = notifier_chain_register(&die_chain, nb);
+	spin_unlock_irqrestore(&die_notifier_lock, flags);
+	return err;
+}
 
 static inline void conditional_sti(struct pt_regs *regs)
 {
@@ -458,7 +470,6 @@ asmlinkage void do_##name(struct pt_regs
 }
 
 DO_ERROR_INFO( 0, SIGFPE,  "divide error", divide_error, FPE_INTDIV, regs->rip)
-DO_ERROR( 3, SIGTRAP, "int3", int3);
 DO_ERROR( 4, SIGSEGV, "overflow", overflow)
 DO_ERROR( 5, SIGSEGV, "bounds", bounds)
 DO_ERROR_INFO( 6, SIGILL,  "invalid operand", invalid_op, ILL_ILLOPN, regs->rip)
@@ -601,6 +612,15 @@ asmlinkage void default_do_nmi(struct pt
 	inb(0x71);		/* dummy */
 }
 
+asmlinkage void do_int3(struct pt_regs * regs, long error_code)
+{
+	if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP) == NOTIFY_STOP) {
+		return;
+	}
+	do_trap(3, SIGTRAP, "int3", regs, error_code, NULL);
+	return;
+}
+
 /* runs on IST stack. */
 asmlinkage void *do_debug(struct pt_regs * regs, unsigned long error_code)
 {
@@ -630,6 +650,10 @@ asmlinkage void *do_debug(struct pt_regs
 
 	asm("movq %%db6,%0" : "=r" (condition));
 
+	if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code,
+						SIGTRAP) == NOTIFY_STOP) {
+		return regs;
+	}
 	conditional_sti(regs);
 
 	/* Mask out spurious debug traps due to lazy DR7 setting */
@@ -862,8 +886,8 @@ void __init trap_init(void)
 	set_intr_gate(0,&divide_error);
 	set_intr_gate_ist(1,&debug,DEBUG_STACK);
 	set_intr_gate_ist(2,&nmi,NMI_STACK);
-	set_system_gate(3,&int3);	/* int3-5 can be called from all */
-	set_system_gate(4,&overflow);
+	set_intr_gate(3,&int3);
+	set_system_gate(4,&overflow);	/* int4-5 can be called from all */
 	set_system_gate(5,&bounds);
 	set_intr_gate(6,&invalid_op);
 	set_intr_gate(7,&device_not_available);
arch/x86_64/kernel/x8664_ksyms.c renamed from arch/x86_64/kernel/x8664_ksyms.c~kprobes-x86_64-port-2.6.9-final +2 -0
diff -puN arch/x86_64/kernel/x8664_ksyms.c~kprobes-x86_64-port-2.6.9-final arch/x86_64/kernel/x8664_ksyms.c
@@ -32,6 +32,7 @@
 #include <asm/unistd.h>
 #include <asm/delay.h>
 #include <asm/tlbflush.h>
+#include <asm/kdebug.h>
 
 extern spinlock_t rtc_lock;
 
@@ -197,6 +198,7 @@ EXPORT_SYMBOL(atomic_dec_and_lock);
 #endif
 
 EXPORT_SYMBOL(die_chain);
+EXPORT_SYMBOL(register_die_notifier);
 
 #ifdef CONFIG_SMP
 EXPORT_SYMBOL(cpu_sibling_map);
arch/x86_64/mm/fault.c renamed from arch/x86_64/mm/fault.c~kprobes-x86_64-port-2.6.9-final +5 -0
diff -puN arch/x86_64/mm/fault.c~kprobes-x86_64-port-2.6.9-final arch/x86_64/mm/fault.c
@@ -23,6 +23,7 @@
 #include <linux/vt_kern.h>		/* For unblank_screen() */
 #include <linux/compiler.h>
 #include <linux/module.h>
+#include <linux/kprobes.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -32,6 +33,7 @@
 #include <asm/proto.h>
 #include <asm/kdebug.h>
 #include <asm-generic/sections.h>
+#include <asm/kdebug.h>
 
 void bust_spinlocks(int yes)
 {
@@ -268,6 +270,9 @@ asmlinkage void do_page_fault(struct pt_
 
 	/* get the address */
 	__asm__("movq %%cr2,%0":"=r" (address));
+	if (notify_die(DIE_PAGE_FAULT, "page fault", regs, error_code, 14,
+					SIGSEGV) == NOTIFY_STOP)
+		return;
 
 	if (likely(regs->eflags & X86_EFLAGS_IF))
 		local_irq_enable();
include/asm-x86_64/kdebug.h renamed from include/asm-x86_64/kdebug.h~kprobes-x86_64-port-2.6.9-final +2 -1
diff -puN include/asm-x86_64/kdebug.h~kprobes-x86_64-port-2.6.9-final include/asm-x86_64/kdebug.h
@@ -16,8 +16,8 @@ struct die_args { 
 /* Note - you should never unregister because that can race with NMIs.
    If you really want to do it first unregister - then synchronize_kernel - then free. 
   */
+int register_die_notifier(struct notifier_block *nb);
 extern struct notifier_block *die_chain;
-
 /* Grossly misnamed. */
 enum die_val { 
 	DIE_OOPS = 1,
@@ -32,6 +32,7 @@ enum die_val { 
 	DIE_GPF,
 	DIE_CALL,
 	DIE_NMI_IPI,
+	DIE_PAGE_FAULT,
 }; 
 	
 static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err,int trap, int sig)
include/asm-x86_64/kprobes.h new file +61 -0

61 lines not on this page · raw message

_

Signature · 6 lines
Thanks & Regards
Prasanna S Panchamukhi
Linux Technology Center
India Software Labs, IBM Bangalore
Ph: 91-80-25044636
<[email protected]>
#6 lore raw

Re: [patch 1/3] kprobes: Minor i386 changes required for porting kprobes to x86_64

In reply to Prasanna S Panchamukhi

-Kprobes structure has been modified to support copying of original instruction as required by the architecture. On x86_64 normal pages we get from kmalloc or vmalloc are not executable. Single-stepping an instruction on such a page yields an oops. So instead of storing the instruction copies in their respective kprobe objects, we allocate a page, map it executable, and store all the instruction copies there and store the pointer of the copied instruction in the specific kprobes object.
-jprobe_return_end is moved into inline assembly to avoid compiler optimization. -arch_prepare_kprobe() now returns an integer,since arch_prepare_kprobe() might fail on other architectures.
-added arch_remove_kprobe() routine, since other architectures requires it.

---

---

 linux-2.6.9-final-prasanna/arch/i386/kernel/kprobes.c |   32 ++++++++++--------
 linux-2.6.9-final-prasanna/include/asm-i386/kprobes.h |    7 +++
 linux-2.6.9-final-prasanna/include/linux/kprobes.h    |    5 +-
 linux-2.6.9-final-prasanna/kernel/kprobes.c           |    6 ++-
 4 files changed, 34 insertions(+), 16 deletions(-)
arch/i386/kernel/kprobes.c renamed from arch/i386/kernel/kprobes.c~kprobes-arch-i386-changes +19 -13
diff -puN arch/i386/kernel/kprobes.c~kprobes-arch-i386-changes arch/i386/kernel/kprobes.c
@@ -42,6 +42,7 @@ static struct pt_regs jprobe_saved_regs;
 static long *jprobe_saved_esp;
 /* copy of the kernel stack at the probe fire time */
 static kprobe_opcode_t jprobes_stack[MAX_STACK_SIZE];
+void jprobe_return_end(void);
 
 /*
  * returns non-zero if opcode modifies the interrupt flag.
@@ -58,9 +59,14 @@ static inline int is_IF_modifier(kprobe_
 	return 0;
 }
 
-void arch_prepare_kprobe(struct kprobe *p)
+int arch_prepare_kprobe(struct kprobe *p)
+{
+	memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
+	return 0;
+}
+
+void arch_remove_kprobe(struct kprobe *p)
 {
-	memcpy(p->insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
 }
 
 static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs)
@@ -73,7 +79,7 @@ static inline void prepare_singlestep(st
 {
 	regs->eflags |= TF_MASK;
 	regs->eflags &= ~IF_MASK;
-	regs->eip = (unsigned long)&p->insn;
+	regs->eip = (unsigned long)&p->ainsn.insn;
 }
 
 /*
@@ -153,7 +159,7 @@ static inline int kprobe_handler(struct 
  * instruction.  To avoid the SMP problems that can occur when we
  * temporarily put back the original opcode to single-step, we
  * single-stepped a copy of the instruction.  The address of this
- * copy is p->insn.
+ * copy is p->ainsn.insn.
  *
  * This function prepares to return from the post-single-step
  * interrupt.  We have to fix up the stack as follows:
@@ -173,10 +179,10 @@ static void resume_execution(struct kpro
 {
 	unsigned long *tos = (unsigned long *)&regs->esp;
 	unsigned long next_eip = 0;
-	unsigned long copy_eip = (unsigned long)&p->insn;
+	unsigned long copy_eip = (unsigned long)&p->ainsn.insn;
 	unsigned long orig_eip = (unsigned long)p->addr;
 
-	switch (p->insn[0]) {
+	switch (p->ainsn.insn[0]) {
 	case 0x9c:		/* pushfl */
 		*tos &= ~(TF_MASK | IF_MASK);
 		*tos |= kprobe_old_eflags;
@@ -185,13 +191,13 @@ static void resume_execution(struct kpro
 		*tos = orig_eip + (*tos - copy_eip);
 		break;
 	case 0xff:
-		if ((p->insn[1] & 0x30) == 0x10) {
+		if ((p->ainsn.insn[1] & 0x30) == 0x10) {
 			/* call absolute, indirect */
 			/* Fix return addr; eip is correct. */
 			next_eip = regs->eip;
 			*tos = orig_eip + (*tos - copy_eip);
-		} else if (((p->insn[1] & 0x31) == 0x20) ||	/* jmp near, absolute indirect */
-			   ((p->insn[1] & 0x31) == 0x21)) {	/* jmp far, absolute indirect */
+		} else if (((p->ainsn.insn[1] & 0x31) == 0x20) ||	/* jmp near, absolute indirect */
+			   ((p->ainsn.insn[1] & 0x31) == 0x21)) {	/* jmp far, absolute indirect */
 			/* eip is correct. */
 			next_eip = regs->eip;
 		}
@@ -315,12 +321,12 @@ void jprobe_return(void)
 {
 	preempt_enable_no_resched();
 	asm volatile ("       xchgl   %%ebx,%%esp     \n"
-		      "       int3			\n"::"b"
+		      "       int3			\n"
+		      "       .globl jprobe_return_end	\n"
+		      "       jprobe_return_end:	\n"
+		      "       nop			\n"::"b"
 		      (jprobe_saved_esp):"memory");
 }
-void jprobe_return_end(void)
-{
-};
 
 int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
 {
include/asm-i386/kprobes.h renamed from include/asm-i386/kprobes.h~kprobes-arch-i386-changes +7 -0
diff -puN include/asm-i386/kprobes.h~kprobes-arch-i386-changes include/asm-i386/kprobes.h
@@ -38,6 +38,13 @@ typedef u8 kprobe_opcode_t;
 	? (MAX_STACK_SIZE) \
 	: (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))
 
+/* Architecture specific copy of original instruction*/
+struct arch_specific_insn {
+	/* copy of the original instruction */
+	kprobe_opcode_t insn[MAX_INSN_SIZE];
+};
+
+
 /* trap3/1 are intr gates for kprobes.  So, restore the status of IF,
  * if necessary, before executing the original int3/1 (trap) handler.
  */
include/linux/kprobes.h renamed from include/linux/kprobes.h~kprobes-arch-i386-changes +3 -2
diff -puN include/linux/kprobes.h~kprobes-arch-i386-changes include/linux/kprobes.h
@@ -64,7 +64,7 @@ struct kprobe {
 	kprobe_opcode_t opcode;
 
 	/* copy of the original instruction */
-	kprobe_opcode_t insn[MAX_INSN_SIZE];
+	struct arch_specific_insn ainsn;
 };
 
 /*
@@ -94,7 +94,8 @@ static inline int kprobe_running(void)
 	return kprobe_cpu == smp_processor_id();
 }
 
-extern void arch_prepare_kprobe(struct kprobe *p);
+extern int arch_prepare_kprobe(struct kprobe *p);
+extern void arch_remove_kprobe(struct kprobe *p);
 extern void show_registers(struct pt_regs *regs);
 
 /* Get the kprobe at this addr (if any).  Must have called lock_kprobes */
kernel/kprobes.c renamed from kernel/kprobes.c~kprobes-arch-i386-changes +5 -1
diff -puN kernel/kprobes.c~kprobes-arch-i386-changes kernel/kprobes.c
@@ -84,10 +84,13 @@ int register_kprobe(struct kprobe *p)
 		ret = -EEXIST;
 		goto out;
 	}
+
+	if ((ret = arch_prepare_kprobe(p)) != 0) {
+		goto out;
+	}
 	hlist_add_head(&p->hlist,
 		       &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]);
 
-	arch_prepare_kprobe(p);
 	p->opcode = *p->addr;
 	*p->addr = BREAKPOINT_INSTRUCTION;
 	flush_icache_range((unsigned long) p->addr,
@@ -101,6 +104,7 @@ void unregister_kprobe(struct kprobe *p)
 {
 	unsigned long flags;
 	spin_lock_irqsave(&kprobe_lock, flags);
+	arch_remove_kprobe(p);
 	*p->addr = p->opcode;
 	hlist_del(&p->hlist);
 	flush_icache_range((unsigned long) p->addr,

_

Signature · 6 lines
Thanks & Regards
Prasanna S Panchamukhi
Linux Technology Center
India Software Labs, IBM Bangalore
Ph: 91-80-25044636
<[email protected]>
#7 lore raw
In reply to Prasanna S Panchamukhi
Quoted 9 lines · Prasanna S Panchamukhi

Hi,

Below are the Kprobes patches ported to x86_64 architecture. I have updated these patches with suggestions from Andi Kleen. Thanks Andi for reviewing and providing your feedback. These patches can be applied over 2.6.9-final. Please see the description in the individual patches.

Please review and provide your feedback.

The patch is not ready to be applied yet. You didn't address some issues from the last review.

Like I still would like to have the page fault notifier completely moved out of the fast path into no_context (that i386 has it there is also wrong). Adding kprobe_runn doesn't make a difference.

And the jprobe_return_end change is wrong, my suggestion was to move it into the inline assembler statement. Adding asmlinkage doesn't help at all
(I think i386 gets this wrong too)

-Andi

#8 lore raw

Hi Andi,

Please see my comments inline below.

Thanks
Prasanna

Quoted 5 lines · Andi Kleen

Like I still would like to have the page fault notifier completely moved out of the fast path into no_context (that i386 has it there is also wrong). Adding kprobe_runn doesn't make a difference.

The kprobes fault handler is called if an exception is
generated for any instruction within the fault-handler or when Kprobes single-steps the probed instruction.
AFAIK kprobes does not handle page faults in the above case and just returns immediately resuming the normal execution.

Quoted 6 lines

And the jprobe_return_end change is wrong, my suggestion was to move it into the inline assembler statement. Adding asmlinkage doesn't help at all
(I think i386 gets this wrong too)

In the below code, I moved jprobe_return_end to inline assembler statement.

void jprobe_return(void)
{
preempt_enable_no_resched();
asm volatile (" xchg %%rbx,%%rsp \n"
" int3 \n"
" .globl jprobe_return_end \n"
" jprobe_return_end: \n"
" nop \n"::"b"
(jprobe_saved_rsp):"memory");
}

Quoted 1 line

-Andi

Signature · 6 lines
Thanks & Regards
Prasanna S Panchamukhi
Linux Technology Center
India Software Labs, IBM Bangalore
Ph: 91-80-25044636
<[email protected]>
#9 lore raw
Quoted 8 lines · Prasanna S Panchamukhi
Quoted 5 lines · Andi Kleen

Like I still would like to have the page fault notifier completely moved out of the fast path into no_context (that i386 has it there is also wrong). Adding kprobe_runn doesn't make a difference.

Quoted 5 lines

The kprobes fault handler is called if an exception is
generated for any instruction within the fault-handler or when Kprobes single-steps the probed instruction.
AFAIK kprobes does not handle page faults in the above case and just returns immediately resuming the normal execution.

Ok. It's ugly, but ok. Can you remove the bogus kprobes_running() then please, it's unnecessary?

With that change it would be ok to merge from my side.

-Andi

maintainer
#10 lore raw
In reply to Andi Kleen
Quoted 4 lines · On 28 Oct 2004 13:37:44 +0200 Andi Kleen <[email protected]> wrote:

Like I still would like to have the page fault notifier completely moved out of the fast path into no_context (that i386 has it there is also wrong). Adding kprobe_runn doesn't make a difference.

You can't do that actually Andi.

The kprobe notifier has to run for the case where the kernel does a userspace access and faults. This is to handle the case of setting a kprobe on a userspace access instruction. In such a event, we must unwind the reset the program counter so that exception table processing is done with the correct PC not the temporary one kprobes is using to execute the instruction where the breakpoint currently lives.

#11 lore raw
Quoted 7 lines

The kprobe notifier has to run for the case where the kernel does a userspace access and faults. This is to handle the case of setting a kprobe on a userspace access instruction. In such a event, we must unwind the reset the program counter so that exception table processing is done with the correct PC not the temporary one kprobes is using to execute the instruction where the breakpoint currently lives.

Ok that makes sense. Thanks for the explanation (the original submitter didn't give one).

-Andi