2 * Copyright (c) 2007 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 /* #pragma ident "@(#)fbt.c 1.15 05/09/19 SMI" */
34 #define _KERNEL /* Solaris vs. Darwin */
38 #define MACH__POSIX_C_SOURCE_PRIVATE 1 /* pulls in suitable savearea from
39 * mach/ppc/thread_status.h */
40 #include <kern/thread.h>
41 #include <mach/thread_status.h>
42 #include <arm/proc_reg.h>
43 #include <arm/caches_internal.h>
45 #include <mach-o/loader.h>
46 #include <mach-o/nlist.h>
47 #include <libkern/kernel_mach_header.h>
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/errno.h>
53 #include <sys/ioctl.h>
55 #include <sys/fcntl.h>
56 #include <miscfs/devfs/devfs.h>
58 #include <sys/dtrace.h>
59 #include <sys/dtrace_impl.h>
62 #include <sys/dtrace_glue.h>
64 #define DTRACE_INVOP_PUSH_FRAME 11
66 #define DTRACE_INVOP_NOP_SKIP 4
67 #define DTRACE_INVOP_ADD_FP_SP_SKIP 4
69 #define DTRACE_INVOP_POP_PC_SKIP 2
72 * stp fp, lr, [sp, #val]
73 * stp fp, lr, [sp, #val]!
75 #define FBT_IS_ARM64_FRAME_PUSH(x) \
76 (((x) & 0xffc07fff) == 0xa9007bfd || ((x) & 0xffc07fff) == 0xa9807bfd)
79 * stp Xt1, Xt2, [sp, #val]
80 * stp Xt1, Xt2, [sp, #val]!
82 #define FBT_IS_ARM64_PUSH(x) \
83 (((x) & 0xffc003e0) == 0xa90003e0 || ((x) & 0xffc003e0) == 0xa98003e0)
86 * ldp fp, lr, [sp, #val]
87 * ldp fp, lr, [sp], #val
89 #define FBT_IS_ARM64_FRAME_POP(x) \
90 (((x) & 0xffc07fff) == 0xa9407bfd || ((x) & 0xffc07fff) == 0xa8c07bfd)
92 #define FBT_IS_ARM64_ADD_FP_SP(x) (((x) & 0xffc003ff) == 0x910003fd) /* add fp, sp, #val (add fp, sp, #0 == mov fp, sp) */
93 #define FBT_IS_ARM64_RET(x) ((x) == 0xd65f03c0) /* ret */
96 #define FBT_B_MASK 0xff000000
97 #define FBT_B_IMM_MASK 0x00ffffff
98 #define FBT_B_INSTR 0x14000000
100 #define FBT_IS_ARM64_B_INSTR(x) ((x & FBT_B_MASK) == FBT_B_INSTR)
101 #define FBT_GET_ARM64_B_IMM(x) ((x & FBT_B_IMM_MASK) << 2)
103 #define FBT_PATCHVAL 0xe7eeee7e
104 #define FBT_AFRAMES_ENTRY 7
105 #define FBT_AFRAMES_RETURN 7
107 #define FBT_ENTRY "entry"
108 #define FBT_RETURN "return"
109 #define FBT_ADDR2NDX(addr) ((((uintptr_t)(addr)) >> 4) & fbt_probetab_mask)
111 extern dtrace_provider_id_t fbt_id
;
112 extern fbt_probe_t
**fbt_probetab
;
113 extern int fbt_probetab_mask
;
115 kern_return_t
fbt_perfCallback(int, struct arm_saved_state
*, __unused
int, __unused
int);
118 fbt_invop(uintptr_t addr
, uintptr_t * stack
, uintptr_t rval
)
120 fbt_probe_t
*fbt
= fbt_probetab
[FBT_ADDR2NDX(addr
)];
122 for (; fbt
!= NULL
; fbt
= fbt
->fbtp_hashnext
) {
123 if ((uintptr_t) fbt
->fbtp_patchpoint
== addr
) {
124 if (0 == CPU
->cpu_dtrace_invop_underway
) {
125 CPU
->cpu_dtrace_invop_underway
= 1; /* Race not possible on
126 * this per-cpu state */
128 if (fbt
->fbtp_roffset
== 0) {
130 * Stack looks like this:
135 * Extra args for callee
136 * ------------------------
137 * Frame from traced function: <previous sp (e.g. 0x1000), return address>
138 * ------------------------
140 * ------------------------
141 * Frame from trap handler: <previous sp (e.g. 0x1000) , traced PC >
142 * The traced function never got to mov fp, sp,
143 * so there is no frame in the backtrace pointing
144 * to the frame on the stack containing the LR in the
146 * ------------------------
149 * | stack grows this way
156 arm_saved_state_t
*regs
= (arm_saved_state_t
*)(&((arm_context_t
*)stack
)->ss
);
159 * cpu_dtrace_caller compensates for fact that the traced function never got to update its fp.
160 * When walking the stack, when we reach the frame where we extract a PC in the patched
161 * function, we put the cpu_dtrace_caller in the backtrace instead. The next frame we extract
162 * will be in the caller's caller, so we output a backtrace starting at the caller and going
163 * sequentially up the stack.
165 CPU
->cpu_dtrace_caller
= get_saved_state_lr(regs
);
166 dtrace_probe(fbt
->fbtp_id
, get_saved_state_reg(regs
, 0), get_saved_state_reg(regs
, 1),
167 get_saved_state_reg(regs
, 2), get_saved_state_reg(regs
, 3),get_saved_state_reg(regs
, 4));
168 CPU
->cpu_dtrace_caller
= 0;
171 * When fbtp_roffset is non-zero, we know we are handling a return probe point.
174 * Stack looks like this, as we've already popped the frame in the traced callee, and
175 * we trap with lr set to the return address in the caller.
179 * Extra args for callee
180 * ------------------------
182 * ------------------------
183 * Frame from trap handler: <sp at time of trap, traced PC >
184 * ------------------------
187 * | stack grows this way
193 arm_saved_state_t
*regs
= (arm_saved_state_t
*)(&((arm_context_t
*)stack
)->ss
);
195 CPU
->cpu_dtrace_caller
= get_saved_state_lr(regs
);
196 dtrace_probe(fbt
->fbtp_id
, fbt
->fbtp_roffset
, rval
, 0, 0, 0);
197 CPU
->cpu_dtrace_caller
= 0;
199 CPU
->cpu_dtrace_invop_underway
= 0;
203 On other architectures, we return a DTRACE constant to let the callback function
204 know what was replaced. On the ARM, since the function prologue/epilogue machine code
205 can vary, we need the actual bytes of the instruction, so return the savedval instead.
207 return (fbt
->fbtp_savedval
);
214 #define IS_USER_TRAP(regs) (PSR64_IS_USER(get_saved_state_cpsr(regs)))
215 #define T_INVALID_OPCODE EXC_BAD_INSTRUCTION
216 #define FBT_EXCEPTION_CODE T_INVALID_OPCODE
221 struct arm_saved_state
* regs
,
222 __unused
int unused1
,
223 __unused
int unused2
)
225 kern_return_t retval
= KERN_FAILURE
;
227 if (FBT_EXCEPTION_CODE
== trapno
&& !IS_USER_TRAP(regs
)) {
228 boolean_t oldlevel
= 0;
229 machine_inst_t emul
= 0;
230 uint64_t sp
, pc
, lr
, imm
;
232 oldlevel
= ml_set_interrupts_enabled(FALSE
);
235 "Ldtrace_invop_callsite_pre_label:\n"
237 ".private_extern _dtrace_invop_callsite_pre\n"
238 "_dtrace_invop_callsite_pre:\n"
239 " .quad Ldtrace_invop_callsite_pre_label\n"
243 emul
= dtrace_invop(get_saved_state_pc(regs
), (uintptr_t*) regs
, get_saved_state_reg(regs
,0));
246 "Ldtrace_invop_callsite_post_label:\n"
248 ".private_extern _dtrace_invop_callsite_post\n"
249 "_dtrace_invop_callsite_post:\n"
250 " .quad Ldtrace_invop_callsite_post_label\n"
254 if (emul
== DTRACE_INVOP_NOP
) {
256 * Skip over the patched NOP planted by sdt
258 pc
= get_saved_state_pc(regs
);
259 set_saved_state_pc(regs
, pc
+ DTRACE_INVOP_NOP_SKIP
);
260 retval
= KERN_SUCCESS
;
261 } else if (FBT_IS_ARM64_ADD_FP_SP(emul
)) {
262 /* retrieve the value to add */
263 uint64_t val
= (emul
>> 10) & 0xfff;
267 sp
= get_saved_state_sp(regs
);
270 * emulate the instruction:
273 assert(sp
< (UINT64_MAX
- val
));
274 set_saved_state_fp(regs
, sp
+ val
);
276 /* skip over the bytes of the patched instruction */
277 pc
= get_saved_state_pc(regs
);
278 set_saved_state_pc(regs
, pc
+ DTRACE_INVOP_ADD_FP_SP_SKIP
);
280 retval
= KERN_SUCCESS
;
281 } else if (FBT_IS_ARM64_RET(emul
)) {
282 lr
= get_saved_state_lr(regs
);
283 set_saved_state_pc(regs
, lr
);
284 retval
= KERN_SUCCESS
;
285 } else if (FBT_IS_ARM64_B_INSTR(emul
)) {
286 pc
= get_saved_state_pc(regs
);
287 imm
= FBT_GET_ARM64_B_IMM(emul
);
288 set_saved_state_pc(regs
, pc
+ imm
);
289 retval
= KERN_SUCCESS
;
290 } else if (emul
== FBT_PATCHVAL
) {
291 /* Means we encountered an error but handled it, try same inst again */
292 retval
= KERN_SUCCESS
;
294 retval
= KERN_FAILURE
;
297 ml_set_interrupts_enabled(oldlevel
);
304 fbt_provide_probe(struct modctl
*ctl
, uintptr_t instrLow
, uintptr_t instrHigh
, char *modname
, char* symbolName
, machine_inst_t
* symbolStart
)
310 fbt_probe_t
*newfbt
, *retfbt
, *entryfbt
;
311 machine_inst_t
*instr
, *pushinstr
= NULL
, *limit
, theInstr
;
312 int foundPushLR
, savedRegs
;
315 * Guard against null symbols
317 if (!symbolStart
|| !instrLow
|| !instrHigh
) {
318 kprintf("dtrace: %s has an invalid address\n", symbolName
);
323 * Assume the compiler doesn't schedule instructions in the prologue.
328 limit
= (machine_inst_t
*)instrHigh
;
330 assert(sizeof(*instr
) == 4);
332 for (j
= 0, instr
= symbolStart
, theInstr
= 0;
333 (j
< 8) && ((uintptr_t)instr
>= instrLow
) && (instrHigh
> (uintptr_t)(instr
)); j
++, instr
++)
336 * Count the number of time we pushed something onto the stack
337 * before hitting a frame push. That will give us an estimation
338 * of how many stack pops we should expect when looking for the
342 if (FBT_IS_ARM64_FRAME_PUSH(theInstr
)) {
347 if (foundPushLR
&& (FBT_IS_ARM64_ADD_FP_SP(theInstr
)))
348 /* Guard against a random setting of fp from sp, we make sure we found the push first */
350 if (FBT_IS_ARM64_RET(theInstr
)) /* We've gone too far, bail. */
352 if (FBT_IS_ARM64_FRAME_POP(theInstr
)) /* We've gone too far, bail. */
356 if (!(foundPushLR
&& (FBT_IS_ARM64_ADD_FP_SP(theInstr
)))) {
360 thisid
= dtrace_probe_lookup(fbt_id
, modname
, symbolName
, FBT_ENTRY
);
361 newfbt
= kmem_zalloc(sizeof(fbt_probe_t
), KM_SLEEP
);
362 newfbt
->fbtp_next
= NULL
;
363 strlcpy( (char *)&(newfbt
->fbtp_name
), symbolName
, MAX_FBTP_NAME_CHARS
);
367 * The dtrace_probe previously existed, so we have to hook
368 * the newfbt entry onto the end of the existing fbt's
370 * If we find an fbt entry that was previously patched to
371 * fire, (as indicated by the current patched value), then
372 * we want to enable this newfbt on the spot.
374 entryfbt
= dtrace_probe_arg (fbt_id
, thisid
);
375 ASSERT (entryfbt
!= NULL
);
376 for(; entryfbt
!= NULL
; entryfbt
= entryfbt
->fbtp_next
) {
377 if (entryfbt
->fbtp_currentval
== entryfbt
->fbtp_patchval
)
380 if (entryfbt
->fbtp_next
== NULL
) {
381 entryfbt
->fbtp_next
= newfbt
;
382 newfbt
->fbtp_id
= entryfbt
->fbtp_id
;
389 * The dtrace_probe did not previously exist, so we
390 * create it and hook in the newfbt. Since the probe is
391 * new, we obviously do not need to enable it on the spot.
393 newfbt
->fbtp_id
= dtrace_probe_create(fbt_id
, modname
, symbolName
, FBT_ENTRY
, FBT_AFRAMES_ENTRY
, newfbt
);
397 newfbt
->fbtp_patchpoint
= instr
;
398 newfbt
->fbtp_ctl
= ctl
;
399 newfbt
->fbtp_loadcnt
= ctl
->mod_loadcnt
;
400 newfbt
->fbtp_rval
= DTRACE_INVOP_PUSH_FRAME
;
401 newfbt
->fbtp_savedval
= theInstr
;
402 newfbt
->fbtp_patchval
= FBT_PATCHVAL
;
403 newfbt
->fbtp_currentval
= 0;
404 newfbt
->fbtp_hashnext
= fbt_probetab
[FBT_ADDR2NDX(instr
)];
405 fbt_probetab
[FBT_ADDR2NDX(instr
)] = newfbt
;
408 fbt_enable(NULL
, newfbt
->fbtp_id
, newfbt
);
411 * The fbt entry chain is in place, one entry point per symbol.
412 * The fbt return chain can have multiple return points per
414 * Here we find the end of the fbt return chain.
419 thisid
= dtrace_probe_lookup(fbt_id
, modname
, symbolName
, FBT_RETURN
);
422 /* The dtrace_probe previously existed, so we have to
423 * find the end of the existing fbt chain. If we find
424 * an fbt return that was previously patched to fire,
425 * (as indicated by the currrent patched value), then
426 * we want to enable any new fbts on the spot.
428 retfbt
= dtrace_probe_arg (fbt_id
, thisid
);
429 ASSERT(retfbt
!= NULL
);
430 for (; retfbt
!= NULL
; retfbt
= retfbt
->fbtp_next
) {
431 if (retfbt
->fbtp_currentval
== retfbt
->fbtp_patchval
)
433 if(retfbt
->fbtp_next
== NULL
)
443 * Go back to the start of the function, in case
444 * the compiler emitted pcrel data loads
445 * before FP was adjusted.
447 instr
= pushinstr
+ 1;
452 /* XXX FIXME ... extra jump table detection? */
455 * OK, it's an instruction.
459 /* Walked onto the start of the next routine? If so, bail out from this function */
460 if (FBT_IS_ARM64_FRAME_PUSH(theInstr
)) {
462 kprintf("dtrace: fbt: No return probe for %s, walked to next routine at 0x%016llx\n",symbolName
,(uint64_t)instr
);
466 /* XXX fancy detection of end of function using PC-relative loads */
470 * ldp fp, lr, [sp], #val
471 * ldp fp, lr, [sp, #val]
473 if (!FBT_IS_ARM64_FRAME_POP(theInstr
)) {
478 /* go to the next instruction */
481 /* Scan ahead for a ret or a branch outside the function */
482 for (; instr
< limit
; instr
++) {
484 if (FBT_IS_ARM64_RET(theInstr
))
486 if (FBT_IS_ARM64_B_INSTR(theInstr
)) {
487 machine_inst_t
*dest
= instr
+ FBT_GET_ARM64_B_IMM(theInstr
);
489 * Check whether the destination of the branch
490 * is outside of the function
492 if (dest
>= limit
|| dest
< symbolStart
)
497 if (!FBT_IS_ARM64_RET(theInstr
) && !FBT_IS_ARM64_B_INSTR(theInstr
))
500 newfbt
= kmem_zalloc(sizeof(fbt_probe_t
), KM_SLEEP
);
501 newfbt
->fbtp_next
= NULL
;
502 strlcpy( (char *)&(newfbt
->fbtp_name
), symbolName
, MAX_FBTP_NAME_CHARS
);
504 if (retfbt
== NULL
) {
505 newfbt
->fbtp_id
= dtrace_probe_create(fbt_id
, modname
,
506 symbolName
, FBT_RETURN
, FBT_AFRAMES_RETURN
, newfbt
);
508 retfbt
->fbtp_next
= newfbt
;
509 newfbt
->fbtp_id
= retfbt
->fbtp_id
;
513 newfbt
->fbtp_patchpoint
= instr
;
514 newfbt
->fbtp_ctl
= ctl
;
515 newfbt
->fbtp_loadcnt
= ctl
->mod_loadcnt
;
517 ASSERT(FBT_IS_ARM64_RET(theInstr
));
518 newfbt
->fbtp_rval
= DTRACE_INVOP_RET
;
519 newfbt
->fbtp_roffset
= (uintptr_t) ((uint8_t*) instr
- (uint8_t *)symbolStart
);
520 newfbt
->fbtp_savedval
= theInstr
;
521 newfbt
->fbtp_patchval
= FBT_PATCHVAL
;
522 newfbt
->fbtp_currentval
= 0;
523 newfbt
->fbtp_hashnext
= fbt_probetab
[FBT_ADDR2NDX(instr
)];
524 fbt_probetab
[FBT_ADDR2NDX(instr
)] = newfbt
;
527 fbt_enable(NULL
, newfbt
->fbtp_id
, newfbt
);
534 fbt_provide_module_kernel_syms(struct modctl
*ctl
)
536 kernel_mach_header_t
*mh
;
537 struct load_command
*cmd
;
538 kernel_segment_command_t
*orig_ts
= NULL
, *orig_le
= NULL
;
539 struct symtab_command
*orig_st
= NULL
;
540 kernel_nlist_t
*sym
= NULL
;
542 uintptr_t instrLow
, instrHigh
;
546 mh
= (kernel_mach_header_t
*)(ctl
->mod_address
);
547 modname
= ctl
->mod_modname
;
550 * Employees of dtrace and their families are ineligible. Void
554 if (mh
->magic
!= MH_MAGIC_KERNEL
)
557 cmd
= (struct load_command
*) & mh
[1];
558 for (i
= 0; i
< mh
->ncmds
; i
++) {
559 if (cmd
->cmd
== LC_SEGMENT_KERNEL
) {
560 kernel_segment_command_t
*orig_sg
= (kernel_segment_command_t
*) cmd
;
562 if (LIT_STRNEQL(orig_sg
->segname
, SEG_TEXT
))
564 else if (LIT_STRNEQL(orig_sg
->segname
, SEG_LINKEDIT
))
566 else if (LIT_STRNEQL(orig_sg
->segname
, ""))
567 orig_ts
= orig_sg
; /* kexts have a single
569 } else if (cmd
->cmd
== LC_SYMTAB
)
570 orig_st
= (struct symtab_command
*) cmd
;
572 cmd
= (struct load_command
*) ((caddr_t
) cmd
+ cmd
->cmdsize
);
575 if ((orig_ts
== NULL
) || (orig_st
== NULL
) || (orig_le
== NULL
))
578 sym
= (kernel_nlist_t
*)(orig_le
->vmaddr
+ orig_st
->symoff
- orig_le
->fileoff
);
579 strings
= (char *)(orig_le
->vmaddr
+ orig_st
->stroff
- orig_le
->fileoff
);
581 /* Find extent of the TEXT section */
582 instrLow
= (uintptr_t) orig_ts
->vmaddr
;
583 instrHigh
= (uintptr_t) (orig_ts
->vmaddr
+ orig_ts
->vmsize
);
585 for (i
= 0; i
< orig_st
->nsyms
; i
++) {
586 uint8_t n_type
= sym
[i
].n_type
& (N_TYPE
| N_EXT
);
587 char *name
= strings
+ sym
[i
].n_un
.n_strx
;
589 /* Check that the symbol is a global and that it has a name. */
590 if (((N_SECT
| N_EXT
) != n_type
&& (N_ABS
| N_EXT
) != n_type
))
593 if (0 == sym
[i
].n_un
.n_strx
) /* iff a null, "", name. */
596 /* Lop off omnipresent leading underscore. */
601 * We're only blacklisting functions in the kernel for now.
603 if (MOD_IS_MACH_KERNEL(ctl
) && fbt_excluded(name
))
606 fbt_provide_probe(ctl
, instrLow
, instrHigh
, modname
, name
, (machine_inst_t
*)sym
[i
].n_value
);