or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Do you want to remove this version name and description?
Syncing
xxxxxxxxxx
Cleanup behaviour when panic happens with no-unwind function frames
The question is what's the behaviour for destructors of local variables on stack frames when a panic happens, with no-unwind function frames, e.g.
extern "C"
or#[rustc_nounwind]
. Additionally, the same MIR construct controls the behaviour when unwinding past cleanup blocks.Note that when this scenario happens, if none of the destructors diverge, the process would be terminated with a no-unwind panic, similar to
std::terminate
in C++. The discussion is about what destructors are to be executed before eventual process termination.Example snippet:
Another case where it's not allowed to unwind past is cleanup blocks, which we use similar MIR construct today:
Current Behaviour
When a panic happens with a
extern "C"
frame on stack:extern "C"
frame (all destructors of the unwound frames are executed).extern "C"
frame is not executed.This means that with the example above,
foo
is printed but notbar
.When a panic happens within cleanup (nested panic):
This means that with the cleanup example above,
foo
is printed but notbaz
.This behaviour is similar to perform a manual
catch_unwind
for each function call in such functions:C++ Behaviour
Details here: https://github.com/rust-lang/rust/issues/123231#issuecomment-2028182949
Summary: In C++, it is not specified on how many desturctors will be executed when an unwinding will eventually terminate due to an
noexcept
frame.Implementation-wise, when optimisation is not enabled, both GCC and Clang have similar behaviour to Rust today. Notably, Clang codegen as if all functions are called within a try-catch block with
std::terminate
called in the catch block. A C++ equivalent of the above Rust example would showfoo
being printed only.GCC codegen this differently, instead omitting the callsite from the unwind action table, and this is interpreted by the personality function to terminate the process. When optimisation is enabled, GCC can propagate this to inlined callee and mark additional callsites in the callee frames as unwind-terminate, and therefore skipping additional destructors. The G++ equivalent of the above Rust example might print neither
foo
norbar
.Possible Behaviours
If foreign frames are present, it is difficult if not impossible for Rust to decide their behaviour. For example, if Rust panic traverses a C++ noexcept frame, it's up to the personality function of the C++ frame to decide what to happen.
In Itanium ABI, 2-phase unwinding exists, so if an exception is not to be caught, then the unwind would fail to initiate and none of the frames would be unwound and therefore no destructors are to be executed. This would make option 2 mentioned below more preferrable IMO because otherwise we may have a weird case where switching from
extern "C-unwind"
toextern "C"
causes more frames to be unwound and more destructor to be executed.Option -1
Specify that it's unspecified if and what destructors are to be executed before termination.
Option 0
Specify that function calls inside
extern "C"
functions act liketry {...} catch (...) { terminate(); }
.This codifies today's behaviour.
Option 1
Specify that a
extern "C"
frames to act liketry {...} catch (...) { terminate(); }
. This will mean that it will be treated as a catch frame.extern "C"
to this option.Ensures both
foo
/bar
are printed. How about cleanup case?Option 2
Specify that
extern "C"
frame introduces an nounwind context, and unwinding is disallowed within such contexts. Unwind contexts are reintroduced bycatch_unwind
function.panic!()
is to be treated aspanic_nounwind!()
, which aborts the process without executing any destructor.extern "C"
frames in SEH.Ensures neither
foo
/bar
are printed.Itanium implementation would be (without diagnostic improvement):
Option 1.5
Specify that both option 1 and option 2 are allowed, and leave the door open for future decision.
Ensures either
foo
/bar
are both printed and neither are.