1 | //! Implementation of various bits and pieces of the `panic!` macro and |
2 | //! associated runtime pieces. |
3 | //! |
4 | //! Specifically, this module contains the implementation of: |
5 | //! |
6 | //! * Panic hooks |
7 | //! * Executing a panic up to doing the actual implementation |
8 | //! * Shims around "try" |
9 | |
10 | #![deny (unsafe_op_in_unsafe_fn)] |
11 | |
12 | use crate::panic::BacktraceStyle; |
13 | use core::panic::{Location, PanicInfo, PanicPayload}; |
14 | |
15 | use crate::any::Any; |
16 | use crate::fmt; |
17 | use crate::intrinsics; |
18 | use crate::mem::{self, ManuallyDrop}; |
19 | use crate::process; |
20 | use crate::sync::atomic::{AtomicBool, Ordering}; |
21 | use crate::sync::{PoisonError, RwLock}; |
22 | use crate::sys::stdio::panic_output; |
23 | use crate::sys_common::backtrace; |
24 | use crate::thread; |
25 | |
26 | #[cfg (not(test))] |
27 | use crate::io::try_set_output_capture; |
28 | // make sure to use the stderr output configured |
29 | // by libtest in the real copy of std |
30 | #[cfg (test)] |
31 | use realstd::io::try_set_output_capture; |
32 | |
33 | // Binary interface to the panic runtime that the standard library depends on. |
34 | // |
35 | // The standard library is tagged with `#![needs_panic_runtime]` (introduced in |
36 | // RFC 1513) to indicate that it requires some other crate tagged with |
37 | // `#![panic_runtime]` to exist somewhere. Each panic runtime is intended to |
38 | // implement these symbols (with the same signatures) so we can get matched up |
39 | // to them. |
40 | // |
41 | // One day this may look a little less ad-hoc with the compiler helping out to |
42 | // hook up these functions, but it is not this day! |
43 | #[allow (improper_ctypes)] |
44 | extern "C" { |
45 | fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static); |
46 | } |
47 | |
48 | extern "Rust" { |
49 | /// `PanicPayload` lazily performs allocation only when needed (this avoids |
50 | /// allocations when using the "abort" panic runtime). |
51 | fn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32; |
52 | } |
53 | |
54 | /// This function is called by the panic runtime if FFI code catches a Rust |
55 | /// panic but doesn't rethrow it. We don't support this case since it messes |
56 | /// with our panic count. |
57 | #[cfg (not(test))] |
58 | #[rustc_std_internal_symbol ] |
59 | extern "C" fn __rust_drop_panic() -> ! { |
60 | rtabort!("Rust panics must be rethrown" ); |
61 | } |
62 | |
63 | /// This function is called by the panic runtime if it catches an exception |
64 | /// object which does not correspond to a Rust panic. |
65 | #[cfg (not(test))] |
66 | #[rustc_std_internal_symbol ] |
67 | extern "C" fn __rust_foreign_exception() -> ! { |
68 | rtabort!("Rust cannot catch foreign exceptions" ); |
69 | } |
70 | |
71 | enum Hook { |
72 | Default, |
73 | Custom(Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>), |
74 | } |
75 | |
76 | impl Hook { |
77 | #[inline ] |
78 | fn into_box(self) -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> { |
79 | match self { |
80 | Hook::Default => Box::new(default_hook), |
81 | Hook::Custom(hook: Box) + Sync + Send>) => hook, |
82 | } |
83 | } |
84 | } |
85 | |
86 | impl Default for Hook { |
87 | #[inline ] |
88 | fn default() -> Hook { |
89 | Hook::Default |
90 | } |
91 | } |
92 | |
93 | static HOOK: RwLock<Hook> = RwLock::new(Hook::Default); |
94 | |
95 | /// Registers a custom panic hook, replacing the previously registered hook. |
96 | /// |
97 | /// The panic hook is invoked when a thread panics, but before the panic runtime |
98 | /// is invoked. As such, the hook will run with both the aborting and unwinding |
99 | /// runtimes. |
100 | /// |
101 | /// The default hook, which is registered at startup, prints a message to standard error and |
102 | /// generates a backtrace if requested. This behavior can be customized using the `set_hook` function. |
103 | /// The current hook can be retrieved while reinstating the default hook with the [`take_hook`] |
104 | /// function. |
105 | /// |
106 | /// [`take_hook`]: ./fn.take_hook.html |
107 | /// |
108 | /// The hook is provided with a `PanicInfo` struct which contains information |
109 | /// about the origin of the panic, including the payload passed to `panic!` and |
110 | /// the source code location from which the panic originated. |
111 | /// |
112 | /// The panic hook is a global resource. |
113 | /// |
114 | /// # Panics |
115 | /// |
116 | /// Panics if called from a panicking thread. |
117 | /// |
118 | /// # Examples |
119 | /// |
120 | /// The following will print "Custom panic hook": |
121 | /// |
122 | /// ```should_panic |
123 | /// use std::panic; |
124 | /// |
125 | /// panic::set_hook(Box::new(|_| { |
126 | /// println!("Custom panic hook" ); |
127 | /// })); |
128 | /// |
129 | /// panic!("Normal panic" ); |
130 | /// ``` |
131 | #[stable (feature = "panic_hooks" , since = "1.10.0" )] |
132 | pub fn set_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>) { |
133 | if thread::panicking() { |
134 | panic!("cannot modify the panic hook from a panicking thread" ); |
135 | } |
136 | |
137 | let new: Hook = Hook::Custom(hook); |
138 | let mut hook: RwLockWriteGuard<'_, Hook> = HOOK.write().unwrap_or_else(op:PoisonError::into_inner); |
139 | let old: Hook = mem::replace(&mut *hook, src:new); |
140 | drop(hook); |
141 | // Only drop the old hook after releasing the lock to avoid deadlocking |
142 | // if its destructor panics. |
143 | drop(old); |
144 | } |
145 | |
146 | /// Unregisters the current panic hook and returns it, registering the default hook |
147 | /// in its place. |
148 | /// |
149 | /// *See also the function [`set_hook`].* |
150 | /// |
151 | /// [`set_hook`]: ./fn.set_hook.html |
152 | /// |
153 | /// If the default hook is registered it will be returned, but remain registered. |
154 | /// |
155 | /// # Panics |
156 | /// |
157 | /// Panics if called from a panicking thread. |
158 | /// |
159 | /// # Examples |
160 | /// |
161 | /// The following will print "Normal panic": |
162 | /// |
163 | /// ```should_panic |
164 | /// use std::panic; |
165 | /// |
166 | /// panic::set_hook(Box::new(|_| { |
167 | /// println!("Custom panic hook" ); |
168 | /// })); |
169 | /// |
170 | /// let _ = panic::take_hook(); |
171 | /// |
172 | /// panic!("Normal panic" ); |
173 | /// ``` |
174 | #[must_use ] |
175 | #[stable (feature = "panic_hooks" , since = "1.10.0" )] |
176 | pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> { |
177 | if thread::panicking() { |
178 | panic!("cannot modify the panic hook from a panicking thread" ); |
179 | } |
180 | |
181 | let mut hook: RwLockWriteGuard<'_, Hook> = HOOK.write().unwrap_or_else(op:PoisonError::into_inner); |
182 | let old_hook: Hook = mem::take(&mut *hook); |
183 | drop(hook); |
184 | |
185 | old_hook.into_box() |
186 | } |
187 | |
188 | /// Atomic combination of [`take_hook`] and [`set_hook`]. Use this to replace the panic handler with |
189 | /// a new panic handler that does something and then executes the old handler. |
190 | /// |
191 | /// [`take_hook`]: ./fn.take_hook.html |
192 | /// [`set_hook`]: ./fn.set_hook.html |
193 | /// |
194 | /// # Panics |
195 | /// |
196 | /// Panics if called from a panicking thread. |
197 | /// |
198 | /// # Examples |
199 | /// |
200 | /// The following will print the custom message, and then the normal output of panic. |
201 | /// |
202 | /// ```should_panic |
203 | /// #![feature(panic_update_hook)] |
204 | /// use std::panic; |
205 | /// |
206 | /// // Equivalent to |
207 | /// // let prev = panic::take_hook(); |
208 | /// // panic::set_hook(move |info| { |
209 | /// // println!("..."); |
210 | /// // prev(info); |
211 | /// // ); |
212 | /// panic::update_hook(move |prev, info| { |
213 | /// println!("Print custom message and execute panic handler as usual" ); |
214 | /// prev(info); |
215 | /// }); |
216 | /// |
217 | /// panic!("Custom and then normal" ); |
218 | /// ``` |
219 | #[unstable (feature = "panic_update_hook" , issue = "92649" )] |
220 | pub fn update_hook<F>(hook_fn: F) |
221 | where |
222 | F: Fn(&(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static), &PanicInfo<'_>) |
223 | + Sync |
224 | + Send |
225 | + 'static, |
226 | { |
227 | if thread::panicking() { |
228 | panic!("cannot modify the panic hook from a panicking thread" ); |
229 | } |
230 | |
231 | let mut hook: RwLockWriteGuard<'_, Hook> = HOOK.write().unwrap_or_else(op:PoisonError::into_inner); |
232 | let prev: Box) + Sync + Send> = mem::take(&mut *hook).into_box(); |
233 | *hook = Hook::Custom(Box::new(move |info: &PanicInfo<'_>| hook_fn(&prev, info))); |
234 | } |
235 | |
236 | /// The default panic handler. |
237 | fn default_hook(info: &PanicInfo<'_>) { |
238 | // If this is a double panic, make sure that we print a backtrace |
239 | // for this panic. Otherwise only print it if logging is enabled. |
240 | let backtrace = if info.force_no_backtrace() { |
241 | None |
242 | } else if panic_count::get_count() >= 2 { |
243 | BacktraceStyle::full() |
244 | } else { |
245 | crate::panic::get_backtrace_style() |
246 | }; |
247 | |
248 | // The current implementation always returns `Some`. |
249 | let location = info.location().unwrap(); |
250 | |
251 | let msg = match info.payload().downcast_ref::<&'static str>() { |
252 | Some(s) => *s, |
253 | None => match info.payload().downcast_ref::<String>() { |
254 | Some(s) => &s[..], |
255 | None => "Box<dyn Any>" , |
256 | }, |
257 | }; |
258 | let thread = thread::try_current(); |
259 | let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>" ); |
260 | |
261 | let write = |err: &mut dyn crate::io::Write| { |
262 | let _ = writeln!(err, "thread ' {name}' panicked at {location}: \n{msg}" ); |
263 | |
264 | static FIRST_PANIC: AtomicBool = AtomicBool::new(true); |
265 | |
266 | match backtrace { |
267 | Some(BacktraceStyle::Short) => { |
268 | drop(backtrace::print(err, crate::backtrace_rs::PrintFmt::Short)) |
269 | } |
270 | Some(BacktraceStyle::Full) => { |
271 | drop(backtrace::print(err, crate::backtrace_rs::PrintFmt::Full)) |
272 | } |
273 | Some(BacktraceStyle::Off) => { |
274 | if FIRST_PANIC.swap(false, Ordering::Relaxed) { |
275 | let _ = writeln!( |
276 | err, |
277 | "note: run with `RUST_BACKTRACE=1` environment variable to display a \ |
278 | backtrace" |
279 | ); |
280 | if cfg!(miri) { |
281 | let _ = writeln!( |
282 | err, |
283 | "note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` \ |
284 | for the environment variable to have an effect" |
285 | ); |
286 | } |
287 | } |
288 | } |
289 | // If backtraces aren't supported or are forced-off, do nothing. |
290 | None => {} |
291 | } |
292 | }; |
293 | |
294 | if let Ok(Some(local)) = try_set_output_capture(None) { |
295 | write(&mut *local.lock().unwrap_or_else(|e| e.into_inner())); |
296 | try_set_output_capture(Some(local)).ok(); |
297 | } else if let Some(mut out) = panic_output() { |
298 | write(&mut out); |
299 | } |
300 | } |
301 | |
302 | #[cfg (not(test))] |
303 | #[doc (hidden)] |
304 | #[cfg (feature = "panic_immediate_abort" )] |
305 | #[unstable (feature = "update_panic_count" , issue = "none" )] |
306 | pub mod panic_count { |
307 | /// A reason for forcing an immediate abort on panic. |
308 | #[derive (Debug)] |
309 | pub enum MustAbort { |
310 | AlwaysAbort, |
311 | PanicInHook, |
312 | } |
313 | |
314 | #[inline ] |
315 | pub fn increase(run_panic_hook: bool) -> Option<MustAbort> { |
316 | None |
317 | } |
318 | |
319 | #[inline ] |
320 | pub fn finished_panic_hook() {} |
321 | |
322 | #[inline ] |
323 | pub fn decrease() {} |
324 | |
325 | #[inline ] |
326 | pub fn set_always_abort() {} |
327 | |
328 | // Disregards ALWAYS_ABORT_FLAG |
329 | #[inline ] |
330 | #[must_use ] |
331 | pub fn get_count() -> usize { |
332 | 0 |
333 | } |
334 | |
335 | #[must_use ] |
336 | #[inline ] |
337 | pub fn count_is_zero() -> bool { |
338 | true |
339 | } |
340 | } |
341 | |
342 | #[cfg (not(test))] |
343 | #[doc (hidden)] |
344 | #[cfg (not(feature = "panic_immediate_abort" ))] |
345 | #[unstable (feature = "update_panic_count" , issue = "none" )] |
346 | pub mod panic_count { |
347 | use crate::cell::Cell; |
348 | use crate::sync::atomic::{AtomicUsize, Ordering}; |
349 | |
350 | const ALWAYS_ABORT_FLAG: usize = 1 << (usize::BITS - 1); |
351 | |
352 | /// A reason for forcing an immediate abort on panic. |
353 | #[derive (Debug)] |
354 | pub enum MustAbort { |
355 | AlwaysAbort, |
356 | PanicInHook, |
357 | } |
358 | |
359 | // Panic count for the current thread and whether a panic hook is currently |
360 | // being executed.. |
361 | thread_local! { |
362 | static LOCAL_PANIC_COUNT: Cell<(usize, bool)> = const { Cell::new((0, false)) } |
363 | } |
364 | |
365 | // Sum of panic counts from all threads. The purpose of this is to have |
366 | // a fast path in `count_is_zero` (which is used by `panicking`). In any particular |
367 | // thread, if that thread currently views `GLOBAL_PANIC_COUNT` as being zero, |
368 | // then `LOCAL_PANIC_COUNT` in that thread is zero. This invariant holds before |
369 | // and after increase and decrease, but not necessarily during their execution. |
370 | // |
371 | // Additionally, the top bit of GLOBAL_PANIC_COUNT (GLOBAL_ALWAYS_ABORT_FLAG) |
372 | // records whether panic::always_abort() has been called. This can only be |
373 | // set, never cleared. |
374 | // panic::always_abort() is usually called to prevent memory allocations done by |
375 | // the panic handling in the child created by `libc::fork`. |
376 | // Memory allocations performed in a child created with `libc::fork` are undefined |
377 | // behavior in most operating systems. |
378 | // Accessing LOCAL_PANIC_COUNT in a child created by `libc::fork` would lead to a memory |
379 | // allocation. Only GLOBAL_PANIC_COUNT can be accessed in this situation. This is |
380 | // sufficient because a child process will always have exactly one thread only. |
381 | // See also #85261 for details. |
382 | // |
383 | // This could be viewed as a struct containing a single bit and an n-1-bit |
384 | // value, but if we wrote it like that it would be more than a single word, |
385 | // and even a newtype around usize would be clumsy because we need atomics. |
386 | // But we use such a tuple for the return type of increase(). |
387 | // |
388 | // Stealing a bit is fine because it just amounts to assuming that each |
389 | // panicking thread consumes at least 2 bytes of address space. |
390 | static GLOBAL_PANIC_COUNT: AtomicUsize = AtomicUsize::new(0); |
391 | |
392 | // Increases the global and local panic count, and returns whether an |
393 | // immediate abort is required. |
394 | // |
395 | // This also updates thread-local state to keep track of whether a panic |
396 | // hook is currently executing. |
397 | pub fn increase(run_panic_hook: bool) -> Option<MustAbort> { |
398 | let global_count = GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed); |
399 | if global_count & ALWAYS_ABORT_FLAG != 0 { |
400 | // Do *not* access thread-local state, we might be after a `fork`. |
401 | return Some(MustAbort::AlwaysAbort); |
402 | } |
403 | |
404 | LOCAL_PANIC_COUNT.with(|c| { |
405 | let (count, in_panic_hook) = c.get(); |
406 | if in_panic_hook { |
407 | return Some(MustAbort::PanicInHook); |
408 | } |
409 | c.set((count + 1, run_panic_hook)); |
410 | None |
411 | }) |
412 | } |
413 | |
414 | pub fn finished_panic_hook() { |
415 | LOCAL_PANIC_COUNT.with(|c| { |
416 | let (count, _) = c.get(); |
417 | c.set((count, false)); |
418 | }); |
419 | } |
420 | |
421 | pub fn decrease() { |
422 | GLOBAL_PANIC_COUNT.fetch_sub(1, Ordering::Relaxed); |
423 | LOCAL_PANIC_COUNT.with(|c| { |
424 | let (count, _) = c.get(); |
425 | c.set((count - 1, false)); |
426 | }); |
427 | } |
428 | |
429 | pub fn set_always_abort() { |
430 | GLOBAL_PANIC_COUNT.fetch_or(ALWAYS_ABORT_FLAG, Ordering::Relaxed); |
431 | } |
432 | |
433 | // Disregards ALWAYS_ABORT_FLAG |
434 | #[must_use ] |
435 | pub fn get_count() -> usize { |
436 | LOCAL_PANIC_COUNT.with(|c| c.get().0) |
437 | } |
438 | |
439 | // Disregards ALWAYS_ABORT_FLAG |
440 | #[must_use ] |
441 | #[inline ] |
442 | pub fn count_is_zero() -> bool { |
443 | if GLOBAL_PANIC_COUNT.load(Ordering::Relaxed) & !ALWAYS_ABORT_FLAG == 0 { |
444 | // Fast path: if `GLOBAL_PANIC_COUNT` is zero, all threads |
445 | // (including the current one) will have `LOCAL_PANIC_COUNT` |
446 | // equal to zero, so TLS access can be avoided. |
447 | // |
448 | // In terms of performance, a relaxed atomic load is similar to a normal |
449 | // aligned memory read (e.g., a mov instruction in x86), but with some |
450 | // compiler optimization restrictions. On the other hand, a TLS access |
451 | // might require calling a non-inlinable function (such as `__tls_get_addr` |
452 | // when using the GD TLS model). |
453 | true |
454 | } else { |
455 | is_zero_slow_path() |
456 | } |
457 | } |
458 | |
459 | // Slow path is in a separate function to reduce the amount of code |
460 | // inlined from `count_is_zero`. |
461 | #[inline (never)] |
462 | #[cold ] |
463 | fn is_zero_slow_path() -> bool { |
464 | LOCAL_PANIC_COUNT.with(|c| c.get().0 == 0) |
465 | } |
466 | } |
467 | |
468 | #[cfg (test)] |
469 | pub use realstd::rt::panic_count; |
470 | |
471 | /// Invoke a closure, capturing the cause of an unwinding panic if one occurs. |
472 | #[cfg (feature = "panic_immediate_abort" )] |
473 | pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> { |
474 | Ok(f()) |
475 | } |
476 | |
477 | /// Invoke a closure, capturing the cause of an unwinding panic if one occurs. |
478 | #[cfg (not(feature = "panic_immediate_abort" ))] |
479 | pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> { |
480 | union Data<F, R> { |
481 | f: ManuallyDrop<F>, |
482 | r: ManuallyDrop<R>, |
483 | p: ManuallyDrop<Box<dyn Any + Send>>, |
484 | } |
485 | |
486 | // We do some sketchy operations with ownership here for the sake of |
487 | // performance. We can only pass pointers down to `do_call` (can't pass |
488 | // objects by value), so we do all the ownership tracking here manually |
489 | // using a union. |
490 | // |
491 | // We go through a transition where: |
492 | // |
493 | // * First, we set the data field `f` to be the argumentless closure that we're going to call. |
494 | // * When we make the function call, the `do_call` function below, we take |
495 | // ownership of the function pointer. At this point the `data` union is |
496 | // entirely uninitialized. |
497 | // * If the closure successfully returns, we write the return value into the |
498 | // data's return slot (field `r`). |
499 | // * If the closure panics (`do_catch` below), we write the panic payload into field `p`. |
500 | // * Finally, when we come back out of the `try` intrinsic we're |
501 | // in one of two states: |
502 | // |
503 | // 1. The closure didn't panic, in which case the return value was |
504 | // filled in. We move it out of `data.r` and return it. |
505 | // 2. The closure panicked, in which case the panic payload was |
506 | // filled in. We move it out of `data.p` and return it. |
507 | // |
508 | // Once we stack all that together we should have the "most efficient' |
509 | // method of calling a catch panic whilst juggling ownership. |
510 | let mut data = Data { f: ManuallyDrop::new(f) }; |
511 | |
512 | let data_ptr = core::ptr::addr_of_mut!(data) as *mut u8; |
513 | // SAFETY: |
514 | // |
515 | // Access to the union's fields: this is `std` and we know that the `r#try` |
516 | // intrinsic fills in the `r` or `p` union field based on its return value. |
517 | // |
518 | // The call to `intrinsics::catch_unwind` is made safe by: |
519 | // - `do_call`, the first argument, can be called with the initial `data_ptr`. |
520 | // - `do_catch`, the second argument, can be called with the `data_ptr` as well. |
521 | // See their safety preconditions for more information |
522 | unsafe { |
523 | return if intrinsics::catch_unwind(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 { |
524 | Ok(ManuallyDrop::into_inner(data.r)) |
525 | } else { |
526 | Err(ManuallyDrop::into_inner(data.p)) |
527 | }; |
528 | } |
529 | |
530 | // We consider unwinding to be rare, so mark this function as cold. However, |
531 | // do not mark it no-inline -- that decision is best to leave to the |
532 | // optimizer (in most cases this function is not inlined even as a normal, |
533 | // non-cold function, though, as of the writing of this comment). |
534 | #[cold ] |
535 | unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send + 'static> { |
536 | // SAFETY: The whole unsafe block hinges on a correct implementation of |
537 | // the panic handler `__rust_panic_cleanup`. As such we can only |
538 | // assume it returns the correct thing for `Box::from_raw` to work |
539 | // without undefined behavior. |
540 | let obj = unsafe { Box::from_raw(__rust_panic_cleanup(payload)) }; |
541 | panic_count::decrease(); |
542 | obj |
543 | } |
544 | |
545 | // SAFETY: |
546 | // data must be non-NUL, correctly aligned, and a pointer to a `Data<F, R>` |
547 | // Its must contains a valid `f` (type: F) value that can be use to fill |
548 | // `data.r`. |
549 | // |
550 | // This function cannot be marked as `unsafe` because `intrinsics::catch_unwind` |
551 | // expects normal function pointers. |
552 | #[inline ] |
553 | fn do_call<F: FnOnce() -> R, R>(data: *mut u8) { |
554 | // SAFETY: this is the responsibility of the caller, see above. |
555 | unsafe { |
556 | let data = data as *mut Data<F, R>; |
557 | let data = &mut (*data); |
558 | let f = ManuallyDrop::take(&mut data.f); |
559 | data.r = ManuallyDrop::new(f()); |
560 | } |
561 | } |
562 | |
563 | // We *do* want this part of the catch to be inlined: this allows the |
564 | // compiler to properly track accesses to the Data union and optimize it |
565 | // away most of the time. |
566 | // |
567 | // SAFETY: |
568 | // data must be non-NUL, correctly aligned, and a pointer to a `Data<F, R>` |
569 | // Since this uses `cleanup` it also hinges on a correct implementation of |
570 | // `__rustc_panic_cleanup`. |
571 | // |
572 | // This function cannot be marked as `unsafe` because `intrinsics::catch_unwind` |
573 | // expects normal function pointers. |
574 | #[inline ] |
575 | #[rustc_nounwind ] // `intrinsic::r#try` requires catch fn to be nounwind |
576 | fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) { |
577 | // SAFETY: this is the responsibility of the caller, see above. |
578 | // |
579 | // When `__rustc_panic_cleaner` is correctly implemented we can rely |
580 | // on `obj` being the correct thing to pass to `data.p` (after wrapping |
581 | // in `ManuallyDrop`). |
582 | unsafe { |
583 | let data = data as *mut Data<F, R>; |
584 | let data = &mut (*data); |
585 | let obj = cleanup(payload); |
586 | data.p = ManuallyDrop::new(obj); |
587 | } |
588 | } |
589 | } |
590 | |
591 | /// Determines whether the current thread is unwinding because of panic. |
592 | #[inline ] |
593 | pub fn panicking() -> bool { |
594 | !panic_count::count_is_zero() |
595 | } |
596 | |
597 | /// Entry point of panics from the core crate (`panic_impl` lang item). |
598 | #[cfg (not(any(test, doctest)))] |
599 | #[panic_handler ] |
600 | pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { |
601 | struct FormatStringPayload<'a> { |
602 | inner: &'a fmt::Arguments<'a>, |
603 | string: Option<String>, |
604 | } |
605 | |
606 | impl<'a> FormatStringPayload<'a> { |
607 | fn new(inner: &'a fmt::Arguments<'a>) -> Self { |
608 | Self { inner, string: None } |
609 | } |
610 | |
611 | fn fill(&mut self) -> &mut String { |
612 | use crate::fmt::Write; |
613 | |
614 | let inner = self.inner; |
615 | // Lazily, the first time this gets called, run the actual string formatting. |
616 | self.string.get_or_insert_with(|| { |
617 | let mut s = String::new(); |
618 | let _err = s.write_fmt(*inner); |
619 | s |
620 | }) |
621 | } |
622 | } |
623 | |
624 | unsafe impl<'a> PanicPayload for FormatStringPayload<'a> { |
625 | fn take_box(&mut self) -> *mut (dyn Any + Send) { |
626 | // We do two allocations here, unfortunately. But (a) they're required with the current |
627 | // scheme, and (b) we don't handle panic + OOM properly anyway (see comment in |
628 | // begin_panic below). |
629 | let contents = mem::take(self.fill()); |
630 | Box::into_raw(Box::new(contents)) |
631 | } |
632 | |
633 | fn get(&mut self) -> &(dyn Any + Send) { |
634 | self.fill() |
635 | } |
636 | } |
637 | |
638 | struct StaticStrPayload(&'static str); |
639 | |
640 | unsafe impl PanicPayload for StaticStrPayload { |
641 | fn take_box(&mut self) -> *mut (dyn Any + Send) { |
642 | Box::into_raw(Box::new(self.0)) |
643 | } |
644 | |
645 | fn get(&mut self) -> &(dyn Any + Send) { |
646 | &self.0 |
647 | } |
648 | } |
649 | |
650 | let loc = info.location().unwrap(); // The current implementation always returns Some |
651 | let msg = info.message().unwrap(); // The current implementation always returns Some |
652 | crate::sys_common::backtrace::__rust_end_short_backtrace(move || { |
653 | // FIXME: can we just pass `info` along rather than taking it apart here, only to have |
654 | // `rust_panic_with_hook` construct a new `PanicInfo`? |
655 | if let Some(msg) = msg.as_str() { |
656 | rust_panic_with_hook( |
657 | &mut StaticStrPayload(msg), |
658 | info.message(), |
659 | loc, |
660 | info.can_unwind(), |
661 | info.force_no_backtrace(), |
662 | ); |
663 | } else { |
664 | rust_panic_with_hook( |
665 | &mut FormatStringPayload::new(msg), |
666 | info.message(), |
667 | loc, |
668 | info.can_unwind(), |
669 | info.force_no_backtrace(), |
670 | ); |
671 | } |
672 | }) |
673 | } |
674 | |
675 | /// This is the entry point of panicking for the non-format-string variants of |
676 | /// panic!() and assert!(). In particular, this is the only entry point that supports |
677 | /// arbitrary payloads, not just format strings. |
678 | #[unstable (feature = "libstd_sys_internals" , reason = "used by the panic! macro" , issue = "none" )] |
679 | #[cfg_attr (not(any(test, doctest)), lang = "begin_panic" )] |
680 | // lang item for CTFE panic support |
681 | // never inline unless panic_immediate_abort to avoid code |
682 | // bloat at the call sites as much as possible |
683 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
684 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
685 | #[track_caller ] |
686 | #[rustc_do_not_const_check ] // hooked by const-eval |
687 | pub const fn begin_panic<M: Any + Send>(msg: M) -> ! { |
688 | if cfg!(feature = "panic_immediate_abort" ) { |
689 | intrinsics::abort() |
690 | } |
691 | |
692 | let loc = Location::caller(); |
693 | return crate::sys_common::backtrace::__rust_end_short_backtrace(move || { |
694 | rust_panic_with_hook( |
695 | &mut Payload::new(msg), |
696 | None, |
697 | loc, |
698 | /* can_unwind */ true, |
699 | /* force_no_backtrace */ false, |
700 | ) |
701 | }); |
702 | |
703 | struct Payload<A> { |
704 | inner: Option<A>, |
705 | } |
706 | |
707 | impl<A: Send + 'static> Payload<A> { |
708 | fn new(inner: A) -> Payload<A> { |
709 | Payload { inner: Some(inner) } |
710 | } |
711 | } |
712 | |
713 | unsafe impl<A: Send + 'static> PanicPayload for Payload<A> { |
714 | fn take_box(&mut self) -> *mut (dyn Any + Send) { |
715 | // Note that this should be the only allocation performed in this code path. Currently |
716 | // this means that panic!() on OOM will invoke this code path, but then again we're not |
717 | // really ready for panic on OOM anyway. If we do start doing this, then we should |
718 | // propagate this allocation to be performed in the parent of this thread instead of the |
719 | // thread that's panicking. |
720 | let data = match self.inner.take() { |
721 | Some(a) => Box::new(a) as Box<dyn Any + Send>, |
722 | None => process::abort(), |
723 | }; |
724 | Box::into_raw(data) |
725 | } |
726 | |
727 | fn get(&mut self) -> &(dyn Any + Send) { |
728 | match self.inner { |
729 | Some(ref a) => a, |
730 | None => process::abort(), |
731 | } |
732 | } |
733 | } |
734 | } |
735 | |
736 | /// Central point for dispatching panics. |
737 | /// |
738 | /// Executes the primary logic for a panic, including checking for recursive |
739 | /// panics, panic hooks, and finally dispatching to the panic runtime to either |
740 | /// abort or unwind. |
741 | fn rust_panic_with_hook( |
742 | payload: &mut dyn PanicPayload, |
743 | message: Option<&fmt::Arguments<'_>>, |
744 | location: &Location<'_>, |
745 | can_unwind: bool, |
746 | force_no_backtrace: bool, |
747 | ) -> ! { |
748 | let must_abort = panic_count::increase(true); |
749 | |
750 | // Check if we need to abort immediately. |
751 | if let Some(must_abort) = must_abort { |
752 | match must_abort { |
753 | panic_count::MustAbort::PanicInHook => { |
754 | // Don't try to format the message in this case, perhaps that is causing the |
755 | // recursive panics. However if the message is just a string, no user-defined |
756 | // code is involved in printing it, so that is risk-free. |
757 | let msg_str = message.and_then(|m| m.as_str()).map(|m| [m]); |
758 | let message = msg_str.as_ref().map(|m| fmt::Arguments::new_const(m)); |
759 | let panicinfo = PanicInfo::internal_constructor( |
760 | message.as_ref(), |
761 | location, |
762 | can_unwind, |
763 | force_no_backtrace, |
764 | ); |
765 | rtprintpanic!(" {panicinfo}\nthread panicked while processing panic. aborting. \n" ); |
766 | } |
767 | panic_count::MustAbort::AlwaysAbort => { |
768 | // Unfortunately, this does not print a backtrace, because creating |
769 | // a `Backtrace` will allocate, which we must avoid here. |
770 | let panicinfo = PanicInfo::internal_constructor( |
771 | message, |
772 | location, |
773 | can_unwind, |
774 | force_no_backtrace, |
775 | ); |
776 | rtprintpanic!(" {panicinfo}\npanicked after panic::always_abort(), aborting. \n" ); |
777 | } |
778 | } |
779 | crate::sys::abort_internal(); |
780 | } |
781 | |
782 | let mut info = |
783 | PanicInfo::internal_constructor(message, location, can_unwind, force_no_backtrace); |
784 | let hook = HOOK.read().unwrap_or_else(PoisonError::into_inner); |
785 | match *hook { |
786 | // Some platforms (like wasm) know that printing to stderr won't ever actually |
787 | // print anything, and if that's the case we can skip the default |
788 | // hook. Since string formatting happens lazily when calling `payload` |
789 | // methods, this means we avoid formatting the string at all! |
790 | // (The panic runtime might still call `payload.take_box()` though and trigger |
791 | // formatting.) |
792 | Hook::Default if panic_output().is_none() => {} |
793 | Hook::Default => { |
794 | info.set_payload(payload.get()); |
795 | default_hook(&info); |
796 | } |
797 | Hook::Custom(ref hook) => { |
798 | info.set_payload(payload.get()); |
799 | hook(&info); |
800 | } |
801 | }; |
802 | drop(hook); |
803 | |
804 | // Indicate that we have finished executing the panic hook. After this point |
805 | // it is fine if there is a panic while executing destructors, as long as it |
806 | // it contained within a `catch_unwind`. |
807 | panic_count::finished_panic_hook(); |
808 | |
809 | if !can_unwind { |
810 | // If a thread panics while running destructors or tries to unwind |
811 | // through a nounwind function (e.g. extern "C") then we cannot continue |
812 | // unwinding and have to abort immediately. |
813 | rtprintpanic!("thread caused non-unwinding panic. aborting. \n" ); |
814 | crate::sys::abort_internal(); |
815 | } |
816 | |
817 | rust_panic(payload) |
818 | } |
819 | |
820 | /// This is the entry point for `resume_unwind`. |
821 | /// It just forwards the payload to the panic runtime. |
822 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
823 | pub fn rust_panic_without_hook(payload: Box<dyn Any + Send>) -> ! { |
824 | panic_count::increase(run_panic_hook:false); |
825 | |
826 | struct RewrapBox(Box<dyn Any + Send>); |
827 | |
828 | unsafe impl PanicPayload for RewrapBox { |
829 | fn take_box(&mut self) -> *mut (dyn Any + Send) { |
830 | Box::into_raw(mem::replace(&mut self.0, src:Box::new(()))) |
831 | } |
832 | |
833 | fn get(&mut self) -> &(dyn Any + Send) { |
834 | &*self.0 |
835 | } |
836 | } |
837 | |
838 | rust_panic(&mut RewrapBox(payload)) |
839 | } |
840 | |
841 | /// An unmangled function (through `rustc_std_internal_symbol`) on which to slap |
842 | /// yer breakpoints. |
843 | #[inline (never)] |
844 | #[cfg_attr (not(test), rustc_std_internal_symbol)] |
845 | #[cfg (not(feature = "panic_immediate_abort" ))] |
846 | fn rust_panic(msg: &mut dyn PanicPayload) -> ! { |
847 | let code: u32 = unsafe { __rust_start_panic(payload:msg) }; |
848 | rtabort!("failed to initiate panic, error {code}" ) |
849 | } |
850 | |
851 | #[cfg_attr (not(test), rustc_std_internal_symbol)] |
852 | #[cfg (feature = "panic_immediate_abort" )] |
853 | fn rust_panic(_: &mut dyn PanicPayload) -> ! { |
854 | unsafe { |
855 | crate::intrinsics::abort(); |
856 | } |
857 | } |
858 | |