X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=uprobe%2Farch%2Fasm-x86%2Fswap_uprobes.c;h=2ec59ac64a4ed2218dc97d8283c6be7689f97d2f;hb=e970dee4cbab29d3973d369a75e25e8b89eb3080;hp=b03ef062b8c517131a1d730896711bda2ce6a318;hpb=18c041e613967cb7c93350f8a48c17922247c8ff;p=kernel%2Fswap-modules.git diff --git a/uprobe/arch/asm-x86/swap_uprobes.c b/uprobe/arch/asm-x86/swap_uprobes.c index b03ef06..2ec59ac 100644 --- a/uprobe/arch/asm-x86/swap_uprobes.c +++ b/uprobe/arch/asm-x86/swap_uprobes.c @@ -25,16 +25,22 @@ */ #include -#include -#include -#include -#include +#include +#include +#include +#include struct uprobe_ctlblk { unsigned long flags; struct kprobe *p; }; +static unsigned long trampoline_addr(struct uprobe *up) +{ + return (unsigned long)(up->kp.ainsn.insn + + UPROBES_TRAMP_RET_BREAK_IDX); +} + static DEFINE_PER_CPU(struct uprobe_ctlblk, ucb) = { 0, NULL }; static struct kprobe *get_current_probe(void) @@ -63,44 +69,30 @@ static void restore_current_flags(struct pt_regs *regs) regs->EREG(flags) |= __get_cpu_var(ucb).flags & IF_MASK; } -int arch_prepare_uprobe(struct uprobe *up, struct hlist_head *page_list) +int arch_prepare_uprobe(struct uprobe *up) { int ret = 0; - struct kprobe *p = &up->kp; + struct kprobe *p = up2kp(up); struct task_struct *task = up->task; - kprobe_opcode_t insns[UPROBES_TRAMP_LEN]; - - if (!ret) { - kprobe_opcode_t insn[MAX_INSN_SIZE]; - struct arch_specific_insn ainsn; - - 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); - - ainsn.insn = insn; - ret = arch_check_insn(&ainsn); - if (!ret) { - p->opcode = insn[0]; - p->ainsn.insn = alloc_insn_slot(up->sm); - if (!p->ainsn.insn) - return -ENOMEM; - - if (can_boost(insn)) - p->ainsn.boostable = 0; - else - p->ainsn.boostable = -1; - - memcpy(&insns[UPROBES_TRAMP_INSN_IDX], insn, MAX_INSN_SIZE*sizeof(kprobe_opcode_t)); - insns[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; - - if (!write_proc_vm_atomic(task, (unsigned long)p->ainsn.insn, insns, sizeof(insns))) { - free_insn_slot(up->sm, p->ainsn.insn); - panic("failed to write memory %p!\n", p->ainsn.insn); - return -EINVAL; - } - } + u8 *tramp = up->atramp.tramp; + enum { call_relative_opcode = 0xe8 }; + + if (!read_proc_vm_atomic(task, (unsigned long)p->addr, + tramp, MAX_INSN_SIZE)) + panic("failed to read memory %p!\n", p->addr); + /* TODO: this is a workaround */ + if (tramp[0] == call_relative_opcode) { + printk("cannot install probe: 1st instruction is call\n"); + return -1; } + tramp[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION; + + /* TODO: remove dual info */ + p->opcode = tramp[0]; + + p->ainsn.boostable = can_boost(tramp) ? 0 : -1; + return ret; } @@ -110,7 +102,7 @@ int setjmp_upre_handler(struct kprobe *p, struct pt_regs *regs) 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; - unsigned long addr, args[6]; + unsigned long args[6]; /* FIXME some user space apps crash if we clean interrupt bit */ //regs->EREG(flags) &= ~IF_MASK; @@ -123,7 +115,7 @@ int setjmp_upre_handler(struct kprobe *p, struct pt_regs *regs) panic("failed to read user space func arguments %lx!\n", regs->EREG(sp) + 4); if (pre_entry) - p->ss_addr = pre_entry(jp->priv_arg, regs); + p->ss_addr = (kprobe_opcode_t *)pre_entry(jp->priv_arg, regs); if (entry) entry(args[0], args[1], args[2], args[3], args[4], args[5]); @@ -136,7 +128,8 @@ int setjmp_upre_handler(struct kprobe *p, struct pt_regs *regs) void arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs) { /* Replace the return addr with trampoline addr */ - unsigned long ra = (unsigned long)(ri->rp->up.kp.ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX); + unsigned long ra = trampoline_addr(&ri->rp->up); + ri->sp = (kprobe_opcode_t *)regs->sp; if (!read_proc_vm_atomic(current, regs->EREG(sp), &(ri->ret_addr), sizeof(ri->ret_addr))) panic("failed to read user space func ra %lx!\n", regs->EREG(sp)); @@ -145,9 +138,41 @@ void arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs) panic("failed to write user space func ra %lx!\n", regs->EREG(sp)); } +int arch_disarm_urp_inst(struct uretprobe_instance *ri, + struct task_struct *task) +{ + int len; + unsigned long ret_addr; + unsigned long sp = (unsigned long)ri->sp; + unsigned long tramp_addr = trampoline_addr(&ri->rp->up); + len = read_proc_vm_atomic(task, sp, &ret_addr, sizeof(ret_addr)); + if (len != sizeof(ret_addr)) { + printk("---> %s (%d/%d): failed to read stack from %08lx\n", + task->comm, task->tgid, task->pid, sp); + return -EFAULT; + } + + if (tramp_addr == ret_addr) { + len = write_proc_vm_atomic(task, sp, &ri->ret_addr, + sizeof(ri->ret_addr)); + if (len != sizeof(ri->ret_addr)) { + printk("---> %s (%d/%d): failed to write " + "orig_ret_addr to %08lx", + task->comm, task->tgid, task->pid, sp); + return -EFAULT; + } + } else { + printk("---> %s (%d/%d): trampoline NOT found at sp = %08lx\n", + task->comm, task->tgid, task->pid, sp); + return -ENOENT; + } + + return 0; +} + unsigned long arch_get_trampoline_addr(struct kprobe *p, struct pt_regs *regs) { - return (unsigned long)(p->ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX); + return trampoline_addr(kp2up(p)); } void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs) @@ -256,6 +281,31 @@ no_change: return; } +static int make_trampoline(struct uprobe *up) +{ + struct kprobe *p = up2kp(up); + struct task_struct *task = up->task; + void *tramp; + + tramp = alloc_insn_slot(up->sm); + if (tramp == 0) { + printk("trampoline out of memory\n"); + return -ENOMEM; + } + + if (!write_proc_vm_atomic(task, (unsigned long)tramp, + up->atramp.tramp, + sizeof(up->atramp.tramp))) { + free_insn_slot(up->sm, tramp); + panic("failed to write memory %p!\n", tramp); + return -EINVAL; + } + + p->ainsn.insn = tramp; + + return 0; +} + static int uprobe_handler(struct pt_regs *regs) { struct kprobe *p; @@ -269,8 +319,9 @@ static int uprobe_handler(struct pt_regs *regs) p = get_ukprobe(addr, tgid); if (p == NULL) { - p = get_ukprobe_by_insn_slot(addr, tgid, regs); + void *tramp_addr = (void *)addr - UPROBES_TRAMP_RET_BREAK_IDX; + p = get_ukprobe_by_insn_slot(tramp_addr, tgid, regs); if (p == NULL) { printk("no_uprobe\n"); return 0; @@ -279,6 +330,15 @@ static int uprobe_handler(struct pt_regs *regs) trampoline_uprobe_handler(p, regs); return 1; } else { + if (p->ainsn.insn == NULL) { + struct uprobe *up = kp2up(p); + + make_trampoline(up); + + /* for uretprobe */ + add_uprobe_table(p); + } + if (!p->pre_handler || !p->pre_handler(p, regs)) { if (p->ainsn.boostable == 1 && !p->post_handler) { regs->EREG(ip) = (unsigned long)p->ainsn.insn; @@ -315,7 +375,7 @@ static int uprobe_exceptions_notify(struct notifier_block *self, unsigned long v struct die_args *args = (struct die_args *)data; int ret = NOTIFY_DONE; - if (args->regs && !user_mode_vm(args->regs)) + if (args->regs == NULL || !user_mode_vm(args->regs)) return ret; switch (val) {