Recursive calls fixed, thumb functions odd address modifications, etc...
authorAlexander Shirshikov <a.shirshikov@samsung.com>
Tue, 8 Feb 2011 12:05:13 +0000 (15:05 +0300)
committerAlexander Shirshikov <a.shirshikov@samsung.com>
Tue, 8 Feb 2011 12:05:13 +0000 (15:05 +0300)
driver/us_proc_inst.c
kprobe/arch/asm-arm/dbi_kprobes.c
kprobe/arch/asm-arm/dbi_kprobes.h
kprobe/arch/asm-arm/dbi_kprobes_thumb.S
kprobe/arch/asm-arm/dbi_kprobes_thumb.h
kprobe/dbi_uprobes.c

index cde8e3c..d232421 100644 (file)
@@ -889,14 +889,34 @@ unsigned long ujprobe_event_pre_handler (us_proc_ip_t * ip, struct pt_regs *regs
 void ujprobe_event_handler (unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5, unsigned long arg6)
 {
        us_proc_ip_t *ip = __get_cpu_var (gpCurIp);
+
+#if defined(CONFIG_ARM)
+       if (ip->offset & 0x01)
+       {
+               pack_event_info (US_PROBE_ID, RECORD_ENTRY, "ppppppp", ((unsigned long)ip->jprobe.kp.addr | 0x01), arg1, arg2, arg3, arg4, arg5, arg6);
+       }else{
+               pack_event_info (US_PROBE_ID, RECORD_ENTRY, "ppppppp", ip->jprobe.kp.addr, arg1, arg2, arg3, arg4, arg5, arg6);
+       }
+#else
        pack_event_info (US_PROBE_ID, RECORD_ENTRY, "ppppppp", ip->jprobe.kp.addr, arg1, arg2, arg3, arg4, arg5, arg6);
+#endif
        uprobe_return ();
 }
 
 int uretprobe_event_handler (struct kretprobe_instance *probe, struct pt_regs *regs, us_proc_ip_t * ip)
 {
        int retval = regs_return_value(regs);
+
+#if defined(CONFIG_ARM)
+       if (ip->offset & 0x01)
+       {
+               pack_event_info (US_PROBE_ID, RECORD_RET, "pd", ((unsigned long)ip->retprobe.kp.addr | 0x01), retval);
+       }else{
+               pack_event_info (US_PROBE_ID, RECORD_RET, "pd", ip->retprobe.kp.addr, retval);
+       }
+#else
        pack_event_info (US_PROBE_ID, RECORD_RET, "pd", ip->retprobe.kp.addr, retval);
+#endif
        return 0;
 }
 
index 4a75b35..1015e52 100644 (file)
@@ -151,6 +151,47 @@ int prep_pc_dep_insn_execbuf (kprobe_opcode_t * insns, kprobe_opcode_t insn, int
 }
 
 
+
+int prep_pc_dep_insn_execbuf_thumb (kprobe_opcode_t * insns, kprobe_opcode_t insn, int uregs)
+{
+       unsigned char mreg = 0;
+       unsigned char reg = ((insn & 0xffff) & uregs) >> 8;
+
+       printk ("prep_pc_dep_insn_execbuf_thumb: using PC, changing Rd = R%x\n", reg);
+
+       if (reg == 6 || reg == 7)
+       {
+               printk(" >>>>> R%d in use!\n", reg);
+
+               mreg = reg - 2;
+               printk(" >>>>> remap to R%d\n", mreg);
+
+               *((unsigned short*)insns + 0) = (*((unsigned short*)insns + 0) & 0x00ff) | (mreg | (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 + 5) = (*((unsigned short*)insns + 5) & 0xf8ff) | (mreg << 8);
+               *((unsigned short*)insns + 6) = (*((unsigned short*)insns + 6) & 0xffc7) | ((mreg + 1) << 3);
+               *((unsigned short*)insns + 7) = (*((unsigned short*)insns + 7) & 0xffc7) | ((mreg + 1) << 3);
+               *((unsigned short*)insns + 8) = (*((unsigned short*)insns + 0) & 0x00ff) | (mreg | (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);                      // ADD Rd, SP, #immed_8*4
+       }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);             // LDR Rd, [SP, #immed_8*4]
+               }
+       }
+       return 0;
+}
+
+
+
 int arch_check_insn_arm (struct arch_specific_insn *ainsn)
 {
        int ret = 0;
@@ -210,8 +251,7 @@ int arch_check_insn_thumb (struct arch_specific_insn *ainsn)
                THUMB2_INSN_MATCH (BLX1, ainsn->insn[0]) ||
                THUMB_INSN_MATCH (BLX2, ainsn->insn[0]) ||
                THUMB_INSN_MATCH (BX, ainsn->insn[0]) ||
-               THUMB2_INSN_MATCH (BXJ, ainsn->insn[0]) ||
-               THUMB_INSN_MATCH (LRO3, ainsn->insn[0]))                        // LDR Rd, [PC, #<immed_8> * 4]
+               THUMB2_INSN_MATCH (BXJ, ainsn->insn[0]))
        {
                DBPRINTF ("Bad insn arch_check_insn_thumb: %lx\n", ainsn->insn[0]);
                ret = -EFAULT;
@@ -368,12 +408,6 @@ int arch_prepare_uprobe (struct kprobe *p, struct task_struct *task, int atomic)
 {
        int ret = 0;
 
-       my_p[my_probe] = p;
-       my_task[my_probe] = task;
-       my_atomic[my_probe] = atomic;
-
-       my_probe++;
-
        if ((unsigned long) p->addr & 0x01)
        {
                DBPRINTF ("Attempt to register kprobe at an unaligned address");
@@ -381,6 +415,12 @@ int arch_prepare_uprobe (struct kprobe *p, struct task_struct *task, int atomic)
        }
        if (!ret)
        {
+               my_p[my_probe] = p;
+               my_task[my_probe] = task;
+               my_atomic[my_probe] = atomic;
+
+               my_probe++;
+
                kprobe_opcode_t insn[MAX_INSN_SIZE];
 
                if (!read_proc_vm_atomic (task, (unsigned long) p->addr, &insn, MAX_INSN_SIZE * sizeof(kprobe_opcode_t))) panic ("failed to read memory %p!\n", p->addr);
@@ -454,13 +494,14 @@ int arch_copy_trampoline_arm_uprobe (struct kprobe *p, struct task_struct *task,
                kprobe_opcode_t insn[MAX_INSN_SIZE];
                struct arch_specific_insn ainsn;
 
-               *p->addr = p->opcode;
-               flush_icache_range ((unsigned long) p->addr, (unsigned long) p->addr + sizeof (kprobe_opcode_t));
-
+//             *p->addr = p->opcode;
+//             flush_icache_range ((unsigned long) p->addr, (unsigned long) p->addr + sizeof (kprobe_opcode_t));
 
                if (!read_proc_vm_atomic (task, (unsigned long) p->addr, &insn, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)))
                        panic ("failed to read memory %p!\n", p->addr);
 
+               insn[0] = p->opcode;
+
                ainsn.insn = insn;
 
                ret = arch_check_insn_arm (&ainsn);
@@ -561,6 +602,7 @@ int arch_copy_trampoline_arm_uprobe (struct kprobe *p, struct task_struct *task,
                        }
                }
        }
+
        return ret;
 }
 
@@ -569,8 +611,9 @@ int arch_copy_trampoline_arm_uprobe (struct kprobe *p, struct task_struct *task,
 int arch_copy_trampoline_thumb_uprobe (struct kprobe *p, struct task_struct *task, int atomic)
 {
        int ret = 0;
+       int uregs;
 
-       kprobe_opcode_t insns[UPROBES_TRAMP_LEN];
+       kprobe_opcode_t insns[UPROBES_TRAMP_LEN * 2];
 
        if ((unsigned long) p->addr & 0x01)
        {
@@ -582,12 +625,14 @@ int arch_copy_trampoline_thumb_uprobe (struct kprobe *p, struct task_struct *tas
                kprobe_opcode_t insn[MAX_INSN_SIZE];
                struct arch_specific_insn ainsn;
 
-               *p->addr = p->opcode;
-               flush_icache_range ((unsigned long) p->addr, (unsigned long) p->addr + sizeof (kprobe_opcode_t));
+//             *p->addr = p->opcode;
+//             flush_icache_range ((unsigned long) p->addr, (unsigned long) p->addr + sizeof (kprobe_opcode_t));
 
                if (!read_proc_vm_atomic (task, (unsigned long) p->addr, &insn, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)))
                        panic ("failed to read memory %p!\n", p->addr);
 
+               insn[0] = p->opcode;
+
                ainsn.insn = insn;
 
                ret = arch_check_insn_thumb (&ainsn);
@@ -596,11 +641,31 @@ int arch_copy_trampoline_thumb_uprobe (struct kprobe *p, struct task_struct *tas
                        if (!p->ainsn.insn)
                                return -ENOMEM;
 
-                       memcpy (insns, gen_insn_execbuf_thumb, 14*2);
-                       *((unsigned short*)insns + 2) = insn[0];
-                       *((unsigned short*)insns + 9) = 0xffff;
-                       *((unsigned short*)insns + 12) = ((unsigned short) (p->addr) + 2) & 0x0000ffff;
-                       *((unsigned short*)insns + 13) = ((unsigned short) (p->addr) + 2) >> 16;
+                       if (THUMB_INSN_MATCH (APC, insn[0]) || THUMB_INSN_MATCH (LRO3, insn[0]))
+                       {
+                               uregs = 0x0700;// Rd 8-10
+
+                               memcpy (insns, pc_dep_insn_execbuf_thumb, 16 * 2);
+                               if (prep_pc_dep_insn_execbuf_thumb (insns, insn[0], uregs) != 0)
+                               {
+                                       DBPRINTF ("failed to prepare exec buffer for insn %lx!", insn[0]);
+                                       free_insn_slot (&uprobe_insn_pages, task, p->ainsn.insn, 0);
+                                       return -EINVAL;
+                               }
+                               *((unsigned short*)insns + 11) = 0xffff;
+
+                               *((unsigned short*)insns + 12) = ((unsigned short) (p->addr) + 4) & 0x0000ffff;
+                               *((unsigned short*)insns + 13) = ((unsigned short) (p->addr) + 4) >> 16;
+
+                               *((unsigned short*)insns + 14) = ((unsigned short) (p->addr) + 2) & 0x0000ffff;
+                               *((unsigned short*)insns + 15) = ((unsigned short) (p->addr) + 2) >> 16;
+                       }else{
+                               memcpy (insns, gen_insn_execbuf_thumb, 16 * 2);
+                               *((unsigned short*)insns + 2) = insn[0];
+                               *((unsigned short*)insns + 11) = 0xffff;
+                               *((unsigned short*)insns + 14) = ((unsigned short) (p->addr) + 2) & 0x0000ffff;
+                               *((unsigned short*)insns + 15) = ((unsigned short) (p->addr) + 2) >> 16;
+                       }
 
                        if (!write_proc_vm_atomic (task, (unsigned long) p->ainsn.insn, insns, sizeof (insns)))
                        {
@@ -612,6 +677,7 @@ int arch_copy_trampoline_thumb_uprobe (struct kprobe *p, struct task_struct *tas
                        }
                }
        }
+
        return ret;
 }
 
@@ -623,13 +689,13 @@ int kprobe_handler (struct pt_regs *regs)
        kprobe_opcode_t *addr = NULL, *ssaddr = 0;
        struct kprobe_ctlblk *kcb;
        int my_pr = 0;
+       int i = 0;
 
        if (user_mode(regs))
        {
                if (!thumb_mode ( regs )) addr = (kprobe_opcode_t *) (regs->uregs[15] - 4);
                else addr = (kprobe_opcode_t *) (regs->uregs[15] - 2);
 
-               int i;
                for(i = 0; i < my_probe; i++)
                {
                        if (my_p[i]->addr == addr)
@@ -641,11 +707,8 @@ int kprobe_handler (struct pt_regs *regs)
                                my_handler_lock = 1;
                        }
                }
-       }
 
-       if (0 == my_handler_lock)
-       {
-               if (user_mode(regs))
+               if (0 == my_handler_lock)
                {
                        my_handler_lock = 1;
 
@@ -728,7 +791,7 @@ int kprobe_handler (struct pt_regs *regs)
                                if (!thumb_mode ( regs ))
                                        p = get_kprobe_by_insn_slot (addr-UPROBES_TRAMP_RET_BREAK_IDX, pid, current);
                                else
-                                       p = get_kprobe_by_insn_slot ((unsigned long)addr - 0x12, pid, current);
+                                       p = get_kprobe_by_insn_slot ((unsigned long)addr - 0x16, pid, current);
 
                                if (p) {
                                        save_previous_kprobe (kcb, p);
@@ -785,7 +848,7 @@ int kprobe_handler (struct pt_regs *regs)
                        if (!thumb_mode ( regs ))
                                p = get_kprobe_by_insn_slot (addr-UPROBES_TRAMP_RET_BREAK_IDX, pid, current);
                        else
-                               p = get_kprobe_by_insn_slot ((unsigned long)addr - 0x12, pid, current);
+                               p = get_kprobe_by_insn_slot ((unsigned long)addr - 0x16, pid, current);
 
                        if (!p) {
                                /* Not one of ours: let kernel handle it */
@@ -802,12 +865,14 @@ int kprobe_handler (struct pt_regs *regs)
                }
        }
 
+
        // restore second opcode for thumb app
        if (user_mode( regs ) && thumb_mode( regs ))
        {
                *((unsigned short*)p->addr + 1) = p->opcode >> 16;
                flush_icache_range ((unsigned int) p->addr, (unsigned int) (((unsigned int) p->addr) + (sizeof (kprobe_opcode_t) * 2)));
        }
+
        set_current_kprobe (p, regs, kcb);
 
        if(!reenter)
@@ -1021,7 +1086,7 @@ int trampoline_probe_handler (struct kprobe *p, struct pt_regs *regs)
                if (!thumb_mode( regs ))
                        trampoline_address = (unsigned long)(p->ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
                else
-                       trampoline_address = (unsigned long)(p->ainsn.insn + 0x13);
+                       trampoline_address = (unsigned long)(p->ainsn.insn + 0x17);
        }
 
        INIT_HLIST_HEAD (&empty_rp);
@@ -1093,7 +1158,7 @@ int trampoline_probe_handler (struct kprobe *p, struct pt_regs *regs)
 
                if (thumb_mode( regs ) && !(regs->uregs[14] & 0x01))
                {
-                       regs->ARM_cpsr ^= 0x20;
+                       regs->ARM_cpsr &= 0xFFFFFFDF;
                }else{
                        if (user_mode( regs ) && (regs->uregs[14] & 0x01))
                        {
@@ -1169,7 +1234,7 @@ void  __arch_prepare_kretprobe (struct kretprobe *rp, struct pt_regs *regs)
                        if (!thumb_mode( regs ))
                                regs->uregs[14] = (unsigned long) (rp->kp.ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
                        else
-                               regs->uregs[14] = (unsigned long) (rp->kp.ainsn.insn) + 0x13;
+                               regs->uregs[14] = (unsigned long) (rp->kp.ainsn.insn) + 0x17;
 
                else    /* Replace the return addr with trampoline addr */
                        regs->uregs[14] = (unsigned long) &kretprobe_trampoline;
index 2d0b459..c34383a 100644 (file)
@@ -164,6 +164,9 @@ typedef unsigned long kprobe_opcode_t;
 # define MASK_THUMB_INSN_DP            0xFC00                  // 111111xxxxxxxxxx
 # define PTRN_THUMB_INSN_DP            0x4000                  // 010000xxxxxxxxxx
 
+# define MASK_THUMB_INSN_APC           0xF800                  // 11111xxxxxxxxxxx
+# define PTRN_THUMB_INSN_APC           0xA000                  // 10100xxxxxxxxxxx     ADD Rd, [PC, #<immed_8> * 4]
+
 # define MASK_THUMB2_INSN_DPI          0xFBE08000              // 11111x11111xxxxx1xxxxxxxxxxxxxxx
 //# define PTRN_THUMB2_INSN_DPI                0xF0000000              // 11110x0xxxxxxxxx0xxxxxxxxxxxxxxx                             /? A6-19 ARM DDI 0406B
 # define PTRN_THUMB2_INSN_DPI          0xF2000000              // 11110x1xxxxxxxxx0xxxxxxxxxxxxxxx                             /? A6-19 ARM DDI 0406B
index 8debc2c..159bde6 100644 (file)
@@ -4,8 +4,8 @@
  *     - When the probed function returns, this probe
  *             causes the handlers to fire
  */
-               .thumb
 
+               .thumb
 
 
                .global gen_insn_execbuf_thumb
@@ -14,13 +14,36 @@ gen_insn_execbuf_thumb:
                nop
                nop                     // original instruction
                nop
+               nop
+               nop
                push    {r7}
-               ldr     r7, [pc, #12]   // ssbreak
-               mov     lr, r7
+               ldr     r7, [pc, #12]
+               mov     ip, r7
                pop     {r7}
-               mov     pc, lr
+               mov     pc, ip          // 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
+pc_dep_insn_execbuf_thumb:
+               push    {r6, r7}
+               ldr     r6, i1
+               mov     r7, sp
+               mov     sp, r6
+               nop                     // PC -> SP
+               ldr     r6, i2
+               mov     ip, r6
+               mov     sp, r7
+               pop     {r6, r7}
+               nop
+               mov     pc, ip          // ssbreak
+               nop                     // retbreak
+               nop                     // stored PC hi
+i1:            nop                     // stored PC lo
+               nop                     // stored PC-4(next insn addr) hi
+i2:            nop                     // stored PC-4(next insn addr) lo
index afc8842..3859f9c 100644 (file)
@@ -1 +1,2 @@
 void gen_insn_execbuf_thumb(void);
+void pc_dep_insn_execbuf_thumb(void);
index 9931740..fc124b3 100644 (file)
@@ -55,6 +55,14 @@ int __register_uprobe (struct kprobe *p, struct task_struct *task, int atomic, u
 
        DBPRINTF ("p->addr = 0x%p p = 0x%p\n", p->addr, p);
 
+// thumb address = address-1;
+#if defined(CONFIG_ARM)
+       if ((unsigned long) p->addr & 0x01)
+       {
+               p->addr = (unsigned long)p->addr & 0xfffffffe;
+       }
+#endif
+
        p->mod_refcounted = 0;
        p->nmissed = 0;
 #ifdef KPROBES_PROFILE