#include <uprobe/swap_uprobes.h>
#include <kprobe/swap_kprobes_deps.h>
#include <kprobe/swap_slots.h>
+#include "../probes/compat_arm64.h"
+#define PTR_TO_U32(x) ((u32)(unsigned long)(x))
+#define U32_TO_PTR(x) ((void *)(unsigned long)(x))
+
#define flush_insns(addr, size) \
flush_icache_range((unsigned long)(addr), \
(unsigned long)(addr) + (size))
flush_insns(vaddr, len);
}
}
+
+/**
+ * @brief Prepates uretprobe for ARM.
+ *
+ * @param ri Pointer to the uretprobe instance.
+ * @param regs Pointer to CPU register data.
+ * @return Error code.
+ */
+int prepare_uretprobe_arm(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ unsigned long bp_offset;
+
+ bp_offset = thumb_mode(regs) ? 0x1b : 4 * PROBES_TRAMP_RET_BREAK_IDX;
+
+ /* save original return address */
+ ri->ret_addr = (uprobe_opcode_t *)regs->ARM_lr;
+
+ /* replace return address with break point adddress */
+ regs->ARM_lr = (unsigned long)(ri->rp->up.insn) + bp_offset;
+
+ /* save stack pointer address */
+ ri->sp = (uprobe_opcode_t *)regs->ARM_sp;
+
+ /* Set flag of current mode */
+ ri->sp = (uprobe_opcode_t *)((long)ri->sp | !!thumb_mode(regs));
+
+ return 0;
+}
+
+/**
+ * @brief Restores return address.
+ *
+ * @param orig_ret_addr Original return address.
+ * @param regs Pointer to CPU register data.
+ * @return Void.
+ */
+void set_orig_ret_addr_arm(unsigned long orig_ret_addr, struct pt_regs *regs)
+{
+ regs->ARM_lr = orig_ret_addr;
+ regs->ARM_pc = orig_ret_addr & ~0x1;
+
+ if (regs->ARM_lr & 0x1)
+ regs->ARM_cpsr |= PSR_T_BIT;
+ else
+ regs->ARM_cpsr &= ~PSR_T_BIT;
+}
+
+void arch_opcode_analysis_uretprobe_arm(struct uretprobe *rp)
+{
+ /* Remove retprobe if first insn overwrites lr */
+ rp->thumb_noret = noret_thumb(rp->up.opcode);
+ rp->arm_noret = noret_arm(rp->up.opcode);
+}
+
+unsigned long arch_get_trampoline_addr_arm(struct uprobe *p,
+ struct pt_regs *regs)
+{
+ return thumb_mode(regs) ?
+ PTR_TO_U32(p->insn) + 0x1b :
+ PTR_TO_U32(p->insn +
+ PROBES_TRAMP_RET_BREAK_IDX);
+}
+
+unsigned long arch_tramp_by_ri_arm(struct uretprobe_instance *ri)
+{
+ /* Understand function mode */
+ return (PTR_TO_U32(ri->sp) & 1) ?
+ PTR_TO_U32(ri->rp->up.insn) + 0x1b :
+ PTR_TO_U32(ri->rp->up.insn +
+ PROBES_TRAMP_RET_BREAK_IDX);
+}
+
+int arch_disarm_urp_inst_arm(struct uretprobe_instance *ri,
+ struct task_struct *task)
+{
+ struct pt_regs *uregs = task_pt_regs(ri->task);
+ u32 ra = uregs->ARM_lr;
+ u32 vaddr, tramp, found = 0;
+ u32 sp = PTR_TO_U32(ri->sp) & ~1;
+ u32 ret_addr = PTR_TO_U32(ri->ret_addr);
+ u32 stack = sp - 4 * (RETPROBE_STACK_DEPTH + 1);
+ u32 buf[RETPROBE_STACK_DEPTH];
+ int i, ret;
+
+ vaddr = PTR_TO_U32(ri->rp->up.addr);
+ tramp = arch_tramp_by_ri_arm(ri);
+
+ /* check stack */
+ ret = read_proc_vm_atomic(task, stack, buf, sizeof(buf));
+ if (ret != sizeof(buf)) {
+ pr_info("---> %s (%d/%d): failed to read stack from %08x\n",
+ task->comm, task->tgid, task->pid, stack);
+ ret = -EFAULT;
+ goto check_lr;
+ }
+
+ /* search the stack from the bottom */
+ for (i = RETPROBE_STACK_DEPTH - 1; i >= 0; i--) {
+ if (buf[i] == tramp) {
+ found = stack + 4 * i;
+ break;
+ }
+ }
+
+ if (!found) {
+ ret = -ESRCH;
+ goto check_lr;
+ }
+
+ pr_info("---> %s (%d/%d): trampoline found at "
+ "%08x (%08x /%+d) - %x, set ret_addr=%08x\n",
+ task->comm, task->tgid, task->pid,
+ found, sp,
+ found - sp, vaddr, ret_addr);
+ ret = write_proc_vm_atomic(task, found, &ret_addr, 4);
+ if (ret != 4) {
+ pr_info("---> %s (%d/%d): failed to write value to %08x",
+ task->comm, task->tgid, task->pid, found);
+ ret = -EFAULT;
+ } else {
+ ret = 0;
+ }
+
+check_lr: /* check lr anyway */
+ if (ra == tramp) {
+ pr_info("---> %s (%d/%d): trampoline found at "
+ "lr = %08x - %x, set ret_addr=%08x\n",
+ task->comm, task->tgid, task->pid, ra, vaddr,
+ ret_addr);
+
+ /* set ret_addr */
+ uregs->ARM_lr = ret_addr;
+ ret = 0;
+ } else if (ret) {
+ pr_info("---> %s (%d/%d): trampoline NOT found at "
+ "sp=%08x, lr=%08x - %x, ret_addr=%08x\n",
+ task->comm, task->tgid, task->pid,
+ sp, ra, vaddr, ret_addr);
+ }
+
+ return ret;
+}
int arch_arm_uprobe_arm(struct uprobe *p);
void arch_disarm_uprobe_arm(struct uprobe *p, struct task_struct *task);
+int prepare_uretprobe_arm(struct uretprobe_instance *ri, struct pt_regs *regs);
+void set_orig_ret_addr_arm(unsigned long orig_ret_addr, struct pt_regs *regs);
+void arch_opcode_analysis_uretprobe_arm(struct uretprobe *rp);
+unsigned long arch_get_trampoline_addr_arm(struct uprobe *p,
+ struct pt_regs *regs);
+unsigned long arch_tramp_by_ri_arm(struct uretprobe_instance *ri);
+int arch_disarm_urp_inst_arm(struct uretprobe_instance *ri,
+ struct task_struct *task);
+
#endif /* _SWAP_ASM_ARM_UPROBE_H */
#include <kprobe/swap_td_raw.h>
#include <kprobe/swap_kprobes_deps.h> /* FIXME: remove it */
#include <swap-asm/swap_probes.h>
+#include <arch/arm/uprobe/swap_uprobe.h>
#include <swap-asm/dbg_interface.h>
#include "uprobes-arm64.h"
}
-int arch_prepare_uretprobe(struct uretprobe_instance *ri,
- struct pt_regs *regs)
+static int prepare_uretprobe_arm64(struct uretprobe_instance *ri,
+ struct pt_regs *regs)
{
unsigned long bp_addr = (unsigned long)(ri->rp->up.insn +
URP_RET_BREAK_IDX);
+ unsigned long ret_addr = regs->regs[30] | ARM64_MODE_VADDR_MASK;
ri->sp = (kprobe_opcode_t *)regs->sp;
+ ri->ret_addr = (kprobe_opcode_t *)ret_addr;
/* replace the return address (regs[30] - lr) */
- ri->ret_addr = (kprobe_opcode_t *)regs->regs[30];
regs->regs[30] = bp_addr;
return 0;
}
+int arch_prepare_uretprobe(struct uretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ if (get_arch_mode((unsigned long)ri->rp->up.addr) == AM_ARM64)
+ return prepare_uretprobe_arm64(ri, regs);
+ else
+ return prepare_uretprobe_arm(ri, regs);
+}
+
+static void arch_opcode_analysis_uretprobe_arm64(struct uretprobe *rp)
+{
+ WARN(1, "not implemented"); /* FIXME: to implement */
+}
+
+void arch_opcode_analysis_uretprobe(struct uretprobe *rp)
+{
+ if (get_arch_mode((unsigned long)rp->up.addr) == AM_ARM64)
+ arch_opcode_analysis_uretprobe_arm64(rp);
+ else
+ arch_opcode_analysis_uretprobe_arm(rp);
+}
+
+static unsigned long arch_get_trampoline_addr_arm64(struct uprobe *p,
+ struct pt_regs *regs)
+{
+ WARN(1, "not implemented"); /* FIXME: to implement */
+ return 0;
+}
+
+/**
+ * @brief Gets trampoline address.
+ *
+ * @param p Pointer to the kprobe.
+ * @param regs Pointer to CPU register data.
+ * @return Trampoline address.
+ */
+unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs)
+{
+ if (get_arch_mode((unsigned long)p->addr) == AM_ARM64)
+ return arch_get_trampoline_addr_arm64(p, regs);
+ else
+ return arch_get_trampoline_addr_arm(p, regs);
+}
+
+static unsigned long arch_tramp_by_ri_arm64(struct uretprobe_instance *ri)
+{
+ WARN(1, "not implemented"); /* FIXME: to implement */
+ return 0;
+}
+
+unsigned long arch_tramp_by_ri(struct uretprobe_instance *ri)
+{
+ if (get_arch_mode((unsigned long)ri->rp->up.addr) == AM_ARM64)
+ return arch_tramp_by_ri_arm64(ri);
+ else
+ return arch_tramp_by_ri_arm(ri);
+}
+
+static int arch_disarm_urp_inst_arm64(struct uretprobe_instance *ri,
+ struct task_struct *task)
+{
+ WARN(1, "not implemented"); /* FIXME: to implement */
+ return 0;
+}
+
+/**
+ * @brief Disarms uretprobe instance.
+ *
+ * @param ri Pointer to the uretprobe instance
+ * @param task Pointer to the task for which the uretprobe instance
+ * @return 0 on success,\n
+ * negative error code on error.
+ */
+int arch_disarm_urp_inst(struct uretprobe_instance *ri,
+ struct task_struct *task)
+{
+ if (get_arch_mode((unsigned long)ri->rp->up.addr) == AM_ARM64)
+ return arch_disarm_urp_inst_arm64(ri, task);
+ else
+ return arch_disarm_urp_inst_arm(ri, task);
+}
+
void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs)
{
- regs->pc = orig_ret_addr;
+ if (get_arch_mode(orig_ret_addr) == AM_ARM64) {
+ regs->pc = get_real_addr(orig_ret_addr);
+ } else {
+ set_orig_ret_addr_arm(orig_ret_addr, regs);
+ }
}
static int make_urp_arm64(struct uprobe *p)
pr_err("%s failed to write memory %p!\n", __func__, utramp);
}
- add_uprobe_table(p);
-
return 0;
}
ret = arch_prepare_uprobe_arm(p);
}
+ if (!ret) {
+ /* for uretprobe */
+ add_uprobe_table(p);
+ }
+
return ret;
}
}
}
+static int urp_handler_aarch32(struct pt_regs *regs, pid_t tgid)
+{
+ struct uprobe *p;
+ unsigned long vaddr = regs->pc;
+ unsigned long offset_bp = compat_thumb_mode(regs) ?
+ 0x1a :
+ 4 * PROBES_TRAMP_RET_BREAK_IDX;
+ unsigned long tramp_addr = vaddr - offset_bp;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ p = get_uprobe_by_insn_slot((void *)tramp_addr, tgid, regs);
+ if (unlikely(p == NULL)) {
+ local_irq_restore(flags);
+
+ pr_info("no_uprobe: Not one of ours: let kernel handle it %lx\n",
+ vaddr);
+ return 1;
+ }
+
+ get_up(p);
+ local_irq_restore(flags);
+ trampoline_uprobe_handler(p, regs);
+ put_up(p);
+
+ return 0;
+}
+
static int uprobe_handler_aarch32(struct pt_regs *regs, u32 instr)
{
+ int ret = 0;
struct uprobe *p;
unsigned long flags;
unsigned long vaddr = regs->pc | !!compat_thumb_mode(regs);
put_up(p);
} else {
local_irq_restore(flags);
+ ret = urp_handler_aarch32(regs, tgid);
+
+ /* check ARM/THUMB CPU mode matches installed probe mode */
+ if (ret == 1) {
+ vaddr ^= 1;
+
+ local_irq_save(flags);
+ p = get_uprobe((uprobe_opcode_t *)vaddr, tgid);
+ if (p) {
+ get_up(p);
+ local_irq_restore(flags);
+ pr_err("invalid mode: thumb=%d addr=%p insn=%08x\n",
+ !!compat_thumb_mode(regs), p->addr, p->opcode);
+ ret = 0;
+
+ disarm_uprobe(p, current);
+ put_up(p);
+ } else {
+ local_irq_restore(flags);
+ }
+ }
}
- return 0;
+ return ret;
}
#include <linux/types.h>
+#include <linux/ptrace.h>
#include <arch/arm/probes/probes.h>
void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task);
-static inline unsigned long arch_get_trampoline_addr(struct uprobe *p,
- struct pt_regs *regs)
-{
- WARN(1, "not implemented"); /* FIXME: to implement */
- return 0;
-}
-
+unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs);
void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs);
int arch_prepare_uretprobe(struct uretprobe_instance *ri,
struct pt_regs *regs);
-static inline int arch_opcode_analysis_uretprobe(struct uretprobe *rp)
-{
- WARN(1, "not implemented"); /* FIXME: to implement */
- return 0;
-}
-
-static inline int arch_disarm_urp_inst(struct uretprobe_instance *ri,
- struct task_struct *task)
-{
- WARN(1, "not implemented"); /* FIXME: to implement */
- return 0;
-}
+void arch_opcode_analysis_uretprobe(struct uretprobe *rp);
+int arch_disarm_urp_inst(struct uretprobe_instance *ri,
+ struct task_struct *task);
static inline void arch_ujprobe_return(void)
{
static int pre_handler_uretprobe(struct uprobe *p, struct pt_regs *regs)
{
struct uretprobe *rp = container_of(p, struct uretprobe, up);
-#ifdef CONFIG_ARM
- int noret = thumb_mode(regs) ? rp->thumb_noret : rp->arm_noret;
-#endif
struct uretprobe_instance *ri;
int ret = 0;
-#ifdef CONFIG_ARM
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+# if defined(CONFIG_ARM64)
+# define thumb_mode(regs) compat_thumb_mode(regs)
+# endif /* defined(CONFIG_ARM64) */
+ int noret = thumb_mode(regs) ? rp->thumb_noret : rp->arm_noret;
+
if (noret)
return 0;
-#endif
+#endif /* defined(CONFIG_ARM) || defined(CONFIG_ARM64) */
/* TODO: consider to only swap the
* RA after the last pre_handler fired */
struct hlist_head free_instances; /**< Free instances list */
struct hlist_head used_instances; /**< Used instances list */
-#ifdef CONFIG_ARM
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
unsigned arm_noret:1; /**< No-return flag for ARM */
unsigned thumb_noret:1; /**< No-return flag for Thumb */
#endif