From: masami.hiramatsu.pt@hitachi.com (Masami Hiramatsu)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v3] kprobes: arm: enable OPTPROBES for ARM 32
Date: Tue, 12 Aug 2014 10:38:07 +0900 [thread overview]
Message-ID: <53E96FFF.8030101@hitachi.com> (raw)
In-Reply-To: <20140811134832.GD15853@arm.com>
(2014/08/11 22:48), Will Deacon wrote:
> Hello,
>
> On Sat, Aug 09, 2014 at 03:12:19AM +0100, Wang Nan wrote:
>> This patch introduce kprobeopt for ARM 32.
>>
>> Limitations:
>> - Currently only kernel compiled with ARM ISA is supported.
>>
>> - Offset between probe point and optinsn slot must not larger than
>> 32MiB. Masami Hiramatsu suggests replacing 2 words, it will make
>> things complex. Futher patch can make such optimization.
>>
>> Kprobe opt on ARM is relatively simpler than kprobe opt on x86 because
>> ARM instruction is always 4 bytes aligned and 4 bytes long. This patch
>> replace probed instruction by a 'b', branch to trampoline code and then
>> calls optimized_callback(). optimized_callback() calls opt_pre_handler()
>> to execute kprobe handler. It also emulate/simulate replaced instruction.
>
> Could you briefly describe the optimisation please?
On arm32, optimization means "replacing a breakpoint with a branch".
Of course simple branch instruction doesn't memorize the source(probe)
address, optprobe makes a trampoline code for each probe point and
each trampoline stores "struct kprobe" of that probe point.
At first, the kprobe puts a breakpoint into the probe site, and builds
a trampoline. After a while, it starts optimizing the probe site by
replacing the breakpoint with a branch.
> I'm not familiar with
> kprobes internals, but if you're trying to patch an arbitrary instruction
> with a branch then that's not guaranteed to be atomic by the ARM
> architecture.
Hmm, I'm not sure about arm32 too. Would you mean patch_text() can't
replace an instruction atomically? Or only the breakpoint is special?
(for cache?)
optprobe always swaps branch and breakpoint, isn't that safe?
>
> We can, however, patch branches with other branches.
>
> Anyway, minor comments in-line:
>
>> +/* Caller must ensure addr & 3 == 0 */
>> +static int can_optimize(unsigned long paddr)
>> +{
>> + return 1;
>> +}
>
> Why not check the paddr alignment here, rather than have a comment?
Actually, we don't need to care about that. The alignment is already
checked before calling this function (at arch_prepare_kprobe() in
arch/arm/kernel/kprobes.c).
>
>> +/* Free optimized instruction slot */
>> +static void
>> +__arch_remove_optimized_kprobe(struct optimized_kprobe *op, int dirty)
>> +{
>> + if (op->optinsn.insn) {
>> + free_optinsn_slot(op->optinsn.insn, dirty);
>> + op->optinsn.insn = NULL;
>> + }
>> +}
>> +
>> +extern void kprobe_handler(struct pt_regs *regs);
>> +
>> +static void
>> +optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
>> +{
>> + unsigned long flags;
>> + struct kprobe *p = &op->kp;
>> + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
>> +
>> + /* Save skipped registers */
>> + regs->ARM_pc = (unsigned long)op->kp.addr;
>> + regs->ARM_ORIG_r0 = ~0UL;
>
> Why are you writing ORIG_r0?
In x86, optimization(breakpoint to jump) is transparently done, thus
we have to mimic all registers as the breakpoint exception. And in x86
int3(which is the breakpoint) exception sets -1 to orig_ax.
So, if arm32's breakpoint doesn't attach the ARM_ORIG_r0, you don't
need to touch it. We just consider the pt_regs looks same as that
at the breakpoint handler.
>
>> + local_irq_save(flags);
>> +
>> + if (kprobe_running()) {
>> + kprobes_inc_nmissed_count(&op->kp);
>> + } else {
>> + __this_cpu_write(current_kprobe, &op->kp);
>> + kcb->kprobe_status = KPROBE_HIT_ACTIVE;
>> + opt_pre_handler(&op->kp, regs);
>> + __this_cpu_write(current_kprobe, NULL);
>> + }
>> +
>> + /* In each case, we must singlestep the replaced instruction. */
>> + op->kp.ainsn.insn_singlestep(p->opcode, &p->ainsn, regs);
>> +
>> + local_irq_restore(flags);
>> +}
>> +
>> +int arch_prepare_optimized_kprobe(struct optimized_kprobe *op)
>> +{
>> + u8 *buf;
>> + unsigned long rel_chk;
>> + unsigned long val;
>> +
>> + if (!can_optimize((unsigned long)op->kp.addr))
>> + return -EILSEQ;
>> +
>> + op->optinsn.insn = get_optinsn_slot();
>> + if (!op->optinsn.insn)
>> + return -ENOMEM;
>> +
>> + /*
>> + * Verify if the address gap is in 32MiB range, because this uses
>> + * a relative jump.
>> + *
>> + * kprobe opt use a 'b' instruction to branch to optinsn.insn.
>> + * According to ARM manual, branch instruction is:
>> + *
>> + * 31 28 27 24 23 0
>> + * +------+---+---+---+---+----------------+
>> + * | cond | 1 | 0 | 1 | 0 | imm24 |
>> + * +------+---+---+---+---+----------------+
>> + *
>> + * imm24 is a signed 24 bits integer. The real branch offset is computed
>> + * by: imm32 = SignExtend(imm24:'00', 32);
>> + *
>> + * So the maximum forward branch should be:
>> + * (0x007fffff << 2) = 0x01fffffc = 0x1fffffc
>> + * The maximum backword branch should be:
>> + * (0xff800000 << 2) = 0xfe000000 = -0x2000000
>> + *
>> + * We can simply check (rel & 0xfe000003):
>> + * if rel is positive, (rel & 0xfe000000) shoule be 0
>> + * if rel is negitive, (rel & 0xfe000000) should be 0xfe000000
>> + * the last '3' is used for alignment checking.
>> + */
>> + rel_chk = (unsigned long)((long)op->optinsn.insn -
>> + (long)op->kp.addr + 8) & 0xfe000003;
>> +
>> + if ((rel_chk != 0) && (rel_chk != 0xfe000000)) {
>> + __arch_remove_optimized_kprobe(op, 0);
>> + return -ERANGE;
>> + }
>> +
>> + buf = (u8 *)op->optinsn.insn;
>> +
>> + /* Copy arch-dep-instance from template */
>> + memcpy(buf, &optprobe_template_entry, TMPL_END_IDX);
>> +
>> + /* Set probe information */
>> + val = (unsigned long)op;
>> + memcpy(buf + TMPL_VAL_IDX, &val, sizeof(val));
>> +
>> + /* Set probe function call */
>> + val = (unsigned long)optimized_callback;
>> + memcpy(buf + TMPL_CALL_IDX, &val, sizeof(val));
>
> Ok, so this is updating the `offset' portion of a b instruction, right? What
> if memcpy does that byte-by-byte?
No, as you can see a indirect call "blx r2" in optprobe_template_entry(
inline asm), this sets .data bytes at optprobe_template_call which is loaded
to r2 register. :-)
So all the 4bytes are used for storing the address.
Thank you,
--
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Research Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: masami.hiramatsu.pt at hitachi.com
WARNING: multiple messages have this Message-ID (diff)
From: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
To: Will Deacon <will.deacon@arm.com>
Cc: Wang Nan <wangnan0@huawei.com>,
Russell King - ARM Linux <linux@arm.linux.org.uk>,
"Jon Medhurst (Tixy)" <tixy@linaro.org>,
"ananth@in.ibm.com" <ananth@in.ibm.com>,
"anil.s.keshavamurthy@intel.com" <anil.s.keshavamurthy@intel.com>,
"davem@davemloft.net" <davem@davemloft.net>,
"linux-arm-kernel@lists.infradead.org"
<linux-arm-kernel@lists.infradead.org>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"peifeiyue@huawei.com" <peifeiyue@huawei.com>,
"lizefan@huawei.com" <lizefan@huawei.com>
Subject: Re: [PATCH v3] kprobes: arm: enable OPTPROBES for ARM 32
Date: Tue, 12 Aug 2014 10:38:07 +0900 [thread overview]
Message-ID: <53E96FFF.8030101@hitachi.com> (raw)
In-Reply-To: <20140811134832.GD15853@arm.com>
(2014/08/11 22:48), Will Deacon wrote:
> Hello,
>
> On Sat, Aug 09, 2014 at 03:12:19AM +0100, Wang Nan wrote:
>> This patch introduce kprobeopt for ARM 32.
>>
>> Limitations:
>> - Currently only kernel compiled with ARM ISA is supported.
>>
>> - Offset between probe point and optinsn slot must not larger than
>> 32MiB. Masami Hiramatsu suggests replacing 2 words, it will make
>> things complex. Futher patch can make such optimization.
>>
>> Kprobe opt on ARM is relatively simpler than kprobe opt on x86 because
>> ARM instruction is always 4 bytes aligned and 4 bytes long. This patch
>> replace probed instruction by a 'b', branch to trampoline code and then
>> calls optimized_callback(). optimized_callback() calls opt_pre_handler()
>> to execute kprobe handler. It also emulate/simulate replaced instruction.
>
> Could you briefly describe the optimisation please?
On arm32, optimization means "replacing a breakpoint with a branch".
Of course simple branch instruction doesn't memorize the source(probe)
address, optprobe makes a trampoline code for each probe point and
each trampoline stores "struct kprobe" of that probe point.
At first, the kprobe puts a breakpoint into the probe site, and builds
a trampoline. After a while, it starts optimizing the probe site by
replacing the breakpoint with a branch.
> I'm not familiar with
> kprobes internals, but if you're trying to patch an arbitrary instruction
> with a branch then that's not guaranteed to be atomic by the ARM
> architecture.
Hmm, I'm not sure about arm32 too. Would you mean patch_text() can't
replace an instruction atomically? Or only the breakpoint is special?
(for cache?)
optprobe always swaps branch and breakpoint, isn't that safe?
>
> We can, however, patch branches with other branches.
>
> Anyway, minor comments in-line:
>
>> +/* Caller must ensure addr & 3 == 0 */
>> +static int can_optimize(unsigned long paddr)
>> +{
>> + return 1;
>> +}
>
> Why not check the paddr alignment here, rather than have a comment?
Actually, we don't need to care about that. The alignment is already
checked before calling this function (at arch_prepare_kprobe() in
arch/arm/kernel/kprobes.c).
>
>> +/* Free optimized instruction slot */
>> +static void
>> +__arch_remove_optimized_kprobe(struct optimized_kprobe *op, int dirty)
>> +{
>> + if (op->optinsn.insn) {
>> + free_optinsn_slot(op->optinsn.insn, dirty);
>> + op->optinsn.insn = NULL;
>> + }
>> +}
>> +
>> +extern void kprobe_handler(struct pt_regs *regs);
>> +
>> +static void
>> +optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
>> +{
>> + unsigned long flags;
>> + struct kprobe *p = &op->kp;
>> + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
>> +
>> + /* Save skipped registers */
>> + regs->ARM_pc = (unsigned long)op->kp.addr;
>> + regs->ARM_ORIG_r0 = ~0UL;
>
> Why are you writing ORIG_r0?
In x86, optimization(breakpoint to jump) is transparently done, thus
we have to mimic all registers as the breakpoint exception. And in x86
int3(which is the breakpoint) exception sets -1 to orig_ax.
So, if arm32's breakpoint doesn't attach the ARM_ORIG_r0, you don't
need to touch it. We just consider the pt_regs looks same as that
at the breakpoint handler.
>
>> + local_irq_save(flags);
>> +
>> + if (kprobe_running()) {
>> + kprobes_inc_nmissed_count(&op->kp);
>> + } else {
>> + __this_cpu_write(current_kprobe, &op->kp);
>> + kcb->kprobe_status = KPROBE_HIT_ACTIVE;
>> + opt_pre_handler(&op->kp, regs);
>> + __this_cpu_write(current_kprobe, NULL);
>> + }
>> +
>> + /* In each case, we must singlestep the replaced instruction. */
>> + op->kp.ainsn.insn_singlestep(p->opcode, &p->ainsn, regs);
>> +
>> + local_irq_restore(flags);
>> +}
>> +
>> +int arch_prepare_optimized_kprobe(struct optimized_kprobe *op)
>> +{
>> + u8 *buf;
>> + unsigned long rel_chk;
>> + unsigned long val;
>> +
>> + if (!can_optimize((unsigned long)op->kp.addr))
>> + return -EILSEQ;
>> +
>> + op->optinsn.insn = get_optinsn_slot();
>> + if (!op->optinsn.insn)
>> + return -ENOMEM;
>> +
>> + /*
>> + * Verify if the address gap is in 32MiB range, because this uses
>> + * a relative jump.
>> + *
>> + * kprobe opt use a 'b' instruction to branch to optinsn.insn.
>> + * According to ARM manual, branch instruction is:
>> + *
>> + * 31 28 27 24 23 0
>> + * +------+---+---+---+---+----------------+
>> + * | cond | 1 | 0 | 1 | 0 | imm24 |
>> + * +------+---+---+---+---+----------------+
>> + *
>> + * imm24 is a signed 24 bits integer. The real branch offset is computed
>> + * by: imm32 = SignExtend(imm24:'00', 32);
>> + *
>> + * So the maximum forward branch should be:
>> + * (0x007fffff << 2) = 0x01fffffc = 0x1fffffc
>> + * The maximum backword branch should be:
>> + * (0xff800000 << 2) = 0xfe000000 = -0x2000000
>> + *
>> + * We can simply check (rel & 0xfe000003):
>> + * if rel is positive, (rel & 0xfe000000) shoule be 0
>> + * if rel is negitive, (rel & 0xfe000000) should be 0xfe000000
>> + * the last '3' is used for alignment checking.
>> + */
>> + rel_chk = (unsigned long)((long)op->optinsn.insn -
>> + (long)op->kp.addr + 8) & 0xfe000003;
>> +
>> + if ((rel_chk != 0) && (rel_chk != 0xfe000000)) {
>> + __arch_remove_optimized_kprobe(op, 0);
>> + return -ERANGE;
>> + }
>> +
>> + buf = (u8 *)op->optinsn.insn;
>> +
>> + /* Copy arch-dep-instance from template */
>> + memcpy(buf, &optprobe_template_entry, TMPL_END_IDX);
>> +
>> + /* Set probe information */
>> + val = (unsigned long)op;
>> + memcpy(buf + TMPL_VAL_IDX, &val, sizeof(val));
>> +
>> + /* Set probe function call */
>> + val = (unsigned long)optimized_callback;
>> + memcpy(buf + TMPL_CALL_IDX, &val, sizeof(val));
>
> Ok, so this is updating the `offset' portion of a b instruction, right? What
> if memcpy does that byte-by-byte?
No, as you can see a indirect call "blx r2" in optprobe_template_entry(
inline asm), this sets .data bytes at optprobe_template_call which is loaded
to r2 register. :-)
So all the 4bytes are used for storing the address.
Thank you,
--
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Research Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: masami.hiramatsu.pt@hitachi.com
next prev parent reply other threads:[~2014-08-12 1:38 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-08-09 2:12 [PATCH v3] kprobes: arm: enable OPTPROBES for ARM 32 Wang Nan
2014-08-09 2:12 ` Wang Nan
2014-08-09 10:10 ` Masami Hiramatsu
2014-08-09 10:10 ` Masami Hiramatsu
2014-08-11 13:48 ` Will Deacon
2014-08-11 13:48 ` Will Deacon
2014-08-12 1:38 ` Masami Hiramatsu [this message]
2014-08-12 1:38 ` Masami Hiramatsu
2014-08-12 3:37 ` Wang Nan
2014-08-12 3:37 ` Wang Nan
2014-08-12 9:04 ` Will Deacon
2014-08-12 9:04 ` Will Deacon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=53E96FFF.8030101@hitachi.com \
--to=masami.hiramatsu.pt@hitachi.com \
--cc=linux-arm-kernel@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.