From 3dbe931fa74516d58db157984dbfda772d74d84c Mon Sep 17 00:00:00 2001 From: Dmitry Kovalenko Date: Thu, 22 Dec 2016 15:21:42 +0300 Subject: [PATCH 01/16] loader: fix error: 'LOADER_CALLER' defined but not used Change-Id: If342b77f0dccf76c0cc8f4c4e6ac4767ad0bdf50 Signed-off-by: Dongkyun, Son --- loader/loader_debugfs.c | 1 - 1 file changed, 1 deletion(-) diff --git a/loader/loader_debugfs.c b/loader/loader_debugfs.c index 44ad077..cc56148 100644 --- a/loader/loader_debugfs.c +++ b/loader/loader_debugfs.c @@ -17,7 +17,6 @@ static const char LOADER_FOLDER[] = "loader"; static const char LOADER_LOADER[] = "loader"; static const char LOADER_LOADER_OFFSET[] = "loader_offset"; static const char LOADER_LOADER_PATH[] = "loader_path"; -static const char LOADER_CALLER[] = "caller"; static const char LOADER_LINKER_DATA[] = "linker"; static const char LOADER_LINKER_PATH[] = "linker_path"; static const char LOADER_LINKER_R_DEBUG_OFFSET[] = "r_debug_offset"; -- 2.7.4 From be89f4e94cac1b6b9cd8c0d4450f0ad9b52079a6 Mon Sep 17 00:00:00 2001 From: Dmitry Kovalenko Date: Thu, 18 Aug 2016 15:35:10 +0300 Subject: [PATCH 02/16] Energy: Support new function names in new kernel sock_aio_read/write -> sock_read/write_iter Change-Id: I868d148ed380417a5a93e35b8928093b236ab192 --- energy/energy.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/energy/energy.c b/energy/energy.c index 49e238a..73ab3b0 100644 --- a/energy/energy.c +++ b/energy/energy.c @@ -680,6 +680,14 @@ static struct kretprobe sock_aio_write_krp = { .data_size = sizeof(struct socket *) }; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) +static const char sock_aio_read[] = "sock_read_iter"; +static const char sock_aio_write[] = "sock_write_iter"; +#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) */ +static const char sock_aio_read[] = "sock_aio_read"; +static const char sock_aio_write[] = "sock_aio_write"; +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) */ + static struct kern_probe wifi_probes[] = { { .name = "sock_recvmsg", @@ -690,11 +698,11 @@ static struct kern_probe wifi_probes[] = { .rp = &sock_send_krp, }, { - .name = "sock_aio_read", + .name = sock_aio_read, .rp = &sock_aio_read_krp, }, { - .name = "sock_aio_write", + .name = sock_aio_write, .rp = &sock_aio_write_krp, } }; -- 2.7.4 From eabbd2d40b74408264a5f0ec8e828c5290d95ec4 Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Fri, 7 Feb 2014 14:29:13 +0400 Subject: [PATCH 03/16] energy: remove arch dependency code using swap_get_sarg() instead of get_arg0() Change-Id: I164aaf9e47035e90e2119b16e848d6ae8b023630 Signed-off-by: Vyacheslav Cherkashin --- energy/energy.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/energy/energy.c b/energy/energy.c index 73ab3b0..ada5ae3 100644 --- a/energy/energy.c +++ b/energy/energy.c @@ -344,17 +344,6 @@ static int check_file(int fd) return 0; } -static unsigned long get_arg0(struct pt_regs *regs) -{ -#if defined(CONFIG_ARM) - return regs->ARM_r0; -#elif defined(CONFIG_X86_32) - return regs->bx; -#else - #error "this architecture is not supported" -#endif /* CONFIG_arch */ -} - @@ -459,7 +448,7 @@ static int entry_handler_sys_read(struct kretprobe_instance *ri, { struct sys_read_data *srd = (struct sys_read_data *)ri->data; - srd->fd = (int)get_arg0(regs); + srd->fd = (int)swap_get_sarg(regs, 0); return 0; } @@ -506,7 +495,7 @@ static int entry_handler_sys_write(struct kretprobe_instance *ri, { struct sys_read_data *srd = (struct sys_read_data *)ri->data; - srd->fd = (int)get_arg0(regs); + srd->fd = (int)swap_get_sarg(regs, 0); return 0; } -- 2.7.4 From de4096d1fc0dd7781d5d325d127ba655f4a03538 Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Fri, 11 Nov 2016 20:43:57 +0300 Subject: [PATCH 04/16] loader: Remove unused code Change-Id: Ic71e40a93e64ac98e27c46af1a6266265061fd9f Signed-off-by: Vyacheslav Cherkashin --- loader/loader_module.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/loader/loader_module.c b/loader/loader_module.c index e5efd43..54d8948 100644 --- a/loader/loader_module.c +++ b/loader/loader_module.c @@ -119,18 +119,6 @@ static inline void __prepare_ujump(struct uretprobe_instance *ri, swap_set_instr_ptr(regs, vaddr); } -static inline int __push(struct pt_regs *regs, void *buf, size_t len) -{ - unsigned long sp = swap_get_stack_ptr(regs) - len; - - sp = PTR_ALIGN(sp, sizeof(unsigned long)); - if (copy_to_user((void __user *)sp, buf, len)) - return -EIO; - swap_set_stack_ptr(regs, sp); - - return 0; -} - static inline void __save_uregs(struct uretprobe_instance *ri, struct pt_regs *regs) { -- 2.7.4 From 670a18517e5594822dc1d8d419a01f6424beb829 Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Fri, 11 Nov 2016 20:53:46 +0300 Subject: [PATCH 05/16] loader: Replace outdated interface Change functions: swap_get_arg() --> swap_get_uarg() swap_set_arg() --> swap_put_uarg() Change-Id: Ia667b2d56fdfd730a4c864482aced52f7d5579f4 Signed-off-by: Vyacheslav Cherkashin --- loader/loader_module.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/loader/loader_module.c b/loader/loader_module.c index 54d8948..8ee703e 100644 --- a/loader/loader_module.c +++ b/loader/loader_module.c @@ -125,8 +125,8 @@ static inline void __save_uregs(struct uretprobe_instance *ri, struct us_priv *priv = (struct us_priv *)ri->data; memcpy(ri->data, regs, sizeof(*regs)); - priv->arg0 = swap_get_arg(regs, 0); - priv->arg1 = swap_get_arg(regs, 1); + priv->arg0 = swap_get_uarg(regs, 0); + priv->arg1 = swap_get_uarg(regs, 1); priv->raddr = swap_get_ret_addr(regs); } @@ -136,8 +136,8 @@ static inline void __restore_uregs(struct uretprobe_instance *ri, struct us_priv *priv = (struct us_priv *)ri->data; memcpy(regs, ri->data, sizeof(*regs)); - swap_set_arg(regs, 0, priv->arg0); - swap_set_arg(regs, 1, priv->arg1); + swap_put_uarg(regs, 0, priv->arg0); + swap_put_uarg(regs, 1, priv->arg1); swap_set_ret_addr(regs, priv->raddr); #ifndef CONFIG_ARM /* need to do it only on x86 */ @@ -174,7 +174,7 @@ static inline void print_regs(const char *prefix, struct pt_regs *regs, (char *)("NULL"), (int)lpd_get_state(hd), prefix, (unsigned long)ri->rp->up.addr, - regs->EREG(ip), swap_get_arg(regs, 0), swap_get_arg(regs, 1), + regs->EREG(ip), swap_get_uarg(regs, 0), swap_get_uarg(regs, 1), swap_get_ret_addr(regs)); #endif /* CONFIG_ARM */ } @@ -428,8 +428,8 @@ static unsigned long __not_loaded_entry(struct uretprobe_instance *ri, path = lpd_get_path(pd, hd); /* set dlopen args: filename, flags */ - swap_set_arg(regs, 0, (unsigned long)path/*swap_get_stack_ptr(regs)*/); - swap_set_arg(regs, 1, 2 /* RTLD_NOW */); + swap_put_uarg(regs, 0, (unsigned long)path); + swap_put_uarg(regs, 1, 2 /* RTLD_NOW */); /* do the jump to dlopen */ __prepare_ujump(ri, regs, vaddr); -- 2.7.4 From 5b105e38313443163c3f8eb427e3cb876e7ded4e Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Fri, 7 Feb 2014 15:25:29 +0400 Subject: [PATCH 06/16] ARM64 initial version use dummy for arch dependent functions Change-Id: Ib63a5efc58ec36f362c688213196e46416d41e6b Signed-off-by: Vyacheslav Cherkashin Signed-off-by: Alexander Aksenov --- build.sh | 4 +- energy/lcd/lcd_base.c | 6 +- energy/lcd/lcd_base.h | 4 + energy/lcd/maru.c | 2 +- energy/lcd/s6e8aa0.c | 4 +- energy/lcd/s6e8aa0_panel.c | 4 +- kprobe/Kbuild | 4 + kprobe/arch/arm64/swap-asm/swap_kprobes.c | 39 +++++++ kprobe/arch/arm64/swap-asm/swap_kprobes.h | 168 ++++++++++++++++++++++++++++++ kprobe/swap_kprobes.c | 4 +- ks_features/ks_features.c | 2 +- loader/loader_module.c | 15 ++- parser/msg_cmd.c | 12 +-- preload/preload_module.c | 4 +- uprobe/Kbuild | 4 + uprobe/arch/arm64/swap-asm/swap_uprobes.c | 35 +++++++ uprobe/arch/arm64/swap-asm/swap_uprobes.h | 145 ++++++++++++++++++++++++++ us_manager/helper.c | 2 + us_manager/pf/proc_filters.c | 11 +- us_manager/us_slot_manager.c | 1 + writer/kernel_operations.h | 12 ++- writer/swap_msg.c | 2 +- 22 files changed, 452 insertions(+), 32 deletions(-) create mode 100644 kprobe/arch/arm64/swap-asm/swap_kprobes.c create mode 100644 kprobe/arch/arm64/swap-asm/swap_kprobes.h create mode 100644 uprobe/arch/arm64/swap-asm/swap_uprobes.c create mode 100644 uprobe/arch/arm64/swap-asm/swap_uprobes.h diff --git a/build.sh b/build.sh index b974c24..935d317 100755 --- a/build.sh +++ b/build.sh @@ -11,7 +11,7 @@ show_usage_and_exit () { echo -e "\t--file " # echo -e "\t--debug" echo -e "\t--kernel " - echo -e "\t--arch " + echo -e "\t--arch " echo -e "\t[--toolchain ]" exit 1 } @@ -68,6 +68,8 @@ fi if [ "${ARCH}" = "arm" ] ; then LINKNAME="arm" +elif [ "${ARCH}" = "arm64" ] ; then + LINKNAME="arm64" elif [ "${ARCH}" = "i386" ] ; then LINKNAME="x86" else diff --git a/energy/lcd/lcd_base.c b/energy/lcd/lcd_base.c index 018fe7b..12ee466 100644 --- a/energy/lcd/lcd_base.c +++ b/energy/lcd/lcd_base.c @@ -99,7 +99,7 @@ static void *create_lcd_priv(struct lcd_ops *ops, size_t tms_brt_cnt) struct lcd_priv_data *lcd; if (tms_brt_cnt <= 0) { - printk(KERN_INFO "error variable tms_brt_cnt=%d\n", + printk(KERN_INFO "error variable tms_brt_cnt=%zu\n", tms_brt_cnt); return NULL; } @@ -247,10 +247,10 @@ static int func_notifier_lcd(struct lcd_ops *ops, enum lcd_action_type action, { switch (action) { case LAT_BRIGHTNESS: - set_brightness(ops, (int)data); + set_brightness(ops, VOIDP2INT(data)); break; case LAT_POWER: - set_power(ops, (int)data); + set_power(ops, VOIDP2INT(data)); break; default: printk(KERN_INFO "LCD energy error: action=%d\n", action); diff --git a/energy/lcd/lcd_base.h b/energy/lcd/lcd_base.h index 44de0b4..85d59c6 100644 --- a/energy/lcd/lcd_base.h +++ b/energy/lcd/lcd_base.h @@ -32,6 +32,10 @@ #include +#define VOIDP2INT(x) ((int)(unsigned long)(x)) +#define INT2VOIDP(x) ((void *)(unsigned long)(x)) + + /** Description of actions */ enum lcd_action_type { LAT_BRIGHTNESS, /**< LCD brightness */ diff --git a/energy/lcd/maru.c b/energy/lcd/maru.c index 7431b44..e6a9c2c 100644 --- a/energy/lcd/maru.c +++ b/energy/lcd/maru.c @@ -149,7 +149,7 @@ static int ret_handler_set_backlight(struct kretprobe_instance *ri, if (!ret && maru_ops.notifier) maru_ops.notifier(&maru_ops, LAT_BRIGHTNESS, - (void *)*brightness); + INT2VOIDP(*brightness)); return 0; } diff --git a/energy/lcd/s6e8aa0.c b/energy/lcd/s6e8aa0.c index b987bae..241003b 100644 --- a/energy/lcd/s6e8aa0.c +++ b/energy/lcd/s6e8aa0.c @@ -143,7 +143,7 @@ static int ret_handler_set_power(struct kretprobe_instance *ri, int *power = (int *)ri->data; if (!ret && s6e8aa0_ops.notifier) - s6e8aa0_ops.notifier(&s6e8aa0_ops, LAT_POWER, (void *)*power); + s6e8aa0_ops.notifier(&s6e8aa0_ops, LAT_POWER, INT2VOIDP(*power)); return 0; } @@ -173,7 +173,7 @@ static int ret_handler_set_backlight(struct kretprobe_instance *ri, if (!ret && s6e8aa0_ops.notifier) s6e8aa0_ops.notifier(&s6e8aa0_ops, LAT_BRIGHTNESS, - (void *)*brightness); + INT2VOIDP(*brightness)); return 0; } diff --git a/energy/lcd/s6e8aa0_panel.c b/energy/lcd/s6e8aa0_panel.c index 2119af1..e2cd8fa 100644 --- a/energy/lcd/s6e8aa0_panel.c +++ b/energy/lcd/s6e8aa0_panel.c @@ -142,7 +142,7 @@ static int ret_handler_set_power(struct kretprobe_instance *ri, if (!ret && s6e8aa0_ops.notifier) s6e8aa0_ops.notifier(&s6e8aa0_ops, LAT_POWER, - (void *)*power); + INT2VOIDP(*power)); return 0; } @@ -175,7 +175,7 @@ static int ret_handler_set_backlight(struct kretprobe_instance *ri, if (!ret && s6e8aa0_ops.notifier) s6e8aa0_ops.notifier(&s6e8aa0_ops, LAT_BRIGHTNESS, - (void *)*brightness); + INT2VOIDP(*brightness)); return 0; } diff --git a/kprobe/Kbuild b/kprobe/Kbuild index 09e7c43..489db75 100644 --- a/kprobe/Kbuild +++ b/kprobe/Kbuild @@ -15,5 +15,9 @@ swap_kprobe-$(CONFIG_ARM) += arch/arm/swap-asm/memory_rwx.o endif #ifeq ($(CONFIG_STRICT_MEMORY_RWX), y) +### ARM64 +swap_kprobe-$(CONFIG_ARM64) += arch/arm64/swap-asm/swap_kprobes.o + + ### X86 swap_kprobe-$(CONFIG_X86) += arch/x86/swap-asm/swap_kprobes.o diff --git a/kprobe/arch/arm64/swap-asm/swap_kprobes.c b/kprobe/arch/arm64/swap-asm/swap_kprobes.c new file mode 100644 index 0000000..bfc29e1 --- /dev/null +++ b/kprobe/arch/arm64/swap-asm/swap_kprobes.c @@ -0,0 +1,39 @@ +/* + * 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, 2014 + * + * 2014 Vyacheslav Cherkashin + * + */ + + +#include + + +struct kprobe; +struct pt_regs; + + +int swap_arch_init_kprobes(void) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +void swap_arch_exit_kprobes(void) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ +} diff --git a/kprobe/arch/arm64/swap-asm/swap_kprobes.h b/kprobe/arch/arm64/swap-asm/swap_kprobes.h new file mode 100644 index 0000000..e738a89 --- /dev/null +++ b/kprobe/arch/arm64/swap-asm/swap_kprobes.h @@ -0,0 +1,168 @@ +#ifndef _ASM_ARM64_KPROBES_H +#define _ASM_ARM64_KPROBES_H + +/* + * 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, 2014 + * + * 2014 Vyacheslav Cherkashin + * + */ + + +#include + + +#define BREAKPOINT_INSTRUCTION 0 /* FIXME: to implement */ +#define KPROBES_TRAMP_LEN 10 /* FIXME: to implement */ + + +typedef u32 kprobe_opcode_t; + +struct arch_specific_insn { /* FIXME: to implement */ + /* copy of the original instruction */ + kprobe_opcode_t *insn; +}; + +/* per-cpu kprobe control block */ +struct kp_core_ctlblk { /* FIXME: to implement */ + unsigned long kp_core_status; +}; + + +struct kprobe; +struct kp_core; +struct slot_manager; +struct kretprobe_instance; + + +static inline unsigned long swap_get_karg(struct pt_regs *regs, + unsigned long n) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +static inline unsigned long swap_get_sarg(struct pt_regs *regs, + unsigned long n) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +static inline unsigned long swap_get_instr_ptr(struct pt_regs *regs) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0xdeadc0de; +} + +static inline void swap_set_instr_ptr(struct pt_regs *regs, unsigned long val) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ +} + +static inline unsigned long swap_get_ret_addr(struct pt_regs *regs) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0xdeadc0de; +} + +static inline void swap_set_ret_addr(struct pt_regs *regs, unsigned long val) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ +} + +static inline int arch_kp_core_prepare(struct kp_core *p, + struct slot_manager *sm) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +static inline void arch_kp_core_arm(struct kp_core *p) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ +} + +static inline void arch_kp_core_disarm(struct kp_core *p) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ +} + + +static inline void restore_previous_kp_core(struct kp_core_ctlblk *kcb) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ +} + +static inline void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ +} + +static inline void swap_kretprobe_trampoline(void) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ +} + + +static inline unsigned long arch_get_task_pc(struct task_struct *p) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0xdeadc0de; +} + +static inline void arch_set_task_pc(struct task_struct *p, unsigned long val) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ +} + +static inline int swap_setjmp_pre_handler(struct kprobe *p, + struct pt_regs *regs) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +/* jumper */ +typedef unsigned long (*jumper_cb_t)(void *); + +static inline unsigned long get_jump_addr(void) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0x87654321; +} + +static inline int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs, + jumper_cb_t cb, void *data, size_t size) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +static inline int arch_init_module_deps(void) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + + +int swap_arch_init_kprobes(void); +void swap_arch_exit_kprobes(void); + + +#endif /* _ASM_ARM64_KPROBES_H */ diff --git a/kprobe/swap_kprobes.c b/kprobe/swap_kprobes.c index 918d5a6..62538ff 100644 --- a/kprobe/swap_kprobes.c +++ b/kprobe/swap_kprobes.c @@ -1054,12 +1054,12 @@ static int swap_disarm_krp_inst(struct kretprobe_instance *ri) if (found) { printk(KERN_INFO "---> [%d] %s (%d/%d): tramp (%08lx) " - "found at %08lx (%08lx /%+d) - %08lx\n", + "found at %08lx (%08lx /%+ld) - %08lx\n", task_cpu(ri->task), ri->task->comm, ri->task->tgid, ri->task->pid, (long unsigned int)tramp, (long unsigned int)found, (long unsigned int)ri->sp, - found - ri->sp, ri->rp ? ri->rp->kp.addr : 0); + (unsigned long)(found - ri->sp), ri->rp ? ri->rp->kp.addr : 0); *found = (unsigned long)ri->ret_addr; retval = 0; } else { diff --git a/ks_features/ks_features.c b/ks_features/ks_features.c index a572b67..7957b60 100644 --- a/ks_features/ks_features.c +++ b/ks_features/ks_features.c @@ -571,7 +571,7 @@ static void print_feature(struct feature *f) size_t i; for (i = 0; i < f->cnt; ++i) - printk(KERN_INFO " feature[%3u]: %s\n", i, + printk(KERN_INFO " feature[%3zu]: %s\n", i, get_sys_name(f->feature_list[i])); } diff --git a/loader/loader_module.c b/loader/loader_module.c index 8ee703e..53c38c5 100644 --- a/loader/loader_module.c +++ b/loader/loader_module.c @@ -139,10 +139,10 @@ static inline void __restore_uregs(struct uretprobe_instance *ri, swap_put_uarg(regs, 0, priv->arg0); swap_put_uarg(regs, 1, priv->arg1); swap_set_ret_addr(regs, priv->raddr); -#ifndef CONFIG_ARM +#ifdef CONFIG_X86_32 /* need to do it only on x86 */ regs->EREG(ip) -= 1; -#endif /* !CONFIG_ARM */ +#endif /* CONFIG_X86_32 */ /* we have just restored the registers => no need to do it in * trampoline_uprobe_handler */ ri->ret_addr = NULL; @@ -153,7 +153,7 @@ static inline void print_regs(const char *prefix, struct pt_regs *regs, { struct dentry *dentry = lpd_get_dentry(hd); -#ifdef CONFIG_ARM +#if defined(CONFIG_ARM) printk(LOADER_PREFIX "%s[%d/%d] %s (%d) %s addr(%08lx), " "r0(%08lx), r1(%08lx), r2(%08lx), r3(%08lx), " "r4(%08lx), r5(%08lx), r6(%08lx), r7(%08lx), " @@ -166,7 +166,7 @@ static inline void print_regs(const char *prefix, struct pt_regs *regs, regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3, regs->ARM_r4, regs->ARM_r5, regs->ARM_r6, regs->ARM_r7, regs->ARM_sp, regs->ARM_lr, regs->ARM_pc); -#else /* !CONFIG_ARM */ +#elif defined(CONFIG_X86_32) printk(LOADER_PREFIX "%s[%d/%d] %s (%d) %s addr(%08lx), " "ip(%08lx), arg0(%08lx), arg1(%08lx), raddr(%08lx)\n", current->comm, current->tgid, current->pid, @@ -176,7 +176,12 @@ static inline void print_regs(const char *prefix, struct pt_regs *regs, prefix, (unsigned long)ri->rp->up.addr, regs->EREG(ip), swap_get_uarg(regs, 0), swap_get_uarg(regs, 1), swap_get_ret_addr(regs)); -#endif /* CONFIG_ARM */ +#elif defined(CONFIG_ARM64) + WARN(1, "not implemented"); /* FIXME: to implement */ + (void)dentry; +#else /* CONFIG_arch */ +# error "this architecture is not supported" +#endif /* CONFIG_arch */ } static inline unsigned long __get_r_debug_off(struct vm_area_struct *linker_vma) diff --git a/parser/msg_cmd.c b/parser/msg_cmd.c index b0ff1c5..9d032fd 100644 --- a/parser/msg_cmd.c +++ b/parser/msg_cmd.c @@ -61,7 +61,7 @@ static int set_config(struct conf_data *conf) int msg_keep_alive(struct msg_buf *mb) { if (!is_end_mb(mb)) { - print_err("to long message, remained=%u", remained_mb(mb)); + print_err("to long message, remained=%zu", remained_mb(mb)); return -EINVAL; } @@ -88,7 +88,7 @@ int msg_start(struct msg_buf *mb) return -EINVAL; if (!is_end_mb(mb)) { - print_err("to long message, remained=%u", remained_mb(mb)); + print_err("to long message, remained=%zu", remained_mb(mb)); ret = -EINVAL; goto free_us_inst; } @@ -128,7 +128,7 @@ int msg_stop(struct msg_buf *mb) int discarded; if (!is_end_mb(mb)) { - print_err("to long message, remained=%u", remained_mb(mb)); + print_err("to long message, remained=%zu", remained_mb(mb)); return -EINVAL; } @@ -170,7 +170,7 @@ int msg_config(struct msg_buf *mb) return -EINVAL; if (!is_end_mb(mb)) { - print_err("to long message, remained=%u", remained_mb(mb)); + print_err("to long message, remained=%zu", remained_mb(mb)); ret = -EINVAL; goto free_conf_data; } @@ -207,7 +207,7 @@ int msg_swap_inst_add(struct msg_buf *mb) return -EINVAL; if (!is_end_mb(mb)) { - print_err("to long message, remained=%u", remained_mb(mb)); + print_err("to long message, remained=%zu", remained_mb(mb)); ret = -EINVAL; goto free_us_inst; } @@ -251,7 +251,7 @@ int msg_swap_inst_remove(struct msg_buf *mb) return -EINVAL; if (!is_end_mb(mb)) { - print_err("to long message, remained=%u", remained_mb(mb)); + print_err("to long message, remained=%zu", remained_mb(mb)); ret = -EINVAL; goto free_us_inst; } diff --git a/preload/preload_module.c b/preload/preload_module.c index e0a80b7..779f755 100644 --- a/preload/preload_module.c +++ b/preload/preload_module.c @@ -333,14 +333,14 @@ static int write_msg_handler(struct uprobe *p, struct pt_regs *regs) buf = kmalloc(len, GFP_ATOMIC); if (buf == NULL) { - printk(PRELOAD_PREFIX "No mem for buffer! Size = %d\n", len); + printk(PRELOAD_PREFIX "No mem for buffer! Size = %zd\n", len); return 0; } ret = read_proc_vm_atomic(current, (unsigned long)user_buf, buf, len); if (ret < 0) { printk(PRELOAD_PREFIX "Cannot copy data from userspace! Size = " - "%d ptr 0x%lx ret %d\n", len, + "%zd ptr 0x%lx ret %d\n", len, (unsigned long)user_buf, ret); goto write_msg_fail; } diff --git a/uprobe/Kbuild b/uprobe/Kbuild index bed91d2..553aae1 100644 --- a/uprobe/Kbuild +++ b/uprobe/Kbuild @@ -12,6 +12,10 @@ swap_uprobe-$(CONFIG_ARM) += \ arch/arm/swap-asm/thumb_tramps.o +### ARM64 +swap_uprobe-$(CONFIG_ARM64) += arch/arm64/swap-asm/swap_uprobes.o + + ### X86 swap_uprobe-$(CONFIG_X86) += arch/x86/swap-asm/swap_uprobes.o \ arch/x86/swap-asm/swap_sc_patch.o diff --git a/uprobe/arch/arm64/swap-asm/swap_uprobes.c b/uprobe/arch/arm64/swap-asm/swap_uprobes.c new file mode 100644 index 0000000..6de4d31 --- /dev/null +++ b/uprobe/arch/arm64/swap-asm/swap_uprobes.c @@ -0,0 +1,35 @@ +/* + * 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, 2014 + * + * 2014 Vyacheslav Cherkashin + * + */ + + +#include + + +int swap_arch_init_uprobes(void) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +void swap_arch_exit_uprobes(void) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ +} diff --git a/uprobe/arch/arm64/swap-asm/swap_uprobes.h b/uprobe/arch/arm64/swap-asm/swap_uprobes.h new file mode 100644 index 0000000..0ca8347 --- /dev/null +++ b/uprobe/arch/arm64/swap-asm/swap_uprobes.h @@ -0,0 +1,145 @@ +#ifndef _ASM_ARM64_UPROBES_H +#define _ASM_ARM64_UPROBES_H + +/* + * 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, 2014 + * + * 2014 Vyacheslav Cherkashin + * + */ + + +#define UPROBES_TRAMP_LEN 1 /* FIXME: to implement */ + + +struct kprobe; +struct uprobe; +struct uretprobe; +struct uretprobe_instance; + + +typedef u32 uprobe_opcode_t; + +struct arch_insn { + uprobe_opcode_t *insn; /* FIXME: to implement */ +}; + + +static inline u32 swap_get_urp_float(struct pt_regs *regs) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +static inline u64 swap_get_urp_double(struct pt_regs *regs) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +static inline unsigned long swap_get_uarg(struct pt_regs *regs, unsigned long n) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0x12345678; +} + +static inline void swap_put_uarg(struct pt_regs *regs, unsigned long n, + unsigned long val) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ +} + +static inline int arch_prepare_uprobe(struct uprobe *up) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +static inline void arch_remove_uprobe(struct uprobe *up) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ +} + +static inline int setjmp_upre_handler(struct uprobe *p, struct pt_regs *regs) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +static inline int longjmp_break_uhandler(struct uprobe *p, + struct pt_regs *regs) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +static inline int arch_arm_uprobe(struct uprobe *p) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +static inline void arch_disarm_uprobe(struct uprobe *p, + struct task_struct *task) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ +} + + +static inline unsigned long arch_get_trampoline_addr(struct uprobe *p, + struct pt_regs *regs) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +static inline void arch_set_orig_ret_addr(unsigned long orig_ret_addr, + struct pt_regs *regs) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ +} + +static inline int arch_prepare_uretprobe(struct uretprobe_instance *ri, + struct pt_regs *regs) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +static inline int arch_opcode_analysis_uretprobe(struct uretprobe *rp) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +static inline int arch_disarm_urp_inst(struct uretprobe_instance *ri, + struct task_struct *task) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + +static inline void arch_ujprobe_return(void) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ +} + +int swap_arch_init_uprobes(void); +void swap_arch_exit_uprobes(void); + + +#endif /* _ASM_ARM64_UPROBES_H */ diff --git a/us_manager/helper.c b/us_manager/helper.c index 5923868..7b48ccc 100644 --- a/us_manager/helper.c +++ b/us_manager/helper.c @@ -64,6 +64,8 @@ static int entry_handler_pf(struct kretprobe_instance *ri, struct pt_regs *regs) data->addr = read_cr2(); data->pf_regs = (struct pt_regs *)swap_get_karg(regs, 0); data->save_pc = data->pf_regs->ip; +#elif defined(CONFIG_ARM64) + WARN(1, "not implemented"); /* FIXME: to implement */ #else #error "this architecture is not supported" #endif /* CONFIG_arch */ diff --git a/us_manager/pf/proc_filters.c b/us_manager/pf/proc_filters.c index 5806048..d3d5d98 100644 --- a/us_manager/pf/proc_filters.c +++ b/us_manager/pf/proc_filters.c @@ -31,6 +31,10 @@ #include "proc_filters.h" #include + +#define VOIDP2PID(x) ((pid_t)(unsigned long)(x)) +#define PID2VOIDP(x) ((void *)(unsigned long)(x)) + static int check_dentry(struct task_struct *task, struct dentry *dentry) { struct vm_area_struct *vma; @@ -66,7 +70,7 @@ static inline void free_by_dentry(struct proc_filter *self) static struct task_struct *call_by_tgid(struct proc_filter *self, struct task_struct *task) { - pid_t tgid = (pid_t)self->data; + pid_t tgid = VOIDP2PID(self->data); if (task->tgid == tgid) return task; @@ -134,7 +138,7 @@ void set_pf_by_dentry(struct proc_filter *pf, struct dentry *dentry, void *priv) void set_pf_by_tgid(struct proc_filter *pf, pid_t tgid, void *priv) { pf->call = &call_by_tgid; - pf->data = (void *)tgid; + pf->data = PID2VOIDP(tgid); pf->priv = priv; } @@ -221,7 +225,8 @@ int check_pf_by_dentry(struct proc_filter *filter, struct dentry *dentry) */ int check_pf_by_tgid(struct proc_filter *filter, pid_t tgid) { - return filter->data == (void *)tgid && filter->call == &call_by_tgid; + return filter->data == PID2VOIDP(tgid) + && filter->call == &call_by_tgid; } /** diff --git a/us_manager/us_slot_manager.c b/us_manager/us_slot_manager.c index a48972f..56e8f28 100644 --- a/us_manager/us_slot_manager.c +++ b/us_manager/us_slot_manager.c @@ -32,6 +32,7 @@ #include #include +#include #include "us_manager_common.h" diff --git a/writer/kernel_operations.h b/writer/kernel_operations.h index f7f312b..6fa27cc 100644 --- a/writer/kernel_operations.h +++ b/writer/kernel_operations.h @@ -43,14 +43,12 @@ /* Regs manipulations */ #if defined(CONFIG_ARM) -#define get_regs_ip(regs) (regs->ARM_pc) /**< Get pc reg. */ #define get_regs_ret_func(regs) (regs->ARM_lr) /**< Get lr reg. */ #define get_regs_ret_val(regs) (regs->ARM_r0) /**< Get ret val. */ #define get_regs_stack_ptr(regs) (regs->ARM_sp) /**< Get stack pointer. */ #elif defined(CONFIG_X86_32) -#define get_regs_ip(regs) (regs->ip - 1) /**< Get ip. */ #define get_regs_ret_val(regs) (regs->ax) /**< Get ret val. */ #define get_regs_stack_ptr(regs) (regs->sp) /**< Get stack pointer. */ @@ -62,7 +60,7 @@ static inline u32 get_regs_ret_func(struct pt_regs *regs) sp = (u32 *)regs->sp; if (get_user(addr, sp)) pr_info("failed to dereference a pointer, sp=%p, " - "pc=%lx\n", sp, get_regs_ip(regs)); + "pc=%lx\n", sp, regs->ip - 1); } else { sp = (u32 *)kernel_stack_pointer(regs); addr = *sp; @@ -71,6 +69,14 @@ static inline u32 get_regs_ret_func(struct pt_regs *regs) return addr; } +#elif defined(CONFIG_ARM64) + +static inline u64 get_regs_ret_func(struct pt_regs *regs) +{ + WARN(1, "not implemented"); /* FIXME: to implement */ + return 0; +} + #endif /* CONFIG_arch */ #endif /* __KERNEL_OPERATIONS_H__ */ diff --git a/writer/swap_msg.c b/writer/swap_msg.c index fcd0a82..81b3f0d 100644 --- a/writer/swap_msg.c +++ b/writer/swap_msg.c @@ -486,7 +486,7 @@ int swap_msg_raw(void *data, size_t size) struct swap_msg *m = (struct swap_msg *)data; if (sizeof(*m) > size) { - pr_err(MSG_PREFIX "ERROR: message RAW small size=%u\n", size); + pr_err(MSG_PREFIX "ERROR: message RAW small size=%zu\n", size); return -EINVAL; } -- 2.7.4 From 1c7192082e7f7abe829096b4c70a8986b9a05f33 Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Thu, 20 Feb 2014 17:21:49 +0400 Subject: [PATCH 07/16] ARM64: implement brk hook Create interface for using software breakpoint Change-Id: Ic080dc6f79746c9cadacd336facdef49557aaa01 Signed-off-by: Vyacheslav Cherkashin --- kprobe/Kbuild | 3 +- kprobe/arch/arm64/swap-asm/dbg_interface.c | 137 +++++++++++++++++++++++++++++ kprobe/arch/arm64/swap-asm/dbg_interface.h | 64 ++++++++++++++ 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 kprobe/arch/arm64/swap-asm/dbg_interface.c create mode 100644 kprobe/arch/arm64/swap-asm/dbg_interface.h diff --git a/kprobe/Kbuild b/kprobe/Kbuild index 489db75..2501018 100644 --- a/kprobe/Kbuild +++ b/kprobe/Kbuild @@ -16,7 +16,8 @@ endif #ifeq ($(CONFIG_STRICT_MEMORY_RWX), y) ### ARM64 -swap_kprobe-$(CONFIG_ARM64) += arch/arm64/swap-asm/swap_kprobes.o +swap_kprobe-$(CONFIG_ARM64) += arch/arm64/swap-asm/swap_kprobes.o \ + arch/arm64/swap-asm/dbg_interface.o ### X86 diff --git a/kprobe/arch/arm64/swap-asm/dbg_interface.c b/kprobe/arch/arm64/swap-asm/dbg_interface.c new file mode 100644 index 0000000..438b7b8 --- /dev/null +++ b/kprobe/arch/arm64/swap-asm/dbg_interface.c @@ -0,0 +1,137 @@ +/* + * 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, 2014 + * + * 2014 Vyacheslav Cherkashin + * + */ + + +#include +#include +#include +#include +#include "dbg_interface.h" + + + + +/* ============================================================================ + * = BRK IMPLEMENT = + * ============================================================================ + */ +static LIST_HEAD(brk_list); +static DEFINE_RWLOCK(brk_list_lock); + +void dbg_brk_hook_reg(struct brk_hook *hook) +{ + write_lock(&brk_list_lock); + list_add(&hook->list, &brk_list); + write_unlock(&brk_list_lock); +} +EXPORT_SYMBOL_GPL(dbg_brk_hook_reg); + +void dbg_brk_hook_unreg(struct brk_hook *hook) +{ + write_lock(&brk_list_lock); + list_del(&hook->list); + write_unlock(&brk_list_lock); +} +EXPORT_SYMBOL_GPL(dbg_brk_hook_unreg); + +static enum dbg_code call_brk_hook(struct pt_regs *regs, unsigned int esr) +{ + struct brk_hook *hook; + enum dbg_code (*fn)(struct pt_regs *regs, unsigned int esr) = NULL; + + read_lock(&brk_list_lock); + list_for_each_entry(hook, &brk_list, list) + if (((esr & hook->esr_mask) == hook->esr_val) && + ((regs->pstate & hook->spsr_mask) == hook->spsr_val)) + fn = hook->fn; + read_unlock(&brk_list_lock); + + return fn ? fn(regs, esr) : DBG_ERROR; +} + + +typedef int (*dbg_fn_t)(unsigned long addr, unsigned int esr, + struct pt_regs *regs); + +static dbg_fn_t *brk_handler_ptr; +static dbg_fn_t orig_brk_handler; + +static int brk_handler(unsigned long addr, unsigned int esr, + struct pt_regs *regs) +{ + /* call the registered breakpoint handler */ + if (call_brk_hook(regs, esr) == DBG_HANDLED) + return 0; + + return orig_brk_handler(addr, esr, regs); +} + +static void init_brk(dbg_fn_t *fn) +{ + brk_handler_ptr = fn; + orig_brk_handler = *brk_handler_ptr; + *brk_handler_ptr = brk_handler; +} + +static void uninit_brk(void) +{ + *brk_handler_ptr = orig_brk_handler; +} + + + + + + +/* ============================================================================ + * = INIT / EXIT = + * ============================================================================ + */ +int dbg_iface_init(void) +{ + struct fault_info { + int (*fn)(unsigned long addr, unsigned int esr, + struct pt_regs *regs); + int sig; + int code; + const char *name; + }; + + struct fault_info *debug_finfo; + struct fault_info *finfo_brk; + + debug_finfo = (struct fault_info *)swap_ksyms("debug_fault_info"); + if (debug_finfo == NULL) { + pr_err("cannot found 'debug_fault_info'\n"); + return -EINVAL; + } + + finfo_brk = &debug_finfo[DBG_ESR_EVT_BRK]; + + init_brk(&finfo_brk->fn); + + return 0; +} + +void dbg_iface_uninit(void) +{ + uninit_brk(); +} diff --git a/kprobe/arch/arm64/swap-asm/dbg_interface.h b/kprobe/arch/arm64/swap-asm/dbg_interface.h new file mode 100644 index 0000000..185d5ae --- /dev/null +++ b/kprobe/arch/arm64/swap-asm/dbg_interface.h @@ -0,0 +1,64 @@ +#ifndef _ASM_DBG_INTERFACE_H +#define _ASM_DBG_INTERFACE_H + +/* + * 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, 2014 + * + * 2014 Vyacheslav Cherkashin + * + */ + + +#include +#include + + +#define ESR_ELx_IL (1 << 25) +#define ESR_ELx_EC_BRK 0xf0000000 +#define ESR_ELx_EC_MASK 0xfc000000 +#define BRK_COMM_MASK 0x0000ffff + +#define DBG_BRK_ESR_MASK (ESR_ELx_EC_MASK | ESR_ELx_IL | BRK_COMM_MASK) +#define DBG_BRK_ESR(x) (ESR_ELx_EC_BRK | ESR_ELx_IL | (BRK_COMM_MASK & (x))) + +#define MAKE_BRK(v) ((((v) & 0xffff) << 5) | 0xd4200000) + + +enum dbg_code { + DBG_HANDLED, + DBG_ERROR, +}; + + +struct brk_hook { + struct list_head list; + u32 spsr_mask; + u32 spsr_val; + u32 esr_mask; + u32 esr_val; + enum dbg_code (*fn)(struct pt_regs *regs, unsigned int esr); +}; + + +void dbg_brk_hook_reg(struct brk_hook *hook); +void dbg_brk_hook_unreg(struct brk_hook *hook); + +int dbg_iface_init(void); +void dbg_iface_uninit(void); + + +#endif /* _ASM_DBG_INTERFACE_H */ -- 2.7.4 From be2d5007aef70f27e1b540329bcb9fdc3460ae66 Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Wed, 12 Feb 2014 15:19:50 +0400 Subject: [PATCH 08/16] ARM64: implement kprobe implementation is taken from Linaro kernel Change-Id: I47a33512920956fbb2e9653c1d27c0bc6c81035a Signed-off-by: Vyacheslav Cherkashin Signed-off-by: Alexander Aksenov --- kprobe/Kbuild | 5 +- kprobe/arch/arm64/swap-asm/condn-helpers.c | 126 +++++++++ kprobe/arch/arm64/swap-asm/kprobes-arm64.c | 308 +++++++++++++++++++++ kprobe/arch/arm64/swap-asm/kprobes-arm64.h | 32 +++ kprobe/arch/arm64/swap-asm/probes-decode.h | 112 ++++++++ kprobe/arch/arm64/swap-asm/simulate-insn.c | 164 +++++++++++ kprobe/arch/arm64/swap-asm/simulate-insn.h | 35 +++ kprobe/arch/arm64/swap-asm/swap_kprobes.c | 419 ++++++++++++++++++++++++++++- kprobe/arch/arm64/swap-asm/swap_kprobes.h | 93 ++++--- 9 files changed, 1247 insertions(+), 47 deletions(-) create mode 100644 kprobe/arch/arm64/swap-asm/condn-helpers.c create mode 100644 kprobe/arch/arm64/swap-asm/kprobes-arm64.c create mode 100644 kprobe/arch/arm64/swap-asm/kprobes-arm64.h create mode 100644 kprobe/arch/arm64/swap-asm/probes-decode.h create mode 100644 kprobe/arch/arm64/swap-asm/simulate-insn.c create mode 100644 kprobe/arch/arm64/swap-asm/simulate-insn.h diff --git a/kprobe/Kbuild b/kprobe/Kbuild index 2501018..9403c42 100644 --- a/kprobe/Kbuild +++ b/kprobe/Kbuild @@ -17,7 +17,10 @@ endif #ifeq ($(CONFIG_STRICT_MEMORY_RWX), y) ### ARM64 swap_kprobe-$(CONFIG_ARM64) += arch/arm64/swap-asm/swap_kprobes.o \ - arch/arm64/swap-asm/dbg_interface.o + arch/arm64/swap-asm/dbg_interface.o \ + arch/arm64/swap-asm/kprobes-arm64.o \ + arch/arm64/swap-asm/condn-helpers.o \ + arch/arm64/swap-asm/simulate-insn.o ### X86 diff --git a/kprobe/arch/arm64/swap-asm/condn-helpers.c b/kprobe/arch/arm64/swap-asm/condn-helpers.c new file mode 100644 index 0000000..c8561cc --- /dev/null +++ b/kprobe/arch/arm64/swap-asm/condn-helpers.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) Samsung Electronics, 2014 + * + * Copied from: arch/arm64/kernel/condn-helpers.c + * + * Copyright (C) 2013 Linaro Limited + * + * Copied from: arch/arm/kernel/kprobes-common.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * Description: + * + * AArch64 and AArch32 shares same conditional(CNZV) flags encoding. + * This file implements conditional check helpers compatible with + * both AArch64 and AArch32 modes. Uprobes on v8 can handle both 32-bit + * & 64-bit user-space instructions, so we abstract the common functions + * in this file. While AArch64 and AArch32 specific instruction handling + * are implemented in separate files, this file contains common bits. + */ + + +#include +#include "swap_kprobes.h" + + +static unsigned long __check_eq(unsigned long pstate) +{ + return pstate & PSR_Z_BIT; +} + +static unsigned long __check_ne(unsigned long pstate) +{ + return (~pstate) & PSR_Z_BIT; +} + +static unsigned long __check_cs(unsigned long pstate) +{ + return pstate & PSR_C_BIT; +} + +static unsigned long __check_cc(unsigned long pstate) +{ + return (~pstate) & PSR_C_BIT; +} + +static unsigned long __check_mi(unsigned long pstate) +{ + return pstate & PSR_N_BIT; +} + +static unsigned long __check_pl(unsigned long pstate) +{ + return (~pstate) & PSR_N_BIT; +} + +static unsigned long __check_vs(unsigned long pstate) +{ + return pstate & PSR_V_BIT; +} + +static unsigned long __check_vc(unsigned long pstate) +{ + return (~pstate) & PSR_V_BIT; +} + +static unsigned long __check_hi(unsigned long pstate) +{ + pstate &= ~(pstate >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */ + return pstate & PSR_C_BIT; +} + +static unsigned long __check_ls(unsigned long pstate) +{ + pstate &= ~(pstate >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */ + return (~pstate) & PSR_C_BIT; +} + +static unsigned long __check_ge(unsigned long pstate) +{ + pstate ^= (pstate << 3); /* PSR_N_BIT ^= PSR_V_BIT */ + return (~pstate) & PSR_N_BIT; +} + +static unsigned long __check_lt(unsigned long pstate) +{ + pstate ^= (pstate << 3); /* PSR_N_BIT ^= PSR_V_BIT */ + return pstate & PSR_N_BIT; +} + +static unsigned long __check_gt(unsigned long pstate) +{ + /*PSR_N_BIT ^= PSR_V_BIT */ + unsigned long temp = pstate ^ (pstate << 3); + + temp |= (pstate << 1); /*PSR_N_BIT |= PSR_Z_BIT */ + return (~temp) & PSR_N_BIT; +} + +static unsigned long __check_le(unsigned long pstate) +{ + /*PSR_N_BIT ^= PSR_V_BIT */ + unsigned long temp = pstate ^ (pstate << 3); + + temp |= (pstate << 1); /*PSR_N_BIT |= PSR_Z_BIT */ + return temp & PSR_N_BIT; +} + +static unsigned long __check_al(unsigned long pstate) +{ + return true; +} + +kp_core_pstate_check_t * const kp_core_condition_checks[16] = { + &__check_eq, &__check_ne, &__check_cs, &__check_cc, + &__check_mi, &__check_pl, &__check_vs, &__check_vc, + &__check_hi, &__check_ls, &__check_ge, &__check_lt, + &__check_gt, &__check_le, &__check_al, &__check_al +}; diff --git a/kprobe/arch/arm64/swap-asm/kprobes-arm64.c b/kprobe/arch/arm64/swap-asm/kprobes-arm64.c new file mode 100644 index 0000000..d42ffac --- /dev/null +++ b/kprobe/arch/arm64/swap-asm/kprobes-arm64.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) Samsung Electronics, 2014 + * + * Copied from: arch/arm64/kernel/kprobes-arm64.c + * + * Copyright (C) 2013 Linaro Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#include +#include +#include +#include + +#include "probes-decode.h" +#include "kprobes-arm64.h" +#include "simulate-insn.h" + + +/* + * condition check functions for kp_cores simulation + */ +static unsigned long __check_pstate(struct kp_core *p, struct pt_regs *regs) +{ + struct arch_specific_insn *asi = &p->ainsn; + unsigned long pstate = regs->pstate & 0xffffffff; + + return asi->pstate_cc(pstate); +} + +static unsigned long __check_cbz(struct kp_core *p, struct pt_regs *regs) +{ + return check_cbz((u32)p->opcode, regs); +} + +static unsigned long __check_cbnz(struct kp_core *p, struct pt_regs *regs) +{ + return check_cbnz((u32)p->opcode, regs); +} + +static unsigned long __check_tbz(struct kp_core *p, struct pt_regs *regs) +{ + return check_tbz((u32)p->opcode, regs); +} + +static unsigned long __check_tbnz(struct kp_core *p, struct pt_regs *regs) +{ + return check_tbnz((u32)p->opcode, regs); +} + +/* + * prepare functions for instruction simulation + */ +static void prepare_none(struct kp_core *p, struct arch_specific_insn *asi) +{ +} + +static void prepare_bcond(struct kp_core *p, struct arch_specific_insn *asi) +{ + kprobe_opcode_t insn = p->opcode; + + asi->check_condn = __check_pstate; + asi->pstate_cc = kp_core_condition_checks[insn & 0xf]; +} + +static void prepare_cbz_cbnz(struct kp_core *p, struct arch_specific_insn *asi) +{ + kprobe_opcode_t insn = p->opcode; + + asi->check_condn = (insn & (1 << 24)) ? __check_cbnz : __check_cbz; +} + +static void prepare_tbz_tbnz(struct kp_core *p, struct arch_specific_insn *asi) +{ + kprobe_opcode_t insn = p->opcode; + + asi->check_condn = (insn & (1 << 24)) ? __check_tbnz : __check_tbz; +} + + +/* Load literal (PC-relative) instructions + * Encoding: xx01 1x00 xxxx xxxx xxxx xxxx xxxx xxxx + * + * opcode[26]: V=0, Load GP registers, simulate them. + * Encoding: xx01 1000 xxxx xxxx xxxx xxxx xxxx xxxx + * opcode[31:30]: op = 00, 01 - LDR literal + * opcode[31:30]: op = 10, - LDRSW literal + * + * 1. V=1 -Load FP/AdvSIMD registers + * Encoding: xx01 1100 xxxx xxxx xxxx xxxx xxxx xxxx + * 2. V=0,opc=11 -PRFM(Prefetch literal) + * Encoding: 1101 1000 xxxx xxxx xxxx xxxx xxxx xxxx + * Reject FP/AdvSIMD literal load & PRFM literal. + */ +static const struct aarch64_decode_item load_literal_subtable[] = { + DECODE_REJECT(0x1C000000, 0x3F000000), + DECODE_REJECT(0xD8000000, 0xFF000000), + DECODE_LITERAL(0x18000000, 0xBF000000, prepare_none, + simulate_ldr_literal), + DECODE_LITERAL(0x98000000, 0xFF000000, prepare_none, + simulate_ldrsw_literal), + DECODE_END, +}; + +/* AArch64 instruction decode table for kp_cores: + * The instruction will fall into one of the 3 groups: + * 1. Single stepped out-of-the-line slot. + * -Most instructions fall in this group, those does not + * depend on PC address. + * + * 2. Should be simulated because of PC-relative/literal access. + * -All branching and PC-relative insrtcutions are simulated + * in C code, making use of saved pt_regs + * Catch: SIMD/NEON register context are not saved while + * entering debug exception, so are rejected for now. + * + * 3. Cannot be probed(not safe) so are rejected. + * - Exception generation and exception return instructions + * - Exclusive monitor(LDREX/STREX family) + * + */ +static const struct aarch64_decode_item aarch64_decode_table[] = { + /* + * Data processing - PC relative(literal) addressing: + * Encoding: xxx1 0000 xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_LITERAL(0x10000000, 0x1F000000, prepare_none, + simulate_adr_adrp), + + /* + * Data processing - Add/Substract Immediate: + * Encoding: xxx1 0001 xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_SINGLESTEP(0x11000000, 0x1F000000), + + /* + * Data processing + * Encoding: + * xxx1 0010 0xxx xxxx xxxx xxxx xxxx xxxx (Logical) + * xxx1 0010 1xxx xxxx xxxx xxxx xxxx xxxx (Move wide) + * xxx1 0011 0xxx xxxx xxxx xxxx xxxx xxxx (Bitfield) + * xxx1 0011 1xxx xxxx xxxx xxxx xxxx xxxx (Extract) + */ + DECODE_SINGLESTEP(0x12000000, 0x1E000000), + + /* + * Data processing - SIMD/FP/AdvSIMD/Crypto-AES/SHA + * Encoding: xxx0 111x xxxx xxxx xxxx xxxx xxxx xxxx + * Encoding: xxx1 111x xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_SINGLESTEP(0x0E000000, 0x0E000000), + + /* + * Data processing - Register + * Encoding: xxxx 101x xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_SINGLESTEP(0x0A000000, 0x0E000000), + + /* Branching Instructions + * + * Encoding: + * x001 01xx xxxx xxxx xxxx xxxx xxxx xxxx (uncondtional Branch) + * x011 010x xxxx xxxx xxxx xxxx xxxx xxxx (compare & branch) + * x011 011x xxxx xxxx xxxx xxxx xxxx xxxx (Test & Branch) + * 0101 010x xxxx xxxx xxxx xxxx xxxx xxxx (Conditional, immediate) + * 1101 011x xxxx xxxx xxxx xxxx xxxx xxxx (Unconditional,register) + */ + DECODE_BRANCH(0x14000000, 0x7C000000, prepare_none, + simulate_b_bl), + DECODE_BRANCH(0x34000000, 0x7E000000, prepare_cbz_cbnz, + simulate_cbz_cbnz), + DECODE_BRANCH(0x36000000, 0x7E000000, prepare_tbz_tbnz, + simulate_tbz_tbnz), + DECODE_BRANCH(0x54000000, 0xFE000000, prepare_bcond, + simulate_b_cond), + DECODE_BRANCH(0xD6000000, 0xFE000000, prepare_none, + simulate_br_blr_ret), + + /* System insn: + * Encoding: 1101 0101 00xx xxxx xxxx xxxx xxxx xxxx + * + * Note: MSR immediate (update PSTATE daif) is not safe handling + * within kp_cores, rejected. + * + * Don't re-arrange these decode table entries. + */ + DECODE_REJECT(0xD500401F, 0xFFF8F01F), + DECODE_SINGLESTEP(0xD5000000, 0xFFC00000), + + /* Exception Generation: + * Encoding: 1101 0100 xxxx xxxx xxxx xxxx xxxx xxxx + * Instructions: SVC, HVC, SMC, BRK, HLT, DCPS1, DCPS2, DCPS3 + */ + DECODE_REJECT(0xD4000000, 0xFF000000), + + /* + * Load/Store - Exclusive monitor + * Encoding: xx00 1000 xxxx xxxx xxxx xxxx xxxx xxxx + * + * Reject exlusive monitor'ed instructions + */ + DECODE_REJECT(0x08000000, 0x3F000000), + + /* + * Load/Store - PC relative(literal): + * Encoding: xx01 1x00 xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE(0x18000000, 0x3B000000, load_literal_subtable), + + /* + * Load/Store - Register Pair + * Encoding: + * xx10 1x00 0xxx xxxx xxxx xxxx xxxx xxxx + * xx10 1x00 1xxx xxxx xxxx xxxx xxxx xxxx + * xx10 1x01 0xxx xxxx xxxx xxxx xxxx xxxx + * xx10 1x01 1xxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_SINGLESTEP(0x28000000, 0x3A000000), + + /* + * Load/Store - Register + * Encoding: + * xx11 1x00 xx0x xxxx xxxx 00xx xxxx xxxx (unscaled imm) + * xx11 1x00 xx0x xxxx xxxx 01xx xxxx xxxx (imm post-indexed) + * xx11 1x00 xx0x xxxx xxxx 10xx xxxx xxxx (unpriviledged) + * xx11 1x00 xx0x xxxx xxxx 11xx xxxx xxxx (imm pre-indexed) + * + * xx11 1x00 xx10 xxxx xxxx xx10 xxxx xxxx (register offset) + * + * xx11 1x01 xxxx xxxx xxxx xxxx xxxx xxxx (unsigned imm) + */ + DECODE_SINGLESTEP(0x38000000, 0x3B200000), + DECODE_SINGLESTEP(0x38200200, 0x38300300), + DECODE_SINGLESTEP(0x39000000, 0x3B000000), + + /* + * Load/Store - AdvSIMD + * Encoding: + * 0x00 1100 0x00 0000 xxxx xxxx xxxx xxxx (Multiple-structure) + * 0x00 1100 1x0x xxxx xxxx xxxx xxxx xxxx (Multi-struct post-indexed) + * 0x00 1101 0xx0 0000 xxxx xxxx xxxx xxxx (Single-structure)) + * 0x00 1101 1xxx xxxx xxxx xxxx xxxx xxxx (Single-struct post-index) + */ + DECODE_SINGLESTEP(0x0C000000, 0xBFBF0000), + DECODE_SINGLESTEP(0x0C800000, 0xBFA00000), + DECODE_SINGLESTEP(0x0D000000, 0xBF9F0000), + DECODE_SINGLESTEP(0x0D800000, 0xBF800000), + + /* Unallocated: xxx0 0xxx xxxx xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT(0x00000000, 0x18000000), + DECODE_END, +}; + +static int kp_core_decode_insn(kprobe_opcode_t insn, + struct arch_specific_insn *asi, + const struct aarch64_decode_item *tbl) +{ + unsigned int entry, ret = INSN_REJECTED; + + for (entry = 0; !decode_table_end(tbl[entry]); entry++) { + if (decode_table_hit(tbl[entry], insn)) + break; + } + + switch (decode_get_type(tbl[entry])) { + case DECODE_TYPE_END: + case DECODE_TYPE_REJECT: + default: + ret = INSN_REJECTED; + break; + + case DECODE_TYPE_SINGLESTEP: + ret = INSN_GOOD; + break; + + case DECODE_TYPE_SIMULATE: + asi->prepare = decode_prepare_fn(tbl[entry]); + asi->handler = decode_handler_fn(tbl[entry]); + ret = INSN_GOOD_NO_SLOT; + break; + + case DECODE_TYPE_TABLE: + /* recurse with next level decode table */ + ret = kp_core_decode_insn(insn, asi, + decode_sub_table(tbl[entry])); + }; + + return ret; +} + +/* Return: + * INSN_REJECTED If instruction is one not allowed to kprobe, + * INSN_GOOD If instruction is supported and uses instruction slot, + * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot. + */ +enum kp_core_insn arm_kp_core_decode_insn(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + return kp_core_decode_insn(insn, asi, aarch64_decode_table); +} diff --git a/kprobe/arch/arm64/swap-asm/kprobes-arm64.h b/kprobe/arch/arm64/swap-asm/kprobes-arm64.h new file mode 100644 index 0000000..410a9c0 --- /dev/null +++ b/kprobe/arch/arm64/swap-asm/kprobes-arm64.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) Samsung Electronics, 2014 + * + * Copied from: arch/arm64/kernel/kprobes-arm64.h + * + * Copyright (C) 2013 Linaro Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#ifndef _ARM_KERNEL_KPROBES_ARM64_H +#define _ARM_KERNEL_KPROBES_ARM64_H + +enum kp_core_insn { + INSN_REJECTED, + INSN_GOOD_NO_SLOT, + INSN_GOOD, +}; + +extern kp_core_pstate_check_t * const kp_core_condition_checks[16]; + +enum kp_core_insn arm_kp_core_decode_insn(kprobe_opcode_t insn, + struct arch_specific_insn *asi); + +#endif /* _ARM_KERNEL_KPROBES_ARM64_H */ diff --git a/kprobe/arch/arm64/swap-asm/probes-decode.h b/kprobe/arch/arm64/swap-asm/probes-decode.h new file mode 100644 index 0000000..0a210ef --- /dev/null +++ b/kprobe/arch/arm64/swap-asm/probes-decode.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) Samsung Electronics, 2014 + * + * Copied from: arch/arm64/kernel/probes-decode.h + * + * Copyright (C) 2013 Linaro Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#ifndef _ARM_KERNEL_PROBES_DECODE_H +#define _ARM_KERNEL_PROBES_DECODE_H + +/* + * The following definitions and macros are used to build instruction + * decoding tables. + */ +enum decode_type { + DECODE_TYPE_END, + DECODE_TYPE_SINGLESTEP, + DECODE_TYPE_SIMULATE, + DECODE_TYPE_TABLE, + DECODE_TYPE_REJECT, +}; + +struct aarch64_decode_item; + +struct aarch64_decode_header { + enum decode_type type; + u32 mask; + u32 val; +}; + +struct aarch64_decode_actions { + kp_core_prepare_t *prepare; + kp_core_handler_t *handler; +}; + +struct aarch64_decode_table { + const struct aarch64_decode_item *tbl; +}; + +union aarch64_decode_handler { + struct aarch64_decode_actions actions; + struct aarch64_decode_table table; +}; + +struct aarch64_decode_item { + struct aarch64_decode_header header; + union aarch64_decode_handler decode; +}; + +#define decode_get_type(_entry) ((_entry).header.type) + +#define decode_table_end(_entry) \ + ((_entry).header.type == DECODE_TYPE_END) + +#define decode_table_hit(_entry, insn) \ + ((insn & (_entry).header.mask) == (_entry).header.val) + +#define decode_prepare_fn(_entry) ((_entry).decode.actions.prepare) +#define decode_handler_fn(_entry) ((_entry).decode.actions.handler) +#define decode_sub_table(_entry) ((_entry).decode.table.tbl) + +#define DECODE_ADD_HEADER(_type, _val, _mask) \ + .header = { \ + .type = _type, \ + .mask = _mask, \ + .val = _val, \ + } + +#define DECODE_ADD_ACTION(_prepare, _handler) \ + .decode = { \ + .actions = { \ + .prepare = _prepare, \ + .handler = _handler, \ + } \ + } + +#define DECODE_ADD_TABLE(_table) \ + .decode = { \ + .table = {.tbl = _table} \ + } + +#define DECODE_REJECT(_v, _m) \ + { DECODE_ADD_HEADER(DECODE_TYPE_REJECT, _v, _m) } + +#define DECODE_SINGLESTEP(_v, _m) \ + { DECODE_ADD_HEADER(DECODE_TYPE_SINGLESTEP, _v, _m) } + +#define DECODE_SIMULATE(_v, _m, _p, _h) \ + { DECODE_ADD_HEADER(DECODE_TYPE_SIMULATE, _v, _m), \ + DECODE_ADD_ACTION(_p, _h) } + +#define DECODE_TABLE(_v, _m, _table) \ + { DECODE_ADD_HEADER(DECODE_TYPE_TABLE, _v, _m), \ + DECODE_ADD_TABLE(_table) } + +#define DECODE_LITERAL(_v, _m, _p, _h) DECODE_SIMULATE(_v, _m, _p, _h) +#define DECODE_BRANCH(_v, _m, _p, _h) DECODE_SIMULATE(_v, _m, _p, _h) + +/* should be the last element in decode structure */ +#define DECODE_END { .header = {.type = DECODE_TYPE_END, } } + +#endif /* _ARM_KERNEL_PROBES_DECODE_H */ diff --git a/kprobe/arch/arm64/swap-asm/simulate-insn.c b/kprobe/arch/arm64/swap-asm/simulate-insn.c new file mode 100644 index 0000000..0d79671 --- /dev/null +++ b/kprobe/arch/arm64/swap-asm/simulate-insn.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) Samsung Electronics, 2014 + * + * Copied from: arch/arm64/kernel/simulate-insn.c + * + * Copyright (C) 2013 Linaro Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#include +#include +#include +#include + +#include "simulate-insn.h" + +#define sign_extend(x, signbit) \ + ((x) | (0 - ((x) & ((long)1 << (signbit))))) + +#define bbl_displacement(insn) \ + sign_extend(((insn) & 0x3ffffff) << 2, 27) + +#define bcond_displacement(insn) \ + sign_extend(((insn >> 5) & 0xfffff) << 2, 21) + +#define cbz_displacement(insn) \ + sign_extend(((insn >> 5) & 0xfffff) << 2, 21) + +#define tbz_displacement(insn) \ + sign_extend(((insn >> 5) & 0x3fff) << 2, 15) + +#define ldr_displacement(insn) \ + sign_extend(((insn >> 5) & 0xfffff) << 2, 21) + + +unsigned long check_cbz(u32 opcode, struct pt_regs *regs) +{ + int xn = opcode & 0x1f; + + return (opcode & (1 << 31)) ? + !(regs->regs[xn]) : !(regs->regs[xn] & 0xffffffff); +} + +unsigned long check_cbnz(u32 opcode, struct pt_regs *regs) +{ + int xn = opcode & 0x1f; + + return (opcode & (1 << 31)) ? + (regs->regs[xn]) : (regs->regs[xn] & 0xffffffff); +} + +unsigned long check_tbz(u32 opcode, struct pt_regs *regs) +{ + int xn = opcode & 0x1f; + int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f); + + return ~((regs->regs[xn] >> bit_pos) & 0x1); +} + +unsigned long check_tbnz(u32 opcode, struct pt_regs *regs) +{ + int xn = opcode & 0x1f; + int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f); + + return (regs->regs[xn] >> bit_pos) & 0x1; +} + +/* + * instruction simulate functions + */ +void simulate_none(u32 opcode, long addr, struct pt_regs *regs) +{ +} + +void simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs) +{ + long res, imm, xn, shift; + + xn = opcode & 0x1f; + shift = (opcode >> 31) ? 12 : 0; /* check insn ADRP/ADR */ + imm = ((opcode >> 3) & 0xffffc) | ((opcode >> 29) & 0x3); + res = addr + 8 + (sign_extend(imm, 20) << shift); + + regs->regs[xn] = opcode & 0x80000000 ? res & 0xfffffffffffff000 : res; + regs->pc += 4; +} + +void simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs) +{ + int disp = bbl_displacement(opcode); + + /* Link register is x30 */ + if (opcode & (1 << 31)) + regs->regs[30] = addr + 4; + + regs->pc = addr + disp; +} + +void simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs) +{ + int disp = bcond_displacement(opcode); + + regs->pc = addr + disp; +} + +void simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs) +{ + int xn = (opcode >> 5) & 0x1f; + + /* Link register is x30 */ + if (((opcode >> 21) & 0x3) == 1) + regs->regs[30] = addr + 4; + + regs->pc = regs->regs[xn]; +} + +void simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs) +{ + int disp = cbz_displacement(opcode); + + regs->pc = addr + disp; +} + +void simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs) +{ + int disp = tbz_displacement(opcode); + + regs->pc = addr + disp; +} + +void simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs) +{ + u64 *load_addr; + int xn = opcode & 0x1f; + int disp = ldr_displacement(opcode); + + load_addr = (u64 *) (addr + disp); + + if (opcode & (1 << 30)) /* x0-x31 */ + regs->regs[xn] = *load_addr; + else /* w0-w31 */ + *(u32 *) (®s->regs[xn]) = (*(u32 *) (load_addr)); +} + +void simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs) +{ + u64 *load_addr; + long data; + int xn = opcode & 0x1f; + int disp = ldr_displacement(opcode); + + load_addr = (u64 *) (addr + disp); + data = *load_addr; + + regs->regs[xn] = sign_extend(data, 63); +} diff --git a/kprobe/arch/arm64/swap-asm/simulate-insn.h b/kprobe/arch/arm64/swap-asm/simulate-insn.h new file mode 100644 index 0000000..07a4939 --- /dev/null +++ b/kprobe/arch/arm64/swap-asm/simulate-insn.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) Samsung Electronics, 2014 + * + * Copied from: arch/arm64/kernel/simulate-insn.h + * + * Copyright (C) 2013 Linaro Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#ifndef _ARM_KERNEL_SIMULATE_INSN_H +#define _ARM_KERNEL_SIMULATE_INSN_H + +unsigned long check_cbz(u32 opcode, struct pt_regs *regs); +unsigned long check_cbnz(u32 opcode, struct pt_regs *regs); +unsigned long check_tbz(u32 opcode, struct pt_regs *regs); +unsigned long check_tbnz(u32 opcode, struct pt_regs *regs); +void simulate_none(u32 opcode, long addr, struct pt_regs *regs); +void simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs); +void simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs); +void simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs); +void simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs); +void simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs); +void simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs); +void simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs); +void simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs); + +#endif /* _ARM_KERNEL_SIMULATE_INSN_H */ diff --git a/kprobe/arch/arm64/swap-asm/swap_kprobes.c b/kprobe/arch/arm64/swap-asm/swap_kprobes.c index bfc29e1..e277147 100644 --- a/kprobe/arch/arm64/swap-asm/swap_kprobes.c +++ b/kprobe/arch/arm64/swap-asm/swap_kprobes.c @@ -1,4 +1,11 @@ /* + * Copied from: arch/arm64/kernel/kprobes.c + * + * Kprobes support for ARM64 + * + * Copyright (C) 2013 Linaro Limited. + * Author: Sandeepa Prabhu + * * 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 @@ -20,20 +27,422 @@ */ -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "swap_kprobes.h" +#include "kprobes-arm64.h" +#include "dbg_interface.h" + + +#define BRK_BP 0x63 +#define BRK_PSEUDO_SS 0x64 +#define BRK64_OPCODE_BP MAKE_BRK(BRK_BP) +#define BRK64_OPCODE_PSEUDO_SS MAKE_BRK(BRK_PSEUDO_SS) + + +#ifdef CONFIG_CPU_BIG_ENDIAN +static u32 conv_inst(u32 inst) +{ + return swab32(inst); +} +#else /* CONFIG_CPU_BIG_ENDIAN */ +static u32 conv_inst(u32 inst) +{ + return inst; +} +#endif /* CONFIG_CPU_BIG_ENDIAN */ + + +static void flush_icache(unsigned long addr, size_t size) +{ + flush_icache_range(addr, addr + size); +} + +static void write_u32(u32 *addr, u32 val) +{ + *addr = val; + flush_icache((unsigned long)addr, sizeof(val)); +} + +void arch_kp_core_arm(struct kp_core *p) +{ + write_u32((u32 *)p->addr, conv_inst(BRK64_OPCODE_BP)); +} + +void arch_kp_core_disarm(struct kp_core *p) +{ + write_u32((u32 *)p->addr, p->opcode); +} + + +struct restore_data { + unsigned long restore_addr; +}; + +static void ktd_restore_init(struct task_struct *task, void *data) +{ + struct restore_data *rdata = (struct restore_data *)data; + + rdata->restore_addr = 0; +} + +static void ktd_restore_exit(struct task_struct *task, void *data) +{ + struct restore_data *rdata = (struct restore_data *)data; + + WARN(rdata->restore_addr, "restore_addr=%lx", rdata->restore_addr); +} + +struct ktask_data ktd_restore = { + .init = ktd_restore_init, + .exit = ktd_restore_exit, + .size = sizeof(struct restore_data), +}; + +static DEFINE_PER_CPU(struct restore_data, per_cpu_restore_i); +static DEFINE_PER_CPU(struct restore_data, per_cpu_restore_st); + +static struct restore_data *current_restore_td(void) +{ + if (swap_in_interrupt()) + return &__get_cpu_var(per_cpu_restore_i); + else if (switch_to_bits_get(current_kctx, SWITCH_TO_ALL)) + return &__get_cpu_var(per_cpu_restore_st); + + return (struct restore_data *)swap_ktd(&ktd_restore, current); +} + + +static void arch_prepare_ss_slot(struct kp_core *p) +{ + /* prepare insn slot */ + p->ainsn.insn[0] = p->opcode; + p->ainsn.insn[1] = conv_inst(BRK64_OPCODE_PSEUDO_SS); + + flush_icache((unsigned long)p->ainsn.insn, KPROBES_TRAMP_LEN); +} + +static void arch_prepare_simulate(struct kp_core *p) +{ + if (p->ainsn.prepare) + p->ainsn.prepare(p, &p->ainsn); +} + +int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm) +{ + kprobe_opcode_t insn; + + /* copy instruction */ + insn = *(kprobe_opcode_t *)p->addr; + p->opcode = insn; + + /* decode instruction */ + switch (arm_kp_core_decode_insn(insn, &p->ainsn)) { + case INSN_REJECTED: /* insn not supported */ + return -EINVAL; + + case INSN_GOOD_NO_SLOT: /* insn need simulation */ + p->ainsn.insn = NULL; + arch_prepare_simulate(p); + break; + + case INSN_GOOD: /* instruction uses slot */ + p->ainsn.insn = swap_slot_alloc(sm); + if (!p->ainsn.insn) + return -ENOMEM; + + arch_prepare_ss_slot(p); + break; + }; + + return 0; +} + +static void save_previous_kp_core(struct kp_core_ctlblk *kcb, + unsigned long restore_addr) +{ + kcb->prev_kp_core.p = kp_core_running(); + kcb->prev_kp_core.status = kcb->kp_core_status; + kcb->prev_kp_core.restore_addr = restore_addr; +} + +void restore_previous_kp_core(struct kp_core_ctlblk *kcb) +{ + kp_core_running_set(kcb->prev_kp_core.p); + kcb->kp_core_status = kcb->prev_kp_core.status; + current_restore_td()->restore_addr = kcb->prev_kp_core.restore_addr; +} + +static void set_ss_context(struct kp_core_ctlblk *kcb, unsigned long addr) +{ + kcb->ss_ctx.ss_status = KP_CORE_STEP_PENDING; + kcb->ss_ctx.match_addr = addr + sizeof(kprobe_opcode_t); +} + +static void clear_ss_context(struct kp_core_ctlblk *kcb) +{ + kcb->ss_ctx.ss_status = KP_CORE_STEP_NONE; + kcb->ss_ctx.match_addr = 0; +} + +static void nop_singlestep_skip(struct pt_regs *regs) +{ + /* set return addr to next pc to continue */ + regs->pc += sizeof(kprobe_opcode_t); +} + +static int post_kp_core_handler(struct kp_core_ctlblk *kcb, + struct pt_regs *regs); + +static void arch_simulate_insn(struct kp_core *p, struct pt_regs *regs) +{ + struct kp_core_ctlblk *kcb = kp_core_ctlblk(); + + if (p->ainsn.handler) + p->ainsn.handler(p->opcode, (long)p->addr, regs); + + /* single step simulated, now go for post processing */ + post_kp_core_handler(kcb, regs); +} + +static bool is_ss_setup(struct restore_data *restore) +{ + return !!restore->restore_addr; +} + +static void setup_singlestep(struct kp_core *p, struct pt_regs *regs, + struct kp_core_ctlblk *kcb, int reenter) +{ + struct restore_data *restore = current_restore_td(); + + if (reenter) { + save_previous_kp_core(kcb, restore->restore_addr); + kp_core_running_set(p); + kcb->kp_core_status = KPROBE_REENTER; + } else { + kcb->kp_core_status = KPROBE_HIT_SS; + } + + if (p->ainsn.insn) { + unsigned long slot = (unsigned long)p->ainsn.insn; + + /* prepare for single stepping */ + restore->restore_addr = regs->pc + 4; + regs->pc = slot; + set_ss_context(kcb, slot); /* mark pending ss */ + } else { + restore->restore_addr = 0; /* reset */ + + /* insn simulation */ + arch_simulate_insn(p, regs); + } +} + +static int reenter_kp_core(struct kp_core *p, struct pt_regs *regs, + struct kp_core_ctlblk *kcb) +{ + switch (kcb->kp_core_status) { + case KPROBE_HIT_SSDONE: + case KPROBE_HIT_ACTIVE: + if (!p->ainsn.check_condn || p->ainsn.check_condn(p, regs)) { + setup_singlestep(p, regs, kcb, 1); + } else { + /* condition failed, it's NOP so skip stepping */ + nop_singlestep_skip(regs); + } + break; + case KPROBE_HIT_SS: + pr_warn("Unrecoverable kp_core detected at %lx\n", p->addr); + BUG(); + default: + WARN_ON(1); + return 0; + } + + return 1; +} -struct kprobe; -struct pt_regs; +static int post_kp_core_handler(struct kp_core_ctlblk *kcb, + struct pt_regs *regs) +{ + struct kp_core *cur = kp_core_running(); + struct restore_data *restore = current_restore_td(); + + if (cur == NULL) { + WARN_ON(1); + return 0; + } + + /* return addr restore if non-branching insn */ + if (is_ss_setup(restore)) { + regs->pc = restore->restore_addr; + restore->restore_addr = 0; + kp_core_put(cur); + } else { + WARN_ON(1); + } + + /* restore back original saved kp_core variables and continue */ + if (kcb->kp_core_status == KPROBE_REENTER) { + restore_previous_kp_core(kcb); + } else { /* call post handler */ + kcb->kp_core_status = KPROBE_HIT_SSDONE; + kp_core_running_set(NULL); + } + + return 1; +} + +static enum dbg_code kprobe_handler(struct pt_regs *regs, unsigned int esr) +{ + struct kp_core *p, *cur; + struct kp_core_ctlblk *kcb; + unsigned long addr = regs->pc; + struct restore_data *restore = current_restore_td(); + kcb = kp_core_ctlblk(); + cur = kp_core_running(); + + rcu_read_lock(); + p = kp_core_by_addr(addr); + if (p) + kp_core_get(p); + rcu_read_unlock(); + + if (p) { + if (cur && reenter_kp_core(p, regs, kcb)) { + if (!is_ss_setup(restore)) + kp_core_put(p); + + return DBG_HANDLED; + } else if (!p->ainsn.check_condn || + p->ainsn.check_condn(p, regs)) { + /* Probe hit and conditional execution check ok. */ + kp_core_running_set(p); + kcb->kp_core_status = KPROBE_HIT_ACTIVE; + + if (!(regs->pstate & PSR_I_BIT)) + local_irq_enable(); + + if (!p->handlers.pre(p, regs)) { + kcb->kp_core_status = KPROBE_HIT_SS; + setup_singlestep(p, regs, kcb, 0); + } + + local_irq_disable(); + } else { + /* + * Breakpoint hit but conditional check failed, + * so just skip handling since it is NOP. + */ + nop_singlestep_skip(regs); + } + + if (!is_ss_setup(restore)) + kp_core_put(p); + } else if (*(kprobe_opcode_t *)addr != BRK64_OPCODE_BP) { + /* + * The breakpoint instruction was removed right + * after we hit it. Another cpu has removed + * either a probepoint or a debugger breakpoint + * at this address. In either case, no further + * handling of this interrupt is appropriate. + * Return back to original instruction, and continue. + */ + } else { + pr_info("no_kprobe: pc=%llx\n", regs->pc); + } + + return DBG_HANDLED; +} + +static enum dbg_code kp_core_ss_hit(struct kp_core_ctlblk *kcb, + unsigned long addr) +{ + if ((kcb->ss_ctx.ss_status == KP_CORE_STEP_PENDING) + && (kcb->ss_ctx.match_addr == addr)) { + /* clear pending ss */ + clear_ss_context(kcb); + return DBG_HANDLED; + } + + /* not ours, kp_cores should ignore it */ + return DBG_ERROR; +} + +static enum dbg_code kprobe_ss_handler(struct pt_regs *regs, unsigned int esr) +{ + enum dbg_code ret; + struct kp_core_ctlblk *kcb = kp_core_ctlblk(); + + /* check, and return error if this is not our step */ + ret = kp_core_ss_hit(kcb, regs->pc); + if (ret == DBG_HANDLED) { + /* single step complete, call post handlers */ + post_kp_core_handler(kcb, regs); + } + + return ret; +} + +static struct brk_hook dbg_bp = { + .spsr_mask = PSR_MODE_MASK, + .spsr_val = PSR_MODE_EL1h, + .esr_mask = DBG_BRK_ESR_MASK, + .esr_val = DBG_BRK_ESR(BRK_BP), + .fn = kprobe_handler, +}; + +static struct brk_hook dbg_ss = { + .spsr_mask = PSR_MODE_MASK, + .spsr_val = PSR_MODE_EL1h, + .esr_mask = DBG_BRK_ESR_MASK, + .esr_val = DBG_BRK_ESR(BRK_PSEUDO_SS), + .fn = kprobe_ss_handler, +}; + + + + + +/* ============================================================================ + * = ARCH INIT/EXIT KPROBES = + * ============================================================================ + */ +int arch_init_module_deps(void) +{ + return 0; +} int swap_arch_init_kprobes(void) { - WARN(1, "not implemented"); /* FIXME: to implement */ + int ret; + + ret = swap_ktd_reg(&ktd_restore); + if (ret) + return ret; + + ret = dbg_iface_init(); + if (ret) + swap_ktd_unreg(&ktd_restore); + + dbg_brk_hook_reg(&dbg_ss); + dbg_brk_hook_reg(&dbg_bp); + return 0; } void swap_arch_exit_kprobes(void) { - WARN(1, "not implemented"); /* FIXME: to implement */ + dbg_brk_hook_unreg(&dbg_ss); + dbg_brk_hook_unreg(&dbg_bp); + dbg_iface_uninit(); + swap_ktd_unreg(&ktd_restore); } diff --git a/kprobe/arch/arm64/swap-asm/swap_kprobes.h b/kprobe/arch/arm64/swap-asm/swap_kprobes.h index e738a89..447018d 100644 --- a/kprobe/arch/arm64/swap-asm/swap_kprobes.h +++ b/kprobe/arch/arm64/swap-asm/swap_kprobes.h @@ -2,6 +2,10 @@ #define _ASM_ARM64_KPROBES_H /* + * Copied from: arch/arm64/kernel/kprobes.h + * + * Copyright (C) 2013 Linaro Limited. + * * 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 @@ -26,41 +30,69 @@ #include -#define BREAKPOINT_INSTRUCTION 0 /* FIXME: to implement */ -#define KPROBES_TRAMP_LEN 10 /* FIXME: to implement */ +#define MAX_INSN_SIZE 2 +#define KPROBES_TRAMP_LEN (MAX_INSN_SIZE * 4) /* 4 - instruction size */ + + +struct kprobe; +struct kp_core; +struct slot_manager; +struct arch_specific_insn; +struct kretprobe_instance; typedef u32 kprobe_opcode_t; +typedef unsigned long (kp_core_pstate_check_t)(unsigned long); +typedef unsigned long (kp_core_condition_check_t)(struct kp_core *, + struct pt_regs *); +typedef void (kp_core_prepare_t)(struct kp_core *, struct arch_specific_insn *); +typedef void (kp_core_handler_t)(u32 opcode, long addr, struct pt_regs *); + -struct arch_specific_insn { /* FIXME: to implement */ - /* copy of the original instruction */ +/* architecture specific copy of original instruction */ +struct arch_specific_insn { kprobe_opcode_t *insn; + kp_core_pstate_check_t *pstate_cc; + kp_core_condition_check_t *check_condn; + kp_core_prepare_t *prepare; + kp_core_handler_t *handler; }; -/* per-cpu kprobe control block */ -struct kp_core_ctlblk { /* FIXME: to implement */ - unsigned long kp_core_status; +struct prev_kp_core { + struct kp_core *p; + unsigned int status; + unsigned long restore_addr; /* restore address after single step */ }; +enum ss_status { + KP_CORE_STEP_NONE, + KP_CORE_STEP_PENDING, +}; -struct kprobe; -struct kp_core; -struct slot_manager; -struct kretprobe_instance; +/* Single step context for kp_core */ +struct kp_core_step_ctx { + enum ss_status ss_status; + unsigned long match_addr; +}; + +/* kp_core control block */ +struct kp_core_ctlblk { + unsigned int kp_core_status; + struct prev_kp_core prev_kp_core; + struct kp_core_step_ctx ss_ctx; +}; static inline unsigned long swap_get_karg(struct pt_regs *regs, unsigned long n) { - WARN(1, "not implemented"); /* FIXME: to implement */ - return 0; + return n < 8 ? regs->regs[n] : *(((long *)regs->sp) + (n - 8)); } static inline unsigned long swap_get_sarg(struct pt_regs *regs, unsigned long n) { - WARN(1, "not implemented"); /* FIXME: to implement */ - return 0; + return swap_get_karg(regs, n); } static inline unsigned long swap_get_instr_ptr(struct pt_regs *regs) @@ -85,28 +117,12 @@ static inline void swap_set_ret_addr(struct pt_regs *regs, unsigned long val) WARN(1, "not implemented"); /* FIXME: to implement */ } -static inline int arch_kp_core_prepare(struct kp_core *p, - struct slot_manager *sm) -{ - WARN(1, "not implemented"); /* FIXME: to implement */ - return 0; -} - -static inline void arch_kp_core_arm(struct kp_core *p) -{ - WARN(1, "not implemented"); /* FIXME: to implement */ -} - -static inline void arch_kp_core_disarm(struct kp_core *p) -{ - WARN(1, "not implemented"); /* FIXME: to implement */ -} +void arch_kp_core_arm(struct kp_core *p); +void arch_kp_core_disarm(struct kp_core *p); -static inline void restore_previous_kp_core(struct kp_core_ctlblk *kcb) -{ - WARN(1, "not implemented"); /* FIXME: to implement */ -} +int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm); +void restore_previous_kp_core(struct kp_core_ctlblk *kcb); static inline void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) @@ -154,12 +170,7 @@ static inline int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs, return 0; } -static inline int arch_init_module_deps(void) -{ - WARN(1, "not implemented"); /* FIXME: to implement */ - return 0; -} - +int arch_init_module_deps(void); int swap_arch_init_kprobes(void); void swap_arch_exit_kprobes(void); -- 2.7.4 From 4b2cbc9eaf7e2b2271de717924fff2e9ddb23890 Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Thu, 13 Feb 2014 22:46:01 +0400 Subject: [PATCH 09/16] ARM64: implement kretprobe Change-Id: I1616fce545b816d7a537ca2a3b0a55fb9f482cbc Signed-off-by: Vyacheslav Cherkashin --- kprobe/arch/arm/swap-asm/swap_kprobes.c | 5 ++--- kprobe/arch/arm64/swap-asm/swap_kprobes.c | 35 +++++++++++++++++++++++++++++++ kprobe/arch/arm64/swap-asm/swap_kprobes.h | 13 +++--------- kprobe/arch/x86/swap-asm/swap_kprobes.c | 7 +------ kprobe/swap_kprobes.c | 4 ++-- kprobe/swap_kprobes.h | 2 +- 6 files changed, 44 insertions(+), 22 deletions(-) diff --git a/kprobe/arch/arm/swap-asm/swap_kprobes.c b/kprobe/arch/arm/swap-asm/swap_kprobes.c index 4006855..f4e004c 100644 --- a/kprobe/arch/arm/swap-asm/swap_kprobes.c +++ b/kprobe/arch/arm/swap-asm/swap_kprobes.c @@ -546,9 +546,8 @@ void __naked swap_kretprobe_trampoline(void) { __asm__ __volatile__ ( "stmdb sp!, {r0 - r11}\n" - "mov r1, sp\n" - "mov r0, #0\n" - "bl trampoline_probe_handler\n" + "mov r0, sp\n" /* struct pt_regs -> r0 */ + "bl trampoline_handler\n" "mov lr, r0\n" "ldmia sp!, {r0 - r11}\n" "bx lr\n" diff --git a/kprobe/arch/arm64/swap-asm/swap_kprobes.c b/kprobe/arch/arm64/swap-asm/swap_kprobes.c index e277147..a12a15f 100644 --- a/kprobe/arch/arm64/swap-asm/swap_kprobes.c +++ b/kprobe/arch/arm64/swap-asm/swap_kprobes.c @@ -413,6 +413,41 @@ static struct brk_hook dbg_ss = { /* ============================================================================ + * = KRETPROBE = + * ============================================================================ + */ +void swap_kretprobe_trampoline(void); +__asm( + ".text\n" + ".global swap_kretprobe_trampoline\n" + "swap_kretprobe_trampoline:\n" + "stp x6, x7, [sp,#-16]!\n" + "stp x4, x5, [sp,#-16]!\n" + "stp x2, x3, [sp,#-16]!\n" + "stp x0, x1, [sp,#-16]!\n" + "mov x0, sp\n" /* struct pt_regs (x0..x7) */ + "bl trampoline_handler\n" + "mov x30, x0\n" /* set real lr */ + "ldp x0, x1, [sp],#16\n" + "ldp x2, x3, [sp],#16\n" + "ldp x4, x5, [sp],#16\n" + "ldp x6, x7, [sp],#16\n" + "ret\n" +); + +void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + ri->ret_addr = (unsigned long *)regs->regs[30]; /* lr */ + regs->regs[30] = (unsigned long)&swap_kretprobe_trampoline; + ri->sp = (unsigned long *)regs->sp; +} + + + + + +/* ============================================================================ * = ARCH INIT/EXIT KPROBES = * ============================================================================ */ diff --git a/kprobe/arch/arm64/swap-asm/swap_kprobes.h b/kprobe/arch/arm64/swap-asm/swap_kprobes.h index 447018d..784cf06 100644 --- a/kprobe/arch/arm64/swap-asm/swap_kprobes.h +++ b/kprobe/arch/arm64/swap-asm/swap_kprobes.h @@ -124,16 +124,9 @@ void arch_kp_core_disarm(struct kp_core *p); int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm); void restore_previous_kp_core(struct kp_core_ctlblk *kcb); -static inline void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri, - struct pt_regs *regs) -{ - WARN(1, "not implemented"); /* FIXME: to implement */ -} - -static inline void swap_kretprobe_trampoline(void) -{ - WARN(1, "not implemented"); /* FIXME: to implement */ -} +void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri, + struct pt_regs *regs); +void swap_kretprobe_trampoline(void); static inline unsigned long arch_get_task_pc(struct task_struct *p) diff --git a/kprobe/arch/x86/swap-asm/swap_kprobes.c b/kprobe/arch/x86/swap-asm/swap_kprobes.c index 79aa609..deb0bc8 100644 --- a/kprobe/arch/x86/swap-asm/swap_kprobes.c +++ b/kprobe/arch/x86/swap-asm/swap_kprobes.c @@ -83,7 +83,7 @@ __asm( "pushf\n" SWAP_SAVE_REGS_STRING "movl %esp, %eax\n" - "call trampoline_probe_handler_x86\n" + "call trampoline_handler\n" /* move eflags to cs */ "movl 56(%esp), %edx\n" "movl %edx, 52(%esp)\n" @@ -864,11 +864,6 @@ void arch_kp_core_disarm(struct kp_core *p) swap_text_poke((void *)p->addr, &p->opcode, 1); } -static __used void *trampoline_probe_handler_x86(struct pt_regs *regs) -{ - return (void *)trampoline_probe_handler(NULL, regs); -} - /** * @brief Prepares kretprobes, saves ret address, makes function return to * trampoline. diff --git a/kprobe/swap_kprobes.c b/kprobe/swap_kprobes.c index 62538ff..003f858 100644 --- a/kprobe/swap_kprobes.c +++ b/kprobe/swap_kprobes.c @@ -659,7 +659,7 @@ static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs) * @param regs Pointer to CPU registers data. * @return orig_ret_address */ -int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) +unsigned long trampoline_handler(struct pt_regs *regs) { struct kretprobe_instance *ri = NULL; struct hlist_head *head; @@ -745,7 +745,7 @@ int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) * to run (and have re-enabled preemption) */ - return (int)orig_ret_address; + return orig_ret_address; } #define SCHED_RP_NR 200 diff --git a/kprobe/swap_kprobes.h b/kprobe/swap_kprobes.h index bc662f6..1fca695 100644 --- a/kprobe/swap_kprobes.h +++ b/kprobe/swap_kprobes.h @@ -279,7 +279,7 @@ void swap_unregister_kretprobe_bottom(struct kretprobe *rp); void swap_unregister_kretprobes_bottom(struct kretprobe **rps, size_t size); -int trampoline_probe_handler (struct kprobe *p, struct pt_regs *regs); +unsigned long trampoline_handler(struct pt_regs *regs); extern atomic_t kprobe_count; -- 2.7.4 From 124970b46f5c3482e60a77aa067260d60bb60a6d Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Thu, 13 Oct 2016 20:07:48 +0300 Subject: [PATCH 10/16] ks_feature: fix memory allocation size Correct memory allocation size for kretprobe pointers array. Change-Id: Ic5fa0582936a59730cacd2f7fa889927971fbd49 Signed-off-by: Vyacheslav Cherkashin --- ks_features/ks_features.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ks_features/ks_features.c b/ks_features/ks_features.c index 7957b60..14aa8d9 100644 --- a/ks_features/ks_features.c +++ b/ks_features/ks_features.c @@ -294,9 +294,8 @@ static int unregister_multiple_syscalls(size_t *id_p, size_t cnt) if (cnt == 1) return unregister_syscall(id_p[0]); - --cnt; - rpp = kmalloc(GFP_KERNEL, sizeof(*rpp) * cnt); + --cnt; if (rpp == NULL) { for (; cnt != end; --cnt) { ret = unregister_syscall(id_p[cnt]); -- 2.7.4 From 5298ee484a47c201b1170d8136a57eaede645e86 Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Thu, 13 Oct 2016 20:15:40 +0300 Subject: [PATCH 11/16] ks_feature: fix memory corruption Change function arguments order. Change-Id: I8ec95c4b359f2452a20ea88b17cefaaf88656f05 Signed-off-by: Vyacheslav Cherkashin --- ks_features/ks_features.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ks_features/ks_features.c b/ks_features/ks_features.c index 14aa8d9..503612f 100644 --- a/ks_features/ks_features.c +++ b/ks_features/ks_features.c @@ -294,7 +294,7 @@ static int unregister_multiple_syscalls(size_t *id_p, size_t cnt) if (cnt == 1) return unregister_syscall(id_p[0]); - rpp = kmalloc(GFP_KERNEL, sizeof(*rpp) * cnt); + rpp = kmalloc(sizeof(*rpp) * cnt, GFP_KERNEL); --cnt; if (rpp == NULL) { for (; cnt != end; --cnt) { @@ -337,7 +337,7 @@ static void do_uninstall_features(struct feature *f, size_t i) size_t cnt = 0; const size_t end = ((size_t) 0) - 1; - id_p = kmalloc(GFP_KERNEL, sizeof(id) * (i + 1)); + id_p = kmalloc(sizeof(id) * (i + 1), GFP_KERNEL); /* NULL check is below in loop */ for (; i != end; --i) { -- 2.7.4 From 9452811f831d76b17c8632415cb6933b9698e5d9 Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Fri, 1 Jul 2016 15:40:03 +0300 Subject: [PATCH 12/16] ARM64: create jumper Change-Id: I5eab4cd515d0a64cd8991d779811644f98a14c65 Signed-off-by: Vyacheslav Cherkashin --- kprobe/arch/arm64/swap-asm/swap_kprobes.c | 88 +++++++++++++++++++++++++++++++ kprobe/arch/arm64/swap-asm/swap_kprobes.h | 14 ++--- 2 files changed, 91 insertions(+), 11 deletions(-) diff --git a/kprobe/arch/arm64/swap-asm/swap_kprobes.c b/kprobe/arch/arm64/swap-asm/swap_kprobes.c index a12a15f..88083bf 100644 --- a/kprobe/arch/arm64/swap-asm/swap_kprobes.c +++ b/kprobe/arch/arm64/swap-asm/swap_kprobes.c @@ -27,6 +27,7 @@ */ +#include #include #include #include @@ -448,6 +449,93 @@ void swap_arch_prepare_kretprobe(struct kretprobe_instance *ri, /* ============================================================================ + * = JUMPER = + * ============================================================================ + */ +struct cb_data { + unsigned long ret_addr; + unsigned long x0; + + jumper_cb_t cb; + char data[0]; +}; + +static unsigned long __used get_x0(struct cb_data *data) +{ + return data->x0; +} + +static unsigned long __used jump_handler(struct cb_data *data) +{ + unsigned long ret_addr = data->ret_addr; + + /* call callback */ + data->cb(data->data); + + /* FIXME: potential memory leak, when process kill */ + kfree(data); + + return ret_addr; +} + +void jump_trampoline(void); +__asm( + ".text\n" + "jump_trampoline:\n" + + "stp x6, x7, [sp,#-16]!\n" + "stp x4, x5, [sp,#-16]!\n" + "stp x2, x3, [sp,#-16]!\n" + "stp x0, x1, [sp,#-16]!\n" + "mov x1, x0\n" /* data --> x1 */ + "bl get_x0\n" + "str x0, [sp]\n" /* restore x0 */ + "mov x0, x1\n" /* data --> x0 */ + "bl jump_handler\n" + "mov x30, x0\n" /* set lr */ + "ldp x0, x1, [sp],#16\n" + "ldp x2, x3, [sp],#16\n" + "ldp x4, x5, [sp],#16\n" + "ldp x6, x7, [sp],#16\n" + "ret\n" +); + +unsigned long get_jump_addr(void) +{ + return (unsigned long)&jump_trampoline; +} +EXPORT_SYMBOL_GPL(get_jump_addr); + +int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs, + jumper_cb_t cb, void *data, size_t size) +{ + struct cb_data *cb_data; + + cb_data = kmalloc(sizeof(*cb_data) + size, GFP_ATOMIC); + if (cb_data == NULL) + return -ENOMEM; + + /* save data */ + if (size) + memcpy(cb_data->data, data, size); + + /* save info for restore */ + cb_data->ret_addr = ret_addr; + cb_data->cb = cb; + cb_data->x0 = regs->regs[0]; + + /* save cb_data to x0 */ + regs->regs[0] = (long)cb_data; + + return 0; +} +EXPORT_SYMBOL_GPL(set_jump_cb); + + + + + +/* ============================================================================ * = ARCH INIT/EXIT KPROBES = * ============================================================================ */ diff --git a/kprobe/arch/arm64/swap-asm/swap_kprobes.h b/kprobe/arch/arm64/swap-asm/swap_kprobes.h index 784cf06..a7708c6 100644 --- a/kprobe/arch/arm64/swap-asm/swap_kprobes.h +++ b/kprobe/arch/arm64/swap-asm/swap_kprobes.h @@ -150,18 +150,10 @@ static inline int swap_setjmp_pre_handler(struct kprobe *p, /* jumper */ typedef unsigned long (*jumper_cb_t)(void *); -static inline unsigned long get_jump_addr(void) -{ - WARN(1, "not implemented"); /* FIXME: to implement */ - return 0x87654321; -} +unsigned long get_jump_addr(void); +int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs, + jumper_cb_t cb, void *data, size_t size); -static inline int set_jump_cb(unsigned long ret_addr, struct pt_regs *regs, - jumper_cb_t cb, void *data, size_t size) -{ - WARN(1, "not implemented"); /* FIXME: to implement */ - return 0; -} int arch_init_module_deps(void); -- 2.7.4 From fd67fbc01505dd91cb22e41be5b913ae95b84f4f Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Fri, 1 Jul 2016 17:36:06 +0300 Subject: [PATCH 13/16] Add dentry_get()/dentry_put() Change-Id: Ida2003a217d7de0cdf9c001e211f51185e8ec930 Signed-off-by: Vyacheslav Cherkashin --- us_manager/pf/pf_group.c | 53 ++++++++++++++++++++++++++++-------------------- us_manager/pf/pf_group.h | 2 ++ 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/us_manager/pf/pf_group.c b/us_manager/pf/pf_group.c index 38cfb85..5de3f48 100644 --- a/us_manager/pf/pf_group.c +++ b/us_manager/pf/pf_group.c @@ -230,6 +230,31 @@ static void subsequent_install(struct task_struct *task, up_write(&task->mm->mmap_sem); } +struct dentry *dentry_get(const char *path) +{ + struct dentry *d; + struct path st_path; + + if (kern_path(path, LOOKUP_FOLLOW, &st_path) != 0) { + pr_err("failed to lookup dentry for path %s!\n", path); + return NULL; + } + + d = st_path.dentry; + dget(d); + path_put(&st_path); + + return d; +} +EXPORT_SYMBOL_GPL(dentry_get); + +void dentry_put(struct dentry *d) +{ + dput(d); +} +EXPORT_SYMBOL_GPL(dentry_put); + + /** * @brief Get dentry struct by path * @@ -238,29 +263,13 @@ static void subsequent_install(struct task_struct *task, */ struct dentry *dentry_by_path(const char *path) { - struct dentry *dentry; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) - struct path st_path; - if (kern_path(path, LOOKUP_FOLLOW, &st_path) != 0) { -#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */ - struct nameidata nd; - if (path_lookup(path, LOOKUP_FOLLOW, &nd) != 0) { -#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */ - printk("failed to lookup dentry for path %s!\n", path); - return NULL; - } + struct dentry *d; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) - dentry = nd.dentry; - path_release(&nd); -#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 38) - dentry = nd.path.dentry; - path_put(&nd.path); -#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) */ - dentry = st_path.dentry; - path_put(&st_path); -#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) */ - return dentry; + d = dentry_get(path); + if (d) + dput(d); + + return d; } EXPORT_SYMBOL_GPL(dentry_by_path); diff --git a/us_manager/pf/pf_group.h b/us_manager/pf/pf_group.h index 3c170fa..20da025 100644 --- a/us_manager/pf/pf_group.h +++ b/us_manager/pf/pf_group.h @@ -44,6 +44,8 @@ struct pfg_msg_cb { /* FIXME: create and use get_dentry() and put_dentry() */ struct dentry *dentry_by_path(const char *path); +struct dentry *dentry_get(const char *path); +void dentry_put(struct dentry *d); struct pf_group *get_pf_group_by_dentry(struct dentry *dentry, void *priv); struct pf_group *get_pf_group_by_tgid(pid_t tgid, void *priv); -- 2.7.4 From f9190221fe22a4131a4103aad4d544940970e4aa Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Mon, 3 Mar 2014 15:22:30 +0400 Subject: [PATCH 14/16] ARM64: implement uprobe Change-Id: I350fac0dc590be63a2cbeef6fa8c6a8bb7d1086f Signed-off-by: Vyacheslav Cherkashin Signed-off-by: Alexander Aksenov --- kprobe/arch/arm64/swap-asm/condn-helpers.c | 5 +- kprobe/arch/arm64/swap-asm/condn-helpers.h | 32 +++ kprobe/arch/arm64/swap-asm/kprobes-arm64.c | 3 +- kprobe/arch/arm64/swap-asm/simulate-insn.c | 13 +- uprobe/Kbuild | 4 +- uprobe/arch/arm64/swap-asm/swap_uprobes.c | 292 +++++++++++++++++++++++++- uprobe/arch/arm64/swap-asm/swap_uprobes.h | 68 ++++--- uprobe/arch/arm64/swap-asm/uprobes-arm64.c | 306 ++++++++++++++++++++++++++++ uprobe/arch/arm64/swap-asm/uprobes-arm64.h | 32 +++ uprobe/arch/arm64/swap-asm/uprobes-decode.h | 116 +++++++++++ us_manager/helper.c | 7 +- 11 files changed, 845 insertions(+), 33 deletions(-) create mode 100644 kprobe/arch/arm64/swap-asm/condn-helpers.h create mode 100644 uprobe/arch/arm64/swap-asm/uprobes-arm64.c create mode 100644 uprobe/arch/arm64/swap-asm/uprobes-arm64.h create mode 100644 uprobe/arch/arm64/swap-asm/uprobes-decode.h diff --git a/kprobe/arch/arm64/swap-asm/condn-helpers.c b/kprobe/arch/arm64/swap-asm/condn-helpers.c index c8561cc..708fb7f 100644 --- a/kprobe/arch/arm64/swap-asm/condn-helpers.c +++ b/kprobe/arch/arm64/swap-asm/condn-helpers.c @@ -28,7 +28,7 @@ #include -#include "swap_kprobes.h" +#include "condn-helpers.h" static unsigned long __check_eq(unsigned long pstate) @@ -118,9 +118,10 @@ static unsigned long __check_al(unsigned long pstate) return true; } -kp_core_pstate_check_t * const kp_core_condition_checks[16] = { +probes_pstate_check_t * const probe_condition_checks[16] = { &__check_eq, &__check_ne, &__check_cs, &__check_cc, &__check_mi, &__check_pl, &__check_vs, &__check_vc, &__check_hi, &__check_ls, &__check_ge, &__check_lt, &__check_gt, &__check_le, &__check_al, &__check_al }; +EXPORT_SYMBOL_GPL(probe_condition_checks); diff --git a/kprobe/arch/arm64/swap-asm/condn-helpers.h b/kprobe/arch/arm64/swap-asm/condn-helpers.h new file mode 100644 index 0000000..088974d --- /dev/null +++ b/kprobe/arch/arm64/swap-asm/condn-helpers.h @@ -0,0 +1,32 @@ +#ifndef _ASM_CONDN_HELPER_H +#define _ASM_CONDN_HELPER_H + +/* + * 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, 2014 + * + * 2014 Vyacheslav Cherkashin + * + */ + + +typedef unsigned long (probes_pstate_check_t)(unsigned long); + + +extern probes_pstate_check_t * const probe_condition_checks[16]; + + +#endif /* _ASM_CONDN_HELPER_H */ diff --git a/kprobe/arch/arm64/swap-asm/kprobes-arm64.c b/kprobe/arch/arm64/swap-asm/kprobes-arm64.c index d42ffac..f715938 100644 --- a/kprobe/arch/arm64/swap-asm/kprobes-arm64.c +++ b/kprobe/arch/arm64/swap-asm/kprobes-arm64.c @@ -23,6 +23,7 @@ #include "probes-decode.h" #include "kprobes-arm64.h" #include "simulate-insn.h" +#include "condn-helpers.h" /* @@ -68,7 +69,7 @@ static void prepare_bcond(struct kp_core *p, struct arch_specific_insn *asi) kprobe_opcode_t insn = p->opcode; asi->check_condn = __check_pstate; - asi->pstate_cc = kp_core_condition_checks[insn & 0xf]; + asi->pstate_cc = probe_condition_checks[insn & 0xf]; } static void prepare_cbz_cbnz(struct kp_core *p, struct arch_specific_insn *asi) diff --git a/kprobe/arch/arm64/swap-asm/simulate-insn.c b/kprobe/arch/arm64/swap-asm/simulate-insn.c index 0d79671..c959a1d 100644 --- a/kprobe/arch/arm64/swap-asm/simulate-insn.c +++ b/kprobe/arch/arm64/swap-asm/simulate-insn.c @@ -18,7 +18,6 @@ #include #include #include -#include #include "simulate-insn.h" @@ -48,6 +47,7 @@ unsigned long check_cbz(u32 opcode, struct pt_regs *regs) return (opcode & (1 << 31)) ? !(regs->regs[xn]) : !(regs->regs[xn] & 0xffffffff); } +EXPORT_SYMBOL_GPL(check_cbz); unsigned long check_cbnz(u32 opcode, struct pt_regs *regs) { @@ -56,6 +56,7 @@ unsigned long check_cbnz(u32 opcode, struct pt_regs *regs) return (opcode & (1 << 31)) ? (regs->regs[xn]) : (regs->regs[xn] & 0xffffffff); } +EXPORT_SYMBOL_GPL(check_cbnz); unsigned long check_tbz(u32 opcode, struct pt_regs *regs) { @@ -64,6 +65,7 @@ unsigned long check_tbz(u32 opcode, struct pt_regs *regs) return ~((regs->regs[xn] >> bit_pos) & 0x1); } +EXPORT_SYMBOL_GPL(check_tbz); unsigned long check_tbnz(u32 opcode, struct pt_regs *regs) { @@ -72,6 +74,7 @@ unsigned long check_tbnz(u32 opcode, struct pt_regs *regs) return (regs->regs[xn] >> bit_pos) & 0x1; } +EXPORT_SYMBOL_GPL(check_tbnz); /* * instruction simulate functions @@ -92,6 +95,7 @@ void simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs) regs->regs[xn] = opcode & 0x80000000 ? res & 0xfffffffffffff000 : res; regs->pc += 4; } +EXPORT_SYMBOL_GPL(simulate_adr_adrp); void simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs) { @@ -103,6 +107,7 @@ void simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs) regs->pc = addr + disp; } +EXPORT_SYMBOL_GPL(simulate_b_bl); void simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs) { @@ -110,6 +115,7 @@ void simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs) regs->pc = addr + disp; } +EXPORT_SYMBOL_GPL(simulate_b_cond); void simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs) { @@ -121,6 +127,7 @@ void simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs) regs->pc = regs->regs[xn]; } +EXPORT_SYMBOL_GPL(simulate_br_blr_ret); void simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs) { @@ -128,6 +135,7 @@ void simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs) regs->pc = addr + disp; } +EXPORT_SYMBOL_GPL(simulate_cbz_cbnz); void simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs) { @@ -135,6 +143,7 @@ void simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs) regs->pc = addr + disp; } +EXPORT_SYMBOL_GPL(simulate_tbz_tbnz); void simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs) { @@ -149,6 +158,7 @@ void simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs) else /* w0-w31 */ *(u32 *) (®s->regs[xn]) = (*(u32 *) (load_addr)); } +EXPORT_SYMBOL_GPL(simulate_ldr_literal); void simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs) { @@ -162,3 +172,4 @@ void simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs) regs->regs[xn] = sign_extend(data, 63); } +EXPORT_SYMBOL_GPL(simulate_ldrsw_literal); diff --git a/uprobe/Kbuild b/uprobe/Kbuild index 553aae1..e4e96f0 100644 --- a/uprobe/Kbuild +++ b/uprobe/Kbuild @@ -13,7 +13,9 @@ swap_uprobe-$(CONFIG_ARM) += \ ### ARM64 -swap_uprobe-$(CONFIG_ARM64) += arch/arm64/swap-asm/swap_uprobes.o +swap_uprobe-$(CONFIG_ARM64) += \ + arch/arm64/swap-asm/swap_uprobes.o \ + arch/arm64/swap-asm/uprobes-arm64.o ### X86 diff --git a/uprobe/arch/arm64/swap-asm/swap_uprobes.c b/uprobe/arch/arm64/swap-asm/swap_uprobes.c index 6de4d31..b75cb28 100644 --- a/uprobe/arch/arm64/swap-asm/swap_uprobes.c +++ b/uprobe/arch/arm64/swap-asm/swap_uprobes.c @@ -20,16 +20,302 @@ */ -#include +#include +#include +#include +#include +#include /* FIXME: remove it */ +#include +#include "uprobes-arm64.h" + + +#define BRK_BP 0x45 +#define BRK_PSEUDO_SS 0x54 +#define BRK64_OPCODE_BP MAKE_BRK(BRK_BP) +#define BRK64_OPCODE_PSEUDO_SS MAKE_BRK(BRK_PSEUDO_SS) + + +struct uprobe_ctlblk { + struct uprobe *p; +}; + +static struct td_raw td_raw; + +static struct uprobe_ctlblk *current_ctlblk(void) +{ + return (struct uprobe_ctlblk *)swap_td_raw(&td_raw, current); +} + +static struct uprobe *get_current_uprobe(void) +{ + return current_ctlblk()->p; +} + +static void set_current_uprobe(struct uprobe *p) +{ + current_ctlblk()->p = p; +} + +static void reset_current_uprobe(void) +{ + set_current_uprobe(NULL); +} + + +int arch_arm_uprobe(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)); + if (!ret) { + pr_err("failed to write memory addr=%p\n", p->addr); + return -EACCES; + } + + return 0; +} + +void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task) +{ + int ret; + + ret = write_proc_vm_atomic(task, (unsigned long)p->addr, + &p->opcode, sizeof(p->opcode)); + if (!ret) + pr_err("failed to write memory addr=%p!\n", p->addr); +} + + +static void arch_prepare_simulate_arm64(struct uprobe *p) +{ + if (p->ainsn.prepare) + p->ainsn.prepare(p, &p->ainsn); +} + +static void arch_prepare_ss_arm64(struct uprobe *p) +{ + /* prepare insn slot */ + p->ainsn.tramp_arm64[0] = p->opcode; + p->ainsn.tramp_arm64[1] = BRK64_OPCODE_PSEUDO_SS; + +} + +static int arch_prepare_uprobe_arm64(struct uprobe *p, u32 insn) +{ + switch (arm64_uprobe_decode_insn(insn, &p->ainsn)) { + case INSN_REJECTED: /* insn not supported */ + return -EINVAL; + + case INSN_GOOD_NO_SLOT: /* insn need simulation */ + p->ainsn.insn = NULL; + p->ainsn.matrioshka_flags |= MF_ARM64_SIMUL; + arch_prepare_simulate_arm64(p); + break; + + case INSN_GOOD: /* instruction uses slot */ + p->ainsn.insn = NULL; + p->ainsn.matrioshka_flags = MF_ARM64_EMUL; + + arch_prepare_ss_arm64(p); + break; + }; + + return 0; +} + +int arch_prepare_uprobe(struct uprobe *p) +{ + struct task_struct *task = p->task; + unsigned long vaddr = (unsigned long)p->addr; + u32 insn; + + if (vaddr & 0x01) { + pr_err("Error in %s at %d: attempt to register uprobe " + "at an unaligned address\n", __FILE__, __LINE__); + return -EINVAL; + } + + 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); +} + +void arch_remove_uprobe(struct uprobe *p) +{ + swap_slot_free(p->sm, p->ainsn.insn); +} + +static void simulate_insn_arm64(struct uprobe *p, struct pt_regs *regs) +{ + if (p->ainsn.handler) + p->ainsn.handler(p->opcode, (long)p->addr, regs); + + reset_current_uprobe(); +} + +static void setup_ss_arm64(struct uprobe *p, struct pt_regs *regs) +{ + /* set trampoline */ + regs->pc = (u64)p->ainsn.insn; + + set_current_uprobe(p); +} + +static void setup_matrioshka_arm64(struct uprobe *p, struct pt_regs *regs) +{ + int m; + + m = p->ainsn.matrioshka_flags & MF_ARM64_MASK; + switch (m) { + case MF_ARM64_SIMUL: + simulate_insn_arm64(p, regs); + break; + case MF_ARM64_EMUL: + setup_ss_arm64(p, regs); + break; + default: + pr_err("ERROR: unknown matrioshka mode(%x) for ARM64\n", m); + } +} + +static int make_trampoline_arm64(struct uprobe *p, struct pt_regs *regs) +{ + u32 *tramp, *utramp; + + tramp = p->ainsn.tramp_arm64; + + utramp = swap_slot_alloc(p->sm); + if (utramp == NULL) { + pr_err("ERROR: cannot allocate trampoline\n"); + return -ENOMEM; + } + + if (!write_proc_vm_atomic(p->task, (unsigned long)utramp, tramp, + UPROBES_TRAMP_LEN)) + pr_err("failed to write memory %p!\n", utramp); + + p->ainsn.insn = utramp; + + return 0; +} + +static int make_matrioshka_arm64(struct uprobe *p, struct pt_regs *regs) +{ + int m, ret = 0; + + m = p->ainsn.matrioshka_flags & MF_ARM64_MASK; + switch (m) { + case MF_ARM64_SIMUL: + break; + case MF_ARM64_EMUL: + ret = make_trampoline_arm64(p, regs); + if (ret) + disarm_uprobe(p, p->task); + break; + default: + pr_err("ERROR: we are in arm64 mode " + "(!) and check instruction was fail " + "(%x instruction at %p address)!\n", + p->opcode, p->addr); + + disarm_uprobe(p, p->task); + return -EINVAL; + } + + return ret; +} + +static enum dbg_code uprobe_handler_compat(struct pt_regs *regs) +{ + pr_err("ARM and THUMB modes not supported\n"); + return DBG_ERROR; +} + +static enum dbg_code uprobe_handler_arm64(struct pt_regs *regs) +{ + struct uprobe *p; + + p = get_uprobe((void *)regs->pc, current->tgid); + if (p) { + if (!(p->ainsn.matrioshka_flags & MF_SET)) { + p->ainsn.matrioshka_flags |= MF_SET; + if (make_matrioshka_arm64(p, regs)) { + pr_err("no_uprobe live\n"); + goto out_ok; + } + } + + if (!p->pre_handler || !p->pre_handler(p, regs)) + setup_matrioshka_arm64(p, regs); + } else { + return DBG_ERROR; + } + +out_ok: + return DBG_HANDLED; +} + +static enum dbg_code uprobe_handler(struct pt_regs *regs, unsigned int esr) +{ + local_irq_enable(); + + return compat_user_mode(regs) ? + uprobe_handler_compat(regs) : + uprobe_handler_arm64(regs); +} + +static enum dbg_code uprobe_ss_handler(struct pt_regs *regs, unsigned int esr) +{ + struct uprobe *p; + + p = get_current_uprobe(); + if (p) { + regs->pc = (u64)p->addr + 4; + reset_current_uprobe(); + } + + return DBG_HANDLED; +} + + +static struct brk_hook dbg_up_bp = { + .spsr_mask = PSR_MODE_MASK, + .spsr_val = PSR_MODE_EL0t, + .esr_mask = DBG_BRK_ESR_MASK, + .esr_val = DBG_BRK_ESR(BRK_BP), + .fn = uprobe_handler, +}; + +static struct brk_hook dbg_up_ss = { + .spsr_mask = PSR_MODE_MASK, + .spsr_val = PSR_MODE_EL0t, + .esr_mask = DBG_BRK_ESR_MASK, + .esr_val = DBG_BRK_ESR(BRK_PSEUDO_SS), + .fn = uprobe_ss_handler, +}; int swap_arch_init_uprobes(void) { - WARN(1, "not implemented"); /* FIXME: to implement */ + int ret; + + ret = swap_td_raw_reg(&td_raw, sizeof(struct uprobe_ctlblk)); + if (ret) + return ret; + + dbg_brk_hook_reg(&dbg_up_ss); + dbg_brk_hook_reg(&dbg_up_bp); + return 0; } void swap_arch_exit_uprobes(void) { - WARN(1, "not implemented"); /* FIXME: to implement */ + dbg_brk_hook_unreg(&dbg_up_bp); + dbg_brk_hook_unreg(&dbg_up_ss); + swap_td_raw_unreg(&td_raw); } diff --git a/uprobe/arch/arm64/swap-asm/swap_uprobes.h b/uprobe/arch/arm64/swap-asm/swap_uprobes.h index 0ca8347..bfa205b 100644 --- a/uprobe/arch/arm64/swap-asm/swap_uprobes.h +++ b/uprobe/arch/arm64/swap-asm/swap_uprobes.h @@ -23,22 +23,58 @@ */ -#define UPROBES_TRAMP_LEN 1 /* FIXME: to implement */ +#include + + +#define UP_TRAMP_INSN_CNT 2 /* | opcode | ss_bp | */ +#define UPROBES_TRAMP_LEN (UP_TRAMP_INSN_CNT * 4) /* 4 - instruction size */ -struct kprobe; struct uprobe; +struct arch_insn; struct uretprobe; +struct task_struct; struct uretprobe_instance; -typedef u32 uprobe_opcode_t; +typedef unsigned long (uprobes_pstate_check_t)(unsigned long pstate); +typedef unsigned long (uprobes_condition_check_t)(struct uprobe *p, + struct pt_regs *regs); +typedef void (uprobes_prepare_t)(struct uprobe *p, struct arch_insn *asi); +typedef void (uprobes_handler_t)(u32 opcode, long addr, struct pt_regs *regs); + + +enum { + MF_ARM64_SIMUL = 1 << 0, + MF_ARM64_EMUL = 1 << 1, + MF_ARM64_MASK = MF_ARM64_SIMUL | MF_ARM64_EMUL, + + MF_ARM_SIMUL = 1 << 2, + MF_ARM_EMUL = 1 << 3, + MF_ARM_MASK = MF_ARM_SIMUL | MF_ARM_EMUL, + + MF_THUMB_SIMUL = 1 << 4, + MF_THUMB_EMUL = 1 << 5, + MF_THUMB_MASK = MF_THUMB_SIMUL | MF_THUMB_SIMUL, + + MF_SET = 1 << 6 +}; + struct arch_insn { - uprobe_opcode_t *insn; /* FIXME: to implement */ + u32 *insn; + unsigned long matrioshka_flags; + uprobes_pstate_check_t *pstate_cc; + uprobes_condition_check_t *check_condn; + uprobes_prepare_t *prepare; + uprobes_handler_t *handler; + + u32 tramp_arm64[UP_TRAMP_INSN_CNT]; }; +typedef u32 uprobe_opcode_t; + static inline u32 swap_get_urp_float(struct pt_regs *regs) { WARN(1, "not implemented"); /* FIXME: to implement */ @@ -63,16 +99,8 @@ static inline void swap_put_uarg(struct pt_regs *regs, unsigned long n, WARN(1, "not implemented"); /* FIXME: to implement */ } -static inline int arch_prepare_uprobe(struct uprobe *up) -{ - WARN(1, "not implemented"); /* FIXME: to implement */ - return 0; -} - -static inline void arch_remove_uprobe(struct uprobe *up) -{ - WARN(1, "not implemented"); /* FIXME: to implement */ -} +int arch_prepare_uprobe(struct uprobe *p); +void arch_remove_uprobe(struct uprobe *p); static inline int setjmp_upre_handler(struct uprobe *p, struct pt_regs *regs) { @@ -87,17 +115,9 @@ static inline int longjmp_break_uhandler(struct uprobe *p, return 0; } -static inline int arch_arm_uprobe(struct uprobe *p) -{ - WARN(1, "not implemented"); /* FIXME: to implement */ - return 0; -} -static inline void arch_disarm_uprobe(struct uprobe *p, - struct task_struct *task) -{ - WARN(1, "not implemented"); /* FIXME: to implement */ -} +int arch_arm_uprobe(struct uprobe *p); +void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task); static inline unsigned long arch_get_trampoline_addr(struct uprobe *p, diff --git a/uprobe/arch/arm64/swap-asm/uprobes-arm64.c b/uprobe/arch/arm64/swap-asm/uprobes-arm64.c new file mode 100644 index 0000000..0dd96ea --- /dev/null +++ b/uprobe/arch/arm64/swap-asm/uprobes-arm64.c @@ -0,0 +1,306 @@ +/* + * Copyright (C) Samsung Electronics, 2014 + * + * Copied from: arch/arm64/kernel/kprobes-arm64.c + * + * Copyright (C) 2013 Linaro Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + + +#include +#include +#include +#include + +#include "uprobes-decode.h" +#include "uprobes-arm64.h" + + +/* + * condition check functions for uprobes simulation + */ +static unsigned long __check_pstate(struct uprobe *p, struct pt_regs *regs) +{ + struct arch_insn *asi = &p->ainsn; + unsigned long pstate = regs->pstate & 0xffffffff; + + return asi->pstate_cc(pstate); +} + +static unsigned long __check_cbz(struct uprobe *p, struct pt_regs *regs) +{ + return check_cbz((u32)p->opcode, regs); +} + +static unsigned long __check_cbnz(struct uprobe *p, struct pt_regs *regs) +{ + return check_cbnz((u32)p->opcode, regs); +} + +static unsigned long __check_tbz(struct uprobe *p, struct pt_regs *regs) +{ + return check_tbz((u32)p->opcode, regs); +} + +static unsigned long __check_tbnz(struct uprobe *p, struct pt_regs *regs) +{ + return check_tbnz((u32)p->opcode, regs); +} + +/* + * prepare functions for instruction simulation + */ +static void prepare_none(struct uprobe *p, struct arch_insn *asi) +{ +} + +static void prepare_bcond(struct uprobe *p, struct arch_insn *asi) +{ + kprobe_opcode_t insn = p->opcode; + + asi->check_condn = __check_pstate; + asi->pstate_cc = probe_condition_checks[insn & 0xf]; +} + +static void prepare_cbz_cbnz(struct uprobe *p, struct arch_insn *asi) +{ + kprobe_opcode_t insn = p->opcode; + + asi->check_condn = (insn & (1 << 24)) ? __check_cbnz : __check_cbz; +} + +static void prepare_tbz_tbnz(struct uprobe *p, struct arch_insn *asi) +{ + kprobe_opcode_t insn = p->opcode; + + asi->check_condn = (insn & (1 << 24)) ? __check_tbnz : __check_tbz; +} + + +/* Load literal (PC-relative) instructions + * Encoding: xx01 1x00 xxxx xxxx xxxx xxxx xxxx xxxx + * + * opcode[26]: V=0, Load GP registers, simulate them. + * Encoding: xx01 1000 xxxx xxxx xxxx xxxx xxxx xxxx + * opcode[31:30]: op = 00, 01 - LDR literal + * opcode[31:30]: op = 10, - LDRSW literal + * + * 1. V=1 -Load FP/AdvSIMD registers + * Encoding: xx01 1100 xxxx xxxx xxxx xxxx xxxx xxxx + * 2. V=0,opc=11 -PRFM(Prefetch literal) + * Encoding: 1101 1000 xxxx xxxx xxxx xxxx xxxx xxxx + * Reject FP/AdvSIMD literal load & PRFM literal. + */ +static const struct aarch64_decode_item load_literal_subtable[] = { + DECODE_REJECT(0x1C000000, 0x3F000000), + DECODE_REJECT(0xD8000000, 0xFF000000), + DECODE_LITERAL(0x18000000, 0xBF000000, prepare_none, + simulate_ldr_literal), + DECODE_LITERAL(0x98000000, 0xFF000000, prepare_none, + simulate_ldrsw_literal), + DECODE_END, +}; + +/* AArch64 instruction decode table for kprobes: + * The instruction will fall into one of the 3 groups: + * 1. Single stepped out-of-the-line slot. + * -Most instructions fall in this group, those does not + * depend on PC address. + * + * 2. Should be simulated because of PC-relative/literal access. + * -All branching and PC-relative insrtcutions are simulated + * in C code, making use of saved pt_regs + * Catch: SIMD/NEON register context are not saved while + * entering debug exception, so are rejected for now. + * + * 3. Cannot be probed(not safe) so are rejected. + * - Exception generation and exception return instructions + * - Exclusive monitor(LDREX/STREX family) + * + */ +static const struct aarch64_decode_item aarch64_decode_table[] = { + /* + * Data processing - PC relative(literal) addressing: + * Encoding: xxx1 0000 xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_LITERAL(0x10000000, 0x1F000000, prepare_none, + simulate_adr_adrp), + + /* + * Data processing - Add/Substract Immediate: + * Encoding: xxx1 0001 xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_SINGLESTEP(0x11000000, 0x1F000000), + + /* + * Data processing + * Encoding: + * xxx1 0010 0xxx xxxx xxxx xxxx xxxx xxxx (Logical) + * xxx1 0010 1xxx xxxx xxxx xxxx xxxx xxxx (Move wide) + * xxx1 0011 0xxx xxxx xxxx xxxx xxxx xxxx (Bitfield) + * xxx1 0011 1xxx xxxx xxxx xxxx xxxx xxxx (Extract) + */ + DECODE_SINGLESTEP(0x12000000, 0x1E000000), + + /* + * Data processing - SIMD/FP/AdvSIMD/Crypto-AES/SHA + * Encoding: xxx0 111x xxxx xxxx xxxx xxxx xxxx xxxx + * Encoding: xxx1 111x xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_SINGLESTEP(0x0E000000, 0x0E000000), + + /* + * Data processing - Register + * Encoding: xxxx 101x xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_SINGLESTEP(0x0A000000, 0x0E000000), + + /* Branching Instructions + * + * Encoding: + * x001 01xx xxxx xxxx xxxx xxxx xxxx xxxx (uncondtional Branch) + * x011 010x xxxx xxxx xxxx xxxx xxxx xxxx (compare & branch) + * x011 011x xxxx xxxx xxxx xxxx xxxx xxxx (Test & Branch) + * 0101 010x xxxx xxxx xxxx xxxx xxxx xxxx (Conditional, immediate) + * 1101 011x xxxx xxxx xxxx xxxx xxxx xxxx (Unconditional,register) + */ + DECODE_BRANCH(0x14000000, 0x7C000000, prepare_none, + simulate_b_bl), + DECODE_BRANCH(0x34000000, 0x7E000000, prepare_cbz_cbnz, + simulate_cbz_cbnz), + DECODE_BRANCH(0x36000000, 0x7E000000, prepare_tbz_tbnz, + simulate_tbz_tbnz), + DECODE_BRANCH(0x54000000, 0xFE000000, prepare_bcond, + simulate_b_cond), + DECODE_BRANCH(0xD6000000, 0xFE000000, prepare_none, + simulate_br_blr_ret), + + /* System insn: + * Encoding: 1101 0101 00xx xxxx xxxx xxxx xxxx xxxx + * + * Note: MSR immediate (update PSTATE daif) is not safe handling + * within kprobes, rejected. + * + * Don't re-arrange these decode table entries. + */ + DECODE_REJECT(0xD500401F, 0xFFF8F01F), + DECODE_SINGLESTEP(0xD5000000, 0xFFC00000), + + /* Exception Generation: + * Encoding: 1101 0100 xxxx xxxx xxxx xxxx xxxx xxxx + * Instructions: SVC, HVC, SMC, BRK, HLT, DCPS1, DCPS2, DCPS3 + */ + DECODE_REJECT(0xD4000000, 0xFF000000), + + /* + * Load/Store - Exclusive monitor + * Encoding: xx00 1000 xxxx xxxx xxxx xxxx xxxx xxxx + * + * Reject exlusive monitor'ed instructions + */ + DECODE_REJECT(0x08000000, 0x3F000000), + + /* + * Load/Store - PC relative(literal): + * Encoding: xx01 1x00 xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE(0x18000000, 0x3B000000, load_literal_subtable), + + /* + * Load/Store - Register Pair + * Encoding: + * xx10 1x00 0xxx xxxx xxxx xxxx xxxx xxxx + * xx10 1x00 1xxx xxxx xxxx xxxx xxxx xxxx + * xx10 1x01 0xxx xxxx xxxx xxxx xxxx xxxx + * xx10 1x01 1xxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_SINGLESTEP(0x28000000, 0x3A000000), + + /* + * Load/Store - Register + * Encoding: + * xx11 1x00 xx0x xxxx xxxx 00xx xxxx xxxx (unscaled imm) + * xx11 1x00 xx0x xxxx xxxx 01xx xxxx xxxx (imm post-indexed) + * xx11 1x00 xx0x xxxx xxxx 10xx xxxx xxxx (unpriviledged) + * xx11 1x00 xx0x xxxx xxxx 11xx xxxx xxxx (imm pre-indexed) + * + * xx11 1x00 xx10 xxxx xxxx xx10 xxxx xxxx (register offset) + * + * xx11 1x01 xxxx xxxx xxxx xxxx xxxx xxxx (unsigned imm) + */ + DECODE_SINGLESTEP(0x38000000, 0x3B200000), + DECODE_SINGLESTEP(0x38200200, 0x38300300), + DECODE_SINGLESTEP(0x39000000, 0x3B000000), + + /* + * Load/Store - AdvSIMD + * Encoding: + * 0x00 1100 0x00 0000 xxxx xxxx xxxx xxxx (Multiple-structure) + * 0x00 1100 1x0x xxxx xxxx xxxx xxxx xxxx (Multi-struct post-indexed) + * 0x00 1101 0xx0 0000 xxxx xxxx xxxx xxxx (Single-structure)) + * 0x00 1101 1xxx xxxx xxxx xxxx xxxx xxxx (Single-struct post-index) + */ + DECODE_SINGLESTEP(0x0C000000, 0xBFBF0000), + DECODE_SINGLESTEP(0x0C800000, 0xBFA00000), + DECODE_SINGLESTEP(0x0D000000, 0xBF9F0000), + DECODE_SINGLESTEP(0x0D800000, 0xBF800000), + + /* Unallocated: xxx0 0xxx xxxx xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT(0x00000000, 0x18000000), + DECODE_END, +}; + +static int uprobe_decode_insn(u32 insn, struct arch_insn *asi, + const struct aarch64_decode_item *tbl) +{ + unsigned int entry, ret = INSN_REJECTED; + + for (entry = 0; !decode_table_end(tbl[entry]); entry++) { + if (decode_table_hit(tbl[entry], insn)) + break; + } + + switch (decode_get_type(tbl[entry])) { + case DECODE_TYPE_END: + case DECODE_TYPE_REJECT: + default: + ret = INSN_REJECTED; + break; + + case DECODE_TYPE_SINGLESTEP: + ret = INSN_GOOD; + break; + + case DECODE_TYPE_SIMULATE: + asi->prepare = decode_prepare_fn(tbl[entry]); + asi->handler = decode_handler_fn(tbl[entry]); + ret = INSN_GOOD_NO_SLOT; + break; + + case DECODE_TYPE_TABLE: + /* recurse with next level decode table */ + ret = uprobe_decode_insn(insn, asi, + decode_sub_table(tbl[entry])); + }; + + return ret; +} + +/* Return: + * INSN_REJECTED If instruction is one not allowed to kprobe, + * INSN_GOOD If instruction is supported and uses instruction slot, + * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot. + */ +enum uprobe_insn arm64_uprobe_decode_insn(u32 insn, struct arch_insn *asi) +{ + return uprobe_decode_insn(insn, asi, aarch64_decode_table); +} diff --git a/uprobe/arch/arm64/swap-asm/uprobes-arm64.h b/uprobe/arch/arm64/swap-asm/uprobes-arm64.h new file mode 100644 index 0000000..6eed2d6 --- /dev/null +++ b/uprobe/arch/arm64/swap-asm/uprobes-arm64.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) Samsung Electronics, 2014 + * + * Copied from: arch/arm64/kernel/kprobes-arm64.h + * + * Copyright (C) 2013 Linaro Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#ifndef _ARM_UPROBES_ARM64_H +#define _ARM_UPROBES_ARM64_H + + +enum uprobe_insn { + INSN_REJECTED, + INSN_GOOD_NO_SLOT, + INSN_GOOD, +}; + + +enum uprobe_insn arm64_uprobe_decode_insn(u32 insn, struct arch_insn *asi); + + +#endif /* _ARM_UPROBES_ARM64_H */ diff --git a/uprobe/arch/arm64/swap-asm/uprobes-decode.h b/uprobe/arch/arm64/swap-asm/uprobes-decode.h new file mode 100644 index 0000000..8564df2 --- /dev/null +++ b/uprobe/arch/arm64/swap-asm/uprobes-decode.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) Samsung Electronics, 2014 + * + * Copied from: arch/arm64/kernel/probes-decode.h + * + * Copyright (C) 2013 Linaro Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#ifndef _ARM64_KERNEL_UPROBES_DECODE_H +#define _ARM64_KERNEL_UPROBES_DECODE_H + + +#include "swap_uprobes.h" + + +/* + * The following definitions and macros are used to build instruction + * decoding tables. + */ +enum decode_type { + DECODE_TYPE_END, + DECODE_TYPE_SINGLESTEP, + DECODE_TYPE_SIMULATE, + DECODE_TYPE_TABLE, + DECODE_TYPE_REJECT, +}; + +struct aarch64_decode_item; + +struct aarch64_decode_header { + enum decode_type type; + u32 mask; + u32 val; +}; + +struct aarch64_decode_actions { + uprobes_prepare_t *prepare; + uprobes_handler_t *handler; +}; + +struct aarch64_decode_table { + const struct aarch64_decode_item *tbl; +}; + +union aarch64_decode_handler { + struct aarch64_decode_actions actions; + struct aarch64_decode_table table; +}; + +struct aarch64_decode_item { + struct aarch64_decode_header header; + union aarch64_decode_handler decode; +}; + +#define decode_get_type(_entry) ((_entry).header.type) + +#define decode_table_end(_entry) \ + ((_entry).header.type == DECODE_TYPE_END) + +#define decode_table_hit(_entry, insn) \ + ((insn & (_entry).header.mask) == (_entry).header.val) + +#define decode_prepare_fn(_entry) ((_entry).decode.actions.prepare) +#define decode_handler_fn(_entry) ((_entry).decode.actions.handler) +#define decode_sub_table(_entry) ((_entry).decode.table.tbl) + +#define DECODE_ADD_HEADER(_type, _val, _mask) \ + .header = { \ + .type = _type, \ + .mask = _mask, \ + .val = _val, \ + } + +#define DECODE_ADD_ACTION(_prepare, _handler) \ + .decode = { \ + .actions = { \ + .prepare = _prepare, \ + .handler = _handler, \ + } \ + } + +#define DECODE_ADD_TABLE(_table) \ + .decode = { \ + .table = {.tbl = _table} \ + } + +#define DECODE_REJECT(_v, _m) \ + { DECODE_ADD_HEADER(DECODE_TYPE_REJECT, _v, _m) } + +#define DECODE_SINGLESTEP(_v, _m) \ + { DECODE_ADD_HEADER(DECODE_TYPE_SINGLESTEP, _v, _m) } + +#define DECODE_SIMULATE(_v, _m, _p, _h) \ + { DECODE_ADD_HEADER(DECODE_TYPE_SIMULATE, _v, _m), \ + DECODE_ADD_ACTION(_p, _h) } + +#define DECODE_TABLE(_v, _m, _table) \ + { DECODE_ADD_HEADER(DECODE_TYPE_TABLE, _v, _m), \ + DECODE_ADD_TABLE(_table) } + +#define DECODE_LITERAL(_v, _m, _p, _h) DECODE_SIMULATE(_v, _m, _p, _h) +#define DECODE_BRANCH(_v, _m, _p, _h) DECODE_SIMULATE(_v, _m, _p, _h) + +/* should be the last element in decode structure */ +#define DECODE_END { .header = {.type = DECODE_TYPE_END, } } + +#endif /* _ARM64_KERNEL_UPROBES_DECODE_H */ diff --git a/us_manager/helper.c b/us_manager/helper.c index 7b48ccc..fbf81b6 100644 --- a/us_manager/helper.c +++ b/us_manager/helper.c @@ -65,7 +65,9 @@ static int entry_handler_pf(struct kretprobe_instance *ri, struct pt_regs *regs) data->pf_regs = (struct pt_regs *)swap_get_karg(regs, 0); data->save_pc = data->pf_regs->ip; #elif defined(CONFIG_ARM64) - WARN(1, "not implemented"); /* FIXME: to implement */ + data->addr = swap_get_karg(regs, 0); + data->pf_regs = (struct pt_regs *)swap_get_karg(regs, 2); + data->save_pc = data->pf_regs->pc; #else #error "this architecture is not supported" #endif /* CONFIG_arch */ @@ -117,6 +119,9 @@ static int ret_handler_pf(struct kretprobe_instance *ri, struct pt_regs *regs) #elif defined(CONFIG_X86_32) if (data->save_pc != data->pf_regs->ip) return 0; +#elif defined(CONFIG_ARM64) + if (data->save_pc != data->pf_regs->pc) + return 0; #endif /* CONFIG_arch */ /* TODO: check return value */ -- 2.7.4 From c84fd0450b802d6c514fd2e451fd76b121e40908 Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Fri, 1 Jul 2016 21:48:20 +0300 Subject: [PATCH 15/16] ARM64: implement uretprobe Change-Id: I9775576f8901eac71e62356ee0ae56942f1e5406 Signed-off-by: Vyacheslav Cherkashin --- uprobe/arch/arm64/swap-asm/swap_uprobes.c | 84 +++++++++++++++++++++++++++++++ uprobe/arch/arm64/swap-asm/swap_uprobes.h | 18 ++----- 2 files changed, 89 insertions(+), 13 deletions(-) diff --git a/uprobe/arch/arm64/swap-asm/swap_uprobes.c b/uprobe/arch/arm64/swap-asm/swap_uprobes.c index b75cb28..f2e7a9a 100644 --- a/uprobe/arch/arm64/swap-asm/swap_uprobes.c +++ b/uprobe/arch/arm64/swap-asm/swap_uprobes.c @@ -31,8 +31,10 @@ #define BRK_BP 0x45 #define BRK_PSEUDO_SS 0x54 +#define BRK_URP 0x67 #define BRK64_OPCODE_BP MAKE_BRK(BRK_BP) #define BRK64_OPCODE_PSEUDO_SS MAKE_BRK(BRK_PSEUDO_SS) +#define BRK64_OPCODE_URP MAKE_BRK(BRK_URP) struct uprobe_ctlblk { @@ -62,6 +64,72 @@ static void reset_current_uprobe(void) } +int arch_prepare_uretprobe(struct uretprobe_instance *ri, + struct pt_regs *regs) +{ + unsigned long bp_addr = (unsigned long)(ri->rp->up.ainsn.insn + + URP_RET_BREAK_IDX); + + ri->sp = (kprobe_opcode_t *)regs->sp; + + /* replace the return address (regs[30] - lr) */ + ri->ret_addr = (kprobe_opcode_t *)regs->regs[30]; + regs->regs[30] = bp_addr; + + return 0; +} + +void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs) +{ + regs->pc = orig_ret_addr; +} + +static int make_urp_arm64(struct uprobe *p) +{ + u32 *utramp, urp_brk; + + if (p->ainsn.insn == NULL) { + utramp = swap_slot_alloc(p->sm); + if (utramp == NULL) + return -ENOMEM; + + p->ainsn.insn = utramp; + } + + utramp = p->ainsn.insn; + urp_brk = BRK64_OPCODE_URP; + + if (!write_proc_vm_atomic(p->task, + (long)&utramp[URP_RET_BREAK_IDX], + &urp_brk, sizeof(urp_brk))) { + pr_err("%s failed to write memory %p!\n", __func__, utramp); + } + + add_uprobe_table(p); + + return 0; +} + +static enum dbg_code urp_handler(struct pt_regs *regs, unsigned int esr) +{ + struct uprobe *p; + unsigned long pc = regs->pc; + unsigned long insn_addr = pc - sizeof(u32) * URP_RET_BREAK_IDX; + + p = get_uprobe_by_insn_slot((void *)insn_addr, current->tgid, regs); + if (p == NULL) { + pr_err("no_uretprobe: Not one of ours: let " + "kernel handle it %lx\n", pc); + return DBG_ERROR; + } + + local_irq_enable(); + trampoline_uprobe_handler(p, regs); + + return DBG_HANDLED; +} + + int arch_arm_uprobe(struct uprobe *p) { uprobe_opcode_t insn = BRK64_OPCODE_BP; @@ -247,6 +315,12 @@ static enum dbg_code uprobe_handler_arm64(struct pt_regs *regs) pr_err("no_uprobe live\n"); goto out_ok; } + + if (make_urp_arm64(p)) { + disarm_uprobe(p, p->task); + pr_err("no_uretprobe\n"); + goto out_ok; + } } if (!p->pre_handler || !p->pre_handler(p, regs)) @@ -298,6 +372,14 @@ static struct brk_hook dbg_up_ss = { .fn = uprobe_ss_handler, }; +static struct brk_hook dbg_urp_bp = { + .spsr_mask = PSR_MODE_MASK, + .spsr_val = PSR_MODE_EL0t, + .esr_mask = DBG_BRK_ESR_MASK, + .esr_val = DBG_BRK_ESR(BRK_URP), + .fn = urp_handler, +}; + int swap_arch_init_uprobes(void) { @@ -309,12 +391,14 @@ int swap_arch_init_uprobes(void) dbg_brk_hook_reg(&dbg_up_ss); dbg_brk_hook_reg(&dbg_up_bp); + dbg_brk_hook_reg(&dbg_urp_bp); return 0; } void swap_arch_exit_uprobes(void) { + dbg_brk_hook_unreg(&dbg_urp_bp); dbg_brk_hook_unreg(&dbg_up_bp); dbg_brk_hook_unreg(&dbg_up_ss); swap_td_raw_unreg(&td_raw); diff --git a/uprobe/arch/arm64/swap-asm/swap_uprobes.h b/uprobe/arch/arm64/swap-asm/swap_uprobes.h index bfa205b..bca4f84 100644 --- a/uprobe/arch/arm64/swap-asm/swap_uprobes.h +++ b/uprobe/arch/arm64/swap-asm/swap_uprobes.h @@ -26,8 +26,9 @@ #include -#define UP_TRAMP_INSN_CNT 2 /* | opcode | ss_bp | */ +#define UP_TRAMP_INSN_CNT 3 /* | opcode | ss_bp | urp_bp | */ #define UPROBES_TRAMP_LEN (UP_TRAMP_INSN_CNT * 4) /* 4 - instruction size */ +#define URP_RET_BREAK_IDX 2 struct uprobe; @@ -127,18 +128,9 @@ static inline unsigned long arch_get_trampoline_addr(struct uprobe *p, return 0; } -static inline void arch_set_orig_ret_addr(unsigned long orig_ret_addr, - struct pt_regs *regs) -{ - WARN(1, "not implemented"); /* FIXME: to implement */ -} - -static inline int arch_prepare_uretprobe(struct uretprobe_instance *ri, - struct pt_regs *regs) -{ - WARN(1, "not implemented"); /* FIXME: to implement */ - return 0; -} +void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs); +int arch_prepare_uretprobe(struct uretprobe_instance *ri, + struct pt_regs *regs); static inline int arch_opcode_analysis_uretprobe(struct uretprobe *rp) { -- 2.7.4 From 6e93dc0be825e88bd4f724c1ef67e522c97b4bdf Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Fri, 8 Jul 2016 10:47:21 +0300 Subject: [PATCH 16/16] ARM: separate instructions decoding move ARM instruction decoding (kprobe -> probes) move THUMB instruction deconding (uprobe -> probes) Change-Id: Ief70952068b9a607d675ea797b186605e5b8950b Signed-off-by: Vyacheslav Cherkashin --- arch/arm/probes/decode_arm_old.h | 128 +++++ .../swap-asm => arch/arm/probes}/decode_thumb.c | 45 +- .../swap-asm => arch/arm/probes}/decode_thumb.h | 2 +- arch/arm/probes/decode_thumb_old.h | 282 ++++++++++ arch/arm/probes/probes_arm.c | 283 +++++++++++ .../thumb_tramps.h => arch/arm/probes/probes_arm.h | 19 +- arch/arm/probes/probes_thumb.c | 565 +++++++++++++++++++++ arch/arm/probes/probes_thumb.h | 34 ++ arch/arm/probes/tramps_arm.c | 86 ++++ .../arm/probes/tramps_arm.h | 12 +- arch/arm/probes/tramps_thumb.c | 169 ++++++ .../arm/probes/tramps_thumb.h | 12 +- kprobe/Kbuild | 7 +- kprobe/arch/arm/swap-asm/swap_kprobes.c | 275 +--------- kprobe/arch/arm/swap-asm/swap_kprobes.h | 375 -------------- kprobe/arch/arm/swap-asm/trampoline_arm.S | 63 --- uprobe/Kbuild | 6 +- uprobe/arch/arm/swap-asm/swap_uprobes.c | 550 +------------------- uprobe/arch/arm/swap-asm/thumb_tramps.c | 69 --- uprobe/arch/arm/swap-asm/trampoline_thumb.S | 134 ----- 20 files changed, 1641 insertions(+), 1475 deletions(-) create mode 100644 arch/arm/probes/decode_arm_old.h rename {uprobe/arch/arm/swap-asm => arch/arm/probes}/decode_thumb.c (83%) rename {uprobe/arch/arm/swap-asm => arch/arm/probes}/decode_thumb.h (97%) create mode 100644 arch/arm/probes/decode_thumb_old.h create mode 100644 arch/arm/probes/probes_arm.c rename uprobe/arch/arm/swap-asm/thumb_tramps.h => arch/arm/probes/probes_arm.h (64%) create mode 100644 arch/arm/probes/probes_thumb.c create mode 100644 arch/arm/probes/probes_thumb.h create mode 100644 arch/arm/probes/tramps_arm.c rename kprobe/arch/arm/swap-asm/trampoline_arm.h => arch/arm/probes/tramps_arm.h (86%) create mode 100644 arch/arm/probes/tramps_thumb.c rename uprobe/arch/arm/swap-asm/trampoline_thumb.h => arch/arm/probes/tramps_thumb.h (86%) delete mode 100644 kprobe/arch/arm/swap-asm/trampoline_arm.S delete mode 100644 uprobe/arch/arm/swap-asm/thumb_tramps.c delete mode 100644 uprobe/arch/arm/swap-asm/trampoline_thumb.S diff --git a/arch/arm/probes/decode_arm_old.h b/arch/arm/probes/decode_arm_old.h new file mode 100644 index 0000000..ae6b6c8 --- /dev/null +++ b/arch/arm/probes/decode_arm_old.h @@ -0,0 +1,128 @@ +/* + * 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 + * + */ + + +#ifndef _SWAP_ASM_DECODE_ARM_OLD_H +#define _SWAP_ASM_DECODE_ARM_OLD_H + + +#define ARM_INSN_MATCH(name, insn) \ + ((insn & MASK_ARM_INSN_##name) == PTRN_ARM_INSN_##name) +#define ARM_INSN_REG_RN(insn) ((insn & 0x000F0000) >> 16) +#define ARM_INSN_REG_SET_RN(insn, nreg) { insn &= ~0x000F0000; insn |= nreg << 16; } +#define ARM_INSN_REG_RD(insn) ((insn & 0x0000F000) >> 12) +#define ARM_INSN_REG_SET_RD(insn, nreg) { insn &= ~0x0000F000; insn |= nreg << 12; } +#define ARM_INSN_REG_RS(insn) ((insn & 0x00000F00) >> 8) +#define ARM_INSN_REG_SET_RS(insn, nreg) { insn &= ~0x00000F00; insn |= nreg << 8; } +#define ARM_INSN_REG_RM(insn) (insn & 0x0000000F) +#define ARM_INSN_REG_SET_RM(insn, nreg) { insn &= ~0x0000000F; insn |= nreg; } +#define ARM_INSN_REG_MR(insn, nreg) (insn & (1 << nreg)) +#define ARM_INSN_REG_SET_MR(insn, nreg) { insn |= (1 << nreg); } +#define ARM_INSN_REG_CLEAR_MR(insn, nreg) { insn &= ~(1 << nreg); } + + +/* Undefined */ +#define MASK_ARM_INSN_UNDEF 0x0FF00000 +#define PTRN_ARM_INSN_UNDEF 0x03000000 + +/* Architecturally undefined */ +#define MASK_ARM_INSN_AUNDEF 0x0FF000F0 +#define PTRN_ARM_INSN_AUNDEF 0x07F000F0 + +/* Branches */ +#define MASK_ARM_INSN_B 0x0F000000 +#define PTRN_ARM_INSN_B 0x0A000000 + +#define MASK_ARM_INSN_BL 0x0F000000 +#define PTRN_ARM_INSN_BL 0x0B000000 + +#define MASK_ARM_INSN_BLX1 0xFE000000 +#define PTRN_ARM_INSN_BLX1 0xFA000000 + +#define MASK_ARM_INSN_BLX2 0x0FF000F0 +#define PTRN_ARM_INSN_BLX2 0x01200030 + +#define MASK_ARM_INSN_BX 0x0FF000F0 +#define PTRN_ARM_INSN_BX 0x01200010 + +#define MASK_ARM_INSN_BXJ 0x0FF000F0 +#define PTRN_ARM_INSN_BXJ 0x01200020 + +/* Software interrupts */ +#define MASK_ARM_INSN_SWI 0x0F000000 +#define PTRN_ARM_INSN_SWI 0x0F000000 + +/* Break */ +#define MASK_ARM_INSN_BREAK 0xFFF000F0 +#define PTRN_ARM_INSN_BREAK 0xE1200070 +/* A8-56 ARM DDI 046B if cond != ‘1110’ then UNPREDICTABLE; */ + +/* CLZ */ +#define MASK_ARM_INSN_CLZ 0x0FFF0FF0 +#define PTRN_ARM_INSN_CLZ 0x016F0F10 + +/* Data processing immediate shift */ +#define MASK_ARM_INSN_DPIS 0x0E000010 +#define PTRN_ARM_INSN_DPIS 0x00000000 + +/* Data processing register shift */ +#define MASK_ARM_INSN_DPRS 0x0E000090 +#define PTRN_ARM_INSN_DPRS 0x00000010 + +/* Data processing immediate */ +#define MASK_ARM_INSN_DPI 0x0E000000 +#define PTRN_ARM_INSN_DPI 0x02000000 + +/* Load immediate offset */ +#define MASK_ARM_INSN_LIO 0x0E100000 +#define PTRN_ARM_INSN_LIO 0x04100000 + +/* Store immediate offset */ +#define MASK_ARM_INSN_SIO MASK_ARM_INSN_LIO +#define PTRN_ARM_INSN_SIO 0x04000000 + +/* Load register offset */ +#define MASK_ARM_INSN_LRO 0x0E100010 +#define PTRN_ARM_INSN_LRO 0x06100000 + +/* Store register offset */ +#define MASK_ARM_INSN_SRO MASK_ARM_INSN_LRO +#define PTRN_ARM_INSN_SRO 0x06000000 + +/* Load multiple */ +#define MASK_ARM_INSN_LM 0x0E100000 +#define PTRN_ARM_INSN_LM 0x08100000 + +/* Store multiple */ +#define MASK_ARM_INSN_SM MASK_ARM_INSN_LM +#define PTRN_ARM_INSN_SM 0x08000000 + + +/* Coprocessor load/store and double register transfers */ +#define MASK_ARM_INSN_CLS 0x0E000000 +#define PTRN_ARM_INSN_CLS 0x0C000000 + +/* Coprocessor register transfers */ +#define MASK_ARM_INSN_CRT 0x0F000010 +#define PTRN_ARM_INSN_CRT 0x0E000010 + + +#endif /* _SWAP_ASM_DECODE_ARM_OLD_H */ diff --git a/uprobe/arch/arm/swap-asm/decode_thumb.c b/arch/arm/probes/decode_thumb.c similarity index 83% rename from uprobe/arch/arm/swap-asm/decode_thumb.c rename to arch/arm/probes/decode_thumb.c index 9e35adb..3fe96c7 100644 --- a/uprobe/arch/arm/swap-asm/decode_thumb.c +++ b/arch/arm/probes/decode_thumb.c @@ -24,7 +24,7 @@ #include #include #include "decode_thumb.h" -#include "thumb_tramps.h" +#include "tramps_thumb.h" #define GET_BIT(x, n) ((x >> n) & 0x1) @@ -46,6 +46,49 @@ typedef union thumb_insn { typedef int (*decode_handler_t)(thumb_insn_t insn, struct decode_info *info); +static void make_def(void *tramp, unsigned long insn, + unsigned long vaddr, bool t2) +{ + const unsigned long URET_BP = 0xdeff; /* breakpoint for uretprobe */ + 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; +} + +static 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); +} + +static 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; +} + + static bool bad_reg(int n) { return n == 13 || n == 15; diff --git a/uprobe/arch/arm/swap-asm/decode_thumb.h b/arch/arm/probes/decode_thumb.h similarity index 97% rename from uprobe/arch/arm/swap-asm/decode_thumb.h rename to arch/arm/probes/decode_thumb.h index 8c1142a..b33c305 100644 --- a/uprobe/arch/arm/swap-asm/decode_thumb.h +++ b/arch/arm/probes/decode_thumb.h @@ -24,7 +24,7 @@ #define _ARM_DECODE_THUMB_H -#include "swap_uprobes.h" +#include struct decode_info { diff --git a/arch/arm/probes/decode_thumb_old.h b/arch/arm/probes/decode_thumb_old.h new file mode 100644 index 0000000..df1faba --- /dev/null +++ b/arch/arm/probes/decode_thumb_old.h @@ -0,0 +1,282 @@ +/* + * 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 + * + */ + + +#ifndef _SWAP_ASM_DECODE_THUMB_OLD_H +#define _SWAP_ASM_DECODE_THUMB_OLD_H + + +/* == THUMB == */ +#define THUMB_INSN_MATCH(name, insn) \ + (((insn & 0x0000FFFF) & MASK_THUMB_INSN_##name) == \ + PTRN_THUMB_INSN_##name) + + +/* Undefined */ +#define MASK_THUMB_INSN_UNDEF 0xFE00 +#define PTRN_THUMB_INSN_UNDEF 0xDE00 + +/* Branches */ +#define MASK_THUMB_INSN_B1 0xF000 +#define PTRN_THUMB_INSN_B1 0xD000 /* b label */ + +#define MASK_THUMB_INSN_B2 0xF800 +#define PTRN_THUMB_INSN_B2 0xE000 /* b label */ + +#define MASK_THUMB_INSN_CBZ 0xF500 +#define PTRN_THUMB_INSN_CBZ 0xB100 /* CBZ/CBNZ */ + +#define MASK_THUMB_INSN_BLX2 0xFF80 /* blx reg */ +#define PTRN_THUMB_INSN_BLX2 0x4780 + +#define MASK_THUMB_INSN_BX 0xFF80 +#define PTRN_THUMB_INSN_BX 0x4700 + +/* Software interrupts */ +#define MASK_THUMB_INSN_SWI 0xFF00 +#define PTRN_THUMB_INSN_SWI 0xDF00 + +/* Break */ +#define MASK_THUMB_INSN_BREAK 0xFF00 +#define PTRN_THUMB_INSN_BREAK 0xBE00 + +/* Data processing immediate */ +#define MASK_THUMB_INSN_DP 0xFC00 +#define PTRN_THUMB_INSN_DP 0x4000 + +#define MASK_THUMB_INSN_APC 0xF800 +#define PTRN_THUMB_INSN_APC 0xA000 /* ADD Rd, [PC, # * 4] */ + +#define MASK_THUMB_INSN_MOV3 0xFF00 +#define PTRN_THUMB_INSN_MOV3 0x4600 /* MOV Rd, PC */ + +/* Load immediate offset */ +#define MASK_THUMB_INSN_LIO1 0xF800 +#define PTRN_THUMB_INSN_LIO1 0x6800 /* LDR */ + +#define MASK_THUMB_INSN_LIO2 MASK_THUMB_INSN_LIO1 +#define PTRN_THUMB_INSN_LIO2 0x7800 /* LDRB */ + +#define MASK_THUMB_INSN_LIO3 MASK_THUMB_INSN_LIO1 +#define PTRN_THUMB_INSN_LIO3 0x8800 /* LDRH */ + +#define MASK_THUMB_INSN_LIO4 MASK_THUMB_INSN_LIO1 +#define PTRN_THUMB_INSN_LIO4 0x9800 /* LDR SP relative */ + +/* Store immediate offset */ +#define MASK_THUMB_INSN_SIO1 MASK_THUMB_INSN_LIO1 +#define PTRN_THUMB_INSN_SIO1 0x6000 /* STR */ + +#define MASK_THUMB_INSN_SIO2 MASK_THUMB_INSN_LIO1 +#define PTRN_THUMB_INSN_SIO2 0x7000 /* STRB */ + +#define MASK_THUMB_INSN_SIO3 MASK_THUMB_INSN_LIO1 +#define PTRN_THUMB_INSN_SIO3 0x8000 /* STRH */ + +#define MASK_THUMB_INSN_SIO4 MASK_THUMB_INSN_LIO1 +#define PTRN_THUMB_INSN_SIO4 0x9000 /* STR SP relative */ + +/* Load register offset */ +#define MASK_THUMB_INSN_LRO1 0xFE00 +#define PTRN_THUMB_INSN_LRO1 0x5600 /* LDRSB */ + +#define MASK_THUMB_INSN_LRO2 MASK_THUMB_INSN_LRO1 +#define PTRN_THUMB_INSN_LRO2 0x5800 /* LDR */ + +#define MASK_THUMB_INSN_LRO3 0xf800 +#define PTRN_THUMB_INSN_LRO3 0x4800 /* LDR Rd, [PC, # * 4] */ + +#define MASK_THUMB_INSN_LRO4 MASK_THUMB_INSN_LRO1 +#define PTRN_THUMB_INSN_LRO4 0x5A00 /* LDRH */ + +#define MASK_THUMB_INSN_LRO5 MASK_THUMB_INSN_LRO1 +#define PTRN_THUMB_INSN_LRO5 0x5C00 /* LDRB */ + +#define MASK_THUMB_INSN_LRO6 MASK_THUMB_INSN_LRO1 +#define PTRN_THUMB_INSN_LRO6 0x5E00 /* LDRSH */ + +/* Store register offset */ +#define MASK_THUMB_INSN_SRO1 MASK_THUMB_INSN_LRO1 +#define PTRN_THUMB_INSN_SRO1 0x5000 /* STR */ + +#define MASK_THUMB_INSN_SRO2 MASK_THUMB_INSN_LRO1 +#define PTRN_THUMB_INSN_SRO2 0x5200 /* STRH */ + +#define MASK_THUMB_INSN_SRO3 MASK_THUMB_INSN_LRO1 +#define PTRN_THUMB_INSN_SRO3 0x5400 /* STRB */ + + +/* == THUMB2 == */ +#define THUMB2_INSN_MATCH(name, insn) \ + ((insn & MASK_THUMB2_INSN_##name) == PTRN_THUMB2_INSN_##name) + +#define THUMB2_INSN_REG_RT(insn) ((insn & 0xf0000000) >> 28) +#define THUMB2_INSN_REG_RT2(insn) ((insn & 0x0f000000) >> 24) +#define THUMB2_INSN_REG_RN(insn) (insn & 0x0000000f) +#define THUMB2_INSN_REG_RD(insn) ((insn & 0x0f000000) >> 24) +#define THUMB2_INSN_REG_RM(insn) ((insn & 0x000f0000) >> 16) + + +/* Branches */ +#define MASK_THUMB2_INSN_B1 0xD000F800 +#define PTRN_THUMB2_INSN_B1 0x8000F000 + +#define MASK_THUMB2_INSN_B2 0xD000F800 +#define PTRN_THUMB2_INSN_B2 0x9000F000 + +#define MASK_THUMB2_INSN_BL 0xD000F800 +#define PTRN_THUMB2_INSN_BL 0xD000F000 /* bl imm swapped */ + +#define MASK_THUMB2_INSN_BLX1 0xD001F800 +#define PTRN_THUMB2_INSN_BLX1 0xC000F000 + +#define MASK_THUMB2_INSN_BXJ 0xD000FFF0 +#define PTRN_THUMB2_INSN_BXJ 0x8000F3C0 + +/* Data processing register shift */ +#define MASK_THUMB2_INSN_DPRS 0xFFE00000 +#define PTRN_THUMB2_INSN_DPRS 0xEA000000 + +/* Data processing immediate */ +#define MASK_THUMB2_INSN_DPI 0xFBE08000 +#define PTRN_THUMB2_INSN_DPI 0xF2000000 + +#define MASK_THUMB2_INSN_RSBW 0x8000fbe0 +#define PTRN_THUMB2_INSN_RSBW 0x0000f1c0 /* RSB{S}.W Rd,Rn,# */ + +#define MASK_THUMB2_INSN_RORW 0xf0f0ffe0 +#define PTRN_THUMB2_INSN_RORW 0xf000fa60 /* ROR{S}.W Rd, Rn, Rm */ + +#define MASK_THUMB2_INSN_ROR 0x0030ffef +#define PTRN_THUMB2_INSN_ROR 0x0030ea4f /* ROR{S} Rd, Rm, # */ + +#define MASK_THUMB2_INSN_LSLW1 0xf0f0ffe0 +#define PTRN_THUMB2_INSN_LSLW1 0xf000fa00 /* LSL{S}.W Rd, Rn, Rm */ + +#define MASK_THUMB2_INSN_LSLW2 0x0030ffef +#define PTRN_THUMB2_INSN_LSLW2 0x0000ea4f /* LSL{S}.W Rd, Rm, #*/ + +#define MASK_THUMB2_INSN_LSRW1 0xf0f0ffe0 +#define PTRN_THUMB2_INSN_LSRW1 0xf000fa20 /* LSR{S}.W Rd, Rn, Rm */ + +#define MASK_THUMB2_INSN_LSRW2 0x0030ffef +#define PTRN_THUMB2_INSN_LSRW2 0x0010ea4f /* LSR{S}.W Rd, Rm, # */ + +#define MASK_THUMB2_INSN_TEQ1 0x8f00fbf0 +#define PTRN_THUMB2_INSN_TEQ1 0x0f00f090 /* TEQ Rn, # */ + +#define MASK_THUMB2_INSN_TEQ2 0x0f00fff0 +#define PTRN_THUMB2_INSN_TEQ2 0x0f00ea90 /* TEQ Rn, Rm{,} */ + +#define MASK_THUMB2_INSN_TST1 0x8f00fbf0 +#define PTRN_THUMB2_INSN_TST1 0x0f00f010 /* TST Rn, # */ + +#define MASK_THUMB2_INSN_TST2 0x0f00fff0 +#define PTRN_THUMB2_INSN_TST2 0x0f00ea10 /* TST Rn, Rm{,} */ + +/* Load immediate offset */ +#define MASK_THUMB2_INSN_LDRW 0x0000fff0 +#define PTRN_THUMB2_INSN_LDRW 0x0000f850 /* LDR.W Rt, [Rn, #-] */ + +#define MASK_THUMB2_INSN_LDRW1 MASK_THUMB2_INSN_LDRW +#define PTRN_THUMB2_INSN_LDRW1 0x0000f8d0 /* LDR.W Rt, [Rn, #] */ + +#define MASK_THUMB2_INSN_LDRBW MASK_THUMB2_INSN_LDRW +#define PTRN_THUMB2_INSN_LDRBW 0x0000f810 /* LDRB.W Rt, [Rn, #-] */ + +#define MASK_THUMB2_INSN_LDRBW1 MASK_THUMB2_INSN_LDRW +#define PTRN_THUMB2_INSN_LDRBW1 0x0000f890 /* LDRB.W Rt, [Rn, #] */ + +#define MASK_THUMB2_INSN_LDRHW MASK_THUMB2_INSN_LDRW +#define PTRN_THUMB2_INSN_LDRHW 0x0000f830 /* LDRH.W Rt, [Rn, #-] */ + +#define MASK_THUMB2_INSN_LDRHW1 MASK_THUMB2_INSN_LDRW +#define PTRN_THUMB2_INSN_LDRHW1 0x0000f8b0 /* LDRH.W Rt, [Rn, #] */ + +#define MASK_THUMB2_INSN_LDRD 0x0000fed0 +#define PTRN_THUMB2_INSN_LDRD 0x0000e850 /* LDRD Rt, Rt2, [Rn, #-] */ + +#define MASK_THUMB2_INSN_LDRD1 MASK_THUMB2_INSN_LDRD +#define PTRN_THUMB2_INSN_LDRD1 0x0000e8d0 /* LDRD Rt, Rt2, [Rn, #] */ + +#define MASK_THUMB2_INSN_LDRWL 0x0fc0fff0 +#define PTRN_THUMB2_INSN_LDRWL 0x0000f850 /* LDR.W Rt, [Rn,Rm,LSL #] */ + +#define MASK_THUMB2_INSN_LDREX 0x0f00ffff +#define PTRN_THUMB2_INSN_LDREX 0x0f00e85f /* LDREX Rt, [PC, #] */ + +#define MASK_THUMB2_INSN_MUL 0xf0f0fff0 +#define PTRN_THUMB2_INSN_MUL 0xf000fb00 /* MUL Rd, Rn, Rm */ + +#define MASK_THUMB2_INSN_DP 0x0000ff00 +#define PTRN_THUMB2_INSN_DP 0x0000eb00 /* ADD/SUB/SBC/...Rd,Rn,Rm{,} */ + +/* Store immediate offset */ +#define MASK_THUMB2_INSN_STRW 0x0fc0fff0 +#define PTRN_THUMB2_INSN_STRW 0x0000f840 /* STR.W Rt,[Rn,Rm,{LSL #}] */ + +#define MASK_THUMB2_INSN_STRW1 0x0000fff0 +#define PTRN_THUMB2_INSN_STRW1 0x0000f8c0 /* STR.W Rt, [Rn, #imm12] + * STR.W Rt, [PC, #imm12] shall be + * skipped, because it hangs + * on Tegra. WTF */ + +#define MASK_THUMB2_INSN_STRHW MASK_THUMB2_INSN_STRW +#define PTRN_THUMB2_INSN_STRHW 0x0000f820 /* STRH.W Rt,[Rn,Rm,{LSL #}] */ + +#define MASK_THUMB2_INSN_STRHW1 0x0000fff0 +#define PTRN_THUMB2_INSN_STRHW1 0x0000f8a0 /* STRH.W Rt, [Rn, #] */ + +#define MASK_THUMB2_INSN_STRHT 0x0f00fff0 /* strht r1, [pc, #imm] illegal + * instruction on Tegra. WTF */ +#define PTRN_THUMB2_INSN_STRHT 0x0e00f820 /* STRHT Rt, [Rn, #] */ + +#define MASK_THUMB2_INSN_STRT 0x0f00fff0 +#define PTRN_THUMB2_INSN_STRT 0x0e00f840 /* STRT Rt, [Rn, #] */ + +#define MASK_THUMB2_INSN_STRBW MASK_THUMB2_INSN_STRW +#define PTRN_THUMB2_INSN_STRBW 0x0000f800 /* STRB.W Rt,[Rn,Rm,{LSL #}] */ + +#define MASK_THUMB2_INSN_STRBW1 0x0000fff0 +#define PTRN_THUMB2_INSN_STRBW1 0x0000f880 /* STRB.W Rt, [Rn, #] + * STRB.W Rt, [PC, #imm12] shall be + * skipped, because it hangs + * on Tegra. WTF */ + +#define MASK_THUMB2_INSN_STRBT 0x0f00fff0 +#define PTRN_THUMB2_INSN_STRBT 0x0e00f800 /* STRBT Rt, [Rn, #}] */ + +#define MASK_THUMB2_INSN_STRD 0x0000fe50 +#define PTRN_THUMB2_INSN_STRD 0x0000e840 /* STR{D,EX,EXB,EXH,EXD} Rt, Rt2, [Rn, #] */ + +/* Load register offset */ +#define MASK_THUMB2_INSN_ADR 0x8000fa1f +#define PTRN_THUMB2_INSN_ADR 0x0000f20f + +/* Load multiple */ +#define MASK_THUMB2_INSN_LDMIA 0x8000ffd0 +#define PTRN_THUMB2_INSN_LDMIA 0x8000e890 /* LDMIA(.W) Rn(!),{Rx-PC} */ + +#define MASK_THUMB2_INSN_LDMDB 0x8000ffd0 +#define PTRN_THUMB2_INSN_LDMDB 0x8000e910 /* LDMDB(.W) Rn(!), {Rx-PC} */ + + +#endif /* _SWAP_ASM_DECODE_THUMB_OLD_H */ diff --git a/arch/arm/probes/probes_arm.c b/arch/arm/probes/probes_arm.c new file mode 100644 index 0000000..d310ddb --- /dev/null +++ b/arch/arm/probes/probes_arm.c @@ -0,0 +1,283 @@ +/* + * 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 + * + */ + + +#include +#include +#include /* nedded for printk.h */ +#include /* needed for printk.h */ +#include +#include +#include "tramps_arm.h" +#include "decode_arm_old.h" + + +#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit))))) +#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25) + + +static unsigned long get_addr_b(unsigned long insn, unsigned long addr) +{ + /* real position less then PC by 8 */ + return ((long)addr + 8 + branch_displacement(insn)); +} + +static int prep_pc_dep_insn_execbuf(unsigned long *insns, + unsigned long insn, int uregs) +{ + int i; + + if (uregs & 0x10) { + int reg_mask = 0x1; + /* search in reg list */ + for (i = 0; i < 13; i++, reg_mask <<= 1) { + if (!(insn & reg_mask)) + break; + } + } else { + for (i = 0; i < 13; i++) { + if ((uregs & 0x1) && (ARM_INSN_REG_RN(insn) == i)) + continue; + if ((uregs & 0x2) && (ARM_INSN_REG_RD(insn) == i)) + continue; + if ((uregs & 0x4) && (ARM_INSN_REG_RS(insn) == i)) + continue; + if ((uregs & 0x8) && (ARM_INSN_REG_RM(insn) == i)) + continue; + break; + } + } + + if (i == 13) { + pr_err("there are no free register %x in insn %lx!", + uregs, insn); + return -EINVAL; + } + + /* set register to save */ + ARM_INSN_REG_SET_RD(insns[0], i); + /* set register to load address to */ + ARM_INSN_REG_SET_RD(insns[1], i); + /* set instruction to execute and patch it */ + if (uregs & 0x10) { + ARM_INSN_REG_CLEAR_MR(insn, 15); + ARM_INSN_REG_SET_MR(insn, i); + } else { + if ((uregs & 0x1) && (ARM_INSN_REG_RN(insn) == 15)) + ARM_INSN_REG_SET_RN(insn, i); + if ((uregs & 0x2) && (ARM_INSN_REG_RD(insn) == 15)) + ARM_INSN_REG_SET_RD(insn, i); + if ((uregs & 0x4) && (ARM_INSN_REG_RS(insn) == 15)) + ARM_INSN_REG_SET_RS(insn, i); + if ((uregs & 0x8) && (ARM_INSN_REG_RM(insn) == 15)) + ARM_INSN_REG_SET_RM(insn, i); + } + + insns[UPROBES_TRAMP_INSN_IDX] = insn; + /* set register to restore */ + ARM_INSN_REG_SET_RD(insns[3], i); + + return 0; +} + +static int arch_check_insn_arm(unsigned long insn) +{ + /* check instructions that can change PC by nature */ + if ( + /* ARM_INSN_MATCH(UNDEF, insn) || */ + ARM_INSN_MATCH(AUNDEF, insn) || + ARM_INSN_MATCH(SWI, insn) || + ARM_INSN_MATCH(BREAK, insn) || + ARM_INSN_MATCH(BXJ, insn)) { + goto bad_insn; +#ifndef CONFIG_CPU_V7 + /* check instructions that can write result to PC */ + } else if ((ARM_INSN_MATCH(DPIS, insn) || + ARM_INSN_MATCH(DPRS, insn) || + ARM_INSN_MATCH(DPI, insn) || + ARM_INSN_MATCH(LIO, insn) || + ARM_INSN_MATCH(LRO, insn)) && + (ARM_INSN_REG_RD(insn) == 15)) { + goto bad_insn; +#endif /* CONFIG_CPU_V7 */ + /* check special instruction loads store multiple registers */ + } else if ((ARM_INSN_MATCH(LM, insn) || ARM_INSN_MATCH(SM, insn)) && + /* store PC or load to PC */ + (ARM_INSN_REG_MR(insn, 15) || + /* store/load with PC update */ + ((ARM_INSN_REG_RN(insn) == 15) && (insn & 0x200000)))) { + goto bad_insn; + } + + return 0; + +bad_insn: + return -EFAULT; +} + +static int make_branch_tarmpoline(unsigned long addr, unsigned long insn, + unsigned long *tramp) +{ + int ok = 0; + + /* B */ + if (ARM_INSN_MATCH(B, insn) && + !ARM_INSN_MATCH(BLX1, insn)) { + /* B check can be false positive on BLX1 instruction */ + memcpy(tramp, b_cond_insn_execbuf, KPROBES_TRAMP_LEN); + tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; + tramp[0] |= insn & 0xf0000000; + tramp[6] = get_addr_b(insn, addr); + tramp[7] = addr + 4; + ok = 1; + /* BX, BLX (Rm) */ + } else if (ARM_INSN_MATCH(BX, insn) || + ARM_INSN_MATCH(BLX2, insn)) { + memcpy(tramp, b_r_insn_execbuf, KPROBES_TRAMP_LEN); + tramp[0] = insn; + tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; + tramp[7] = addr + 4; + ok = 1; + /* BL, BLX (Off) */ + } else if (ARM_INSN_MATCH(BLX1, insn)) { + memcpy(tramp, blx_off_insn_execbuf, KPROBES_TRAMP_LEN); + tramp[0] |= 0xe0000000; + tramp[1] |= 0xe0000000; + tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; + tramp[6] = get_addr_b(insn, addr) + + 2 * (insn & 01000000) + 1; /* jump to thumb */ + tramp[7] = addr + 4; + ok = 1; + /* BL */ + } else if (ARM_INSN_MATCH(BL, insn)) { + memcpy(tramp, blx_off_insn_execbuf, KPROBES_TRAMP_LEN); + tramp[0] |= insn & 0xf0000000; + tramp[1] |= insn & 0xf0000000; + tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; + tramp[6] = get_addr_b(insn, addr); + tramp[7] = addr + 4; + ok = 1; + } + + return ok; +} + +/** + * @brief Creates ARM trampoline. + * + * @param addr Probe address. + * @param insn Instuction at this address. + * @param tramp Pointer to memory for trampoline. + * @return 0 on success, error code on error. + */ +int make_trampoline_arm(unsigned long addr, unsigned long insn, + unsigned long *tramp) +{ + int ret, uregs, pc_dep; + + if (addr & 0x03) { + pr_err("Error in %s at %d: attempt to register uprobe " + "at an unaligned address\n", __FILE__, __LINE__); + return -EINVAL; + } + + ret = arch_check_insn_arm(insn); + if (ret) + return ret; + + if (make_branch_tarmpoline(addr, insn, tramp)) + return 0; + + uregs = pc_dep = 0; + /* Rm */ + if (ARM_INSN_MATCH(CLZ, insn)) { + uregs = 0xa; + if (ARM_INSN_REG_RM(insn) == 15) + pc_dep = 1; + /* Rn, Rm ,Rd */ + } else if (ARM_INSN_MATCH(DPIS, insn) || ARM_INSN_MATCH(LRO, insn) || + ARM_INSN_MATCH(SRO, insn)) { + uregs = 0xb; + if ((ARM_INSN_REG_RN(insn) == 15) || + (ARM_INSN_REG_RM(insn) == 15) || + (ARM_INSN_MATCH(SRO, insn) && + (ARM_INSN_REG_RD(insn) == 15))) { + pc_dep = 1; + } + /* Rn ,Rd */ + } else if (ARM_INSN_MATCH(DPI, insn) || ARM_INSN_MATCH(LIO, insn) || + ARM_INSN_MATCH(SIO, insn)) { + uregs = 0x3; + if ((ARM_INSN_REG_RN(insn) == 15) || + (ARM_INSN_MATCH(SIO, insn) && + (ARM_INSN_REG_RD(insn) == 15))) { + pc_dep = 1; + } + /* Rn, Rm, Rs */ + } else if (ARM_INSN_MATCH(DPRS, insn)) { + uregs = 0xd; + if ((ARM_INSN_REG_RN(insn) == 15) || + (ARM_INSN_REG_RM(insn) == 15) || + (ARM_INSN_REG_RS(insn) == 15)) { + pc_dep = 1; + } + /* register list */ + } else if (ARM_INSN_MATCH(SM, insn)) { + uregs = 0x10; + if (ARM_INSN_REG_MR(insn, 15)) + pc_dep = 1; + } + + /* check instructions that can write result to SP and uses PC */ + if (pc_dep && (ARM_INSN_REG_RD(insn) == 13)) { + pr_err("Error in %s at %d: instruction check failed (arm)\n", + __FILE__, __LINE__); + return -EFAULT; + } + + if (unlikely(uregs && pc_dep)) { + memcpy(tramp, pc_dep_insn_execbuf, KPROBES_TRAMP_LEN); + if (prep_pc_dep_insn_execbuf(tramp, insn, uregs) != 0) { + pr_err("Error in %s at %d: failed " + "to prepare exec buffer for insn %lx!", + __FILE__, __LINE__, insn); + return -EINVAL; + } + + tramp[6] = addr + 8; + } else { + memcpy(tramp, gen_insn_execbuf, KPROBES_TRAMP_LEN); + tramp[KPROBES_TRAMP_INSN_IDX] = insn; + } + + /* TODO: remove for kprobe */ + tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; + tramp[7] = addr + 4; + + return 0; +} + +int noret_arm(u32 opcode) +{ + return !!(ARM_INSN_MATCH(BL, opcode) || + ARM_INSN_MATCH(BLX1, opcode) || + ARM_INSN_MATCH(BLX2, opcode)); +} diff --git a/uprobe/arch/arm/swap-asm/thumb_tramps.h b/arch/arm/probes/probes_arm.h similarity index 64% rename from uprobe/arch/arm/swap-asm/thumb_tramps.h rename to arch/arm/probes/probes_arm.h index 0b45415..19f74df 100644 --- a/uprobe/arch/arm/swap-asm/thumb_tramps.h +++ b/arch/arm/probes/probes_arm.h @@ -13,24 +13,21 @@ * 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 + * Copyright (C) Samsung Electronics, 2016 * - * 2015 Vyacheslav Cherkashin + * 2016 Vyacheslav Cherkashin * */ -#ifndef _ARM_THUMB_TRAMPS_H -#define _ARM_THUMB_TRAMPS_H +#ifndef _SWAP_ASM_PROBES_ARM_H +#define _SWAP_ASM_PROBES_ARM_H -#include +int make_trampoline_arm(unsigned long addr, unsigned long insn, + unsigned long *tramp); +int noret_arm(u32 opcode); -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 */ +#endif /* _SWAP_ASM_PROBES_ARM_H */ diff --git a/arch/arm/probes/probes_thumb.c b/arch/arm/probes/probes_thumb.c new file mode 100644 index 0000000..b6cd77c --- /dev/null +++ b/arch/arm/probes/probes_thumb.c @@ -0,0 +1,565 @@ +/* + * 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 + * + */ + + +#include "tramps_thumb.h" +#include "decode_thumb.h" +#include "decode_thumb_old.h" + + +static inline long branch_t16_dest(unsigned long insn, unsigned int insn_addr) +{ + long offset = insn & 0x3ff; + offset -= insn & 0x400; + return insn_addr + 4 + offset * 2; +} + +static inline long branch_cond_t16_dest(unsigned long insn, + unsigned int insn_addr) +{ + long offset = insn & 0x7f; + offset -= insn & 0x80; + return insn_addr + 4 + offset * 2; +} + +static inline long branch_t32_dest(unsigned long insn, unsigned int insn_addr) +{ + unsigned int poff = insn & 0x3ff; + unsigned int offset = (insn & 0x07fe0000) >> 17; + + poff -= (insn & 0x400); + + if (insn & (1 << 12)) + return insn_addr + 4 + (poff << 12) + offset * 4; + + return (insn_addr + 4 + (poff << 12) + offset * 4) & ~3; +} + +static inline long cbz_t16_dest(unsigned long insn, unsigned int insn_addr) +{ + unsigned int i = (insn & 0x200) >> 3; + unsigned int offset = (insn & 0xf8) >> 2; + return insn_addr + 4 + i + offset; +} + +/* is instruction Thumb2 and NOT a branch, etc... */ +static int is_thumb2(unsigned long insn) +{ + return ((insn & 0xf800) == 0xe800 || + (insn & 0xf800) == 0xf000 || + (insn & 0xf800) == 0xf800); +} + +static int prep_pc_dep_insn_execbuf_thumb(unsigned long *insns, + unsigned long insn, int uregs) +{ + unsigned char mreg = 0; + unsigned char reg = 0; + + if (THUMB_INSN_MATCH(APC, insn) || + THUMB_INSN_MATCH(LRO3, insn)) { + reg = ((insn & 0xffff) & uregs) >> 8; + } else if (THUMB_INSN_MATCH(MOV3, insn)) { + if (((((unsigned char)insn) & 0xff) >> 3) == 15) + reg = (insn & 0xffff) & uregs; + else + return 0; + } else if (THUMB2_INSN_MATCH(ADR, insn)) { + reg = ((insn >> 16) & uregs) >> 8; + if (reg == 15) + return 0; + } else if (THUMB2_INSN_MATCH(LDRW, insn) || + THUMB2_INSN_MATCH(LDRW1, insn) || + THUMB2_INSN_MATCH(LDRHW, insn) || + THUMB2_INSN_MATCH(LDRHW1, insn) || + THUMB2_INSN_MATCH(LDRWL, insn)) { + reg = ((insn >> 16) & uregs) >> 12; + if (reg == 15) + return 0; + /* + * LDRB.W PC, [PC, #immed] => PLD [PC, #immed], so Rt == PC is skipped + */ + } else if (THUMB2_INSN_MATCH(LDRBW, insn) || + THUMB2_INSN_MATCH(LDRBW1, insn) || + THUMB2_INSN_MATCH(LDREX, insn)) { + reg = ((insn >> 16) & uregs) >> 12; + } else if (THUMB2_INSN_MATCH(DP, insn)) { + reg = ((insn >> 16) & uregs) >> 12; + if (reg == 15) + return 0; + } else if (THUMB2_INSN_MATCH(RSBW, insn)) { + reg = ((insn >> 12) & uregs) >> 8; + if (reg == 15) + return 0; + } else if (THUMB2_INSN_MATCH(RORW, insn)) { + reg = ((insn >> 12) & uregs) >> 8; + if (reg == 15) + return 0; + } else if (THUMB2_INSN_MATCH(ROR, insn) || + THUMB2_INSN_MATCH(LSLW1, insn) || + THUMB2_INSN_MATCH(LSLW2, insn) || + THUMB2_INSN_MATCH(LSRW1, insn) || + THUMB2_INSN_MATCH(LSRW2, insn)) { + reg = ((insn >> 12) & uregs) >> 8; + if (reg == 15) + return 0; + } else if (THUMB2_INSN_MATCH(TEQ1, insn) || + THUMB2_INSN_MATCH(TST1, insn)) { + reg = 15; + } else if (THUMB2_INSN_MATCH(TEQ2, insn) || + THUMB2_INSN_MATCH(TST2, insn)) { + reg = THUMB2_INSN_REG_RM(insn); + } + + if ((THUMB2_INSN_MATCH(STRW, insn) || + THUMB2_INSN_MATCH(STRBW, insn) || + THUMB2_INSN_MATCH(STRD, insn) || + THUMB2_INSN_MATCH(STRHT, insn) || + THUMB2_INSN_MATCH(STRT, insn) || + THUMB2_INSN_MATCH(STRHW1, insn) || + THUMB2_INSN_MATCH(STRHW, insn)) && + THUMB2_INSN_REG_RT(insn) == 15) { + reg = THUMB2_INSN_REG_RT(insn); + } + + if (reg == 6 || reg == 7) { + *((unsigned short *)insns + 0) = + (*((unsigned short *)insns + 0) & 0x00ff) | + ((1 << mreg) | (1 << (mreg + 1))); + *((unsigned short *)insns + 1) = + (*((unsigned short *)insns + 1) & 0xf8ff) | (mreg << 8); + *((unsigned short *)insns + 2) = + (*((unsigned short *)insns + 2) & 0xfff8) | (mreg + 1); + *((unsigned short *)insns + 3) = + (*((unsigned short *)insns + 3) & 0xffc7) | (mreg << 3); + *((unsigned short *)insns + 7) = + (*((unsigned short *)insns + 7) & 0xf8ff) | (mreg << 8); + *((unsigned short *)insns + 8) = + (*((unsigned short *)insns + 8) & 0xffc7) | (mreg << 3); + *((unsigned short *)insns + 9) = + (*((unsigned short *)insns + 9) & 0xffc7) | + ((mreg + 1) << 3); + *((unsigned short *)insns + 10) = + (*((unsigned short *)insns + 10) & 0x00ff) | + ((1 << mreg) | (1 << (mreg + 1))); + } + + if (THUMB_INSN_MATCH(APC, insn)) { + /* ADD Rd, PC, #immed_8*4 -> ADD Rd, SP, #immed_8*4 */ + *((unsigned short *)insns + 4) = ((insn & 0xffff) | 0x800); + } else if (THUMB_INSN_MATCH(LRO3, insn)) { + /* LDR Rd, [PC, #immed_8*4] -> + * LDR Rd, [SP, #immed_8*4] */ + *((unsigned short *)insns + 4) = + ((insn & 0xffff) + 0x5000); + } else if (THUMB_INSN_MATCH(MOV3, insn)) { + /* MOV Rd, PC -> MOV Rd, SP */ + *((unsigned short *)insns + 4) = + ((insn & 0xffff) ^ 0x10); + } else if (THUMB2_INSN_MATCH(ADR, insn)) { + /* ADDW Rd,PC,#imm -> ADDW Rd,SP,#imm */ + insns[2] = (insn & 0xfffffff0) | 0x0d; + } else if (THUMB2_INSN_MATCH(LDRW, insn) || + THUMB2_INSN_MATCH(LDRBW, insn) || + THUMB2_INSN_MATCH(LDRHW, insn)) { + /* LDR.W Rt, [PC, #-] -> + * LDR.W Rt, [SP, #-] + * !!!!!!!!!!!!!!!!!!!!!!!! + * !!! imm_12 vs. imm_8 !!! + * !!!!!!!!!!!!!!!!!!!!!!!! */ + insns[2] = (insn & 0xf0fffff0) | 0x0c00000d; + } else if (THUMB2_INSN_MATCH(LDRW1, insn) || + THUMB2_INSN_MATCH(LDRBW1, insn) || + THUMB2_INSN_MATCH(LDRHW1, insn) || + THUMB2_INSN_MATCH(LDRD, insn) || + THUMB2_INSN_MATCH(LDRD1, insn) || + THUMB2_INSN_MATCH(LDREX, insn)) { + /* LDRx.W Rt, [PC, #+] -> + * LDRx.W Rt, [SP, #+] + (+/-imm_8 for LDRD Rt, Rt2, [PC, #] */ + insns[2] = (insn & 0xfffffff0) | 0xd; + } else if (THUMB2_INSN_MATCH(MUL, insn)) { + /* MUL Rd, Rn, SP */ + insns[2] = (insn & 0xfff0ffff) | 0x000d0000; + } else if (THUMB2_INSN_MATCH(DP, insn)) { + if (THUMB2_INSN_REG_RM(insn) == 15) + /* DP Rd, Rn, PC */ + insns[2] = (insn & 0xfff0ffff) | 0x000d0000; + else if (THUMB2_INSN_REG_RN(insn) == 15) + /* DP Rd, PC, Rm */ + insns[2] = (insn & 0xfffffff0) | 0xd; + } else if (THUMB2_INSN_MATCH(LDRWL, insn)) { + /* LDRx.W Rt, [PC, #] -> + * LDRx.W Rt, [SP, #+] + * (+/-imm_8 for LDRD Rt, Rt2, [PC, #] */ + insns[2] = (insn & 0xfffffff0) | 0xd; + } else if (THUMB2_INSN_MATCH(RSBW, insn)) { + /* RSB{S}.W Rd, PC, # -> RSB{S}.W Rd, SP, # */ + insns[2] = (insn & 0xfffffff0) | 0xd; + } else if (THUMB2_INSN_MATCH(RORW, insn) || + THUMB2_INSN_MATCH(LSLW1, insn) || + THUMB2_INSN_MATCH(LSRW1, insn)) { + if ((THUMB2_INSN_REG_RM(insn) == 15) && + (THUMB2_INSN_REG_RN(insn) == 15)) + /* ROR.W Rd, PC, PC */ + insns[2] = (insn & 0xfffdfffd); + else if (THUMB2_INSN_REG_RM(insn) == 15) + /* ROR.W Rd, Rn, PC */ + insns[2] = (insn & 0xfff0ffff) | 0xd0000; + else if (THUMB2_INSN_REG_RN(insn) == 15) + /* ROR.W Rd, PC, Rm */ + insns[2] = (insn & 0xfffffff0) | 0xd; + } else if (THUMB2_INSN_MATCH(ROR, insn) || + THUMB2_INSN_MATCH(LSLW2, insn) || + THUMB2_INSN_MATCH(LSRW2, insn)) { + /* ROR{S} Rd, PC, # -> ROR{S} Rd, SP, # */ + insns[2] = (insn & 0xfff0ffff) | 0xd0000; + } + + if (THUMB2_INSN_MATCH(STRW, insn) || + THUMB2_INSN_MATCH(STRBW, insn)) { + /* STRx.W Rt, [Rn, SP] */ + insns[2] = (insn & 0xfff0ffff) | 0x000d0000; + } else if (THUMB2_INSN_MATCH(STRD, insn) || + THUMB2_INSN_MATCH(STRHT, insn) || + THUMB2_INSN_MATCH(STRT, insn) || + THUMB2_INSN_MATCH(STRHW1, insn)) { + if (THUMB2_INSN_REG_RN(insn) == 15) + /* STRD/T/HT{.W} Rt, [SP, ...] */ + insns[2] = (insn & 0xfffffff0) | 0xd; + else + insns[2] = insn; + } else if (THUMB2_INSN_MATCH(STRHW, insn) && + (THUMB2_INSN_REG_RN(insn) == 15)) { + if (THUMB2_INSN_REG_RN(insn) == 15) + /* STRH.W Rt, [SP, #-] */ + insns[2] = (insn & 0xf0fffff0) | 0x0c00000d; + else + insns[2] = insn; + } + + /* STRx PC, xxx */ + if ((reg == 15) && (THUMB2_INSN_MATCH(STRW, insn) || + THUMB2_INSN_MATCH(STRBW, insn) || + THUMB2_INSN_MATCH(STRD, insn) || + THUMB2_INSN_MATCH(STRHT, insn) || + THUMB2_INSN_MATCH(STRT, insn) || + THUMB2_INSN_MATCH(STRHW1, insn) || + THUMB2_INSN_MATCH(STRHW, insn))) { + insns[2] = (insns[2] & 0x0fffffff) | 0xd0000000; + } + + if (THUMB2_INSN_MATCH(TEQ1, insn) || + THUMB2_INSN_MATCH(TST1, insn)) { + /* TEQ SP, # */ + insns[2] = (insn & 0xfffffff0) | 0xd; + } else if (THUMB2_INSN_MATCH(TEQ2, insn) || + THUMB2_INSN_MATCH(TST2, insn)) { + if ((THUMB2_INSN_REG_RN(insn) == 15) && + (THUMB2_INSN_REG_RM(insn) == 15)) + /* TEQ/TST PC, PC */ + insns[2] = (insn & 0xfffdfffd); + else if (THUMB2_INSN_REG_RM(insn) == 15) + /* TEQ/TST Rn, PC */ + insns[2] = (insn & 0xfff0ffff) | 0xd0000; + else if (THUMB2_INSN_REG_RN(insn) == 15) + /* TEQ/TST PC, Rm */ + insns[2] = (insn & 0xfffffff0) | 0xd; + } + + return 0; +} + +static int arch_check_insn_thumb(unsigned long insn) +{ + int ret = 0; + + /* check instructions that can change PC */ + if (THUMB_INSN_MATCH(UNDEF, insn) || + THUMB2_INSN_MATCH(BLX1, insn) || + THUMB2_INSN_MATCH(BL, insn) || + THUMB_INSN_MATCH(SWI, insn) || + THUMB_INSN_MATCH(BREAK, insn) || + THUMB2_INSN_MATCH(B1, insn) || + THUMB2_INSN_MATCH(B2, insn) || + THUMB2_INSN_MATCH(BXJ, insn) || + (THUMB2_INSN_MATCH(ADR, insn) && + THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(LDRW, insn) && THUMB2_INSN_REG_RT(insn) == 15) || + (THUMB2_INSN_MATCH(LDRW1, insn) && + THUMB2_INSN_REG_RT(insn) == 15) || + (THUMB2_INSN_MATCH(LDRHW, insn) && + THUMB2_INSN_REG_RT(insn) == 15) || + (THUMB2_INSN_MATCH(LDRHW1, insn) && + THUMB2_INSN_REG_RT(insn) == 15) || + (THUMB2_INSN_MATCH(LDRWL, insn) && + THUMB2_INSN_REG_RT(insn) == 15) || + THUMB2_INSN_MATCH(LDMIA, insn) || + THUMB2_INSN_MATCH(LDMDB, insn) || + (THUMB2_INSN_MATCH(DP, insn) && THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(RSBW, insn) && THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(RORW, insn) && THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(ROR, insn) && THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(LSLW1, insn) && + THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(LSLW2, insn) && + THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(LSRW1, insn) && + THUMB2_INSN_REG_RD(insn) == 15) || + (THUMB2_INSN_MATCH(LSRW2, insn) && + THUMB2_INSN_REG_RD(insn) == 15) || + /* skip PC, #-imm12 -> SP, #-imm8 and Tegra-hanging instructions */ + (THUMB2_INSN_MATCH(STRW1, insn) && + THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_MATCH(STRBW1, insn) && + THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_MATCH(STRHW1, insn) && + THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_MATCH(STRW, insn) && THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_MATCH(STRHW, insn) && + THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_MATCH(LDRW, insn) && THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_MATCH(LDRBW, insn) && + THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_MATCH(LDRHW, insn) && + THUMB2_INSN_REG_RN(insn) == 15) || + /* skip STRDx/LDRDx Rt, Rt2, [Rd, ...] */ + (THUMB2_INSN_MATCH(LDRD, insn) || THUMB2_INSN_MATCH(LDRD1, insn) || + THUMB2_INSN_MATCH(STRD, insn))) { + ret = -EFAULT; + } + + return ret; +} + +static int do_make_trampoline_thumb(unsigned long vaddr, unsigned long insn, + unsigned long *tramp, size_t tramp_len) +{ + int ret; + int uregs = 0; + int pc_dep = 0; + unsigned int addr; + + ret = arch_check_insn_thumb(insn); + if (ret) + return ret; + + if (THUMB_INSN_MATCH(APC, insn) || THUMB_INSN_MATCH(LRO3, insn)) { + uregs = 0x0700; /* 8-10 */ + pc_dep = 1; + } else if (THUMB_INSN_MATCH(MOV3, insn) && + (((((unsigned char)insn) & 0xff) >> 3) == 15)) { + /* MOV Rd, PC */ + uregs = 0x07; + pc_dep = 1; + } else if THUMB2_INSN_MATCH(ADR, insn) { + uregs = 0x0f00; /* Rd 8-11 */ + pc_dep = 1; + } else if (((THUMB2_INSN_MATCH(LDRW, insn) || + THUMB2_INSN_MATCH(LDRW1, insn) || + THUMB2_INSN_MATCH(LDRBW, insn) || + THUMB2_INSN_MATCH(LDRBW1, insn) || + THUMB2_INSN_MATCH(LDRHW, insn) || + THUMB2_INSN_MATCH(LDRHW1, insn) || + THUMB2_INSN_MATCH(LDRWL, insn)) && + THUMB2_INSN_REG_RN(insn) == 15) || + THUMB2_INSN_MATCH(LDREX, insn) || + ((THUMB2_INSN_MATCH(STRW, insn) || + THUMB2_INSN_MATCH(STRBW, insn) || + THUMB2_INSN_MATCH(STRHW, insn) || + THUMB2_INSN_MATCH(STRHW1, insn)) && + (THUMB2_INSN_REG_RN(insn) == 15 || + THUMB2_INSN_REG_RT(insn) == 15)) || + ((THUMB2_INSN_MATCH(STRT, insn) || + THUMB2_INSN_MATCH(STRHT, insn)) && + (THUMB2_INSN_REG_RN(insn) == 15 || + THUMB2_INSN_REG_RT(insn) == 15))) { + uregs = 0xf000; /* Rt 12-15 */ + pc_dep = 1; + } else if ((THUMB2_INSN_MATCH(LDRD, insn) || + THUMB2_INSN_MATCH(LDRD1, insn)) && + (THUMB2_INSN_REG_RN(insn) == 15)) { + uregs = 0xff00; /* Rt 12-15, Rt2 8-11 */ + pc_dep = 1; + } else if (THUMB2_INSN_MATCH(MUL, insn) && + THUMB2_INSN_REG_RM(insn) == 15) { + uregs = 0xf; + pc_dep = 1; + } else if (THUMB2_INSN_MATCH(DP, insn) && + (THUMB2_INSN_REG_RN(insn) == 15 || + THUMB2_INSN_REG_RM(insn) == 15)) { + uregs = 0xf000; /* Rd 12-15 */ + pc_dep = 1; + } else if (THUMB2_INSN_MATCH(STRD, insn) && + ((THUMB2_INSN_REG_RN(insn) == 15) || + (THUMB2_INSN_REG_RT(insn) == 15) || + THUMB2_INSN_REG_RT2(insn) == 15)) { + uregs = 0xff00; /* Rt 12-15, Rt2 8-11 */ + pc_dep = 1; + } else if (THUMB2_INSN_MATCH(RSBW, insn) && + THUMB2_INSN_REG_RN(insn) == 15) { + uregs = 0x0f00; /* Rd 8-11 */ + pc_dep = 1; + } else if (THUMB2_INSN_MATCH(RORW, insn) && + (THUMB2_INSN_REG_RN(insn) == 15 || + THUMB2_INSN_REG_RM(insn) == 15)) { + uregs = 0x0f00; + pc_dep = 1; + } else if ((THUMB2_INSN_MATCH(ROR, insn) || + THUMB2_INSN_MATCH(LSLW2, insn) || + THUMB2_INSN_MATCH(LSRW2, insn)) && + THUMB2_INSN_REG_RM(insn) == 15) { + uregs = 0x0f00; /* Rd 8-11 */ + pc_dep = 1; + } else if ((THUMB2_INSN_MATCH(LSLW1, insn) || + THUMB2_INSN_MATCH(LSRW1, insn)) && + (THUMB2_INSN_REG_RN(insn) == 15 || + THUMB2_INSN_REG_RM(insn) == 15)) { + uregs = 0x0f00; /* Rd 8-11 */ + pc_dep = 1; + } else if ((THUMB2_INSN_MATCH(TEQ1, insn) || + THUMB2_INSN_MATCH(TST1, insn)) && + THUMB2_INSN_REG_RN(insn) == 15) { + uregs = 0xf0000; /* Rn 0-3 (16-19) */ + pc_dep = 1; + } else if ((THUMB2_INSN_MATCH(TEQ2, insn) || + THUMB2_INSN_MATCH(TST2, insn)) && + (THUMB2_INSN_REG_RN(insn) == 15 || + THUMB2_INSN_REG_RM(insn) == 15)) { + uregs = 0xf0000; /* Rn 0-3 (16-19) */ + pc_dep = 1; + } + + if (unlikely(uregs && pc_dep)) { + memcpy(tramp, pc_dep_insn_execbuf_thumb, tramp_len); + prep_pc_dep_insn_execbuf_thumb(tramp, insn, uregs); + + addr = vaddr + 4; + *((unsigned short *)tramp + 13) = 0xdeff; + *((unsigned short *)tramp + 14) = addr & 0x0000ffff; + *((unsigned short *)tramp + 15) = addr >> 16; + if (!is_thumb2(insn)) { + addr = vaddr + 2; + *((unsigned short *)tramp + 16) = + (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 17) = addr >> 16; + } else { + addr = vaddr + 4; + *((unsigned short *)tramp + 16) = + (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 17) = addr >> 16; + } + } else { + memcpy(tramp, gen_insn_execbuf_thumb, tramp_len); + *((unsigned short *)tramp + 13) = 0xdeff; + if (!is_thumb2(insn)) { + addr = vaddr + 2; + *((unsigned short *)tramp + 2) = insn; + *((unsigned short *)tramp + 16) = + (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 17) = addr >> 16; + } else { + addr = vaddr + 4; + tramp[1] = insn; + *((unsigned short *)tramp + 16) = + (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 17) = addr >> 16; + } + } + + if (THUMB_INSN_MATCH(B2, insn)) { + memcpy(tramp, b_off_insn_execbuf_thumb, tramp_len); + *((unsigned short *)tramp + 13) = 0xdeff; + addr = branch_t16_dest(insn, vaddr); + *((unsigned short *)tramp + 14) = (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 15) = addr >> 16; + *((unsigned short *)tramp + 16) = 0; + *((unsigned short *)tramp + 17) = 0; + + } else if (THUMB_INSN_MATCH(B1, insn)) { + memcpy(tramp, b_cond_insn_execbuf_thumb, tramp_len); + *((unsigned short *)tramp + 13) = 0xdeff; + *((unsigned short *)tramp + 0) |= (insn & 0xf00); + addr = branch_cond_t16_dest(insn, vaddr); + *((unsigned short *)tramp + 14) = (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 15) = addr >> 16; + addr = vaddr + 2; + *((unsigned short *)tramp + 16) = (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 17) = addr >> 16; + + } else if (THUMB_INSN_MATCH(BLX2, insn) || + THUMB_INSN_MATCH(BX, insn)) { + memcpy(tramp, b_r_insn_execbuf_thumb, tramp_len); + *((unsigned short *)tramp + 13) = 0xdeff; + *((unsigned short *)tramp + 4) = insn; + addr = vaddr + 2; + *((unsigned short *)tramp + 16) = (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 17) = addr >> 16; + + } else if (THUMB_INSN_MATCH(CBZ, insn)) { + memcpy(tramp, cbz_insn_execbuf_thumb, tramp_len); + *((unsigned short *)tramp + 13) = 0xdeff; + /* zero out original branch displacement (imm5 = 0; i = 0) */ + *((unsigned short *)tramp + 0) = insn & (~0x2f8); + /* replace it with 8 bytes offset in execbuf (imm5 = 0b00010) */ + *((unsigned short *)tramp + 0) |= 0x20; + addr = cbz_t16_dest(insn, vaddr); + *((unsigned short *)tramp + 14) = (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 15) = addr >> 16; + addr = vaddr + 2; + *((unsigned short *)tramp + 16) = (addr & 0x0000ffff) | 0x1; + *((unsigned short *)tramp + 17) = addr >> 16; + } + + return 0; +} + +int make_trampoline_thumb(struct uprobe *p, + unsigned long vaddr, unsigned long insn, + unsigned long *tramp, size_t tramp_len) +{ + int ret; + + ret = do_make_trampoline_thumb(vaddr, insn, tramp, tramp_len); + if (ret) { + struct decode_info info = { + .vaddr = vaddr, + .tramp = tramp, + .handeler = NULL, + }; + + ret = decode_thumb(insn, &info); + if (info.handeler) { + unsigned short *tr = (unsigned short *)tramp; + tr[13] = 0xdeff; /* breakpoint for uretprobe */ + p->ainsn.handler = info.handeler; + } + } + + return ret; +} + +int noret_thumb(u32 opcode) +{ + return !!(THUMB2_INSN_MATCH(BL, opcode) || + THUMB2_INSN_MATCH(BLX1, opcode) || + THUMB_INSN_MATCH(BLX2, opcode)); +} diff --git a/arch/arm/probes/probes_thumb.h b/arch/arm/probes/probes_thumb.h new file mode 100644 index 0000000..a8d2f97 --- /dev/null +++ b/arch/arm/probes/probes_thumb.h @@ -0,0 +1,34 @@ +/* + * 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 + * + */ + + +#ifndef _SWAP_ASM_PROBES_THUMB_H +#define _SWAP_ASM_PROBES_THUMB_H + + +int make_trampoline_thumb(struct uprobe *p, + unsigned long vaddr, unsigned long insn, + unsigned long *tramp, size_t tramp_len); + +int noret_thumb(u32 opcode); + + +#endif /* _SWAP_ASM_PROBES_THUMB_H */ diff --git a/arch/arm/probes/tramps_arm.c b/arch/arm/probes/tramps_arm.c new file mode 100644 index 0000000..84d978d --- /dev/null +++ b/arch/arm/probes/tramps_arm.c @@ -0,0 +1,86 @@ +/* + * 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 + * + */ + + +__asm( +".text\n" +".arm\n" +".global gen_insn_execbuf\n" +"gen_insn_execbuf:\n" +" nop\n" +" nop\n" +" nop\n" /* original instruction */ +" nop\n" +" ldr pc, [pc, #4]\n" /* ssbreak */ +" nop\n" /* retbreak */ +" nop\n" +" nop\n" /* stored PC-4(next insn addr) */ + +".global pc_dep_insn_execbuf\n" +"pc_dep_insn_execbuf:\n" +" str r0, [sp, #-4]\n" +" ldr r0, [pc, #12]\n" +" nop\n" /* instruction with replaced PC */ +" ldr r0, [sp, #-4]\n" +" ldr pc, [pc, #4]\n" /* ssbreak */ +" nop\n" /* retbreak */ +" nop\n" /* stored PC */ +" nop\n" /* stored PC-4 (next insn addr) */ + +".global b_r_insn_execbuf\n" +"b_r_insn_execbuf:\n" +" nop\n" /* bx, blx (Rm) */ +" ldr pc, np1\n" +" nop\n" +" nop\n" +" nop\n" +" nop\n" /* retbreak */ +" nop\n" +"np1:\n" +" nop\n" /* stored PC-4 (next insn addr) */ + +".global b_cond_insn_execbuf\n" +"b_cond_insn_execbuf:\n" +" beq condway\n" +" ldr pc, np2\n" +"condway:\n" +" ldr pc, bd2\n" +" nop\n" +" nop\n" +" nop\n" /* retbreak */ +"bd2:\n" +" nop\n" /* branch displacement */ +"np2:\n" +" nop\n" /* stored PC-4 (next insn addr) */ + +".global blx_off_insn_execbuf\n" +"blx_off_insn_execbuf:\n" +" ldreq lr, bd3\n" +" blxeq lr\n" +" ldr pc, np3\n" +" nop\n" +" nop\n" +" nop\n" /* retbreak */ +"bd3:\n" +" nop\n" /* branch displacement */ +"np3:\n" +" nop\n" /* stored PC-4 (next insn addr) */ +); diff --git a/kprobe/arch/arm/swap-asm/trampoline_arm.h b/arch/arm/probes/tramps_arm.h similarity index 86% rename from kprobe/arch/arm/swap-asm/trampoline_arm.h rename to arch/arm/probes/tramps_arm.h index 0bbf930..6989b8b 100644 --- a/kprobe/arch/arm/swap-asm/trampoline_arm.h +++ b/arch/arm/probes/tramps_arm.h @@ -1,5 +1,4 @@ /** - * @file kprobe/arch/asm-arm/trampoline_arm.h * @author Ekaterina Gorelkina : initial implementation for ARM/MIPS * @author Alexey Gerenkov User-Space * Probes initial implementation; @@ -29,13 +28,11 @@ * * Copyright (C) Samsung Electronics, 2006-2010 * - * @section DESCRIPTION - * - * Provides intefrace for trampoline_arm.S */ -#ifndef __ASM_ARM_TRAMPOLINE_ARM_H -#define __ASM_ARM_TRAMPOLINE_ARM_H +#ifndef _SWAP_ASM_TRAMPS_ARM_H +#define _SWAP_ASM_TRAMPS_ARM_H + void gen_insn_execbuf(void); void pc_dep_insn_execbuf(void); @@ -43,4 +40,5 @@ void b_r_insn_execbuf(void); void b_cond_insn_execbuf(void); void blx_off_insn_execbuf(void); -#endif /* __ASM_ARM_TRAMPOLINE_ARM_H */ + +#endif /* _SWAP_ASM_TRAMPS_ARM_H */ diff --git a/arch/arm/probes/tramps_thumb.c b/arch/arm/probes/tramps_thumb.c new file mode 100644 index 0000000..c9ce11d --- /dev/null +++ b/arch/arm/probes/tramps_thumb.c @@ -0,0 +1,169 @@ +/* + * 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 + * + */ + + +__asm( +".text\n" +".thumb\n" + +".global gen_insn_execbuf_thumb\n" +"gen_insn_execbuf_thumb:\n" + "nop\n" + "nop\n" + "nop\n" /* original instruction */ + "nop\n" /* original instruction */ + "nop\n" + "nop\n" + "nop\n" + "sub sp, sp, #8\n" + "str r0, [sp, #0]\n" + "ldr r0, [pc, #12]\n" + "str r0, [sp, #4]\n" + "nop\n" + "pop {r0, pc}\n" /* ssbreak */ + "nop\n" /* retbreak */ + "nop\n" + "nop\n" + "nop\n" /* stored PC-4(next insn addr) hi */ + "nop\n" /* stored PC-4(next insn addr) lo */ + + "nop\n" + +".global pc_dep_insn_execbuf_thumb\n" +".align 4\n" +"pc_dep_insn_execbuf_thumb:\n" + "push {r6, r7}\n" + "ldr r6, i1\n" + "mov r7, sp\n" + "mov sp, r6\n" + "nop\n" /* PC -> SP */ + "nop\n" /* PC -> SP */ + "mov sp, r7\n" + "pop {r6, r7}\n" + "push {r0, r1}\n" + "ldr r0, i2\n" + "nop\n" + "str r0, [sp, #4]\n" + "pop {r0, pc}\n" /* ssbreak */ + "nop\n" /* retbreak */ +"i1:\n" + "nop\n" /* stored PC hi */ + "nop\n" /* stored PC lo */ +"i2:\n" + "nop\n" /* stored PC-4(next insn addr) hi */ + "nop\n" /* stored PC-4(next insn addr) lo */ + +".global b_r_insn_execbuf_thumb\n" +".align 4\n" +"b_r_insn_execbuf_thumb:\n" + "nop\n" + "nop\n" + "nop\n" + "nop\n" + "nop\n" /* bx,blx (Rm) */ + "nop\n" + "push {r0,r1}\n" + "ldr r0, np\n" + "nop\n" + "str r0, [sp, #4]\n" + "pop {r0,pc}\n" + "nop\n" + "nop\n" /* ssbreak */ + "nop\n" /* retbreak */ + "nop\n" + "nop\n" +"np:\n" + "nop\n" /* stored PC-4(next insn addr) hi */ + "nop\n" /* stored PC-4(next insn addr) lo */ + +".global b_off_insn_execbuf_thumb\n" +".align 4\n" +"b_off_insn_execbuf_thumb:\n" + "push {r0,r1}\n" + "ldr r0, bd\n" + "str r0, [sp, #4]\n" + "pop {r0, pc}\n" + "nop\n" + "nop\n" + "push {r0,r1}\n" + "ldr r0, np2\n" + "nop\n" + "str r0, [sp, #4]\n" + "pop {r0,pc}\n" + "nop\n" + "nop\n" /* ssbreak */ + "nop\n" /* retbreak */ +"bd:\n" + "nop\n" /* branch displacement hi */ + "nop\n" /* branch displacement lo */ +"np2:\n" + "nop\n" /* stored PC-4(next insn addr) hi */ + "nop\n" /* stored PC-4(next insn addr) lo */ + +".global b_cond_insn_execbuf_thumb\n" +".align 4\n" +"b_cond_insn_execbuf_thumb:\n" + "beq condway\n" + "push {r0,r1}\n" + "ldr r0, np4\n" + "nop\n" + "str r0, [sp, #4]\n" + "pop {r0,pc}\n" +"condway:\n" + "push {r0,r1}\n" + "ldr r0, bd4\n" + "str r0, [sp, #4]\n" + "pop {r0,pc}\n" + "nop\n" + "nop\n" + "nop\n" /* ssbreak */ + "nop\n" /* retbreak */ +"bd4:\n" + "nop\n" /* branch displacement hi */ + "nop\n" /* branch displacement lo */ +"np4:\n" + "nop\n" /* stored PC-4(next insn addr) hi */ + "nop\n" /* stored PC-4(next insn addr) lo */ + +".global cbz_insn_execbuf_thumb\n" +".align 4\n" +"cbz_insn_execbuf_thumb:\n" + "nop\n" /* cbz */ + "push {r0,r1}\n" + "ldr r0, np5\n" + "nop\n" + "str r0, [sp, #4]\n" + "pop {r0,pc}\n" + "push {r0,r1}\n" + "ldr r0, bd5\n" + "str r0, [sp, #4]\n" + "pop {r0,pc}\n" + "nop\n" + "nop\n" + "nop\n" /* ssbreak */ + "nop\n" /* retbreak */ +"bd5:\n" + "nop\n" /* branch displacement hi */ + "nop\n" /* branch displacement lo */ +"np5:\n" + "nop\n" /* stored PC-4(next insn addr) hi */ + "nop\n" /* stored PC-4(next insn addr) lo */ +); diff --git a/uprobe/arch/arm/swap-asm/trampoline_thumb.h b/arch/arm/probes/tramps_thumb.h similarity index 86% rename from uprobe/arch/arm/swap-asm/trampoline_thumb.h rename to arch/arm/probes/tramps_thumb.h index c67cbf8..5503890 100644 --- a/uprobe/arch/arm/swap-asm/trampoline_thumb.h +++ b/arch/arm/probes/tramps_thumb.h @@ -1,5 +1,4 @@ /** - * @file uprobe/arch/asm-arm/trampoline_thumb.h * @author Alexey Gerenkov User-Space Probes initial * implementation; Support x86/ARM/MIPS for both user and kernel spaces. * @author Ekaterina Gorelkina : redesign module for @@ -25,14 +24,12 @@ * * Copyright (C) Samsung Electronics, 2006-2010 * - * @section DESCRIPTION - * - * Thumb trampolines. */ -#ifndef __ASM_ARM_TRAMPOLINE_THUMB_H -#define __ASM_ARM_TRAMPOLINE_THUMB_H +#ifndef _SWAP_ASM_TRAMPS_THUMB_H +#define _SWAP_ASM_TRAMPS_THUMB_H + void gen_insn_execbuf_thumb(void); void pc_dep_insn_execbuf_thumb(void); @@ -41,4 +38,5 @@ void b_off_insn_execbuf_thumb(void); void b_cond_insn_execbuf_thumb(void); void cbz_insn_execbuf_thumb(void); -#endif /* __ASM_ARM_TRAMPOLINE_THUMB_H */ + +#endif /* _SWAP_ASM_TRAMPS_THUMB_H */ diff --git a/kprobe/Kbuild b/kprobe/Kbuild index 9403c42..7af581e 100644 --- a/kprobe/Kbuild +++ b/kprobe/Kbuild @@ -8,8 +8,11 @@ swap_kprobe-y := swap_kprobes.o \ swap_ktd.o ### ARM -swap_kprobe-$(CONFIG_ARM) += arch/arm/swap-asm/swap_kprobes.o \ - arch/arm/swap-asm/trampoline_arm.o +swap_kprobe-$(CONFIG_ARM) += \ + arch/arm/swap-asm/swap_kprobes.o \ + ../arch/arm/probes/probes_arm.o \ + ../arch/arm/probes/tramps_arm.o + ifeq ($(CONFIG_STRICT_MEMORY_RWX), y) swap_kprobe-$(CONFIG_ARM) += arch/arm/swap-asm/memory_rwx.o endif #ifeq ($(CONFIG_STRICT_MEMORY_RWX), y) diff --git a/kprobe/arch/arm/swap-asm/swap_kprobes.c b/kprobe/arch/arm/swap-asm/swap_kprobes.c index f4e004c..7f438c4 100644 --- a/kprobe/arch/arm/swap-asm/swap_kprobes.c +++ b/kprobe/arch/arm/swap-asm/swap_kprobes.c @@ -35,274 +35,23 @@ */ #include -#include - -#include "swap_kprobes.h" -#include "trampoline_arm.h" -#include - -#include -#include -#include -#include - #include #include #include #include #include - - -#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit))))) -#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25) +#include +#include +#include +#include +#include +#include +#include "swap_kprobes.h" static void (*__swap_register_undef_hook)(struct undef_hook *hook); static void (*__swap_unregister_undef_hook)(struct undef_hook *hook); -static unsigned long get_addr_b(unsigned long insn, unsigned long addr) -{ - /* real position less then PC by 8 */ - return (kprobe_opcode_t)((long)addr + 8 + branch_displacement(insn)); -} - -static int prep_pc_dep_insn_execbuf(kprobe_opcode_t *insns, - kprobe_opcode_t insn, int uregs) -{ - int i; - - if (uregs & 0x10) { - int reg_mask = 0x1; - /* search in reg list */ - for (i = 0; i < 13; i++, reg_mask <<= 1) { - if (!(insn & reg_mask)) - break; - } - } else { - for (i = 0; i < 13; i++) { - if ((uregs & 0x1) && (ARM_INSN_REG_RN(insn) == i)) - continue; - if ((uregs & 0x2) && (ARM_INSN_REG_RD(insn) == i)) - continue; - if ((uregs & 0x4) && (ARM_INSN_REG_RS(insn) == i)) - continue; - if ((uregs & 0x8) && (ARM_INSN_REG_RM(insn) == i)) - continue; - break; - } - } - - if (i == 13) { - DBPRINTF("there are no free register %x in insn %lx!", - uregs, insn); - return -EINVAL; - } - DBPRINTF("prep_pc_dep_insn_execbuf: using R%d, changing regs %x", - i, uregs); - - /* set register to save */ - ARM_INSN_REG_SET_RD(insns[0], i); - /* set register to load address to */ - ARM_INSN_REG_SET_RD(insns[1], i); - /* set instruction to execute and patch it */ - if (uregs & 0x10) { - ARM_INSN_REG_CLEAR_MR(insn, 15); - ARM_INSN_REG_SET_MR(insn, i); - } else { - if ((uregs & 0x1) && (ARM_INSN_REG_RN(insn) == 15)) - ARM_INSN_REG_SET_RN(insn, i); - if ((uregs & 0x2) && (ARM_INSN_REG_RD(insn) == 15)) - ARM_INSN_REG_SET_RD(insn, i); - if ((uregs & 0x4) && (ARM_INSN_REG_RS(insn) == 15)) - ARM_INSN_REG_SET_RS(insn, i); - if ((uregs & 0x8) && (ARM_INSN_REG_RM(insn) == 15)) - ARM_INSN_REG_SET_RM(insn, i); - } - - insns[UPROBES_TRAMP_INSN_IDX] = insn; - /* set register to restore */ - ARM_INSN_REG_SET_RD(insns[3], i); - - return 0; -} - -static int arch_check_insn_arm(unsigned long insn) -{ - /* check instructions that can change PC by nature */ - if ( - /* ARM_INSN_MATCH(UNDEF, insn) || */ - ARM_INSN_MATCH(AUNDEF, insn) || - ARM_INSN_MATCH(SWI, insn) || - ARM_INSN_MATCH(BREAK, insn) || - ARM_INSN_MATCH(BXJ, insn)) { - goto bad_insn; -#ifndef CONFIG_CPU_V7 - /* check instructions that can write result to PC */ - } else if ((ARM_INSN_MATCH(DPIS, insn) || - ARM_INSN_MATCH(DPRS, insn) || - ARM_INSN_MATCH(DPI, insn) || - ARM_INSN_MATCH(LIO, insn) || - ARM_INSN_MATCH(LRO, insn)) && - (ARM_INSN_REG_RD(insn) == 15)) { - goto bad_insn; -#endif /* CONFIG_CPU_V7 */ - /* check special instruction loads store multiple registers */ - } else if ((ARM_INSN_MATCH(LM, insn) || ARM_INSN_MATCH(SM, insn)) && - /* store PC or load to PC */ - (ARM_INSN_REG_MR(insn, 15) || - /* store/load with PC update */ - ((ARM_INSN_REG_RN(insn) == 15) && (insn & 0x200000)))) { - goto bad_insn; - } - - return 0; - -bad_insn: - return -EFAULT; -} - -static int make_branch_tarmpoline(unsigned long addr, unsigned long insn, - unsigned long *tramp) -{ - int ok = 0; - - /* B */ - if (ARM_INSN_MATCH(B, insn) && - !ARM_INSN_MATCH(BLX1, insn)) { - /* B check can be false positive on BLX1 instruction */ - memcpy(tramp, b_cond_insn_execbuf, KPROBES_TRAMP_LEN); - tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; - tramp[0] |= insn & 0xf0000000; - tramp[6] = get_addr_b(insn, addr); - tramp[7] = addr + 4; - ok = 1; - /* BX, BLX (Rm) */ - } else if (ARM_INSN_MATCH(BX, insn) || - ARM_INSN_MATCH(BLX2, insn)) { - memcpy(tramp, b_r_insn_execbuf, KPROBES_TRAMP_LEN); - tramp[0] = insn; - tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; - tramp[7] = addr + 4; - ok = 1; - /* BL, BLX (Off) */ - } else if (ARM_INSN_MATCH(BLX1, insn)) { - memcpy(tramp, blx_off_insn_execbuf, KPROBES_TRAMP_LEN); - tramp[0] |= 0xe0000000; - tramp[1] |= 0xe0000000; - tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; - tramp[6] = get_addr_b(insn, addr) + - 2 * (insn & 01000000) + 1; /* jump to thumb */ - tramp[7] = addr + 4; - ok = 1; - /* BL */ - } else if (ARM_INSN_MATCH(BL, insn)) { - memcpy(tramp, blx_off_insn_execbuf, KPROBES_TRAMP_LEN); - tramp[0] |= insn & 0xf0000000; - tramp[1] |= insn & 0xf0000000; - tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; - tramp[6] = get_addr_b(insn, addr); - tramp[7] = addr + 4; - ok = 1; - } - - return ok; -} - -/** - * @brief Creates ARM trampoline. - * - * @param addr Probe address. - * @param insn Instuction at this address. - * @param tramp Pointer to memory for trampoline. - * @return 0 on success, error code on error. - */ -int arch_make_trampoline_arm(unsigned long addr, unsigned long insn, - unsigned long *tramp) -{ - int ret, uregs, pc_dep; - - if (addr & 0x03) { - printk(KERN_INFO "Error in %s at %d: attempt to register uprobe " - "at an unaligned address\n", __FILE__, __LINE__); - return -EINVAL; - } - - ret = arch_check_insn_arm(insn); - if (ret) - return ret; - - if (make_branch_tarmpoline(addr, insn, tramp)) - return 0; - - uregs = pc_dep = 0; - /* Rm */ - if (ARM_INSN_MATCH(CLZ, insn)) { - uregs = 0xa; - if (ARM_INSN_REG_RM(insn) == 15) - pc_dep = 1; - /* Rn, Rm ,Rd */ - } else if (ARM_INSN_MATCH(DPIS, insn) || ARM_INSN_MATCH(LRO, insn) || - ARM_INSN_MATCH(SRO, insn)) { - uregs = 0xb; - if ((ARM_INSN_REG_RN(insn) == 15) || - (ARM_INSN_REG_RM(insn) == 15) || - (ARM_INSN_MATCH(SRO, insn) && - (ARM_INSN_REG_RD(insn) == 15))) { - pc_dep = 1; - } - /* Rn ,Rd */ - } else if (ARM_INSN_MATCH(DPI, insn) || ARM_INSN_MATCH(LIO, insn) || - ARM_INSN_MATCH(SIO, insn)) { - uregs = 0x3; - if ((ARM_INSN_REG_RN(insn) == 15) || - (ARM_INSN_MATCH(SIO, insn) && - (ARM_INSN_REG_RD(insn) == 15))) { - pc_dep = 1; - } - /* Rn, Rm, Rs */ - } else if (ARM_INSN_MATCH(DPRS, insn)) { - uregs = 0xd; - if ((ARM_INSN_REG_RN(insn) == 15) || - (ARM_INSN_REG_RM(insn) == 15) || - (ARM_INSN_REG_RS(insn) == 15)) { - pc_dep = 1; - } - /* register list */ - } else if (ARM_INSN_MATCH(SM, insn)) { - uregs = 0x10; - if (ARM_INSN_REG_MR(insn, 15)) - pc_dep = 1; - } - - /* check instructions that can write result to SP and uses PC */ - if (pc_dep && (ARM_INSN_REG_RD(insn) == 13)) { - printk(KERN_INFO "Error in %s at %d: instruction check failed (arm)\n", - __FILE__, __LINE__); - return -EFAULT; - } - - if (unlikely(uregs && pc_dep)) { - memcpy(tramp, pc_dep_insn_execbuf, KPROBES_TRAMP_LEN); - if (prep_pc_dep_insn_execbuf(tramp, insn, uregs) != 0) { - printk(KERN_INFO "Error in %s at %d: failed " - "to prepare exec buffer for insn %lx!", - __FILE__, __LINE__, insn); - return -EINVAL; - } - - tramp[6] = addr + 8; - } else { - memcpy(tramp, gen_insn_execbuf, KPROBES_TRAMP_LEN); - tramp[KPROBES_TRAMP_INSN_IDX] = insn; - } - - /* TODO: remove for kprobe */ - tramp[KPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; - tramp[7] = addr + 4; - - return 0; -} -EXPORT_SYMBOL_GPL(arch_make_trampoline_arm); /** * @brief Creates trampoline for kprobe. @@ -321,7 +70,7 @@ int arch_kp_core_prepare(struct kp_core *p, struct slot_manager *sm) return -ENOMEM; p->opcode = *(unsigned long *)p->addr; - ret = arch_make_trampoline_arm(p->addr, p->opcode, tramp); + ret = make_trampoline_arm(p->addr, p->opcode, tramp); if (ret) { swap_slot_free(sm, tramp); return ret; @@ -781,7 +530,13 @@ void swap_arch_exit_kprobes(void) swap_unregister_undef_hook(&undef_ho_k); } -/* export symbol for trampoline_arm.h */ + +/* export symbol for probes_arm.h */ +EXPORT_SYMBOL_GPL(noret_arm); +EXPORT_SYMBOL_GPL(make_trampoline_arm); + +#include +/* export symbol for tramps_arm.h */ EXPORT_SYMBOL_GPL(gen_insn_execbuf); EXPORT_SYMBOL_GPL(pc_dep_insn_execbuf); EXPORT_SYMBOL_GPL(b_r_insn_execbuf); diff --git a/kprobe/arch/arm/swap-asm/swap_kprobes.h b/kprobe/arch/arm/swap-asm/swap_kprobes.h index 9f16dbf..90945ba 100644 --- a/kprobe/arch/arm/swap-asm/swap_kprobes.h +++ b/kprobe/arch/arm/swap-asm/swap_kprobes.h @@ -236,378 +236,6 @@ static inline void swap_set_arg(struct pt_regs *regs, int num, regs->uregs[num] = val; } -/* undefined */ -#define MASK_ARM_INSN_UNDEF 0x0FF00000 -#define PTRN_ARM_INSN_UNDEF 0x03000000 - -#define MASK_THUMB_INSN_UNDEF 0xFE00 -#define PTRN_THUMB_INSN_UNDEF 0xDE00 - -/* architecturally undefined */ -#define MASK_ARM_INSN_AUNDEF 0x0FF000F0 -#define PTRN_ARM_INSN_AUNDEF 0x07F000F0 - -/* branches */ -#define MASK_ARM_INSN_B 0x0F000000 -#define PTRN_ARM_INSN_B 0x0A000000 - -#define MASK_THUMB_INSN_B1 0xF000 -#define PTRN_THUMB_INSN_B1 0xD000 /* b label */ - -#define MASK_THUMB_INSN_B2 0xF800 -#define PTRN_THUMB_INSN_B2 0xE000 /* b label */ - -#define MASK_THUMB_INSN_CBZ 0xF500 -#define PTRN_THUMB_INSN_CBZ 0xB100 /* CBZ/CBNZ */ - -#define MASK_THUMB2_INSN_B1 0xD000F800 -#define PTRN_THUMB2_INSN_B1 0x8000F000 - -#define MASK_THUMB2_INSN_B2 0xD000F800 -#define PTRN_THUMB2_INSN_B2 0x9000F000 - -#define MASK_ARM_INSN_BL 0x0F000000 -#define PTRN_ARM_INSN_BL 0x0B000000 - -/* #define MASK_THUMB_INSN_BL 0xF800 */ -/* #define PTRN_THUMB_INSN_BL 0xF000 shared between BL and BLX */ -/* #define PTRN_THUMB_INSN_BL 0xF800 */ - -#define MASK_THUMB2_INSN_BL 0xD000F800 -#define PTRN_THUMB2_INSN_BL 0xD000F000 /* bl imm swapped */ - -#define MASK_ARM_INSN_BLX1 0xFE000000 -#define PTRN_ARM_INSN_BLX1 0xFA000000 - -/* #define MASK_THUMB_INSN_BLX1 0xF800 */ -/* #define PTRN_THUMB_INSN_BLX1 0xF000 */ - -#define MASK_THUMB2_INSN_BLX1 0xD001F800 -#define PTRN_THUMB2_INSN_BLX1 0xC000F000 - -#define MASK_ARM_INSN_BLX2 0x0FF000F0 -#define PTRN_ARM_INSN_BLX2 0x01200030 - -#define MASK_THUMB_INSN_BLX2 0xFF80 /* blx reg */ -#define PTRN_THUMB_INSN_BLX2 0x4780 - -#define MASK_ARM_INSN_BX 0x0FF000F0 -#define PTRN_ARM_INSN_BX 0x01200010 - -#define MASK_THUMB_INSN_BX 0xFF80 -#define PTRN_THUMB_INSN_BX 0x4700 - -#define MASK_ARM_INSN_BXJ 0x0FF000F0 -#define PTRN_ARM_INSN_BXJ 0x01200020 - -#define MASK_THUMB2_INSN_BXJ 0xD000FFF0 -#define PTRN_THUMB2_INSN_BXJ 0x8000F3C0 - - -/* software interrupts */ -#define MASK_ARM_INSN_SWI 0x0F000000 -#define PTRN_ARM_INSN_SWI 0x0F000000 - -#define MASK_THUMB_INSN_SWI 0xFF00 -#define PTRN_THUMB_INSN_SWI 0xDF00 - -/* break */ -#define MASK_ARM_INSN_BREAK 0xFFF000F0 -#define PTRN_ARM_INSN_BREAK 0xE1200070 -/* A8-56 ARM DDI 046B if cond != ‘1110’ then UNPREDICTABLE; */ - -#define MASK_THUMB_INSN_BREAK 0xFF00 -#define PTRN_THUMB_INSN_BREAK 0xBE00 - -/* CLZ */ -#define MASK_ARM_INSN_CLZ 0x0FFF0FF0 -#define PTRN_ARM_INSN_CLZ 0x016F0F10 - -/* Data processing immediate shift */ -#define MASK_ARM_INSN_DPIS 0x0E000010 -#define PTRN_ARM_INSN_DPIS 0x00000000 -/* Data processing register shift */ -#define MASK_ARM_INSN_DPRS 0x0E000090 -#define PTRN_ARM_INSN_DPRS 0x00000010 - -#define MASK_THUMB2_INSN_DPRS 0xFFE00000 -#define PTRN_THUMB2_INSN_DPRS 0xEA000000 - -/* Data processing immediate */ -#define MASK_ARM_INSN_DPI 0x0E000000 -#define PTRN_ARM_INSN_DPI 0x02000000 - -#define MASK_THUMB_INSN_DP 0xFC00 -#define PTRN_THUMB_INSN_DP 0x4000 - -#define MASK_THUMB_INSN_APC 0xF800 -#define PTRN_THUMB_INSN_APC 0xA000 /* ADD Rd, [PC, # * 4] */ - -#define MASK_THUMB2_INSN_DPI 0xFBE08000 -/* #define PTRN_THUMB2_INSN_DPI 0xF0000000 */ -/* A6-19 ARM DDI 0406B */ -#define PTRN_THUMB2_INSN_DPI 0xF2000000 -/* A6-19 ARM DDI 0406B */ - -#define MASK_THUMB_INSN_MOV3 0xFF00 -#define PTRN_THUMB_INSN_MOV3 0x4600 /* MOV Rd, PC */ - -#define MASK_THUMB2_INSN_RSBW 0x8000fbe0 -#define PTRN_THUMB2_INSN_RSBW 0x0000f1c0 /* RSB{S}.W Rd,Rn,# */ - -#define MASK_THUMB2_INSN_RORW 0xf0f0ffe0 -#define PTRN_THUMB2_INSN_RORW 0xf000fa60 /* ROR{S}.W Rd, Rn, Rm */ - -#define MASK_THUMB2_INSN_ROR 0x0030ffef -#define PTRN_THUMB2_INSN_ROR 0x0030ea4f /* ROR{S} Rd, Rm, # */ - -#define MASK_THUMB2_INSN_LSLW1 0xf0f0ffe0 -#define PTRN_THUMB2_INSN_LSLW1 0xf000fa00 /* LSL{S}.W Rd, Rn, Rm */ - -#define MASK_THUMB2_INSN_LSLW2 0x0030ffef -#define PTRN_THUMB2_INSN_LSLW2 0x0000ea4f /* LSL{S}.W Rd, Rm, #*/ - -#define MASK_THUMB2_INSN_LSRW1 0xf0f0ffe0 -#define PTRN_THUMB2_INSN_LSRW1 0xf000fa20 /* LSR{S}.W Rd, Rn, Rm */ - -#define MASK_THUMB2_INSN_LSRW2 0x0030ffef -#define PTRN_THUMB2_INSN_LSRW2 0x0010ea4f /* LSR{S}.W Rd, Rm, # */ - -#define MASK_THUMB2_INSN_TEQ1 0x8f00fbf0 -#define PTRN_THUMB2_INSN_TEQ1 0x0f00f090 /* TEQ Rn, # */ - -#define MASK_THUMB2_INSN_TEQ2 0x0f00fff0 -#define PTRN_THUMB2_INSN_TEQ2 0x0f00ea90 /* TEQ Rn, Rm{,} */ - -#define MASK_THUMB2_INSN_TST1 0x8f00fbf0 -#define PTRN_THUMB2_INSN_TST1 0x0f00f010 /* TST Rn, # */ - -#define MASK_THUMB2_INSN_TST2 0x0f00fff0 -#define PTRN_THUMB2_INSN_TST2 0x0f00ea10 /* TST Rn, Rm{,} */ - - -/* Load immediate offset */ -#define MASK_ARM_INSN_LIO 0x0E100000 -#define PTRN_ARM_INSN_LIO 0x04100000 - -#define MASK_THUMB_INSN_LIO1 0xF800 -#define PTRN_THUMB_INSN_LIO1 0x6800 /* LDR */ - -#define MASK_THUMB_INSN_LIO2 MASK_THUMB_INSN_LIO1 -#define PTRN_THUMB_INSN_LIO2 0x7800 /* LDRB */ - -#define MASK_THUMB_INSN_LIO3 MASK_THUMB_INSN_LIO1 -#define PTRN_THUMB_INSN_LIO3 0x8800 /* LDRH */ - -#define MASK_THUMB_INSN_LIO4 MASK_THUMB_INSN_LIO1 -#define PTRN_THUMB_INSN_LIO4 0x9800 /* LDR SP relative */ - -#define MASK_THUMB2_INSN_LDRW 0x0000fff0 -#define PTRN_THUMB2_INSN_LDRW 0x0000f850 /* LDR.W Rt, [Rn, #-] */ - -#define MASK_THUMB2_INSN_LDRW1 MASK_THUMB2_INSN_LDRW -#define PTRN_THUMB2_INSN_LDRW1 0x0000f8d0 /* LDR.W Rt, [Rn, #] */ - -#define MASK_THUMB2_INSN_LDRBW MASK_THUMB2_INSN_LDRW -#define PTRN_THUMB2_INSN_LDRBW 0x0000f810 /* LDRB.W Rt, [Rn, #-] */ - -#define MASK_THUMB2_INSN_LDRBW1 MASK_THUMB2_INSN_LDRW -#define PTRN_THUMB2_INSN_LDRBW1 0x0000f890 /* LDRB.W Rt, [Rn, #] */ - -#define MASK_THUMB2_INSN_LDRHW MASK_THUMB2_INSN_LDRW -#define PTRN_THUMB2_INSN_LDRHW 0x0000f830 /* LDRH.W Rt, [Rn, #-] */ - -#define MASK_THUMB2_INSN_LDRHW1 MASK_THUMB2_INSN_LDRW -#define PTRN_THUMB2_INSN_LDRHW1 0x0000f8b0 /* LDRH.W Rt, [Rn, #] */ - -#define MASK_THUMB2_INSN_LDRD 0x0000fed0 -#define PTRN_THUMB2_INSN_LDRD 0x0000e850 /* LDRD Rt, Rt2, [Rn, #-] */ - -#define MASK_THUMB2_INSN_LDRD1 MASK_THUMB2_INSN_LDRD -#define PTRN_THUMB2_INSN_LDRD1 0x0000e8d0 /* LDRD Rt, Rt2, [Rn, #] */ - -#define MASK_THUMB2_INSN_LDRWL 0x0fc0fff0 -#define PTRN_THUMB2_INSN_LDRWL 0x0000f850 /* LDR.W Rt, [Rn,Rm,LSL #] */ - -#define MASK_THUMB2_INSN_LDREX 0x0f00ffff -#define PTRN_THUMB2_INSN_LDREX 0x0f00e85f /* LDREX Rt, [PC, #] */ - -#define MASK_THUMB2_INSN_MUL 0xf0f0fff0 -#define PTRN_THUMB2_INSN_MUL 0xf000fb00 /* MUL Rd, Rn, Rm */ - -#define MASK_THUMB2_INSN_DP 0x0000ff00 -#define PTRN_THUMB2_INSN_DP 0x0000eb00 /* ADD/SUB/SBC/...Rd,Rn,Rm{,} */ - - - - -/* Store immediate offset */ -#define MASK_ARM_INSN_SIO MASK_ARM_INSN_LIO -#define PTRN_ARM_INSN_SIO 0x04000000 - -#define MASK_THUMB_INSN_SIO1 MASK_THUMB_INSN_LIO1 -#define PTRN_THUMB_INSN_SIO1 0x6000 /* STR */ - -#define MASK_THUMB_INSN_SIO2 MASK_THUMB_INSN_LIO1 -#define PTRN_THUMB_INSN_SIO2 0x7000 /* STRB */ - -#define MASK_THUMB_INSN_SIO3 MASK_THUMB_INSN_LIO1 -#define PTRN_THUMB_INSN_SIO3 0x8000 /* STRH */ - -#define MASK_THUMB_INSN_SIO4 MASK_THUMB_INSN_LIO1 -#define PTRN_THUMB_INSN_SIO4 0x9000 /* STR SP relative */ - -#define MASK_THUMB2_INSN_STRW 0x0fc0fff0 -#define PTRN_THUMB2_INSN_STRW 0x0000f840 /* STR.W Rt,[Rn,Rm,{LSL #}] */ - -#define MASK_THUMB2_INSN_STRW1 0x0000fff0 -#define PTRN_THUMB2_INSN_STRW1 0x0000f8c0 /* STR.W Rt, [Rn, #imm12] - * STR.W Rt, [PC, #imm12] shall be - * skipped, because it hangs - * on Tegra. WTF */ - -#define MASK_THUMB2_INSN_STRHW MASK_THUMB2_INSN_STRW -#define PTRN_THUMB2_INSN_STRHW 0x0000f820 /* STRH.W Rt,[Rn,Rm,{LSL #}] */ - -#define MASK_THUMB2_INSN_STRHW1 0x0000fff0 -#define PTRN_THUMB2_INSN_STRHW1 0x0000f8a0 /* STRH.W Rt, [Rn, #] */ - -#define MASK_THUMB2_INSN_STRHT 0x0f00fff0 /* strht r1, [pc, #imm] illegal - * instruction on Tegra. WTF */ -#define PTRN_THUMB2_INSN_STRHT 0x0e00f820 /* STRHT Rt, [Rn, #] */ - -#define MASK_THUMB2_INSN_STRT 0x0f00fff0 -#define PTRN_THUMB2_INSN_STRT 0x0e00f840 /* STRT Rt, [Rn, #] */ - -#define MASK_THUMB2_INSN_STRBW MASK_THUMB2_INSN_STRW -#define PTRN_THUMB2_INSN_STRBW 0x0000f800 /* STRB.W Rt,[Rn,Rm,{LSL #}] */ - -#define MASK_THUMB2_INSN_STRBW1 0x0000fff0 -#define PTRN_THUMB2_INSN_STRBW1 0x0000f880 /* STRB.W Rt, [Rn, #] - * STRB.W Rt, [PC, #imm12] shall be - * skipped, because it hangs - * on Tegra. WTF */ - -#define MASK_THUMB2_INSN_STRBT 0x0f00fff0 -#define PTRN_THUMB2_INSN_STRBT 0x0e00f800 /* STRBT Rt, [Rn, #}] */ - -#define MASK_THUMB2_INSN_STRD 0x0000fe50 -/* STR{D,EX,EXB,EXH,EXD} Rt, Rt2, [Rn, #] */ -#define PTRN_THUMB2_INSN_STRD 0x0000e840 - - -/* Load register offset */ -#define MASK_ARM_INSN_LRO 0x0E100010 -#define PTRN_ARM_INSN_LRO 0x06100000 - -#define MASK_THUMB_INSN_LRO1 0xFE00 -#define PTRN_THUMB_INSN_LRO1 0x5600 /* LDRSB */ - -#define MASK_THUMB_INSN_LRO2 MASK_THUMB_INSN_LRO1 -#define PTRN_THUMB_INSN_LRO2 0x5800 /* LDR */ - -#define MASK_THUMB_INSN_LRO3 0xf800 -#define PTRN_THUMB_INSN_LRO3 0x4800 /* LDR Rd, [PC, # * 4] */ - -#define MASK_THUMB_INSN_LRO4 MASK_THUMB_INSN_LRO1 -#define PTRN_THUMB_INSN_LRO4 0x5A00 /* LDRH */ - -#define MASK_THUMB_INSN_LRO5 MASK_THUMB_INSN_LRO1 -#define PTRN_THUMB_INSN_LRO5 0x5C00 /* LDRB */ - -#define MASK_THUMB_INSN_LRO6 MASK_THUMB_INSN_LRO1 -#define PTRN_THUMB_INSN_LRO6 0x5E00 /* LDRSH */ - -#define MASK_THUMB2_INSN_ADR 0x8000fa1f -#define PTRN_THUMB2_INSN_ADR 0x0000f20f - - - -/* Store register offset */ -#define MASK_ARM_INSN_SRO MASK_ARM_INSN_LRO -#define PTRN_ARM_INSN_SRO 0x06000000 - -#define MASK_THUMB_INSN_SRO1 MASK_THUMB_INSN_LRO1 -#define PTRN_THUMB_INSN_SRO1 0x5000 /* STR */ - -#define MASK_THUMB_INSN_SRO2 MASK_THUMB_INSN_LRO1 -#define PTRN_THUMB_INSN_SRO2 0x5200 /* STRH */ - -#define MASK_THUMB_INSN_SRO3 MASK_THUMB_INSN_LRO1 -#define PTRN_THUMB_INSN_SRO3 0x5400 /* STRB */ - -/* Load multiple */ -#define MASK_ARM_INSN_LM 0x0E100000 -#define PTRN_ARM_INSN_LM 0x08100000 - -#define MASK_THUMB2_INSN_LDMIA 0x8000ffd0 -#define PTRN_THUMB2_INSN_LDMIA 0x8000e890 /* LDMIA(.W) Rn(!),{Rx-PC} */ - -#define MASK_THUMB2_INSN_LDMDB 0x8000ffd0 -#define PTRN_THUMB2_INSN_LDMDB 0x8000e910 /* LDMDB(.W) Rn(!), {Rx-PC} */ - -/* Store multiple */ -#define MASK_ARM_INSN_SM MASK_ARM_INSN_LM -#define PTRN_ARM_INSN_SM 0x08000000 - - -/* Coprocessor load/store and double register transfers */ -#define MASK_ARM_INSN_CLS 0x0E000000 -#define PTRN_ARM_INSN_CLS 0x0C000000 -/* Coprocessor register transfers */ -#define MASK_ARM_INSN_CRT 0x0F000010 -#define PTRN_ARM_INSN_CRT 0x0E000010 - -#define ARM_INSN_MATCH(name, insn) \ - ((insn & MASK_ARM_INSN_##name) == PTRN_ARM_INSN_##name) -#define THUMB_INSN_MATCH(name, insn) \ - (((insn & 0x0000FFFF) & MASK_THUMB_INSN_##name) == \ - PTRN_THUMB_INSN_##name) -#define THUMB2_INSN_MATCH(name, insn) \ - ((insn & MASK_THUMB2_INSN_##name) == PTRN_THUMB2_INSN_##name) - -#define ARM_INSN_REG_RN(insn) \ - ((insn & 0x000F0000)>>16) - -#define ARM_INSN_REG_SET_RN(insn, nreg) \ - { insn &= ~0x000F0000; insn |= nreg<<16; } - -#define ARM_INSN_REG_RD(insn) \ - ((insn & 0x0000F000)>>12) - -#define ARM_INSN_REG_SET_RD(insn, nreg) \ - { insn &= ~0x0000F000; insn |= nreg<<12; } - -#define ARM_INSN_REG_RS(insn) \ - ((insn & 0x00000F00)>>8) - -#define ARM_INSN_REG_SET_RS(insn, nreg) \ - { insn &= ~0x00000F00; insn |= nreg<<8; } - -#define ARM_INSN_REG_RM(insn) \ - (insn & 0x0000000F) - -#define ARM_INSN_REG_SET_RM(insn, nreg) \ - { insn &= ~0x0000000F; insn |= nreg; } - -#define ARM_INSN_REG_MR(insn, nreg) \ - (insn & (1 << nreg)) - -#define ARM_INSN_REG_SET_MR(insn, nreg) \ - { insn |= (1 << nreg); } - -#define ARM_INSN_REG_CLEAR_MR(insn, nreg) \ - { insn &= ~(1 << nreg); } - -#define THUMB2_INSN_REG_RT(insn) ((insn & 0xf0000000) >> 28) -#define THUMB2_INSN_REG_RT2(insn) ((insn & 0x0f000000) >> 24) -#define THUMB2_INSN_REG_RN(insn) (insn & 0x0000000f) -#define THUMB2_INSN_REG_RD(insn) ((insn & 0x0f000000) >> 24) -#define THUMB2_INSN_REG_RM(insn) ((insn & 0x000f0000) >> 16) - - - - /** * @struct kp_core_ctlblk * @brief Per-cpu kp_core control block. @@ -642,9 +270,6 @@ void swap_unregister_undef_hook(struct undef_hook *hook); int arch_init_module_deps(void); -int arch_make_trampoline_arm(unsigned long addr, unsigned long insn, - unsigned long *tramp); - struct slot_manager; struct kretprobe; struct kretprobe_instance; diff --git a/kprobe/arch/arm/swap-asm/trampoline_arm.S b/kprobe/arch/arm/swap-asm/trampoline_arm.S deleted file mode 100644 index 8e26146..0000000 --- a/kprobe/arch/arm/swap-asm/trampoline_arm.S +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Function return probe trampoline: - * - init_kprobes() establishes a probepoint here - * - When the probed function returns, this probe - * causes the handlers to fire - */ - - .global gen_insn_execbuf -gen_insn_execbuf: - nop - nop - nop //original instruction - nop - ldr pc, [pc, #4] //ssbreak - nop //retbreak - nop - nop //stored PC-4(next insn addr) - - - - .global pc_dep_insn_execbuf -pc_dep_insn_execbuf: - str r0, [sp, #-4] - ldr r0, [pc, #12] - nop // instruction with replaced PC - ldr r0, [sp, #-4] - ldr pc, [pc, #4] //ssbreak - nop // retbreak - nop // stored PC - nop // stored PC-4 (next insn addr) - - .global b_r_insn_execbuf -b_r_insn_execbuf: - nop // bx, blx (Rm) - ldr pc, np1 - nop - nop - nop - nop // retbreak - nop -np1: nop // stored PC-4 (next insn addr) - - .global b_cond_insn_execbuf -b_cond_insn_execbuf: - beq condway - ldr pc, np2 -condway: ldr pc, bd2 - nop - nop - nop // retbreak -bd2: nop // branch displacement -np2: nop // stored PC-4 (next insn addr) - - .global blx_off_insn_execbuf -blx_off_insn_execbuf: - ldreq lr, bd3 - blxeq lr - ldr pc, np3 - nop - nop - nop // retbreak -bd3: nop // branch displacement -np3: nop // stored PC-4 (next insn addr) diff --git a/uprobe/Kbuild b/uprobe/Kbuild index e4e96f0..08fb3dd 100644 --- a/uprobe/Kbuild +++ b/uprobe/Kbuild @@ -7,9 +7,9 @@ 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 \ - arch/arm/swap-asm/decode_thumb.o \ - arch/arm/swap-asm/thumb_tramps.o + ../arch/arm/probes/probes_thumb.o \ + ../arch/arm/probes/tramps_thumb.o \ + ../arch/arm/probes/decode_thumb.o ### ARM64 diff --git a/uprobe/arch/arm/swap-asm/swap_uprobes.c b/uprobe/arch/arm/swap-asm/swap_uprobes.c index 771906d..2b9be2e 100644 --- a/uprobe/arch/arm/swap-asm/swap_uprobes.c +++ b/uprobe/arch/arm/swap-asm/swap_uprobes.c @@ -38,15 +38,12 @@ #include #include -#include #include #include - +#include +#include #include -#include -#include "decode_thumb.h" #include "swap_uprobes.h" -#include "trampoline_thumb.h" #define UBP_ARM (BREAKPOINT_INSTRUCTION) @@ -60,516 +57,6 @@ flush_icache_range((unsigned long)(addr), \ (unsigned long)(addr) + (size)) -static inline long branch_t16_dest(uprobe_opcode_t insn, unsigned int insn_addr) -{ - long offset = insn & 0x3ff; - offset -= insn & 0x400; - return insn_addr + 4 + offset * 2; -} - -static inline long branch_cond_t16_dest(uprobe_opcode_t insn, - unsigned int insn_addr) -{ - long offset = insn & 0x7f; - offset -= insn & 0x80; - return insn_addr + 4 + offset * 2; -} - -static inline long branch_t32_dest(uprobe_opcode_t insn, unsigned int insn_addr) -{ - unsigned int poff = insn & 0x3ff; - unsigned int offset = (insn & 0x07fe0000) >> 17; - - poff -= (insn & 0x400); - - if (insn & (1 << 12)) - return insn_addr + 4 + (poff << 12) + offset * 4; - else - return (insn_addr + 4 + (poff << 12) + offset * 4) & ~3; -} - -static inline long cbz_t16_dest(uprobe_opcode_t insn, unsigned int insn_addr) -{ - unsigned int i = (insn & 0x200) >> 3; - unsigned int offset = (insn & 0xf8) >> 2; - return insn_addr + 4 + i + offset; -} - -/* is instruction Thumb2 and NOT a branch, etc... */ -static int is_thumb2(uprobe_opcode_t insn) -{ - return ((insn & 0xf800) == 0xe800 || - (insn & 0xf800) == 0xf000 || - (insn & 0xf800) == 0xf800); -} - -static int arch_check_insn_thumb(unsigned long insn) -{ - int ret = 0; - - /* check instructions that can change PC */ - if (THUMB_INSN_MATCH(UNDEF, insn) || - THUMB2_INSN_MATCH(BLX1, insn) || - THUMB2_INSN_MATCH(BL, insn) || - THUMB_INSN_MATCH(SWI, insn) || - THUMB_INSN_MATCH(BREAK, insn) || - THUMB2_INSN_MATCH(B1, insn) || - THUMB2_INSN_MATCH(B2, insn) || - THUMB2_INSN_MATCH(BXJ, insn) || - (THUMB2_INSN_MATCH(ADR, insn) && - THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(LDRW, insn) && THUMB2_INSN_REG_RT(insn) == 15) || - (THUMB2_INSN_MATCH(LDRW1, insn) && - THUMB2_INSN_REG_RT(insn) == 15) || - (THUMB2_INSN_MATCH(LDRHW, insn) && - THUMB2_INSN_REG_RT(insn) == 15) || - (THUMB2_INSN_MATCH(LDRHW1, insn) && - THUMB2_INSN_REG_RT(insn) == 15) || - (THUMB2_INSN_MATCH(LDRWL, insn) && - THUMB2_INSN_REG_RT(insn) == 15) || - THUMB2_INSN_MATCH(LDMIA, insn) || - THUMB2_INSN_MATCH(LDMDB, insn) || - (THUMB2_INSN_MATCH(DP, insn) && THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(RSBW, insn) && THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(RORW, insn) && THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(ROR, insn) && THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(LSLW1, insn) && - THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(LSLW2, insn) && - THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(LSRW1, insn) && - THUMB2_INSN_REG_RD(insn) == 15) || - (THUMB2_INSN_MATCH(LSRW2, insn) && - THUMB2_INSN_REG_RD(insn) == 15) || - /* skip PC, #-imm12 -> SP, #-imm8 and Tegra-hanging instructions */ - (THUMB2_INSN_MATCH(STRW1, insn) && - THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_MATCH(STRBW1, insn) && - THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_MATCH(STRHW1, insn) && - THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_MATCH(STRW, insn) && THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_MATCH(STRHW, insn) && - THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_MATCH(LDRW, insn) && THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_MATCH(LDRBW, insn) && - THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_MATCH(LDRHW, insn) && - THUMB2_INSN_REG_RN(insn) == 15) || - /* skip STRDx/LDRDx Rt, Rt2, [Rd, ...] */ - (THUMB2_INSN_MATCH(LDRD, insn) || THUMB2_INSN_MATCH(LDRD1, insn) || - THUMB2_INSN_MATCH(STRD, insn))) { - ret = -EFAULT; - } - - return ret; -} - -static int prep_pc_dep_insn_execbuf_thumb(uprobe_opcode_t *insns, - uprobe_opcode_t insn, int uregs) -{ - unsigned char mreg = 0; - unsigned char reg = 0; - - if (THUMB_INSN_MATCH(APC, insn) || - THUMB_INSN_MATCH(LRO3, insn)) { - reg = ((insn & 0xffff) & uregs) >> 8; - } else if (THUMB_INSN_MATCH(MOV3, insn)) { - if (((((unsigned char)insn) & 0xff) >> 3) == 15) - reg = (insn & 0xffff) & uregs; - else - return 0; - } else if (THUMB2_INSN_MATCH(ADR, insn)) { - reg = ((insn >> 16) & uregs) >> 8; - if (reg == 15) - return 0; - } else if (THUMB2_INSN_MATCH(LDRW, insn) || - THUMB2_INSN_MATCH(LDRW1, insn) || - THUMB2_INSN_MATCH(LDRHW, insn) || - THUMB2_INSN_MATCH(LDRHW1, insn) || - THUMB2_INSN_MATCH(LDRWL, insn)) { - reg = ((insn >> 16) & uregs) >> 12; - if (reg == 15) - return 0; - /* - * LDRB.W PC, [PC, #immed] => PLD [PC, #immed], so Rt == PC is skipped - */ - } else if (THUMB2_INSN_MATCH(LDRBW, insn) || - THUMB2_INSN_MATCH(LDRBW1, insn) || - THUMB2_INSN_MATCH(LDREX, insn)) { - reg = ((insn >> 16) & uregs) >> 12; - } else if (THUMB2_INSN_MATCH(DP, insn)) { - reg = ((insn >> 16) & uregs) >> 12; - if (reg == 15) - return 0; - } else if (THUMB2_INSN_MATCH(RSBW, insn)) { - reg = ((insn >> 12) & uregs) >> 8; - if (reg == 15) - return 0; - } else if (THUMB2_INSN_MATCH(RORW, insn)) { - reg = ((insn >> 12) & uregs) >> 8; - if (reg == 15) - return 0; - } else if (THUMB2_INSN_MATCH(ROR, insn) || - THUMB2_INSN_MATCH(LSLW1, insn) || - THUMB2_INSN_MATCH(LSLW2, insn) || - THUMB2_INSN_MATCH(LSRW1, insn) || - THUMB2_INSN_MATCH(LSRW2, insn)) { - reg = ((insn >> 12) & uregs) >> 8; - if (reg == 15) - return 0; - } else if (THUMB2_INSN_MATCH(TEQ1, insn) || - THUMB2_INSN_MATCH(TST1, insn)) { - reg = 15; - } else if (THUMB2_INSN_MATCH(TEQ2, insn) || - THUMB2_INSN_MATCH(TST2, insn)) { - reg = THUMB2_INSN_REG_RM(insn); - } - - if ((THUMB2_INSN_MATCH(STRW, insn) || - THUMB2_INSN_MATCH(STRBW, insn) || - THUMB2_INSN_MATCH(STRD, insn) || - THUMB2_INSN_MATCH(STRHT, insn) || - THUMB2_INSN_MATCH(STRT, insn) || - THUMB2_INSN_MATCH(STRHW1, insn) || - THUMB2_INSN_MATCH(STRHW, insn)) && - THUMB2_INSN_REG_RT(insn) == 15) { - reg = THUMB2_INSN_REG_RT(insn); - } - - if (reg == 6 || reg == 7) { - *((unsigned short *)insns + 0) = - (*((unsigned short *)insns + 0) & 0x00ff) | - ((1 << mreg) | (1 << (mreg + 1))); - *((unsigned short *)insns + 1) = - (*((unsigned short *)insns + 1) & 0xf8ff) | (mreg << 8); - *((unsigned short *)insns + 2) = - (*((unsigned short *)insns + 2) & 0xfff8) | (mreg + 1); - *((unsigned short *)insns + 3) = - (*((unsigned short *)insns + 3) & 0xffc7) | (mreg << 3); - *((unsigned short *)insns + 7) = - (*((unsigned short *)insns + 7) & 0xf8ff) | (mreg << 8); - *((unsigned short *)insns + 8) = - (*((unsigned short *)insns + 8) & 0xffc7) | (mreg << 3); - *((unsigned short *)insns + 9) = - (*((unsigned short *)insns + 9) & 0xffc7) | - ((mreg + 1) << 3); - *((unsigned short *)insns + 10) = - (*((unsigned short *)insns + 10) & 0x00ff) | - ((1 << mreg) | (1 << (mreg + 1))); - } - - if (THUMB_INSN_MATCH(APC, insn)) { - /* ADD Rd, PC, #immed_8*4 -> ADD Rd, SP, #immed_8*4 */ - *((unsigned short *)insns + 4) = ((insn & 0xffff) | 0x800); - } else if (THUMB_INSN_MATCH(LRO3, insn)) { - /* LDR Rd, [PC, #immed_8*4] -> - * LDR Rd, [SP, #immed_8*4] */ - *((unsigned short *)insns + 4) = - ((insn & 0xffff) + 0x5000); - } else if (THUMB_INSN_MATCH(MOV3, insn)) { - /* MOV Rd, PC -> MOV Rd, SP */ - *((unsigned short *)insns + 4) = - ((insn & 0xffff) ^ 0x10); - } else if (THUMB2_INSN_MATCH(ADR, insn)) { - /* ADDW Rd,PC,#imm -> ADDW Rd,SP,#imm */ - insns[2] = (insn & 0xfffffff0) | 0x0d; - } else if (THUMB2_INSN_MATCH(LDRW, insn) || - THUMB2_INSN_MATCH(LDRBW, insn) || - THUMB2_INSN_MATCH(LDRHW, insn)) { - /* LDR.W Rt, [PC, #-] -> - * LDR.W Rt, [SP, #-] - * !!!!!!!!!!!!!!!!!!!!!!!! - * !!! imm_12 vs. imm_8 !!! - * !!!!!!!!!!!!!!!!!!!!!!!! */ - insns[2] = (insn & 0xf0fffff0) | 0x0c00000d; - } else if (THUMB2_INSN_MATCH(LDRW1, insn) || - THUMB2_INSN_MATCH(LDRBW1, insn) || - THUMB2_INSN_MATCH(LDRHW1, insn) || - THUMB2_INSN_MATCH(LDRD, insn) || - THUMB2_INSN_MATCH(LDRD1, insn) || - THUMB2_INSN_MATCH(LDREX, insn)) { - /* LDRx.W Rt, [PC, #+] -> - * LDRx.W Rt, [SP, #+] - (+/-imm_8 for LDRD Rt, Rt2, [PC, #] */ - insns[2] = (insn & 0xfffffff0) | 0xd; - } else if (THUMB2_INSN_MATCH(MUL, insn)) { - /* MUL Rd, Rn, SP */ - insns[2] = (insn & 0xfff0ffff) | 0x000d0000; - } else if (THUMB2_INSN_MATCH(DP, insn)) { - if (THUMB2_INSN_REG_RM(insn) == 15) - /* DP Rd, Rn, PC */ - insns[2] = (insn & 0xfff0ffff) | 0x000d0000; - else if (THUMB2_INSN_REG_RN(insn) == 15) - /* DP Rd, PC, Rm */ - insns[2] = (insn & 0xfffffff0) | 0xd; - } else if (THUMB2_INSN_MATCH(LDRWL, insn)) { - /* LDRx.W Rt, [PC, #] -> - * LDRx.W Rt, [SP, #+] - * (+/-imm_8 for LDRD Rt, Rt2, [PC, #] */ - insns[2] = (insn & 0xfffffff0) | 0xd; - } else if (THUMB2_INSN_MATCH(RSBW, insn)) { - /* RSB{S}.W Rd, PC, # -> RSB{S}.W Rd, SP, # */ - insns[2] = (insn & 0xfffffff0) | 0xd; - } else if (THUMB2_INSN_MATCH(RORW, insn) || - THUMB2_INSN_MATCH(LSLW1, insn) || - THUMB2_INSN_MATCH(LSRW1, insn)) { - if ((THUMB2_INSN_REG_RM(insn) == 15) && - (THUMB2_INSN_REG_RN(insn) == 15)) - /* ROR.W Rd, PC, PC */ - insns[2] = (insn & 0xfffdfffd); - else if (THUMB2_INSN_REG_RM(insn) == 15) - /* ROR.W Rd, Rn, PC */ - insns[2] = (insn & 0xfff0ffff) | 0xd0000; - else if (THUMB2_INSN_REG_RN(insn) == 15) - /* ROR.W Rd, PC, Rm */ - insns[2] = (insn & 0xfffffff0) | 0xd; - } else if (THUMB2_INSN_MATCH(ROR, insn) || - THUMB2_INSN_MATCH(LSLW2, insn) || - THUMB2_INSN_MATCH(LSRW2, insn)) { - /* ROR{S} Rd, PC, # -> ROR{S} Rd, SP, # */ - insns[2] = (insn & 0xfff0ffff) | 0xd0000; - } - - if (THUMB2_INSN_MATCH(STRW, insn) || - THUMB2_INSN_MATCH(STRBW, insn)) { - /* STRx.W Rt, [Rn, SP] */ - insns[2] = (insn & 0xfff0ffff) | 0x000d0000; - } else if (THUMB2_INSN_MATCH(STRD, insn) || - THUMB2_INSN_MATCH(STRHT, insn) || - THUMB2_INSN_MATCH(STRT, insn) || - THUMB2_INSN_MATCH(STRHW1, insn)) { - if (THUMB2_INSN_REG_RN(insn) == 15) - /* STRD/T/HT{.W} Rt, [SP, ...] */ - insns[2] = (insn & 0xfffffff0) | 0xd; - else - insns[2] = insn; - } else if (THUMB2_INSN_MATCH(STRHW, insn) && - (THUMB2_INSN_REG_RN(insn) == 15)) { - if (THUMB2_INSN_REG_RN(insn) == 15) - /* STRH.W Rt, [SP, #-] */ - insns[2] = (insn & 0xf0fffff0) | 0x0c00000d; - else - insns[2] = insn; - } - - /* STRx PC, xxx */ - if ((reg == 15) && (THUMB2_INSN_MATCH(STRW, insn) || - THUMB2_INSN_MATCH(STRBW, insn) || - THUMB2_INSN_MATCH(STRD, insn) || - THUMB2_INSN_MATCH(STRHT, insn) || - THUMB2_INSN_MATCH(STRT, insn) || - THUMB2_INSN_MATCH(STRHW1, insn) || - THUMB2_INSN_MATCH(STRHW, insn))) { - insns[2] = (insns[2] & 0x0fffffff) | 0xd0000000; - } - - if (THUMB2_INSN_MATCH(TEQ1, insn) || - THUMB2_INSN_MATCH(TST1, insn)) { - /* TEQ SP, # */ - insns[2] = (insn & 0xfffffff0) | 0xd; - } else if (THUMB2_INSN_MATCH(TEQ2, insn) || - THUMB2_INSN_MATCH(TST2, insn)) { - if ((THUMB2_INSN_REG_RN(insn) == 15) && - (THUMB2_INSN_REG_RM(insn) == 15)) - /* TEQ/TST PC, PC */ - insns[2] = (insn & 0xfffdfffd); - else if (THUMB2_INSN_REG_RM(insn) == 15) - /* TEQ/TST Rn, PC */ - insns[2] = (insn & 0xfff0ffff) | 0xd0000; - else if (THUMB2_INSN_REG_RN(insn) == 15) - /* TEQ/TST PC, Rm */ - insns[2] = (insn & 0xfffffff0) | 0xd; - } - - return 0; -} - -static int arch_make_trampoline_thumb(unsigned long vaddr, unsigned long insn, - unsigned long *tramp, size_t tramp_len) -{ - int ret; - int uregs = 0; - int pc_dep = 0; - unsigned int addr; - - ret = arch_check_insn_thumb(insn); - if (ret) { - pr_err("THUMB inst isn't support vaddr=%lx insn=%08lx\n", - vaddr, insn); - return ret; - } - - if (THUMB_INSN_MATCH(APC, insn) || THUMB_INSN_MATCH(LRO3, insn)) { - uregs = 0x0700; /* 8-10 */ - pc_dep = 1; - } else if (THUMB_INSN_MATCH(MOV3, insn) && - (((((unsigned char)insn) & 0xff) >> 3) == 15)) { - /* MOV Rd, PC */ - uregs = 0x07; - pc_dep = 1; - } else if THUMB2_INSN_MATCH(ADR, insn) { - uregs = 0x0f00; /* Rd 8-11 */ - pc_dep = 1; - } else if (((THUMB2_INSN_MATCH(LDRW, insn) || - THUMB2_INSN_MATCH(LDRW1, insn) || - THUMB2_INSN_MATCH(LDRBW, insn) || - THUMB2_INSN_MATCH(LDRBW1, insn) || - THUMB2_INSN_MATCH(LDRHW, insn) || - THUMB2_INSN_MATCH(LDRHW1, insn) || - THUMB2_INSN_MATCH(LDRWL, insn)) && - THUMB2_INSN_REG_RN(insn) == 15) || - THUMB2_INSN_MATCH(LDREX, insn) || - ((THUMB2_INSN_MATCH(STRW, insn) || - THUMB2_INSN_MATCH(STRBW, insn) || - THUMB2_INSN_MATCH(STRHW, insn) || - THUMB2_INSN_MATCH(STRHW1, insn)) && - (THUMB2_INSN_REG_RN(insn) == 15 || - THUMB2_INSN_REG_RT(insn) == 15)) || - ((THUMB2_INSN_MATCH(STRT, insn) || - THUMB2_INSN_MATCH(STRHT, insn)) && - (THUMB2_INSN_REG_RN(insn) == 15 || - THUMB2_INSN_REG_RT(insn) == 15))) { - uregs = 0xf000; /* Rt 12-15 */ - pc_dep = 1; - } else if ((THUMB2_INSN_MATCH(LDRD, insn) || - THUMB2_INSN_MATCH(LDRD1, insn)) && - (THUMB2_INSN_REG_RN(insn) == 15)) { - uregs = 0xff00; /* Rt 12-15, Rt2 8-11 */ - pc_dep = 1; - } else if (THUMB2_INSN_MATCH(MUL, insn) && - THUMB2_INSN_REG_RM(insn) == 15) { - uregs = 0xf; - pc_dep = 1; - } else if (THUMB2_INSN_MATCH(DP, insn) && - (THUMB2_INSN_REG_RN(insn) == 15 || - THUMB2_INSN_REG_RM(insn) == 15)) { - uregs = 0xf000; /* Rd 12-15 */ - pc_dep = 1; - } else if (THUMB2_INSN_MATCH(STRD, insn) && - ((THUMB2_INSN_REG_RN(insn) == 15) || - (THUMB2_INSN_REG_RT(insn) == 15) || - THUMB2_INSN_REG_RT2(insn) == 15)) { - uregs = 0xff00; /* Rt 12-15, Rt2 8-11 */ - pc_dep = 1; - } else if (THUMB2_INSN_MATCH(RSBW, insn) && - THUMB2_INSN_REG_RN(insn) == 15) { - uregs = 0x0f00; /* Rd 8-11 */ - pc_dep = 1; - } else if (THUMB2_INSN_MATCH(RORW, insn) && - (THUMB2_INSN_REG_RN(insn) == 15 || - THUMB2_INSN_REG_RM(insn) == 15)) { - uregs = 0x0f00; - pc_dep = 1; - } else if ((THUMB2_INSN_MATCH(ROR, insn) || - THUMB2_INSN_MATCH(LSLW2, insn) || - THUMB2_INSN_MATCH(LSRW2, insn)) && - THUMB2_INSN_REG_RM(insn) == 15) { - uregs = 0x0f00; /* Rd 8-11 */ - pc_dep = 1; - } else if ((THUMB2_INSN_MATCH(LSLW1, insn) || - THUMB2_INSN_MATCH(LSRW1, insn)) && - (THUMB2_INSN_REG_RN(insn) == 15 || - THUMB2_INSN_REG_RM(insn) == 15)) { - uregs = 0x0f00; /* Rd 8-11 */ - pc_dep = 1; - } else if ((THUMB2_INSN_MATCH(TEQ1, insn) || - THUMB2_INSN_MATCH(TST1, insn)) && - THUMB2_INSN_REG_RN(insn) == 15) { - uregs = 0xf0000; /* Rn 0-3 (16-19) */ - pc_dep = 1; - } else if ((THUMB2_INSN_MATCH(TEQ2, insn) || - THUMB2_INSN_MATCH(TST2, insn)) && - (THUMB2_INSN_REG_RN(insn) == 15 || - THUMB2_INSN_REG_RM(insn) == 15)) { - uregs = 0xf0000; /* Rn 0-3 (16-19) */ - pc_dep = 1; - } - - if (unlikely(uregs && pc_dep)) { - memcpy(tramp, pc_dep_insn_execbuf_thumb, tramp_len); - prep_pc_dep_insn_execbuf_thumb(tramp, insn, uregs); - - addr = vaddr + 4; - *((unsigned short *)tramp + 13) = 0xdeff; - *((unsigned short *)tramp + 14) = addr & 0x0000ffff; - *((unsigned short *)tramp + 15) = addr >> 16; - if (!is_thumb2(insn)) { - addr = vaddr + 2; - *((unsigned short *)tramp + 16) = - (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 17) = addr >> 16; - } else { - addr = vaddr + 4; - *((unsigned short *)tramp + 16) = - (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 17) = addr >> 16; - } - } else { - memcpy(tramp, gen_insn_execbuf_thumb, tramp_len); - *((unsigned short *)tramp + 13) = 0xdeff; - if (!is_thumb2(insn)) { - addr = vaddr + 2; - *((unsigned short *)tramp + 2) = insn; - *((unsigned short *)tramp + 16) = - (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 17) = addr >> 16; - } else { - addr = vaddr + 4; - tramp[1] = insn; - *((unsigned short *)tramp + 16) = - (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 17) = addr >> 16; - } - } - - if (THUMB_INSN_MATCH(B2, insn)) { - memcpy(tramp, b_off_insn_execbuf_thumb, tramp_len); - *((unsigned short *)tramp + 13) = 0xdeff; - addr = branch_t16_dest(insn, vaddr); - *((unsigned short *)tramp + 14) = (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 15) = addr >> 16; - *((unsigned short *)tramp + 16) = 0; - *((unsigned short *)tramp + 17) = 0; - - } else if (THUMB_INSN_MATCH(B1, insn)) { - memcpy(tramp, b_cond_insn_execbuf_thumb, tramp_len); - *((unsigned short *)tramp + 13) = 0xdeff; - *((unsigned short *)tramp + 0) |= (insn & 0xf00); - addr = branch_cond_t16_dest(insn, vaddr); - *((unsigned short *)tramp + 14) = (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 15) = addr >> 16; - addr = vaddr + 2; - *((unsigned short *)tramp + 16) = (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 17) = addr >> 16; - - } else if (THUMB_INSN_MATCH(BLX2, insn) || - THUMB_INSN_MATCH(BX, insn)) { - memcpy(tramp, b_r_insn_execbuf_thumb, tramp_len); - *((unsigned short *)tramp + 13) = 0xdeff; - *((unsigned short *)tramp + 4) = insn; - addr = vaddr + 2; - *((unsigned short *)tramp + 16) = (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 17) = addr >> 16; - - } else if (THUMB_INSN_MATCH(CBZ, insn)) { - memcpy(tramp, cbz_insn_execbuf_thumb, tramp_len); - *((unsigned short *)tramp + 13) = 0xdeff; - /* zero out original branch displacement (imm5 = 0; i = 0) */ - *((unsigned short *)tramp + 0) = insn & (~0x2f8); - /* replace it with 8 bytes offset in execbuf (imm5 = 0b00010) */ - *((unsigned short *)tramp + 0) |= 0x20; - addr = cbz_t16_dest(insn, vaddr); - *((unsigned short *)tramp + 14) = (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 15) = addr >> 16; - addr = vaddr + 2; - *((unsigned short *)tramp + 16) = (addr & 0x0000ffff) | 0x1; - *((unsigned short *)tramp + 17) = addr >> 16; - } - - return 0; -} - /** * @brief Prepares uprobe for ARM. * @@ -593,26 +80,10 @@ int arch_prepare_uprobe(struct uprobe *p) return -EINVAL; } - if (thumb_mode) { - ret = arch_make_trampoline_thumb(vaddr, insn, - tramp, tramp_len); - if (ret) { - struct decode_info info = { - .vaddr = vaddr, - .tramp = tramp, - .handeler = NULL, - }; - - ret = decode_thumb(insn, &info); - if (info.handeler) { - unsigned short *tr = (unsigned short *)tramp; - tr[13] = 0xdeff; /* breakpoint for uretprobe */ - p->ainsn.handler = info.handeler; - } - } - } else { - ret = arch_make_trampoline_arm(vaddr, insn, tramp); - } + if (thumb_mode) + ret = make_trampoline_thumb(p, vaddr, insn, tramp, tramp_len); + else + ret = make_trampoline_arm(vaddr, insn, tramp); if (ret) { pr_err("failed to make tramp, addr=%p\n", p->addr); @@ -652,13 +123,8 @@ int arch_prepare_uprobe(struct uprobe *p) void arch_opcode_analysis_uretprobe(struct uretprobe *rp) { /* Remove retprobe if first insn overwrites lr */ - rp->thumb_noret = !!(THUMB2_INSN_MATCH(BL, rp->up.opcode) || - THUMB2_INSN_MATCH(BLX1, rp->up.opcode) || - THUMB_INSN_MATCH(BLX2, rp->up.opcode)); - - rp->arm_noret = !!(ARM_INSN_MATCH(BL, rp->up.opcode) || - ARM_INSN_MATCH(BLX1, rp->up.opcode) || - ARM_INSN_MATCH(BLX2, rp->up.opcode)); + rp->thumb_noret = noret_thumb(rp->up.opcode); + rp->arm_noret = noret_arm(rp->up.opcode); } /** diff --git a/uprobe/arch/arm/swap-asm/thumb_tramps.c b/uprobe/arch/arm/swap-asm/thumb_tramps.c deleted file mode 100644 index 07bf494..0000000 --- a/uprobe/arch/arm/swap-asm/thumb_tramps.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 - * - */ - - -#include -#include -#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/trampoline_thumb.S b/uprobe/arch/arm/swap-asm/trampoline_thumb.S deleted file mode 100644 index 5fee5f9..0000000 --- a/uprobe/arch/arm/swap-asm/trampoline_thumb.S +++ /dev/null @@ -1,134 +0,0 @@ - .thumb - - .global gen_insn_execbuf_thumb -gen_insn_execbuf_thumb: - nop - nop - nop // original instruction - nop // original instruction - nop - nop - nop - sub sp, sp, #8 - str r0, [sp, #0] - ldr r0, [pc, #12] - str r0, [sp, #4] - nop - pop {r0, pc} // ssbreak - nop // retbreak - nop - nop - nop // stored PC-4(next insn addr) hi - nop // stored PC-4(next insn addr) lo - - nop - - .global pc_dep_insn_execbuf_thumb - .align 4 -pc_dep_insn_execbuf_thumb: - push {r6, r7} - ldr r6, i1 - mov r7, sp - mov sp, r6 - nop // PC -> SP - nop // PC -> SP - mov sp, r7 - pop {r6, r7} - push {r0, r1} - ldr r0, i2 - nop - str r0, [sp, #4] - pop {r0, pc} // ssbreak - nop // retbreak -i1: nop // stored PC hi - nop // stored PC lo -i2: nop // stored PC-4(next insn addr) hi - nop // stored PC-4(next insn addr) lo - - .global b_r_insn_execbuf_thumb - .align 4 -b_r_insn_execbuf_thumb: - nop - nop - nop - nop - nop // bx,blx (Rm) - nop // - push {r0,r1} - ldr r0, np - nop - str r0, [sp, #4] - pop {r0,pc} - nop - nop // ssbreak - nop // retbreak - nop - nop -np: nop // stored PC-4(next insn addr) hi - nop // stored PC-4(next insn addr) lo - - .global b_off_insn_execbuf_thumb - .align 4 -b_off_insn_execbuf_thumb: - push {r0,r1} - ldr r0, bd - str r0, [sp, #4] - pop {r0, pc} - nop - nop - push {r0,r1} - ldr r0, np2 - nop - str r0, [sp, #4] - pop {r0,pc} - nop - nop // ssbreak - nop // retbreak -bd: nop // branch displacement hi - nop // branch displacement lo -np2: nop // stored PC-4(next insn addr) hi - nop // stored PC-4(next insn addr) lo - - .global b_cond_insn_execbuf_thumb - .align 4 -b_cond_insn_execbuf_thumb: - beq condway - push {r0,r1} - ldr r0, np4 - nop - str r0, [sp, #4] - pop {r0,pc} -condway: push {r0,r1} - ldr r0, bd4 - str r0, [sp, #4] - pop {r0,pc} - nop - nop - nop // ssbreak - nop // retbreak -bd4: nop // branch displacement hi - nop // branch displacement lo -np4: nop // stored PC-4(next insn addr) hi - nop // stored PC-4(next insn addr) lo - - .global cbz_insn_execbuf_thumb - .align 4 -cbz_insn_execbuf_thumb: - nop // cbz - push {r0,r1} - ldr r0, np5 - nop - str r0, [sp, #4] - pop {r0,pc} - push {r0,r1} - ldr r0, bd5 - str r0, [sp, #4] - pop {r0,pc} - nop - nop - nop // ssbreak - nop // retbreak -bd5: nop // branch displacement hi - nop // branch displacement lo -np5: nop // stored PC-4(next insn addr) hi - nop // stored PC-4(next insn addr) lo -- 2.7.4