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