#include <dbi_kprobes.h>
#include <asm/dbi_kprobes.h>
+#include <asm/trampoline_arm.h>
#include <asm/traps.h>
#include <swap_uprobes.h>
+#include <asm/swap_uprobes.h>
+#include <dbi_insn_slots.h>
+#include <dbi_kprobes_deps.h>
+#include "trampoline_thumb.h"
// FIXME:
#include <dbi_kdebug.h>
-extern struct hlist_head uprobe_insn_pages;
-kprobe_opcode_t *get_insn_slot(struct task_struct *task, struct hlist_head *page_list, int atomic);
-int arch_check_insn_arm(struct arch_specific_insn *ainsn);
-int prep_pc_dep_insn_execbuf(kprobe_opcode_t *insns, kprobe_opcode_t insn, int uregs);
-void free_insn_slot(struct hlist_head *page_list, struct task_struct *task, kprobe_opcode_t *slot);
-void pc_dep_insn_execbuf(void);
-void gen_insn_execbuf(void);
-void gen_insn_execbuf_thumb(void);
-void pc_dep_insn_execbuf_thumb(void);
-int kprobe_trap_handler(struct pt_regs *regs, unsigned int instr);
-void add_rp_inst(struct kretprobe_instance *ri);
-struct kretprobe_instance *get_free_rp_inst (struct kretprobe *rp);
+#define flush_insns(addr, size) \
+ flush_icache_range((unsigned long)(addr), \
+ (unsigned long)(addr) + (size))
+
#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
return 0;
}
-int arch_prepare_uprobe(struct kprobe *p, struct task_struct *task, int atomic)
+int arch_prepare_uprobe(struct uprobe *up, struct hlist_head *page_list, int atomic)
{
int ret = 0;
+ struct kprobe *p = &up->kp;
+ struct task_struct *task = up->task;
kprobe_opcode_t insn[MAX_INSN_SIZE];
if ((unsigned long)p->addr & 0x01) {
}
p->opcode = insn[0];
- p->ainsn.insn_arm = get_insn_slot(task, &uprobe_insn_pages, atomic);
+ p->ainsn.insn_arm = alloc_insn_slot(up->sm);
if (!p->ainsn.insn_arm) {
printk("Error in %s at %d: kprobe slot allocation error (arm)\n", __FILE__, __LINE__);
return -ENOMEM;
ret = arch_copy_trampoline_arm_uprobe(p, task, 1);
if (ret) {
- free_insn_slot(&uprobe_insn_pages, task, p->ainsn.insn_arm);
+ free_insn_slot(up->sm, p->ainsn.insn_arm);
return -EFAULT;
}
- p->ainsn.insn_thumb = get_insn_slot(task, &uprobe_insn_pages, atomic);
+ p->ainsn.insn_thumb = alloc_insn_slot(up->sm);
if (!p->ainsn.insn_thumb) {
printk("Error in %s at %d: kprobe slot allocation error (thumb)\n", __FILE__, __LINE__);
return -ENOMEM;
ret = arch_copy_trampoline_thumb_uprobe(p, task, 1);
if (ret) {
- free_insn_slot(&uprobe_insn_pages, task, p->ainsn.insn_arm);
- free_insn_slot(&uprobe_insn_pages, task, p->ainsn.insn_thumb);
+ free_insn_slot(up->sm, p->ainsn.insn_arm);
+ free_insn_slot(up->sm, p->ainsn.insn_thumb);
return -EFAULT;
}
panic("Failed to write memory %p!\n", p->addr);
}
- free_insn_slot(&uprobe_insn_pages, task, p->ainsn.insn_arm);
- free_insn_slot(&uprobe_insn_pages, task, p->ainsn.insn_thumb);
+ free_insn_slot(up->sm, p->ainsn.insn_arm);
+ free_insn_slot(up->sm, p->ainsn.insn_thumb);
return -EFAULT;
}
return ret;
}
-void arch_prepare_uretprobe_hl(struct kretprobe *rp, struct pt_regs *regs)
+void arch_prepare_uretprobe_hl(struct uretprobe_instance *ri,
+ struct pt_regs *regs)
{
- struct kretprobe_instance *ri;
-
- /* TODO: test - remove retprobe after func entry but before its exit */
- if ((ri = get_free_rp_inst(rp)) != NULL) {
- ri->rp = rp;
- ri->rp2 = NULL;
- ri->task = current;
- ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr;
- ri->sp = (kprobe_opcode_t *)regs->ARM_sp;
-
- if (thumb_mode(regs)) {
- regs->ARM_lr = (unsigned long)(rp->kp.ainsn.insn) + 0x1b;
- } else {
- regs->ARM_lr = (unsigned long)(rp->kp.ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
- }
+ ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr;
+ ri->sp = (kprobe_opcode_t *)regs->ARM_sp;
+
+ /* Set flag of current mode */
+ ri->sp = (kprobe_opcode_t *)((long)ri->sp | !!thumb_mode(regs));
- add_rp_inst(ri);
+ if (thumb_mode(regs)) {
+ regs->ARM_lr = (unsigned long)(ri->rp->up.kp.ainsn.insn) + 0x1b;
} else {
- ++rp->nmissed;
+ regs->ARM_lr = (unsigned long)(ri->rp->up.kp.ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
}
}
int setjmp_upre_handler(struct kprobe *p, struct pt_regs *regs)
{
- struct jprobe *jp = container_of(p, struct jprobe, kp);
+ struct uprobe *up = container_of(p, struct uprobe, kp);
+ struct ujprobe *jp = container_of(up, struct ujprobe, up);
+
kprobe_pre_entry_handler_t pre_entry = (kprobe_pre_entry_handler_t)jp->pre_entry;
entry_point_t entry = (entry_point_t)jp->entry;
dbi_arch_uprobe_return();
}
- prepare_singlestep(p, regs);
-
- return 1;
+ return 0;
}
-int trampoline_uprobe_handler(struct kprobe *p, struct pt_regs *regs)
+unsigned long arch_get_trampoline_addr(struct kprobe *p, struct pt_regs *regs)
{
- struct kretprobe_instance *ri = NULL;
- struct hlist_head *head;
- struct hlist_node *node, *tmp;
- unsigned long flags, orig_ret_address = 0;
- unsigned long trampoline_address = 0;
- struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ return thumb_mode(regs) ?
+ (unsigned long)(p->ainsn.insn) + 0x1b :
+ (unsigned long)(p->ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
+}
- if (thumb_mode(regs)) {
- trampoline_address = (unsigned long)(p->ainsn.insn) + 0x1b;
- } else {
- trampoline_address = (unsigned long)(p->ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
- }
-
- spin_lock_irqsave(&kretprobe_lock, flags);
-
- head = kretprobe_inst_table_head(current->mm);
-
- /*
- * It is possible to have multiple instances associated with a given
- * task either because an multiple functions in the call path
- * have a return probe installed on them, and/or more then one
- * return probe was registered for a target function.
- *
- * We can handle this because:
- * - instances are always inserted at the head of the list
- * - when multiple return probes are registered for the same
- * function, the first instance's ret_addr will point to the
- * real return address, and all the rest will point to
- * kretprobe_trampoline
- */
- hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
- if (ri->task != current) {
- /* another task is sharing our hash bucket */
- continue;
- }
+void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs)
+{
+ regs->ARM_lr = orig_ret_addr;
+ regs->ARM_pc = orig_ret_addr & ~0x1;
- if (ri->rp && ri->rp->handler) {
- ri->rp->handler(ri, regs, ri->rp->priv_arg);
- }
+ if (regs->ARM_lr & 0x1)
+ regs->ARM_cpsr |= PSR_T_BIT;
+ else
+ regs->ARM_cpsr &= ~PSR_T_BIT;
+}
- orig_ret_address = (unsigned long)ri->ret_addr;
- recycle_rp_inst(ri);
+static int check_validity_insn(struct kprobe *p, struct pt_regs *regs)
+{
+ struct kprobe *kp;
- if (orig_ret_address != trampoline_address) {
- /*
- * This is the real return address. Any other
- * instances associated with this task are for
- * other calls deeper on the call stack
- */
- break;
+ if (unlikely(thumb_mode(regs))) {
+ if (p->safe_thumb == -1) {
+ goto disarm;
}
- }
- regs->ARM_pc = orig_ret_address;
-
- if(p) {
- if (thumb_mode(regs) && !(regs->ARM_lr & 0x01)) {
- regs->ARM_cpsr &= 0xFFFFFFDF;
- } else if (user_mode(regs) && (regs->ARM_lr & 0x01)) {
- regs->ARM_cpsr |= 0x20;
+ p->ainsn.insn = p->ainsn.insn_thumb;
+ list_for_each_entry_rcu(kp, &p->list, list) {
+ kp->ainsn.insn = p->ainsn.insn_thumb;
+ }
+ } else {
+ if (p->safe_arm == -1) {
+ goto disarm;
}
- if (kcb->kprobe_status == KPROBE_REENTER) {
- restore_previous_kprobe(kcb);
- } else {
- reset_current_kprobe();
+ p->ainsn.insn = p->ainsn.insn_arm;
+ list_for_each_entry_rcu(kp, &p->list, list) {
+ kp->ainsn.insn = p->ainsn.insn_arm;
}
}
- spin_unlock_irqrestore(&kretprobe_lock, flags);
+ return 0;
- /*
- * By returning a non-zero value, we are telling
- * kprobe_handler() that we don't want the post_handler
- * to run (and have re-enabled preemption)
- */
+disarm:
+ printk("Error in %s at %d: we are in arm mode (!) and check "
+ "instruction was fail (%0lX instruction at %p address)!\n",
+ __FILE__, __LINE__, p->opcode, p->addr);
- return 1;
+ /* Test case when we do our actions on already running application */
+ disarm_uprobe(kp2up(p));
+ return -1;
}
-static int check_validity_insn(struct kprobe *p, struct pt_regs *regs, struct task_struct *task)
+static void restore_opcode_for_thumb(struct kprobe *p, struct pt_regs *regs)
{
- struct kprobe *kp;
-
- if (unlikely(thumb_mode(regs))) {
- if (p->safe_thumb != -1) {
- p->ainsn.insn = p->ainsn.insn_thumb;
- list_for_each_entry_rcu(kp, &p->list, list) {
- kp->ainsn.insn = p->ainsn.insn_thumb;
- }
- } else {
- printk("Error in %s at %d: we are in thumb mode (!) and check instruction was fail \
- (%0lX instruction at %p address)!\n", __FILE__, __LINE__, p->opcode, p->addr);
- // Test case when we do our actions on already running application
- arch_disarm_uprobe(p, task);
- return -1;
- }
- } else {
- if (p->safe_arm != -1) {
- p->ainsn.insn = p->ainsn.insn_arm;
- list_for_each_entry_rcu(kp, &p->list, list) {
- kp->ainsn.insn = p->ainsn.insn_arm;
- }
- } else {
- printk("Error in %s at %d: we are in arm mode (!) and check instruction was fail \
- (%0lX instruction at %p address)!\n", __FILE__, __LINE__, p->opcode, p->addr);
- // Test case when we do our actions on already running application
- arch_disarm_uprobe(p, task);
- return -1;
- }
+ if (thumb_mode(regs) && !is_thumb2(p->opcode)) {
+ u16 tmp = p->opcode >> 16;
+ write_proc_vm_atomic(current,
+ (unsigned long)((u16*)p->addr + 1), &tmp, 2);
+ flush_insns(p->addr, 4);
}
-
- return 0;
}
static int uprobe_handler(struct pt_regs *regs)
{
- int err_out = 0;
- char *msg_out = NULL;
+ kprobe_opcode_t *addr = (kprobe_opcode_t *)(regs->ARM_pc);
struct task_struct *task = current;
pid_t tgid = task->tgid;
- kprobe_opcode_t *addr = (kprobe_opcode_t *)(regs->ARM_pc);
- struct kprobe *p = NULL;
- int ret = 0, retprobe = 0;
- struct kprobe_ctlblk *kcb;
+ struct kprobe *p;
-#ifdef SUPRESS_BUG_MESSAGES
- int swap_oops_in_progress;
- // oops_in_progress used to avoid BUG() messages that slow down kprobe_handler() execution
- swap_oops_in_progress = oops_in_progress;
- oops_in_progress = 1;
-#endif
-
- p = get_uprobe(addr, tgid);
-
- if (p && (check_validity_insn(p, regs, task) != 0)) {
- goto no_uprobe_live;
- }
-
- /* We're in an interrupt, but this is clear and BUG()-safe. */
- kcb = get_kprobe_ctlblk();
+ p = get_ukprobe(addr, tgid);
if (p == NULL) {
- p = get_kprobe_by_insn_slot(addr, tgid, regs);
+ p = get_ukprobe_by_insn_slot(addr, tgid, regs);
if (p == NULL) {
- /* Not one of ours: let kernel handle it */
- goto no_uprobe;
+ printk("no_uprobe\n");
+ return 1;
}
- retprobe = 1;
- }
-
- /* restore opcode for thumb app */
- if (thumb_mode(regs)) {
- if (!is_thumb2(p->opcode)) {
- unsigned long tmp = p->opcode >> 16;
- write_proc_vm_atomic(task, (unsigned long)((unsigned short*)p->addr + 1), &tmp, 2);
-
- // "2*sizeof(kprobe_opcode_t)" - strange. Should be "sizeof(kprobe_opcode_t)", need to test
- flush_icache_range((unsigned int)p->addr, ((unsigned int)p->addr) + (2 * sizeof(kprobe_opcode_t)));
- }
- }
-
- set_current_kprobe(p, NULL, NULL);
- kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+ trampoline_uprobe_handler(p, regs);
+ } else if (check_validity_insn(p, regs) != 0) {
+ printk("no_uprobe live\n");
+ } else {
+ restore_opcode_for_thumb(p, regs);
- if (retprobe) {
- ret = trampoline_uprobe_handler(p, regs);
- } else if (p->pre_handler) {
- ret = p->pre_handler(p, regs);
- if(p->pre_handler != trampoline_uprobe_handler) {
- reset_current_kprobe();
+ if (!p->pre_handler || !p->pre_handler(p, regs)) {
+ prepare_singlestep(p, regs);
}
}
- if (ret) {
- /* handler has already set things up, so skip ss setup */
- err_out = 0;
- goto out;
- }
-
-no_uprobe:
- msg_out = "no_uprobe\n";
- err_out = 1; // return with death
- goto out;
-
-no_uprobe_live:
- msg_out = "no_uprobe live\n";
- err_out = 0; // ok - life is life
- goto out;
-
-out:
-#ifdef SUPRESS_BUG_MESSAGES
- oops_in_progress = swap_oops_in_progress;
-#endif
-
- if(msg_out) {
- printk(msg_out);
- }
-
- return err_out;
+ return 0;
}
int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)