Skip to content

Commit

Permalink
panic runtime and C-unwind documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
BatmanAoD committed Oct 2, 2024
1 parent 9c21bee commit aa9a282
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 20 deletions.
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
15 changes: 13 additions & 2 deletions src/behavior-considered-undefined.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ undefined behavior, it is *unsound*.
* Invoking undefined behavior via compiler intrinsics.
* Executing code compiled with platform features that the current platform
does not support (see [`target_feature`]), *except* if the platform explicitly documents this to be safe.
* 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).
* Producing an [invalid value][invalid-values]. "Producing" a
value happens any time a value is assigned to or read from a place, passed to
a function/primitive operation or returned from a function/primitive
Expand All @@ -61,6 +63,14 @@ undefined behavior, it is *unsound*.
some allocated object as a non-pointer type (such as integers).
'Reinterpreting' refers to loading the pointer value at integer type without a
cast, e.g. by doing raw pointer casts or using a union.
* Violating assumptions of the Rust runtime. This is only possible using
mechanisms outside Rust. 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
Expand Down Expand Up @@ -160,11 +170,11 @@ a restricted set of valid values. In other words, the only cases in which
reading uninitialized memory is permitted are inside `union`s and in "padding"
(the gaps between the fields of a type).


[`bool`]: types/boolean.md
[`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]: abi.md
[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 @@ -178,4 +188,5 @@ 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
12 changes: 12 additions & 0 deletions src/crates-and-source-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,17 @@ 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 -->

### Uncaught foreign unwinding

When a "foreign" unwind (e.g. an exception thrown from C++ code, or a `panic!`
in Rust code compiled or linked with a different runtime) is not caught before
reaching 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].

### The `no_main` attribute

The *`no_main` [attribute]* may be applied at the crate level to disable
Expand Down Expand Up @@ -142,6 +153,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 @@ -268,7 +268,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 @@ -407,6 +408,8 @@ let x = (&temp()).use_temp(); // ERROR

## Not running destructors

### `forget`

r[destructors.forget]

[`std::mem::forget`] can be used to prevent the destructor of a variable from being run,
Expand All @@ -416,6 +419,20 @@ 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.
### 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-mode] 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 @@ -425,11 +442,15 @@ 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
[panic-mode]: panic.md#panic-runtimes
[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 @@ -53,7 +53,7 @@ Just as with methods, Rust will also insert dereference operations on `a` repeat

Indices are zero-based for arrays and slices.
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 @@ -84,5 +84,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
32 changes: 24 additions & 8 deletions src/items/external-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ standard C ABI on the specific platform. Other ABIs may be specified using an
unsafe extern "stdcall" { }
```

There are three ABI strings which are cross-platform, and which all compilers
There are five ABI strings which are cross-platform, and which all compilers
are guaranteed to support:

* `unsafe extern "Rust"` -- The default ABI when you write a normal `fn foo()` in any
Expand All @@ -80,6 +80,9 @@ are guaranteed to support:
* `unsafe extern "system"` -- Usually the same as `extern "C"`, except on Win32, in
which case it's `"stdcall"`, or what you should use to link to the Windows
API itself
* `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).

There are also some platform-specific ABI strings:

Expand All @@ -96,6 +99,18 @@ There are also some platform-specific ABI strings:
`__thiscall` and GCC and clang's `__attribute__((thiscall))`
* `unsafe extern "efiapi"` -- The ABI used for [UEFI] functions.

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

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

## Variadic functions

Functions within external blocks may be variadic by specifying `...` as the
Expand Down Expand Up @@ -316,10 +331,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 @@ -329,11 +343,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
56 changes: 50 additions & 6 deletions src/items/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,56 @@ extern "C" fn new_i32() -> i32 { 0 }
let fptr: extern "C" fn() -> i32 = new_i32;
```

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

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 mode][panic-modes],
determines the behavior when unwinding out of a function.

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 note below) | [undefined behavior] |
| `panic=abort` | unwinding | `panic` aborts without unwinding | abort |
| `panic=abort` | non-unwinding | `panic` aborts without unwinding | [undefined behavior] |

> Note: 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.
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-modes]: ../panic.md#panic-runtimes
[panic-ffi]: ../panic.md#unwinding-across-ffi-boundaries
[panicking]: ../panic.md
[undefined behavior]: ../behavior-considered-undefined.md

## Const functions

Expand Down
18 changes: 18 additions & 0 deletions src/linkage.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,24 @@ a statically linked binary on MSVC you would execute:
RUSTFLAGS='-C target-feature=+crt-static' cargo build --target x86_64-pc-windows-msvc
```

## Prohibited linkage scenarios

No crate may be linked with [the `panic=abort` runtime][panic-runtime] if it has
both of the following characteristics:

* It contains a call to an `-unwind` foreign function or function pointer
* It was compiled with `panic=unwind`

`rustc` enforces this restriction at link-time. To guarantee that
a library will be linkable regardless of the panic mode used at
link-time, the [`ffi_unwind_calls` lint] may be used. The lint flags any
calls to `-unwind` foreign functions or function pointers.

Note: Cargo will automatically unify all crates to use the same `panic`
runtime, so this prohibition does not apply to projects compiled with Cargo.

[`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
[panic-runtime]: panic.md#panic-runtimes
[procedural macros]: procedural-macros.md
Loading

0 comments on commit aa9a282

Please sign in to comment.