Re: [PATCH x86 v2] uprobe: emulate push insns for uprobe on x86

From: Yonghong Song
Date: Thu Nov 09 2017 - 12:23:23 EST




On 11/9/17 3:26 AM, David Laight wrote:
From: Yonghong Song
Sent: 09 November 2017 00:55

Uprobe is a tracing mechanism for userspace programs.
Typical uprobe will incur overhead of two traps.
First trap is caused by replaced trap insn, and
the second trap is to execute the original displaced
insn in user space.

To reduce the overhead, kernel provides hooks
for architectures to emulate the original insn
and skip the second trap. In x86, emulation
is done for certain branch insns.

This patch extends the emulation to "push <reg>"
insns. These insns are typical in the beginning
of the function. For example, bcc
...
diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h
index 74f4c2f..f9d2b43 100644
--- a/arch/x86/include/asm/uprobes.h
+++ b/arch/x86/include/asm/uprobes.h
@@ -33,6 +33,11 @@ typedef u8 uprobe_opcode_t;
...
@@ -53,6 +59,10 @@ struct arch_uprobe {
u8 fixups;
u8 ilen;
} defparam;
+ struct {
+ u8 rex_prefix;

Just call this 'reg_high' and set to 0 or 1.

Will do.


+ u8 opc1;
+ } push;
};
};

diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
index a3755d2..5ace65c 100644
--- a/arch/x86/kernel/uprobes.c
+++ b/arch/x86/kernel/uprobes.c
@@ -640,11 +640,71 @@ static bool check_jmp_cond(struct arch_uprobe *auprobe, struct pt_regs *regs)
#undef COND
#undef CASE_COND

-static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
+static unsigned long *get_push_reg_ptr(struct arch_uprobe *auprobe,
+ struct pt_regs *regs)
{
- unsigned long new_ip = regs->ip += auprobe->branch.ilen;
- unsigned long offs = (long)auprobe->branch.offs;
+#if defined(CONFIG_X86_64)
+ switch (auprobe->push.opc1) {
+ case 0x50:
+ return auprobe->push.rex_prefix ? &regs->r8 : &regs->ax;
+ case 0x51:
+ return auprobe->push.rex_prefix ? &regs->r9 : &regs->cx;
+ case 0x52:
+ return auprobe->push.rex_prefix ? &regs->r10 : &regs->dx;
+ case 0x53:
+ return auprobe->push.rex_prefix ? &regs->r11 : &regs->bx;
+ case 0x54:
+ return auprobe->push.rex_prefix ? &regs->r12 : &regs->sp;
+ case 0x55:
+ return auprobe->push.rex_prefix ? &regs->r13 : &regs->bp;
+ case 0x56:
+ return auprobe->push.rex_prefix ? &regs->r14 : &regs->si;
+ }
+
+ /* opc1 0x57 */
+ return auprobe->push.rex_prefix ? &regs->r15 : &regs->di;

The bottom of that switch statement is horrid....
Actually why can't you sort out this address in the code that
sets up 'reg_prefix' (etc);

Good suggestion. Will do.


David