-
Notifications
You must be signed in to change notification settings - Fork 475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
panic runtime and C-unwind documentation #1226
base: master
Are you sure you want to change the base?
Conversation
Hm... not sure how to fix the links to the newly-introduced page. Is there an index page I need to edit? Edit: I think I found it |
src/items/functions.md
Outdated
| panic runtime | ABI | `panic`-unwind | Unforced foreign unwind | | ||
| -------------- | ------------ | ------------------------------------- | ----------------------- | | ||
| `panic=unwind` | `"C-unwind"` | unwind | unwind | | ||
| `panic=unwind` | `"C"` | abort | UB | | ||
| `panic=abort` | `"C-unwind"` | `panic!` aborts | abort | | ||
| `panic=abort` | `"C"` | `panic!` aborts (no unwinding occurs) | UB | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| panic runtime | ABI | `panic`-unwind | Unforced foreign unwind | | |
| -------------- | ------------ | ------------------------------------- | ----------------------- | | |
| `panic=unwind` | `"C-unwind"` | unwind | unwind | | |
| `panic=unwind` | `"C"` | abort | UB | | |
| `panic=abort` | `"C-unwind"` | `panic!` aborts | abort | | |
| `panic=abort` | `"C"` | `panic!` aborts (no unwinding occurs) | UB | | |
| panic runtime | ABI | `panic`-unwind | Unforced foreign unwind | | |
| -------------- | ------------ | ------------------------------------- | ----------------------- | | |
| `panic=unwind` | `"C-unwind"` | unwind | unwind | | |
| `panic=unwind` | `"C"` | abort if unwinding reaches the function | UB if unwinding reaches the function | | |
| `panic=abort` | `"C-unwind"` | aborts immediately (no unwinding occurs) | abort if unwinding reaches the function | | |
| `panic=abort` | `"C"` | aborts immediately (no unwinding occurs) | UB if unwinding reaches the function | |
I found this a bit confusing. I believe there are subtle differences in terms of where the aborts occur and so forth. I have tried to clarify above, but I think it may be worth further clarifying.
It may also be worth adding some (perhaps non-normative) discussion of implementation:
- When compiling a function F with
panic=unwind
andextern "C"
, the compiler inserts unwinding guards for Rust panics that trigger an abort when unwinding reaches F.
I am also be misunderstanding what's going on. I was a bit surprised to see "UB" for unforced-foreign-unwind with C=unwind. I guess that this table is combining two scenarios:
- what happens when you call a C++ function declared as extern "C", and it unwinds (UB, we haven't compiled any guards)
- what happens when an
extern "C"
Rust function invokes some C++ function that throws (probably, in practice, an abort, but perhaps we have simplified to call it UB?)
Is that right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's only UB for a foreign function declared as extern "C"
to unwind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nbdd0121 what happens when an extern "C"
Rust function unwinds? I believe we insert an abort guard, but this table doesn't clarify that, right? Or maybe I don't understand what it's trying to convey. I'm imagining a scenario like
extern "C-unwind" fn throws();
extern "C" fn rust_fn() {
throws(); // unwinds
}
In this case, I presume you get an abort -- and I think we guarantee that? But the way I read this table, it would be listed as UB.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm....I don't know if the panic
abort guard would currently catch and abort in that case, or if it relies on the personality function to only abort on true Rust panic
s. I agree that the behavior in the table as-written is UB.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nbdd0121 what happens when an
extern "C"
Rust function unwinds? I believe we insert an abort guard, but this table doesn't clarify that, right? Or maybe I don't understand what it's trying to convey. I'm imagining a scenario likeextern "C-unwind" fn throws(); extern "C" fn rust_fn() { throws(); // unwinds }In this case, I presume you get an abort -- and I think we guarantee that? But the way I read this table, it would be listed as UB.
Unwinding out from extern "C"
functions (defined in either Rust or foreign language) is UB.
In the case you listed, we insert guard to prevent unwinding from actually leaving a Rust extern "C"
functions, therefore the function does not unwind, so UB is prevented; in this case we never unwinds out from a extern "C"
Rust functions.
If you define a extern "C-unwind"
Rust function and transmute it to extern "C"
and then call it, it's not UB if unwinding does not happen, and it's UB if unwinding happens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nikomatsakis With the change to the verbiage above, explaining that the table entries are specifically describing behavior at function boundaries, do you still want to make a change here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please check whether the notes I suggested to add under the table are correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nikomatsakis Can I resolve this comment thread now?
Sorry for the delay; I think I've addressed all comments. |
@tmandry @nikomatsakis I'm not sure you saw my comments & changes last week, but I think this is ready for re-review. |
Could you squash the commits? |
@nbdd0121 Can that be done on merge? I've heard that GitHub sometimes has trouble with PR branches that receive force-pushes. |
I think I've resolved all open questions and concerns. Is there anything else needed from me at the moment? |
This really needs rebasing now. |
e8c62b4
to
6e83797
Compare
@nbdd0121 Done! |
@tmandry two changes since your review:
|
Co-authored-by: Travis Cross <tc@traviscross.com>
This looks reasonable to me, though I'm also relying on the strength of the many earlier reviews. Last call for comments. Otherwise we'll get this merged in the coming days. |
…n foreign unwind causes process termination
6b624fe
to
e57a0cd
Compare
6d840d4
to
2c341e6
Compare
2c341e6
to
e8fd24d
Compare
No crate may be linked with [the `panic=abort` runtime][panic-runtime] if it has | ||
both of the following characteristics: | ||
|
||
* It contains a call to an `-unwind` foreign function or function pointer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This includes "Rust" foreign function calls, right? Like a separate copy of the Rust runtime, built with the same compiler and hence ABI compatible.
In the other file you carefully define unwinding and non-unwinding ABIs; can that be reused here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I'm not sure what you're asking with regard to runtimes. This is talking about the ABI strings C-unwind
, system-unwind
, etc; yes, the code being invoked may be Rust code.
What verbiage are you suggesting be reused? I don't think listing all the ABI strings seems necessary, but I can add a link to the functions.md
section explaining unwinding vs non-unwinding ABIs, with the caveat that the Rust ABI itself doesn't prevent linking crates with different panic strategies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm saying that this needs to also include extern "Rust" fn
foreign function calls, but the current wording does not seem to cover those functions. I'm not sure what is the best way to fix that, but functions.md
introduces the concept of an ""unwinding ABI" and that seems to also be the right concept here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, Rust calls are specifically excluded here; fn
is entirely equivalent to extern "Rust" fn
. So including extern "Rust"
here would effectively mean "it's never legal to link panic=abort
crates against panic=unwind
crates," which is clearly not true.
... possibly I am still misunderstanding the question?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. What makes "Rust" different from the other unwinding APIs?
IIRC the argument was that if we link in the abort runtime, Rust itself will never trigger unwinding, so if your program has no other "*-unwind" functions anywhere (meaning there's no foreign-language unwinding) then there can be no unwinding in general?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh, I thought I remembered how and why this rule was fashioned, but now that I'm thinking about it, I don't think I understand the problem it was intended to solve.
Part of what the "call to...foreign function" verbiage seems to be trying to capture is that the function call is actually linked to code from a language other than Rust, e.g. C++; because indeed, if that's the case, all bets are off about whether an unwind will occur or not. But I think that would apply equally to an opaque binary shared-object that happens to have been compiled from Rust code and linked against the panic=unwind
runtime (so that in the final program, both panic=unwind
and panic=abort
runtimes will be used).
If you compile with panic=unwind
and you use extern "C"
(or other non-unwinding calls) everywhere that you call into the other language runtime (be that C++, Rust, or something else), then you're safe: the panic=unwind
crate has shims at each extern "C"
boundary that will catch a panic and abort before it "escapes" into any stack frames compiled with panic=abort
. But the extern "C-unwind"
boundaries will have no such shims.
So I guess maybe extern "Rust"
isn't an exception after all, if it's actually used to call into an opaque shared object that might unwind; but is that even possible? Can you use an extern "Rust"
call as an entrypoint into a "foreign" binary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually that may simply not be allowed? (Though that should probably be explicit.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we allow this when both sides were built with the exact same Rust compiler. But maybe we should then also demand that the same -Cpanic
was used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pure-Rust case is okay even if one object was built with unwind=abort
and the other with unwind=panic
, as long as the only panic runtime linked is the abort
runtime. This should be ensured even without Cargo just by duplicate-symbol linker errors. (And I'm pretty sure that compiling for panic=abort
and then using the panic=unwind
runtime is impossible with rustc
, and if it were somehow managed, panicking would certainly be UB.)
So I think this additional restriction really does only matter in the case as described, where there's only one Rust panic runtime, but a different language, such as C++, may introduce panics; in this case, you're safe only if all the Rust stack frames that a C++ exception might touch are compiled with exception support. The phrasing in the PR ensures this because extern "C"
will safely abort if a C++ exception comes through, if that frame was compiled with panic=unwind
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be ensured even without Cargo just by duplicate-symbol linker errors.
How that?
And I'm pretty sure that compiling for panic=abort and then using the panic=unwind runtime is impossible with rustc
Why is it impossible? What happens if I build one crate (type lib
) with -Cpanic=abort
, and then include it in a bin
crate with -Cpanic=unind
? Do we have a test for that.
78717a6
to
d53e2c1
Compare
Tracking issue: rust-lang/rust#74990