/*
- * Copyright (c) 2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2018 Apple Inc. All rights reserved.
*/
/*
* CDDL HEADER START
* Use is subject to license terms.
*/
-/* #pragma ident "@(#)fbt.c 1.15 05/09/19 SMI" */
-
-#ifdef KERNEL
-#ifndef _KERNEL
-#define _KERNEL /* Solaris vs. Darwin */
-#endif
-#endif
-
-#define MACH__POSIX_C_SOURCE_PRIVATE 1 /* pulls in suitable savearea from
- * mach/ppc/thread_status.h */
#include <kern/thread.h>
#include <mach/thread_status.h>
#include <arm/proc_reg.h>
CPU->cpu_dtrace_invop_underway = 1; /* Race not possible on
* this per-cpu state */
+ /*
+ * Stack looks like this:
+ *
+ * [Higher addresses]
+ *
+ * Frame of caller
+ * Extra args for callee
+ * ------------------------
+ * fbt entry probe:
+ * Frame from traced function: <previous sp (e.g. 0x1000), return address>
+ * fbt return probe:
+ * Missing as the return probe has already popped the frame in the callee and
+ * traps with LR set to the return address in caller.
+ * ------------------------
+ * arm_context_t
+ * ------------------------
+ * Frame from trap handler: <previous sp (e.g. 0x1000) , traced PC >
+ * The traced function has either never pushed the frame
+ * or already popped it. So there is no frame in the
+ * backtrace pointing to the frame on the stack containing
+ * the LR in the caller.
+ * ------------------------
+ * |
+ * |
+ * | stack grows this way
+ * |
+ * |
+ * v
+ * [Lower addresses]
+ *
+ * cpu_dtrace_caller compensates for fact that the LR is not stored on stack as explained
+ * above. When walking the stack, when we reach the frame where we extract a PC in the
+ * patched function, we put the cpu_dtrace_caller in the backtrace instead. The next
+ * frame we extract will be in the caller's caller, so we output a backtrace starting
+ * at the caller and going sequentially up the stack.
+ */
+ arm_saved_state_t *regs = (arm_saved_state_t *)(&((arm_context_t *)stack)->ss);
+
+ CPU->cpu_dtrace_caller = get_saved_state_lr(regs);
+
+ /* When fbt_roffset is non-zero, we know we are handling a return probe point. */
if (fbt->fbtp_roffset == 0) {
- /*
- * Stack looks like this:
- *
- * [Higher addresses]
- *
- * Frame of caller
- * Extra args for callee
- * ------------------------
- * Frame from traced function: <previous sp (e.g. 0x1000), return address>
- * ------------------------
- * arm_context_t
- * ------------------------
- * Frame from trap handler: <previous sp (e.g. 0x1000) , traced PC >
- * The traced function never got to mov fp, sp,
- * so there is no frame in the backtrace pointing
- * to the frame on the stack containing the LR in the
- * caller.
- * ------------------------
- * |
- * |
- * | stack grows this way
- * |
- * |
- * v
- * [Lower addresses]
- */
-
- arm_saved_state_t *regs = (arm_saved_state_t *)(&((arm_context_t *)stack)->ss);
-
- /*
- * cpu_dtrace_caller compensates for fact that the traced function never got to update its fp.
- * When walking the stack, when we reach the frame where we extract a PC in the patched
- * function, we put the cpu_dtrace_caller in the backtrace instead. The next frame we extract
- * will be in the caller's caller, so we output a backtrace starting at the caller and going
- * sequentially up the stack.
- */
- CPU->cpu_dtrace_caller = get_saved_state_lr(regs);
dtrace_probe(fbt->fbtp_id, get_saved_state_reg(regs, 0), get_saved_state_reg(regs, 1),
get_saved_state_reg(regs, 2), get_saved_state_reg(regs, 3), get_saved_state_reg(regs, 4));
- CPU->cpu_dtrace_caller = 0;
} else {
- /*
- * When fbtp_roffset is non-zero, we know we are handling a return probe point.
- *
- *
- * Stack looks like this, as we've already popped the frame in the traced callee, and
- * we trap with lr set to the return address in the caller.
- * [Higher addresses]
- *
- * Frame of caller
- * Extra args for callee
- * ------------------------
- * arm_context_t
- * ------------------------
- * Frame from trap handler: <sp at time of trap, traced PC >
- * ------------------------
- * |
- * |
- * | stack grows this way
- * |
- * |
- * v
- * [Lower addresses]
- */
- arm_saved_state_t *regs = (arm_saved_state_t *)(&((arm_context_t *)stack)->ss);
-
- CPU->cpu_dtrace_caller = get_saved_state_lr(regs);
dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset, rval, 0, 0, 0);
- CPU->cpu_dtrace_caller = 0;
}
+
+ CPU->cpu_dtrace_caller = 0;
CPU->cpu_dtrace_invop_underway = 0;
}
if (FBT_EXCEPTION_CODE == trapno && !IS_USER_TRAP(regs)) {
boolean_t oldlevel = 0;
machine_inst_t emul = 0;
- uint64_t sp, pc, lr, imm;
+ uint64_t sp, lr;
+ uint32_t imm;
oldlevel = ml_set_interrupts_enabled(FALSE);
/*
* Skip over the patched NOP planted by sdt
*/
- pc = get_saved_state_pc(regs);
- set_saved_state_pc(regs, pc + DTRACE_INVOP_NOP_SKIP);
+ add_saved_state_pc(regs, DTRACE_INVOP_NOP_SKIP);
retval = KERN_SUCCESS;
} else if (FBT_IS_ARM64_ADD_FP_SP(emul)) {
/* retrieve the value to add */
set_saved_state_fp(regs, sp + val);
/* skip over the bytes of the patched instruction */
- pc = get_saved_state_pc(regs);
- set_saved_state_pc(regs, pc + DTRACE_INVOP_ADD_FP_SP_SKIP);
+ add_saved_state_pc(regs, DTRACE_INVOP_ADD_FP_SP_SKIP);
retval = KERN_SUCCESS;
} else if (FBT_IS_ARM64_RET(emul)) {
set_saved_state_pc(regs, lr);
retval = KERN_SUCCESS;
} else if (FBT_IS_ARM64_B_INSTR(emul)) {
- pc = get_saved_state_pc(regs);
imm = FBT_GET_ARM64_B_IMM(emul);
- set_saved_state_pc(regs, pc + imm);
+ add_saved_state_pc(regs, imm);
retval = KERN_SUCCESS;
} else if (emul == FBT_PATCHVAL) {
/* Means we encountered an error but handled it, try same inst again */
newfbt->fbtp_ctl = ctl;
newfbt->fbtp_loadcnt = ctl->mod_loadcnt;
- ASSERT(FBT_IS_ARM64_RET(theInstr));
+ ASSERT(FBT_IS_ARM64_RET(theInstr) || FBT_IS_ARM64_B_INSTR(theInstr));
newfbt->fbtp_rval = DTRACE_INVOP_RET;
newfbt->fbtp_roffset = (uintptr_t) ((uint8_t*) instr - (uint8_t *)symbolStart);
newfbt->fbtp_savedval = theInstr;