ARM64: implement uprobe for aarch32 mode 12/111112/4
authorVyacheslav Cherkashin <v.cherkashin@samsung.com>
Tue, 9 Aug 2016 20:04:16 +0000 (23:04 +0300)
committerVyacheslav Cherkashin <v.cherkashin@samsung.com>
Tue, 31 Jan 2017 07:21:19 +0000 (10:21 +0300)
Change-Id: I518e8c528ee2eb455e7d6ffba4942a111928a79e
Signed-off-by: Vyacheslav Cherkashin <v.cherkashin@samsung.com>
arch/arm/probes/compat_arm64.h [new file with mode: 0644]
arch/arm/probes/decode_thumb.c
arch/arm/uprobe/swap_uprobe.c [new file with mode: 0644]
arch/arm/uprobe/swap_uprobe.h [new file with mode: 0644]
uprobe/Kbuild
uprobe/arch/arm/swap-asm/swap_uprobes.c
uprobe/arch/arm/swap-asm/swap_uprobes.h
uprobe/arch/arm64/swap-asm/swap_uprobes.c
uprobe/arch/arm64/swap-asm/swap_uprobes.h
us_manager/sspt/sspt.h

diff --git a/arch/arm/probes/compat_arm64.h b/arch/arm/probes/compat_arm64.h
new file mode 100644 (file)
index 0000000..e85766e
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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) Samsung Electronics, 2016
+ *
+ * 2016         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_ASM_COMPAT_ARM64_H
+#define _SWAP_ASM_COMPAT_ARM64_H
+
+
+#ifdef CONFIG_ARM64
+
+# define PSR_T_BIT             COMPAT_PSR_T_BIT
+
+# define ARM_r0                        compat_usr(0)
+# define ARM_r1                        compat_usr(1)
+# define ARM_r2                        compat_usr(2)
+# define ARM_r3                        compat_usr(3)
+# define ARM_r4                        compat_usr(4)
+# define ARM_r5                        compat_usr(5)
+# define ARM_r6                        compat_usr(6)
+# define ARM_r7                        compat_usr(7)
+# define ARM_r8                        compat_usr(8)
+# define ARM_r9                        compat_usr(9)
+# define ARM_r10               compat_usr(10)
+# define ARM_fp                        compat_fp
+# define ARM_ip                        compat_usr(12)
+# define ARM_sp                        compat_sp
+# define ARM_lr                        compat_lr
+# define ARM_pc                        pc
+# define ARM_cpsr              pstate
+
+# define thumb_mode(regs)      compat_thumb_mode(regs)
+
+#endif /* CONFIG_ARM64 */
+
+
+#endif /* _SWAP_ASM_COMPAT_ARM64_H */
+
+
+
+
+
+
index b04f27d..71f5da5 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/ptrace.h>
 #include "decode_thumb.h"
 #include "tramps_thumb.h"
+#include "compat_arm64.h"
 
 
 #define GET_BIT(x, n)          ((x >> n) & 0x1)
diff --git a/arch/arm/uprobe/swap_uprobe.c b/arch/arm/uprobe/swap_uprobe.c
new file mode 100644 (file)
index 0000000..3773e25
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * 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) Samsung Electronics, 2016
+ *
+ * 2016         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+
+#include <arch/arm/probes/probes.h>
+#include <arch/arm/probes/probes_arm.h>
+#include <arch/arm/probes/probes_thumb.h>
+#include <uprobe/swap_uprobes.h>
+#include <kprobe/swap_kprobes_deps.h>
+#include <kprobe/swap_slots.h>
+
+
+#define flush_insns(addr, size) \
+               flush_icache_range((unsigned long)(addr), \
+                                  (unsigned long)(addr) + (size))
+
+
+/**
+ * @brief Prepares uprobe for ARM.
+ *
+ * @param up Pointer to the uprobe.
+ * @return 0 on success,\n
+ * negative error code on error.
+ */
+int arch_prepare_uprobe_arm(struct uprobe *p)
+{
+       int ret;
+       struct task_struct *task = p->task;
+       unsigned long vaddr = (unsigned long)p->addr;
+       u32 insn;
+       u32 tramp[UPROBES_TRAMP_LEN];
+       u32 __user *utramp;
+       enum { tramp_len = sizeof(tramp) };
+
+       if (!read_proc_vm_atomic(task, vaddr & ~1, &insn, sizeof(insn))) {
+               printk(KERN_ERR "failed to read memory %lx!\n", vaddr);
+               return -EINVAL;
+       }
+
+       ret = make_tramp(&p->ainsn.insn, vaddr, insn, tramp, tramp_len);
+       if (ret) {
+               pr_err("failed to make tramp, addr=%p\n", p->addr);
+               return ret;
+       }
+
+       utramp = swap_slot_alloc(p->sm);
+       if (utramp == NULL) {
+               printk(KERN_INFO "Error: swap_slot_alloc failed (%08lx)\n",
+                      vaddr);
+               return -ENOMEM;
+       }
+
+       if (!write_proc_vm_atomic(p->task, (unsigned long)utramp, tramp,
+                                 tramp_len)) {
+               pr_err("failed to write memory tramp=%p!\n", utramp);
+               swap_slot_free(p->sm, utramp);
+               return -EINVAL;
+       }
+
+       flush_insns(utramp, tramp_len);
+       p->insn = utramp;
+       p->opcode = insn;
+
+       return 0;
+}
+
+int arch_arm_uprobe_arm(struct uprobe *p)
+{
+       int ret;
+       unsigned long vaddr = (unsigned long)p->addr & ~((unsigned long)1);
+       int thumb_mode = (unsigned long)p->addr & 1;
+       int len = 4 >> thumb_mode;      /* if thumb_mode then len = 2 */
+       unsigned long insn = thumb_mode ? BREAK_THUMB : BREAK_ARM;
+
+       ret = write_proc_vm_atomic(p->task, vaddr, &insn, len);
+       if (!ret) {
+               pr_err("failed to write memory tgid=%u addr=%08lx len=%d\n",
+                      p->task->tgid, vaddr, len);
+
+               return -EACCES;
+       } else {
+               flush_insns(vaddr, len);
+       }
+
+       return 0;
+}
+
+void arch_disarm_uprobe_arm(struct uprobe *p, struct task_struct *task)
+{
+       int ret;
+
+       unsigned long vaddr = (unsigned long)p->addr & ~((unsigned long)1);
+       int thumb_mode = (unsigned long)p->addr & 1;
+       int len = 4 >> thumb_mode;      /* if thumb_mode then len = 2 */
+
+       ret = write_proc_vm_atomic(task, vaddr, &p->opcode, len);
+       if (!ret) {
+               pr_err("Failed to write memory tgid=%u addr=%08lx len=%d\n",
+                      task->tgid, vaddr, len);
+       } else {
+               flush_insns(vaddr, len);
+       }
+}
diff --git a/arch/arm/uprobe/swap_uprobe.h b/arch/arm/uprobe/swap_uprobe.h
new file mode 100644 (file)
index 0000000..ba645b3
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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) Samsung Electronics, 2016
+ *
+ * 2016         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _SWAP_ASM_ARM_UPROBE_H
+#define _SWAP_ASM_ARM_UPROBE_H
+
+
+#include <linux/uaccess.h>
+#include "../probes/compat_arm64.h"
+
+
+struct pt_regs;
+struct uprobe;
+struct uretprobe;
+struct uretprobe_instance;
+
+
+static inline unsigned long swap_get_uarg_arm(struct pt_regs *regs,
+                                             unsigned long n)
+{
+       u32 *ptr, val = 0;
+
+       switch (n) {
+       case 0:
+               return regs->ARM_r0;
+       case 1:
+               return regs->ARM_r1;
+       case 2:
+               return regs->ARM_r2;
+       case 3:
+               return regs->ARM_r3;
+       default:
+               ptr = (u32 *)regs->ARM_sp + n - 4;
+               if (get_user(val, ptr))
+                       pr_err("Failed to dereference a pointer[%p]\n", ptr);
+               break;
+       }
+
+       return val;
+}
+
+static inline void swap_put_uarg_arm(struct pt_regs *regs, unsigned long n,
+                                    unsigned long val)
+{
+       u32 *ptr;
+
+       switch (n) {
+       case 0:
+               regs->ARM_r0 = val;
+               break;
+       case 1:
+               regs->ARM_r1 = val;
+               break;
+       case 2:
+               regs->ARM_r2 = val;
+               break;
+       case 3:
+               regs->ARM_r3 = val;
+               break;
+       default:
+               ptr = (u32 *)regs->ARM_sp + n - 4;
+               if (put_user(val, ptr))
+                       pr_err("Failed to dereference a pointer[%p]\n", ptr);
+       }
+}
+
+int arch_prepare_uprobe_arm(struct uprobe *p);
+int arch_arm_uprobe_arm(struct uprobe *p);
+void arch_disarm_uprobe_arm(struct uprobe *p, struct task_struct *task);
+
+
+#endif /* _SWAP_ASM_ARM_UPROBE_H */
+
index a3ceaf9..94f0b7e 100644 (file)
@@ -10,13 +10,21 @@ swap_uprobe-$(CONFIG_ARM) += \
        ../arch/arm/probes/probes_thumb.o \
        ../arch/arm/probes/decode_thumb.o \
        ../arch/arm/probes/tramps_thumb_img.o \
-       ../arch/arm/probes/probes.o
+       ../arch/arm/probes/probes.o \
+       ../arch/arm/uprobe/swap_uprobe.o
 
 
 ### ARM64
 swap_uprobe-$(CONFIG_ARM64) += \
        arch/arm64/swap-asm/swap_uprobes.o \
-       arch/arm64/swap-asm/uprobes-arm64.o
+       arch/arm64/swap-asm/uprobes-arm64.o \
+       ../arch/arm/probes/probes_arm.o \
+       ../arch/arm/probes/tramps_arm_img.o \
+       ../arch/arm/probes/decode_thumb.o \
+       ../arch/arm/probes/probes_thumb.o \
+       ../arch/arm/probes/tramps_thumb_img.o \
+       ../arch/arm/probes/probes.o \
+       ../arch/arm/uprobe/swap_uprobe.o
 
 
 ### X86
index ee95d27..2da7b7e 100644 (file)
 
 
 /**
- * @def flush_insns
- * @brief Flushes instructions.
- */
-#define flush_insns(addr, size)                                        \
-       flush_icache_range((unsigned long)(addr),               \
-                          (unsigned long)(addr) + (size))
-
-/**
  * @brief Prepares uprobe for ARM.
  *
  * @param up Pointer to the uprobe.
 int arch_prepare_uprobe(struct uprobe *p)
 {
        int ret;
-       struct task_struct *task = p->task;
-       unsigned long vaddr = (unsigned long)p->addr;
-       unsigned long insn;
-       u32 tramp[UPROBES_TRAMP_LEN];
-       unsigned long __user *utramp;
-       enum { tramp_len = sizeof(tramp) };
-
-       if (!read_proc_vm_atomic(task, vaddr & ~1, &insn, sizeof(insn))) {
-               printk(KERN_ERR "failed to read memory %lx!\n", vaddr);
-               return -EINVAL;
-       }
 
-       ret = make_tramp(&p->ainsn.insn, vaddr, insn, tramp, tramp_len);
-       if (ret) {
-               pr_err("failed to make tramp, addr=%p\n", p->addr);
-               return ret;
-       }
-
-       utramp = swap_slot_alloc(p->sm);
-       if (utramp == NULL) {
-               printk(KERN_INFO "Error: swap_slot_alloc failed (%08lx)\n",
-                      vaddr);
-               return -ENOMEM;
-       }
-
-       if (!write_proc_vm_atomic(p->task, (unsigned long)utramp, tramp,
-                                 tramp_len)) {
-               pr_err("failed to write memory tramp=%p!\n", utramp);
-               swap_slot_free(p->sm, utramp);
-               return -EINVAL;
+       ret = arch_prepare_uprobe_arm(p);
+       if (!ret) {
+               /* for uretprobe */
+               add_uprobe_table(p);
        }
 
-       flush_insns(utramp, tramp_len);
-       p->insn = utramp;
-       p->opcode = insn;
-
-       /* for uretprobe */
-       add_uprobe_table(p);
-
-       return 0;
+       return ret;
 }
 
 /**
@@ -303,44 +263,6 @@ void arch_remove_uprobe(struct uprobe *up)
        swap_slot_free(up->sm, up->insn);
 }
 
-int arch_arm_uprobe(struct uprobe *p)
-{
-       int ret;
-       unsigned long vaddr = (unsigned long)p->addr & ~((unsigned long)1);
-       int thumb_mode = (unsigned long)p->addr & 1;
-       int len = 4 >> thumb_mode;      /* if thumb_mode then len = 2 */
-       unsigned long insn = thumb_mode ? BREAK_THUMB : BREAK_ARM;
-
-       ret = write_proc_vm_atomic(p->task, vaddr, &insn, len);
-       if (!ret) {
-               pr_err("arch_arm_uprobe: failed to write memory tgid=%u addr=%08lx len=%d\n",
-                      p->task->tgid, vaddr, len);
-
-               return -EACCES;
-       } else {
-               flush_insns(vaddr, len);
-       }
-
-       return 0;
-}
-
-void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task)
-{
-       int ret;
-
-       unsigned long vaddr = (unsigned long)p->addr & ~((unsigned long)1);
-       int thumb_mode = (unsigned long)p->addr & 1;
-       int len = 4 >> thumb_mode;      /* if thumb_mode then len = 2 */
-
-       ret = write_proc_vm_atomic(task, vaddr, &p->opcode, len);
-       if (!ret) {
-               pr_err("arch_disarm_uprobe: failed to write memory tgid=%u addr=%08lx len=%d\n",
-                      task->tgid, vaddr, len);
-       } else {
-               flush_insns(vaddr, len);
-       }
-}
-
 static int urp_handler(struct pt_regs *regs, pid_t tgid)
 {
        struct uprobe *p;
@@ -421,7 +343,7 @@ static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
                        if (p) {
                                get_up(p);
                                local_irq_restore(flags);
-                               pr_err("invalid mode: thumb=%d addr=%p insn=%08lx\n",
+                               pr_err("invalid mode: thumb=%d addr=%p insn=%08x\n",
                                       !!thumb_mode(regs), p->addr, p->opcode);
                                ret = 0;
 
index 28018ee..ba954ec 100644 (file)
@@ -37,6 +37,7 @@
 
 #include <linux/uaccess.h>
 #include <arch/arm/probes/probes.h>
+#include <arch/arm/uprobe/swap_uprobe.h>
 
 
 /** Uprobes trampoline length */
@@ -49,7 +50,7 @@ struct arch_insn;
 struct uretprobe;
 struct uretprobe_instance;
 
-typedef unsigned long uprobe_opcode_t;
+typedef u32 uprobe_opcode_t;
 
 
 /**
@@ -79,7 +80,6 @@ static inline void arch_ujprobe_return(void)
 }
 
 int arch_prepare_uprobe(struct uprobe *up);
-
 int setjmp_upre_handler(struct uprobe *p, struct pt_regs *regs);
 static inline int longjmp_break_uhandler(struct uprobe *p, struct pt_regs *regs)
 {
@@ -94,55 +94,26 @@ int arch_disarm_urp_inst(struct uretprobe_instance *ri,
 unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs);
 void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs);
 void arch_remove_uprobe(struct uprobe *up);
-int arch_arm_uprobe(struct uprobe *p);
-void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task);
+
+static inline int arch_arm_uprobe(struct uprobe *p)
+{
+       return arch_arm_uprobe_arm(p);
+}
+
+static inline void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task)
+{
+       arch_disarm_uprobe_arm(p, task);
+}
 
 static inline unsigned long swap_get_uarg(struct pt_regs *regs, unsigned long n)
 {
-       u32 *ptr, addr = 0;
-
-       switch (n) {
-       case 0:
-               return regs->ARM_r0;
-       case 1:
-               return regs->ARM_r1;
-       case 2:
-               return regs->ARM_r2;
-       case 3:
-               return regs->ARM_r3;
-       }
-
-       ptr = (u32 *)regs->ARM_sp + n - 4;
-       if (get_user(addr, ptr))
-               printk(KERN_INFO "failed to dereference a pointer, ptr=%p\n",
-                      ptr);
-
-       return addr;
+       return swap_get_uarg_arm(regs, n);
 }
 
 static inline void swap_put_uarg(struct pt_regs *regs, unsigned long n,
                                  unsigned long val)
 {
-       u32 *ptr;
-
-       switch (n) {
-       case 0:
-               regs->ARM_r0 = val;
-               break;
-       case 1:
-               regs->ARM_r1 = val;
-               break;
-       case 2:
-               regs->ARM_r2 = val;
-               break;
-       case 3:
-               regs->ARM_r3 = val;
-               break;
-       default:
-               ptr = (u32 *)regs->ARM_sp + n - 4;
-               if (put_user(val, ptr))
-                       pr_err("failed to dereference a pointer[%p]\n", ptr);
-       }
+       swap_put_uarg_arm(regs, n, val);
 }
 
 int swap_arch_init_uprobes(void);
index fa12ee9..1be33c0 100644 (file)
 
 
 #include <linux/types.h>
+#include <asm/traps.h>
+#include <arch/arm/uprobe/swap_uprobe.h>
 #include <uprobe/swap_uprobes.h>
 #include <kprobe/swap_slots.h>
 #include <kprobe/swap_td_raw.h>
 #include <kprobe/swap_kprobes_deps.h>  /* FIXME: remove it */
+#include <swap-asm/swap_probes.h>
 #include <swap-asm/dbg_interface.h>
 #include "uprobes-arm64.h"
 
 #define BRK64_OPCODE_URP       MAKE_BRK(BRK_URP)
 
 
+enum arch_mode {
+       AM_UNKNOWN,
+       AM_THUMB,
+       AM_ARM,
+       AM_ARM64
+};
+
+#define ARM64_MODE_VADDR_MASK          ((unsigned long)1 << 63)
+
+static enum arch_mode get_arch_mode(unsigned long vaddr)
+{
+       if (vaddr & 1)
+               return AM_THUMB;
+
+       if (vaddr & ARM64_MODE_VADDR_MASK)
+               return AM_ARM64;
+
+       return AM_ARM;
+}
+
+static unsigned long get_real_addr(unsigned long vaddr)
+{
+       return vaddr & ~(ARM64_MODE_VADDR_MASK | 1);
+}
+
+
 struct uprobe_ctlblk {
        struct uprobe *p;
 };
@@ -129,28 +158,71 @@ static enum dbg_code urp_handler(struct pt_regs *regs, unsigned int esr)
        return DBG_HANDLED;
 }
 
-
-int arch_arm_uprobe(struct uprobe *p)
+static int arch_arm_uprobe_arm64(struct uprobe *p)
 {
-       uprobe_opcode_t insn = BRK64_OPCODE_BP;
-       int ret = write_proc_vm_atomic(p->task, (unsigned long)p->addr,
-                                      &insn, sizeof(insn));
+       int ret;
+       unsigned long vaddr = (unsigned long)p->addr;
+       unsigned long raddr = get_real_addr(vaddr);
+       u32 insn = BRK64_OPCODE_BP;
+
+       ret = write_proc_vm_atomic(p->task, raddr, &insn, sizeof(insn));
        if (!ret) {
-               pr_err("failed to write memory addr=%p\n", p->addr);
+               pr_err("failed to write memory addr=%lx\n", vaddr);
                return -EACCES;
        }
 
        return 0;
 }
 
-void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task)
+static void arch_disarm_uprobe_arm64(struct uprobe *p,
+                                    struct task_struct *task)
 {
+       unsigned long vaddr = (unsigned long)p->addr;
+       unsigned long raddr = get_real_addr(vaddr);
        int ret;
 
-       ret = write_proc_vm_atomic(task, (unsigned long)p->addr,
-                                  &p->opcode, sizeof(p->opcode));
+       ret = write_proc_vm_atomic(task, raddr, &p->opcode, sizeof(p->opcode));
        if (!ret)
-               pr_err("failed to write memory addr=%p!\n", p->addr);
+               pr_err("failed to write memory vaddr=%lx\n", vaddr);
+}
+
+int arch_arm_uprobe(struct uprobe *p)
+{
+       int ret;
+       unsigned long vaddr = (unsigned long)p->addr;
+
+       switch (get_arch_mode(vaddr)) {
+       case AM_THUMB:
+       case AM_ARM:
+               ret = arch_arm_uprobe_arm(p);
+               break;
+       case AM_ARM64:
+               ret = arch_arm_uprobe_arm64(p);
+               break;
+       default:
+               pr_err("Error: unknown mode vaddr=%lx\n", vaddr);
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
+void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task)
+{
+       unsigned long vaddr = (unsigned long)p->addr;
+
+       switch (get_arch_mode(vaddr)) {
+       case AM_THUMB:
+       case AM_ARM:
+               arch_disarm_uprobe_arm(p, task);
+               break;
+       case AM_ARM64:
+               arch_disarm_uprobe_arm64(p, task);
+               break;
+       default:
+               pr_err("Error: unknown mode vaddr=%lx\n", vaddr);
+               break;
+       }
 }
 
 
@@ -168,8 +240,20 @@ static void arch_prepare_ss_arm64(struct uprobe *p)
 
 }
 
-static int arch_prepare_uprobe_arm64(struct uprobe *p, u32 insn)
+static int arch_prepare_uprobe_arm64(struct uprobe *p)
 {
+       struct task_struct *task = p->task;
+       unsigned long vaddr = (unsigned long)p->addr;
+       unsigned long raddr = get_real_addr(vaddr);
+       u32 insn;
+
+       if (!read_proc_vm_atomic(task, raddr, &insn, sizeof(insn))) {
+               pr_err("failed to read memory %lx!\n", raddr);
+               return -EINVAL;
+       }
+
+       p->ainsn.matrioshka_flags = 0;
+
        switch (arm64_uprobe_decode_insn(insn, &p->ainsn)) {
        case INSN_REJECTED:     /* insn not supported */
                return -EINVAL;
@@ -193,23 +277,15 @@ static int arch_prepare_uprobe_arm64(struct uprobe *p, u32 insn)
 
 int arch_prepare_uprobe(struct uprobe *p)
 {
-       struct task_struct *task = p->task;
-       unsigned long vaddr = (unsigned long)p->addr;
-       u32 insn;
+       int ret;
 
-       if (vaddr & 0x01) {
-               pr_err("Error in %s at %d: attempt to register uprobe "
-                      "at an unaligned address\n", __FILE__, __LINE__);
-               return -EINVAL;
+       if (get_arch_mode((unsigned long)p->addr) == AM_ARM64) {
+               ret = arch_prepare_uprobe_arm64(p);
+       } else {
+               ret = arch_prepare_uprobe_arm(p);
        }
 
-       if (!read_proc_vm_atomic(task, vaddr, &insn, sizeof(insn)))
-               pr_err("failed to read memory %lx!\n", vaddr);
-
-       p->opcode = insn;
-       p->ainsn.matrioshka_flags = 0;
-
-       return arch_prepare_uprobe_arm64(p, insn);
+       return ret;
 }
 
 void arch_remove_uprobe(struct uprobe *p)
@@ -381,23 +457,115 @@ static struct brk_hook dbg_urp_bp = {
 };
 
 
+static void arch_prepare_singlestep(struct uprobe *p, struct pt_regs *regs)
+{
+       if (p->ainsn.insn.handler) {
+               regs->pc += 4;
+               p->ainsn.insn.handler(p->opcode, &p->ainsn.insn, regs);
+       } else {
+               regs->pc = (unsigned long)p->insn;
+       }
+}
+
+static int uprobe_handler_aarch32(struct pt_regs *regs, u32 instr)
+{
+       struct uprobe *p;
+       unsigned long flags;
+       unsigned long vaddr = regs->pc | !!compat_thumb_mode(regs);
+       pid_t tgid = current->tgid;
+
+       local_irq_enable();
+
+       local_irq_save(flags);
+       p = get_uprobe((uprobe_opcode_t *)vaddr, tgid);
+       if (p) {
+               get_up(p);
+               local_irq_restore(flags);
+
+               if (!p->pre_handler || !p->pre_handler(p, regs))
+                       arch_prepare_singlestep(p, regs);
+
+               put_up(p);
+       } else {
+               local_irq_restore(flags);
+       }
+
+       return 0;
+}
+
+
+static void (*__register_undef_hook)(struct undef_hook *hook);
+static void (*__unregister_undef_hook)(struct undef_hook *hook);
+
+static int undef_hook_once(void)
+{
+       const char *sym;
+
+       sym = "register_undef_hook";
+       __register_undef_hook = (void *)swap_ksyms(sym);
+       if (__register_undef_hook == NULL)
+               goto not_found;
+
+       sym = "unregister_undef_hook";
+       __unregister_undef_hook = (void *)swap_ksyms(sym);
+       if (__unregister_undef_hook == NULL)
+               goto not_found;
+
+       return 0;
+
+not_found:
+       pr_err("ERROR: symbol '%s' not found\n", sym);
+       return -ESRCH;
+
+}
+
+static struct undef_hook undef_hook_arm = {
+       .instr_mask = 0xffffffff,
+       .instr_val = BREAK_ARM,
+       .pstate_mask = COMPAT_PSR_MODE_MASK,
+       .pstate_val = COMPAT_PSR_MODE_USR,
+       .fn = uprobe_handler_aarch32,
+};
+
+static struct undef_hook undef_hook_thumb = {
+       .instr_mask = 0xffff,
+       .instr_val = BREAK_THUMB,
+       .pstate_mask = COMPAT_PSR_MODE_MASK,
+       .pstate_val = COMPAT_PSR_MODE_USR,
+       .fn = uprobe_handler_aarch32,
+};
+
 int swap_arch_init_uprobes(void)
 {
        int ret;
 
+       ret = undef_hook_once();
+       if (ret)
+               return ret;
+
        ret = swap_td_raw_reg(&td_raw, sizeof(struct uprobe_ctlblk));
        if (ret)
                return ret;
 
+       /* for aarch64 */
        dbg_brk_hook_reg(&dbg_up_ss);
        dbg_brk_hook_reg(&dbg_up_bp);
        dbg_brk_hook_reg(&dbg_urp_bp);
 
+       /* for aarch32 */
+       __register_undef_hook(&undef_hook_arm);
+       __register_undef_hook(&undef_hook_thumb);
+
        return 0;
 }
 
 void swap_arch_exit_uprobes(void)
 {
+       /* for aarch32 */
+       __unregister_undef_hook(&undef_hook_thumb);
+       __unregister_undef_hook(&undef_hook_arm);
+
+       /* for aarch64 */
        dbg_brk_hook_unreg(&dbg_urp_bp);
        dbg_brk_hook_unreg(&dbg_up_bp);
        dbg_brk_hook_unreg(&dbg_up_ss);
index a3a8efd..5494e08 100644 (file)
@@ -24,6 +24,7 @@
 
 
 #include <linux/types.h>
+#include <arch/arm/probes/probes.h>
 
 
 #define UP_TRAMP_INSN_CNT  3 /* | opcode | ss_bp | urp_bp | */
@@ -63,6 +64,10 @@ enum {
 
 
 struct arch_insn {
+       /* arm */
+       struct arch_insn_arm insn;
+
+       /* arm64 */
        unsigned long matrioshka_flags;
        uprobes_pstate_check_t *pstate_cc;
        uprobes_condition_check_t *check_condn;
index 8bec181..1754acc 100644 (file)
@@ -58,7 +58,7 @@ static inline int sspt_register_usprobe(struct sspt_ip *ip)
                return -EINVAL;
        }
 
-       up->addr = (kprobe_opcode_t *)ip->orig_addr;
+       up->addr = (uprobe_opcode_t *)ip->orig_addr;
        up->task = ip->page->file->proc->leader;
        up->sm = ip->page->file->proc->sm;