From 5f63e4659234ed6d0acf2dc3f8d978c02db7f112 Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Tue, 1 Oct 2024 22:54:54 -0600 Subject: [PATCH 01/42] panic runtime and C-unwind documentation --- src/SUMMARY.md | 2 + src/behavior-considered-undefined.md | 16 ++++- src/crates-and-source-files.md | 14 +++- src/destructors.md | 24 ++++++- src/expressions/array-expr.md | 3 +- src/items/external-blocks.md | 34 ++++++--- src/items/functions.md | 55 +++++++++++++-- src/linkage.md | 39 +++++++++++ src/panic.md | 101 +++++++++++++++++++++++++++ src/runtime.md | 7 +- 10 files changed, 276 insertions(+), 19 deletions(-) create mode 100644 src/panic.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 91f343b8d..60787c339 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -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) diff --git a/src/behavior-considered-undefined.md b/src/behavior-considered-undefined.md index ceb646937..a1fc8de1d 100644 --- a/src/behavior-considered-undefined.md +++ b/src/behavior-considered-undefined.md @@ -77,7 +77,9 @@ r[undefined.target-feature] does not support (see [`target_feature`]), *except* if the platform explicitly documents this to be safe. r[undefined.call] -* 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). r[undefined.invalid] * Producing an [invalid value][invalid-values]. "Producing" a @@ -96,6 +98,16 @@ r[undefined.const-transmute-ptr2int] 'Reinterpreting' refers to loading the pointer value at integer type without a cast, e.g. by doing raw pointer casts or using a union. +r[undefined.runtime] +* 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 > 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" [`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`]: std::cell::UnsafeCell @@ -258,5 +271,6 @@ 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 [lifetime-extended]: destructors.md#temporary-lifetime-extension diff --git a/src/crates-and-source-files.md b/src/crates-and-source-files.md index e91eadb87..915860960 100644 --- a/src/crates-and-source-files.md +++ b/src/crates-and-source-files.md @@ -122,10 +122,21 @@ use foo::bar as main; +r[crate.uncaught-foreign-unwinding] +### 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]. + r[crate.no_main] ### The `no_main` attribute - 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`. @@ -166,6 +177,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 diff --git a/src/destructors.md b/src/destructors.md index 7ec6d6fe1..4ac4d2773 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -274,7 +274,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); @@ -425,6 +426,7 @@ let x = (&temp()).use_temp(); // ERROR r[destructors.forget] ## Not running destructors +### `forget` [`std::mem::forget`] can be used to prevent the destructor of a variable from being run, and [`std::mem::ManuallyDrop`] provides a wrapper to prevent a @@ -433,6 +435,22 @@ 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 + +r[destructors.process-termination] + +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 @@ -442,11 +460,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 diff --git a/src/expressions/array-expr.md b/src/expressions/array-expr.md index b1f6c0526..aa6a911b2 100644 --- a/src/expressions/array-expr.md +++ b/src/expressions/array-expr.md @@ -83,7 +83,7 @@ Indices are zero-based for arrays and slices. r[expr.array.index.const] 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. @@ -115,5 +115,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 diff --git a/src/items/external-blocks.md b/src/items/external-blocks.md index 4e08bc17d..ae702200a 100644 --- a/src/items/external-blocks.md +++ b/src/items/external-blocks.md @@ -106,7 +106,7 @@ unsafe extern "stdcall" { } ``` r[items.extern.abi.standard] -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: r[items.extern.abi.rust] @@ -122,6 +122,11 @@ r[items.extern.abi.system] which case it's `"stdcall"`, or what you should use to link to the Windows API itself +r[items.extern.abi.unwind] +* `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). + r[items.extern.abi.platform] There are also some platform-specific ABI strings: @@ -151,6 +156,18 @@ r[items.extern.abi.thiscall] r[items.extern.abi.efiapi] * `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"` + r[items.extern.variadic] ## Variadic functions @@ -428,10 +445,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 @@ -441,11 +457,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 diff --git a/src/items/functions.md b/src/items/functions.md index a4d0ee6d0..8ac01c407 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -253,13 +253,58 @@ extern "C" fn new_i32() -> i32 { 0 } let fptr: extern "C" fn() -> i32 = new_i32; ``` +### Unwinding + r[items.fn.extern.unwind] -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. +r[items.fn.extern.unwind.into] +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 notes 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 r[items.fn.const] ## Const functions diff --git a/src/linkage.md b/src/linkage.md index 23bd44c2c..7db900fc8 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -253,6 +253,9 @@ RUSTFLAGS='-C target-feature=+crt-static' cargo build --target x86_64-pc-windows ## Mixed Rust and foreign codebases +r[link.foreign-code] + +r[link.foreign-code.foreign-linkers] If you are mixing Rust with foreign code (e.g. C, C++) and wish to make a single binary containing both types of code, you have two approaches for the final binary link: @@ -268,6 +271,42 @@ binary link: Passing `rlib`s directly into your foreign linker is currently unsupported. +### Prohibited linkage and foreign unwinding + +r[link.foreign-code.prohibited] + +Undfined behavior may be caused by foreign code unwinding into a Rust crate +with these characteristics: + +* The foreign unwind enters Rust via a function or function pointer declared + with an ABI that permits unwinding, that is, `"Rust"` (the default ABI) or + any `-unwind` ABI +* The Rust crate containing the `-unwind` ABI declaration was compiled with + `panic=unwind` +* The final binary is linked with [the `panic=abort` runtime][panic-runtime] + +> **Note**: To protect against this undefined behavior, `rustc` does not permit +> linking the `panic=abort` runtime against any crate that was compiled with +> `panic=unwind` if that crate also contains a call to a foreign function or +> function pointer declared with an `-unwind` ABI. Note that this prohibition +> applies even when linking a static or dynamic library that only includes Rust +> code, since the resulting library may be subsequently linked against another +> library that may unwind. However, use of the `Rust` (default) ABI does not +> cause a link-error, since that ABI is not expected to be used as an +> entrypoint into a static or shared library. + +r[link.foreign-code.prohibited.lint.ffi_unwind_calls] +To guarantee that a library will be sound (and linkable with `rustc`) +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**: the restriction can only be violated when mixing code with different +> `-C panic` flags. This is not possible in normal use of cargo, so most users +> need not be concerned about this. + [`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 diff --git a/src/panic.md b/src/panic.md new file mode 100644 index 000000000..b0462dbd1 --- /dev/null +++ b/src/panic.md @@ -0,0 +1,101 @@ +# Panic + +Rust provides a mechanism to prevent a function from returning normally, and +instead "panic," which is a response to an error condition that is typically +not expected to be recoverable within the context in which the error is +encountered. + +Some language constructs, such as out-of-bounds [array indexing], panic +automatically. There are also language features that provide a level of control +over panic behavior: +* A [_panic runtime_](#panic-runtimes) defined how a panic is handled during + runtime. +* [FFI ABIs](items/functions.md#unwinding) may alter how panics behave. + +> **Note**: The standard library provides the capability to explicitly panic +> via the [`panic!` macro][macro-panic]. + +## Unwinding + +Panicking may either be recoverable or non-recoverable, though it can be +configured (via `panic=abort`) to always be non-recoverable. (The converse is +not true: `panic=unwind` does not guarantee that all panics are recoverable, +only that panicking via the `panic!` macro and similar standard library +mechanisms is recoverable.) When panic recovery occurs, the runtime "unwinds" +Rust frames, just as C++'s `throw` unwinds C++ frames, until the panic reaches +the point of recovery (for instance at a thread boundary). This means that as +the panic traverses Rust frames, live objects in those frames that [implement +`Drop`][destructors] will have their `drop` methods called. Thus, when normal +execution resumes, no-longer-accessible objects will have been "cleaned up" +just as if they had gone out of scope normally. + +> **Note**: As long as this guarantee of resource-cleanup is preserved, +> "unwinding" may be implemented without actually using the mechanism used by +> C++ for the target platform. + +> **Note**: The standard library provides two mechanisms for recovering from a panic, +> [`catch_unwind`][fn-catch-unwind] (which enables recovery within the +> panicking thread) and [`thread::spawn`][thread-spawn] (which automatically +> sets up panic recovery for the spawned thread so that other threads may +> continue running). + +### Unwinding across FFI boundaries + +It is possible to unwind across FFI boundaries using an [appropriate ABI +declaration][unwind-abi]. While useful in certain cases, this creates unique +opportunities for undefined behavior, especially when multiple language runtimes +are involved. + +Unwinding with the wrong ABI is undefined behavior: + +* Causing an unwind into Rust code from a foreign function that was called via a + function declaration or pointer declared with a non-unwinding ABI, such as `"C"`, + `"system"`, etc. (For example, this case occurs when such a function written in + C++ throws an exception that is uncaught and propagates to Rust.) +* Calling a Rust `extern` function that unwinds (with `extern "C-unwind"` or + another ABI that permits unwinding) from a runtime that does not support. + unwinding, such as code compiled with GCC or Clang using `-fno-exceptions` + +Catching a foreign unwinding operation (such as a C++ exception) using +`catch_unwind`, `JoinHandle::join`, or by letting it propagate all the way to a +Rust `main()` function will have one of two behaviors, and it is unspecified +which will occur: +* The process aborts. +* The function returns a `Result::Err` containing an opaque type. + +Note that Rust code compiled or linked with a different runtime counts as a +"foreign exception" for the purpose of this guarantee. Thus, a library that +uses `panic!` and is linked against one version of the Rust standard library, +invoked from an application that uses a different version of the standard +library, may cause the entire application to crash even if the library is only +used within a child thread. + +There are currently no guarantees about the behavior that occurs when a foreign +runtime attempts to dispose of, or rethrow, a Rust `panic` payload. In other +words, an unwind originated from a Rust runtime must either lead to termination +of the process or be caught by the same runtime. + +## Panic runtimes + +The actual behavior and implementation of `panic!` is controlled by the _panic +runtime_. + +> **Note**: The Rust standard library provides two panic runtimes: +> `panic_unwind` (which unwinds the stack and is potentially recoverable) and +> `panic_abort` (which aborts the process and is non-recoverable). The default +> runtime depends on the target platform, but is generally `panic_unwind` on +> platforms with native support for C++ exceptions. + +When compiling code that is guaranteed to be linked to a non-recoverable panic +runtime, the optimizer may assume that unwinding across Rust frames is +impossible, which can result in both code-size and runtime speed improvements. + +See also the [`panic_handler` attribute](runtime.md#the-panic_handler-attribute) which can be used to change the behavior of panics. + +[array indexing]: expressions/array-expr.md#array-and-slice-indexing-expressions +[destructors]: destructors.md +[fn-catch-unwind]: ../std/panic/fn.catch_unwind.html +[macro-panic]: ../std/macro.panic.html +[runtime]: runtime.md +[thread-spawn]: ../std/thread/fn.spawn.html +[unwind-abi]: items/functions.md#unwinding diff --git a/src/runtime.md b/src/runtime.md index 22bfd4672..27d088878 100644 --- a/src/runtime.md +++ b/src/runtime.md @@ -58,8 +58,10 @@ r[runtime.panic_handler.std] The standard library provides an implementation of `panic_handler` that defaults to unwinding the stack but that can be [changed to abort the -process][abort]. The standard library's panic behavior can be modified at -runtime with the [set_hook] function. +process][abort]. See [panic runtimes] for more details. + +The standard library's panic behavior can be modified at runtime with the +[`set_hook` function][set_hook]. r[runtime.global_allocator] ## The `global_allocator` attribute @@ -100,6 +102,7 @@ display a console window on startup. It will run detached from any existing cons [abort]: ../book/ch09-01-unrecoverable-errors-with-panic.html [attribute]: attributes.md [crate types]: linkage.md +[panic runtimes]: panic.md#panic-runtimes [set_hook]: std::panic::set_hook [static item]: items/static-items.md [subsystem]: https://msdn.microsoft.com/en-us/library/fcc1zstk.aspx From c8fc0e85ebdcfa82c901c580d3dd86188e4dcaa7 Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Sat, 23 Nov 2024 21:30:09 -0700 Subject: [PATCH 02/42] Rust version number for destructors guarantee Co-authored-by: Daira-Emma Hopwood --- src/items/functions.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/items/functions.md b/src/items/functions.md index 8ac01c407..ee1f1466c 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -293,9 +293,10 @@ from the "Native unwind" column in the table. | `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. +> **Note**: The following guarantee applies from Rust 1.82 onward: 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]. From edd5b18e254a95ac1e995520fd3596039f0952c3 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Mon, 25 Nov 2024 14:59:17 -0500 Subject: [PATCH 03/42] Fix identifeirs in functions.md and move the guarantee about destructor execution out of a note --- src/items/functions.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/items/functions.md b/src/items/functions.md index ee1f1466c..814887792 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -257,12 +257,13 @@ let fptr: extern "C" fn() -> i32 = new_i32; r[items.fn.extern.unwind] -r[items.fn.extern.unwind.into] +r[items.fn.extern.unwind.info] 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. +r[items.fn.extern.unwind.behavior] 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 @@ -293,10 +294,12 @@ from the "Native unwind" column in the table. | `panic=abort` | unwinding | `panic` aborts without unwinding | abort | | `panic=abort` | non-unwinding | `panic` aborts without unwinding | [undefined behavior] | -> **Note**: The following guarantee applies from Rust 1.82 onward: 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. +r[items.fn.extern.abort] +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. + +> [!NOTE] +> Prior to Rust 1.82, it is possible for only some of the destructors to executive, and not others. For other considerations and limitations regarding unwinding across FFI boundaries, see the [relevant section in the Panic documentation][panic-ffi]. From 39de76ceb5333a43bca3c12acd9c39e65ee719a0 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Mon, 25 Nov 2024 15:07:09 -0500 Subject: [PATCH 04/42] Add identifier syntax to panic.md --- src/panic.md | 58 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/src/panic.md b/src/panic.md index b0462dbd1..8066f85aa 100644 --- a/src/panic.md +++ b/src/panic.md @@ -1,39 +1,54 @@ # Panic +r[panic] + +r[panic.intro] Rust provides a mechanism to prevent a function from returning normally, and instead "panic," which is a response to an error condition that is typically not expected to be recoverable within the context in which the error is encountered. +r[panic.lang-ops] Some language constructs, such as out-of-bounds [array indexing], panic -automatically. There are also language features that provide a level of control +automatically. + +r[panic.control] +There are also language features that provide a level of control over panic behavior: * A [_panic runtime_](#panic-runtimes) defined how a panic is handled during runtime. * [FFI ABIs](items/functions.md#unwinding) may alter how panics behave. -> **Note**: The standard library provides the capability to explicitly panic +> [!NOTE] +> The standard library provides the capability to explicitly panic > via the [`panic!` macro][macro-panic]. ## Unwinding +r[panic.unwind] + +r[panic.unwind.intro] Panicking may either be recoverable or non-recoverable, though it can be configured (via `panic=abort`) to always be non-recoverable. (The converse is not true: `panic=unwind` does not guarantee that all panics are recoverable, only that panicking via the `panic!` macro and similar standard library -mechanisms is recoverable.) When panic recovery occurs, the runtime "unwinds" -Rust frames, just as C++'s `throw` unwinds C++ frames, until the panic reaches +mechanisms is recoverable.) + +r[panic.unwind.destruction] +When panic recovery occurs, the runtime "unwinds" Rust frames, just as C++'s `throw` unwinds C++ frames, until the panic reaches the point of recovery (for instance at a thread boundary). This means that as the panic traverses Rust frames, live objects in those frames that [implement `Drop`][destructors] will have their `drop` methods called. Thus, when normal execution resumes, no-longer-accessible objects will have been "cleaned up" just as if they had gone out of scope normally. -> **Note**: As long as this guarantee of resource-cleanup is preserved, +> [!NOTE] +> As long as this guarantee of resource-cleanup is preserved, > "unwinding" may be implemented without actually using the mechanism used by > C++ for the target platform. -> **Note**: The standard library provides two mechanisms for recovering from a panic, +> [!NOTE] +> The standard library provides two mechanisms for recovering from a panic, > [`catch_unwind`][fn-catch-unwind] (which enables recovery within the > panicking thread) and [`thread::spawn`][thread-spawn] (which automatically > sets up panic recovery for the spawned thread so that other threads may @@ -41,11 +56,15 @@ just as if they had gone out of scope normally. ### Unwinding across FFI boundaries +r[panic.unwind.ffi] + +r[panic.unwind.ffi.intro] It is possible to unwind across FFI boundaries using an [appropriate ABI declaration][unwind-abi]. While useful in certain cases, this creates unique opportunities for undefined behavior, especially when multiple language runtimes are involved. +r[panic.unwind.ffi.undefined] Unwinding with the wrong ABI is undefined behavior: * Causing an unwind into Rust code from a foreign function that was called via a @@ -56,6 +75,7 @@ Unwinding with the wrong ABI is undefined behavior: another ABI that permits unwinding) from a runtime that does not support. unwinding, such as code compiled with GCC or Clang using `-fno-exceptions` +r[panic.unwind.ffi.catch-foreign] Catching a foreign unwinding operation (such as a C++ exception) using `catch_unwind`, `JoinHandle::join`, or by letting it propagate all the way to a Rust `main()` function will have one of two behaviors, and it is unspecified @@ -63,13 +83,15 @@ which will occur: * The process aborts. * The function returns a `Result::Err` containing an opaque type. -Note that Rust code compiled or linked with a different runtime counts as a -"foreign exception" for the purpose of this guarantee. Thus, a library that -uses `panic!` and is linked against one version of the Rust standard library, -invoked from an application that uses a different version of the standard -library, may cause the entire application to crash even if the library is only -used within a child thread. +> [!NOTE] +> Rust code compiled or linked with a different runtime counts as a +> "foreign exception" for the purpose of this guarantee. Thus, a library that +> uses `panic!` and is linked against one version of the Rust standard library, +> invoked from an application that uses a different version of the standard +> library, may cause the entire application to crash even if the library is only +> used within a child thread. +r[panic.unwind.ffi.dipose-panic] There are currently no guarantees about the behavior that occurs when a foreign runtime attempts to dispose of, or rethrow, a Rust `panic` payload. In other words, an unwind originated from a Rust runtime must either lead to termination @@ -77,18 +99,22 @@ of the process or be caught by the same runtime. ## Panic runtimes +r[panic.runtime] + The actual behavior and implementation of `panic!` is controlled by the _panic runtime_. -> **Note**: The Rust standard library provides two panic runtimes: +> [!NOTE] +> The Rust standard library provides two panic runtimes: > `panic_unwind` (which unwinds the stack and is potentially recoverable) and > `panic_abort` (which aborts the process and is non-recoverable). The default > runtime depends on the target platform, but is generally `panic_unwind` on > platforms with native support for C++ exceptions. -When compiling code that is guaranteed to be linked to a non-recoverable panic -runtime, the optimizer may assume that unwinding across Rust frames is -impossible, which can result in both code-size and runtime speed improvements. +> [!NOTE] +> When compiling code that is guaranteed to be linked to a non-recoverable panic +> runtime, the optimizer may assume that unwinding across Rust frames is +> impossible, which can result in both code-size and runtime speed improvements. See also the [`panic_handler` attribute](runtime.md#the-panic_handler-attribute) which can be used to change the behavior of panics. From 6413901b098ce23cc7c04e7c6dbb88b629b1ad95 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Mon, 25 Nov 2024 15:09:22 -0500 Subject: [PATCH 05/42] Move note in linkage.md --- src/linkage.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/linkage.md b/src/linkage.md index 7db900fc8..60382e908 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -295,15 +295,16 @@ with these characteristics: > cause a link-error, since that ABI is not expected to be used as an > entrypoint into a static or shared library. +> **Note**: the restriction can only be violated when mixing code with different +> `-C panic` flags. This is not possible in normal use of cargo, so most users +> need not be concerned about this. + r[link.foreign-code.prohibited.lint.ffi_unwind_calls] To guarantee that a library will be sound (and linkable with `rustc`) 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**: the restriction can only be violated when mixing code with different -> `-C panic` flags. This is not possible in normal use of cargo, so most users -> need not be concerned about this. [`cfg` attribute `target_feature` option]: conditional-compilation.md#target_feature [`ffi_unwind_calls` lint]: ../rustc/lints/listing/allowed-by-default.html#ffi-unwind-calls From 70f8292ceb10360a705812e7fdb2f5af38f0566e Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Mon, 25 Nov 2024 15:24:50 -0500 Subject: [PATCH 06/42] Fix whitespace issues in previous commit --- src/panic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panic.md b/src/panic.md index 8066f85aa..203a31cfa 100644 --- a/src/panic.md +++ b/src/panic.md @@ -32,7 +32,7 @@ Panicking may either be recoverable or non-recoverable, though it can be configured (via `panic=abort`) to always be non-recoverable. (The converse is not true: `panic=unwind` does not guarantee that all panics are recoverable, only that panicking via the `panic!` macro and similar standard library -mechanisms is recoverable.) +mechanisms is recoverable.) r[panic.unwind.destruction] When panic recovery occurs, the runtime "unwinds" Rust frames, just as C++'s `throw` unwinds C++ frames, until the panic reaches From d08e4060170aedec7ae5949fe2f117c3b16db212 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Mon, 25 Nov 2024 15:25:48 -0500 Subject: [PATCH 07/42] Fix omitted whitespace issue (thanks VSC) --- src/linkage.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/linkage.md b/src/linkage.md index 60382e908..89b820ee9 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -305,7 +305,6 @@ 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. - [`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 From a20802eb4044c61524644dc7403eb6dd40be6639 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 26 Nov 2024 18:15:04 -0500 Subject: [PATCH 08/42] Update src/items/functions.md Co-authored-by: Ralf Jung --- src/items/functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/items/functions.md b/src/items/functions.md index 814887792..9fbc35d04 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -299,7 +299,7 @@ With `panic=unwind`, when a `panic` is turned into an abort by a non-unwinding A up until the ABI boundary will run. > [!NOTE] -> Prior to Rust 1.82, it is possible for only some of the destructors to executive, and not others. +> Prior to Rust 1.82, it is possible for only some of the destructors to execute, and not others. For other considerations and limitations regarding unwinding across FFI boundaries, see the [relevant section in the Panic documentation][panic-ffi]. From 2525ab4b6a32502fa9914afa0909bcfb125a40cc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 29 Nov 2024 17:31:30 +0100 Subject: [PATCH 09/42] fix wording regarding UB due to unwinding linkage --- src/linkage.md | 62 +++++++++++++++++++++++++++----------------------- src/panic.md | 2 +- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/linkage.md b/src/linkage.md index 89b820ee9..b68c725b1 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -271,35 +271,39 @@ binary link: Passing `rlib`s directly into your foreign linker is currently unsupported. -### Prohibited linkage and foreign unwinding - -r[link.foreign-code.prohibited] - -Undfined behavior may be caused by foreign code unwinding into a Rust crate -with these characteristics: - -* The foreign unwind enters Rust via a function or function pointer declared - with an ABI that permits unwinding, that is, `"Rust"` (the default ABI) or - any `-unwind` ABI -* The Rust crate containing the `-unwind` ABI declaration was compiled with - `panic=unwind` -* The final binary is linked with [the `panic=abort` runtime][panic-runtime] - -> **Note**: To protect against this undefined behavior, `rustc` does not permit -> linking the `panic=abort` runtime against any crate that was compiled with -> `panic=unwind` if that crate also contains a call to a foreign function or -> function pointer declared with an `-unwind` ABI. Note that this prohibition -> applies even when linking a static or dynamic library that only includes Rust -> code, since the resulting library may be subsequently linked against another -> library that may unwind. However, use of the `Rust` (default) ABI does not -> cause a link-error, since that ABI is not expected to be used as an -> entrypoint into a static or shared library. - -> **Note**: the restriction can only be violated when mixing code with different -> `-C panic` flags. This is not possible in normal use of cargo, so most users -> need not be concerned about this. - -r[link.foreign-code.prohibited.lint.ffi_unwind_calls] +> [!NOTE] +> Rust code compiled or linked with a different instance of the Rust runtime counts as a +> "foreign code" for the purpose of this section. + +### Prohibited linkage and unwinding + +r[link.unwinding] +If you are *not* using `rustc` to link Rust files, you must take care to ensure that unwinding is +handled consistently across the entire binary. This includes using `dlopen` or similar facilities +where linking is done by the system runtime without `rustc` being involved. + +r[link.unwinding.potential] +A Rust binary or `staticlib` is called *potentially unwinding* if any of the following conditions +is met: +- The binary or `staticlib` is linked with [the `panic=unwind` runtime][panic-runtime]. +- The binary or `staticlib` contains a crate built with `-Cpanic=unwind` that makes a call + to a function using a `-unwind` ABI. +- The binary or `staticlib` makes a `"Rust"` ABI call to code running in a separately built + `staticlib` (i.e., a separate instance of the Rust runtime), and that other `staticlib` is + potentially unwinding. + +> [!NOTE] +> This definition captures whether a `"Rust"` ABI call inside a Rust binary or `staticlib` can ever +> unwind. + +r[link.unwinding.prohibited] +If a Rust binary or `staticlib` is potentially unwinding, then all its crates must be built with `-Cpanic=unwind`. + +> [!NOTE] +> This restriction can only be violated when mixing code with different `-C panic` flags +> while also using a non-`rustc` linker. Most users to not have to be concerned about this. + +r[link.unwinding.prohibited.lint.ffi_unwind_calls] To guarantee that a library will be sound (and linkable with `rustc`) 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 diff --git a/src/panic.md b/src/panic.md index 203a31cfa..af47eb9dd 100644 --- a/src/panic.md +++ b/src/panic.md @@ -84,7 +84,7 @@ which will occur: * The function returns a `Result::Err` containing an opaque type. > [!NOTE] -> Rust code compiled or linked with a different runtime counts as a +> Rust code compiled or linked with a different instance of the Rust runtime counts as a > "foreign exception" for the purpose of this guarantee. Thus, a library that > uses `panic!` and is linked against one version of the Rust standard library, > invoked from an application that uses a different version of the standard From 5bff1e36b35e8247a4d081b794e3a9a8ff5339f5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 21 Dec 2024 14:36:11 +0100 Subject: [PATCH 10/42] improve terminology --- src/linkage.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/linkage.md b/src/linkage.md index b68c725b1..adda83fc0 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -283,21 +283,20 @@ handled consistently across the entire binary. This includes using `dlopen` or s where linking is done by the system runtime without `rustc` being involved. r[link.unwinding.potential] -A Rust binary or `staticlib` is called *potentially unwinding* if any of the following conditions -is met: -- The binary or `staticlib` is linked with [the `panic=unwind` runtime][panic-runtime]. -- The binary or `staticlib` contains a crate built with `-Cpanic=unwind` that makes a call +A Rust artifact is called *potentially unwinding* if any of the following conditions is met: +- The artifact is linked with [the `panic=unwind` runtime][panic-runtime]. +- The artifact contains a crate built with `-Cpanic=unwind` that makes a call to a function using a `-unwind` ABI. -- The binary or `staticlib` makes a `"Rust"` ABI call to code running in a separately built - `staticlib` (i.e., a separate instance of the Rust runtime), and that other `staticlib` is +- 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. > [!NOTE] -> This definition captures whether a `"Rust"` ABI call inside a Rust binary or `staticlib` can ever +> This definition captures whether a `"Rust"` ABI call inside a Rust artifact can ever > unwind. r[link.unwinding.prohibited] -If a Rust binary or `staticlib` is potentially unwinding, then all its crates must be built with `-Cpanic=unwind`. +If a Rust artifact is potentially unwinding, then all its crates must be built with `-Cpanic=unwind`. > [!NOTE] > This restriction can only be violated when mixing code with different `-C panic` flags From 7453efd6d670bb68a192a0f8e7ac8c4e4a70d453 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 9 Jan 2025 16:25:17 -0800 Subject: [PATCH 11/42] Do not document a rustc-lint as a language rule We don't document rustc lints as language rules. I think in this case, it's fine to mention it, but it should just be in a note block. --- src/linkage.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/linkage.md b/src/linkage.md index adda83fc0..94b58fa53 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -302,11 +302,11 @@ If a Rust artifact is potentially unwinding, then all its crates must be built w > This restriction can only be violated when mixing code with different `-C panic` flags > while also using a non-`rustc` linker. Most users to not have to be concerned about this. -r[link.unwinding.prohibited.lint.ffi_unwind_calls] -To guarantee that a library will be sound (and linkable with `rustc`) -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] +> To guarantee that a library will be sound (and linkable with `rustc`) +> 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. [`cfg` attribute `target_feature` option]: conditional-compilation.md#target_feature [`ffi_unwind_calls` lint]: ../rustc/lints/listing/allowed-by-default.html#ffi-unwind-calls From c3cc7fadde9fce8b534cc173c7cb7e5eff245d3d Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 9 Jan 2025 16:25:49 -0800 Subject: [PATCH 12/42] Use built-in std linking --- src/panic.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/panic.md b/src/panic.md index af47eb9dd..8edb52ab3 100644 --- a/src/panic.md +++ b/src/panic.md @@ -21,7 +21,7 @@ over panic behavior: > [!NOTE] > The standard library provides the capability to explicitly panic -> via the [`panic!` macro][macro-panic]. +> via the [`panic!` macro][panic!]. ## Unwinding @@ -49,8 +49,8 @@ just as if they had gone out of scope normally. > [!NOTE] > The standard library provides two mechanisms for recovering from a panic, -> [`catch_unwind`][fn-catch-unwind] (which enables recovery within the -> panicking thread) and [`thread::spawn`][thread-spawn] (which automatically +> [`std::panic::catch_unwind`] (which enables recovery within the +> panicking thread) and [`std::thread::spawn`] (which automatically > sets up panic recovery for the spawned thread so that other threads may > continue running). @@ -77,11 +77,11 @@ Unwinding with the wrong ABI is undefined behavior: r[panic.unwind.ffi.catch-foreign] Catching a foreign unwinding operation (such as a C++ exception) using -`catch_unwind`, `JoinHandle::join`, or by letting it propagate all the way to a +[`std::panic::catch_unwind`], [`std::thread::JoinHandle::join`], or by letting it propagate all the way to a Rust `main()` function will have one of two behaviors, and it is unspecified which will occur: * The process aborts. -* The function returns a `Result::Err` containing an opaque type. +* The function returns a [`Result::Err`] containing an opaque type. > [!NOTE] > Rust code compiled or linked with a different instance of the Rust runtime counts as a @@ -120,8 +120,5 @@ See also the [`panic_handler` attribute](runtime.md#the-panic_handler-attribute) [array indexing]: expressions/array-expr.md#array-and-slice-indexing-expressions [destructors]: destructors.md -[fn-catch-unwind]: ../std/panic/fn.catch_unwind.html -[macro-panic]: ../std/macro.panic.html [runtime]: runtime.md -[thread-spawn]: ../std/thread/fn.spawn.html [unwind-abi]: items/functions.md#unwinding From 860691e191d4f53ed794aa2ea885dc590fa7e965 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 9 Jan 2025 16:26:00 -0800 Subject: [PATCH 13/42] Fix misspelled word I believe this is what was intended. --- src/panic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panic.md b/src/panic.md index 8edb52ab3..38ccb0603 100644 --- a/src/panic.md +++ b/src/panic.md @@ -91,7 +91,7 @@ which will occur: > library, may cause the entire application to crash even if the library is only > used within a child thread. -r[panic.unwind.ffi.dipose-panic] +r[panic.unwind.ffi.dispose-panic] There are currently no guarantees about the behavior that occurs when a foreign runtime attempts to dispose of, or rethrow, a Rust `panic` payload. In other words, an unwind originated from a Rust runtime must either lead to termination From 543bc27accc3788c9f96e3d489c59b7d4de02c74 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 14 Jan 2025 08:32:51 -0800 Subject: [PATCH 14/42] Remove vectorcall-unwind, it is unstable https://github.com/rust-lang/rust/blob/c074d8eee765cfd64e6e143d2894c85c7f3ddc1d/compiler/rustc_abi/src/extern_abi/mod.rs#L214-L217 --- src/items/external-blocks.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/items/external-blocks.md b/src/items/external-blocks.md index ae702200a..4056030e2 100644 --- a/src/items/external-blocks.md +++ b/src/items/external-blocks.md @@ -162,7 +162,6 @@ Like `"C"` and `"system"`, most platform-specific ABI strings also have a * `"cdecl-unwind"` * `"stdcall-unwind"` * `"fastcall-unwind"` -* `"vectorcall-unwind"` * `"thiscall-unwind"` * `"aapcs-unwind"` * `"win64-unwind"` From 4bb02482bf078195cac5570b8ee9b022f2d7acf0 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 14 Jan 2025 08:33:26 -0800 Subject: [PATCH 15/42] Sort unwind list --- src/items/external-blocks.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/items/external-blocks.md b/src/items/external-blocks.md index 4056030e2..b30a20506 100644 --- a/src/items/external-blocks.md +++ b/src/items/external-blocks.md @@ -159,13 +159,13 @@ r[items.extern.abi.efiapi] Like `"C"` and `"system"`, most platform-specific ABI strings also have a [corresponding `-unwind` variant][unwind-behavior]; specifically, these are: +* `"aapcs-unwind"` * `"cdecl-unwind"` -* `"stdcall-unwind"` * `"fastcall-unwind"` +* `"stdcall-unwind"` +* `"sysv64-unwind"` * `"thiscall-unwind"` -* `"aapcs-unwind"` * `"win64-unwind"` -* `"sysv64-unwind"` r[items.extern.variadic] ## Variadic functions From 386b2f3e29afe65913429a146fbddb49121649ed Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 14 Jan 2025 08:39:40 -0800 Subject: [PATCH 16/42] Switch link for ABI to the list of ABIs This should probably be reworked in the future so the ABI chapter owns this information. But for now, it doesn't really say anything useful. --- src/behavior-considered-undefined.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/behavior-considered-undefined.md b/src/behavior-considered-undefined.md index a1fc8de1d..3ccf97977 100644 --- a/src/behavior-considered-undefined.md +++ b/src/behavior-considered-undefined.md @@ -257,7 +257,7 @@ reading uninitialized memory is permitted are inside `union`s and in "padding" [`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 +[abi]: items/external-blocks.md#abi [undef]: http://llvm.org/docs/LangRef.html#undefined-values [`target_feature`]: attributes/codegen.md#the-target_feature-attribute [`UnsafeCell`]: std::cell::UnsafeCell From c7a796107fbc54b0510535aeb89b8ad4d949ab20 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 14 Jan 2025 08:40:19 -0800 Subject: [PATCH 17/42] Tweak wording of "beyond rust main function" This is to make it clear that the unwind is going *past* it, not just into it. --- src/crates-and-source-files.md | 4 ++-- src/panic.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/crates-and-source-files.md b/src/crates-and-source-files.md index 915860960..dbae80fa9 100644 --- a/src/crates-and-source-files.md +++ b/src/crates-and-source-files.md @@ -126,8 +126,8 @@ r[crate.uncaught-foreign-unwinding] ### 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 +in Rust code compiled or linked with a different runtime) 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`. diff --git a/src/panic.md b/src/panic.md index 38ccb0603..b47891119 100644 --- a/src/panic.md +++ b/src/panic.md @@ -77,8 +77,8 @@ Unwinding with the wrong ABI is undefined behavior: r[panic.unwind.ffi.catch-foreign] Catching a foreign unwinding operation (such as a C++ exception) using -[`std::panic::catch_unwind`], [`std::thread::JoinHandle::join`], or by letting it propagate all the way to a -Rust `main()` function will have one of two behaviors, and it is unspecified +[`std::panic::catch_unwind`], [`std::thread::JoinHandle::join`], or by letting it propagate beyond the +Rust `main()` function or thread root will have one of two behaviors, and it is unspecified which will occur: * The process aborts. * The function returns a [`Result::Err`] containing an opaque type. From 20af0b1dc16be2418a9c5c47016a2e15d8af0bef Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 14 Jan 2025 08:40:40 -0800 Subject: [PATCH 18/42] Make sure a terminal rule is used for this definition --- src/linkage.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/linkage.md b/src/linkage.md index 94b58fa53..dba494357 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -278,6 +278,8 @@ Passing `rlib`s directly into your foreign linker is currently unsupported. ### Prohibited linkage and unwinding r[link.unwinding] + +r[link.unwinding.consistency] If you are *not* using `rustc` to link Rust files, you must take care to ensure that unwinding is handled consistently across the entire binary. This includes using `dlopen` or similar facilities where linking is done by the system runtime without `rustc` being involved. From da53531236dc150ddc8d4c83c522fb4a5558d9d4 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 14 Jan 2025 08:41:02 -0800 Subject: [PATCH 19/42] Remove historical note We generally don't document old behavior in the reference. --- src/items/functions.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/items/functions.md b/src/items/functions.md index 9fbc35d04..57fc2d784 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -298,9 +298,6 @@ r[items.fn.extern.abort] 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. -> [!NOTE] -> Prior to Rust 1.82, it is possible for only some of the destructors to execute, and not others. - For other considerations and limitations regarding unwinding across FFI boundaries, see the [relevant section in the Panic documentation][panic-ffi]. From 8fd82089e451a0089a40be3af64571dd4040f8b3 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 14 Jan 2025 08:41:49 -0800 Subject: [PATCH 20/42] Clarify that this is unspecified --- src/items/functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/items/functions.md b/src/items/functions.md index 57fc2d784..8d7ecd8e5 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -296,7 +296,7 @@ from the "Native unwind" column in the table. r[items.fn.extern.abort] 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. +up until the ABI boundary will run. It is unspecified which of those two behaviors will happen. For other considerations and limitations regarding unwinding across FFI boundaries, see the [relevant section in the Panic documentation][panic-ffi]. From 0f3c015e46c84bd89871c82337190f642649e077 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 14 Jan 2025 08:41:59 -0800 Subject: [PATCH 21/42] Fix rule name to follow style --- src/items/functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/items/functions.md b/src/items/functions.md index 8d7ecd8e5..020ecb7c5 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -257,7 +257,7 @@ let fptr: extern "C" fn() -> i32 = new_i32; r[items.fn.extern.unwind] -r[items.fn.extern.unwind.info] +r[items.fn.extern.unwind.intro] 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 From 63677c156f1d5b2fccc29fbbe1ffe50906f71f9a Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 14 Jan 2025 08:42:05 -0800 Subject: [PATCH 22/42] Add missing rule name --- src/items/external-blocks.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/items/external-blocks.md b/src/items/external-blocks.md index b30a20506..90421bd71 100644 --- a/src/items/external-blocks.md +++ b/src/items/external-blocks.md @@ -156,6 +156,7 @@ r[items.extern.abi.thiscall] r[items.extern.abi.efiapi] * `unsafe extern "efiapi"` -- The ABI used for [UEFI] functions. +r[items.extern.abi.platform-unwind-variants] Like `"C"` and `"system"`, most platform-specific ABI strings also have a [corresponding `-unwind` variant][unwind-behavior]; specifically, these are: From caa17fc461d1efebf3aaa3f9e7d7e4767f4824b4 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 14 Jan 2025 08:42:32 -0800 Subject: [PATCH 23/42] Reword the title of this section This is intended since there are multiple ways to suppress a destructor. --- src/destructors.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/destructors.md b/src/destructors.md index 4ac4d2773..ff4f969d4 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -426,8 +426,9 @@ let x = (&temp()).use_temp(); // ERROR r[destructors.forget] ## Not running destructors -### `forget` +### Manually preventing destructors +r[destructors.manually-preventing] [`std::mem::forget`] can be used to prevent the destructor of a variable from being run, and [`std::mem::ManuallyDrop`] provides a wrapper to prevent a variable or field from being dropped automatically. From 0b37538afc719c4f9af19d9455760106a7ea9188 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 14 Jan 2025 08:47:09 -0800 Subject: [PATCH 24/42] Unwrap the panic chapter Per the style guide, lines should not be wrapped. --- src/panic.md | 86 ++++++++++++---------------------------------------- 1 file changed, 20 insertions(+), 66 deletions(-) diff --git a/src/panic.md b/src/panic.md index b47891119..a752e306d 100644 --- a/src/panic.md +++ b/src/panic.md @@ -3,118 +3,72 @@ r[panic] r[panic.intro] -Rust provides a mechanism to prevent a function from returning normally, and -instead "panic," which is a response to an error condition that is typically -not expected to be recoverable within the context in which the error is -encountered. +Rust provides a mechanism to prevent a function from returning normally, and instead "panic," which is a response to an error condition that is typically not expected to be recoverable within the context in which the error is encountered. r[panic.lang-ops] -Some language constructs, such as out-of-bounds [array indexing], panic -automatically. +Some language constructs, such as out-of-bounds [array indexing], panic automatically. r[panic.control] -There are also language features that provide a level of control -over panic behavior: -* A [_panic runtime_](#panic-runtimes) defined how a panic is handled during - runtime. +There are also language features that provide a level of control over panic behavior: + +* A [_panic runtime_](#panic-runtimes) defined how a panic is handled during runtime. * [FFI ABIs](items/functions.md#unwinding) may alter how panics behave. > [!NOTE] -> The standard library provides the capability to explicitly panic -> via the [`panic!` macro][panic!]. +> The standard library provides the capability to explicitly panic via the [`panic!` macro][panic!]. ## Unwinding r[panic.unwind] r[panic.unwind.intro] -Panicking may either be recoverable or non-recoverable, though it can be -configured (via `panic=abort`) to always be non-recoverable. (The converse is -not true: `panic=unwind` does not guarantee that all panics are recoverable, -only that panicking via the `panic!` macro and similar standard library -mechanisms is recoverable.) +Panicking may either be recoverable or non-recoverable, though it can be configured (via `panic=abort`) to always be non-recoverable. (The converse is not true: `panic=unwind` does not guarantee that all panics are recoverable, only that panicking via the `panic!` macro and similar standard library mechanisms is recoverable.) r[panic.unwind.destruction] -When panic recovery occurs, the runtime "unwinds" Rust frames, just as C++'s `throw` unwinds C++ frames, until the panic reaches -the point of recovery (for instance at a thread boundary). This means that as -the panic traverses Rust frames, live objects in those frames that [implement -`Drop`][destructors] will have their `drop` methods called. Thus, when normal -execution resumes, no-longer-accessible objects will have been "cleaned up" -just as if they had gone out of scope normally. +When panic recovery occurs, the runtime "unwinds" Rust frames, just as C++'s `throw` unwinds C++ frames, until the panic reaches the point of recovery (for instance at a thread boundary). This means that as the panic traverses Rust frames, live objects in those frames that [implement `Drop`][destructors] will have their `drop` methods called. Thus, when normal execution resumes, no-longer-accessible objects will have been "cleaned up" just as if they had gone out of scope normally. > [!NOTE] -> As long as this guarantee of resource-cleanup is preserved, -> "unwinding" may be implemented without actually using the mechanism used by -> C++ for the target platform. +> As long as this guarantee of resource-cleanup is preserved, "unwinding" may be implemented without actually using the mechanism used by C++ for the target platform. > [!NOTE] -> The standard library provides two mechanisms for recovering from a panic, -> [`std::panic::catch_unwind`] (which enables recovery within the -> panicking thread) and [`std::thread::spawn`] (which automatically -> sets up panic recovery for the spawned thread so that other threads may -> continue running). +> The standard library provides two mechanisms for recovering from a panic, [`std::panic::catch_unwind`] (which enables recovery within the panicking thread) and [`std::thread::spawn`] (which automatically sets up panic recovery for the spawned thread so that other threads may continue running). ### Unwinding across FFI boundaries r[panic.unwind.ffi] r[panic.unwind.ffi.intro] -It is possible to unwind across FFI boundaries using an [appropriate ABI -declaration][unwind-abi]. While useful in certain cases, this creates unique -opportunities for undefined behavior, especially when multiple language runtimes -are involved. +It is possible to unwind across FFI boundaries using an [appropriate ABI declaration][unwind-abi]. While useful in certain cases, this creates unique opportunities for undefined behavior, especially when multiple language runtimes are involved. r[panic.unwind.ffi.undefined] Unwinding with the wrong ABI is undefined behavior: -* Causing an unwind into Rust code from a foreign function that was called via a - function declaration or pointer declared with a non-unwinding ABI, such as `"C"`, - `"system"`, etc. (For example, this case occurs when such a function written in - C++ throws an exception that is uncaught and propagates to Rust.) -* Calling a Rust `extern` function that unwinds (with `extern "C-unwind"` or - another ABI that permits unwinding) from a runtime that does not support. - unwinding, such as code compiled with GCC or Clang using `-fno-exceptions` +* Causing an unwind into Rust code from a foreign function that was called via a function declaration or pointer declared with a non-unwinding ABI, such as `"C"`, `"system"`, etc. (For example, this case occurs when such a function written in C++ throws an exception that is uncaught and propagates to Rust.) +* Calling a Rust `extern` function that unwinds (with `extern "C-unwind"` or another ABI that permits unwinding) from a runtime that does not support. unwinding, such as code compiled with GCC or Clang using `-fno-exceptions` r[panic.unwind.ffi.catch-foreign] -Catching a foreign unwinding operation (such as a C++ exception) using -[`std::panic::catch_unwind`], [`std::thread::JoinHandle::join`], or by letting it propagate beyond the -Rust `main()` function or thread root will have one of two behaviors, and it is unspecified -which will occur: +Catching a foreign unwinding operation (such as a C++ exception) using [`std::panic::catch_unwind`], [`std::thread::JoinHandle::join`], or by letting it propagate beyond the Rust `main()` function or thread root will have one of two behaviors, and it is unspecified which will occur: + * The process aborts. * The function returns a [`Result::Err`] containing an opaque type. > [!NOTE] -> Rust code compiled or linked with a different instance of the Rust runtime counts as a -> "foreign exception" for the purpose of this guarantee. Thus, a library that -> uses `panic!` and is linked against one version of the Rust standard library, -> invoked from an application that uses a different version of the standard -> library, may cause the entire application to crash even if the library is only -> used within a child thread. +> Rust code compiled or linked with a different instance of the Rust runtime counts as a "foreign exception" for the purpose of this guarantee. Thus, a library that uses `panic!` and is linked against one version of the Rust standard library, invoked from an application that uses a different version of the standard library, may cause the entire application to crash even if the library is only used within a child thread. r[panic.unwind.ffi.dispose-panic] -There are currently no guarantees about the behavior that occurs when a foreign -runtime attempts to dispose of, or rethrow, a Rust `panic` payload. In other -words, an unwind originated from a Rust runtime must either lead to termination -of the process or be caught by the same runtime. +There are currently no guarantees about the behavior that occurs when a foreign runtime attempts to dispose of, or rethrow, a Rust `panic` payload. In other words, an unwind originated from a Rust runtime must either lead to termination of the process or be caught by the same runtime. ## Panic runtimes r[panic.runtime] -The actual behavior and implementation of `panic!` is controlled by the _panic -runtime_. +The actual behavior and implementation of `panic!` is controlled by the _panic runtime_. > [!NOTE] -> The Rust standard library provides two panic runtimes: -> `panic_unwind` (which unwinds the stack and is potentially recoverable) and -> `panic_abort` (which aborts the process and is non-recoverable). The default -> runtime depends on the target platform, but is generally `panic_unwind` on -> platforms with native support for C++ exceptions. +> The Rust standard library provides two panic runtimes: `panic_unwind` (which unwinds the stack and is potentially recoverable) and `panic_abort` (which aborts the process and is non-recoverable). The default runtime depends on the target platform, but is generally `panic_unwind` on platforms with native support for C++ exceptions. > [!NOTE] -> When compiling code that is guaranteed to be linked to a non-recoverable panic -> runtime, the optimizer may assume that unwinding across Rust frames is -> impossible, which can result in both code-size and runtime speed improvements. +> When compiling code that is guaranteed to be linked to a non-recoverable panic runtime, the optimizer may assume that unwinding across Rust frames is impossible, which can result in both code-size and runtime speed improvements. See also the [`panic_handler` attribute](runtime.md#the-panic_handler-attribute) which can be used to change the behavior of panics. From eafe95daf91191903b58d5ed3d5500e930dd42cb Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 14 Jan 2025 09:37:08 -0800 Subject: [PATCH 25/42] Update rules to be with headers --- src/destructors.md | 5 ++--- src/items/functions.md | 3 +-- src/linkage.md | 3 +-- src/panic.md | 12 ++++-------- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/destructors.md b/src/destructors.md index ff4f969d4..301fc24a5 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -426,9 +426,9 @@ let x = (&temp()).use_temp(); // ERROR r[destructors.forget] ## Not running destructors +r[destructors.manually-preventing] ### Manually preventing destructors -r[destructors.manually-preventing] [`std::mem::forget`] can be used to prevent the destructor of a variable from being run, and [`std::mem::ManuallyDrop`] provides a wrapper to prevent a variable or field from being dropped automatically. @@ -436,9 +436,8 @@ 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 - r[destructors.process-termination] +### Process termination without unwinding There are some ways to terminate the process without [unwinding], in which case destructors will not be run. diff --git a/src/items/functions.md b/src/items/functions.md index 020ecb7c5..a9f6316d2 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -253,9 +253,8 @@ extern "C" fn new_i32() -> i32 { 0 } let fptr: extern "C" fn() -> i32 = new_i32; ``` -### Unwinding - r[items.fn.extern.unwind] +### Unwinding r[items.fn.extern.unwind.intro] Most ABI strings come in two variants, one with an `-unwind` suffix and one without. diff --git a/src/linkage.md b/src/linkage.md index dba494357..f17560b87 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -275,9 +275,8 @@ Passing `rlib`s directly into your foreign linker is currently unsupported. > Rust code compiled or linked with a different instance of the Rust runtime counts as a > "foreign code" for the purpose of this section. -### Prohibited linkage and unwinding - r[link.unwinding] +### Prohibited linkage and unwinding r[link.unwinding.consistency] If you are *not* using `rustc` to link Rust files, you must take care to ensure that unwinding is diff --git a/src/panic.md b/src/panic.md index a752e306d..b0c16f477 100644 --- a/src/panic.md +++ b/src/panic.md @@ -1,6 +1,5 @@ -# Panic - r[panic] +# Panic r[panic.intro] Rust provides a mechanism to prevent a function from returning normally, and instead "panic," which is a response to an error condition that is typically not expected to be recoverable within the context in which the error is encountered. @@ -17,9 +16,8 @@ There are also language features that provide a level of control over panic beha > [!NOTE] > The standard library provides the capability to explicitly panic via the [`panic!` macro][panic!]. -## Unwinding - r[panic.unwind] +## Unwinding r[panic.unwind.intro] Panicking may either be recoverable or non-recoverable, though it can be configured (via `panic=abort`) to always be non-recoverable. (The converse is not true: `panic=unwind` does not guarantee that all panics are recoverable, only that panicking via the `panic!` macro and similar standard library mechanisms is recoverable.) @@ -33,9 +31,8 @@ When panic recovery occurs, the runtime "unwinds" Rust frames, just as C++'s `th > [!NOTE] > The standard library provides two mechanisms for recovering from a panic, [`std::panic::catch_unwind`] (which enables recovery within the panicking thread) and [`std::thread::spawn`] (which automatically sets up panic recovery for the spawned thread so that other threads may continue running). -### Unwinding across FFI boundaries - r[panic.unwind.ffi] +### Unwinding across FFI boundaries r[panic.unwind.ffi.intro] It is possible to unwind across FFI boundaries using an [appropriate ABI declaration][unwind-abi]. While useful in certain cases, this creates unique opportunities for undefined behavior, especially when multiple language runtimes are involved. @@ -58,9 +55,8 @@ Catching a foreign unwinding operation (such as a C++ exception) using [`std::pa r[panic.unwind.ffi.dispose-panic] There are currently no guarantees about the behavior that occurs when a foreign runtime attempts to dispose of, or rethrow, a Rust `panic` payload. In other words, an unwind originated from a Rust runtime must either lead to termination of the process or be caught by the same runtime. -## Panic runtimes - r[panic.runtime] +## Panic runtimes The actual behavior and implementation of `panic!` is controlled by the _panic runtime_. From 5f21a1ad33330e237b9a1c0ceb3f2da56382abf3 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 14 Jan 2025 09:43:02 -0800 Subject: [PATCH 26/42] Apply suggestions from RalfJung --- src/destructors.md | 4 ++-- src/linkage.md | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/destructors.md b/src/destructors.md index 301fc24a5..8e445e406 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -426,8 +426,8 @@ let x = (&temp()).use_temp(); // ERROR r[destructors.forget] ## Not running destructors -r[destructors.manually-preventing] -### Manually preventing destructors +r[destructors.manually-suppressing] +### Manually suppressing destructors [`std::mem::forget`] can be used to prevent the destructor of a variable from being run, and [`std::mem::ManuallyDrop`] provides a wrapper to prevent a diff --git a/src/linkage.md b/src/linkage.md index f17560b87..bdca74232 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -278,10 +278,11 @@ Passing `rlib`s directly into your foreign linker is currently unsupported. r[link.unwinding] ### Prohibited linkage and unwinding -r[link.unwinding.consistency] +r[link.unwinding.intro] If you are *not* using `rustc` to link Rust files, you must take care to ensure that unwinding is -handled consistently across the entire binary. This includes using `dlopen` or similar facilities -where linking is done by the system runtime without `rustc` being involved. +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. In the following, +we define what exactly is meant by "handling unwinding consistently". r[link.unwinding.potential] A Rust artifact is called *potentially unwinding* if any of the following conditions is met: From 79cc53f0d2f358c697754b676fd3769e6f524bf6 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 15 Jan 2025 09:46:42 -0800 Subject: [PATCH 27/42] Move panic runtimes towards the top of the chapter The intent here is to introduce higher-level concepts first, and then go into the details of unwinding. --- src/panic.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/panic.md b/src/panic.md index b0c16f477..309bca9df 100644 --- a/src/panic.md +++ b/src/panic.md @@ -16,6 +16,19 @@ There are also language features that provide a level of control over panic beha > [!NOTE] > The standard library provides the capability to explicitly panic via the [`panic!` macro][panic!]. +r[panic.runtime] +## Panic runtimes + +The actual behavior and implementation of `panic!` is controlled by the _panic runtime_. + +> [!NOTE] +> The Rust standard library provides two panic runtimes: `panic_unwind` (which unwinds the stack and is potentially recoverable) and `panic_abort` (which aborts the process and is non-recoverable). The default runtime depends on the target platform, but is generally `panic_unwind` on platforms with native support for C++ exceptions. + +> [!NOTE] +> When compiling code that is guaranteed to be linked to a non-recoverable panic runtime, the optimizer may assume that unwinding across Rust frames is impossible, which can result in both code-size and runtime speed improvements. + +See also the [`panic_handler` attribute](runtime.md#the-panic_handler-attribute) which can be used to change the behavior of panics. + r[panic.unwind] ## Unwinding @@ -55,19 +68,6 @@ Catching a foreign unwinding operation (such as a C++ exception) using [`std::pa r[panic.unwind.ffi.dispose-panic] There are currently no guarantees about the behavior that occurs when a foreign runtime attempts to dispose of, or rethrow, a Rust `panic` payload. In other words, an unwind originated from a Rust runtime must either lead to termination of the process or be caught by the same runtime. -r[panic.runtime] -## Panic runtimes - -The actual behavior and implementation of `panic!` is controlled by the _panic runtime_. - -> [!NOTE] -> The Rust standard library provides two panic runtimes: `panic_unwind` (which unwinds the stack and is potentially recoverable) and `panic_abort` (which aborts the process and is non-recoverable). The default runtime depends on the target platform, but is generally `panic_unwind` on platforms with native support for C++ exceptions. - -> [!NOTE] -> When compiling code that is guaranteed to be linked to a non-recoverable panic runtime, the optimizer may assume that unwinding across Rust frames is impossible, which can result in both code-size and runtime speed improvements. - -See also the [`panic_handler` attribute](runtime.md#the-panic_handler-attribute) which can be used to change the behavior of panics. - [array indexing]: expressions/array-expr.md#array-and-slice-indexing-expressions [destructors]: destructors.md [runtime]: runtime.md From c38044da10f2b1830573e1b22295f3bfa4a9a043 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 15 Jan 2025 14:47:04 -0800 Subject: [PATCH 28/42] Add the concept of panic strategy This is intended to clearly define two concepts, the runtime versus the strategy. Although there is significant overlap between them (they are chosen using the same CLI flag after all), I think it is helpful to define them as separate concepts. --- src/linkage.md | 12 +++++++----- src/panic.md | 22 +++++++++++++++++++--- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/linkage.md b/src/linkage.md index bdca74232..bc124ecc0 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -286,8 +286,8 @@ we define what exactly is meant by "handling unwinding consistently". r[link.unwinding.potential] A Rust artifact is called *potentially unwinding* if any of the following conditions is met: -- The artifact is linked with [the `panic=unwind` runtime][panic-runtime]. -- The artifact contains a crate built with `-Cpanic=unwind` that makes a call +- The artifact is linked with [the `unwind` runtime][panic-runtime]. +- The artifact contains a crate built with the `unwind` [panic strategy] that makes a call to a function using a `-unwind` ABI. - 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 @@ -298,15 +298,15 @@ A Rust artifact is called *potentially unwinding* if any of the following condit > unwind. r[link.unwinding.prohibited] -If a Rust artifact is potentially unwinding, then all its crates must be built with `-Cpanic=unwind`. +If a Rust artifact is potentially unwinding, then all its crates must be built with the `unwind` [panic strategy]. > [!NOTE] -> This restriction can only be violated when mixing code with different `-C panic` flags +> This restriction can only be violated when mixing code with different [`-C panic`] flags > while also using a non-`rustc` linker. Most users to not have to be concerned about this. > [!NOTE] > To guarantee that a library will be sound (and linkable with `rustc`) -> regardless of the panic mode used at link-time, the [`ffi_unwind_calls` lint] +> 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. @@ -315,3 +315,5 @@ If a Rust artifact is potentially unwinding, then all its crates must be built w [configuration option]: conditional-compilation.md [panic-runtime]: panic.md#panic-runtimes [procedural macros]: procedural-macros.md +[panic strategy]: panic.md#panic-strategy +[`-C panic`]: ../rustc/codegen-options/index.html#panic diff --git a/src/panic.md b/src/panic.md index 309bca9df..938372594 100644 --- a/src/panic.md +++ b/src/panic.md @@ -19,24 +19,39 @@ There are also language features that provide a level of control over panic beha r[panic.runtime] ## Panic runtimes -The actual behavior and implementation of `panic!` is controlled by the _panic runtime_. +The actual behavior and implementation of a panic is controlled by the _panic runtime_. The panic runtime is a handler linked into the output which provides the necessary implementation for panicking. > [!NOTE] > The Rust standard library provides two panic runtimes: `panic_unwind` (which unwinds the stack and is potentially recoverable) and `panic_abort` (which aborts the process and is non-recoverable). The default runtime depends on the target platform, but is generally `panic_unwind` on platforms with native support for C++ exceptions. +> [!NOTE] +> The panic runtime can be chosen in `rustc` with the [`-C panic`] CLI flag when building any crate type except an rlib. + > [!NOTE] > When compiling code that is guaranteed to be linked to a non-recoverable panic runtime, the optimizer may assume that unwinding across Rust frames is impossible, which can result in both code-size and runtime speed improvements. See also the [`panic_handler` attribute](runtime.md#the-panic_handler-attribute) which can be used to change the behavior of panics. +r[panic.strategy] +## Panic strategy + +r[panic.strategy.intro] +The _panic strategy_ defines the kind of panic runtime that a crate is built to support. + +> [!NOTE] +> The panic strategy can be chosen in `rustc` with the [`-C panic`] CLI flag. + +r[panic.strategy.mixed] +When linking with the `unwind` runtime, all crates must be built with the `unwind` strategy. + r[panic.unwind] ## Unwinding r[panic.unwind.intro] -Panicking may either be recoverable or non-recoverable, though it can be configured (via `panic=abort`) to always be non-recoverable. (The converse is not true: `panic=unwind` does not guarantee that all panics are recoverable, only that panicking via the `panic!` macro and similar standard library mechanisms is recoverable.) +Panicking may either be recoverable or non-recoverable, though it can be configured (by choosing the `abort` panic runtime) to always be non-recoverable. (The converse is not true: the `unwind` runtime does not guarantee that all panics are recoverable, only that panicking via the `panic!` macro and similar standard library mechanisms is recoverable.) r[panic.unwind.destruction] -When panic recovery occurs, the runtime "unwinds" Rust frames, just as C++'s `throw` unwinds C++ frames, until the panic reaches the point of recovery (for instance at a thread boundary). This means that as the panic traverses Rust frames, live objects in those frames that [implement `Drop`][destructors] will have their `drop` methods called. Thus, when normal execution resumes, no-longer-accessible objects will have been "cleaned up" just as if they had gone out of scope normally. +When panic recovery occurs, the `unwind` runtime "unwinds" Rust frames, just as C++'s `throw` unwinds C++ frames, until the panic reaches the point of recovery (for instance at a thread boundary). This means that as the panic traverses Rust frames, live objects in those frames that [implement `Drop`][destructors] will have their `drop` methods called. Thus, when normal execution resumes, no-longer-accessible objects will have been "cleaned up" just as if they had gone out of scope normally. > [!NOTE] > As long as this guarantee of resource-cleanup is preserved, "unwinding" may be implemented without actually using the mechanism used by C++ for the target platform. @@ -72,3 +87,4 @@ There are currently no guarantees about the behavior that occurs when a foreign [destructors]: destructors.md [runtime]: runtime.md [unwind-abi]: items/functions.md#unwinding +[`-C panic`]: ../rustc/codegen-options/index.html#panic From 20bd38f377b0a30e58ebad916d0d0874820f5eac Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 15 Jan 2025 14:47:11 -0800 Subject: [PATCH 29/42] Fix typo --- src/linkage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linkage.md b/src/linkage.md index bc124ecc0..a15937a30 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -302,7 +302,7 @@ If a Rust artifact is potentially unwinding, then all its crates must be built w > [!NOTE] > This restriction can only be violated when mixing code with different [`-C panic`] flags -> while also using a non-`rustc` linker. Most users to not have to be concerned about this. +> while also using a non-`rustc` linker. Most users do not have to be concerned about this. > [!NOTE] > To guarantee that a library will be sound (and linkable with `rustc`) From f309b2fe8820ff459eaa77c5d07974fba1f4f63a Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 15 Jan 2025 14:53:43 -0800 Subject: [PATCH 30/42] Drop sentence about UB of how to violate the Rust runtime I felt like this sentence is making an over-bold statement. There are ways to violate the assumptions of the Rust runtime using Rust code. Usually that is only with `unsafe` code that is otherwise violating requirements, though there may still be some holes where it can be done in safe code (hopefully those holes continue to be plugged). I feel more comfortable without this, since the primary sentence (don't violate the runtime!) is enough on its own. --- src/behavior-considered-undefined.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/behavior-considered-undefined.md b/src/behavior-considered-undefined.md index 3ccf97977..98d4d834c 100644 --- a/src/behavior-considered-undefined.md +++ b/src/behavior-considered-undefined.md @@ -99,9 +99,8 @@ r[undefined.const-transmute-ptr2int] cast, e.g. by doing raw pointer casts or using a union. r[undefined.runtime] -* 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. +* Violating assumptions of the Rust runtime. 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 From 2617cd5e28a69abdfda66238f15cac8358a21834 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 16 Jan 2025 09:14:57 -0800 Subject: [PATCH 31/42] Add "panic" to "unwind runtime" Co-authored-by: Ralf Jung --- src/linkage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linkage.md b/src/linkage.md index a15937a30..da66cc6df 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -286,7 +286,7 @@ we define what exactly is meant by "handling unwinding consistently". r[link.unwinding.potential] A Rust artifact is called *potentially unwinding* if any of the following conditions is met: -- The artifact is linked with [the `unwind` runtime][panic-runtime]. +- The artifact is linked with [the `unwind` panic runtime][panic-runtime]. - The artifact contains a crate built with the `unwind` [panic strategy] that makes a call to a function using a `-unwind` ABI. - The artifact makes a `"Rust"` ABI call to code running in another Rust From d49c90c2374063e822b40ec8f0da9ea9f4e99db6 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 16 Jan 2025 09:18:08 -0800 Subject: [PATCH 32/42] Clarify optimization for abort strategy At least as it is today, rustc doesn't look at the runtime, but instead the strategy (`-Cpanic`) of the current crate to elide landing pads and such when generating object code. --- src/panic.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/panic.md b/src/panic.md index 938372594..28cfe1863 100644 --- a/src/panic.md +++ b/src/panic.md @@ -27,9 +27,6 @@ The actual behavior and implementation of a panic is controlled by the _panic ru > [!NOTE] > The panic runtime can be chosen in `rustc` with the [`-C panic`] CLI flag when building any crate type except an rlib. -> [!NOTE] -> When compiling code that is guaranteed to be linked to a non-recoverable panic runtime, the optimizer may assume that unwinding across Rust frames is impossible, which can result in both code-size and runtime speed improvements. - See also the [`panic_handler` attribute](runtime.md#the-panic_handler-attribute) which can be used to change the behavior of panics. r[panic.strategy] @@ -41,6 +38,9 @@ The _panic strategy_ defines the kind of panic runtime that a crate is built to > [!NOTE] > The panic strategy can be chosen in `rustc` with the [`-C panic`] CLI flag. +> [!NOTE] +> When compiling code with a non-recoverable panic strategy, the optimizer may assume that unwinding across Rust frames is impossible, which can result in both code-size and runtime speed improvements. + r[panic.strategy.mixed] When linking with the `unwind` runtime, all crates must be built with the `unwind` strategy. From 6eaa1fa31d473995a586139d6fe7d3577f96a83f Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 16 Jan 2025 09:46:02 -0800 Subject: [PATCH 33/42] Add another link for panic strategy --- src/conditional-compilation.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/conditional-compilation.md b/src/conditional-compilation.md index 6c2a505a4..95eafcaa0 100644 --- a/src/conditional-compilation.md +++ b/src/conditional-compilation.md @@ -304,7 +304,7 @@ r[cfg.panic] ### `panic` r[cfg.panic.general] -Key-value option set depending on the panic strategy. Note that more values may be added in the future. +Key-value option set depending on the [panic strategy]. Note that more values may be added in the future. r[cfg.panic.values] Example values: @@ -312,6 +312,8 @@ Example values: * `"abort"` * `"unwind"` +[panic strategy]: panic.md#panic-strategy + ## Forms of conditional compilation r[cfg.attr] From a7400020dcae2c7644f710608e706b03ecd53b79 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 16 Jan 2025 10:08:45 -0800 Subject: [PATCH 34/42] Normatively define the abort and unwind runtimes We specifically talk about `abort` and `unwind` already. Let's just go ahead and up-front define that there are two, and not be so hand-wavey about it. In theory the standard library could introduce additional runtimes, and it doesn't have a specific impact on the language per se. However, this is a blurred line where I think it is better to be clear about what there is. We can always revisit this later and adjust if things change. --- src/panic.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/panic.md b/src/panic.md index 28cfe1863..70a61f423 100644 --- a/src/panic.md +++ b/src/panic.md @@ -19,10 +19,18 @@ There are also language features that provide a level of control over panic beha r[panic.runtime] ## Panic runtimes +r[panic.runtime.intro] The actual behavior and implementation of a panic is controlled by the _panic runtime_. The panic runtime is a handler linked into the output which provides the necessary implementation for panicking. -> [!NOTE] -> The Rust standard library provides two panic runtimes: `panic_unwind` (which unwinds the stack and is potentially recoverable) and `panic_abort` (which aborts the process and is non-recoverable). The default runtime depends on the target platform, but is generally `panic_unwind` on platforms with native support for C++ exceptions. +r[panic.runtime.kinds] +The following panic runtimes are provided by the standard library: + +* `unwind` --- unwinds the stack and is potentially recoverable. +* `abort` ---- aborts the process and is non-recoverable. + +Not all targets may provide the `unwind` runtime. + +The default runtime depends on the target platform, but is generally `unwind` on platforms with native support for C++ exceptions. > [!NOTE] > The panic runtime can be chosen in `rustc` with the [`-C panic`] CLI flag when building any crate type except an rlib. @@ -39,7 +47,7 @@ The _panic strategy_ defines the kind of panic runtime that a crate is built to > The panic strategy can be chosen in `rustc` with the [`-C panic`] CLI flag. > [!NOTE] -> When compiling code with a non-recoverable panic strategy, the optimizer may assume that unwinding across Rust frames is impossible, which can result in both code-size and runtime speed improvements. +> When compiling code with the `abort` panic strategy, the optimizer may assume that unwinding across Rust frames is impossible, which can result in both code-size and runtime speed improvements. r[panic.strategy.mixed] When linking with the `unwind` runtime, all crates must be built with the `unwind` strategy. From f7cb25e91440bc2deabc5776f1e0c9921bd433e8 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 16 Jan 2025 10:31:22 -0800 Subject: [PATCH 35/42] Rework link.unwinding This reworks this section to change the emphasis on "using non-rustc linkers". To me, that seemed confusing, since this also applies to using rustc (just that rustc automatically validates and generates an error if it is validated). --- src/linkage.md | 10 ++++------ src/panic.md | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/linkage.md b/src/linkage.md index da66cc6df..ac187ec1d 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -279,10 +279,7 @@ r[link.unwinding] ### Prohibited linkage and unwinding r[link.unwinding.intro] -If you are *not* using `rustc` to link Rust files, 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. In the following, -we define what exactly is meant by "handling unwinding consistently". +Panic unwinding must be supported consistently across the binary per the following rules. r[link.unwinding.potential] A Rust artifact is called *potentially unwinding* if any of the following conditions is met: @@ -301,8 +298,9 @@ r[link.unwinding.prohibited] If a Rust artifact is potentially unwinding, then all its crates must be built with the `unwind` [panic strategy]. > [!NOTE] -> This restriction can only be violated when mixing code with different [`-C panic`] flags -> while also using a non-`rustc` linker. Most users do not have to be concerned about this. +> 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. > [!NOTE] > To guarantee that a library will be sound (and linkable with `rustc`) diff --git a/src/panic.md b/src/panic.md index 70a61f423..94bc4e671 100644 --- a/src/panic.md +++ b/src/panic.md @@ -49,8 +49,8 @@ The _panic strategy_ defines the kind of panic runtime that a crate is built to > [!NOTE] > When compiling code with the `abort` panic strategy, the optimizer may assume that unwinding across Rust frames is impossible, which can result in both code-size and runtime speed improvements. -r[panic.strategy.mixed] -When linking with the `unwind` runtime, all crates must be built with the `unwind` strategy. +> [!NOTE] +> See [link.unwinding] for restrictions on linking crates with different panic strategies. An implication is that crates built with the `unwind` strategy can use the `abort` runtime, but not vice-versa. r[panic.unwind] ## Unwinding From 34e0b68e3276c8aa1742448d1a4116d5a0ebec36 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 17 Jan 2025 07:24:01 -0800 Subject: [PATCH 36/42] Apply suggestions from RalfJung Co-authored-by: Ralf Jung --- src/linkage.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/linkage.md b/src/linkage.md index ac187ec1d..e10740796 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -279,7 +279,7 @@ r[link.unwinding] ### Prohibited linkage and unwinding r[link.unwinding.intro] -Panic unwinding must be supported consistently across the binary per the following rules. +Panic unwinding can only be used if the binary is built consistently according to the following rules. r[link.unwinding.potential] A Rust artifact is called *potentially unwinding* if any of the following conditions is met: @@ -296,8 +296,10 @@ A Rust artifact is called *potentially unwinding* if any of the following condit r[link.unwinding.prohibited] 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. > [!NOTE] +> 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. From 8f4dc2b89c20982f284e22857228fff89bf56eee Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 20 Jan 2025 16:31:06 -0800 Subject: [PATCH 37/42] Clarify function pointer variadics also support `-unwind` --- src/types/function-pointer.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types/function-pointer.md b/src/types/function-pointer.md index 640b3fcfc..37e7479ac 100644 --- a/src/types/function-pointer.md +++ b/src/types/function-pointer.md @@ -54,6 +54,8 @@ r[type.fn-pointer.constraint-variadic] Variadic parameters can only be specified with [`extern`] function types with the `"C"` or `"cdecl"` calling convention. +This also includes the corresponding [`-unwind` variants][items.fn.extern.unwind]. + r[type.fn-pointer.attributes] ## Attributes on function pointer parameters From 6e76b5cc60935f0eb0b6e6ac431469db204ba960 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 30 Jan 2025 08:48:02 -0800 Subject: [PATCH 38/42] Fix minor typo --- src/panic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panic.md b/src/panic.md index 94bc4e671..6725094f9 100644 --- a/src/panic.md +++ b/src/panic.md @@ -77,7 +77,7 @@ r[panic.unwind.ffi.undefined] Unwinding with the wrong ABI is undefined behavior: * Causing an unwind into Rust code from a foreign function that was called via a function declaration or pointer declared with a non-unwinding ABI, such as `"C"`, `"system"`, etc. (For example, this case occurs when such a function written in C++ throws an exception that is uncaught and propagates to Rust.) -* Calling a Rust `extern` function that unwinds (with `extern "C-unwind"` or another ABI that permits unwinding) from a runtime that does not support. unwinding, such as code compiled with GCC or Clang using `-fno-exceptions` +* Calling a Rust `extern` function that unwinds (with `extern "C-unwind"` or another ABI that permits unwinding) from a runtime that does not support unwinding, such as code compiled with GCC or Clang using `-fno-exceptions` r[panic.unwind.ffi.catch-foreign] Catching a foreign unwinding operation (such as a C++ exception) using [`std::panic::catch_unwind`], [`std::thread::JoinHandle::join`], or by letting it propagate beyond the Rust `main()` function or thread root will have one of two behaviors, and it is unspecified which will occur: From 70498862f93ed15d4f5ae46ecf8f764959c140df Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 10 Feb 2025 14:20:00 -0800 Subject: [PATCH 39/42] Simplify wording of standard ABI strings The exact number of strings isn't important (and likely to get out sync). --- src/items/external-blocks.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/items/external-blocks.md b/src/items/external-blocks.md index 90421bd71..5aad880bb 100644 --- a/src/items/external-blocks.md +++ b/src/items/external-blocks.md @@ -106,8 +106,7 @@ unsafe extern "stdcall" { } ``` r[items.extern.abi.standard] -There are five ABI strings which are cross-platform, and which all compilers -are guaranteed to support: +The following ABI strings are supported on all platforms: r[items.extern.abi.rust] * `unsafe extern "Rust"` -- The default ABI when you write a normal `fn foo()` in any From 003a8a4c5234e857dd988b9ac39307cd35531537 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 10 Feb 2025 14:36:31 -0800 Subject: [PATCH 40/42] Remove "panic runtime" and emphasize panic_handler more This is a general rework to avoid using the term "panic runtime" (and "panic mode"), since it is poorly defined, potentially confusing, and potentially exposes some internal terminology and details that we may not want to expose. The general outline here is: - Remove the "panic runtime" section. - Move the `panic_handler` attribute to the Panic chapter. - The `panic_handler` attribute text has some editorial changes, and the rule names have changed. - The "Standard behavior" section for `panic_handler` has been reworked, incorporating some of the content from the old panic runtime section. - Added panic.panic_handler.std.no_std - Reword sentences that refer to "runtime", usually pointing to the "handler" instead. Note that there are a few subtle cases where this is not absolutely true. --- src/attributes.md | 2 +- src/crates-and-source-files.md | 2 +- src/destructors.md | 3 +- src/items/functions.md | 4 +- src/linkage.md | 3 +- src/panic.md | 90 +++++++++++++++++++++++++++------- src/runtime.md | 79 ++++++----------------------- 7 files changed, 91 insertions(+), 92 deletions(-) diff --git a/src/attributes.md b/src/attributes.md index af3285edc..6d286c60d 100644 --- a/src/attributes.md +++ b/src/attributes.md @@ -370,7 +370,7 @@ The following is an index of all built-in attributes. [`no_mangle`]: abi.md#the-no_mangle-attribute [`no_std`]: names/preludes.md#the-no_std-attribute [`non_exhaustive`]: attributes/type_system.md#the-non_exhaustive-attribute -[`panic_handler`]: runtime.md#the-panic_handler-attribute +[`panic_handler`]: panic.md#the-panic_handler-attribute [`path`]: items/modules.md#the-path-attribute [`proc_macro_attribute`]: procedural-macros.md#attribute-macros [`proc_macro_derive`]: procedural-macros.md#derive-macros diff --git a/src/crates-and-source-files.md b/src/crates-and-source-files.md index dbae80fa9..80ae41aca 100644 --- a/src/crates-and-source-files.md +++ b/src/crates-and-source-files.md @@ -126,7 +126,7 @@ r[crate.uncaught-foreign-unwinding] ### 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) propagates beyond +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 diff --git a/src/destructors.md b/src/destructors.md index 8e445e406..85c513647 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -444,7 +444,7 @@ 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 +[panic handler][panic.panic_handler.std] 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 @@ -462,7 +462,6 @@ destructors up until the ABI boundary will run. [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 diff --git a/src/items/functions.md b/src/items/functions.md index a9f6316d2..f48d6acfb 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -259,7 +259,7 @@ r[items.fn.extern.unwind] r[items.fn.extern.unwind.intro] 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 +choice of ABI, together with the runtime [panic handler], determines the behavior when unwinding out of a function. r[items.fn.extern.unwind.behavior] @@ -301,7 +301,7 @@ 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 handler]: ../panic.md#the-panic_handler-attribute [panic-ffi]: ../panic.md#unwinding-across-ffi-boundaries [panicking]: ../panic.md [undefined behavior]: ../behavior-considered-undefined.md diff --git a/src/linkage.md b/src/linkage.md index e10740796..5751ce817 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -283,7 +283,7 @@ Panic unwinding can only be used if the binary is built consistently according t r[link.unwinding.potential] A Rust artifact is called *potentially unwinding* if any of the following conditions is met: -- The artifact is linked with [the `unwind` panic runtime][panic-runtime]. +- The artifact uses the [`unwind` panic handler][panic.panic_handler]. - The artifact contains a crate built with the `unwind` [panic strategy] that makes a call to a function using a `-unwind` ABI. - The artifact makes a `"Rust"` ABI call to code running in another Rust @@ -313,7 +313,6 @@ Otherwise, unwinding can cause undefined behavior. [`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 [panic strategy]: panic.md#panic-strategy [`-C panic`]: ../rustc/codegen-options/index.html#panic diff --git a/src/panic.md b/src/panic.md index 6725094f9..3bac38482 100644 --- a/src/panic.md +++ b/src/panic.md @@ -10,56 +10,104 @@ Some language constructs, such as out-of-bounds [array indexing], panic automati r[panic.control] There are also language features that provide a level of control over panic behavior: -* A [_panic runtime_](#panic-runtimes) defined how a panic is handled during runtime. +* A [_panic handler_][panic handler] defines the behavior of a panic. * [FFI ABIs](items/functions.md#unwinding) may alter how panics behave. > [!NOTE] > The standard library provides the capability to explicitly panic via the [`panic!` macro][panic!]. -r[panic.runtime] -## Panic runtimes +r[panic.panic_handler] +## The `panic_handler` attribute -r[panic.runtime.intro] -The actual behavior and implementation of a panic is controlled by the _panic runtime_. The panic runtime is a handler linked into the output which provides the necessary implementation for panicking. +r[panic.panic_handler.intro] +The *`panic_handler` attribute* can be applied to a function to define the behavior of panics. -r[panic.runtime.kinds] -The following panic runtimes are provided by the standard library: +r[panic.panic_handler.allowed-positions] +The `panic_handler` attribute can only be applied to a function with signature `fn(&PanicInfo) -> !`. + +> [!NOTE] +> The [`PanicInfo`] struct contains information about the location of the panic. + +r[panic.panic_handler.unique] +There must be a single `panic_handler` function in the dependency graph. + +Below is shown a `panic_handler` function that logs the panic message and then halts the thread. + + +```rust,ignore +#![no_std] + +use core::fmt::{self, Write}; +use core::panic::PanicInfo; + +struct Sink { + // .. +# _0: (), +} +# +# impl Sink { +# fn new() -> Sink { Sink { _0: () }} +# } +# +# impl fmt::Write for Sink { +# fn write_str(&mut self, _: &str) -> fmt::Result { Ok(()) } +# } + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + let mut sink = Sink::new(); + + // logs "panicked at '$reason', src/main.rs:27:4" to some `sink` + let _ = writeln!(sink, "{}", info); + + loop {} +} +``` + +r[panic.panic_handler.std] +### Standard behavior + +r[panic.panic_handler.std.kinds] +`std` provides two different panic handlers: * `unwind` --- unwinds the stack and is potentially recoverable. * `abort` ---- aborts the process and is non-recoverable. -Not all targets may provide the `unwind` runtime. - -The default runtime depends on the target platform, but is generally `unwind` on platforms with native support for C++ exceptions. +Not all targets may provide the `unwind` handler. > [!NOTE] -> The panic runtime can be chosen in `rustc` with the [`-C panic`] CLI flag when building any crate type except an rlib. +> The panic handler used when linking with `std` can be set with the [`-C panic`] CLI flag. The default for most targets is `unwind`. +> +> The standard library's panic behavior can be modified at runtime with the [`std::panic::set_hook`] function. -See also the [`panic_handler` attribute](runtime.md#the-panic_handler-attribute) which can be used to change the behavior of panics. +r[panic.panic_handler.std.no_std] +Linking a [`no_std`] binary, dylib, cdylib, or staticlib will require specifying your own panic handler. r[panic.strategy] ## Panic strategy r[panic.strategy.intro] -The _panic strategy_ defines the kind of panic runtime that a crate is built to support. +The _panic strategy_ defines the kind of panic behavior that a crate is built to support. > [!NOTE] > The panic strategy can be chosen in `rustc` with the [`-C panic`] CLI flag. +> +> When generating a binary, dylib, cdylib, or staticlib and linking with `std`, the `-C panic` CLI flag also influences which [panic handler] is used. > [!NOTE] > When compiling code with the `abort` panic strategy, the optimizer may assume that unwinding across Rust frames is impossible, which can result in both code-size and runtime speed improvements. > [!NOTE] -> See [link.unwinding] for restrictions on linking crates with different panic strategies. An implication is that crates built with the `unwind` strategy can use the `abort` runtime, but not vice-versa. +> See [link.unwinding] for restrictions on linking crates with different panic strategies. An implication is that crates built with the `unwind` strategy can use the `abort` panic handler, but the `abort` strategy cannot use the `unwind` panic handler. r[panic.unwind] ## Unwinding r[panic.unwind.intro] -Panicking may either be recoverable or non-recoverable, though it can be configured (by choosing the `abort` panic runtime) to always be non-recoverable. (The converse is not true: the `unwind` runtime does not guarantee that all panics are recoverable, only that panicking via the `panic!` macro and similar standard library mechanisms is recoverable.) +Panicking may either be recoverable or non-recoverable, though it can be configured (by choosing a non-unwinding panic handler) to always be non-recoverable. (The converse is not true: the `unwind` handler does not guarantee that all panics are recoverable, only that panicking via the `panic!` macro and similar standard library mechanisms is recoverable.) r[panic.unwind.destruction] -When panic recovery occurs, the `unwind` runtime "unwinds" Rust frames, just as C++'s `throw` unwinds C++ frames, until the panic reaches the point of recovery (for instance at a thread boundary). This means that as the panic traverses Rust frames, live objects in those frames that [implement `Drop`][destructors] will have their `drop` methods called. Thus, when normal execution resumes, no-longer-accessible objects will have been "cleaned up" just as if they had gone out of scope normally. +When a panic occurs, the `unwind` handler "unwinds" Rust frames, just as C++'s `throw` unwinds C++ frames, until the panic reaches the point of recovery (for instance at a thread boundary). This means that as the panic traverses Rust frames, live objects in those frames that [implement `Drop`][destructors] will have their `drop` methods called. Thus, when normal execution resumes, no-longer-accessible objects will have been "cleaned up" just as if they had gone out of scope normally. > [!NOTE] > As long as this guarantee of resource-cleanup is preserved, "unwinding" may be implemented without actually using the mechanism used by C++ for the target platform. @@ -77,7 +125,7 @@ r[panic.unwind.ffi.undefined] Unwinding with the wrong ABI is undefined behavior: * Causing an unwind into Rust code from a foreign function that was called via a function declaration or pointer declared with a non-unwinding ABI, such as `"C"`, `"system"`, etc. (For example, this case occurs when such a function written in C++ throws an exception that is uncaught and propagates to Rust.) -* Calling a Rust `extern` function that unwinds (with `extern "C-unwind"` or another ABI that permits unwinding) from a runtime that does not support unwinding, such as code compiled with GCC or Clang using `-fno-exceptions` +* Calling a Rust `extern` function that unwinds (with `extern "C-unwind"` or another ABI that permits unwinding) from code that does not support unwinding, such as code compiled with GCC or Clang using `-fno-exceptions` r[panic.unwind.ffi.catch-foreign] Catching a foreign unwinding operation (such as a C++ exception) using [`std::panic::catch_unwind`], [`std::thread::JoinHandle::join`], or by letting it propagate beyond the Rust `main()` function or thread root will have one of two behaviors, and it is unspecified which will occur: @@ -86,13 +134,17 @@ Catching a foreign unwinding operation (such as a C++ exception) using [`std::pa * The function returns a [`Result::Err`] containing an opaque type. > [!NOTE] -> Rust code compiled or linked with a different instance of the Rust runtime counts as a "foreign exception" for the purpose of this guarantee. Thus, a library that uses `panic!` and is linked against one version of the Rust standard library, invoked from an application that uses a different version of the standard library, may cause the entire application to crash even if the library is only used within a child thread. +> Rust code compiled or linked with a different instance of the Rust standard library counts as a "foreign exception" for the purpose of this guarantee. Thus, a library that uses `panic!` and is linked against one version of the Rust standard library, invoked from an application that uses a different version of the standard library, may cause the entire application to abort even if the library is only used within a child thread. r[panic.unwind.ffi.dispose-panic] There are currently no guarantees about the behavior that occurs when a foreign runtime attempts to dispose of, or rethrow, a Rust `panic` payload. In other words, an unwind originated from a Rust runtime must either lead to termination of the process or be caught by the same runtime. +[`-C panic`]: ../rustc/codegen-options/index.html#panic +[`no_std`]: names/preludes.md#the-no_std-attribute +[`PanicInfo`]: core::panic::PanicInfo [array indexing]: expressions/array-expr.md#array-and-slice-indexing-expressions +[attribute]: attributes.md [destructors]: destructors.md +[panic handler]: #the-panic_handler-attribute [runtime]: runtime.md [unwind-abi]: items/functions.md#unwinding -[`-C panic`]: ../rustc/codegen-options/index.html#panic diff --git a/src/runtime.md b/src/runtime.md index 27d088878..986c1db88 100644 --- a/src/runtime.md +++ b/src/runtime.md @@ -3,66 +3,6 @@ r[runtime] This section documents features that define some aspects of the Rust runtime. -r[runtime.panic_handler] -## The `panic_handler` attribute - -r[runtime.panic_handler.allowed-positions] -The *`panic_handler` attribute* can only be applied to a function with signature -`fn(&PanicInfo) -> !`. - -r[runtime.panic_handler.intro] -The function marked with this [attribute] defines the behavior of panics. - -r[runtime.panic_handler.panic-info] -The [`PanicInfo`] struct contains information about the location of the panic. - -r[runtime.panic_handler.unique] -There must be a single `panic_handler` function in the dependency graph of a binary, dylib or cdylib crate. - -Below is shown a `panic_handler` function that logs the panic message and then halts the -thread. - - -```rust,ignore -#![no_std] - -use core::fmt::{self, Write}; -use core::panic::PanicInfo; - -struct Sink { - // .. -# _0: (), -} -# -# impl Sink { -# fn new() -> Sink { Sink { _0: () }} -# } -# -# impl fmt::Write for Sink { -# fn write_str(&mut self, _: &str) -> fmt::Result { Ok(()) } -# } - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - let mut sink = Sink::new(); - - // logs "panicked at '$reason', src/main.rs:27:4" to some `sink` - let _ = writeln!(sink, "{}", info); - - loop {} -} -``` - -r[runtime.panic_handler.std] -### Standard behavior - -The standard library provides an implementation of `panic_handler` that -defaults to unwinding the stack but that can be [changed to abort the -process][abort]. See [panic runtimes] for more details. - -The standard library's panic behavior can be modified at runtime with the -[`set_hook` function][set_hook]. - r[runtime.global_allocator] ## The `global_allocator` attribute @@ -98,11 +38,20 @@ display a console window on startup. It will run detached from any existing cons [_MetaNameValueStr_]: attributes.md#meta-item-attribute-syntax [`GlobalAlloc`]: alloc::alloc::GlobalAlloc -[`PanicInfo`]: core::panic::PanicInfo -[abort]: ../book/ch09-01-unrecoverable-errors-with-panic.html -[attribute]: attributes.md [crate types]: linkage.md -[panic runtimes]: panic.md#panic-runtimes -[set_hook]: std::panic::set_hook [static item]: items/static-items.md [subsystem]: https://msdn.microsoft.com/en-us/library/fcc1zstk.aspx + + From cdd00ded5952a9e658c212d3c250bf3e52e4d5d6 Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Sat, 15 Feb 2025 16:29:09 -0700 Subject: [PATCH 41/42] no unnecessary line-breaks in new text --- src/behavior-considered-undefined.md | 14 +++------ src/crates-and-source-files.md | 11 ++----- src/destructors.md | 15 +++------- src/items/external-blocks.md | 7 ++--- src/items/functions.md | 44 +++++++--------------------- src/linkage.md | 26 +++++----------- 6 files changed, 30 insertions(+), 87 deletions(-) diff --git a/src/behavior-considered-undefined.md b/src/behavior-considered-undefined.md index 98d4d834c..a11e67aec 100644 --- a/src/behavior-considered-undefined.md +++ b/src/behavior-considered-undefined.md @@ -77,9 +77,7 @@ r[undefined.target-feature] does not support (see [`target_feature`]), *except* if the platform explicitly documents this to be safe. r[undefined.call] -* 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). +* 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). r[undefined.invalid] * Producing an [invalid value][invalid-values]. "Producing" a @@ -99,13 +97,9 @@ r[undefined.const-transmute-ptr2int] cast, e.g. by doing raw pointer casts or using a union. r[undefined.runtime] -* Violating assumptions of the Rust runtime. 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`. +* Violating assumptions of the Rust runtime. 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 diff --git a/src/crates-and-source-files.md b/src/crates-and-source-files.md index 80ae41aca..64bff1616 100644 --- a/src/crates-and-source-files.md +++ b/src/crates-and-source-files.md @@ -125,21 +125,14 @@ use foo::bar as main; r[crate.uncaught-foreign-unwinding] ### Uncaught foreign unwinding -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`. +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`. For more information, see the [panic documentation][panic-docs]. r[crate.no_main] ### The `no_main` attribute -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`. +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`. r[crate.crate_name] ## The `crate_name` attribute diff --git a/src/destructors.md b/src/destructors.md index 85c513647..856e15d4b 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -274,8 +274,7 @@ 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][panic]. +operands to an expression breaks out of the expression, returns, or [panics][panic]. ```rust # struct PrintOnDrop(&'static str); @@ -439,17 +438,11 @@ variable or field from being dropped automatically. r[destructors.process-termination] ### Process termination without unwinding -There are some ways to terminate the process without [unwinding], in which case -destructors will not be run. +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 handler][panic.panic_handler.std] is set to `abort`, panicking will always terminate the process -without destructors being run. +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. -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. +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 diff --git a/src/items/external-blocks.md b/src/items/external-blocks.md index 5aad880bb..492621476 100644 --- a/src/items/external-blocks.md +++ b/src/items/external-blocks.md @@ -122,9 +122,7 @@ r[items.extern.abi.system] API itself r[items.extern.abi.unwind] -* `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). +* `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). r[items.extern.abi.platform] There are also some platform-specific ABI strings: @@ -156,8 +154,7 @@ r[items.extern.abi.efiapi] * `unsafe extern "efiapi"` -- The ABI used for [UEFI] functions. r[items.extern.abi.platform-unwind-variants] -Like `"C"` and `"system"`, most platform-specific ABI strings also have a -[corresponding `-unwind` variant][unwind-behavior]; specifically, these are: +Like `"C"` and `"system"`, most platform-specific ABI strings also have a [corresponding `-unwind` variant][unwind-behavior]; specifically, these are: * `"aapcs-unwind"` * `"cdecl-unwind"` diff --git a/src/items/functions.md b/src/items/functions.md index f48d6acfb..049eadd6d 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -257,34 +257,16 @@ r[items.fn.extern.unwind] ### Unwinding r[items.fn.extern.unwind.intro] -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. +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. r[items.fn.extern.unwind.behavior] -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. +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) | | -------------- | ------------ | ------------------------------------- | ----------------------- | @@ -294,11 +276,9 @@ from the "Native unwind" column in the table. | `panic=abort` | non-unwinding | `panic` aborts without unwinding | [undefined behavior] | r[items.fn.extern.abort] -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. +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. -For other considerations and limitations regarding unwinding across FFI -boundaries, see the [relevant section in the Panic documentation][panic-ffi]. +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 handler]: ../panic.md#the-panic_handler-attribute @@ -310,9 +290,7 @@ r[items.fn.const] ## Const functions r[items.fn.const.intro] -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]. +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]. r[items.fn.const.extern] Const functions may use the [`extern`] function qualifier. diff --git a/src/linkage.md b/src/linkage.md index 5751ce817..6d0a69761 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -272,8 +272,7 @@ binary link: Passing `rlib`s directly into your foreign linker is currently unsupported. > [!NOTE] -> Rust code compiled or linked with a different instance of the Rust runtime counts as a -> "foreign code" for the purpose of this section. +> Rust code compiled or linked with a different instance of the Rust runtime counts as "foreign code" for the purpose of this section. r[link.unwinding] ### Prohibited linkage and unwinding @@ -284,31 +283,20 @@ Panic unwinding can only be used if the binary is built consistently according t r[link.unwinding.potential] A Rust artifact is called *potentially unwinding* if any of the following conditions is met: - The artifact uses the [`unwind` panic handler][panic.panic_handler]. -- The artifact contains a crate built with the `unwind` [panic strategy] that makes a call - to a function using a `-unwind` ABI. -- 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. +- The artifact contains a crate built with the `unwind` [panic strategy] that makes a call to a function using a `-unwind` ABI. +- 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. > [!NOTE] -> This definition captures whether a `"Rust"` ABI call inside a Rust artifact can ever -> unwind. +> This definition captures whether a `"Rust"` ABI call inside a Rust artifact can ever unwind. r[link.unwinding.prohibited] -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. +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. > [!NOTE] -> 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. +> 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. > [!NOTE] -> 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. +> 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. [`cfg` attribute `target_feature` option]: conditional-compilation.md#target_feature [`ffi_unwind_calls` lint]: ../rustc/lints/listing/allowed-by-default.html#ffi-unwind-calls From ffc056c23f44bd13f6ca2b67c8aae5ea4c19733f Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Sun, 16 Feb 2025 12:08:56 -0700 Subject: [PATCH 42/42] fix link --- src/attributes/codegen.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attributes/codegen.md b/src/attributes/codegen.md index b9a24b8a7..5133d0bc3 100644 --- a/src/attributes/codegen.md +++ b/src/attributes/codegen.md @@ -132,7 +132,7 @@ r[attributes.codegen.target_feature.allowed-positions] The `#[target_feature]` attribute is not allowed on the following places: - [the `main` function][crate.main] -- a [`panic_handler` function][runtime.panic_handler] +- a [`panic_handler` function][panic.panic_handler] - safe trait methods - safe default functions in traits