rustc: Fill out remaining parts of C-unwind ABI
This commit intends to fill out some of the remaining pieces of the C-unwind ABI. This has a number of other changes with it though to move this design space forward a bit. Notably contained within here is: * On `panic=unwind`, the `extern "C"` ABI is now considered as "may unwind". This fixes a longstanding soundness issue where if you `panic!()` in an `extern "C"` function defined in Rust that's actually UB because the LLVM representation for the function has the `nounwind` attribute, but then you unwind. * Whether or not a function unwinds now mainly considers the ABI of the function instead of first checking the panic strategy. This fixes a miscompile of `extern "C-unwind"` with `panic=abort` because that ABI can still unwind. * The aborting stub for non-unwinding ABIs with `panic=unwind` has been reimplemented. Previously this was done as a small tweak during MIR generation, but this has been moved to a separate and dedicated MIR pass. This new pass will, for appropriate functions and function calls, insert a `cleanup` landing pad for any function call that may unwind within a function that is itself not allowed to unwind. Note that this subtly changes some behavior from before where previously on an unwind which was caught-to-abort it would run active destructors in the function, and now it simply immediately aborts the process. * The `#[unwind]` attribute has been removed and all users in tests and such are now using `C-unwind` and `#![feature(c_unwind)]`. I think this is largely the last piece of the RFC to implement. Unfortunately I believe this is still not stabilizable as-is because activating the feature gate changes the behavior of the existing `extern "C"` ABI in a way that has no replacement. My thinking for how to enable this is that we add support for the `C-unwind` ABI on stable Rust first, and then after it hits stable we change the behavior of the `C` ABI. That way anyone straddling stable/beta/nightly can switch to `C-unwind` safely.
This commit is contained in:
parent
2939249f29
commit
1c07096a45
46 changed files with 431 additions and 478 deletions
|
@ -2,7 +2,6 @@ use crate::build;
|
|||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::scope::DropKind;
|
||||
use crate::thir::pattern::pat_from_hir;
|
||||
use rustc_attr::{self as attr, UnwindAttr};
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
|
@ -19,7 +18,6 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults};
|
|||
use rustc_span::symbol::{kw, sym};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
|
||||
use super::lints;
|
||||
|
||||
|
@ -581,60 +579,6 @@ macro_rules! unpack {
|
|||
}};
|
||||
}
|
||||
|
||||
fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, abi: Abi) -> bool {
|
||||
// Validate `#[unwind]` syntax regardless of platform-specific panic strategy.
|
||||
let attrs = &tcx.get_attrs(fn_def_id.to_def_id());
|
||||
let unwind_attr = attr::find_unwind_attr(&tcx.sess, attrs);
|
||||
|
||||
// We never unwind, so it's not relevant to stop an unwind.
|
||||
if tcx.sess.panic_strategy() != PanicStrategy::Unwind {
|
||||
return false;
|
||||
}
|
||||
|
||||
match unwind_attr {
|
||||
// If an `#[unwind]` attribute was found, we should adhere to it.
|
||||
Some(UnwindAttr::Allowed) => false,
|
||||
Some(UnwindAttr::Aborts) => true,
|
||||
// If no attribute was found and the panic strategy is `unwind`, then we should examine
|
||||
// the function's ABI string to determine whether it should abort upon panic.
|
||||
None if tcx.features().c_unwind => {
|
||||
use Abi::*;
|
||||
match abi {
|
||||
// In the case of ABI's that have an `-unwind` equivalent, check whether the ABI
|
||||
// permits unwinding. If so, we should not abort. Otherwise, we should.
|
||||
C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
|
||||
!unwind
|
||||
}
|
||||
// Rust and `rust-call` functions are allowed to unwind, and should not abort.
|
||||
Rust | RustCall => false,
|
||||
// Other ABI's should abort.
|
||||
Cdecl
|
||||
| Fastcall
|
||||
| Vectorcall
|
||||
| Aapcs
|
||||
| Win64
|
||||
| SysV64
|
||||
| PtxKernel
|
||||
| Msp430Interrupt
|
||||
| X86Interrupt
|
||||
| AmdGpuKernel
|
||||
| EfiApi
|
||||
| AvrInterrupt
|
||||
| AvrNonBlockingInterrupt
|
||||
| CCmseNonSecureCall
|
||||
| Wasm
|
||||
| RustIntrinsic
|
||||
| PlatformIntrinsic
|
||||
| Unadjusted => true,
|
||||
}
|
||||
}
|
||||
// If the `c_unwind` feature gate is not active, follow the behavior that was in place
|
||||
// prior to #76570. This is a special case: some functions have a C ABI but are meant to
|
||||
// unwind anyway. Don't stop them.
|
||||
None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)`
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// the main entry point for building MIR for a function
|
||||
|
||||
|
@ -704,8 +648,7 @@ where
|
|||
}));
|
||||
let source_info = builder.source_info(fn_end);
|
||||
builder.cfg.terminate(return_block, source_info, TerminatorKind::Return);
|
||||
let should_abort = should_abort_on_panic(tcx, fn_def.did, abi);
|
||||
builder.build_drop_trees(should_abort);
|
||||
builder.build_drop_trees();
|
||||
return_block.unit()
|
||||
}));
|
||||
|
||||
|
@ -752,7 +695,7 @@ fn construct_const<'a, 'tcx>(
|
|||
let source_info = builder.source_info(span);
|
||||
builder.cfg.terminate(block, source_info, TerminatorKind::Return);
|
||||
|
||||
builder.build_drop_trees(false);
|
||||
builder.build_drop_trees();
|
||||
|
||||
builder.finish()
|
||||
}
|
||||
|
|
|
@ -1249,21 +1249,20 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
|
|||
}
|
||||
|
||||
/// Build the unwind and generator drop trees.
|
||||
crate fn build_drop_trees(&mut self, should_abort: bool) {
|
||||
crate fn build_drop_trees(&mut self) {
|
||||
if self.generator_kind.is_some() {
|
||||
self.build_generator_drop_trees(should_abort);
|
||||
self.build_generator_drop_trees();
|
||||
} else {
|
||||
Self::build_unwind_tree(
|
||||
&mut self.cfg,
|
||||
&mut self.scopes.unwind_drops,
|
||||
self.fn_span,
|
||||
should_abort,
|
||||
&mut None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn build_generator_drop_trees(&mut self, should_abort: bool) {
|
||||
fn build_generator_drop_trees(&mut self) {
|
||||
// Build the drop tree for dropping the generator while it's suspended.
|
||||
let drops = &mut self.scopes.generator_drops;
|
||||
let cfg = &mut self.cfg;
|
||||
|
@ -1281,7 +1280,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
|
|||
// Build the drop tree for unwinding in the normal control flow paths.
|
||||
let resume_block = &mut None;
|
||||
let unwind_drops = &mut self.scopes.unwind_drops;
|
||||
Self::build_unwind_tree(cfg, unwind_drops, fn_span, should_abort, resume_block);
|
||||
Self::build_unwind_tree(cfg, unwind_drops, fn_span, resume_block);
|
||||
|
||||
// Build the drop tree for unwinding when dropping a suspended
|
||||
// generator.
|
||||
|
@ -1296,26 +1295,20 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
|
|||
drops.entry_points.push((drop_data.1, blocks[drop_idx].unwrap()));
|
||||
}
|
||||
}
|
||||
Self::build_unwind_tree(cfg, drops, fn_span, should_abort, resume_block);
|
||||
Self::build_unwind_tree(cfg, drops, fn_span, resume_block);
|
||||
}
|
||||
|
||||
fn build_unwind_tree(
|
||||
cfg: &mut CFG<'tcx>,
|
||||
drops: &mut DropTree,
|
||||
fn_span: Span,
|
||||
should_abort: bool,
|
||||
resume_block: &mut Option<BasicBlock>,
|
||||
) {
|
||||
let mut blocks = IndexVec::from_elem(None, &drops.drops);
|
||||
blocks[ROOT_NODE] = *resume_block;
|
||||
drops.build_mir::<Unwind>(cfg, &mut blocks);
|
||||
if let (None, Some(resume)) = (*resume_block, blocks[ROOT_NODE]) {
|
||||
// `TerminatorKind::Abort` is used for `#[unwind(aborts)]`
|
||||
// functions.
|
||||
let terminator =
|
||||
if should_abort { TerminatorKind::Abort } else { TerminatorKind::Resume };
|
||||
|
||||
cfg.terminate(resume, SourceInfo::outermost(fn_span), terminator);
|
||||
cfg.terminate(resume, SourceInfo::outermost(fn_span), TerminatorKind::Resume);
|
||||
|
||||
*resume_block = blocks[ROOT_NODE];
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue