|
@@ -59,62 +59,65 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
|
|
int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
|
|
int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
|
|
unsigned long addr)
|
|
unsigned long addr)
|
|
{
|
|
{
|
|
- struct ftrace_insn insn;
|
|
|
|
- unsigned short op;
|
|
|
|
- void *from, *to;
|
|
|
|
- size_t size;
|
|
|
|
-
|
|
|
|
- ftrace_generate_nop_insn(&insn);
|
|
|
|
- size = sizeof(insn);
|
|
|
|
- from = &insn;
|
|
|
|
- to = (void *) rec->ip;
|
|
|
|
- if (probe_kernel_read(&op, (void *) rec->ip, sizeof(op)))
|
|
|
|
|
|
+ struct ftrace_insn orig, new, old;
|
|
|
|
+
|
|
|
|
+ if (probe_kernel_read(&old, (void *) rec->ip, sizeof(old)))
|
|
return -EFAULT;
|
|
return -EFAULT;
|
|
- /*
|
|
|
|
- * If we find a breakpoint instruction, a kprobe has been placed
|
|
|
|
- * at the beginning of the function. We write the constant
|
|
|
|
- * KPROBE_ON_FTRACE_NOP into the remaining four bytes of the original
|
|
|
|
- * instruction so that the kprobes handler can execute a nop, if it
|
|
|
|
- * reaches this breakpoint.
|
|
|
|
- */
|
|
|
|
- if (op == BREAKPOINT_INSTRUCTION) {
|
|
|
|
- size -= 2;
|
|
|
|
- from += 2;
|
|
|
|
- to += 2;
|
|
|
|
- insn.disp = KPROBE_ON_FTRACE_NOP;
|
|
|
|
|
|
+ if (addr == MCOUNT_ADDR) {
|
|
|
|
+ /* Initial code replacement; we expect to see stg r14,8(r15) */
|
|
|
|
+ orig.opc = 0xe3e0;
|
|
|
|
+ orig.disp = 0xf0080024;
|
|
|
|
+ ftrace_generate_nop_insn(&new);
|
|
|
|
+ } else if (old.opc == BREAKPOINT_INSTRUCTION) {
|
|
|
|
+ /*
|
|
|
|
+ * If we find a breakpoint instruction, a kprobe has been
|
|
|
|
+ * placed at the beginning of the function. We write the
|
|
|
|
+ * constant KPROBE_ON_FTRACE_NOP into the remaining four
|
|
|
|
+ * bytes of the original instruction so that the kprobes
|
|
|
|
+ * handler can execute a nop, if it reaches this breakpoint.
|
|
|
|
+ */
|
|
|
|
+ new.opc = orig.opc = BREAKPOINT_INSTRUCTION;
|
|
|
|
+ orig.disp = KPROBE_ON_FTRACE_CALL;
|
|
|
|
+ new.disp = KPROBE_ON_FTRACE_NOP;
|
|
|
|
+ } else {
|
|
|
|
+ /* Replace ftrace call with a nop. */
|
|
|
|
+ ftrace_generate_call_insn(&orig, rec->ip);
|
|
|
|
+ ftrace_generate_nop_insn(&new);
|
|
}
|
|
}
|
|
- if (probe_kernel_write(to, from, size))
|
|
|
|
|
|
+ /* Verify that the to be replaced code matches what we expect. */
|
|
|
|
+ if (memcmp(&orig, &old, sizeof(old)))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ if (probe_kernel_write((void *) rec->ip, &new, sizeof(new)))
|
|
return -EPERM;
|
|
return -EPERM;
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
|
int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
|
{
|
|
{
|
|
- struct ftrace_insn insn;
|
|
|
|
- unsigned short op;
|
|
|
|
- void *from, *to;
|
|
|
|
- size_t size;
|
|
|
|
-
|
|
|
|
- ftrace_generate_call_insn(&insn, rec->ip);
|
|
|
|
- size = sizeof(insn);
|
|
|
|
- from = &insn;
|
|
|
|
- to = (void *) rec->ip;
|
|
|
|
- if (probe_kernel_read(&op, (void *) rec->ip, sizeof(op)))
|
|
|
|
|
|
+ struct ftrace_insn orig, new, old;
|
|
|
|
+
|
|
|
|
+ if (probe_kernel_read(&old, (void *) rec->ip, sizeof(old)))
|
|
return -EFAULT;
|
|
return -EFAULT;
|
|
- /*
|
|
|
|
- * If we find a breakpoint instruction, a kprobe has been placed
|
|
|
|
- * at the beginning of the function. We write the constant
|
|
|
|
- * KPROBE_ON_FTRACE_CALL into the remaining four bytes of the original
|
|
|
|
- * instruction so that the kprobes handler can execute a brasl if it
|
|
|
|
- * reaches this breakpoint.
|
|
|
|
- */
|
|
|
|
- if (op == BREAKPOINT_INSTRUCTION) {
|
|
|
|
- size -= 2;
|
|
|
|
- from += 2;
|
|
|
|
- to += 2;
|
|
|
|
- insn.disp = KPROBE_ON_FTRACE_CALL;
|
|
|
|
|
|
+ if (old.opc == BREAKPOINT_INSTRUCTION) {
|
|
|
|
+ /*
|
|
|
|
+ * If we find a breakpoint instruction, a kprobe has been
|
|
|
|
+ * placed at the beginning of the function. We write the
|
|
|
|
+ * constant KPROBE_ON_FTRACE_CALL into the remaining four
|
|
|
|
+ * bytes of the original instruction so that the kprobes
|
|
|
|
+ * handler can execute a brasl if it reaches this breakpoint.
|
|
|
|
+ */
|
|
|
|
+ new.opc = orig.opc = BREAKPOINT_INSTRUCTION;
|
|
|
|
+ orig.disp = KPROBE_ON_FTRACE_NOP;
|
|
|
|
+ new.disp = KPROBE_ON_FTRACE_CALL;
|
|
|
|
+ } else {
|
|
|
|
+ /* Replace nop with an ftrace call. */
|
|
|
|
+ ftrace_generate_nop_insn(&orig);
|
|
|
|
+ ftrace_generate_call_insn(&new, rec->ip);
|
|
}
|
|
}
|
|
- if (probe_kernel_write(to, from, size))
|
|
|
|
|
|
+ /* Verify that the to be replaced code matches what we expect. */
|
|
|
|
+ if (memcmp(&orig, &old, sizeof(old)))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ if (probe_kernel_write((void *) rec->ip, &new, sizeof(new)))
|
|
return -EPERM;
|
|
return -EPERM;
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|