diff --git a/arch/sw_64/include/asm/uprobes.h b/arch/sw_64/include/asm/uprobes.h index 97b67af25bce2fdb049179aed8f4e57f1c0772c1..2a5b268cb88f41a050a9b871670ad935651d0f6c 100644 --- a/arch/sw_64/include/asm/uprobes.h +++ b/arch/sw_64/include/asm/uprobes.h @@ -35,4 +35,6 @@ struct arch_uprobe_task { unsigned long saved_trap_nr; }; +extern void sw64_fix_uretprobe(struct pt_regs *regs); + #endif /* _ASM_SW64_UPROBES_H */ diff --git a/arch/sw_64/kernel/traps.c b/arch/sw_64/kernel/traps.c index 5fac85c29bf6b1ee91d4c44b99a7f0ee7ddc31ba..9362fcd922c5c8d3f99e2cdab416e94ae4157e9c 100644 --- a/arch/sw_64/kernel/traps.c +++ b/arch/sw_64/kernel/traps.c @@ -267,12 +267,14 @@ do_entIF(unsigned long inst_type, struct pt_regs *regs) case BREAK_KPROBE_SS: if (notify_die(DIE_SSTEPBP, "single_step", regs, 0, 0, SIGTRAP) == NOTIFY_STOP) return; +#ifdef CONFIG_UPROBES case UPROBE_BRK_UPROBE: if (notify_die(DIE_UPROBE, "uprobe", regs, 0, 0, SIGTRAP) == NOTIFY_STOP) - return; + return sw64_fix_uretprobe(regs); case UPROBE_BRK_UPROBE_XOL: if (notify_die(DIE_UPROBE_XOL, "uprobe_xol", regs, 0, 0, SIGTRAP) == NOTIFY_STOP) - return; + return sw64_fix_uretprobe(regs); +#endif } if (user_mode(regs)) diff --git a/arch/sw_64/kernel/uprobes.c b/arch/sw_64/kernel/uprobes.c index 786f2e38a59f831cb832840d5a27e2f50788a5c3..1160ca1e836ae3c900a15253faca87abfefd6470 100644 --- a/arch/sw_64/kernel/uprobes.c +++ b/arch/sw_64/kernel/uprobes.c @@ -151,3 +151,41 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) { return 0; } + +/* + * struct xol_area and get_trampoline_vaddr() are copied from + * kernel/events/uprobes.c to avoid modifying arch-independent + * code. + */ +struct xol_area { + wait_queue_head_t wq; + atomic_t slot_count; + unsigned long *bitmap; + struct vm_special_mapping xol_mapping; + struct page *pages[2]; + unsigned long vaddr; +}; + +static unsigned long get_trampoline_vaddr(void) +{ + struct xol_area *area; + unsigned long trampoline_vaddr = -1; + + area = READ_ONCE(current->mm->uprobes_state.xol_area); + if (area) + trampoline_vaddr = area->vaddr; + + return trampoline_vaddr; +} + +void sw64_fix_uretprobe(struct pt_regs *regs) +{ + unsigned long bp_vaddr; + + bp_vaddr = uprobe_get_swbp_addr(regs); + /* + * regs->pc has been changed to orig_ret_vaddr in handle_trampoline(). + */ + if (bp_vaddr == get_trampoline_vaddr()) + regs->r26 = regs->pc; +}