diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 44de48a3ada51..297ef232a18f5 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -327,6 +327,9 @@ fn process_builtin_attrs( codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED } sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL, + sym::rustc_propagate_ffi_unwind => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::PROPAGATE_FFI_UNWIND + } sym::instruction_set => { codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr) } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index a2acac8b3045d..5615bbc1adcef 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1006,6 +1006,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, ), + rustc_attr!( + rustc_propagate_ffi_unwind, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, + ), rustc_attr!( rustc_allocator_zeroed_variant, Normal, template!(NameValueStr: "function"), ErrorPreceding, EncodeCrossCrate::Yes, diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index d47d811610a74..96938fa9773c6 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -190,6 +190,10 @@ bitflags::bitflags! { const NO_BUILTINS = 1 << 15; /// Marks foreign items, to make `contains_extern_indicator` cheaper. const FOREIGN_ITEM = 1 << 16; + /// `#[rustc_propagate_ffi_unwind]`: indicates that the function may leak unwinds from + /// invoked FFI functions regardless of the panic strategy and should always be considered + /// unwinding. + const PROPAGATE_FFI_UNWIND = 1 << 17; } } rustc_data_structures::external_bitflags_debug! { CodegenFnAttrFlags } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index eefb913a33fa4..e32a187d52278 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1219,10 +1219,15 @@ where #[tracing::instrument(level = "debug", skip(tcx))] pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option, abi: ExternAbi) -> bool { if let Some(did) = fn_def_id { - // Special attribute for functions which can't unwind. - if tcx.codegen_fn_attrs(did).flags.contains(CodegenFnAttrFlags::NEVER_UNWIND) { + // Special attributes for functions which can't unwind or unwind regardless of the panic + // strategy. + let flags = tcx.codegen_fn_attrs(did).flags; + if flags.contains(CodegenFnAttrFlags::NEVER_UNWIND) { return false; } + if flags.contains(CodegenFnAttrFlags::PROPAGATE_FFI_UNWIND) { + return true; + } // With `-C panic=abort`, all non-FFI functions are required to not unwind. // diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs index 7c66783548ea4..ca0d0b27cbed9 100644 --- a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs +++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs @@ -1,5 +1,6 @@ use rustc_abi::ExternAbi; use rustc_hir::def_id::{LOCAL_CRATE, LocalDefId}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::*; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::{self, TyCtxt, layout}; @@ -39,6 +40,12 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool { return false; } + // `#[rustc_propagate_ffi_unwind]` functions are assumed to be unwinding by callers, so their + // bodies don't have to be verified for soundness. + if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::PROPAGATE_FFI_UNWIND) { + return false; + } + let mut tainted = false; for block in body.basic_blocks.iter() { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 223d818a2949b..c590c8e338f2a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1950,6 +1950,7 @@ symbols! { rustc_private, rustc_proc_macro_decls, rustc_promotable, + rustc_propagate_ffi_unwind, rustc_pub_transparent, rustc_reallocator, rustc_regions, diff --git a/library/stdarch/crates/core_arch/src/wasm32/mod.rs b/library/stdarch/crates/core_arch/src/wasm32/mod.rs index 01bf0a71658b8..c9b53171d77f6 100644 --- a/library/stdarch/crates/core_arch/src/wasm32/mod.rs +++ b/library/stdarch/crates/core_arch/src/wasm32/mod.rs @@ -191,16 +191,8 @@ unsafe extern "C-unwind" { // #[cfg_attr(test, assert_instr(throw, TAG = 0, ptr = core::ptr::null_mut()))] #[inline] #[unstable(feature = "wasm_exception_handling_intrinsics", issue = "122465")] -// FIXME: Since this instruction unwinds, `core` built with `-C panic=unwind` -// cannot be linked with `-C panic=abort` programs. But that's not -// entirely supported anyway, because runtimes without EH support won't -// be able to handle `try` blocks in `-C panic=unwind` crates either. -// We ship `-C panic=abort` `core`, so this doesn't affect users -// directly. Resolving this will likely require patching out both `try` -// and `throw` instructions, at which point we can look into whitelisting -// this function in the compiler to allow linking. -// See https://github.com/rust-lang/rust/issues/118168. -#[allow(ffi_unwind_calls)] +// Allow `core` built with `-C panic=unwind` to be linked into `-C panic=abort` programs. +#[rustc_propagate_ffi_unwind] pub unsafe fn throw(ptr: *mut u8) -> ! { static_assert!(TAG == 0); // LLVM only supports tag 0 == C++ right now. wasm_throw(TAG, ptr) diff --git a/tests/codegen-llvm/unwind-abis/propagate-ffi-unwind.rs b/tests/codegen-llvm/unwind-abis/propagate-ffi-unwind.rs new file mode 100644 index 0000000000000..5f0a07f01cbc2 --- /dev/null +++ b/tests/codegen-llvm/unwind-abis/propagate-ffi-unwind.rs @@ -0,0 +1,24 @@ +//@ compile-flags: -C panic=abort -Cno-prepopulate-passes + +// Test that Rustic `#[rustc_propagate_ffi_unwind]` functions are considered unwinding even under +// `-C panic=abort`. We disable optimizations to make sure LLVM doesn't infer attributes. + +#![feature(rustc_attrs)] +#![crate_type = "lib"] + +// CHECK: @caller() unnamed_addr [[ATTR0:#[0-9]+]] +#[no_mangle] +pub fn caller() { + // CHECK: call void @{{.*core9panicking19panic_cannot_unwind}} + may_unwind(); +} + +// This function would typically be in a different crate. +// CHECK: @may_unwind() unnamed_addr [[ATTR1:#[0-9]+]] +#[no_mangle] +#[rustc_propagate_ffi_unwind] +#[inline(never)] +pub fn may_unwind() {} + +// CHECK: attributes [[ATTR0]] = { {{.*}}nounwind{{.*}} } +// CHECK-NOT: attributes [[ATTR1]] = { {{.*}}nounwind{{.*}} } diff --git a/tests/ui/unwind-abis/ffi-unwind-calls-lint.rs b/tests/ui/unwind-abis/ffi-unwind-calls-lint.rs index e05c5ded10d09..dc66be00b1dae 100644 --- a/tests/ui/unwind-abis/ffi-unwind-calls-lint.rs +++ b/tests/ui/unwind-abis/ffi-unwind-calls-lint.rs @@ -1,6 +1,7 @@ //@ build-pass //@ needs-unwind +#![feature(rustc_attrs)] #![warn(ffi_unwind_calls)] mod foo { @@ -16,10 +17,23 @@ fn main() { // Call to Rust function is fine. foo::foo(); // Call to foreign function should warn. - unsafe { foo(); } + unsafe { + foo(); + } //~^ WARNING call to foreign function with FFI-unwind ABI let ptr: extern "C-unwind" fn() = foo::foo; // Call to function pointer should also warn. ptr(); //~^ WARNING call to function pointer with FFI-unwind ABI } + +#[rustc_propagate_ffi_unwind] +fn f() { + // Call to foreign function or a function pointer from within a `#[rustc_propagate_ffi_unwind]` + // function is fine. + unsafe { + foo(); + } + let ptr: extern "C-unwind" fn() = foo::foo; + ptr(); +} diff --git a/tests/ui/unwind-abis/ffi-unwind-calls-lint.stderr b/tests/ui/unwind-abis/ffi-unwind-calls-lint.stderr index cf8a7782e35ee..ef9a2cb379966 100644 --- a/tests/ui/unwind-abis/ffi-unwind-calls-lint.stderr +++ b/tests/ui/unwind-abis/ffi-unwind-calls-lint.stderr @@ -1,8 +1,8 @@ warning: call to foreign function with FFI-unwind ABI - --> $DIR/ffi-unwind-calls-lint.rs:19:14 + --> $DIR/ffi-unwind-calls-lint.rs:20:9 | -LL | unsafe { foo(); } - | ^^^^^ call to foreign function with FFI-unwind ABI +LL | foo(); + | ^^^^^ call to foreign function with FFI-unwind ABI | note: the lint level is defined here --> $DIR/ffi-unwind-calls-lint.rs:4:9 @@ -11,7 +11,7 @@ LL | #![warn(ffi_unwind_calls)] | ^^^^^^^^^^^^^^^^ warning: call to function pointer with FFI-unwind ABI - --> $DIR/ffi-unwind-calls-lint.rs:23:5 + --> $DIR/ffi-unwind-calls-lint.rs:25:5 | LL | ptr(); | ^^^^^ call to function pointer with FFI-unwind ABI diff --git a/tests/ui/wasm/wasm-link-to-panic-abort-issue-148246.rs b/tests/ui/wasm/wasm-link-to-panic-abort-issue-148246.rs new file mode 100644 index 0000000000000..860fb18a726f6 --- /dev/null +++ b/tests/ui/wasm/wasm-link-to-panic-abort-issue-148246.rs @@ -0,0 +1,7 @@ +//@ only-wasm32 +//@ compile-flags: -C panic=abort +//@ build-pass + +// Test that a `-C panic=abort` binary crate can link to a `-C panic=unwind` core. + +fn main() {}