2 * Copyright (c) 2016 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
32 #include <kern/assert.h>
33 #include <kern/backtrace.h>
34 #include <kern/thread.h>
35 #include <sys/errno.h>
36 #include <vm/vm_map.h>
38 #if defined(__arm__) || defined(__arm64__)
39 #include <arm/cpu_data.h>
40 #include <arm/cpu_data_internal.h>
43 #if defined(HAS_APPLE_PAC)
48 unsigned int __attribute__((noinline
))
49 backtrace(uintptr_t *bt
, unsigned int max_frames
, bool *was_truncated_out
)
51 return backtrace_frame(bt
, max_frames
, __builtin_frame_address(0),
56 * This function captures a backtrace from the current stack and returns the
57 * number of frames captured, limited by max_frames and starting at start_frame.
58 * It's fast because it does no checking to make sure there isn't bad data.
59 * Since it's only called from threads that we're going to keep executing,
60 * if there's bad data we were going to die eventually. If this function is
61 * inlined, it doesn't record the frame of the function it's inside (because
62 * there's no stack frame).
64 unsigned int __attribute__((noinline
, not_tail_called
))
65 backtrace_frame(uintptr_t *bt
, unsigned int max_frames
, void *start_frame
,
66 bool *was_truncated_out
)
68 thread_t thread
= current_thread();
70 unsigned int frame_index
= 0;
71 uintptr_t top
, bottom
;
75 assert(max_frames
> 0);
78 bottom
= thread
->kernel_stack
;
79 top
= bottom
+ kernel_stack_size
;
81 #define IN_STK_BOUNDS(__addr) \
82 (((uintptr_t)(__addr) >= (uintptr_t)bottom) && \
83 ((uintptr_t)(__addr) < (uintptr_t)top))
85 in_valid_stack
= IN_STK_BOUNDS(fp
);
87 if (!in_valid_stack
) {
91 while (fp
!= NULL
&& frame_index
< max_frames
) {
92 uintptr_t *next_fp
= (uintptr_t *)*fp
;
93 uintptr_t ret_addr
= *(fp
+ 1); /* return address is one word higher than frame pointer */
96 * If the frame pointer is 0, backtracing has reached the top of
97 * the stack and there is no return address. Some stacks might not
98 * have set this up, so bounds check, as well.
100 in_valid_stack
= IN_STK_BOUNDS(next_fp
);
102 if (next_fp
== NULL
|| !in_valid_stack
) {
106 #if defined(HAS_APPLE_PAC)
107 /* return addresses signed by arm64e ABI */
108 bt
[frame_index
++] = (uintptr_t) ptrauth_strip((void *)ret_addr
, ptrauth_key_return_address
);
109 #else /* defined(HAS_APPLE_PAC) */
110 bt
[frame_index
++] = ret_addr
;
111 #endif /* !defined(HAS_APPLE_PAC) */
113 /* stacks grow down; backtracing should be moving to higher addresses */
120 /* NULL-terminate the list, if space is available */
121 if (frame_index
!= max_frames
) {
125 if (fp
!= NULL
&& frame_index
== max_frames
&& was_truncated_out
) {
126 *was_truncated_out
= true;
133 #if defined(__x86_64__)
136 interrupted_kernel_pc_fp(uintptr_t *pc
, uintptr_t *fp
)
138 x86_saved_state_t
*state
;
142 state
= current_cpu_datap()->cpu_int_state
;
147 state_64
= is_saved_state64(state
);
150 cs
= saved_state64(state
)->isf
.cs
;
152 cs
= saved_state32(state
)->cs
;
154 /* return early if interrupted a thread in user space */
155 if ((cs
& SEL_PL
) == SEL_PL_U
) {
160 *pc
= saved_state64(state
)->isf
.rip
;
161 *fp
= saved_state64(state
)->rbp
;
163 *pc
= saved_state32(state
)->eip
;
164 *fp
= saved_state32(state
)->ebp
;
169 #elif defined(__arm64__)
172 interrupted_kernel_pc_fp(uintptr_t *pc
, uintptr_t *fp
)
174 struct arm_saved_state
*state
;
177 state
= getCpuDatap()->cpu_int_state
;
181 state_64
= is_saved_state64(state
);
183 /* return early if interrupted a thread in user space */
184 if (PSR64_IS_USER(get_saved_state_cpsr(state
))) {
188 *pc
= get_saved_state_pc(state
);
189 *fp
= get_saved_state_fp(state
);
193 #elif defined(__arm__)
196 interrupted_kernel_pc_fp(uintptr_t *pc
, uintptr_t *fp
)
198 struct arm_saved_state
*state
;
200 state
= getCpuDatap()->cpu_int_state
;
205 /* return early if interrupted a thread in user space */
206 if (PSR_IS_USER(get_saved_state_cpsr(state
))) {
210 *pc
= get_saved_state_pc(state
);
211 *fp
= get_saved_state_fp(state
);
215 #else /* defined(__arm__) */
216 #error "interrupted_kernel_pc_fp: unsupported architecture"
217 #endif /* !defined(__arm__) */
220 backtrace_interrupted(uintptr_t *bt
, unsigned int max_frames
,
221 bool *was_truncated_out
)
228 assert(max_frames
> 0);
229 assert(ml_at_interrupt_context() == TRUE
);
231 kr
= interrupted_kernel_pc_fp(&pc
, &fp
);
232 if (kr
!= KERN_SUCCESS
) {
237 if (max_frames
== 1) {
241 return backtrace_frame(bt
+ 1, max_frames
- 1, (void *)fp
,
242 was_truncated_out
) + 1;
246 backtrace_user(uintptr_t *bt
, unsigned int max_frames
,
247 int *error_out
, bool *user_64_out
, bool *was_truncated_out
)
249 return backtrace_thread_user(current_thread(), bt
, max_frames
,
250 error_out
, user_64_out
, was_truncated_out
);
254 backtrace_thread_user(void *thread
, uintptr_t *bt
, unsigned int max_frames
,
255 int *error_out
, bool *user_64_out
, bool *was_truncated_out
)
258 uintptr_t pc
= 0, fp
= 0, next_fp
= 0;
259 vm_map_t map
= NULL
, old_map
= NULL
;
260 unsigned int frame_index
= 0;
262 size_t frame_size
= 0;
265 assert(max_frames
> 0);
267 #if defined(__x86_64__)
269 /* don't allow a malformed user stack to copyin arbitrary kernel data */
270 #define INVALID_USER_FP(FP) ((FP) == 0 || !IS_USERADDR64_CANONICAL((FP)))
272 x86_saved_state_t
*state
= get_user_regs(thread
);
278 user_64
= is_saved_state64(state
);
280 pc
= saved_state64(state
)->isf
.rip
;
281 fp
= saved_state64(state
)->rbp
;
283 pc
= saved_state32(state
)->eip
;
284 fp
= saved_state32(state
)->ebp
;
287 #elif defined(__arm64__)
289 /* ARM expects stack frames to be aligned to 16 bytes */
290 #define INVALID_USER_FP(FP) ((FP) == 0 || ((FP) & 0x3UL) != 0UL)
292 struct arm_saved_state
*state
= get_user_regs(thread
);
297 user_64
= is_saved_state64(state
);
298 pc
= get_saved_state_pc(state
);
299 fp
= get_saved_state_fp(state
);
301 #elif defined(__arm__)
303 /* ARM expects stack frames to be aligned to 16 bytes */
304 #define INVALID_USER_FP(FP) ((FP) == 0 || ((FP) & 0x3UL) != 0UL)
306 struct arm_saved_state
*state
= get_user_regs(thread
);
312 pc
= get_saved_state_pc(state
);
313 fp
= get_saved_state_fp(state
);
315 #else /* defined(__arm__) */
316 #error "backtrace_thread_user: unsupported architecture"
317 #endif /* !defined(__arm__) */
319 bt
[frame_index
++] = pc
;
321 if (frame_index
>= max_frames
) {
325 if (INVALID_USER_FP(fp
)) {
329 assert(ml_get_interrupts_enabled() == TRUE
);
330 if (!ml_get_interrupts_enabled()) {
345 frame_size
= 2 * (user_64
? 8 : 4);
347 /* switch to the correct map, for copyin */
348 if (thread
!= current_thread()) {
349 map
= get_task_map_reference(get_threadtask(thread
));
353 old_map
= vm_map_switch(map
);
358 while (fp
!= 0 && frame_index
< max_frames
) {
359 err
= copyin(fp
, (char *)&frame
, frame_size
);
361 if (was_truncated_out
) {
362 *was_truncated_out
= true;
367 next_fp
= user_64
? frame
.u64
.fp
: frame
.u32
.fp
;
369 if (INVALID_USER_FP(next_fp
)) {
373 uintptr_t ret_addr
= user_64
? frame
.u64
.ret
: frame
.u32
.ret
;
374 #if defined(HAS_APPLE_PAC)
375 /* return addresses signed by arm64e ABI */
376 bt
[frame_index
++] = (uintptr_t)ptrauth_strip((void *)ret_addr
,
377 ptrauth_key_return_address
);
378 #else /* defined(HAS_APPLE_PAC) */
379 bt
[frame_index
++] = ret_addr
;
380 #endif /* !defined(HAS_APPLE_PAC) */
382 /* stacks grow down; backtracing should be moving to higher addresses */
391 (void)vm_map_switch(old_map
);
392 vm_map_deallocate(map
);
395 /* NULL-terminate the list, if space is available */
396 if (frame_index
!= max_frames
) {
400 if (fp
!= 0 && frame_index
== max_frames
&& was_truncated_out
) {
401 *was_truncated_out
= true;
405 *user_64_out
= user_64
;
412 #undef INVALID_USER_FP