2 * Copyright (c) 2007-2018 Apple Inc. All rights reserved.
7 * The contents of this file are subject to the terms of the
8 * Common Development and Distribution License, Version 1.0 only
9 * (the "License"). You may not use this file except in compliance
12 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
13 * or http://www.opensolaris.org/os/licensing.
14 * See the License for the specific language governing permissions
15 * and limitations under the License.
17 * When distributing Covered Code, include this CDDL HEADER in each
18 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
19 * If applicable, add the following below this CDDL HEADER, with the
20 * fields enclosed by brackets "[]" replaced with your own identifying
21 * information: Portions Copyright [yyyy] [name of copyright owner]
26 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
30 #include <kern/thread.h>
31 #include <mach/thread_status.h>
32 #include <arm/proc_reg.h>
33 #include <arm/caches_internal.h>
34 #include <arm/thread.h>
36 #include <mach-o/loader.h>
37 #include <mach-o/nlist.h>
38 #include <libkern/kernel_mach_header.h>
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/errno.h>
44 #include <sys/ioctl.h>
46 #include <sys/fcntl.h>
47 #include <miscfs/devfs/devfs.h>
49 #include <sys/dtrace.h>
50 #include <sys/dtrace_impl.h>
53 #include <sys/dtrace_glue.h>
55 #define DTRACE_INVOP_PUSH_LR 8
56 #define DTRACE_INVOP_BL 9
57 #define DTRACE_INVOP_POP_PC 10
59 #define DTRACE_INVOP_THUMB_NOP_SKIP 2
60 #define DTRACE_INVOP_POP_PC_SKIP 2
61 #define DTRACE_INVOP_THUMB_SET_R7_SKIP 2
62 #define DTRACE_INVOP_THUMB_MOV_SP_TO_R7_SKIP 2
64 #define FBT_IS_THUMB_PUSH_LR(x) (((x) & 0x0000ff00) == 0x0000b500)
65 #define FBT_IS_THUMB_POP_R7(x) (((x) & 0x0000ff80) == 0x0000bc80)
66 #define FBT_IS_THUMB32_POP_R7LR(x, y) (((x) == 0x0000e8bd) && (((y) & 0x00004080) == 0x00004080))
67 #define FBT_IS_THUMB_POP_PC(x) (((x) & 0x0000ff00) == 0x0000bd00)
68 #define FBT_IS_THUMB_SET_R7(x) (((x) & 0x0000ff00) == 0x0000af00)
69 #define FBT_IS_THUMB_MOV_SP_TO_R7(x) (((x) & 0x0000ffff) == 0x0000466f)
70 #define FBT_THUMB_SET_R7_OFFSET(x) (((x) & 0x000000ff) << 2)
71 #define FBT_IS_THUMB_LDR_PC(x) (((x) & 0x0000f800) == 0x00004800)
72 #define FBT_IS_THUMB32_LDR_PC(x, y) ((x) == 0x0000f8df) /* Only for positive offset PC relative loads */
73 #define FBT_THUMB_STACK_REGS(x) ((x) & 0x00FF)
74 #define FBT_IS_THUMB_BX_REG(x) (((x) & 0x0000ff87) == 0x00004700)
76 #define FBT_PATCHVAL 0xdefc
77 #define FBT_AFRAMES_ENTRY 8
78 #define FBT_AFRAMES_RETURN 6
80 #define FBT_ENTRY "entry"
81 #define FBT_RETURN "return"
82 #define FBT_ADDR2NDX(addr) ((((uintptr_t)(addr)) >> 4) & fbt_probetab_mask)
84 #define VFPSAVE_ALIGN_DTRACE 16 /* This value should come from VFPSAVE_ALIGN */
86 extern dtrace_provider_id_t fbt_id
;
87 extern fbt_probe_t
**fbt_probetab
;
88 extern int fbt_probetab_mask
;
90 kern_return_t
fbt_perfCallback(int, struct arm_saved_state
*, __unused
int, __unused
int);
92 extern int dtrace_arm_condition_true(int cond
, int cpsr
);
95 /* Calculate the address of the ldr. (From the ARM Architecture reference) */
96 /* Does not check to see if it's really a load instruction, caller must do that */
99 thumb_ldr_pc_address(uint32_t address
)
101 return (address
& 0xFFFFFFFC) + (*(uint16_t*) address
& 0xFF) * 4 + 4;
105 thumb32_ldr_pc_address(uint32_t address
)
107 return (address
& 0xFFFFFFFC) + (*(uint16_t*) (address
+ 2) & 0xFFF) + 4;
110 /* Extract the current ITSTATE from the CPSR */
112 get_itstate(uint32_t cpsr
)
115 ((cpsr
& 0x06000000) >> 25) |
116 ((cpsr
& 0x0000FC00) >> 8);
120 clear_itstate(uint32_t* cpsr
)
122 *cpsr
&= ~0x0600FC00;
126 fbt_invop(uintptr_t addr
, uintptr_t * stack
, uintptr_t rval
)
128 fbt_probe_t
*fbt
= fbt_probetab
[FBT_ADDR2NDX(addr
)];
130 for (; fbt
!= NULL
; fbt
= fbt
->fbtp_hashnext
) {
131 if ((uintptr_t) fbt
->fbtp_patchpoint
== addr
) {
132 if (0 == CPU
->cpu_dtrace_invop_underway
) {
133 CPU
->cpu_dtrace_invop_underway
= 1; /* Race not possible on
134 * this per-cpu state */
136 struct arm_saved_state
* regs
= (struct arm_saved_state
*) stack
;
137 uintptr_t stack4
= *((uintptr_t*) regs
->sp
);
139 if ((regs
->cpsr
& PSR_MODE_MASK
) == PSR_FIQ_MODE
) {
141 * We do not handle probes firing from FIQ context. We used to
142 * try to undo the patch and rerun the instruction, but
143 * most of the time we can't do that successfully anyway.
144 * Instead, we just panic now so we fail fast.
146 panic("dtrace: fbt: The probe at %08x was called from FIQ_MODE", (unsigned) addr
);
150 * If we are not outside an IT block, and are not executing the last instruction of an IT block,
151 * then that is an instrumentation error or a code gen error. Either way, we panic.
153 uint32_t itstate
= get_itstate(regs
->cpsr
);
154 if ((itstate
& 0x7) != 0) {
155 panic("dtrace: fbt: Instruction stream error: Middle of IT block at %08x", (unsigned) addr
);
158 if (fbt
->fbtp_roffset
== 0) {
160 * We need the frames to set up the backtrace, but we won't have the frame pointers
161 * until after the instruction is emulated. So here we calculate the address of the
162 * frame pointer from the saved instruction and put it in the stack. Yes, we end up
163 * repeating this work again when we emulate the instruction.
165 * This assumes that the frame area is immediately after the saved reg storage!
167 uint32_t offset
= ((uint32_t) regs
) + sizeof(struct arm_saved_state
);
169 /* Match the stack alignment required for arm_vfpsaved_state */
170 offset
&= ~(VFPSAVE_ALIGN_DTRACE
- 1);
171 offset
+= VFPSAVE_ALIGN_DTRACE
+ sizeof(struct arm_vfpsaved_state
);
172 #endif /* __ARM_VFP__ */
173 if (FBT_IS_THUMB_SET_R7(fbt
->fbtp_savedval
)) {
174 *((uint32_t*) offset
) = regs
->sp
+ FBT_THUMB_SET_R7_OFFSET(fbt
->fbtp_savedval
);
176 *((uint32_t*) offset
) = regs
->sp
;
179 CPU
->cpu_dtrace_caller
= regs
->lr
;
180 dtrace_probe(fbt
->fbtp_id
, regs
->r
[0], regs
->r
[1], regs
->r
[2], regs
->r
[3], stack4
);
181 CPU
->cpu_dtrace_caller
= 0;
183 /* Check to see if we're in the middle of an IT block. */
186 * We've already checked previously to see how far we are in the IT block.
187 * Here we must be getting ready to execute the last instruction.
189 int condition_it
= (itstate
& 0xF0) >> 4;
191 if (dtrace_arm_condition_true(condition_it
, regs
->cpsr
) == 0) {
192 /* Condition wasn't true, so becomes a nop. */
193 clear_itstate(®s
->cpsr
);
194 CPU
->cpu_dtrace_invop_underway
= 0;
195 return DTRACE_INVOP_NOP
;
199 dtrace_probe(fbt
->fbtp_id
, fbt
->fbtp_roffset
, rval
, 0, 0, 0);
200 CPU
->cpu_dtrace_caller
= 0;
202 /* The dtrace script may access cpsr, so make sure to clear only after probe fired. */
203 clear_itstate(®s
->cpsr
);
205 CPU
->cpu_dtrace_invop_underway
= 0;
209 * On other architectures, we return a DTRACE constant to let the callback function
210 * know what was replaced. On the ARM, since the function prologue/epilogue machine code
211 * can vary, we need the actual bytes of the instruction, so return the savedval instead.
213 return fbt
->fbtp_savedval
;
220 #define IS_USER_TRAP(regs) (((regs)->cpsr & PSR_MODE_MASK) == PSR_USER_MODE)
221 #define T_INVALID_OPCODE EXC_BAD_INSTRUCTION
222 #define FBT_EXCEPTION_CODE T_INVALID_OPCODE
227 struct arm_saved_state
* regs
,
228 __unused
int unused1
,
229 __unused
int unused2
)
231 #pragma unused (unused1)
232 #pragma unused (unused2)
233 kern_return_t retval
= KERN_FAILURE
;
235 if (FBT_EXCEPTION_CODE
== trapno
&& !IS_USER_TRAP(regs
)) {
236 boolean_t oldlevel
= 0;
237 machine_inst_t emul
= 0;
239 oldlevel
= ml_set_interrupts_enabled(FALSE
);
242 "Ldtrace_invop_callsite_pre_label:\n"
244 ".private_extern _dtrace_invop_callsite_pre\n"
245 "_dtrace_invop_callsite_pre:\n"
246 " .long Ldtrace_invop_callsite_pre_label\n"
250 emul
= dtrace_invop(regs
->pc
, (uintptr_t*) regs
, regs
->r
[0]);
253 "Ldtrace_invop_callsite_post_label:\n"
255 ".private_extern _dtrace_invop_callsite_post\n"
256 "_dtrace_invop_callsite_post:\n"
257 " .long Ldtrace_invop_callsite_post_label\n"
262 * The following emulation code does not execute properly if we are in the middle of
263 * an IT block. IT blocks need to be handled in the dtrace_invop function. If we do
264 * manage to get here and we are inside an IT block, then we missed a case somewhere
265 * prior to this point.
267 uint32_t itstate
= get_itstate(regs
->cpsr
);
269 panic("dtrace: fbt: Not emulated: Middle of IT block at %08x", (unsigned) regs
->pc
);
272 if (emul
== DTRACE_INVOP_NOP
) {
273 regs
->pc
+= DTRACE_INVOP_THUMB_NOP_SKIP
;
274 retval
= KERN_SUCCESS
;
275 } else if (FBT_IS_THUMB_SET_R7(emul
)) {
276 regs
->r
[7] = regs
->sp
+ FBT_THUMB_SET_R7_OFFSET(emul
);
277 regs
->pc
+= DTRACE_INVOP_THUMB_SET_R7_SKIP
;
278 retval
= KERN_SUCCESS
;
279 } else if (FBT_IS_THUMB_MOV_SP_TO_R7(emul
)) {
280 regs
->r
[7] = regs
->sp
;
281 regs
->pc
+= DTRACE_INVOP_THUMB_MOV_SP_TO_R7_SKIP
;
282 retval
= KERN_SUCCESS
;
283 } else if (FBT_IS_THUMB_POP_PC(emul
)) {
284 uintptr_t* sp
= (uintptr_t*) regs
->sp
;
286 machine_inst_t mask
= 0x0001;
288 while (mask
& 0x00ff) {
290 /* Pop this register */
291 regs
->r
[regnum
] = *sp
++;
298 regs
->sp
= (uintptr_t) sp
;
300 regs
->cpsr
|= PSR_TF
;
302 regs
->cpsr
&= ~PSR_TF
;
305 retval
= KERN_SUCCESS
;
306 } else if (FBT_IS_THUMB_BX_REG(emul
)) {
307 regs
->pc
= regs
->r
[(emul
>> 3) & 0xF];
310 regs
->cpsr
|= PSR_TF
;
312 regs
->cpsr
&= ~PSR_TF
;
315 retval
= KERN_SUCCESS
;
316 } else if (emul
== FBT_PATCHVAL
) {
317 /* Means we encountered an error but handled it, try same inst again */
318 retval
= KERN_SUCCESS
;
320 retval
= KERN_FAILURE
;
323 ml_set_interrupts_enabled(oldlevel
);
330 fbt_provide_probe(struct modctl
*ctl
, const char *modname
, const char* symbolName
, machine_inst_t
* symbolStart
, machine_inst_t
*instrHigh
)
336 fbt_probe_t
*newfbt
, *retfbt
, *entryfbt
;
337 machine_inst_t
*instr
, *pushinstr
= NULL
, *limit
, theInstr
;
338 int foundPushLR
, savedRegs
;
341 * Guard against null symbols
343 if (!symbolStart
|| !instrHigh
|| instrHigh
< symbolStart
) {
344 kprintf("dtrace: %s has an invalid address\n", symbolName
);
349 * Assume the compiler doesn't schedule instructions in the prologue.
353 limit
= (machine_inst_t
*)instrHigh
;
354 for (j
= 0, instr
= symbolStart
, theInstr
= 0;
355 (j
< 8) && instr
< instrHigh
; j
++, instr
++) {
357 if (FBT_IS_THUMB_PUSH_LR(theInstr
)) {
359 /* Keep track of what registers we pushed. Compare this against the pop later. */
360 savedRegs
= FBT_THUMB_STACK_REGS(theInstr
);
363 if (foundPushLR
&& (FBT_IS_THUMB_SET_R7(theInstr
) || FBT_IS_THUMB_MOV_SP_TO_R7(theInstr
))) {
364 /* Guard against a random setting of r7 from sp, we make sure we found the push first */
367 if (FBT_IS_THUMB_BX_REG(theInstr
)) { /* We've gone too far, bail. */
370 if (FBT_IS_THUMB_POP_PC(theInstr
)) { /* We've gone too far, bail. */
374 /* Check for 4 byte thumb instruction */
375 if (dtrace_instr_size(theInstr
, 1) == 4) {
380 if (!(foundPushLR
&& (FBT_IS_THUMB_SET_R7(theInstr
) || FBT_IS_THUMB_MOV_SP_TO_R7(theInstr
)))) {
384 thisid
= dtrace_probe_lookup(fbt_id
, modname
, symbolName
, FBT_ENTRY
);
385 newfbt
= kmem_zalloc(sizeof(fbt_probe_t
), KM_SLEEP
);
386 newfbt
->fbtp_next
= NULL
;
387 strlcpy((char *)&(newfbt
->fbtp_name
), symbolName
, MAX_FBTP_NAME_CHARS
);
391 * The dtrace_probe previously existed, so we have to hook
392 * the newfbt entry onto the end of the existing fbt's
394 * If we find an fbt entry that was previously patched to
395 * fire, (as indicated by the current patched value), then
396 * we want to enable this newfbt on the spot.
398 entryfbt
= dtrace_probe_arg(fbt_id
, thisid
);
399 ASSERT(entryfbt
!= NULL
);
400 for (; entryfbt
!= NULL
; entryfbt
= entryfbt
->fbtp_next
) {
401 if (entryfbt
->fbtp_currentval
== entryfbt
->fbtp_patchval
) {
405 if (entryfbt
->fbtp_next
== NULL
) {
406 entryfbt
->fbtp_next
= newfbt
;
407 newfbt
->fbtp_id
= entryfbt
->fbtp_id
;
413 * The dtrace_probe did not previously exist, so we
414 * create it and hook in the newfbt. Since the probe is
415 * new, we obviously do not need to enable it on the spot.
417 newfbt
->fbtp_id
= dtrace_probe_create(fbt_id
, modname
, symbolName
, FBT_ENTRY
, FBT_AFRAMES_ENTRY
, newfbt
);
421 newfbt
->fbtp_patchpoint
= instr
;
422 newfbt
->fbtp_ctl
= ctl
;
423 newfbt
->fbtp_loadcnt
= ctl
->mod_loadcnt
;
424 newfbt
->fbtp_rval
= DTRACE_INVOP_PUSH_LR
;
425 newfbt
->fbtp_savedval
= theInstr
;
426 newfbt
->fbtp_patchval
= FBT_PATCHVAL
;
427 newfbt
->fbtp_currentval
= 0;
428 newfbt
->fbtp_hashnext
= fbt_probetab
[FBT_ADDR2NDX(instr
)];
429 fbt_probetab
[FBT_ADDR2NDX(instr
)] = newfbt
;
432 fbt_enable(NULL
, newfbt
->fbtp_id
, newfbt
);
436 * The fbt entry chain is in place, one entry point per symbol.
437 * The fbt return chain can have multiple return points per
439 * Here we find the end of the fbt return chain.
444 thisid
= dtrace_probe_lookup(fbt_id
, modname
, symbolName
, FBT_RETURN
);
447 /* The dtrace_probe previously existed, so we have to
448 * find the end of the existing fbt chain. If we find
449 * an fbt return that was previously patched to fire,
450 * (as indicated by the currrent patched value), then
451 * we want to enable any new fbts on the spot.
453 retfbt
= dtrace_probe_arg(fbt_id
, thisid
);
454 ASSERT(retfbt
!= NULL
);
455 for (; retfbt
!= NULL
; retfbt
= retfbt
->fbtp_next
) {
456 if (retfbt
->fbtp_currentval
== retfbt
->fbtp_patchval
) {
459 if (retfbt
->fbtp_next
== NULL
) {
469 * Go back to the start of the function, in case
470 * the compiler emitted pcrel data loads
471 * before R7 was adjusted.
473 instr
= pushinstr
+ 1;
475 if (instr
>= limit
) {
480 * We (desperately) want to avoid erroneously instrumenting a
481 * jump table. To determine if we're looking at a true instruction
482 * or an inline jump table that happens to contain the same
483 * byte sequences, we resort to some heuristic sleeze: we
484 * treat this instruction as being contained within a pointer,
485 * and see if that pointer points to within the body of the
486 * function. If it does, we refuse to instrument it.
488 if (((uintptr_t)instr
& 0x3) == 0) {
489 machine_inst_t
*ptr
= *(machine_inst_t
**)(void *)instr
;
491 if (ptr
>= (machine_inst_t
*)symbolStart
&& ptr
< limit
) {
492 /* kprintf("dtrace: fbt: Found jump table in %s, at %08x\n",symbolName,(unsigned)instr); */
499 * OK, it's an instruction.
503 /* Walked onto the start of the next routine? If so, bail out from this function */
504 if (FBT_IS_THUMB_PUSH_LR(theInstr
)) {
506 kprintf("dtrace: fbt: No return probe for %s, walked to next routine at %08x\n", symbolName
, (unsigned)instr
);
511 /* The PC relative data should be stored after the end of the function. If
512 * we see a PC relative load, assume the address to load from is the new end
513 * of the function. */
514 if (FBT_IS_THUMB_LDR_PC(theInstr
)) {
515 uint32_t newlimit
= thumb_ldr_pc_address((uint32_t) instr
);
516 if (newlimit
< (uint32_t) limit
) {
517 limit
= (machine_inst_t
*) newlimit
;
520 if ((instr
+ 1) < limit
&& FBT_IS_THUMB32_LDR_PC(*instr
, *(instr
+ 1))) {
521 uint32_t newlimit
= thumb32_ldr_pc_address((uint32_t) instr
);
522 if (newlimit
< (uint32_t) limit
) {
523 limit
= (machine_inst_t
*) newlimit
;
527 /* Look for the 1. pop { ..., pc } or 2. pop { ..., r7 } ... bx reg or 3. ldmia.w sp!, { ..., r7, lr } ... bx reg */
528 if (!FBT_IS_THUMB_POP_PC(theInstr
) &&
529 !FBT_IS_THUMB_POP_R7(theInstr
) &&
530 !FBT_IS_THUMB32_POP_R7LR(theInstr
, *(instr
+ 1))) {
532 if (dtrace_instr_size(theInstr
, 1) == 4) {
538 if (FBT_IS_THUMB_POP_PC(theInstr
)) {
539 if (savedRegs
!= FBT_THUMB_STACK_REGS(theInstr
)) {
540 /* What we're popping doesn't match what we're pushing, assume that we've
541 * gone too far in the function. Bail.
543 kprintf("dtrace: fbt: No return probe for %s, popped regs don't match at %08x\n", symbolName
, (unsigned)instr
);
547 /* Scan ahead for the bx */
548 for (j
= 0; (j
< 4) && (instr
< limit
); j
++, instr
++) {
550 if (FBT_IS_THUMB_BX_REG(theInstr
)) {
553 if (dtrace_instr_size(theInstr
, 1) == 4) {
558 if (!FBT_IS_THUMB_BX_REG(theInstr
)) {
564 * pop { ..., pc}, bx reg -- We have a winner!
567 newfbt
= kmem_zalloc(sizeof(fbt_probe_t
), KM_SLEEP
);
568 newfbt
->fbtp_next
= NULL
;
569 strlcpy((char *)&(newfbt
->fbtp_name
), symbolName
, MAX_FBTP_NAME_CHARS
);
571 if (retfbt
== NULL
) {
572 newfbt
->fbtp_id
= dtrace_probe_create(fbt_id
, modname
,
573 symbolName
, FBT_RETURN
, FBT_AFRAMES_RETURN
, newfbt
);
575 retfbt
->fbtp_next
= newfbt
;
576 newfbt
->fbtp_id
= retfbt
->fbtp_id
;
580 newfbt
->fbtp_patchpoint
= instr
;
581 newfbt
->fbtp_ctl
= ctl
;
582 newfbt
->fbtp_loadcnt
= ctl
->mod_loadcnt
;
584 ASSERT(FBT_IS_THUMB_POP_PC(theInstr
) || FBT_IS_THUMB_BX_REG(theInstr
));
585 newfbt
->fbtp_rval
= DTRACE_INVOP_POP_PC
;
586 newfbt
->fbtp_roffset
=
587 (uintptr_t) ((uint8_t*) instr
- (uint8_t *)symbolStart
);
588 newfbt
->fbtp_savedval
= theInstr
;
589 newfbt
->fbtp_patchval
= FBT_PATCHVAL
;
590 newfbt
->fbtp_currentval
= 0;
591 newfbt
->fbtp_hashnext
= fbt_probetab
[FBT_ADDR2NDX(instr
)];
592 fbt_probetab
[FBT_ADDR2NDX(instr
)] = newfbt
;
595 fbt_enable(NULL
, newfbt
->fbtp_id
, newfbt
);