[IMPROVE] STRD/LDRD instrumentation for THUMB2 20/53420/3
authorVyacheslav Cherkashin <v.cherkashin@samsung.com>
Thu, 3 Dec 2015 13:09:07 +0000 (16:09 +0300)
committerDmitry Kovalenko <d.kovalenko@samsung.com>
Fri, 18 Dec 2015 12:28:00 +0000 (04:28 -0800)
Change-Id: I386476d736e683c3a5ae9083338d9a9d52b7a4cc
Signed-off-by: Vyacheslav Cherkashin <v.cherkashin@samsung.com>
uprobe/Kbuild
uprobe/arch/arm/swap-asm/decode_thumb.c [new file with mode: 0644]
uprobe/arch/arm/swap-asm/decode_thumb.h [new file with mode: 0644]
uprobe/arch/arm/swap-asm/swap_uprobes.c
uprobe/arch/arm/swap-asm/thumb_tramps.c [new file with mode: 0644]
uprobe/arch/arm/swap-asm/thumb_tramps.h [new file with mode: 0644]

index 11be1b1..bed91d2 100644 (file)
@@ -5,8 +5,11 @@ obj-m := swap_uprobe.o
 swap_uprobe-y := swap_uprobes.o
 
 ### ARM
-swap_uprobe-$(CONFIG_ARM) += arch/arm/swap-asm/swap_uprobes.o \
-                             arch/arm/swap-asm/trampoline_thumb.o
+swap_uprobe-$(CONFIG_ARM) += \
+       arch/arm/swap-asm/swap_uprobes.o \
+       arch/arm/swap-asm/trampoline_thumb.o \
+       arch/arm/swap-asm/decode_thumb.o \
+       arch/arm/swap-asm/thumb_tramps.o
 
 
 ### X86
diff --git a/uprobe/arch/arm/swap-asm/decode_thumb.c b/uprobe/arch/arm/swap-asm/decode_thumb.c
new file mode 100644 (file)
index 0000000..3b1f671
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * 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, 2015
+ *
+ * 2015         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include "decode_thumb.h"
+#include "thumb_tramps.h"
+
+
+#define GET_BIT(x, n)          ((x >> n) & 0x1)
+#define GET_FIELD(x, s, l)      ((x >> s) & ((1 << l) - 1))
+#define SET_FIELD(x, s, l, v)  ({              \
+       typeof(x) mask = (((1 << l) - 1) << s); \
+       (x & ~mask) | ((v << s) & mask);        \
+})
+
+
+typedef union thumb_insn {
+       unsigned long val;
+       struct {
+               unsigned short hw1;
+               unsigned short hw2;
+       } __packed;
+} thumb_insn_t;
+
+typedef int (*decode_handler_t)(thumb_insn_t insn, struct decode_info *info);
+
+
+static bool bad_reg(int n)
+{
+       return n == 13 || n == 15;
+}
+
+static int thumb_not_implement(thumb_insn_t insn, struct decode_info *info)
+{
+       return -EFAULT;
+}
+
+static int thumb_unpredictable(thumb_insn_t insn, struct decode_info *info)
+{
+       return -EINVAL;
+}
+
+/* hw1[1110 100x x1xx ????] */
+static int t32_ldrd_strd(thumb_insn_t insn, struct decode_info *info)
+{
+       int w = GET_BIT(insn.hw1, 5);
+       int n = GET_FIELD(insn.hw1, 0, 4);
+       int t = GET_FIELD(insn.hw2, 12, 4);
+       int t2 = GET_FIELD(insn.hw2, 8, 4);
+
+       if (bad_reg(t) || bad_reg(t2))
+               return thumb_unpredictable(insn, info);
+
+       /* check load flag */
+       if (GET_BIT(insn.hw1, 4)) {
+               /* LDRD */
+               if ((w && (n == 15)) || t == t2)
+                       return thumb_unpredictable(insn, info);
+
+               if (n == 15) {
+                       /* change PC -> SP */
+                       insn.hw1 = SET_FIELD(insn.hw1, 0, 4, 13);
+                       tt_make_pc_deps(info->tramp, insn.val,
+                                       info->vaddr, true);
+
+                       return 0;
+               }
+       } else {
+               /* STRD */
+               if ((w && t == n) || (w && t2 == n) || (n == 15))
+                       return thumb_unpredictable(insn, info);
+       }
+
+       tt_make_common(info->tramp, insn.val, info->vaddr, true);
+
+       return 0;
+}
+
+static int t32_b1110_100x_x1(thumb_insn_t insn, struct decode_info *info)
+{
+       /* check PW bits */
+       if (insn.hw1 & 0x120)
+               return t32_ldrd_strd(insn, info);
+
+       return thumb_not_implement(insn, info);
+}
+
+static int t32_b1110_100(thumb_insn_t insn, struct decode_info *info)
+{
+       if (GET_BIT(insn.hw1, 6))
+               return t32_b1110_100x_x1(insn, info);
+
+       return thumb_not_implement(insn, info);
+}
+
+static int b111(thumb_insn_t insn, struct decode_info *info)
+{
+       /* hw1[111x xxx? ???? ????] */
+       switch (GET_FIELD(insn.hw1, 9, 4)) {
+       case 0b0100:
+               return t32_b1110_100(insn, info);
+       }
+
+       return thumb_not_implement(insn, info);
+}
+
+
+decode_handler_t table_xxx[8] = {
+       /* 000 */       thumb_not_implement,
+       /* 001 */       thumb_not_implement,
+       /* 010 */       thumb_not_implement,
+       /* 011 */       thumb_not_implement,
+       /* 100 */       thumb_not_implement,
+       /* 101 */       thumb_not_implement,
+       /* 110 */       thumb_not_implement,
+       /* 111 */       b111,
+};
+
+
+int decode_thumb(unsigned long insn, struct decode_info *info)
+{
+       thumb_insn_t tinsn = { .val = insn };
+
+       /* check first 3 bits hw1[xxx? ???? ???? ????] */
+       return table_xxx[GET_FIELD(tinsn.hw1, 13, 3)](tinsn, info);
+}
diff --git a/uprobe/arch/arm/swap-asm/decode_thumb.h b/uprobe/arch/arm/swap-asm/decode_thumb.h
new file mode 100644 (file)
index 0000000..b392417
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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, 2015
+ *
+ * 2015         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _ARM_DECODE_THUMB_H
+#define _ARM_DECODE_THUMB_H
+
+
+struct decode_info {
+       unsigned long vaddr;
+       void *tramp;
+};
+
+
+int decode_thumb(unsigned long insn, struct decode_info *info);
+
+
+#endif /* _ARM_DECODE_THUMB_H */
index 43315b7..857c447 100644 (file)
@@ -44,7 +44,7 @@
 
 #include <swap-asm/swap_kprobes.h>
 #include <swap-asm/trampoline_arm.h>
-
+#include "decode_thumb.h"
 #include "swap_uprobes.h"
 #include "trampoline_thumb.h"
 
@@ -602,10 +602,21 @@ int arch_prepare_uprobe(struct uprobe *p)
                return -EINVAL;
        }
 
-       ret = thumb_mode ?
-                       arch_make_trampoline_thumb(vaddr, insn,
-                                                  tramp, tramp_len) :
-                       arch_make_trampoline_arm(vaddr, insn, tramp);
+       if (thumb_mode) {
+               ret = arch_make_trampoline_thumb(vaddr, insn,
+                                                tramp, tramp_len);
+               if (ret) {
+                       struct decode_info info = {
+                               .vaddr = vaddr,
+                               .tramp = tramp,
+                       };
+
+                       ret = decode_thumb(insn, &info);
+               }
+       } else {
+               ret = arch_make_trampoline_arm(vaddr, insn, tramp);
+       }
+
        if (ret) {
                pr_err("failed to make tramp, addr=%p\n", p->addr);
                return ret;
diff --git a/uprobe/arch/arm/swap-asm/thumb_tramps.c b/uprobe/arch/arm/swap-asm/thumb_tramps.c
new file mode 100644 (file)
index 0000000..07bf494
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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, 2015
+ *
+ * 2015         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/string.h>
+#include <swap-asm/swap_kprobes.h>
+#include "trampoline_thumb.h"
+
+
+#define URET_BP                0xdeff          /* breakpoint for uretprobe */
+
+
+static void make_def(void *tramp, unsigned long insn,
+                    unsigned long vaddr, bool t2)
+{
+       unsigned long ret_addr;
+       unsigned short *tr = tramp;
+
+       /*
+        * thumb  - +2
+        * thumb2 - +4
+        */
+       ret_addr = vaddr + (2 << t2);
+       tr[4] = insn & 0x0000ffff;
+       if (t2)
+               tr[5] = insn >> 16;
+
+       tr[13] = URET_BP;
+       tr[16] = (ret_addr & 0x0000ffff) | 0x1;
+       tr[17] = ret_addr >> 16;
+}
+
+void tt_make_common(void *tramp, unsigned long insn,
+                   unsigned long vaddr, bool t2)
+{      memcpy(tramp, gen_insn_execbuf_thumb, 4 * UPROBES_TRAMP_LEN);
+       make_def(tramp, insn, vaddr, t2);
+}
+
+void tt_make_pc_deps(void *tramp, unsigned long mod_insn,
+                    unsigned long vaddr, bool t2)
+{
+       unsigned long pc_val = vaddr + 4;
+       unsigned short *tr = tramp;
+
+       memcpy(tramp, pc_dep_insn_execbuf_thumb, 4 * UPROBES_TRAMP_LEN);
+       make_def(tramp, mod_insn, vaddr, t2);
+
+       /* save PC value */
+       tr[14] = pc_val & 0x0000ffff;
+       tr[15] = pc_val >> 16;
+}
diff --git a/uprobe/arch/arm/swap-asm/thumb_tramps.h b/uprobe/arch/arm/swap-asm/thumb_tramps.h
new file mode 100644 (file)
index 0000000..0b45415
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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, 2015
+ *
+ * 2015         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#ifndef _ARM_THUMB_TRAMPS_H
+#define _ARM_THUMB_TRAMPS_H
+
+
+#include <linux/types.h>
+
+
+void tt_make_common(void *tramp, unsigned long insn,
+                   unsigned long vaddr, bool t2);
+void tt_make_pc_deps(void *tramp, unsigned long mod_insn,
+                    unsigned long vaddr, bool t2);
+
+
+#endif /* _ARM_THUMB_TRAMPS_H */