uprobes/x86: Introduce uprobe_xol_ops and arch_uprobe->ops
Introduce arch_uprobe->ops pointing to the "struct uprobe_xol_ops", move the current UPROBE_FIX_{RIP*,IP,CALL} code into the default set of methods and change arch_uprobe_pre/post_xol() accordingly. This way we can add the new uprobe_xol_ops's to handle the insns which need the special processing (rip-relative jmp/call at least). Signed-off-by: Oleg Nesterov <oleg@redhat.com> Reviewed-by: Jim Keniston <jkenisto@us.ibm.com> Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
This commit is contained in:
parent
34e7317d6a
commit
8ad8e9d3fd
|
@ -33,12 +33,17 @@ typedef u8 uprobe_opcode_t;
|
||||||
#define UPROBE_SWBP_INSN 0xcc
|
#define UPROBE_SWBP_INSN 0xcc
|
||||||
#define UPROBE_SWBP_INSN_SIZE 1
|
#define UPROBE_SWBP_INSN_SIZE 1
|
||||||
|
|
||||||
|
struct uprobe_xol_ops;
|
||||||
|
|
||||||
struct arch_uprobe {
|
struct arch_uprobe {
|
||||||
u16 fixups;
|
|
||||||
union {
|
union {
|
||||||
u8 insn[MAX_UINSN_BYTES];
|
u8 insn[MAX_UINSN_BYTES];
|
||||||
u8 ixol[MAX_UINSN_BYTES];
|
u8 ixol[MAX_UINSN_BYTES];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
u16 fixups;
|
||||||
|
const struct uprobe_xol_ops *ops;
|
||||||
|
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
unsigned long rip_rela_target_address;
|
unsigned long rip_rela_target_address;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -402,6 +402,64 @@ static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm,
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_X86_64 */
|
#endif /* CONFIG_X86_64 */
|
||||||
|
|
||||||
|
struct uprobe_xol_ops {
|
||||||
|
bool (*emulate)(struct arch_uprobe *, struct pt_regs *);
|
||||||
|
int (*pre_xol)(struct arch_uprobe *, struct pt_regs *);
|
||||||
|
int (*post_xol)(struct arch_uprobe *, struct pt_regs *);
|
||||||
|
};
|
||||||
|
|
||||||
|
static int default_pre_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
pre_xol_rip_insn(auprobe, regs, ¤t->utask->autask);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adjust the return address pushed by a call insn executed out of line.
|
||||||
|
*/
|
||||||
|
static int adjust_ret_addr(unsigned long sp, long correction)
|
||||||
|
{
|
||||||
|
int rasize, ncopied;
|
||||||
|
long ra = 0;
|
||||||
|
|
||||||
|
if (is_ia32_task())
|
||||||
|
rasize = 4;
|
||||||
|
else
|
||||||
|
rasize = 8;
|
||||||
|
|
||||||
|
ncopied = copy_from_user(&ra, (void __user *)sp, rasize);
|
||||||
|
if (unlikely(ncopied))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
ra += correction;
|
||||||
|
ncopied = copy_to_user((void __user *)sp, &ra, rasize);
|
||||||
|
if (unlikely(ncopied))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int default_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct uprobe_task *utask = current->utask;
|
||||||
|
long correction = (long)(utask->vaddr - utask->xol_vaddr);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
handle_riprel_post_xol(auprobe, regs, &correction);
|
||||||
|
if (auprobe->fixups & UPROBE_FIX_IP)
|
||||||
|
regs->ip += correction;
|
||||||
|
|
||||||
|
if (auprobe->fixups & UPROBE_FIX_CALL)
|
||||||
|
ret = adjust_ret_addr(regs->sp, correction);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct uprobe_xol_ops default_xol_ops = {
|
||||||
|
.pre_xol = default_pre_xol_op,
|
||||||
|
.post_xol = default_post_xol_op,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* arch_uprobe_analyze_insn - instruction analysis including validity and fixups.
|
* arch_uprobe_analyze_insn - instruction analysis including validity and fixups.
|
||||||
* @mm: the probed address space.
|
* @mm: the probed address space.
|
||||||
|
@ -464,6 +522,7 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
|
||||||
if (fix_call)
|
if (fix_call)
|
||||||
auprobe->fixups |= UPROBE_FIX_CALL;
|
auprobe->fixups |= UPROBE_FIX_CALL;
|
||||||
|
|
||||||
|
auprobe->ops = &default_xol_ops;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,33 +544,8 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||||
if (test_tsk_thread_flag(current, TIF_BLOCKSTEP))
|
if (test_tsk_thread_flag(current, TIF_BLOCKSTEP))
|
||||||
set_task_blockstep(current, false);
|
set_task_blockstep(current, false);
|
||||||
|
|
||||||
pre_xol_rip_insn(auprobe, regs, &utask->autask);
|
if (auprobe->ops->pre_xol)
|
||||||
return 0;
|
return auprobe->ops->pre_xol(auprobe, regs);
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This function is called by arch_uprobe_post_xol() to adjust the return
|
|
||||||
* address pushed by a call instruction executed out of line.
|
|
||||||
*/
|
|
||||||
static int adjust_ret_addr(unsigned long sp, long correction)
|
|
||||||
{
|
|
||||||
int rasize, ncopied;
|
|
||||||
long ra = 0;
|
|
||||||
|
|
||||||
if (is_ia32_task())
|
|
||||||
rasize = 4;
|
|
||||||
else
|
|
||||||
rasize = 8;
|
|
||||||
|
|
||||||
ncopied = copy_from_user(&ra, (void __user *)sp, rasize);
|
|
||||||
if (unlikely(ncopied))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
ra += correction;
|
|
||||||
ncopied = copy_to_user((void __user *)sp, &ra, rasize);
|
|
||||||
if (unlikely(ncopied))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -560,11 +594,8 @@ bool arch_uprobe_xol_was_trapped(struct task_struct *t)
|
||||||
int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct uprobe_task *utask = current->utask;
|
struct uprobe_task *utask = current->utask;
|
||||||
long correction;
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR);
|
WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR);
|
||||||
|
|
||||||
current->thread.trap_nr = utask->autask.saved_trap_nr;
|
current->thread.trap_nr = utask->autask.saved_trap_nr;
|
||||||
/*
|
/*
|
||||||
* arch_uprobe_pre_xol() doesn't save the state of TIF_BLOCKSTEP
|
* arch_uprobe_pre_xol() doesn't save the state of TIF_BLOCKSTEP
|
||||||
|
@ -576,15 +607,9 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||||
else if (!(auprobe->fixups & UPROBE_FIX_SETF))
|
else if (!(auprobe->fixups & UPROBE_FIX_SETF))
|
||||||
regs->flags &= ~X86_EFLAGS_TF;
|
regs->flags &= ~X86_EFLAGS_TF;
|
||||||
|
|
||||||
correction = (long)(utask->vaddr - utask->xol_vaddr);
|
if (auprobe->ops->post_xol)
|
||||||
handle_riprel_post_xol(auprobe, regs, &correction);
|
return auprobe->ops->post_xol(auprobe, regs);
|
||||||
if (auprobe->fixups & UPROBE_FIX_IP)
|
return 0;
|
||||||
regs->ip += correction;
|
|
||||||
|
|
||||||
if (auprobe->fixups & UPROBE_FIX_CALL)
|
|
||||||
result = adjust_ret_addr(regs->sp, correction);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* callback routine for handling exceptions. */
|
/* callback routine for handling exceptions. */
|
||||||
|
@ -642,6 +667,10 @@ static bool __skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (auprobe->ops->emulate)
|
||||||
|
return auprobe->ops->emulate(auprobe, regs);
|
||||||
|
|
||||||
|
/* TODO: move this code into ->emulate() hook */
|
||||||
for (i = 0; i < MAX_UINSN_BYTES; i++) {
|
for (i = 0; i < MAX_UINSN_BYTES; i++) {
|
||||||
if (auprobe->insn[i] == 0x66)
|
if (auprobe->insn[i] == 0x66)
|
||||||
continue;
|
continue;
|
||||||
|
|
Loading…
Reference in New Issue