ARM: kprobes: enable OPTPROBES for ARM32
Subsystems (3)
v8 cover ARM: kprobes: enable OPTPROBES for ARM32
Enable OPTPROBES for ARM 32. Previous discussion can be found:
https://lkml.org/lkml/2014/10/25/48
https://lkml.org/lkml/2014/10/22/254
https://lkml.org/lkml/2014/8/27/255
https://lkml.org/lkml/2014/8/12/12
https://lkml.org/lkml/2014/8/8/992
https://lkml.org/lkml/2014/8/8/5
https://lkml.org/lkml/2014/8/5/63
This series of patches is depend on 'ARM: kprobes: introduces instruction checker.', which can be found at:
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-November/303525.html
Wang Nan (2):
kprobes: copy ainsn after alloc aggr kprobe
ARM: kprobes: enable OPTPROBES for ARM 32
arch/arm/Kconfig | 1 + arch/arm/include/asm/kprobes.h | 26 ++++ arch/arm/kernel/Makefile | 3 +- arch/arm/kernel/kprobes-opt-arm.c | 285 ++++++++++++++++++++++++++++++++++++++ kernel/kprobes.c | 6 + 5 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 arch/arm/kernel/kprobes-opt-arm.c
Signature · 1 line
1.8.4
PATCH v8 1/2 kprobes: copy ainsn after alloc aggr kprobe
Copy old kprobe to newly alloced optimized_kprobe before arch_prepare_optimized_kprobe(). Original kprove can brings more information to optimizer.
v1 -> v2:
- Bugfix: copy p->addr when alloc_aggr_kprobe.
--- kernel/kprobes.c | 6 ++++++ 1 file changed, 6 insertions(+)
kernel/kprobes.c +6 -0
@@ -730,7 +730,13 @@ static struct kprobe *alloc_aggr_kprobe(struct kprobe *p)return NULL ;INIT_LIST_HEAD (&op->list); + +/* +* copy gives arch_prepare_optimized_kprobe +* more information +*/ op->kp.addr = p->addr; +copy_kprobe (p, &op->kp);arch_prepare_optimized_kprobe (op);return &op->kp;
Signature · 1 line
1.8.4
Re: [PATCH v8 1/2] kprobes: copy ainsn after alloc aggr kprobe
Hi Wang,
Quoted 3 lines · Wang Nan
Copy old kprobe to newly alloced
optimized_kprobebeforearch_prepare_optimized_kprobe(). Original kprove can brings more information to optimizer.
As I've asked you on the previous series, I prefer to transfer the original kprobes to arch_prepare_optimized_kprobe().
OK, now I'll send you my patch. If you can agree, feel free to pick that into your series.
Thank you,
Quoted 28 lines
v1 -> v2:
- Bugfix: copyp->addrwhenalloc_aggr_kprobe.Signed-off-by Wang Nan <[email protected]>--- kernel/kprobes.c | 6 ++++++ 1 file changed, 6 insertions(+)kernel/kprobes.c +6
-0@@ -730,7 +730,13 @@ static struct kprobe *alloc_aggr_kprobe(struct kprobe *p)return NULL ;INIT_LIST_HEAD (&op->list); + +/* +* copy gives arch_prepare_optimized_kprobe +* more information +*/ op->kp.addr = p->addr; +copy_kprobe (p, &op->kp);arch_prepare_optimized_kprobe (op);return &op->kp;
Signature · 4 lines
Masami HIRAMATSU Software Platform Research Dept. Linux Technology Research Center Hitachi, Ltd., Yokohama Research Laboratory E-mail: masami.hiramatsu.pt at hitachi.com
Re: [PATCH v8 1/2] kprobes: copy ainsn after alloc aggr kprobe
Quoted 7 lines · Masami Hiramatsu
Hi Wang,
Quoted 3 lines · Wang Nan
Copy old kprobe to newly alloced
optimized_kprobebeforearch_prepare_optimized_kprobe(). Original kprove can brings more information to optimizer.
Quoted 6 lines
As I've asked you on the previous series, I prefer to transfer the original kprobes to
arch_prepare_optimized_kprobe().OK, now I'll send you my patch. If you can agree, feel free to pick that into your series.
Sorry. The new version series has heavy code changes in checker. I think I must miss real optprobes code during my coding. I will collect your patch and adjust my code in the next version.
Thank you.
Quoted 32 lines
Thank you,
Quoted 28 lines
v1 -> v2:
- Bugfix: copyp->addrwhenalloc_aggr_kprobe.Signed-off-by Wang Nan <[email protected]>--- kernel/kprobes.c | 6 ++++++ 1 file changed, 6 insertions(+)kernel/kprobes.c +6
-0@@ -730,7 +730,13 @@ static struct kprobe *alloc_aggr_kprobe(struct kprobe *p)return NULL ;INIT_LIST_HEAD (&op->list); + +/* +* copy gives arch_prepare_optimized_kprobe +* more information +*/ op->kp.addr = p->addr; +copy_kprobe (p, &op->kp);arch_prepare_optimized_kprobe (op);return &op->kp;
PATCH v8 2/2 ARM: kprobes: enable OPTPROBES for ARM 32
In reply to Wang NanThis 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.
When unregistering kprobe, the deferred manner of unoptimizer may leave branch instruction before optimizer is called. Different from x86_64, which only copy the probed insn after optprobe_template_end and reexecute them, this patch call singlestep to emulate/simulate the insn directly. Futher patch can optimize this behavior.
v1 -> v2:
- Improvement: if replaced instruction is conditional, generate a conditional branch instruction for it;
- Introduces RELATIVEJUMP_OPCODES due to ARM kprobe_opcode_t is 4 bytes;
- Removes size field in struct arch_optimized_insn;
- Use arm_gen_branch() to generate branch instruction;
- Remove all recover logic: ARM doesn't use tail buffer, no need recover replaced instructions like x86;
- Remove incorrect CONFIG_THUMB checking;
- can_optimize() always returns true if address is well aligned;
- Improve optimized_callback: using opt_pre_handler();
- Bugfix: correct range checking code and improve comments;
- Fix commit message.
v2 -> v3:
- Rename RELATIVEJUMP_OPCODES to MAX_COPIED_INSNS;
- Remove unneeded checking:
arch_check_optimized_kprobe(), can_optimize();
- Add missing flush_icache_range() in arch_prepare_optimized_kprobe();
- Remove unneeded 'return;'.
v3 -> v4:
- Use __mem_to_opcode_arm() to translate copied_insn to ensure it works in big endian kernel;
- Replace 'nop' placeholder in trampoline code template with '.long 0' to avoid confusion: reader may regard 'nop' as an instruction, but it is value in fact.
v4 -> v5:
- Don't optimize stack store operations.
- Introduce prepared field to arch_optimized_insn to indicate whether it is prepared. Similar to size field with x86. See v1 -> v2.
v5 -> v6:
- Dynamically reserve stack according to instruction.
- Rename: kprobes-opt.c -> kprobes-opt-arm.c.
- Set op->optinsn.insn after all works are done.
v6 -> v7:
- Using checker to check stack consumption.
v7 -> v8:
- Small code adjustments.
--- arch/arm/Kconfig | 1 + arch/arm/include/asm/kprobes.h | 26 ++++ arch/arm/kernel/Makefile | 3 +- arch/arm/kernel/kprobes-opt-arm.c | 285 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 arch/arm/kernel/kprobes-opt-arm.c
arch/arm/Kconfig +1 -0
@@ -59,6 +59,7 @@ config ARM select HAVE_MEMBLOCK select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND select HAVE_OPROFILE if (HAVE_PERF_EVENTS) + select HAVE_OPTPROBES if (!THUMB2_KERNEL) select HAVE_PERF_EVENTS select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMP
arch/arm/include/asm/kprobes.h +26 -0
@@ -50,5 +50,31 @@ int kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr);int kprobe_exceptions_notify (struct notifier_block *self,unsigned long val,void *data); +/* optinsn template addresses */ +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; + +#define MAX_OPTIMIZED_LENGTH (4) +#define MAX_OPTINSN_SIZE \ + (((unsigned long)&optprobe_template_end - \ + (unsigned long)&optprobe_template_entry)) +#define RELATIVEJUMP_SIZE (4) + +struct arch_optimized_insn { +/* +* copy of the original instructions. +* Different from x86, ARM kprobe_opcode_t is u32. +*/ +#define MAX_COPIED_INSN ((RELATIVEJUMP_SIZE) / sizeof(kprobe_opcode_t)) +kprobe_opcode_t copied_insn[MAX_COPIED_INSN ]; +/* detour code buffer */ +kprobe_opcode_t *insn; +/* +* we always copies one instruction on arm32, +* size always be 4, so no size field. +*/ +};#endif /* _ARM_KPROBES_H */
arch/arm/kernel/Makefile +2 -1
@@ -52,11 +52,12 @@ obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_UPROBES) += probes.o probes-arm.o uprobes.o uprobes-arm.o -obj-$(CONFIG_KPROBES) += probes.o kprobes.o kprobes-common.o patch.o probes-checkers-common.o +obj-$(CONFIG_KPROBES) += probes.o kprobes.o kprobes-common.o patch.o probes-checkers-common.o insn.o ifdef CONFIG_THUMB2_KERNEL obj-$(CONFIG_KPROBES) += kprobes-thumb.o probes-thumb.o probes-checkers-thumb.o else obj-$(CONFIG_KPROBES) += kprobes-arm.o probes-arm.o probes-checkers-arm.o +obj-$(CONFIG_OPTPROBES) += kprobes-opt-arm.o endif obj-$(CONFIG_ARM_KPROBES_TEST) += test-kprobes.o test-kprobes-objs := kprobes-test.o
arch/arm/kernel/kprobes-opt-arm.c new file +285 -0
285 lines not on this page · raw message
Signature · 1 line
1.8.4
Re: [PATCH v8 2/2] ARM: kprobes: enable OPTPROBES for ARM 32
Quoted 350 lines · Wang Nan
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()callsopt_pre_handler()to execute kprobe handler. It also emulate/simulate replaced instruction.When unregistering kprobe, the deferred manner of unoptimizer may leave branch instruction before optimizer is called. Different from
x86_64, which only copy the probed insn afteroptprobe_template_endand reexecute them, this patch call singlestep to emulate/simulate the insn directly. Futher patch can optimize this behavior.v1 -> v2:
- Improvement: if replaced instruction is conditional, generate a conditional branch instruction for it;
- Introduces
RELATIVEJUMP_OPCODESdue to ARMkprobe_opcode_tis 4 bytes;- Removes size field in struct
arch_optimized_insn;- Use
arm_gen_branch()to generate branch instruction;- Remove all recover logic: ARM doesn't use tail buffer, no need recover replaced instructions like x86;
- Remove incorrect
CONFIG_THUMBchecking;-
can_optimize()always returns true if address is well aligned;- Improve
optimized_callback: usingopt_pre_handler();- Bugfix: correct range checking code and improve comments;
- Fix commit message.
v2 -> v3:
- Rename
RELATIVEJUMP_OPCODEStoMAX_COPIED_INSNS;- Remove unneeded checking:
arch_check_optimized_kprobe(), can_optimize();- Add missing
flush_icache_range()inarch_prepare_optimized_kprobe();- Remove unneeded 'return;'.
v3 -> v4:
- Use
__mem_to_opcode_arm()to translatecopied_insnto ensure it works in big endian kernel;- Replace 'nop' placeholder in trampoline code template with '.long 0' to avoid confusion: reader may regard 'nop' as an instruction, but it is value in fact.
v4 -> v5:
- Don't optimize stack store operations.
- Introduce prepared field to
arch_optimized_insnto indicate whether it is prepared. Similar to size field with x86. See v1 -> v2.v5 -> v6:
- Dynamically reserve stack according to instruction.
- Rename:
kprobes-opt.c->kprobes-opt-arm.c.- Set
op->optinsn.insnafter all works are done.v6 -> v7:
- Using checker to check stack consumption.
v7 -> v8:
- Small code adjustments.
Signed-off-by Wang Nan <[email protected]>Acked-by Masami Hiramatsu <[email protected]>Cc Jon Medhurst (Tixy) <[email protected]>Cc Russell King - ARM Linux <[email protected]>Cc Will Deacon <[email protected]>--- arch/arm/Kconfig | 1 + arch/arm/include/asm/kprobes.h | 26 ++++ arch/arm/kernel/Makefile | 3 +- arch/arm/kernel/kprobes-opt-arm.c | 285 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 arch/arm/kernel/kprobes-opt-arm.carch/arm/Kconfig +1
-0@@ -59,6 +59,7 @@ config ARM select HAVE_MEMBLOCK select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND select HAVE_OPROFILE if (HAVE_PERF_EVENTS) + select HAVE_OPTPROBES if (!THUMB2_KERNEL) select HAVE_PERF_EVENTS select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMParch/arm/include/asm/kprobes.h +26
-0@@ -50,5 +50,31 @@ int kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr);int kprobe_exceptions_notify (struct notifier_block *self,unsigned long val,void *data); +/* optinsn template addresses */ +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; + +#define MAX_OPTIMIZED_LENGTH (4) +#define MAX_OPTINSN_SIZE \ + (((unsigned long)&optprobe_template_end - \ + (unsigned long)&optprobe_template_entry)) +#define RELATIVEJUMP_SIZE (4) + +struct arch_optimized_insn { +/* +* copy of the original instructions. +* Different from x86, ARM kprobe_opcode_t is u32. +*/ +#define MAX_COPIED_INSN ((RELATIVEJUMP_SIZE) / sizeof(kprobe_opcode_t)) +kprobe_opcode_t copied_insn[MAX_COPIED_INSN ]; +/* detour code buffer */ +kprobe_opcode_t *insn; +/* +* we always copies one instruction on arm32, +* size always be 4, so no size field. +*/ +};#endif /* _ARM_KPROBES_H */ arch/arm/kernel/Makefile +2
-1@@ -52,11 +52,12 @@ obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_UPROBES) += probes.o probes-arm.o uprobes.o uprobes-arm.o -obj-$(CONFIG_KPROBES) += probes.o kprobes.o kprobes-common.o patch.o probes-checkers-common.o +obj-$(CONFIG_KPROBES) += probes.o kprobes.o kprobes-common.o patch.o probes-checkers-common.o insn.o ifdef CONFIG_THUMB2_KERNEL obj-$(CONFIG_KPROBES) += kprobes-thumb.o probes-thumb.o probes-checkers-thumb.o else obj-$(CONFIG_KPROBES) += kprobes-arm.o probes-arm.o probes-checkers-arm.o +obj-$(CONFIG_OPTPROBES) += kprobes-opt-arm.o endif obj-$(CONFIG_ARM_KPROBES_TEST) += test-kprobes.o test-kprobes-objs := kprobes-test.oarch/arm/kernel/kprobes-opt-arm.c new file +176
-0@@ -0,0 +1,285 @@ +/* +* Kernel Probes Jump Optimization (Optprobes) +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +* Copyright (C) IBM Corporation, 2002, 2004 +* Copyright (C) Hitachi Ltd., 2012 +* Copyright (C) Huawei Inc., 2014 +*/ + +#include <linux/kprobes.h> +#include <linux/jump_label.h> +#include <asm/kprobes.h> +#include <asm/cacheflush.h> +/* for arm_gen_branch */ +#include "insn.h" +/* for patch_text */ +#include "patch.h" + +/* +* NOTE: the first sub and add instruction will be modified according +* to the stack cost of the instruction. +*/ +asm ( +".global optprobe_template_entry\n" +"optprobe_template_entry:\n" +" sub sp, sp, #0xff\n" +" stmia sp, {r0 - r14} \n" +" add r3, sp, #0xff\n" +" str r3, [sp, #52]\n" +" mrs r4, cpsr\n" +" str r4, [sp, #64]\n" +" mov r1, sp\n" +" ldr r0, 1f\n" +" ldr r2, 2f\n" +" blx r2\n" +" ldr r1, [sp, #64]\n" +" msr cpsr_fs, r1\n" +" ldmia sp, {r0 - r15}\n" +".global optprobe_template_val\n" +"optprobe_template_val:\n" +"1: .long 0\n" +".global optprobe_template_call\n" +"optprobe_template_call:\n" +"2: .long 0\n" +".global optprobe_template_end\n" +"optprobe_template_end:\n" ); + +#define TMPL_VAL_IDX \ + ((long)&optprobe_template_val - (long)&optprobe_template_entry) +#define TMPL_CALL_IDX \ + ((long)&optprobe_template_call - (long)&optprobe_template_entry) +#define TMPL_END_IDX \ + ((long)&optprobe_template_end - (long)&optprobe_template_entry) + +/* +* ARM can always optimize an instruction when using ARM ISA, except +* instructions like 'str r0, [sp, r1]' which store to stack and unable +* to determine stack space consumption statically. +*/ +int arch_prepared_optinsn (struct arch_optimized_insn *optinsn) +{ +return optinsn->insn !=NULL ; +} + +/* +* In ARM ISA, kprobe opt always replace one instruction (4 bytes +* aligned and 4 bytes long). It is impossiable to encounter another +* kprobe in the address range. So always return 0. +*/ +int arch_check_optimized_kprobe (struct optimized_kprobe *op) +{ +return 0 ; +} + +/* Caller must ensure addr & 3 == 0 */ +static int can_optimize (struct optimized_kprobe *op) +{ +if (op->kp.ainsn.stack_space <0 ) +return 0 ; +/* +* 255 is the biggest imm can be used in 'sub r0, r0, #<imm>'. +* Number larger than 255 needs special encoding. +*/ +if (op->kp.ainsn.stack_space >255 -sizeof (struct pt_regs )) +return 0 ; +return 1 ; +} + +/* 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 ; + +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 *code; +unsigned long rel_chk; +unsigned long val; +unsigned long stack_protect =sizeof (struct pt_regs ); + +if (!can_optimize (op)) +return -EILSEQ ; + + buf = (u8 *)get_optinsn_slot (); +if (!buf) +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 +*
arch/arm/kernel/kprobes-opt-arm.c line 186
Quoted 10 lines
arch/arm/kernel/kprobes-opt-arm.c new file +10
-0+* 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 )buf - + (long )op->kp.addr +8 ) &0xfe000003 ; + +if ((rel_chk !=0 ) && (rel_chk !=0xfe000000 )) { +__arch_remove_optimized_kprobe (op,0 );
Here is a bug. Different from x86_64, ARM code doesn't set op->optinsn.insn here, so __arch_remove_optimized_kprobe won't do anything. In version 9 patch series I fix this problem by calling free_optinsn_slot(buf, 0) directly. However, I forgot to mention it in its commit message.
Quoted 100 lines
arch/arm/kernel/kprobes-opt-arm.c new file +99
-0+return -ERANGE ; + } + +/* Copy arch-dep-instance from template. */ +memcpy (buf, &optprobe_template_entry,TMPL_END_IDX ); + +/* Adjust buffer according to instruction. */ +BUG_ON (op->kp.ainsn.stack_space <0 ); + stack_protect += op->kp.ainsn.stack_space; + +/* Should have been filtered by can_optimize(). */ +BUG_ON (stack_protect >255 ); + +/* Create a 'sub sp, sp, #<stack_protect>' */ + code = (unsigned long *)(buf); + code[0 ] =__opcode_to_mem_arm (0xe24dd000 | stack_protect); +/* Create a 'add r3, sp, #<stack_protect>' */ + code[2 ] =__opcode_to_mem_arm (0xe28d3000 | stack_protect); + +/* 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)); + +flush_icache_range ((unsigned long )buf, + (unsigned long )buf +TMPL_END_IDX ); + +/* Set op->optinsn.insn means prepared */ + op->optinsn.insn = (kprobe_opcode_t *)buf; +return 0 ; +} + +void arch_optimize_kprobes (struct list_head *oplist) +{ +struct optimized_kprobe *op, *tmp; + +list_for_each_entry_safe (op, tmp, oplist, list) { +unsigned long insn; +WARN_ON (kprobe_disabled (&op->kp)); + +/* +* Backup instructions which will be replaced +* by jump address +*/ +memcpy (op->optinsn.copied_insn, op->kp.addr, +RELATIVEJUMP_SIZE ); + + insn =arm_gen_branch ((unsigned long )op->kp.addr, + (unsigned long )op->optinsn.insn); +BUG_ON (insn ==0 ); + +/* +* Make it a conditional branch if replaced insn +* is consitional +*/ + insn = (__mem_to_opcode_arm ( + op->optinsn.copied_insn[0 ]) &0xf0000000 ) | + (insn &0x0fffffff ); + +patch_text (op->kp.addr, insn); + +list_del_init (&op->list); + } +} + +void arch_unoptimize_kprobe (struct optimized_kprobe *op) +{ +arch_arm_kprobe (&op->kp); +} + +/* +* Recover original instructions and breakpoints from relative jumps. +* Caller must call with locking kprobe_mutex. +*/ +void arch_unoptimize_kprobes (struct list_head *oplist, +struct list_head *done_list) +{ +struct optimized_kprobe *op, *tmp; + +list_for_each_entry_safe (op, tmp, oplist, list) { +arch_unoptimize_kprobe (op); +list_move (&op->list, done_list); + } +} + +int arch_within_optimized_kprobe (struct optimized_kprobe *op, +unsigned long addr) +{ +return ((unsigned long )op->kp.addr <= addr && + (unsigned long )op->kp.addr +RELATIVEJUMP_SIZE > addr); +} + +void arch_remove_optimized_kprobe (struct optimized_kprobe *op) +{ +__arch_remove_optimized_kprobe (op,1 ); +}
PATCH 1/1 kprobes: Pass the original kprobe for preparing optimized kprobe
In reply to Wang NanPass the original kprobe for preparing an optimized kprobe arch-dep part, since for some architecture (e.g. ARM32) requires the information in original kprobe.
--- arch/x86/kernel/kprobes/opt.c | 3 ++- include/linux/kprobes.h | 3 ++- kernel/kprobes.c | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-)
arch/x86/kernel/kprobes/opt.c +2 -1
@@ -322,7 +322,8 @@ void arch_remove_optimized_kprobe(struct optimized_kprobe *op)* Target instructions MUST be relocatable (checked inside) * This is called when new aggr(opt)probe is allocated or reused. */ -int arch_prepare_optimized_kprobe (struct optimized_kprobe *op) +int arch_prepare_optimized_kprobe (struct optimized_kprobe *op, +struct kprobe *__unused) {u8 *buf;int ret;
include/linux/kprobes.h +2 -1
@@ -308,7 +308,8 @@ struct optimized_kprobe {/* Architecture dependent functions for direct jump optimization */ extern int arch_prepared_optinsn (struct arch_optimized_insn *optinsn);extern int arch_check_optimized_kprobe (struct optimized_kprobe *op); -extern int arch_prepare_optimized_kprobe (struct optimized_kprobe *op); +extern int arch_prepare_optimized_kprobe (struct optimized_kprobe *op, +struct kprobe *orig);extern void arch_remove_optimized_kprobe (struct optimized_kprobe *op);extern void arch_optimize_kprobes (struct list_head *oplist); extern void arch_unoptimize_kprobes(struct list_head *oplist,
kernel/kprobes.c +2 -2
@@ -717,7 +717,7 @@ static void prepare_optimized_kprobe(struct kprobe *p)struct optimized_kprobe *op; op = container_of(p, struct optimized_kprobe, kp); -arch_prepare_optimized_kprobe (op); +arch_prepare_optimized_kprobe (op, p); }/* Allocate new optimized_kprobe and try to prepare optimized instructions */
@@ -731,7 +731,7 @@ static struct kprobe *alloc_aggr_kprobe(struct kprobe *p)INIT_LIST_HEAD (&op->list); op->kp.addr = p->addr; -arch_prepare_optimized_kprobe (op); +arch_prepare_optimized_kprobe (op, p);return &op->kp; }