Skip to content

Commit bc204ce

Browse files
BatmanAoDehuss
authored andcommitted
panic runtime and C-unwind documentation
1 parent de2d528 commit bc204ce

File tree

10 files changed

+276
-19
lines changed

10 files changed

+276
-19
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
@@ -77,7 +77,9 @@ r[undefined.target-feature]
7777
does not support (see [`target_feature`]), *except* if the platform explicitly documents this to be safe.
7878

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

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

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

src/crates-and-source-files.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,21 @@ use foo::bar as main;
122122
<!-- If the previous section needs updating (from "must take no arguments"
123123
onwards, also update it in the testing.md file -->
124124

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

128-
129140
The *`no_main` [attribute]* may be applied at the crate level to disable
130141
emitting the `main` symbol for an executable binary. This is useful when some
131142
other object being linked to defines `main`.
@@ -166,6 +177,7 @@ or `_` (U+005F) characters.
166177
[function]: items/functions.md
167178
[module]: items/modules.md
168179
[module path]: paths.md
180+
[panic-docs]: panic.md#unwinding-across-ffi-boundaries
169181
[shebang]: input-format.md#shebang-removal
170182
[trait or lifetime bounds]: trait-bounds.md
171183
[where clauses]: items/generics.md#where-clauses

src/destructors.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,8 @@ Temporaries are also created to hold the result of operands to an expression
274274
while the other operands are evaluated. The temporaries are associated to the
275275
scope of the expression with that operand. Since the temporaries are moved from
276276
once the expression is evaluated, dropping them has no effect unless one of the
277-
operands to an expression breaks out of the expression, returns, or panics.
277+
operands to an expression breaks out of the expression, returns, or
278+
[panics][panic].
278279

279280
```rust
280281
# struct PrintOnDrop(&'static str);
@@ -425,6 +426,7 @@ let x = (&temp()).use_temp(); // ERROR
425426
r[destructors.forget]
426427
## Not running destructors
427428

429+
### `forget`
428430

429431
[`std::mem::forget`] can be used to prevent the destructor of a variable from being run,
430432
and [`std::mem::ManuallyDrop`] provides a wrapper to prevent a
@@ -433,6 +435,22 @@ variable or field from being dropped automatically.
433435
> 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`.
434436
> 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.
435437
438+
### Process termination without unwinding
439+
440+
r[destructors.process-termination]
441+
442+
There are some ways to terminate the process without [unwinding], in which case
443+
destructors will not be run.
444+
445+
The standard library provides [`std::process::exit`] and
446+
[`std::process::abort`] to do this explicitly. Additionally, if the
447+
[panic-mode] is set to `abort`, panicking will always terminate the process
448+
without destructors being run.
449+
450+
There is one additional case to be aware of: when a panic reaches a
451+
[non-unwinding ABI boundary], either no destructors will run, or all
452+
destructors up until the ABI boundary will run.
453+
436454
[Assignment]: expressions/operator-expr.md#assignment-expressions
437455
[binding modes]: patterns.md#binding-modes
438456
[closure]: types/closure.md
@@ -442,11 +460,15 @@ variable or field from being dropped automatically.
442460
[initialized]: glossary.md#initialized
443461
[interior mutability]: interior-mutability.md
444462
[lazy boolean expression]: expressions/operator-expr.md#lazy-boolean-operators
463+
[non-unwinding ABI boundary]: items/functions.md#unwinding
464+
[panic]: panic.md
465+
[panic-mode]: panic.md#panic-runtimes
445466
[place context]: expressions.md#place-expressions-and-value-expressions
446467
[promoted]: destructors.md#constant-promotion
447468
[scrutinee]: glossary.md#scrutinee
448469
[statement]: statements.md
449470
[temporary]: expressions.md#temporaries
471+
[unwinding]: panic.md#unwinding
450472
[variable]: variables.md
451473

452474
[array]: types/array.md

src/expressions/array-expr.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ Indices are zero-based for arrays and slices.
8383

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

8888
```rust,should_panic
8989
// lint is deny by default.
@@ -115,5 +115,6 @@ The array index expression can be implemented for types other than arrays and sl
115115
[constant item]: ../items/constant-items.md
116116
[literal]: ../tokens.md#literals
117117
[memory location]: ../expressions.md#place-expressions-and-value-expressions
118+
[panic]: ../panic.md
118119
[path]: path-expr.md
119120
[slice]: ../types/slice.md

src/items/external-blocks.md

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ unsafe extern "stdcall" { }
106106
```
107107

108108
r[items.extern.abi.standard]
109-
There are three ABI strings which are cross-platform, and which all compilers
109+
There are five ABI strings which are cross-platform, and which all compilers
110110
are guaranteed to support:
111111

112112
r[items.extern.abi.rust]
@@ -122,6 +122,11 @@ r[items.extern.abi.system]
122122
which case it's `"stdcall"`, or what you should use to link to the Windows
123123
API itself
124124

125+
r[items.extern.abi.unwind]
126+
* `extern "C-unwind"` and `extern "system-unwind"` -- identical to `"C"` and
127+
`"system"`, respectively, but with [different behavior][unwind-behavior] when
128+
the callee unwinds (by panicking or throwing a C++ style exception).
129+
125130
r[items.extern.abi.platform]
126131
There are also some platform-specific ABI strings:
127132

@@ -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
r[items.extern.variadic]
155172
## Variadic functions
156173

@@ -428,10 +445,9 @@ Attributes on extern function parameters follow the same rules and
428445
restrictions as [regular function parameters].
429446

430447
[IDENTIFIER]: ../identifiers.md
448+
[PE Format]: https://learn.microsoft.com/windows/win32/debug/pe-format#import-name-type
431449
[UEFI]: https://uefi.org/specifications
432450
[WebAssembly module]: https://webassembly.github.io/spec/core/syntax/modules.html
433-
[functions]: functions.md
434-
[statics]: static-items.md
435451
[_Abi_]: functions.md
436452
[_Function_]: functions.md
437453
[_InnerAttribute_]: ../attributes.md
@@ -441,11 +457,13 @@ restrictions as [regular function parameters].
441457
[_OuterAttribute_]: ../attributes.md
442458
[_StaticItem_]: static-items.md
443459
[_Visibility_]: ../visibility-and-privacy.md
444-
[attributes]: ../attributes.md
445-
[regular function parameters]: functions.md#attributes-on-function-parameters
446460
[`bundle` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-bundle
447-
[`whole-archive` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-whole-archive
448-
[`verbatim` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-verbatim
449461
[`dylib` versus `raw-dylib`]: #dylib-versus-raw-dylib
450-
[PE Format]: https://learn.microsoft.com/windows/win32/debug/pe-format#import-name-type
462+
[`verbatim` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-verbatim
463+
[`whole-archive` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-whole-archive
464+
[attributes]: ../attributes.md
465+
[functions]: functions.md
466+
[regular function parameters]: functions.md#attributes-on-function-parameters
467+
[statics]: static-items.md
468+
[unwind-behavior]: functions.md#unwinding
451469
[value namespace]: ../names/namespaces.md

src/items/functions.md

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

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

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

264309
r[items.fn.const]
265310
## Const functions

src/linkage.md

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

254254
## Mixed Rust and foreign codebases
255255

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

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

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

0 commit comments

Comments
 (0)