[IMPROVE] ARM: create jumper
[kernel/swap-modules.git] / kprobe / arch / asm-arm / dbi_kprobes.c
1 /*
2  *  Dynamic Binary Instrumentation Module based on KProbes
3  *  modules/kprobe/arch/asm-arm/dbi_kprobes.c
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  * Copyright (C) Samsung Electronics, 2006-2010
20  *
21  * 2006-2007    Ekaterina Gorelkina <e.gorelkina@samsung.com>: initial implementation for ARM/MIPS
22  * 2008-2009    Alexey Gerenkov <a.gerenkov@samsung.com> User-Space
23  *              Probes initial implementation; Support x86.
24  * 2010         Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
25  * 2010-2011    Alexander Shirshikov <a.shirshikov@samsung.com>: initial implementation for Thumb
26  * 2012         Stanislav Andreev <s.andreev@samsung.com>: added time debug profiling support; BUG() message fix
27  * 2012         Stanislav Andreev <s.andreev@samsung.com>: redesign of kprobe functionality -
28  *              kprobe_handler() now called via undefined instruction hooks
29  * 2012         Stanislav Andreev <s.andreev@samsung.com>: hash tables search implemented for uprobes
30  */
31
32 #include <linux/module.h>
33 #include <linux/mm.h>
34
35 #include "dbi_kprobes.h"
36 #include "trampoline_arm.h"
37 #include <kprobe/dbi_kprobes.h>
38
39 #include <kprobe/dbi_kdebug.h>
40 #include <kprobe/dbi_insn_slots.h>
41 #include <kprobe/dbi_kprobes_deps.h>
42 #include <ksyms/ksyms.h>
43
44 #include <asm/cacheflush.h>
45 #include <asm/traps.h>
46 #include <asm/ptrace.h>
47 #include <linux/list.h>
48 #include <linux/hash.h>
49
50 #define SUPRESS_BUG_MESSAGES
51
52 #define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
53 #define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
54
55 extern struct kprobe * per_cpu__current_kprobe;
56 extern struct hlist_head kprobe_table[KPROBE_TABLE_SIZE];
57
58 static void (*__swap_register_undef_hook)(struct undef_hook *hook);
59 static void (*__swap_unregister_undef_hook)(struct undef_hook *hook);
60
61 static unsigned long get_addr_b(unsigned long insn, unsigned long addr)
62 {
63         /* real position less then PC by 8 */
64         return (kprobe_opcode_t)((long)addr + 8 + branch_displacement(insn));
65 }
66
67 static int prep_pc_dep_insn_execbuf(kprobe_opcode_t *insns,
68                                     kprobe_opcode_t insn, int uregs)
69 {
70         int i;
71
72         if (uregs & 0x10) {
73                 int reg_mask = 0x1;
74                 //search in reg list
75                 for (i = 0; i < 13; i++, reg_mask <<= 1) {
76                         if (!(insn & reg_mask))
77                                 break;
78                 }
79         } else {
80                 for (i = 0; i < 13; i++) {
81                         if ((uregs & 0x1) && (ARM_INSN_REG_RN(insn) == i))
82                                 continue;
83                         if ((uregs & 0x2) && (ARM_INSN_REG_RD(insn) == i))
84                                 continue;
85                         if ((uregs & 0x4) && (ARM_INSN_REG_RS(insn) == i))
86                                 continue;
87                         if ((uregs & 0x8) && (ARM_INSN_REG_RM(insn) == i))
88                                 continue;
89                         break;
90                 }
91         }
92
93         if (i == 13) {
94                 DBPRINTF ("there are no free register %x in insn %lx!", uregs, insn);
95                 return -EINVAL;
96         }
97         DBPRINTF ("prep_pc_dep_insn_execbuf: using R%d, changing regs %x", i, uregs);
98
99         // set register to save
100         ARM_INSN_REG_SET_RD(insns[0], i);
101         // set register to load address to
102         ARM_INSN_REG_SET_RD(insns[1], i);
103         // set instruction to execute and patch it
104         if (uregs & 0x10) {
105                 ARM_INSN_REG_CLEAR_MR(insn, 15);
106                 ARM_INSN_REG_SET_MR(insn, i);
107         } else {
108                 if ((uregs & 0x1) && (ARM_INSN_REG_RN(insn) == 15))
109                         ARM_INSN_REG_SET_RN(insn, i);
110                 if ((uregs & 0x2) && (ARM_INSN_REG_RD(insn) == 15))
111                         ARM_INSN_REG_SET_RD(insn, i);
112                 if ((uregs & 0x4) && (ARM_INSN_REG_RS(insn) == 15))
113                         ARM_INSN_REG_SET_RS(insn, i);
114                 if ((uregs & 0x8) && (ARM_INSN_REG_RM(insn) == 15))
115                         ARM_INSN_REG_SET_RM(insn, i);
116         }
117
118         insns[UPROBES_TRAMP_INSN_IDX] = insn;
119         // set register to restore
120         ARM_INSN_REG_SET_RD(insns[3], i);
121
122         return 0;
123 }
124
125 static int arch_check_insn_arm(unsigned long insn)
126 {
127         /* check instructions that can change PC by nature */
128         if (
129          /* ARM_INSN_MATCH(UNDEF, insn) || */
130             ARM_INSN_MATCH(AUNDEF, insn) ||
131             ARM_INSN_MATCH(SWI, insn) ||
132             ARM_INSN_MATCH(BREAK, insn) ||
133             ARM_INSN_MATCH(BXJ, insn)) {
134                 goto bad_insn;
135 #ifndef CONFIG_CPU_V7
136         /* check instructions that can write result to PC */
137         } else if ((ARM_INSN_MATCH(DPIS, insn) ||
138                     ARM_INSN_MATCH(DPRS, insn) ||
139                     ARM_INSN_MATCH(DPI, insn) ||
140                     ARM_INSN_MATCH(LIO, insn) ||
141                     ARM_INSN_MATCH(LRO, insn)) &&
142                    (ARM_INSN_REG_RD(insn) == 15)) {
143                 goto bad_insn;
144 #endif /* CONFIG_CPU_V7 */
145         /* check special instruction loads store multiple registers */
146         } else if ((ARM_INSN_MATCH(LM, insn) || ARM_INSN_MATCH(SM, insn)) &&
147                         /* store PC or load to PC */
148                    (ARM_INSN_REG_MR(insn, 15) ||
149                          /* store/load with PC update */
150                     ((ARM_INSN_REG_RN(insn) == 15) && (insn & 0x200000)))) {
151                 goto bad_insn;
152         }
153
154         return 0;
155
156 bad_insn:
157         printk("Bad insn arch_check_insn_arm: %lx\n", insn);
158         return -EFAULT;
159 }
160
161 static int make_branch_tarmpoline(unsigned long addr, unsigned long insn,
162                                   unsigned long *tramp)
163 {
164         int ok = 0;
165
166         /* B */
167         if (ARM_INSN_MATCH(B, insn) &&
168             !ARM_INSN_MATCH(BLX1, insn)) {
169                 /* B check can be false positive on BLX1 instruction */
170                 memcpy(tramp, b_cond_insn_execbuf, KPROBES_TRAMP_LEN);
171                 tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
172                 tramp[0] |= insn & 0xf0000000;
173                 tramp[6] = get_addr_b(insn, addr);
174                 tramp[7] = addr + 4;
175                 ok = 1;
176         /* BX, BLX (Rm) */
177         } else if (ARM_INSN_MATCH(BX, insn) ||
178                    ARM_INSN_MATCH(BLX2, insn)) {
179                 memcpy(tramp, b_r_insn_execbuf, KPROBES_TRAMP_LEN);
180                 tramp[0] = insn;
181                 tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
182                 tramp[7] = addr + 4;
183                 ok = 1;
184         /* BL, BLX (Off) */
185         } else if (ARM_INSN_MATCH(BLX1, insn)) {
186                 memcpy(tramp, blx_off_insn_execbuf, KPROBES_TRAMP_LEN);
187                 tramp[0] |= 0xe0000000;
188                 tramp[1] |= 0xe0000000;
189                 tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
190                 tramp[6] = get_addr_b(insn, addr) +
191                            2 * (insn & 01000000) + 1; /* jump to thumb */
192                 tramp[7] = addr + 4;
193                 ok = 1;
194         /* BL */
195         } else if (ARM_INSN_MATCH(BL, insn)) {
196                 memcpy(tramp, blx_off_insn_execbuf, KPROBES_TRAMP_LEN);
197                 tramp[0] |= insn & 0xf0000000;
198                 tramp[1] |= insn & 0xf0000000;
199                 tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
200                 tramp[6] = get_addr_b(insn, addr);
201                 tramp[7] = addr + 4;
202                 ok = 1;
203         }
204
205         return ok;
206 }
207
208 int arch_make_trampoline_arm(unsigned long addr, unsigned long insn,
209                              unsigned long *tramp)
210 {
211         int ret, uregs, pc_dep;
212
213         if (addr & 0x03) {
214                 printk("Error in %s at %d: attempt to register uprobe "
215                        "at an unaligned address\n", __FILE__, __LINE__);
216                 return -EINVAL;
217         }
218
219         ret = arch_check_insn_arm(insn);
220         if (ret)
221                 return ret;
222
223         if (make_branch_tarmpoline(addr, insn, tramp))
224                 return 0;
225
226         uregs = pc_dep = 0;
227         /* Rm */
228         if (ARM_INSN_MATCH(CLZ, insn)) {
229                 uregs = 0xa;
230                 if (ARM_INSN_REG_RM(insn) == 15)
231                         pc_dep = 1;
232         /* Rn, Rm ,Rd */
233         } else if (ARM_INSN_MATCH(DPIS, insn) || ARM_INSN_MATCH(LRO, insn) ||
234             ARM_INSN_MATCH(SRO, insn)) {
235                 uregs = 0xb;
236                 if ((ARM_INSN_REG_RN(insn) == 15) ||
237                     (ARM_INSN_REG_RM(insn) == 15) ||
238                     (ARM_INSN_MATCH(SRO, insn) &&
239                      (ARM_INSN_REG_RD(insn) == 15))) {
240                         pc_dep = 1;
241                 }
242         /* Rn ,Rd */
243         } else if (ARM_INSN_MATCH(DPI, insn) || ARM_INSN_MATCH(LIO, insn) ||
244                    ARM_INSN_MATCH(SIO, insn)) {
245                 uregs = 0x3;
246                 if ((ARM_INSN_REG_RN(insn) == 15) ||
247                     (ARM_INSN_MATCH(SIO, insn) &&
248                     (ARM_INSN_REG_RD(insn) == 15))) {
249                         pc_dep = 1;
250                 }
251         /* Rn, Rm, Rs */
252         } else if (ARM_INSN_MATCH(DPRS, insn)) {
253                 uregs = 0xd;
254                 if ((ARM_INSN_REG_RN(insn) == 15) ||
255                     (ARM_INSN_REG_RM(insn) == 15) ||
256                     (ARM_INSN_REG_RS(insn) == 15)) {
257                         pc_dep = 1;
258                 }
259         /* register list */
260         } else if (ARM_INSN_MATCH(SM, insn)) {
261                 uregs = 0x10;
262                 if (ARM_INSN_REG_MR(insn, 15)) {
263                         pc_dep = 1;
264                 }
265         }
266
267         /* check instructions that can write result to SP and uses PC */
268         if (pc_dep && (ARM_INSN_REG_RD(insn) == 13)) {
269                 printk("Error in %s at %d: instruction check failed (arm)\n",
270                        __FILE__, __LINE__);
271                 return -EFAULT;
272         }
273
274         if (unlikely(uregs && pc_dep)) {
275                 memcpy(tramp, pc_dep_insn_execbuf, KPROBES_TRAMP_LEN);
276                 if (prep_pc_dep_insn_execbuf(tramp, insn, uregs) != 0) {
277                         printk("Error in %s at %d: failed "
278                                "to prepare exec buffer for insn %lx!",
279                                __FILE__, __LINE__, insn);
280                         return -EINVAL;
281                 }
282
283                 tramp[6] = addr + 8;
284         } else {
285                 memcpy(tramp, gen_insn_execbuf, KPROBES_TRAMP_LEN);
286                 tramp[KPROBES_TRAMP_INSN_IDX] = insn;
287         }
288
289         /* TODO: remove for kprobe */
290         tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
291         tramp[7] = addr + 4;
292
293         return 0;
294 }
295 EXPORT_SYMBOL_GPL(arch_make_trampoline_arm);
296
297 int arch_prepare_kprobe(struct kprobe *p, struct slot_manager *sm)
298 {
299         unsigned long addr = (unsigned long)p->addr;
300         unsigned long insn = p->opcode = *p->addr;
301         unsigned long *tramp;
302         int ret;
303
304         tramp = alloc_insn_slot(sm);
305         if (tramp == NULL)
306                 return -ENOMEM;
307
308         ret = arch_make_trampoline_arm(addr, insn, tramp);
309         if (ret) {
310                 free_insn_slot(sm, tramp);
311                 return ret;
312         }
313
314         flush_icache_range((unsigned long)tramp,
315                            (unsigned long)tramp + KPROBES_TRAMP_LEN);
316
317         p->ainsn.insn = tramp;
318
319         return 0;
320 }
321
322 void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
323 {
324         if (p->ss_addr) {
325                 regs->ARM_pc = (unsigned long)p->ss_addr;
326                 p->ss_addr = NULL;
327         } else {
328                 regs->ARM_pc = (unsigned long)p->ainsn.insn;
329         }
330 }
331 EXPORT_SYMBOL_GPL(prepare_singlestep);
332
333 void save_previous_kprobe(struct kprobe_ctlblk *kcb, struct kprobe *p_run)
334 {
335         kcb->prev_kprobe.kp = kprobe_running();
336         kcb->prev_kprobe.status = kcb->kprobe_status;
337 }
338
339 void restore_previous_kprobe(struct kprobe_ctlblk *kcb)
340 {
341         __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
342         kcb->kprobe_status = kcb->prev_kprobe.status;
343 }
344
345 void set_current_kprobe(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb)
346 {
347         __get_cpu_var(current_kprobe) = p;
348         DBPRINTF ("set_current_kprobe: p=%p addr=%p\n", p, p->addr);
349 }
350
351 static int kprobe_handler(struct pt_regs *regs)
352 {
353         struct kprobe *p, *cur;
354         struct kprobe_ctlblk *kcb;
355
356         kcb = get_kprobe_ctlblk();
357         cur = kprobe_running();
358         p = get_kprobe((void *)regs->ARM_pc);
359
360         if (p) {
361                 if (cur) {
362                         /* Kprobe is pending, so we're recursing. */
363                         switch (kcb->kprobe_status) {
364                         case KPROBE_HIT_ACTIVE:
365                         case KPROBE_HIT_SSDONE:
366                                 /* A pre- or post-handler probe got us here. */
367                                 kprobes_inc_nmissed_count(p);
368                                 save_previous_kprobe(kcb, NULL);
369                                 set_current_kprobe(p, 0, 0);
370                                 kcb->kprobe_status = KPROBE_REENTER;
371                                 prepare_singlestep(p, regs);
372                                 restore_previous_kprobe(kcb);
373                                 break;
374                         default:
375                                 /* impossible cases */
376                                 BUG();
377                         }
378                 } else {
379                         set_current_kprobe(p, 0, 0);
380                         kcb->kprobe_status = KPROBE_HIT_ACTIVE;
381
382                         if (!p->pre_handler || !p->pre_handler(p, regs)) {
383                                 kcb->kprobe_status = KPROBE_HIT_SS;
384                                 prepare_singlestep(p, regs);
385                                 reset_current_kprobe();
386                         }
387                 }
388         } else {
389                 goto no_kprobe;
390         }
391
392         return 0;
393
394 no_kprobe:
395         printk("no_kprobe: Not one of ours: let kernel handle it %p\n",
396                         (unsigned long *)regs->ARM_pc);
397         return 1;
398 }
399
400 int kprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
401 {
402         int ret;
403         unsigned long flags;
404
405 #ifdef SUPRESS_BUG_MESSAGES
406         int swap_oops_in_progress;
407         /* oops_in_progress used to avoid BUG() messages
408          * that slow down kprobe_handler() execution */
409         swap_oops_in_progress = oops_in_progress;
410         oops_in_progress = 1;
411 #endif
412
413         local_irq_save(flags);
414         preempt_disable();
415         ret = kprobe_handler(regs);
416         preempt_enable_no_resched();
417         local_irq_restore(flags);
418
419 #ifdef SUPRESS_BUG_MESSAGES
420         oops_in_progress = swap_oops_in_progress;
421 #endif
422
423         return ret;
424 }
425
426 int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
427 {
428         struct jprobe *jp = container_of(p, struct jprobe, kp);
429         kprobe_pre_entry_handler_t pre_entry = (kprobe_pre_entry_handler_t)jp->pre_entry;
430         entry_point_t entry = (entry_point_t)jp->entry;
431         pre_entry = (kprobe_pre_entry_handler_t)jp->pre_entry;
432
433         if (pre_entry) {
434                 p->ss_addr = (void *)pre_entry (jp->priv_arg, regs);
435         }
436
437         if (entry) {
438                 entry(regs->ARM_r0, regs->ARM_r1, regs->ARM_r2,
439                       regs->ARM_r3, regs->ARM_r4, regs->ARM_r5);
440         } else {
441                 dbi_jprobe_return();
442         }
443
444         return 0;
445 }
446
447 void dbi_jprobe_return (void)
448 {
449 }
450
451 int longjmp_break_handler (struct kprobe *p, struct pt_regs *regs)
452 {
453         return 0;
454 }
455 EXPORT_SYMBOL_GPL(longjmp_break_handler);
456
457 #ifdef CONFIG_STRICT_MEMORY_RWX
458 extern void mem_text_write_kernel_word(unsigned long *addr, unsigned long word);
459 #endif
460
461 void arch_arm_kprobe(struct kprobe *p)
462 {
463 #ifdef CONFIG_STRICT_MEMORY_RWX
464         mem_text_write_kernel_word(p->addr, BREAKPOINT_INSTRUCTION);
465 #else
466         *p->addr = BREAKPOINT_INSTRUCTION;
467         flush_icache_range((unsigned long)p->addr, (unsigned long)p->addr + sizeof(kprobe_opcode_t));
468 #endif
469 }
470
471 void arch_disarm_kprobe(struct kprobe *p)
472 {
473 #ifdef CONFIG_STRICT_MEMORY_RWX
474         mem_text_write_kernel_word(p->addr, p->opcode);
475 #else
476         *p->addr = p->opcode;
477         flush_icache_range((unsigned long)p->addr, (unsigned long)p->addr + sizeof(kprobe_opcode_t));
478 #endif
479 }
480
481 void __naked kretprobe_trampoline(void)
482 {
483         __asm__ __volatile__ (
484                 "stmdb  sp!, {r0 - r11}         \n\t"
485                 "mov    r1, sp                  \n\t"
486                 "mov    r0, #0                  \n\t"
487                 "bl     trampoline_probe_handler\n\t"
488                 "mov    lr, r0                  \n\t"
489                 "ldmia  sp!, {r0 - r11}         \n\t"
490                 "bx     lr                      \n\t"
491                 : : : "memory");
492 }
493
494 void arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
495 {
496         unsigned long *ptr_ret_addr;
497
498         /* for __switch_to probe */
499         if ((unsigned long)ri->rp->kp.addr == sched_addr) {
500                 struct thread_info *tinfo = (struct thread_info *)regs->ARM_r2;
501
502                 ptr_ret_addr = (unsigned long *)&tinfo->cpu_context.pc;
503                 ri->sp = NULL;
504                 ri->task = tinfo->task;
505         } else {
506                 ptr_ret_addr = (unsigned long *)&regs->ARM_lr;
507                 ri->sp = (unsigned long *)regs->ARM_sp;
508         }
509
510         /* Save the return address */
511         ri->ret_addr = (unsigned long *)*ptr_ret_addr;
512
513         /* Replace the return addr with trampoline addr */
514         *ptr_ret_addr = (unsigned long)&kretprobe_trampoline;
515 }
516
517
518
519
520
521 /*
522  ******************************************************************************
523  *                                   jumper                                   *
524  ******************************************************************************
525  */
526 struct cb_data {
527         unsigned long ret_addr;
528         unsigned long r0;
529
530         jumper_cb_t cb;
531         char data[0];
532 };
533
534 static unsigned long __used get_r0(struct cb_data *data)
535 {
536         return data->r0;
537 }
538
539 static unsigned long __used jump_handler(struct cb_data *data)
540 {
541         unsigned long ret_addr = data->ret_addr;
542
543         /* call callback */
544         data->cb(data->data);
545
546         /* FIXME: potential memory leak, when process kill */
547         kfree(data);
548
549         return ret_addr;
550 }
551
552 /* FIXME: restore condition flags */
553 void jump_trampoline(void);
554 __asm(
555         "jump_trampoline:               \n"
556
557         "push   {r0 - r12}              \n"
558         "mov    r1, r0                  \n"     /* data --> r1 */
559         "bl     get_r0                  \n"
560         "str    r0, [sp]                \n"     /* restore r0 */
561         "mov    r0, r1                  \n"     /* data --> r0 */
562         "bl     jump_handler            \n"
563         "mov    lr, r0                  \n"
564         "pop    {r0 - r12}              \n"
565         "bx     lr                      \n"
566 );
567
568 unsigned long get_jump_addr(void)
569 {
570         return (unsigned long)&jump_trampoline;
571 }
572 EXPORT_SYMBOL_GPL(get_jump_addr);
573
574 int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs,
575                 jumper_cb_t cb, void *data, size_t size)
576 {
577         struct cb_data *cb_data;
578
579         cb_data = kmalloc(sizeof(*cb_data) + size, GFP_ATOMIC);
580
581         /* save data */
582         cb_data->ret_addr = ret_addr;
583         cb_data->cb = cb;
584         cb_data->r0 = regs->ARM_r0;
585         memcpy(cb_data->data, data, size);
586
587         /* save cb_data to r0 */
588         regs->ARM_r0 = (long)cb_data;
589
590         return 0;
591 }
592 EXPORT_SYMBOL_GPL(set_jump_cb);
593
594
595
596
597
598 void swap_register_undef_hook(struct undef_hook *hook)
599 {
600         __swap_register_undef_hook(hook);
601 }
602 EXPORT_SYMBOL_GPL(swap_register_undef_hook);
603
604 void swap_unregister_undef_hook(struct undef_hook *hook)
605 {
606         __swap_unregister_undef_hook(hook);
607 }
608 EXPORT_SYMBOL_GPL(swap_unregister_undef_hook);
609
610 // kernel probes hook
611 static struct undef_hook undef_ho_k = {
612         .instr_mask     = 0xffffffff,
613         .instr_val      = BREAKPOINT_INSTRUCTION,
614         .cpsr_mask      = MODE_MASK,
615         .cpsr_val       = SVC_MODE,
616         .fn             = kprobe_trap_handler
617 };
618
619 int arch_init_kprobes(void)
620 {
621         // Register hooks (kprobe_handler)
622         __swap_register_undef_hook = (void *)swap_ksyms("register_undef_hook");
623         if (__swap_register_undef_hook == NULL) {
624                 printk("no register_undef_hook symbol found!\n");
625                 return -1;
626         }
627
628         // Unregister hooks (kprobe_handler)
629         __swap_unregister_undef_hook = (void *)swap_ksyms("unregister_undef_hook");
630         if (__swap_unregister_undef_hook == NULL) {
631                 printk("no unregister_undef_hook symbol found!\n");
632                 return -1;
633         }
634
635         swap_register_undef_hook(&undef_ho_k);
636
637         return 0;
638 }
639
640 void arch_exit_kprobes(void)
641 {
642         swap_unregister_undef_hook(&undef_ho_k);
643 }
644
645 /* export symbol for trampoline_arm.h */
646 EXPORT_SYMBOL_GPL(gen_insn_execbuf);
647 EXPORT_SYMBOL_GPL(pc_dep_insn_execbuf);
648 EXPORT_SYMBOL_GPL(b_r_insn_execbuf);
649 EXPORT_SYMBOL_GPL(b_cond_insn_execbuf);
650 EXPORT_SYMBOL_GPL(blx_off_insn_execbuf);