RE: [RFC] ia64 function return probes

From: Lynch, Rusty <rusty.lynch_at_intel.com>
Date: 2005-06-04 04:01:34
From: Keith Owens [mailto:kaos@ocs.com.au]
>Rusty Lynch <rusty.lynch@intel.com> wrote:
>>The following is an implementation of the ia64 specific parts for
>implementing
>>the function return probes functionality that is a part of kprobes.
There
>>were some assumptions about how the architectures work inside
>kernel/kprobes.c
>>that force me to do some odd things in this implementation.
>>[snip]
>>Ok, that was the idea.  For ia64 complexities came up with respect to:
>>
>>* the assumption that kernel/kprobes.c is working with a
>>  stacked based architecture
>>
>>* the assumption that changing the return address to
>kretprobe_trampoline()
>>  will always result in the first instruction of of
kretprobe_trampoline
>>  being executed.
>
>Normal br.call/return will always execute the first slot of the return
>address.  br.call bn sets bn to IP+16, i.e. the start of the next
>bundle, with no slot data.  br.return bn returns to the first slot in
>the bundle defined by bn.  Intel IA64 arch vol3, 24531904.pdf page
>3:21.
>
>What made you think that you needed to handle return to a non-zero slot
>number?  The only instruction that can return to a non-zero slot number
>is rfi and, by definition, code that is entered for an interrupt does
>not have a return address in any branch register.

This was just a misunderstanding on my part.  Looks like I need to
re-read (again) the section talking about the branch unit, but this will
simplify this implementation.

>
>>The following patch works around these problems by:
>>
>>* Providing an empty kretprobe_trampoline(), but we don't really
>>  use it as our trampoline function.  Instead we provide:
>>
>>	/*
>>	 * void ia64_kretprobe_trampoline(void):
>>	 *
>>	 * When a return probe is set on a given function, it's return
>>	 * address (which really just points to the bundle) is set for
>>	 * this single bundle function.
>>	 *
>>	 * We don't know which slot of the bundle will be set, so we set
>>	 * a break using a special immediate value to gain control in
>>	 * each case so the registered return probe can be called and
then
>>	 * restore the cr->iip  back to the real address
>>	 * (i.e. the original return address)
>>	 */
>>GLOBAL_ENTRY(ia64_kretprobe_trampoline)
>>{ .mii
>>	break.m __IA64_BREAK_RPROBE
>>	break.i __IA64_BREAK_RPROBE
>>	break.i __IA64_BREAK_RPROBE
>>}
>>END(ia64_kretprobe_trampoline)
>>
>>  ... and then handle the break interrupts using this new reserved
>immediate
>>  value by just directly calling trampoline_probe_handler().
>
>Implementing a return probe by changing the return address will prevent
>us from getting any backtrace past the return probe.  If the function
>being probed, or any of its callee functions, gets an error then the
>backtrace will terminate at ia64_kretprobe_trampoline, the unwinder may
>even loop.  This makes it impossible to find out what called the
>function.
>
>One option is to hack return probes into the unwinder as yet another a
>special case - not nice.  Another option is this :-
>
>* On entry to the function, arch_prepare_kretprobe() saves the current
>  value of b0 in [sp+8] (architecture defined scratch area on stack),
>  and saves the current value of ar.pfs in [sp].
>
>* arch_prepare_kretprobe() bumps sp by 16 bytes, to account for the
>  saved b0 and ar.pfs.
>
>* arch_prepare_kretprobe() sets b0 to ia64_kretprobe_trampoline.
>
>* arch_prepare_kretprobe() sets the cfm field in ar.pfs to 0.  Of
>  course this is the ar.pfs that was saved in pt_regs when the function
>  being hooked was entered.
>
>* The function being hooked is entered.  When the function returns, it
>  does so to ia64_kretprobe_trampoline with cfm = 0, i.e.
>  ia64_kretprobe_trampoline has a zero sized register frame.
>
>* ia64_kretprobe_trampoline looks like this.  Compiled but not tested.
>
>GLOBAL_ENTRY(ia64_kretprobe_trampoline)
>	.prologue
>	.label_state 1
>	.fframe 16
>	.savesp b0, 8
>	.savesp ar.pfs, 0
>
>	break.m __IA64_BREAK_RPROBE
>	ld8 r3=[sp]		// original ar.pfs
>	add r2=8, sp		// original b0 was saved in sp+8
>	;;
>	ld8 r2=[r2]		// original b0
>
>	.body
>	mov ar.pfs=r3		// restore original ar.pfs
>	;;
>	mov b0=r2		// restore original b0
>	add sp=-16, sp		// arch_prepare_kretprobe bumped stack
by 16
>bytes
>
>	.copy_state 1
>	br.ret.sptk.many rp
>END(ia64_kretprobe_trampoline)
>
>If I got my unwind directives right, that will make the backtrace look
>like this
>
>  ...
>  hooked function
>  ia64_kretprobe_trampoline
>  whatever called the hooked function
>  ...
>
>It also means that the handler for __IA64_BREAK_RPROBE only has to log
>the fact that the function returned then resume at the instruction
>after __IA64_BREAK_RPROBE.  ia64_kretprobe_trampoline then returns to
>the original caller.

Since we are returning twice before getting back to the original parent
(i.e the target function returns and then ia64_kretprobe_trampoline
returns), wouldn't this cause the parent to get it's parent's bsp?

>
>>Index: linux-2.6.12-rc5/arch/ia64/kernel/kprobes.c
>>===================================================================
>>--- linux-2.6.12-rc5.orig/arch/ia64/kernel/kprobes.c
>>+++ linux-2.6.12-rc5/arch/ia64/kernel/kprobes.c
>>@@ -1,4 +1,4 @@
>>-/*
>>+ /*
>
>Interesting addition of white space there :)

Hey, I work in a cube, extra space is always a nice thing :->
-
To unsubscribe from this list: send the line "unsubscribe linux-ia64" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Received on Fri Jun 3 14:02:45 2005

This archive was generated by hypermail 2.1.8 : 2005-08-02 09:20:39 EST