Skip to content

Commit 7965d3d

Browse files
committed
panic runtime and C-unwind documentation
1 parent 4ad2685 commit 7965d3d

File tree

10 files changed

+278
-18
lines changed

10 files changed

+278
-18
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: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ r[undefined.target-feature]
7878
does not support (see [`target_feature`]), *except* if the platform explicitly documents this to be safe.
7979

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

8385
r[undefined.invalid]
8486
* Producing an [invalid value][invalid-values]. "Producing" a
@@ -97,6 +99,16 @@ r[undefined.const-transmute-ptr2int]
9799
'Reinterpreting' refers to loading the pointer value at integer type without a
98100
cast, e.g. by doing raw pointer casts or using a union.
99101

102+
r[undefined.runtime]
103+
* Violating assumptions of the Rust runtime. This is only possible using
104+
mechanisms outside Rust. Most assumptions of the Rust runtime are currently
105+
not explicitly documented.
106+
* For assumptions specifically related to unwinding, see the [panic
107+
documentation][unwinding-ffi].
108+
* The runtime assumes that a Rust stack frame is not deallocated without
109+
executing destructors for local variables owned by the stack frame. This assumption
110+
can be violated by C functions like `longjmp`.
111+
100112
> **Note**: Undefined behavior affects the entire program. For example, calling
101113
> a function in C that exhibits undefined behavior of C means your entire
102114
> program contains undefined behaviour that can also affect the Rust code. And
@@ -246,6 +258,7 @@ reading uninitialized memory is permitted are inside `union`s and in "padding"
246258
[`const`]: items/constant-items.md
247259
[noalias]: http://llvm.org/docs/LangRef.html#noalias
248260
[pointer aliasing rules]: http://llvm.org/docs/LangRef.html#pointer-aliasing-rules
261+
[abi]: abi.md
249262
[undef]: http://llvm.org/docs/LangRef.html#undefined-values
250263
[`target_feature`]: attributes/codegen.md#the-target_feature-attribute
251264
[`UnsafeCell<U>`]: std::cell::UnsafeCell
@@ -259,5 +272,6 @@ reading uninitialized memory is permitted are inside `union`s and in "padding"
259272
[project-field]: expressions/field-expr.md
260273
[project-tuple]: expressions/tuple-expr.md#tuple-indexing-expressions
261274
[project-slice]: expressions/array-expr.md#array-and-slice-indexing-expressions
275+
[unwinding-ffi]: panic.md#unwinding-across-ffi-boundaries
262276
[const-promoted]: destructors.md#constant-promotion
263277
[lifetime-extended]: destructors.md#temporary-lifetime-extension

src/crates-and-source-files.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,19 @@ use foo::bar as main;
124124
<!-- If the previous section needs updating (from "must take no arguments"
125125
onwards, also update it in the testing.md file -->
126126

127+
### Uncaught foreign unwinding
128+
129+
r[crate.uncaught-foreign-unwinding]
130+
131+
When a "foreign" unwind (e.g. an exception thrown from C++ code, or a `panic!`
132+
in Rust code compiled or linked with a different runtime) is not caught before
133+
reaching the `main` function, the process will be safely terminated. This may
134+
take the form of an abort, in which case it is not guaranteed that any `Drop`
135+
calls will be executed, and the error output may be less informative than if the
136+
runtime had been terminated by a "native" Rust `panic`.
137+
138+
For more information, see the [panic documentation][panic-docs].
139+
127140
### The `no_main` attribute
128141

129142
r[crate.no_main]
@@ -169,6 +182,7 @@ or `_` (U+005F) characters.
169182
[function]: items/functions.md
170183
[module]: items/modules.md
171184
[module path]: paths.md
185+
[panic-docs]: panic.md#unwinding-across-ffi-boundaries
172186
[shebang]: input-format.md#shebang-removal
173187
[trait or lifetime bounds]: trait-bounds.md
174188
[where clauses]: items/generics.md#where-clauses

src/destructors.md

Lines changed: 24 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);
@@ -419,6 +420,8 @@ let x = (&temp()).use_temp(); // ERROR
419420

420421
## Not running destructors
421422

423+
### `forget`
424+
422425
r[destructors.forget]
423426

424427
[`std::mem::forget`] can be used to prevent the destructor of a variable from being run,
@@ -428,6 +431,22 @@ variable or field from being dropped automatically.
428431
> 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`.
429432
> 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.
430433
434+
### Process termination without unwinding
435+
436+
r[destructors.process-termination]
437+
438+
There are some ways to terminate the process without [unwinding], in which case
439+
destructors will not be run.
440+
441+
The standard library provides [`std::process::exit`] and
442+
[`std::process::abort`] to do this explicitly. Additionally, if the
443+
[panic-mode] is set to `abort`, panicking will always terminate the process
444+
without destructors being run.
445+
446+
There is one additional case to be aware of: when a panic reaches a
447+
[non-unwinding ABI boundary], either no destructors will run, or all
448+
destructors up until the ABI boundary will run.
449+
431450
[Assignment]: expressions/operator-expr.md#assignment-expressions
432451
[binding modes]: patterns.md#binding-modes
433452
[closure]: types/closure.md
@@ -437,11 +456,15 @@ variable or field from being dropped automatically.
437456
[initialized]: glossary.md#initialized
438457
[interior mutability]: interior-mutability.md
439458
[lazy boolean expression]: expressions/operator-expr.md#lazy-boolean-operators
459+
[non-unwinding ABI boundary]: items/functions.md#unwinding
460+
[panic]: panic.md
461+
[panic-mode]: panic.md#panic-runtimes
440462
[place context]: expressions.md#place-expressions-and-value-expressions
441463
[promoted]: destructors.md#constant-promotion
442464
[scrutinee]: glossary.md#scrutinee
443465
[statement]: statements.md
444466
[temporary]: expressions.md#temporaries
467+
[unwinding]: panic.md#unwinding
445468
[variable]: variables.md
446469

447470
[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: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ unsafe extern "stdcall" { }
102102
```
103103

104104
r[items.extern.abi.standard]
105-
There are three ABI strings which are cross-platform, and which all compilers
105+
There are five ABI strings which are cross-platform, and which all compilers
106106
are guaranteed to support:
107107

108108
r[items.extern.abi.rust]
@@ -118,6 +118,11 @@ r[items.extern.abi.system]
118118
which case it's `"stdcall"`, or what you should use to link to the Windows
119119
API itself
120120

121+
r[items.extern.abi.unwind]
122+
* `extern "C-unwind"` and `extern "system-unwind"` -- identical to `"C"` and
123+
`"system"`, respectively, but with [different behavior][unwind-behavior] when
124+
the callee unwinds (by panicking or throwing a C++ style exception).
125+
121126
r[items.extern.abi.platform]
122127
There are also some platform-specific ABI strings:
123128

@@ -151,6 +156,18 @@ r[items.extern.abi.thiscall]
151156
r[items.extern.abi.efiapi]
152157
* `unsafe extern "efiapi"` -- The ABI used for [UEFI] functions.
153158

159+
Like `"C"` and `"system"`, most platform-specific ABI strings also have a
160+
[corresponding `-unwind` variant][unwind-behavior]; specifically, these are:
161+
162+
* `"cdecl-unwind"`
163+
* `"stdcall-unwind"`
164+
* `"fastcall-unwind"`
165+
* `"vectorcall-unwind"`
166+
* `"thiscall-unwind"`
167+
* `"aapcs-unwind"`
168+
* `"win64-unwind"`
169+
* `"sysv64-unwind"`
170+
154171
## Variadic functions
155172

156173
r[items.extern.variadic]
@@ -439,10 +456,9 @@ Attributes on extern function parameters follow the same rules and
439456
restrictions as [regular function parameters].
440457

441458
[IDENTIFIER]: ../identifiers.md
459+
[PE Format]: https://learn.microsoft.com/windows/win32/debug/pe-format#import-name-type
442460
[UEFI]: https://uefi.org/specifications
443461
[WebAssembly module]: https://webassembly.github.io/spec/core/syntax/modules.html
444-
[functions]: functions.md
445-
[statics]: static-items.md
446462
[_Abi_]: functions.md
447463
[_Function_]: functions.md
448464
[_InnerAttribute_]: ../attributes.md
@@ -452,11 +468,13 @@ restrictions as [regular function parameters].
452468
[_OuterAttribute_]: ../attributes.md
453469
[_StaticItem_]: static-items.md
454470
[_Visibility_]: ../visibility-and-privacy.md
455-
[attributes]: ../attributes.md
456-
[regular function parameters]: functions.md#attributes-on-function-parameters
457471
[`bundle` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-bundle
458-
[`whole-archive` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-whole-archive
459-
[`verbatim` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-verbatim
460472
[`dylib` versus `raw-dylib`]: #dylib-versus-raw-dylib
461-
[PE Format]: https://learn.microsoft.com/windows/win32/debug/pe-format#import-name-type
473+
[`verbatim` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-verbatim
474+
[`whole-archive` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-whole-archive
475+
[attributes]: ../attributes.md
476+
[functions]: functions.md
477+
[regular function parameters]: functions.md#attributes-on-function-parameters
478+
[statics]: static-items.md
479+
[unwind-behavior]: functions.md#unwinding
462480
[value namespace]: ../names/namespaces.md

src/items/functions.md

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -254,13 +254,58 @@ extern "C" fn new_i32() -> i32 { 0 }
254254
let fptr: extern "C" fn() -> i32 = new_i32;
255255
```
256256

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

262-
> **Note**: The LLVM backend of the `rustc` implementation
263-
aborts the process by executing an illegal instruction.
261+
r[items.fn.extern.unwind.into]
262+
Most ABI strings come in two variants, one with an `-unwind` suffix and one without.
263+
The `Rust` ABI always permits unwinding, so there is no `Rust-unwind` ABI. The
264+
choice of ABI, together with the runtime [panic mode][panic-modes], determines
265+
the behavior when unwinding out of a function.
266+
267+
The table below indicates the behavior of an unwinding operation reaching each
268+
type of ABI boundary (function declaration or definition using the
269+
corresponding ABI string). Note that the Rust runtime is not affected by, and
270+
cannot have an effect on, any unwinding that occurs entirely within another
271+
language's runtime, that is, unwinds that are thrown and caught without
272+
reaching a Rust ABI boundary.
273+
274+
The `panic`-unwind column refers to [panicking] via the `panic!` macro and
275+
similar standard library mechanisms, as well as to any other Rust operations
276+
that cause a panic, such as out-of-bounds array indexing or integer overflow.
277+
278+
The "unwinding" ABI category refers to `"Rust"` (the implicit ABI of Rust
279+
functions not marked `extern`), `"C-unwind"`, and any other ABI with `-unwind`
280+
in its name. The "non-unwinding" ABI category refers to all other ABI strings,
281+
including `"C"` and `"stdcall"`.
282+
283+
Native unwinding is defined per-target. On targets that support throwing and
284+
catching C++ exceptions, it refers to the mechanism used to implement this
285+
feature. Some platforms implement a form of unwinding referred to as ["forced
286+
unwinding"][forced-unwinding]; `longjmp` on Windows and `pthread_exit` in
287+
`glibc` are implemented this way. Forced unwinding is explicitly excluded
288+
from the "Native unwind" column in the table.
289+
290+
| panic runtime | ABI | `panic`-unwind | Native unwind (unforced) |
291+
| -------------- | ------------ | ------------------------------------- | ----------------------- |
292+
| `panic=unwind` | unwinding | unwind | unwind |
293+
| `panic=unwind` | non-unwinding | abort (see notes below) | [undefined behavior] |
294+
| `panic=abort` | unwinding | `panic` aborts without unwinding | abort |
295+
| `panic=abort` | non-unwinding | `panic` aborts without unwinding | [undefined behavior] |
296+
297+
> **Note**: With `panic=unwind`, when a `panic` is turned into an abort by a
298+
> non-unwinding ABI boundary, either no destructors (`Drop` calls) will run, or
299+
> all destructors up until the ABI boundary will run.
300+
301+
For other considerations and limitations regarding unwinding across FFI
302+
boundaries, see the [relevant section in the Panic documentation][panic-ffi].
303+
304+
[forced-unwinding]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html#forced-unwinding
305+
[panic-modes]: ../panic.md#panic-runtimes
306+
[panic-ffi]: ../panic.md#unwinding-across-ffi-boundaries
307+
[panicking]: ../panic.md
308+
[undefined behavior]: ../behavior-considered-undefined.md
264309

265310
## Const functions
266311

src/linkage.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,9 @@ RUSTFLAGS='-C target-feature=+crt-static' cargo build --target x86_64-pc-windows
254254

255255
## Mixed Rust and foreign codebases
256256

257+
r[link.foreign-code]
258+
259+
r[link.foreign-code.foreign-linkers]
257260
If you are mixing Rust with foreign code (e.g. C, C++) and wish to make a single
258261
binary containing both types of code, you have two approaches for the final
259262
binary link:
@@ -269,6 +272,42 @@ binary link:
269272

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

275+
### Prohibited linkage and foreign unwinding
276+
277+
r[link.foreign-code.prohibited]
278+
279+
Undfined behavior may be caused by foreign code unwinding into a Rust crate
280+
with these characteristics:
281+
282+
* The foreign unwind enters Rust via a function or function pointer declared
283+
with an ABI that permits unwinding, that is, `"Rust"` (the default ABI) or
284+
any `-unwind` ABI
285+
* The Rust crate containing the `-unwind` ABI declaration was compiled with
286+
`panic=unwind`
287+
* The final binary is linked with [the `panic=abort` runtime][panic-runtime]
288+
289+
> **Note**: To protect against this undefined behavior, `rustc` does not permit
290+
> linking the `panic=abort` runtime against any crate that was compiled with
291+
> `panic=unwind` if that crate also contains a call to a foreign function or
292+
> function pointer declared with an `-unwind` ABI. Note that this prohibition
293+
> applies even when linking a static or dynamic library that only includes Rust
294+
> code, since the resulting library may be subsequently linked against another
295+
> library that may unwind. However, use of the `Rust` (default) ABI does not
296+
> cause a link-error, since that ABI is not expected to be used as an
297+
> entrypoint into a static or shared library.
298+
299+
r[link.foreign-code.prohibited.lint.ffi_unwind_calls]
300+
To guarantee that a library will be sound (and linkable with `rustc`)
301+
regardless of the panic mode used at link-time, the [`ffi_unwind_calls` lint]
302+
may be used. The lint flags any calls to `-unwind` foreign functions or
303+
function pointers.
304+
305+
> **Note**: the restriction can only be violated when mixing code with different
306+
> `-C panic` flags. This is not possible in normal use of cargo, so most users
307+
> need not be concerned about this.
308+
272309
[`cfg` attribute `target_feature` option]: conditional-compilation.md#target_feature
310+
[`ffi_unwind_calls` lint]: ../rustc/lints/listing/allowed-by-default.html#ffi-unwind-calls
273311
[configuration option]: conditional-compilation.md
312+
[panic-runtime]: panic.md#panic-runtimes
274313
[procedural macros]: procedural-macros.md

0 commit comments

Comments
 (0)