*/
#include <linux/kdebug.h>
-#include <asm/dbi_kprobes.h>
-#include <swap_uprobes.h>
-#include <asm/swap_uprobes.h>
-#include <dbi_insn_slots.h>
+#include <kprobe/arch/asm/dbi_kprobes.h>
+#include <uprobe/swap_uprobes.h>
+#include <uprobe/arch/asm/swap_uprobes.h>
+#include <kprobe/dbi_insn_slots.h>
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 };
-int arch_prepare_uprobe(struct uprobe *up, struct hlist_head *page_list)
+static struct kprobe *get_current_probe(void)
+{
+ return __get_cpu_var(ucb).p;
+}
+
+static void set_current_probe(struct kprobe *p)
+{
+ __get_cpu_var(ucb).p = p;
+}
+
+static void reset_current_probe(void)
+{
+ set_current_probe(NULL);
+}
+
+static void save_current_flags(struct pt_regs *regs)
+{
+ __get_cpu_var(ucb).flags = regs->EREG(flags);
+}
+
+static void restore_current_flags(struct pt_regs *regs)
+{
+ regs->EREG(flags) &= ~IF_MASK;
+ regs->EREG(flags) |= __get_cpu_var(ucb).flags & IF_MASK;
+}
+
+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;
}
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;
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]);
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));
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)
switch (insns[0]) {
case 0x9c: /* pushfl */
*tos &= ~(TF_MASK | IF_MASK);
- *tos |= flags;
+ *tos |= flags & (TF_MASK | IF_MASK);
break;
case 0xc2: /* iret/ret/lret */
case 0xc3:
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;
struct task_struct *task = current;
pid_t tgid = task->tgid;
+ save_current_flags(regs);
+
addr = (kprobe_opcode_t *)(regs->EREG(ip) - sizeof(kprobe_opcode_t));
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;
}
trampoline_uprobe_handler(p, regs);
+ return 1;
} else {
- if (!p->pre_handler || !p->pre_handler(p, regs))
+ 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;
+ return 1;
+ }
+
prepare_singlestep(p, regs);
+ }
}
- __get_cpu_var(ucb).p = p;
- __get_cpu_var(ucb).flags = (regs->EREG(flags) & (TF_MASK | IF_MASK));
+ set_current_probe(p);
return 1;
}
static int post_uprobe_handler(struct pt_regs *regs)
{
- struct kprobe *p = __get_cpu_var(ucb).p;
+ struct kprobe *p = get_current_probe();
unsigned long flags = __get_cpu_var(ucb).flags;
+ if (p == NULL)
+ return 0;
+
resume_execution(p, regs, flags);
+ restore_current_flags(regs);
+
+ reset_current_probe();
return 1;
}
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) {