Home Explore Blog CI



rustc

2nd chunk of `src/panic-implementation.md`
03a2382b7d57ddfbcdc2d3d68d8c5e40da0f2238c46a89310000000100000d4e
The special `panic_handler` attribute is resolved via `compiler/rustc_middle/src/middle/lang_items`.
The `extract` function converts the `panic_handler` attribute to a `panic_impl` lang item.

Now, we have a matching `panic_handler` lang item in the `std`. This function goes
through the same process as the `extern { fn panic_impl }` definition in `core`, ending
up with a symbol name of `rust_begin_unwind`. At link time, the symbol reference in `core`
will be resolved to the definition of `std` (the function called `begin_panic_handler` in the
Rust source).

Thus, control flow will pass from core to std at runtime. This allows panics from `core`
to go through the same infrastructure that other panics use (panic hooks, unwinding, etc)

### std implementation of panic!

This is where the actual panic-related logic begins. In `library/std/src/panicking.rs`,
control passes to `rust_panic_with_hook`. This method is responsible
for invoking the global panic hook, and checking for double panics. Finally,
we call `__rust_start_panic`, which is provided by the panic runtime.

The call to `__rust_start_panic` is very weird - it is passed a `*mut &mut dyn PanicPayload`,
converted to an `usize`. Let's break this type down:

1. `PanicPayload` is an internal trait. It is implemented for `PanicPayload`
(a wrapper around the user-supplied payload type), and has a method
`fn take_box(&mut self) -> *mut (dyn Any + Send)`.
This method takes the user-provided payload (`T: Any + Send`),
boxes it, and converts the box to a raw pointer.

2. When we call `__rust_start_panic`, we have an `&mut dyn PanicPayload`.
However, this is a fat pointer (twice the size of a `usize`).
To pass this to the panic runtime across an FFI boundary, we take a mutable
reference *to this mutable reference* (`&mut &mut dyn PanicPayload`), and convert it to a raw
pointer (`*mut &mut dyn PanicPayload`). The outer raw pointer is a thin pointer, since it points to
a `Sized` type (a mutable reference). Therefore, we can convert this thin pointer into a `usize`,
which is suitable for passing across an FFI boundary.

Finally, we call `__rust_start_panic` with this `usize`. We have now entered the panic runtime.

## Step 2: The panic runtime

Rust provides two panic runtimes: `panic_abort` and `panic_unwind`. The user chooses
between them at build time via their `Cargo.toml`

`panic_abort` is extremely simple: its implementation of `__rust_start_panic` just aborts,
as you would expect.

`panic_unwind` is the more interesting case.

In its implementation of `__rust_start_panic`, we take the `usize`, convert
it back to a `*mut &mut dyn PanicPayload`, dereference it, and call `take_box`
on the `&mut dyn PanicPayload`. At this point, we have a raw pointer to the payload
itself (a `*mut (dyn Send + Any)`): that is, a raw pointer to the actual value
provided by the user who called `panic!`.

At this point, the platform-independent code ends. We now call into
platform-specific unwinding logic (e.g `unwind`). This code is
responsible for unwinding the stack, running any 'landing pads' associated
with each frame (currently, running destructors), and transferring control
to the `catch_unwind` frame.

Note that all panics either abort the process or get caught by some call to `catch_unwind`.
In particular, in std's [runtime service],
the call to the user-provided `main` function is wrapped in `catch_unwind`.



Title: Panicking in Rust: The Panic Runtime and `__rust_start_panic`
Summary
The `panic_handler` attribute is processed, leading to the `rust_begin_unwind` symbol, allowing control flow to pass from `core` to `std`. In `std`, `rust_panic_with_hook` invokes the panic hook and checks for double panics before calling `__rust_start_panic` with a raw pointer to the panic payload. The panic runtime is either `panic_abort`, which aborts the process, or `panic_unwind`, which unwinds the stack, runs landing pads (destructors), and transfers control to the `catch_unwind` frame. All panics either abort or are caught by `catch_unwind`, such as the wrapper around the user-provided `main` function.