Skip to content

panic runtime and C-unwind documentation #1226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 42 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
5f63e46
panic runtime and C-unwind documentation
BatmanAoD Oct 2, 2024
c8fc0e8
Rust version number for destructors guarantee
BatmanAoD Nov 24, 2024
edd5b18
Fix identifeirs in functions.md and move the guarantee about destruct…
chorman0773 Nov 25, 2024
39de76c
Add identifier syntax to panic.md
chorman0773 Nov 25, 2024
6413901
Move note in linkage.md
chorman0773 Nov 25, 2024
70f8292
Fix whitespace issues in previous commit
chorman0773 Nov 25, 2024
d08e406
Fix omitted whitespace issue (thanks VSC)
chorman0773 Nov 25, 2024
a20802e
Update src/items/functions.md
chorman0773 Nov 26, 2024
2525ab4
fix wording regarding UB due to unwinding linkage
RalfJung Nov 29, 2024
5bff1e3
improve terminology
RalfJung Dec 21, 2024
7453efd
Do not document a rustc-lint as a language rule
ehuss Jan 10, 2025
c3cc7fa
Use built-in std linking
ehuss Jan 10, 2025
860691e
Fix misspelled word
ehuss Jan 10, 2025
543bc27
Remove vectorcall-unwind, it is unstable
ehuss Jan 14, 2025
4bb0248
Sort unwind list
ehuss Jan 14, 2025
386b2f3
Switch link for ABI to the list of ABIs
ehuss Jan 14, 2025
c7a7961
Tweak wording of "beyond rust main function"
ehuss Jan 14, 2025
20af0b1
Make sure a terminal rule is used for this definition
ehuss Jan 14, 2025
da53531
Remove historical note
ehuss Jan 14, 2025
8fd8208
Clarify that this is unspecified
ehuss Jan 14, 2025
0f3c015
Fix rule name to follow style
ehuss Jan 14, 2025
63677c1
Add missing rule name
ehuss Jan 14, 2025
caa17fc
Reword the title of this section
ehuss Jan 14, 2025
0b37538
Unwrap the panic chapter
ehuss Jan 14, 2025
eafe95d
Update rules to be with headers
ehuss Jan 14, 2025
5f21a1a
Apply suggestions from RalfJung
ehuss Jan 14, 2025
79cc53f
Move panic runtimes towards the top of the chapter
ehuss Jan 15, 2025
c38044d
Add the concept of panic strategy
ehuss Jan 15, 2025
20bd38f
Fix typo
ehuss Jan 15, 2025
f309b2f
Drop sentence about UB of how to violate the Rust runtime
ehuss Jan 15, 2025
2617cd5
Add "panic" to "unwind runtime"
ehuss Jan 16, 2025
d49c90c
Clarify optimization for abort strategy
ehuss Jan 16, 2025
6eaa1fa
Add another link for panic strategy
ehuss Jan 16, 2025
a740002
Normatively define the abort and unwind runtimes
ehuss Jan 16, 2025
f7cb25e
Rework link.unwinding
ehuss Jan 16, 2025
34e0b68
Apply suggestions from RalfJung
ehuss Jan 17, 2025
8f4dc2b
Clarify function pointer variadics also support `-unwind`
ehuss Jan 21, 2025
6e76b5c
Fix minor typo
ehuss Jan 30, 2025
7049886
Simplify wording of standard ABI strings
ehuss Feb 10, 2025
003a8a4
Remove "panic runtime" and emphasize panic_handler more
ehuss Feb 10, 2025
cdd00de
no unnecessary line-breaks in new text
BatmanAoD Feb 15, 2025
ffc056c
fix link
BatmanAoD Feb 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@
- [Memory allocation and lifetime](memory-allocation-and-lifetime.md)
- [Variables](variables.md)

- [Panic](panic.md)

- [Linkage](linkage.md)

- [Inline assembly](inline-assembly.md)
Expand Down
2 changes: 1 addition & 1 deletion src/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ The following is an index of all built-in attributes.
[`no_mangle`]: abi.md#the-no_mangle-attribute
[`no_std`]: names/preludes.md#the-no_std-attribute
[`non_exhaustive`]: attributes/type_system.md#the-non_exhaustive-attribute
[`panic_handler`]: runtime.md#the-panic_handler-attribute
[`panic_handler`]: panic.md#the-panic_handler-attribute
[`path`]: items/modules.md#the-path-attribute
[`proc_macro_attribute`]: procedural-macros.md#attribute-macros
[`proc_macro_derive`]: procedural-macros.md#derive-macros
Expand Down
15 changes: 14 additions & 1 deletion src/behavior-considered-undefined.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ r[undefined.target-feature]
does not support (see [`target_feature`]), *except* if the platform explicitly documents this to be safe.

r[undefined.call]
* Calling a function with the wrong call ABI or unwinding from a function with the wrong unwind ABI.
* Calling a function with the wrong [call ABI][abi], or unwinding past a stack
frame that does not allow unwinding (e.g. by calling a `"C-unwind"` function
imported or transmuted as a `"C"` function or function pointer).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there something this can link to that explicitly says what frames don't allow unwinding?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I recall correctly, the intent here was to leave open the possibility of other future mechanisms prohibiting unwinding. It may also be that there's something else I'm not thinking of at the moment that can make unwinding past a frame illegal or undefined. But I believe that currently this example, i.e. a call that uses a non-unwinding ABI, is the only form of frame that doesn't allow unwinding. It would, at least, be appropriate to link to the "c-unwind" page from here, I think.


r[undefined.invalid]
* Producing an [invalid value][invalid-values]. "Producing" a
Expand All @@ -96,6 +98,15 @@ r[undefined.const-transmute-ptr2int]
'Reinterpreting' refers to loading the pointer value at integer type without a
cast, e.g. by doing raw pointer casts or using a union.

r[undefined.runtime]
* Violating assumptions of the Rust runtime. Most assumptions of the Rust
runtime are currently not explicitly documented.
* For assumptions specifically related to unwinding, see the [panic
documentation][unwinding-ffi].
* The runtime assumes that a Rust stack frame is not deallocated without
executing destructors for local variables owned by the stack frame. This assumption
can be violated by C functions like `longjmp`.

> **Note**: Undefined behavior affects the entire program. For example, calling
> a function in C that exhibits undefined behavior of C means your entire
> program contains undefined behaviour that can also affect the Rust code. And
Expand Down Expand Up @@ -245,6 +256,7 @@ reading uninitialized memory is permitted are inside `union`s and in "padding"
[`const`]: items/constant-items.md
[noalias]: http://llvm.org/docs/LangRef.html#noalias
[pointer aliasing rules]: http://llvm.org/docs/LangRef.html#pointer-aliasing-rules
[abi]: items/external-blocks.md#abi
[undef]: http://llvm.org/docs/LangRef.html#undefined-values
[`target_feature`]: attributes/codegen.md#the-target_feature-attribute
[`UnsafeCell<U>`]: std::cell::UnsafeCell
Expand All @@ -258,5 +270,6 @@ reading uninitialized memory is permitted are inside `union`s and in "padding"
[project-field]: expressions/field-expr.md
[project-tuple]: expressions/tuple-expr.md#tuple-indexing-expressions
[project-slice]: expressions/array-expr.md#array-and-slice-indexing-expressions
[unwinding-ffi]: panic.md#unwinding-across-ffi-boundaries
[const-promoted]: destructors.md#constant-promotion
[lifetime-extended]: destructors.md#temporary-lifetime-extension
4 changes: 3 additions & 1 deletion src/conditional-compilation.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,14 +304,16 @@ r[cfg.panic]
### `panic`

r[cfg.panic.general]
Key-value option set depending on the panic strategy. Note that more values may be added in the future.
Key-value option set depending on the [panic strategy]. Note that more values may be added in the future.

r[cfg.panic.values]
Example values:

* `"abort"`
* `"unwind"`

[panic strategy]: panic.md#panic-strategy

## Forms of conditional compilation

r[cfg.attr]
Expand Down
14 changes: 13 additions & 1 deletion src/crates-and-source-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,21 @@ use foo::bar as main;
<!-- If the previous section needs updating (from "must take no arguments"
onwards, also update it in the testing.md file -->

r[crate.uncaught-foreign-unwinding]
### Uncaught foreign unwinding

When a "foreign" unwind (e.g. an exception thrown from C++ code, or a `panic!`
in Rust code using a different panic handler) propagates beyond
the `main` function, the process will be safely terminated. This may
take the form of an abort, in which case it is not guaranteed that any `Drop`
calls will be executed, and the error output may be less informative than if the
runtime had been terminated by a "native" Rust `panic`.

For more information, see the [panic documentation][panic-docs].

r[crate.no_main]
### The `no_main` attribute


The *`no_main` [attribute]* may be applied at the crate level to disable
emitting the `main` symbol for an executable binary. This is useful when some
other object being linked to defines `main`.
Expand Down Expand Up @@ -166,6 +177,7 @@ or `_` (U+005F) characters.
[function]: items/functions.md
[module]: items/modules.md
[module path]: paths.md
[panic-docs]: panic.md#unwinding-across-ffi-boundaries
[shebang]: input-format.md#shebang-removal
[trait or lifetime bounds]: trait-bounds.md
[where clauses]: items/generics.md#where-clauses
Expand Down
23 changes: 22 additions & 1 deletion src/destructors.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,8 @@ Temporaries are also created to hold the result of operands to an expression
while the other operands are evaluated. The temporaries are associated to the
scope of the expression with that operand. Since the temporaries are moved from
once the expression is evaluated, dropping them has no effect unless one of the
operands to an expression breaks out of the expression, returns, or panics.
operands to an expression breaks out of the expression, returns, or
[panics][panic].

```rust
# struct PrintOnDrop(&'static str);
Expand Down Expand Up @@ -425,6 +426,8 @@ let x = (&temp()).use_temp(); // ERROR
r[destructors.forget]
## Not running destructors

r[destructors.manually-suppressing]
### Manually suppressing destructors

[`std::mem::forget`] can be used to prevent the destructor of a variable from being run,
and [`std::mem::ManuallyDrop`] provides a wrapper to prevent a
Expand All @@ -433,6 +436,21 @@ variable or field from being dropped automatically.
> Note: Preventing a destructor from being run via [`std::mem::forget`] or other means is safe even if it has a type that isn't `'static`.
> Besides the places where destructors are guaranteed to run as defined by this document, types may *not* safely rely on a destructor being run for soundness.

r[destructors.process-termination]
### Process termination without unwinding

There are some ways to terminate the process without [unwinding], in which case
destructors will not be run.

The standard library provides [`std::process::exit`] and
[`std::process::abort`] to do this explicitly. Additionally, if the
[panic handler][panic.panic_handler.std] is set to `abort`, panicking will always terminate the process
without destructors being run.

There is one additional case to be aware of: when a panic reaches a
[non-unwinding ABI boundary], either no destructors will run, or all
destructors up until the ABI boundary will run.

[Assignment]: expressions/operator-expr.md#assignment-expressions
[binding modes]: patterns.md#binding-modes
[closure]: types/closure.md
Expand All @@ -442,11 +460,14 @@ variable or field from being dropped automatically.
[initialized]: glossary.md#initialized
[interior mutability]: interior-mutability.md
[lazy boolean expression]: expressions/operator-expr.md#lazy-boolean-operators
[non-unwinding ABI boundary]: items/functions.md#unwinding
[panic]: panic.md
[place context]: expressions.md#place-expressions-and-value-expressions
[promoted]: destructors.md#constant-promotion
[scrutinee]: glossary.md#scrutinee
[statement]: statements.md
[temporary]: expressions.md#temporaries
[unwinding]: panic.md#unwinding
[variable]: variables.md

[array]: types/array.md
Expand Down
3 changes: 2 additions & 1 deletion src/expressions/array-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Indices are zero-based for arrays and slices.

r[expr.array.index.const]
Array access is a [constant expression], so bounds can be checked at compile-time with a constant index value.
Otherwise a check will be performed at run-time that will put the thread in a _panicked state_ if it fails.
Otherwise a check will be performed at run-time that will put the thread in a [_panicked state_][panic] if it fails.

```rust,should_panic
// lint is deny by default.
Expand Down Expand Up @@ -115,5 +115,6 @@ The array index expression can be implemented for types other than arrays and sl
[constant item]: ../items/constant-items.md
[literal]: ../tokens.md#literals
[memory location]: ../expressions.md#place-expressions-and-value-expressions
[panic]: ../panic.md
[path]: path-expr.md
[slice]: ../types/slice.md
35 changes: 26 additions & 9 deletions src/items/external-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@ unsafe extern "stdcall" { }
```

r[items.extern.abi.standard]
There are three ABI strings which are cross-platform, and which all compilers
are guaranteed to support:
The following ABI strings are supported on all platforms:

r[items.extern.abi.rust]
* `unsafe extern "Rust"` -- The default ABI when you write a normal `fn foo()` in any
Expand All @@ -122,6 +121,11 @@ r[items.extern.abi.system]
which case it's `"stdcall"`, or what you should use to link to the Windows
API itself

r[items.extern.abi.unwind]
* `extern "C-unwind"` and `extern "system-unwind"` -- identical to `"C"` and
`"system"`, respectively, but with [different behavior][unwind-behavior] when
the callee unwinds (by panicking or throwing a C++ style exception).

r[items.extern.abi.platform]
There are also some platform-specific ABI strings:

Expand Down Expand Up @@ -151,6 +155,18 @@ r[items.extern.abi.thiscall]
r[items.extern.abi.efiapi]
* `unsafe extern "efiapi"` -- The ABI used for [UEFI] functions.

r[items.extern.abi.platform-unwind-variants]
Like `"C"` and `"system"`, most platform-specific ABI strings also have a
[corresponding `-unwind` variant][unwind-behavior]; specifically, these are:

* `"aapcs-unwind"`
* `"cdecl-unwind"`
* `"fastcall-unwind"`
* `"stdcall-unwind"`
* `"sysv64-unwind"`
* `"thiscall-unwind"`
* `"win64-unwind"`

r[items.extern.variadic]
## Variadic functions

Expand Down Expand Up @@ -428,10 +444,9 @@ Attributes on extern function parameters follow the same rules and
restrictions as [regular function parameters].

[IDENTIFIER]: ../identifiers.md
[PE Format]: https://learn.microsoft.com/windows/win32/debug/pe-format#import-name-type
[UEFI]: https://uefi.org/specifications
[WebAssembly module]: https://webassembly.github.io/spec/core/syntax/modules.html
[functions]: functions.md
[statics]: static-items.md
[_Abi_]: functions.md
[_Function_]: functions.md
[_InnerAttribute_]: ../attributes.md
Expand All @@ -441,11 +456,13 @@ restrictions as [regular function parameters].
[_OuterAttribute_]: ../attributes.md
[_StaticItem_]: static-items.md
[_Visibility_]: ../visibility-and-privacy.md
[attributes]: ../attributes.md
[regular function parameters]: functions.md#attributes-on-function-parameters
[`bundle` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-bundle
[`whole-archive` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-whole-archive
[`verbatim` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-verbatim
[`dylib` versus `raw-dylib`]: #dylib-versus-raw-dylib
[PE Format]: https://learn.microsoft.com/windows/win32/debug/pe-format#import-name-type
[`verbatim` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-verbatim
[`whole-archive` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-whole-archive
[attributes]: ../attributes.md
[functions]: functions.md
[regular function parameters]: functions.md#attributes-on-function-parameters
[statics]: static-items.md
[unwind-behavior]: functions.md#unwinding
[value namespace]: ../names/namespaces.md
57 changes: 51 additions & 6 deletions src/items/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,12 +254,57 @@ let fptr: extern "C" fn() -> i32 = new_i32;
```

r[items.fn.extern.unwind]
Functions with an ABI that differs from `"Rust"` do not support unwinding in the
exact same way that Rust does. Therefore, unwinding past the end of functions
with such ABIs causes the process to abort.

> **Note**: The LLVM backend of the `rustc` implementation
aborts the process by executing an illegal instruction.
### Unwinding

r[items.fn.extern.unwind.intro]
Most ABI strings come in two variants, one with an `-unwind` suffix and one without.
The `Rust` ABI always permits unwinding, so there is no `Rust-unwind` ABI. The
choice of ABI, together with the runtime [panic handler], determines
the behavior when unwinding out of a function.

r[items.fn.extern.unwind.behavior]
The table below indicates the behavior of an unwinding operation reaching each
type of ABI boundary (function declaration or definition using the
corresponding ABI string). Note that the Rust runtime is not affected by, and
cannot have an effect on, any unwinding that occurs entirely within another
language's runtime, that is, unwinds that are thrown and caught without
reaching a Rust ABI boundary.

The `panic`-unwind column refers to [panicking] via the `panic!` macro and
similar standard library mechanisms, as well as to any other Rust operations
that cause a panic, such as out-of-bounds array indexing or integer overflow.

The "unwinding" ABI category refers to `"Rust"` (the implicit ABI of Rust
functions not marked `extern`), `"C-unwind"`, and any other ABI with `-unwind`
in its name. The "non-unwinding" ABI category refers to all other ABI strings,
including `"C"` and `"stdcall"`.

Native unwinding is defined per-target. On targets that support throwing and
catching C++ exceptions, it refers to the mechanism used to implement this
feature. Some platforms implement a form of unwinding referred to as ["forced
unwinding"][forced-unwinding]; `longjmp` on Windows and `pthread_exit` in
`glibc` are implemented this way. Forced unwinding is explicitly excluded
from the "Native unwind" column in the table.

| panic runtime | ABI | `panic`-unwind | Native unwind (unforced) |
| -------------- | ------------ | ------------------------------------- | ----------------------- |
| `panic=unwind` | unwinding | unwind | unwind |
| `panic=unwind` | non-unwinding | abort (see notes below) | [undefined behavior] |
| `panic=abort` | unwinding | `panic` aborts without unwinding | abort |
| `panic=abort` | non-unwinding | `panic` aborts without unwinding | [undefined behavior] |

r[items.fn.extern.abort]
With `panic=unwind`, when a `panic` is turned into an abort by a non-unwinding ABI boundary, either no destructors (`Drop` calls) will run, or all destructors
up until the ABI boundary will run. It is unspecified which of those two behaviors will happen.

For other considerations and limitations regarding unwinding across FFI
boundaries, see the [relevant section in the Panic documentation][panic-ffi].

[forced-unwinding]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html#forced-unwinding
[panic handler]: ../panic.md#the-panic_handler-attribute
[panic-ffi]: ../panic.md#unwinding-across-ffi-boundaries
[panicking]: ../panic.md
[undefined behavior]: ../behavior-considered-undefined.md

r[items.fn.const]
## Const functions
Expand Down
45 changes: 45 additions & 0 deletions src/linkage.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ RUSTFLAGS='-C target-feature=+crt-static' cargo build --target x86_64-pc-windows

## Mixed Rust and foreign codebases

r[link.foreign-code]

r[link.foreign-code.foreign-linkers]
If you are mixing Rust with foreign code (e.g. C, C++) and wish to make a single
binary containing both types of code, you have two approaches for the final
binary link:
Expand All @@ -268,6 +271,48 @@ binary link:

Passing `rlib`s directly into your foreign linker is currently unsupported.

> [!NOTE]
> Rust code compiled or linked with a different instance of the Rust runtime counts as a
> "foreign code" for the purpose of this section.

r[link.unwinding]
### Prohibited linkage and unwinding

r[link.unwinding.intro]
Panic unwinding can only be used if the binary is built consistently according to the following rules.

r[link.unwinding.potential]
A Rust artifact is called *potentially unwinding* if any of the following conditions is met:
- The artifact uses the [`unwind` panic handler][panic.panic_handler].
- The artifact contains a crate built with the `unwind` [panic strategy] that makes a call
to a function using a `-unwind` ABI.
- The artifact makes a `"Rust"` ABI call to code running in another Rust
artifact that has a separate copy of the Rust runtime, and that other artifact is
potentially unwinding.

> [!NOTE]
> This definition captures whether a `"Rust"` ABI call inside a Rust artifact can ever
> unwind.

r[link.unwinding.prohibited]
If a Rust artifact is potentially unwinding, then all its crates must be built with the `unwind` [panic strategy].
Otherwise, unwinding can cause undefined behavior.

> [!NOTE]
> If you are using `rustc` to link, these rules are enforced automatically.
> If you are *not* using `rustc` to link, you must take care to ensure that unwinding is handled consistently across the entire binary. Linking without `rustc` includes using `dlopen` or similar facilities where linking is done by the system runtime without `rustc` being involved.
>
> This can only happen when mixing code with different [`-C panic`] flags, so most users do not have to be concerned about this.

> [!NOTE]
> To guarantee that a library will be sound (and linkable with `rustc`)
> regardless of the panic runtime used at link-time, the [`ffi_unwind_calls` lint]
> may be used. The lint flags any calls to `-unwind` foreign functions or
> function pointers.

[`cfg` attribute `target_feature` option]: conditional-compilation.md#target_feature
[`ffi_unwind_calls` lint]: ../rustc/lints/listing/allowed-by-default.html#ffi-unwind-calls
[configuration option]: conditional-compilation.md
[procedural macros]: procedural-macros.md
[panic strategy]: panic.md#panic-strategy
[`-C panic`]: ../rustc/codegen-options/index.html#panic
Loading
Loading