[REFACTOR] create trampoline_uprobe_handler()
authorVyacheslav Cherkashin <v.cherkashin@samsung.com>
Tue, 19 Mar 2013 17:32:04 +0000 (21:32 +0400)
committerVyacheslav Cherkashin <v.cherkashin@samsung.com>
Tue, 19 Mar 2013 17:32:04 +0000 (21:32 +0400)
uprobe/arch/asm-arm/swap_uprobes.c

index 4dda941..85dbcef 100644 (file)
@@ -641,6 +641,88 @@ int setjmp_upre_handler(struct kprobe *p, struct pt_regs *regs)
        return 1;
 }
 
+int trampoline_uprobe_handler(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();
+
+       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;
+               }
+
+               if (ri->rp && ri->rp->handler) {
+                       ri->rp->handler(ri, regs, ri->rp->priv_arg);
+               }
+
+               orig_ret_address = (unsigned long)ri->ret_addr;
+               recycle_rp_inst(ri);
+
+               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;
+               }
+       }
+
+       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;
+               }
+
+               if (kcb->kprobe_status == KPROBE_REENTER) {
+                       restore_previous_kprobe(kcb);
+               } else {
+                       reset_current_kprobe();
+               }
+       }
+
+       spin_unlock_irqrestore(&kretprobe_lock, flags);
+
+       /*
+        * 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)
+        */
+
+       return 1;
+}
+
 static int check_validity_insn(struct kprobe *p, struct pt_regs *regs, struct task_struct *task)
 {
        struct kprobe *kp;
@@ -728,10 +810,10 @@ static int uprobe_handler(struct pt_regs *regs)
        kcb->kprobe_status = KPROBE_HIT_ACTIVE;
 
        if (retprobe) {
-               ret = trampoline_probe_handler(p, regs);
+               ret = trampoline_uprobe_handler(p, regs);
        } else if (p->pre_handler) {
                ret = p->pre_handler(p, regs);
-               if(p->pre_handler != trampoline_probe_handler) {
+               if(p->pre_handler != trampoline_uprobe_handler) {
                        reset_current_kprobe();
                }
        }