Skip to content

Commit 45da62b

Browse files
committed
panic runtime and C-unwind documentation
1 parent 9c21bee commit 45da62b

File tree

10 files changed

+249
-20
lines changed

10 files changed

+249
-20
lines changed

src/SUMMARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@
114114
- [Memory allocation and lifetime](memory-allocation-and-lifetime.md)
115115
- [Variables](variables.md)
116116

117+
- [Panic](panic.md)
118+
117119
- [Linkage](linkage.md)
118120

119121
- [Inline assembly](inline-assembly.md)

src/behavior-considered-undefined.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ undefined behavior, it is *unsound*.
4949
* Invoking undefined behavior via compiler intrinsics.
5050
* Executing code compiled with platform features that the current platform
5151
does not support (see [`target_feature`]), *except* if the platform explicitly documents this to be safe.
52-
* Calling a function with the wrong call ABI or unwinding from a function with the wrong unwind ABI.
52+
* Calling a function with the wrong [call ABI][abi], or unwinding past a stack
53+
frame that does not allow unwinding (e.g. by calling a `"C-unwind"` function
54+
imported or transmuted as a `"C"` function or function pointer).
5355
* Producing an [invalid value][invalid-values]. "Producing" a
5456
value happens any time a value is assigned to or read from a place, passed to
5557
a function/primitive operation or returned from a function/primitive
@@ -61,6 +63,14 @@ undefined behavior, it is *unsound*.
6163
some allocated object as a non-pointer type (such as integers).
6264
'Reinterpreting' refers to loading the pointer value at integer type without a
6365
cast, e.g. by doing raw pointer casts or using a union.
66+
* Violating assumptions of the Rust runtime. This is only possible using
67+
mechanisms outside Rust. Most assumptions of the Rust runtime are currently
68+
not explicitly documented.
69+
* For assumptions specifically related to unwinding, see the [panic
70+
documentation][unwinding-ffi].
71+
* The runtime assumes that a Rust stack frame is not deallocated without
72+
executing destructors for local variables owned by the stack frame. This assumption
73+
can be violated by C functions like `longjmp`.
6474

6575
> **Note**: Undefined behavior affects the entire program. For example, calling
6676
> a function in C that exhibits undefined behavior of C means your entire
@@ -160,11 +170,11 @@ a restricted set of valid values. In other words, the only cases in which
160170
reading uninitialized memory is permitted are inside `union`s and in "padding"
161171
(the gaps between the fields of a type).
162172

163-
164173
[`bool`]: types/boolean.md
165174
[`const`]: items/constant-items.md
166175
[noalias]: http://llvm.org/docs/LangRef.html#noalias
167176
[pointer aliasing rules]: http://llvm.org/docs/LangRef.html#pointer-aliasing-rules
177+
[abi]: abi.md
168178
[undef]: http://llvm.org/docs/LangRef.html#undefined-values
169179
[`target_feature`]: attributes/codegen.md#the-target_feature-attribute
170180
[`UnsafeCell<U>`]: std::cell::UnsafeCell
@@ -178,4 +188,5 @@ reading uninitialized memory is permitted are inside `union`s and in "padding"
178188
[project-field]: expressions/field-expr.md
179189
[project-tuple]: expressions/tuple-expr.md#tuple-indexing-expressions
180190
[project-slice]: expressions/array-expr.md#array-and-slice-indexing-expressions
191+
[unwinding-ffi]: panic.md#unwinding-across-ffi-boundaries
181192
[const-promoted]: destructors.md#constant-promotion

src/crates-and-source-files.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ use foo::bar as main;
103103
<!-- If the previous section needs updating (from "must take no arguments"
104104
onwards, also update it in the testing.md file -->
105105

106+
### Uncaught foreign unwinding
107+
108+
When a "foreign" unwind (e.g. an exception thrown from C++ code, or a `panic!`
109+
in Rust code compiled or linked with a different runtime) is not caught before
110+
reaching the `main` function, the process will be safely terminated. This may
111+
take the form of an abort, in which case it is not guaranteed that any `Drop`
112+
calls will be executed, and the error output may be less informative than if the
113+
runtime had been terminated by a "native" Rust `panic`.
114+
115+
For more information, see the [panic documentation][panic-docs].
116+
106117
### The `no_main` attribute
107118

108119
The *`no_main` [attribute]* may be applied at the crate level to disable
@@ -142,6 +153,7 @@ or `_` (U+005F) characters.
142153
[function]: items/functions.md
143154
[module]: items/modules.md
144155
[module path]: paths.md
156+
[panic-docs]: panic.md#unwinding-across-ffi-boundaries
145157
[shebang]: input-format.md#shebang-removal
146158
[trait or lifetime bounds]: trait-bounds.md
147159
[where clauses]: items/generics.md#where-clauses

src/destructors.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,8 @@ Temporaries are also created to hold the result of operands to an expression
268268
while the other operands are evaluated. The temporaries are associated to the
269269
scope of the expression with that operand. Since the temporaries are moved from
270270
once the expression is evaluated, dropping them has no effect unless one of the
271-
operands to an expression breaks out of the expression, returns, or panics.
271+
operands to an expression breaks out of the expression, returns, or
272+
[panics][panic].
272273

273274
```rust
274275
# struct PrintOnDrop(&'static str);
@@ -407,6 +408,8 @@ let x = (&temp()).use_temp(); // ERROR
407408

408409
## Not running destructors
409410

411+
### `forget`
412+
410413
r[destructors.forget]
411414

412415
[`std::mem::forget`] can be used to prevent the destructor of a variable from being run,
@@ -416,6 +419,20 @@ variable or field from being dropped automatically.
416419
> 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`.
417420
> 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.
418421
422+
### Process termination without unwinding
423+
424+
There are some ways to terminate the process without [unwinding], in which case
425+
destructors will not be run.
426+
427+
The standard library provides [`std::process::exit`] and
428+
[`std::process::abort`] to do this explicitly. Additionally, if the
429+
[panic-mode] is set to `abort`, panicking will always terminate the process
430+
without destructors being run.
431+
432+
There is one additional case to be aware of: when a panic reaches a
433+
[non-unwinding ABI boundary], either no destructors will run, or all
434+
destructors up until the ABI boundary will run.
435+
419436
[Assignment]: expressions/operator-expr.md#assignment-expressions
420437
[binding modes]: patterns.md#binding-modes
421438
[closure]: types/closure.md
@@ -425,11 +442,15 @@ variable or field from being dropped automatically.
425442
[initialized]: glossary.md#initialized
426443
[interior mutability]: interior-mutability.md
427444
[lazy boolean expression]: expressions/operator-expr.md#lazy-boolean-operators
445+
[non-unwinding ABI boundary]: items/functions.md#unwinding
446+
[panic]: panic.md
447+
[panic-mode]: ../panic.md#panic-runtimes
428448
[place context]: expressions.md#place-expressions-and-value-expressions
429449
[promoted]: destructors.md#constant-promotion
430450
[scrutinee]: glossary.md#scrutinee
431451
[statement]: statements.md
432452
[temporary]: expressions.md#temporaries
453+
[unwinding]: panic.md#unwinding
433454
[variable]: variables.md
434455

435456
[array]: types/array.md

src/expressions/array-expr.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Just as with methods, Rust will also insert dereference operations on `a` repeat
5353

5454
Indices are zero-based for arrays and slices.
5555
Array access is a [constant expression], so bounds can be checked at compile-time with a constant index value.
56-
Otherwise a check will be performed at run-time that will put the thread in a _panicked state_ if it fails.
56+
Otherwise a check will be performed at run-time that will put the thread in a [_panicked state_][panic] if it fails.
5757

5858
```rust,should_panic
5959
// lint is deny by default.
@@ -84,5 +84,6 @@ The array index expression can be implemented for types other than arrays and sl
8484
[constant item]: ../items/constant-items.md
8585
[literal]: ../tokens.md#literals
8686
[memory location]: ../expressions.md#place-expressions-and-value-expressions
87+
[panic]: ../panic.md
8788
[path]: path-expr.md
8889
[slice]: ../types/slice.md

src/items/external-blocks.md

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ standard C ABI on the specific platform. Other ABIs may be specified using an
7070
unsafe extern "stdcall" { }
7171
```
7272

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

7676
* `unsafe extern "Rust"` -- The default ABI when you write a normal `fn foo()` in any
@@ -80,6 +80,9 @@ are guaranteed to support:
8080
* `unsafe extern "system"` -- Usually the same as `extern "C"`, except on Win32, in
8181
which case it's `"stdcall"`, or what you should use to link to the Windows
8282
API itself
83+
* `extern "C-unwind"` and `extern "system-unwind"` -- identical to `"C"` and
84+
`"system"`, respectively, but with [different behavior][unwind-behavior] when
85+
the callee unwinds (by panicking or throwing a C++ style exception).
8386

8487
There are also some platform-specific ABI strings:
8588

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

102+
Like `"C"` and `"system"`, most platform-specific ABI strings also have a
103+
[corresponding `-unwind` variant][unwind-behavior]; specifically, these are:
104+
105+
* `"cdecl-unwind"`
106+
* `"stdcall-unwind"`
107+
* `"fastcall-unwind"`
108+
* `"vectorcall-unwind"`
109+
* `"thiscall-unwind"`
110+
* `"aapcs-unwind"`
111+
* `"win64-unwind"`
112+
* `"sysv64-unwind"`
113+
99114
## Variadic functions
100115

101116
Functions within external blocks may be variadic by specifying `...` as the
@@ -316,10 +331,9 @@ Attributes on extern function parameters follow the same rules and
316331
restrictions as [regular function parameters].
317332

318333
[IDENTIFIER]: ../identifiers.md
334+
[PE Format]: https://learn.microsoft.com/windows/win32/debug/pe-format#import-name-type
319335
[UEFI]: https://uefi.org/specifications
320336
[WebAssembly module]: https://webassembly.github.io/spec/core/syntax/modules.html
321-
[functions]: functions.md
322-
[statics]: static-items.md
323337
[_Abi_]: functions.md
324338
[_Function_]: functions.md
325339
[_InnerAttribute_]: ../attributes.md
@@ -329,11 +343,13 @@ restrictions as [regular function parameters].
329343
[_OuterAttribute_]: ../attributes.md
330344
[_StaticItem_]: static-items.md
331345
[_Visibility_]: ../visibility-and-privacy.md
332-
[attributes]: ../attributes.md
333-
[regular function parameters]: functions.md#attributes-on-function-parameters
334346
[`bundle` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-bundle
335-
[`whole-archive` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-whole-archive
336-
[`verbatim` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-verbatim
337347
[`dylib` versus `raw-dylib`]: #dylib-versus-raw-dylib
338-
[PE Format]: https://learn.microsoft.com/windows/win32/debug/pe-format#import-name-type
348+
[`verbatim` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-verbatim
349+
[`whole-archive` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-whole-archive
350+
[attributes]: ../attributes.md
351+
[functions]: functions.md
352+
[regular function parameters]: functions.md#attributes-on-function-parameters
353+
[statics]: static-items.md
354+
[unwind-behavior]: functions.md#unwinding
339355
[value namespace]: ../names/namespaces.md

src/items/functions.md

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,56 @@ extern "C" fn new_i32() -> i32 { 0 }
214214
let fptr: extern "C" fn() -> i32 = new_i32;
215215
```
216216

217-
Functions with an ABI that differs from `"Rust"` do not support unwinding in the
218-
exact same way that Rust does. Therefore, unwinding past the end of functions
219-
with such ABIs causes the process to abort.
220-
221-
> **Note**: The LLVM backend of the `rustc` implementation
222-
aborts the process by executing an illegal instruction.
217+
### Unwinding
218+
219+
Most ABI strings come in two variants, one with an `-unwind` suffix and one without.
220+
The `Rust` ABI always permits unwinding, so there is no `Rust-unwind` ABI.
221+
222+
The choice of ABI, together with the runtime [panic mode][panic-modes],
223+
determines the behavior when unwinding out of a function.
224+
225+
The table below indicates the behavior of an unwinding operation reaching each
226+
type of ABI boundary (function declaration or definition using the
227+
corresponding ABI string). Note that the Rust runtime is not affected by, and
228+
cannot have an effect on, any unwinding that occurs entirely within another
229+
language's runtime, that is, unwinds that are thrown and caught without
230+
reaching a Rust ABI boundary.
231+
232+
The `panic`-unwind column refers to [panicking] via the `panic!` macro and
233+
similar standard library mechanisms, as well as to any other Rust operations
234+
that cause a panic, such as out-of-bounds array indexing or integer overflow.
235+
236+
The "unwinding" ABI category refers to `"Rust"` (the implicit ABI of Rust
237+
functions not marked `extern`), `"C-unwind"`, and any other ABI with `-unwind`
238+
in its name. The "non-unwinding" ABI category refers to all other ABI strings,
239+
including `"C"` and `"stdcall"`.
240+
241+
Native unwinding is defined per-target. On targets that support throwing and
242+
catching C++ exceptions, it refers to the mechanism used to implement this
243+
feature. Some platforms implement a form of unwinding referred to as ["forced
244+
unwinding"][forced-unwinding]; `longjmp` on Windows and `pthread_exit` in
245+
`glibc` are implemented this way. Forced unwinding is explicitly excluded
246+
from the "Native unwind" column in the table.
247+
248+
| panic runtime | ABI | `panic`-unwind | Native unwind (unforced) |
249+
| -------------- | ------------ | ------------------------------------- | ----------------------- |
250+
| `panic=unwind` | unwinding | unwind | unwind |
251+
| `panic=unwind` | non-unwinding | abort (see note below) | [undefined behavior] |
252+
| `panic=abort` | unwinding | `panic` aborts without unwinding | abort |
253+
| `panic=abort` | non-unwinding | `panic` aborts without unwinding | [undefined behavior] |
254+
255+
> Note: With `panic=unwind`, when a `panic` is turned into an abort by a
256+
> non-unwinding ABI boundary, either no destructors (`Drop` calls) will run, or
257+
> all destructors up until the ABI boundary will run.
258+
259+
For other considerations and limitations regarding unwinding across FFI
260+
boundaries, see the [relevant section in the Panic documentation][panic-ffi].
261+
262+
[forced-unwinding]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html#forced-unwinding
263+
[panic-modes]: ../panic.md#panic-runtimes
264+
[panic-ffi]: ../panic.md#unwinding-across-ffi-boundaries
265+
[panicking]: ../panic.md
266+
[undefined behavior]: ../behavior-considered-undefined.md
223267

224268
## Const functions
225269

src/linkage.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,24 @@ a statically linked binary on MSVC you would execute:
226226
RUSTFLAGS='-C target-feature=+crt-static' cargo build --target x86_64-pc-windows-msvc
227227
```
228228

229+
## Prohibited linkage scenarios
230+
231+
No crate may be linked with [the `panic=abort` runtime][panic-runtime] if it has
232+
both of the following characteristics:
233+
234+
* It contains a call to an `-unwind` foreign function or function pointer
235+
* It was compiled with `panic=unwind`
236+
237+
`rustc` enforces this restriction at link-time. To guarantee that
238+
a library will be linkable regardless of the panic mode used at
239+
link-time, the [`ffi_unwind_calls` lint] may be used. The lint flags any
240+
calls to `-unwind` foreign functions or function pointers.
241+
242+
Note: Cargo will automatically unify all crates to use the same `panic`
243+
runtime, so this prohibition does not apply to projects compiled with Cargo.
244+
229245
[`cfg` attribute `target_feature` option]: conditional-compilation.md#target_feature
246+
[`ffi_unwind_calls` lint]: ../rustc/lints/listing/allowed-by-default.html#ffi-unwind-calls
230247
[configuration option]: conditional-compilation.md
248+
[panic-runtime]: panic.md#panic-runtimes
231249
[procedural macros]: procedural-macros.md

0 commit comments

Comments
 (0)