Skip to content

Commit 4d92113

Browse files
authored
Merge pull request #1226 from BatmanAoD/c-unwind-documentation
panic runtime and C-unwind documentation
2 parents b73de2a + ffc056c commit 4d92113

14 files changed

+294
-89
lines changed

Diff for: src/SUMMARY.md

+2
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)

Diff for: src/attributes.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ The following is an index of all built-in attributes.
370370
[`no_mangle`]: abi.md#the-no_mangle-attribute
371371
[`no_std`]: names/preludes.md#the-no_std-attribute
372372
[`non_exhaustive`]: attributes/type_system.md#the-non_exhaustive-attribute
373-
[`panic_handler`]: runtime.md#the-panic_handler-attribute
373+
[`panic_handler`]: panic.md#the-panic_handler-attribute
374374
[`path`]: items/modules.md#the-path-attribute
375375
[`proc_macro_attribute`]: procedural-macros.md#attribute-macros
376376
[`proc_macro_derive`]: procedural-macros.md#derive-macros

Diff for: src/attributes/codegen.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ r[attributes.codegen.target_feature.allowed-positions]
132132
The `#[target_feature]` attribute is not allowed on the following places:
133133

134134
- [the `main` function][crate.main]
135-
- a [`panic_handler` function][runtime.panic_handler]
135+
- a [`panic_handler` function][panic.panic_handler]
136136
- safe trait methods
137137
- safe default functions in traits
138138

Diff for: src/behavior-considered-undefined.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ 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 frame that does not allow unwinding (e.g. by calling a `"C-unwind"` function imported or transmuted as a `"C"` function or function pointer).
8181

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

99+
r[undefined.runtime]
100+
* Violating assumptions of the Rust runtime. Most assumptions of the Rust runtime are currently not explicitly documented.
101+
* For assumptions specifically related to unwinding, see the [panic documentation][unwinding-ffi].
102+
* 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`.
103+
99104
> **Note**: Undefined behavior affects the entire program. For example, calling
100105
> a function in C that exhibits undefined behavior of C means your entire
101106
> program contains undefined behaviour that can also affect the Rust code. And
@@ -245,6 +250,7 @@ reading uninitialized memory is permitted are inside `union`s and in "padding"
245250
[`const`]: items/constant-items.md
246251
[noalias]: http://llvm.org/docs/LangRef.html#noalias
247252
[pointer aliasing rules]: http://llvm.org/docs/LangRef.html#pointer-aliasing-rules
253+
[abi]: items/external-blocks.md#abi
248254
[undef]: http://llvm.org/docs/LangRef.html#undefined-values
249255
[`target_feature`]: attributes/codegen.md#the-target_feature-attribute
250256
[`UnsafeCell<U>`]: std::cell::UnsafeCell
@@ -258,5 +264,6 @@ reading uninitialized memory is permitted are inside `union`s and in "padding"
258264
[project-field]: expressions/field-expr.md
259265
[project-tuple]: expressions/tuple-expr.md#tuple-indexing-expressions
260266
[project-slice]: expressions/array-expr.md#array-and-slice-indexing-expressions
267+
[unwinding-ffi]: panic.md#unwinding-across-ffi-boundaries
261268
[const-promoted]: destructors.md#constant-promotion
262269
[lifetime-extended]: destructors.md#temporary-lifetime-extension

Diff for: src/conditional-compilation.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -304,14 +304,16 @@ r[cfg.panic]
304304
### `panic`
305305

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

309309
r[cfg.panic.values]
310310
Example values:
311311

312312
* `"abort"`
313313
* `"unwind"`
314314

315+
[panic strategy]: panic.md#panic-strategy
316+
315317
## Forms of conditional compilation
316318

317319
r[cfg.attr]

Diff for: src/crates-and-source-files.md

+9-4
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,17 @@ 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!` 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`.
129+
130+
For more information, see the [panic documentation][panic-docs].
131+
125132
r[crate.no_main]
126133
### The `no_main` attribute
127134

128-
129-
The *`no_main` [attribute]* may be applied at the crate level to disable
130-
emitting the `main` symbol for an executable binary. This is useful when some
131-
other object being linked to defines `main`.
135+
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`.
132136

133137
r[crate.crate_name]
134138
## The `crate_name` attribute
@@ -166,6 +170,7 @@ or `_` (U+005F) characters.
166170
[function]: items/functions.md
167171
[module]: items/modules.md
168172
[module path]: paths.md
173+
[panic-docs]: panic.md#unwinding-across-ffi-boundaries
169174
[shebang]: input-format.md#shebang-removal
170175
[trait or lifetime bounds]: trait-bounds.md
171176
[where clauses]: items/generics.md#where-clauses

Diff for: src/destructors.md

+15-1
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ 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 [panics][panic].
278278

279279
```rust
280280
# struct PrintOnDrop(&'static str);
@@ -425,6 +425,8 @@ let x = (&temp()).use_temp(); // ERROR
425425
r[destructors.forget]
426426
## Not running destructors
427427

428+
r[destructors.manually-suppressing]
429+
### Manually suppressing destructors
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,15 @@ 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+
r[destructors.process-termination]
439+
### Process termination without unwinding
440+
441+
There are some ways to terminate the process without [unwinding], in which case destructors will not be run.
442+
443+
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.
444+
445+
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.
446+
436447
[Assignment]: expressions/operator-expr.md#assignment-expressions
437448
[binding modes]: patterns.md#binding-modes
438449
[closure]: types/closure.md
@@ -442,11 +453,14 @@ variable or field from being dropped automatically.
442453
[initialized]: glossary.md#initialized
443454
[interior mutability]: interior-mutability.md
444455
[lazy boolean expression]: expressions/operator-expr.md#lazy-boolean-operators
456+
[non-unwinding ABI boundary]: items/functions.md#unwinding
457+
[panic]: panic.md
445458
[place context]: expressions.md#place-expressions-and-value-expressions
446459
[promoted]: destructors.md#constant-promotion
447460
[scrutinee]: glossary.md#scrutinee
448461
[statement]: statements.md
449462
[temporary]: expressions.md#temporaries
463+
[unwinding]: panic.md#unwinding
450464
[variable]: variables.md
451465

452466
[array]: types/array.md

Diff for: src/expressions/array-expr.md

+2-1
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

Diff for: src/items/external-blocks.md

+23-9
Original file line numberDiff line numberDiff line change
@@ -106,8 +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
110-
are guaranteed to support:
109+
The following ABI strings are supported on all platforms:
111110

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

124+
r[items.extern.abi.unwind]
125+
* `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).
126+
125127
r[items.extern.abi.platform]
126128
There are also some platform-specific ABI strings:
127129

@@ -151,6 +153,17 @@ r[items.extern.abi.thiscall]
151153
r[items.extern.abi.efiapi]
152154
* `unsafe extern "efiapi"` -- The ABI used for [UEFI] functions.
153155

156+
r[items.extern.abi.platform-unwind-variants]
157+
Like `"C"` and `"system"`, most platform-specific ABI strings also have a [corresponding `-unwind` variant][unwind-behavior]; specifically, these are:
158+
159+
* `"aapcs-unwind"`
160+
* `"cdecl-unwind"`
161+
* `"fastcall-unwind"`
162+
* `"stdcall-unwind"`
163+
* `"sysv64-unwind"`
164+
* `"thiscall-unwind"`
165+
* `"win64-unwind"`
166+
154167
r[items.extern.variadic]
155168
## Variadic functions
156169

@@ -428,10 +441,9 @@ Attributes on extern function parameters follow the same rules and
428441
restrictions as [regular function parameters].
429442

430443
[IDENTIFIER]: ../identifiers.md
444+
[PE Format]: https://learn.microsoft.com/windows/win32/debug/pe-format#import-name-type
431445
[UEFI]: https://uefi.org/specifications
432446
[WebAssembly module]: https://webassembly.github.io/spec/core/syntax/modules.html
433-
[functions]: functions.md
434-
[statics]: static-items.md
435447
[_Abi_]: functions.md
436448
[_Function_]: functions.md
437449
[_InnerAttribute_]: ../attributes.md
@@ -441,11 +453,13 @@ restrictions as [regular function parameters].
441453
[_OuterAttribute_]: ../attributes.md
442454
[_StaticItem_]: static-items.md
443455
[_Visibility_]: ../visibility-and-privacy.md
444-
[attributes]: ../attributes.md
445-
[regular function parameters]: functions.md#attributes-on-function-parameters
446456
[`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
449457
[`dylib` versus `raw-dylib`]: #dylib-versus-raw-dylib
450-
[PE Format]: https://learn.microsoft.com/windows/win32/debug/pe-format#import-name-type
458+
[`verbatim` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-verbatim
459+
[`whole-archive` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-whole-archive
460+
[attributes]: ../attributes.md
461+
[functions]: functions.md
462+
[regular function parameters]: functions.md#attributes-on-function-parameters
463+
[statics]: static-items.md
464+
[unwind-behavior]: functions.md#unwinding
451465
[value namespace]: ../names/namespaces.md

Diff for: src/items/functions.md

+31-8
Original file line numberDiff line numberDiff line change
@@ -254,20 +254,43 @@ let fptr: extern "C" fn() -> i32 = new_i32;
254254
```
255255

256256
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.
257+
### Unwinding
260258

261-
> **Note**: The LLVM backend of the `rustc` implementation
262-
aborts the process by executing an illegal instruction.
259+
r[items.fn.extern.unwind.intro]
260+
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.
261+
262+
r[items.fn.extern.unwind.behavior]
263+
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.
264+
265+
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.
266+
267+
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"`.
268+
269+
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.
270+
271+
| panic runtime | ABI | `panic`-unwind | Native unwind (unforced) |
272+
| -------------- | ------------ | ------------------------------------- | ----------------------- |
273+
| `panic=unwind` | unwinding | unwind | unwind |
274+
| `panic=unwind` | non-unwinding | abort (see notes below) | [undefined behavior] |
275+
| `panic=abort` | unwinding | `panic` aborts without unwinding | abort |
276+
| `panic=abort` | non-unwinding | `panic` aborts without unwinding | [undefined behavior] |
277+
278+
r[items.fn.extern.abort]
279+
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.
280+
281+
For other considerations and limitations regarding unwinding across FFI boundaries, see the [relevant section in the Panic documentation][panic-ffi].
282+
283+
[forced-unwinding]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html#forced-unwinding
284+
[panic handler]: ../panic.md#the-panic_handler-attribute
285+
[panic-ffi]: ../panic.md#unwinding-across-ffi-boundaries
286+
[panicking]: ../panic.md
287+
[undefined behavior]: ../behavior-considered-undefined.md
263288

264289
r[items.fn.const]
265290
## Const functions
266291

267292
r[items.fn.const.intro]
268-
Functions qualified with the `const` keyword are [const functions], as are
269-
[tuple struct] and [tuple variant] constructors. _Const functions_ can be
270-
called from within [const contexts].
293+
Functions qualified with the `const` keyword are [const functions], as are [tuple struct] and [tuple variant] constructors. _Const functions_ can be called from within [const contexts].
271294

272295
r[items.fn.const.extern]
273296
Const functions may use the [`extern`] function qualifier.

Diff for: src/linkage.md

+33
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,36 @@ binary link:
268271

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

274+
> [!NOTE]
275+
> Rust code compiled or linked with a different instance of the Rust runtime counts as "foreign code" for the purpose of this section.
276+
277+
r[link.unwinding]
278+
### Prohibited linkage and unwinding
279+
280+
r[link.unwinding.intro]
281+
Panic unwinding can only be used if the binary is built consistently according to the following rules.
282+
283+
r[link.unwinding.potential]
284+
A Rust artifact is called *potentially unwinding* if any of the following conditions is met:
285+
- The artifact uses the [`unwind` panic handler][panic.panic_handler].
286+
- The artifact contains a crate built with the `unwind` [panic strategy] that makes a call to a function using a `-unwind` ABI.
287+
- 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.
288+
289+
> [!NOTE]
290+
> This definition captures whether a `"Rust"` ABI call inside a Rust artifact can ever unwind.
291+
292+
r[link.unwinding.prohibited]
293+
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.
294+
295+
> [!NOTE]
296+
> 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.
297+
298+
> [!NOTE]
299+
> 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.
300+
271301
[`cfg` attribute `target_feature` option]: conditional-compilation.md#target_feature
302+
[`ffi_unwind_calls` lint]: ../rustc/lints/listing/allowed-by-default.html#ffi-unwind-calls
272303
[configuration option]: conditional-compilation.md
273304
[procedural macros]: procedural-macros.md
305+
[panic strategy]: panic.md#panic-strategy
306+
[`-C panic`]: ../rustc/codegen-options/index.html#panic

0 commit comments

Comments
 (0)