From 239f833ed1842ce7ce5c9989871a9ce9b1ea3546 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Sat, 21 Mar 2020 07:50:38 +0000 Subject: [PATCH] Abort when catch_unwind catches a foreign exception --- library/panic_abort/src/lib.rs | 11 ++ library/panic_unwind/src/dwarf/eh.rs | 16 +-- library/panic_unwind/src/emcc.rs | 46 +++--- library/panic_unwind/src/gcc.rs | 26 ++-- library/panic_unwind/src/lib.rs | 3 + library/panic_unwind/src/seh.rs | 12 +- library/std/src/panic.rs | 3 + library/std/src/panicking.rs | 8 ++ src/librustc_codegen_llvm/context.rs | 21 +++ src/librustc_codegen_llvm/intrinsic.rs | 136 +++++++++++++++--- src/librustc_hir/weak_lang_items.rs | 1 + src/librustc_middle/middle/lang_items.rs | 4 +- src/librustc_passes/weak_lang_items.rs | 3 + src/librustc_span/symbol.rs | 1 + .../auxiliary/panic-runtime-lang-items.rs | 2 + .../foreign-exceptions/foo.cpp | 6 +- .../foreign-exceptions/foo.rs | 21 ++- src/test/run-make-fulldeps/issue-69368/a.rs | 5 + .../const-eval/const_panic_libcore_main.rs | 2 + .../macros/macro-comma-behavior.core.stderr | 14 +- src/test/ui/macros/macro-comma-behavior.rs | 1 + .../ui/macros/macro-comma-behavior.std.stderr | 20 +-- src/test/ui/no_owned_box_lang_item.rs | 1 + .../auxiliary/panic-runtime-lang-items.rs | 2 + src/test/ui/range/issue-54505-no-std.rs | 3 + src/test/ui/range/issue-54505-no-std.stderr | 12 +- 26 files changed, 275 insertions(+), 105 deletions(-) diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index ccc067a3c943d..8ca25da54a6c8 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -117,6 +117,17 @@ pub mod personalities { 1 // `ExceptionContinueSearch` } + // Similar to above, this corresponds to the `eh_catch_typeinfo` lang item + // that's only used on Emscripten currently. + // + // Since panics don't generate exceptions and foreign exceptions are + // currently UB with -C panic=abort (although this may be subject to + // change), any catch_unwind calls will never use this typeinfo. + #[rustc_std_internal_symbol] + #[allow(non_upper_case_globals)] + #[cfg(target_os = "emscripten")] + static rust_eh_catch_typeinfo: [usize; 2] = [0; 2]; + // These two are called by our startup objects on i686-pc-windows-gnu, but // they don't need to do anything so the bodies are nops. #[rustc_std_internal_symbol] diff --git a/library/panic_unwind/src/dwarf/eh.rs b/library/panic_unwind/src/dwarf/eh.rs index 302478cfac8f5..8ce4dcd2acd9c 100644 --- a/library/panic_unwind/src/dwarf/eh.rs +++ b/library/panic_unwind/src/dwarf/eh.rs @@ -51,11 +51,7 @@ pub enum EHAction { pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm")); -pub unsafe fn find_eh_action( - lsda: *const u8, - context: &EHContext<'_>, - foreign_exception: bool, -) -> Result { +pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result { if lsda.is_null() { return Ok(EHAction::None); } @@ -98,7 +94,7 @@ pub unsafe fn find_eh_action( return Ok(EHAction::None); } else { let lpad = lpad_base + cs_lpad; - return Ok(interpret_cs_action(cs_action, lpad, foreign_exception)); + return Ok(interpret_cs_action(cs_action, lpad)); } } } @@ -123,21 +119,17 @@ pub unsafe fn find_eh_action( // Can never have null landing pad for sjlj -- that would have // been indicated by a -1 call site index. let lpad = (cs_lpad + 1) as usize; - return Ok(interpret_cs_action(cs_action, lpad, foreign_exception)); + return Ok(interpret_cs_action(cs_action, lpad)); } } } } -fn interpret_cs_action(cs_action: u64, lpad: usize, foreign_exception: bool) -> EHAction { +fn interpret_cs_action(cs_action: u64, lpad: usize) -> EHAction { if cs_action == 0 { // If cs_action is 0 then this is a cleanup (Drop::drop). We run these // for both Rust panics and foreign exceptions. EHAction::Cleanup(lpad) - } else if foreign_exception { - // catch_unwind should not catch foreign exceptions, only Rust panics. - // Instead just continue unwinding. - EHAction::None } else { // Stop unwinding Rust panics at catch_unwind. EHAction::Catch(lpad) diff --git a/library/panic_unwind/src/emcc.rs b/library/panic_unwind/src/emcc.rs index a0bdb1481c6b2..e428f2fdaaa39 100644 --- a/library/panic_unwind/src/emcc.rs +++ b/library/panic_unwind/src/emcc.rs @@ -8,8 +8,10 @@ use alloc::boxed::Box; use core::any::Any; +use core::intrinsics; use core::mem; use core::ptr; +use core::sync::atomic::{AtomicBool, Ordering}; use libc::{self, c_int}; use unwind as uw; @@ -47,6 +49,11 @@ static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo { }; struct Exception { + // This is necessary because C++ code can capture our execption with + // std::exception_ptr and rethrow it multiple times, possibly even in + // another thread. + caught: AtomicBool, + // This needs to be an Option because the object's lifetime follows C++ // semantics: when catch_unwind moves the Box out of the exception it must // still leave the exception object in a valid state because its destructor @@ -55,11 +62,27 @@ struct Exception { } pub unsafe fn cleanup(ptr: *mut u8) -> Box { - assert!(!ptr.is_null()); - let adjusted_ptr = __cxa_begin_catch(ptr as *mut libc::c_void) as *mut Exception; - let ex = (*adjusted_ptr).data.take(); + // intrinsics::try actually gives us a pointer to this structure. + #[repr(C)] + struct CatchData { + ptr: *mut u8, + is_rust_panic: bool, + } + let catch_data = &*(ptr as *mut CatchData); + + let adjusted_ptr = __cxa_begin_catch(catch_data.ptr as *mut libc::c_void) as *mut Exception; + let out = if catch_data.is_rust_panic { + let was_caught = (*adjusted_ptr).caught.swap(true, Ordering::SeqCst); + if was_caught { + // Since cleanup() isn't allowed to panic, we just abort instead. + intrinsics::abort(); + } + (*adjusted_ptr).data.take().unwrap() + } else { + super::__rust_foreign_exception(); + }; __cxa_end_catch(); - ex.unwrap() + out } pub unsafe fn panic(data: Box) -> u32 { @@ -68,25 +91,16 @@ pub unsafe fn panic(data: Box) -> u32 { if exception.is_null() { return uw::_URC_FATAL_PHASE1_ERROR as u32; } - ptr::write(exception, Exception { data: Some(data) }); + ptr::write(exception, Exception { caught: AtomicBool::new(false), data: Some(data) }); __cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, exception_cleanup); } -// On WASM and ARM, the destructor returns the pointer to the object. -cfg_if::cfg_if! { - if #[cfg(any(target_arch = "arm", target_arch = "wasm32"))] { - type DestructorRet = *mut libc::c_void; - } else { - type DestructorRet = (); - } -} -extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> DestructorRet { +extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> *mut libc::c_void { unsafe { if let Some(b) = (ptr as *mut Exception).read().data { drop(b); super::__rust_drop_panic(); } - #[cfg(any(target_arch = "arm", target_arch = "wasm32"))] ptr } } @@ -109,7 +123,7 @@ extern "C" { fn __cxa_throw( thrown_exception: *mut libc::c_void, tinfo: *const TypeInfo, - dest: extern "C" fn(*mut libc::c_void) -> DestructorRet, + dest: extern "C" fn(*mut libc::c_void) -> *mut libc::c_void, ) -> !; fn __gxx_personality_v0( version: c_int, diff --git a/library/panic_unwind/src/gcc.rs b/library/panic_unwind/src/gcc.rs index f5d83c21da068..85a2a18947db2 100644 --- a/library/panic_unwind/src/gcc.rs +++ b/library/panic_unwind/src/gcc.rs @@ -73,8 +73,14 @@ pub unsafe fn panic(data: Box) -> u32 { } pub unsafe fn cleanup(ptr: *mut u8) -> Box { - let exception = Box::from_raw(ptr as *mut Exception); - exception.cause + let exception = ptr as *mut uw::_Unwind_Exception; + if (*exception).exception_class != rust_exception_class() { + uw::_Unwind_DeleteException(exception); + super::__rust_foreign_exception(); + } else { + let exception = Box::from_raw(exception as *mut Exception); + exception.cause + } } // Rust's exception class identifier. This is used by personality routines to @@ -164,9 +170,7 @@ cfg_if::cfg_if! { // _Unwind_Context in our libunwind bindings and fetch the required data from there // directly, bypassing DWARF compatibility functions. - let exception_class = (*exception_object).exception_class; - let foreign_exception = exception_class != rust_exception_class(); - let eh_action = match find_eh_action(context, foreign_exception) { + let eh_action = match find_eh_action(context) { Ok(action) => action, Err(_) => return uw::_URC_FAILURE, }; @@ -221,15 +225,14 @@ cfg_if::cfg_if! { // and indirectly on Windows x86_64 via SEH. unsafe extern "C" fn rust_eh_personality_impl(version: c_int, actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, + _exception_class: uw::_Unwind_Exception_Class, exception_object: *mut uw::_Unwind_Exception, context: *mut uw::_Unwind_Context) -> uw::_Unwind_Reason_Code { if version != 1 { return uw::_URC_FATAL_PHASE1_ERROR; } - let foreign_exception = exception_class != rust_exception_class(); - let eh_action = match find_eh_action(context, foreign_exception) { + let eh_action = match find_eh_action(context) { Ok(action) => action, Err(_) => return uw::_URC_FATAL_PHASE1_ERROR, }; @@ -293,10 +296,7 @@ cfg_if::cfg_if! { } } -unsafe fn find_eh_action( - context: *mut uw::_Unwind_Context, - foreign_exception: bool, -) -> Result { +unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result { let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; let mut ip_before_instr: c_int = 0; let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr); @@ -308,7 +308,7 @@ unsafe fn find_eh_action( get_text_start: &|| uw::_Unwind_GetTextRelBase(context), get_data_start: &|| uw::_Unwind_GetDataRelBase(context), }; - eh::find_eh_action(lsda, &eh_context, foreign_exception) + eh::find_eh_action(lsda, &eh_context) } // Frame unwind info registration diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 7d14893c4cc2e..ea296ee0bc3ed 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -88,6 +88,9 @@ extern "C" { /// Handler in libstd called when a panic object is dropped outside of /// `catch_unwind`. fn __rust_drop_panic() -> !; + + /// Handler in libstd called when a foreign exception is caught. + fn __rust_foreign_exception() -> !; } mod dwarf; diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs index 1f812f8df6122..eca169373f39f 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -309,15 +309,21 @@ pub unsafe fn panic(data: Box) -> u32 { extern "system" { #[unwind(allowed)] - pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !; + fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !; } _CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _); } pub unsafe fn cleanup(payload: *mut u8) -> Box { - let exception = &mut *(payload as *mut Exception); - exception.data.take().unwrap() + // A NULL payload here means that we got here from the catch (...) of + // __rust_try. This happens when a non-Rust foreign exception is caught. + if payload.is_null() { + super::__rust_foreign_exception(); + } else { + let exception = &mut *(payload as *mut Exception); + exception.data.take().unwrap() + } } // This is required by the compiler to exist (e.g., it's a lang item), but diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 8fcb24033b1a7..87493945db6dc 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -359,6 +359,9 @@ impl Future for AssertUnwindSafe { /// aborting the process as well. This function *only* catches unwinding panics, /// not those that abort the process. /// +/// Also note that unwinding into Rust code with a foreign exception (e.g. a +/// an exception thrown from C++ code) is undefined behavior. +/// /// # Examples /// /// ``` diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 08d363a9a2964..8dceb12de87b8 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -60,6 +60,14 @@ extern "C" fn __rust_drop_panic() -> ! { rtabort!("Rust panics must be rethrown"); } +/// This function is called by the panic runtime if it catches an exception +/// object which does not correspond to a Rust panic. +#[cfg(not(test))] +#[rustc_std_internal_symbol] +extern "C" fn __rust_foreign_exception() -> ! { + rtabort!("Rust cannot catch foreign exceptions"); +} + #[derive(Copy, Clone)] enum Hook { Default, diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index 26707fdf8395a..1c51a9df5d884 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -82,6 +82,7 @@ pub struct CodegenCx<'ll, 'tcx> { pub dbg_cx: Option>, eh_personality: Cell>, + eh_catch_typeinfo: Cell>, pub rust_try_fn: Cell>, intrinsics: RefCell>, @@ -311,6 +312,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { coverage_cx, dbg_cx, eh_personality: Cell::new(None), + eh_catch_typeinfo: Cell::new(None), rust_try_fn: Cell::new(None), intrinsics: Default::default(), local_gen_sym_counter: Cell::new(0), @@ -819,6 +821,25 @@ impl CodegenCx<'b, 'tcx> { } None } + + crate fn eh_catch_typeinfo(&self) -> &'b Value { + if let Some(eh_catch_typeinfo) = self.eh_catch_typeinfo.get() { + return eh_catch_typeinfo; + } + let tcx = self.tcx; + assert!(self.sess().target.target.options.is_like_emscripten); + let eh_catch_typeinfo = match tcx.lang_items().eh_catch_typeinfo() { + Some(def_id) => self.get_static(def_id), + _ => { + let ty = self + .type_struct(&[self.type_ptr_to(self.type_isize()), self.type_i8p()], false); + self.declare_global("rust_eh_catch_typeinfo", ty) + } + }; + let eh_catch_typeinfo = self.const_bitcast(eh_catch_typeinfo, self.type_i8p()); + self.eh_catch_typeinfo.set(Some(eh_catch_typeinfo)); + eh_catch_typeinfo + } } impl<'b, 'tcx> CodegenCx<'b, 'tcx> { diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index bb79a52dcf95c..c1dfb83b1355f 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -855,6 +855,8 @@ fn try_intrinsic( bx.store(bx.const_i32(0), dest, ret_align); } else if wants_msvc_seh(bx.sess()) { codegen_msvc_try(bx, try_func, data, catch_func, dest); + } else if bx.sess().target.target.options.is_like_emscripten { + codegen_emcc_try(bx, try_func, data, catch_func, dest); } else { codegen_gnu_try(bx, try_func, data, catch_func, dest); } @@ -880,7 +882,8 @@ fn codegen_msvc_try( let mut normal = bx.build_sibling_block("normal"); let mut catchswitch = bx.build_sibling_block("catchswitch"); - let mut catchpad = bx.build_sibling_block("catchpad"); + let mut catchpad_rust = bx.build_sibling_block("catchpad_rust"); + let mut catchpad_foreign = bx.build_sibling_block("catchpad_foreign"); let mut caught = bx.build_sibling_block("caught"); let try_func = llvm::get_param(bx.llfn(), 0); @@ -890,21 +893,26 @@ fn codegen_msvc_try( // We're generating an IR snippet that looks like: // // declare i32 @rust_try(%try_func, %data, %catch_func) { - // %slot = alloca u8* + // %slot = alloca i8* // invoke %try_func(%data) to label %normal unwind label %catchswitch // // normal: // ret i32 0 // // catchswitch: - // %cs = catchswitch within none [%catchpad] unwind to caller + // %cs = catchswitch within none [%catchpad_rust, %catchpad_foreign] unwind to caller // - // catchpad: - // %tok = catchpad within %cs [%type_descriptor, 0, %slot] + // catchpad_rust: + // %tok = catchpad within %cs [%type_descriptor, 8, %slot] // %ptr = load %slot // call %catch_func(%data, %ptr) // catchret from %tok to label %caught // + // catchpad_foreign: + // %tok = catchpad within %cs [null, 64, null] + // call %catch_func(%data, null) + // catchret from %tok to label %caught + // // caught: // ret i32 1 // } @@ -912,13 +920,11 @@ fn codegen_msvc_try( // This structure follows the basic usage of throw/try/catch in LLVM. // For example, compile this C++ snippet to see what LLVM generates: // - // #include - // // struct rust_panic { // rust_panic(const rust_panic&); // ~rust_panic(); // - // uint64_t x[2]; + // void* x[2]; // }; // // int __rust_try( @@ -932,6 +938,9 @@ fn codegen_msvc_try( // } catch(rust_panic& a) { // catch_func(data, &a); // return 1; + // } catch(...) { + // catch_func(data, NULL); + // return 1; // } // } // @@ -942,8 +951,9 @@ fn codegen_msvc_try( normal.ret(bx.const_i32(0)); - let cs = catchswitch.catch_switch(None, None, 1); - catchswitch.add_handler(cs, catchpad.llbb()); + let cs = catchswitch.catch_switch(None, None, 2); + catchswitch.add_handler(cs, catchpad_rust.llbb()); + catchswitch.add_handler(cs, catchpad_foreign.llbb()); // We can't use the TypeDescriptor defined in libpanic_unwind because it // might be in another DLL and the SEH encoding only supports specifying @@ -977,11 +987,17 @@ fn codegen_msvc_try( // // Source: MicrosoftCXXABI::getAddrOfCXXCatchHandlerType in clang let flags = bx.const_i32(8); - let funclet = catchpad.catch_pad(cs, &[tydesc, flags, slot]); - let ptr = catchpad.load(slot, ptr_align); - catchpad.call(catch_func, &[data, ptr], Some(&funclet)); - - catchpad.catch_ret(&funclet, caught.llbb()); + let funclet = catchpad_rust.catch_pad(cs, &[tydesc, flags, slot]); + let ptr = catchpad_rust.load(slot, ptr_align); + catchpad_rust.call(catch_func, &[data, ptr], Some(&funclet)); + catchpad_rust.catch_ret(&funclet, caught.llbb()); + + // The flag value of 64 indicates a "catch-all". + let flags = bx.const_i32(64); + let null = bx.const_null(bx.type_i8p()); + let funclet = catchpad_foreign.catch_pad(cs, &[null, flags, null]); + catchpad_foreign.call(catch_func, &[data, null], Some(&funclet)); + catchpad_foreign.catch_ret(&funclet, caught.llbb()); caught.ret(bx.const_i32(1)); }); @@ -1044,13 +1060,7 @@ fn codegen_gnu_try( // rust_try ignores the selector. let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false); let vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 1); - let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() { - Some(tydesc) => { - let tydesc = bx.get_static(tydesc); - bx.bitcast(tydesc, bx.type_i8p()) - } - None => bx.const_null(bx.type_i8p()), - }; + let tydesc = bx.const_null(bx.type_i8p()); catch.add_clause(vals, tydesc); let ptr = catch.extract_value(vals, 0); catch.call(catch_func, &[data, ptr], None); @@ -1064,6 +1074,88 @@ fn codegen_gnu_try( bx.store(ret, dest, i32_align); } +// Variant of codegen_gnu_try used for emscripten where Rust panics are +// implemented using C++ exceptions. Here we use exceptions of a specific type +// (`struct rust_panic`) to represent Rust panics. +fn codegen_emcc_try( + bx: &mut Builder<'a, 'll, 'tcx>, + try_func: &'ll Value, + data: &'ll Value, + catch_func: &'ll Value, + dest: &'ll Value, +) { + let llfn = get_rust_try_fn(bx, &mut |mut bx| { + // Codegens the shims described above: + // + // bx: + // invoke %try_func(%data) normal %normal unwind %catch + // + // normal: + // ret 0 + // + // catch: + // (%ptr, %selector) = landingpad + // %rust_typeid = @llvm.eh.typeid.for(@_ZTI10rust_panic) + // %is_rust_panic = %selector == %rust_typeid + // %catch_data = alloca { i8*, i8 } + // %catch_data[0] = %ptr + // %catch_data[1] = %is_rust_panic + // call %catch_func(%data, %catch_data) + // ret 1 + + bx.sideeffect(); + + let mut then = bx.build_sibling_block("then"); + let mut catch = bx.build_sibling_block("catch"); + + let try_func = llvm::get_param(bx.llfn(), 0); + let data = llvm::get_param(bx.llfn(), 1); + let catch_func = llvm::get_param(bx.llfn(), 2); + bx.invoke(try_func, &[data], then.llbb(), catch.llbb(), None); + then.ret(bx.const_i32(0)); + + // Type indicator for the exception being thrown. + // + // The first value in this tuple is a pointer to the exception object + // being thrown. The second value is a "selector" indicating which of + // the landing pad clauses the exception's type had been matched to. + let tydesc = bx.eh_catch_typeinfo(); + let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false); + let vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 2); + catch.add_clause(vals, tydesc); + catch.add_clause(vals, bx.const_null(bx.type_i8p())); + let ptr = catch.extract_value(vals, 0); + let selector = catch.extract_value(vals, 1); + + // Check if the typeid we got is the one for a Rust panic. + let llvm_eh_typeid_for = bx.get_intrinsic("llvm.eh.typeid.for"); + let rust_typeid = catch.call(llvm_eh_typeid_for, &[tydesc], None); + let is_rust_panic = catch.icmp(IntPredicate::IntEQ, selector, rust_typeid); + let is_rust_panic = catch.zext(is_rust_panic, bx.type_bool()); + + // We need to pass two values to catch_func (ptr and is_rust_panic), so + // create an alloca and pass a pointer to that. + let ptr_align = bx.tcx().data_layout.pointer_align.abi; + let i8_align = bx.tcx().data_layout.i8_align.abi; + let catch_data = + catch.alloca(bx.type_struct(&[bx.type_i8p(), bx.type_bool()], false), ptr_align); + let catch_data_0 = catch.inbounds_gep(catch_data, &[bx.const_usize(0), bx.const_usize(0)]); + catch.store(ptr, catch_data_0, ptr_align); + let catch_data_1 = catch.inbounds_gep(catch_data, &[bx.const_usize(0), bx.const_usize(1)]); + catch.store(is_rust_panic, catch_data_1, i8_align); + let catch_data = catch.bitcast(catch_data, bx.type_i8p()); + + catch.call(catch_func, &[data, catch_data], None); + catch.ret(bx.const_i32(1)); + }); + + // Note that no invoke is used here because by definition this function + // can't panic (that's what it's catching). + let ret = bx.call(llfn, &[try_func, data, catch_func], None); + let i32_align = bx.tcx().data_layout.i32_align.abi; + bx.store(ret, dest, i32_align); +} + // Helper function to give a Block to a closure to codegen a shim function. // This is currently primarily used for the `try` intrinsic functions above. fn gen_fn<'ll, 'tcx>( diff --git a/src/librustc_hir/weak_lang_items.rs b/src/librustc_hir/weak_lang_items.rs index 74e2a90262ce6..129eec7d29efe 100644 --- a/src/librustc_hir/weak_lang_items.rs +++ b/src/librustc_hir/weak_lang_items.rs @@ -48,5 +48,6 @@ impl LanguageItems { weak_lang_items! { panic_impl, PanicImpl, rust_begin_unwind; eh_personality, EhPersonality, rust_eh_personality; + eh_catch_typeinfo, EhCatchTypeinfo, rust_eh_catch_typeinfo; oom, Oom, rust_oom; } diff --git a/src/librustc_middle/middle/lang_items.rs b/src/librustc_middle/middle/lang_items.rs index 3e1caa3b54977..7194a035e89f6 100644 --- a/src/librustc_middle/middle/lang_items.rs +++ b/src/librustc_middle/middle/lang_items.rs @@ -53,7 +53,9 @@ pub fn required(tcx: TyCtxt<'_>, lang_item: LangItem) -> bool { // symbols. Other panic runtimes ensure that the relevant symbols are // available to link things together, but they're never exercised. match tcx.sess.panic_strategy() { - PanicStrategy::Abort => lang_item != LangItem::EhPersonality, + PanicStrategy::Abort => { + lang_item != LangItem::EhPersonality && lang_item != LangItem::EhCatchTypeinfo + } PanicStrategy::Unwind => true, } } diff --git a/src/librustc_passes/weak_lang_items.rs b/src/librustc_passes/weak_lang_items.rs index f559d66587b24..6bc2110bfb3e6 100644 --- a/src/librustc_passes/weak_lang_items.rs +++ b/src/librustc_passes/weak_lang_items.rs @@ -26,6 +26,9 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItem if items.eh_personality().is_none() { items.missing.push(LangItem::EhPersonality); } + if tcx.sess.target.target.options.is_like_emscripten && items.eh_catch_typeinfo().is_none() { + items.missing.push(LangItem::EhCatchTypeinfo); + } { let mut cx = Context { tcx, items }; diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index 46612145bf02c..5092b945f72c4 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -876,6 +876,7 @@ symbols! { rust_2015_preview, rust_2018_preview, rust_begin_unwind, + rust_eh_catch_typeinfo, rust_eh_personality, rust_eh_register_frames, rust_eh_unregister_frames, diff --git a/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs b/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs index 3e5cdad7ab936..b9ef2f329414e 100644 --- a/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs +++ b/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs @@ -11,3 +11,5 @@ use core::panic::PanicInfo; fn panic_impl(info: &PanicInfo) -> ! { loop {} } #[lang = "eh_personality"] fn eh_personality() {} +#[lang = "eh_catch_typeinfo"] +static EH_CATCH_TYPEINFO: u8 = 0; diff --git a/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp b/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp index b0fd65f88e7de..8182021a2cce4 100644 --- a/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp +++ b/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp @@ -23,15 +23,15 @@ struct drop_check { extern "C" { void rust_catch_callback(void (*cb)(), bool* rust_ok); - static void callback() { + void throw_cxx_exception() { println("throwing C++ exception"); throw exception(); } - void throw_cxx_exception() { + void test_cxx_exception() { bool rust_ok = false; try { - rust_catch_callback(callback, &rust_ok); + rust_catch_callback(throw_cxx_exception, &rust_ok); assert(false && "unreachable"); } catch (exception e) { println("caught C++ exception"); diff --git a/src/test/run-make-fulldeps/foreign-exceptions/foo.rs b/src/test/run-make-fulldeps/foreign-exceptions/foo.rs index 9c2045c8c89f7..b5c8c1962a8ec 100644 --- a/src/test/run-make-fulldeps/foreign-exceptions/foo.rs +++ b/src/test/run-make-fulldeps/foreign-exceptions/foo.rs @@ -1,5 +1,5 @@ -// Tests that C++ exceptions can unwind through Rust code, run destructors and -// are ignored by catch_unwind. Also tests that Rust panics can unwind through +// Tests that C++ exceptions can unwind through Rust code run destructors and +// are caught by catch_unwind. Also tests that Rust panics can unwind through // C++ code. // For linking libstdc++ on MinGW @@ -17,7 +17,7 @@ impl<'a> Drop for DropCheck<'a> { } extern "C" { - fn throw_cxx_exception(); + fn test_cxx_exception(); #[unwind(allowed)] fn cxx_catch_callback(cb: extern "C" fn(), ok: *mut bool); @@ -26,15 +26,12 @@ extern "C" { #[no_mangle] #[unwind(allowed)] extern "C" fn rust_catch_callback(cb: extern "C" fn(), rust_ok: &mut bool) { - let _caught_unwind = catch_unwind(AssertUnwindSafe(|| { - let _drop = DropCheck(rust_ok); - cb(); - unreachable!("should have unwound instead of returned"); - })); - unreachable!("catch_unwind should not have caught foreign exception"); + let _drop = DropCheck(rust_ok); + cb(); + unreachable!("should have unwound instead of returned"); } -fn throw_rust_panic() { +fn test_rust_panic() { #[unwind(allowed)] extern "C" fn callback() { println!("throwing rust panic"); @@ -60,6 +57,6 @@ fn throw_rust_panic() { } fn main() { - unsafe { throw_cxx_exception() }; - throw_rust_panic(); + unsafe { test_cxx_exception() }; + test_rust_panic(); } diff --git a/src/test/run-make-fulldeps/issue-69368/a.rs b/src/test/run-make-fulldeps/issue-69368/a.rs index 726db8746371e..7d339c5a5dab4 100644 --- a/src/test/run-make-fulldeps/issue-69368/a.rs +++ b/src/test/run-make-fulldeps/issue-69368/a.rs @@ -14,3 +14,8 @@ pub fn panic_handler(_: &core::panic::PanicInfo) -> ! { extern "C" fn __rust_drop_panic() -> ! { loop {} } + +#[no_mangle] +extern "C" fn __rust_foreign_exception() -> ! { + loop {} +} diff --git a/src/test/ui/consts/const-eval/const_panic_libcore_main.rs b/src/test/ui/consts/const-eval/const_panic_libcore_main.rs index 6b86feb5921a1..6b03e847def14 100644 --- a/src/test/ui/consts/const-eval/const_panic_libcore_main.rs +++ b/src/test/ui/consts/const-eval/const_panic_libcore_main.rs @@ -17,6 +17,8 @@ const X: () = unimplemented!(); #[lang = "eh_personality"] fn eh() {} +#[lang = "eh_catch_typeinfo"] +static EH_CATCH_TYPEINFO: u8 = 0; #[panic_handler] fn panic(_info: &PanicInfo) -> ! { diff --git a/src/test/ui/macros/macro-comma-behavior.core.stderr b/src/test/ui/macros/macro-comma-behavior.core.stderr index 83a88ab3bd97c..dd0cac659fd31 100644 --- a/src/test/ui/macros/macro-comma-behavior.core.stderr +++ b/src/test/ui/macros/macro-comma-behavior.core.stderr @@ -1,41 +1,41 @@ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:20:23 + --> $DIR/macro-comma-behavior.rs:21:23 | LL | assert_eq!(1, 1, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:23:23 + --> $DIR/macro-comma-behavior.rs:24:23 | LL | assert_ne!(1, 2, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:29:29 + --> $DIR/macro-comma-behavior.rs:30:29 | LL | debug_assert_eq!(1, 1, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:32:29 + --> $DIR/macro-comma-behavior.rs:33:29 | LL | debug_assert_ne!(1, 2, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:53:19 + --> $DIR/macro-comma-behavior.rs:54:19 | LL | format_args!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:71:21 + --> $DIR/macro-comma-behavior.rs:72:21 | LL | unimplemented!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:80:24 + --> $DIR/macro-comma-behavior.rs:81:24 | LL | write!(f, "{}",)?; | ^^ diff --git a/src/test/ui/macros/macro-comma-behavior.rs b/src/test/ui/macros/macro-comma-behavior.rs index 04714c65b5cb7..0bfe068307832 100644 --- a/src/test/ui/macros/macro-comma-behavior.rs +++ b/src/test/ui/macros/macro-comma-behavior.rs @@ -9,6 +9,7 @@ #[cfg(std)] use std::fmt; #[cfg(core)] use core::fmt; #[cfg(core)] #[lang = "eh_personality"] fn eh_personality() {} +#[cfg(core)] #[lang = "eh_catch_typeinfo"] static EH_CATCH_TYPEINFO: u8 = 0; #[cfg(core)] #[lang = "panic_impl"] fn panic_impl(panic: &core::panic::PanicInfo) -> ! { loop {} } // (see documentation of the similarly-named test in run-pass) diff --git a/src/test/ui/macros/macro-comma-behavior.std.stderr b/src/test/ui/macros/macro-comma-behavior.std.stderr index 26445f2c5c542..4372d89fbf522 100644 --- a/src/test/ui/macros/macro-comma-behavior.std.stderr +++ b/src/test/ui/macros/macro-comma-behavior.std.stderr @@ -1,59 +1,59 @@ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:20:23 + --> $DIR/macro-comma-behavior.rs:21:23 | LL | assert_eq!(1, 1, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:23:23 + --> $DIR/macro-comma-behavior.rs:24:23 | LL | assert_ne!(1, 2, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:29:29 + --> $DIR/macro-comma-behavior.rs:30:29 | LL | debug_assert_eq!(1, 1, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:32:29 + --> $DIR/macro-comma-behavior.rs:33:29 | LL | debug_assert_ne!(1, 2, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:37:18 + --> $DIR/macro-comma-behavior.rs:38:18 | LL | eprint!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:49:18 + --> $DIR/macro-comma-behavior.rs:50:18 | LL | format!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:53:19 + --> $DIR/macro-comma-behavior.rs:54:19 | LL | format_args!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:60:17 + --> $DIR/macro-comma-behavior.rs:61:17 | LL | print!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:71:21 + --> $DIR/macro-comma-behavior.rs:72:21 | LL | unimplemented!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:80:24 + --> $DIR/macro-comma-behavior.rs:81:24 | LL | write!(f, "{}",)?; | ^^ diff --git a/src/test/ui/no_owned_box_lang_item.rs b/src/test/ui/no_owned_box_lang_item.rs index 58e45ff73a5e6..bef630d826c0f 100644 --- a/src/test/ui/no_owned_box_lang_item.rs +++ b/src/test/ui/no_owned_box_lang_item.rs @@ -12,4 +12,5 @@ fn main() { } #[lang = "eh_personality"] extern fn eh_personality() {} +#[lang = "eh_catch_typeinfo"] static EH_CATCH_TYPEINFO: u8 = 0; #[lang = "panic_impl"] fn panic_impl(panic: &PanicInfo) -> ! { loop {} } diff --git a/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs b/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs index 3e5cdad7ab936..b9ef2f329414e 100644 --- a/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs +++ b/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs @@ -11,3 +11,5 @@ use core::panic::PanicInfo; fn panic_impl(info: &PanicInfo) -> ! { loop {} } #[lang = "eh_personality"] fn eh_personality() {} +#[lang = "eh_catch_typeinfo"] +static EH_CATCH_TYPEINFO: u8 = 0; diff --git a/src/test/ui/range/issue-54505-no-std.rs b/src/test/ui/range/issue-54505-no-std.rs index c6a3cc346fc80..f5d5823e468b0 100644 --- a/src/test/ui/range/issue-54505-no-std.rs +++ b/src/test/ui/range/issue-54505-no-std.rs @@ -14,6 +14,9 @@ use core::ops::RangeBounds; #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] #[lang = "eh_personality"] extern fn eh_personality() {} +#[cfg(target_os = "emscripten")] +#[lang = "eh_catch_typeinfo"] +static EH_CATCH_TYPEINFO: u8 = 0; // take a reference to any built-in range diff --git a/src/test/ui/range/issue-54505-no-std.stderr b/src/test/ui/range/issue-54505-no-std.stderr index 909340611328a..5537ed4576763 100644 --- a/src/test/ui/range/issue-54505-no-std.stderr +++ b/src/test/ui/range/issue-54505-no-std.stderr @@ -1,7 +1,7 @@ error: `#[panic_handler]` function required, but not found error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:24:16 + --> $DIR/issue-54505-no-std.rs:27:16 | LL | take_range(0..1); | ^^^^ @@ -13,7 +13,7 @@ LL | take_range(0..1); found struct `core::ops::Range<{integer}>` error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:29:16 + --> $DIR/issue-54505-no-std.rs:32:16 | LL | take_range(1..); | ^^^ @@ -25,7 +25,7 @@ LL | take_range(1..); found struct `core::ops::RangeFrom<{integer}>` error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:34:16 + --> $DIR/issue-54505-no-std.rs:37:16 | LL | take_range(..); | ^^ @@ -37,7 +37,7 @@ LL | take_range(..); found struct `core::ops::RangeFull` error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:39:16 + --> $DIR/issue-54505-no-std.rs:42:16 | LL | take_range(0..=1); | ^^^^^ @@ -49,7 +49,7 @@ LL | take_range(0..=1); found struct `core::ops::RangeInclusive<{integer}>` error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:44:16 + --> $DIR/issue-54505-no-std.rs:47:16 | LL | take_range(..5); | ^^^ @@ -61,7 +61,7 @@ LL | take_range(..5); found struct `core::ops::RangeTo<{integer}>` error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:49:16 + --> $DIR/issue-54505-no-std.rs:52:16 | LL | take_range(..=42); | ^^^^^