Abort when catch_unwind catches a foreign exception

This commit is contained in:
Amanieu d'Antras 2020-03-21 07:50:38 +00:00
parent 118860a7e7
commit 239f833ed1
26 changed files with 274 additions and 104 deletions

View file

@ -117,6 +117,17 @@ pub mod personalities {
1 // `ExceptionContinueSearch` 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 // 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. // they don't need to do anything so the bodies are nops.
#[rustc_std_internal_symbol] #[rustc_std_internal_symbol]

View file

@ -51,11 +51,7 @@ pub enum EHAction {
pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm")); pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm"));
pub unsafe fn find_eh_action( pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result<EHAction, ()> {
lsda: *const u8,
context: &EHContext<'_>,
foreign_exception: bool,
) -> Result<EHAction, ()> {
if lsda.is_null() { if lsda.is_null() {
return Ok(EHAction::None); return Ok(EHAction::None);
} }
@ -98,7 +94,7 @@ pub unsafe fn find_eh_action(
return Ok(EHAction::None); return Ok(EHAction::None);
} else { } else {
let lpad = lpad_base + cs_lpad; 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 // Can never have null landing pad for sjlj -- that would have
// been indicated by a -1 call site index. // been indicated by a -1 call site index.
let lpad = (cs_lpad + 1) as usize; 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 == 0 {
// If cs_action is 0 then this is a cleanup (Drop::drop). We run these // If cs_action is 0 then this is a cleanup (Drop::drop). We run these
// for both Rust panics and foreign exceptions. // for both Rust panics and foreign exceptions.
EHAction::Cleanup(lpad) EHAction::Cleanup(lpad)
} else if foreign_exception {
// catch_unwind should not catch foreign exceptions, only Rust panics.
// Instead just continue unwinding.
EHAction::None
} else { } else {
// Stop unwinding Rust panics at catch_unwind. // Stop unwinding Rust panics at catch_unwind.
EHAction::Catch(lpad) EHAction::Catch(lpad)

View file

@ -8,8 +8,10 @@
use alloc::boxed::Box; use alloc::boxed::Box;
use core::any::Any; use core::any::Any;
use core::intrinsics;
use core::mem; use core::mem;
use core::ptr; use core::ptr;
use core::sync::atomic::{AtomicBool, Ordering};
use libc::{self, c_int}; use libc::{self, c_int};
use unwind as uw; use unwind as uw;
@ -47,6 +49,11 @@ static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo {
}; };
struct Exception { 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++ // 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 // 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 // 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<dyn Any + Send> { pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
assert!(!ptr.is_null()); // intrinsics::try actually gives us a pointer to this structure.
let adjusted_ptr = __cxa_begin_catch(ptr as *mut libc::c_void) as *mut Exception; #[repr(C)]
let ex = (*adjusted_ptr).data.take(); 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(); __cxa_end_catch();
ex.unwrap() out
} }
pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 { pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
@ -68,25 +91,16 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
if exception.is_null() { if exception.is_null() {
return uw::_URC_FATAL_PHASE1_ERROR as u32; 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); __cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, exception_cleanup);
} }
// On WASM and ARM, the destructor returns the pointer to the object. extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> *mut libc::c_void {
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 {
unsafe { unsafe {
if let Some(b) = (ptr as *mut Exception).read().data { if let Some(b) = (ptr as *mut Exception).read().data {
drop(b); drop(b);
super::__rust_drop_panic(); super::__rust_drop_panic();
} }
#[cfg(any(target_arch = "arm", target_arch = "wasm32"))]
ptr ptr
} }
} }
@ -109,7 +123,7 @@ extern "C" {
fn __cxa_throw( fn __cxa_throw(
thrown_exception: *mut libc::c_void, thrown_exception: *mut libc::c_void,
tinfo: *const TypeInfo, 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( fn __gxx_personality_v0(
version: c_int, version: c_int,

View file

@ -73,8 +73,14 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
} }
pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> { pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
let exception = Box::from_raw(ptr as *mut Exception); let exception = ptr as *mut uw::_Unwind_Exception;
exception.cause 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 // 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 // _Unwind_Context in our libunwind bindings and fetch the required data from there
// directly, bypassing DWARF compatibility functions. // directly, bypassing DWARF compatibility functions.
let exception_class = (*exception_object).exception_class; let eh_action = match find_eh_action(context) {
let foreign_exception = exception_class != rust_exception_class();
let eh_action = match find_eh_action(context, foreign_exception) {
Ok(action) => action, Ok(action) => action,
Err(_) => return uw::_URC_FAILURE, Err(_) => return uw::_URC_FAILURE,
}; };
@ -221,15 +225,14 @@ cfg_if::cfg_if! {
// and indirectly on Windows x86_64 via SEH. // and indirectly on Windows x86_64 via SEH.
unsafe extern "C" fn rust_eh_personality_impl(version: c_int, unsafe extern "C" fn rust_eh_personality_impl(version: c_int,
actions: uw::_Unwind_Action, actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class, _exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception, exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context) context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code { -> uw::_Unwind_Reason_Code {
if version != 1 { if version != 1 {
return uw::_URC_FATAL_PHASE1_ERROR; return uw::_URC_FATAL_PHASE1_ERROR;
} }
let foreign_exception = exception_class != rust_exception_class(); let eh_action = match find_eh_action(context) {
let eh_action = match find_eh_action(context, foreign_exception) {
Ok(action) => action, Ok(action) => action,
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR, Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
}; };
@ -293,10 +296,7 @@ cfg_if::cfg_if! {
} }
} }
unsafe fn find_eh_action( unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result<EHAction, ()> {
context: *mut uw::_Unwind_Context,
foreign_exception: bool,
) -> Result<EHAction, ()> {
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
let mut ip_before_instr: c_int = 0; let mut ip_before_instr: c_int = 0;
let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr); 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_text_start: &|| uw::_Unwind_GetTextRelBase(context),
get_data_start: &|| uw::_Unwind_GetDataRelBase(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 // Frame unwind info registration

View file

@ -88,6 +88,9 @@ extern "C" {
/// Handler in libstd called when a panic object is dropped outside of /// Handler in libstd called when a panic object is dropped outside of
/// `catch_unwind`. /// `catch_unwind`.
fn __rust_drop_panic() -> !; fn __rust_drop_panic() -> !;
/// Handler in libstd called when a foreign exception is caught.
fn __rust_foreign_exception() -> !;
} }
mod dwarf; mod dwarf;

View file

@ -309,15 +309,21 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
extern "system" { extern "system" {
#[unwind(allowed)] #[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 _); _CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _);
} }
pub unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send> { pub unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send> {
let exception = &mut *(payload as *mut Exception); // A NULL payload here means that we got here from the catch (...) of
exception.data.take().unwrap() // __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 // This is required by the compiler to exist (e.g., it's a lang item), but

View file

@ -359,6 +359,9 @@ impl<F: Future> Future for AssertUnwindSafe<F> {
/// aborting the process as well. This function *only* catches unwinding panics, /// aborting the process as well. This function *only* catches unwinding panics,
/// not those that abort the process. /// 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 /// # Examples
/// ///
/// ``` /// ```

View file

@ -60,6 +60,14 @@ extern "C" fn __rust_drop_panic() -> ! {
rtabort!("Rust panics must be rethrown"); 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)] #[derive(Copy, Clone)]
enum Hook { enum Hook {
Default, Default,

View file

@ -82,6 +82,7 @@ pub struct CodegenCx<'ll, 'tcx> {
pub dbg_cx: Option<debuginfo::CrateDebugContext<'ll, 'tcx>>, pub dbg_cx: Option<debuginfo::CrateDebugContext<'ll, 'tcx>>,
eh_personality: Cell<Option<&'ll Value>>, eh_personality: Cell<Option<&'ll Value>>,
eh_catch_typeinfo: Cell<Option<&'ll Value>>,
pub rust_try_fn: Cell<Option<&'ll Value>>, pub rust_try_fn: Cell<Option<&'ll Value>>,
intrinsics: RefCell<FxHashMap<&'static str, &'ll Value>>, intrinsics: RefCell<FxHashMap<&'static str, &'ll Value>>,
@ -311,6 +312,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
coverage_cx, coverage_cx,
dbg_cx, dbg_cx,
eh_personality: Cell::new(None), eh_personality: Cell::new(None),
eh_catch_typeinfo: Cell::new(None),
rust_try_fn: Cell::new(None), rust_try_fn: Cell::new(None),
intrinsics: Default::default(), intrinsics: Default::default(),
local_gen_sym_counter: Cell::new(0), local_gen_sym_counter: Cell::new(0),
@ -819,6 +821,25 @@ impl CodegenCx<'b, 'tcx> {
} }
None 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> { impl<'b, 'tcx> CodegenCx<'b, 'tcx> {

View file

@ -855,6 +855,8 @@ fn try_intrinsic(
bx.store(bx.const_i32(0), dest, ret_align); bx.store(bx.const_i32(0), dest, ret_align);
} else if wants_msvc_seh(bx.sess()) { } else if wants_msvc_seh(bx.sess()) {
codegen_msvc_try(bx, try_func, data, catch_func, dest); 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 { } else {
codegen_gnu_try(bx, try_func, data, catch_func, dest); 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 normal = bx.build_sibling_block("normal");
let mut catchswitch = bx.build_sibling_block("catchswitch"); 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 mut caught = bx.build_sibling_block("caught");
let try_func = llvm::get_param(bx.llfn(), 0); 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: // We're generating an IR snippet that looks like:
// //
// declare i32 @rust_try(%try_func, %data, %catch_func) { // 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 // invoke %try_func(%data) to label %normal unwind label %catchswitch
// //
// normal: // normal:
// ret i32 0 // ret i32 0
// //
// catchswitch: // catchswitch:
// %cs = catchswitch within none [%catchpad] unwind to caller // %cs = catchswitch within none [%catchpad_rust, %catchpad_foreign] unwind to caller
// //
// catchpad: // catchpad_rust:
// %tok = catchpad within %cs [%type_descriptor, 0, %slot] // %tok = catchpad within %cs [%type_descriptor, 8, %slot]
// %ptr = load %slot // %ptr = load %slot
// call %catch_func(%data, %ptr) // call %catch_func(%data, %ptr)
// catchret from %tok to label %caught // 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: // caught:
// ret i32 1 // ret i32 1
// } // }
@ -912,13 +920,11 @@ fn codegen_msvc_try(
// This structure follows the basic usage of throw/try/catch in LLVM. // This structure follows the basic usage of throw/try/catch in LLVM.
// For example, compile this C++ snippet to see what LLVM generates: // For example, compile this C++ snippet to see what LLVM generates:
// //
// #include <stdint.h>
//
// struct rust_panic { // struct rust_panic {
// rust_panic(const rust_panic&); // rust_panic(const rust_panic&);
// ~rust_panic(); // ~rust_panic();
// //
// uint64_t x[2]; // void* x[2];
// }; // };
// //
// int __rust_try( // int __rust_try(
@ -932,6 +938,9 @@ fn codegen_msvc_try(
// } catch(rust_panic& a) { // } catch(rust_panic& a) {
// catch_func(data, &a); // catch_func(data, &a);
// return 1; // return 1;
// } catch(...) {
// catch_func(data, NULL);
// return 1;
// } // }
// } // }
// //
@ -942,8 +951,9 @@ fn codegen_msvc_try(
normal.ret(bx.const_i32(0)); normal.ret(bx.const_i32(0));
let cs = catchswitch.catch_switch(None, None, 1); let cs = catchswitch.catch_switch(None, None, 2);
catchswitch.add_handler(cs, catchpad.llbb()); 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 // We can't use the TypeDescriptor defined in libpanic_unwind because it
// might be in another DLL and the SEH encoding only supports specifying // 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 // Source: MicrosoftCXXABI::getAddrOfCXXCatchHandlerType in clang
let flags = bx.const_i32(8); let flags = bx.const_i32(8);
let funclet = catchpad.catch_pad(cs, &[tydesc, flags, slot]); let funclet = catchpad_rust.catch_pad(cs, &[tydesc, flags, slot]);
let ptr = catchpad.load(slot, ptr_align); let ptr = catchpad_rust.load(slot, ptr_align);
catchpad.call(catch_func, &[data, ptr], Some(&funclet)); catchpad_rust.call(catch_func, &[data, ptr], Some(&funclet));
catchpad_rust.catch_ret(&funclet, caught.llbb());
catchpad.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)); caught.ret(bx.const_i32(1));
}); });
@ -1044,13 +1060,7 @@ fn codegen_gnu_try(
// rust_try ignores the selector. // rust_try ignores the selector.
let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false); 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 vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 1);
let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() { let tydesc = bx.const_null(bx.type_i8p());
Some(tydesc) => {
let tydesc = bx.get_static(tydesc);
bx.bitcast(tydesc, bx.type_i8p())
}
None => bx.const_null(bx.type_i8p()),
};
catch.add_clause(vals, tydesc); catch.add_clause(vals, tydesc);
let ptr = catch.extract_value(vals, 0); let ptr = catch.extract_value(vals, 0);
catch.call(catch_func, &[data, ptr], None); catch.call(catch_func, &[data, ptr], None);
@ -1064,6 +1074,88 @@ fn codegen_gnu_try(
bx.store(ret, dest, i32_align); 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. // 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. // This is currently primarily used for the `try` intrinsic functions above.
fn gen_fn<'ll, 'tcx>( fn gen_fn<'ll, 'tcx>(

View file

@ -48,5 +48,6 @@ impl LanguageItems {
weak_lang_items! { weak_lang_items! {
panic_impl, PanicImpl, rust_begin_unwind; panic_impl, PanicImpl, rust_begin_unwind;
eh_personality, EhPersonality, rust_eh_personality; eh_personality, EhPersonality, rust_eh_personality;
eh_catch_typeinfo, EhCatchTypeinfo, rust_eh_catch_typeinfo;
oom, Oom, rust_oom; oom, Oom, rust_oom;
} }

View file

@ -53,7 +53,9 @@ pub fn required(tcx: TyCtxt<'_>, lang_item: LangItem) -> bool {
// symbols. Other panic runtimes ensure that the relevant symbols are // symbols. Other panic runtimes ensure that the relevant symbols are
// available to link things together, but they're never exercised. // available to link things together, but they're never exercised.
match tcx.sess.panic_strategy() { match tcx.sess.panic_strategy() {
PanicStrategy::Abort => lang_item != LangItem::EhPersonality, PanicStrategy::Abort => {
lang_item != LangItem::EhPersonality && lang_item != LangItem::EhCatchTypeinfo
}
PanicStrategy::Unwind => true, PanicStrategy::Unwind => true,
} }
} }

View file

@ -26,6 +26,9 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItem
if items.eh_personality().is_none() { if items.eh_personality().is_none() {
items.missing.push(LangItem::EhPersonality); 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 }; let mut cx = Context { tcx, items };

View file

@ -876,6 +876,7 @@ symbols! {
rust_2015_preview, rust_2015_preview,
rust_2018_preview, rust_2018_preview,
rust_begin_unwind, rust_begin_unwind,
rust_eh_catch_typeinfo,
rust_eh_personality, rust_eh_personality,
rust_eh_register_frames, rust_eh_register_frames,
rust_eh_unregister_frames, rust_eh_unregister_frames,

View file

@ -11,3 +11,5 @@ use core::panic::PanicInfo;
fn panic_impl(info: &PanicInfo) -> ! { loop {} } fn panic_impl(info: &PanicInfo) -> ! { loop {} }
#[lang = "eh_personality"] #[lang = "eh_personality"]
fn eh_personality() {} fn eh_personality() {}
#[lang = "eh_catch_typeinfo"]
static EH_CATCH_TYPEINFO: u8 = 0;

View file

@ -23,15 +23,15 @@ struct drop_check {
extern "C" { extern "C" {
void rust_catch_callback(void (*cb)(), bool* rust_ok); void rust_catch_callback(void (*cb)(), bool* rust_ok);
static void callback() { void throw_cxx_exception() {
println("throwing C++ exception"); println("throwing C++ exception");
throw exception(); throw exception();
} }
void throw_cxx_exception() { void test_cxx_exception() {
bool rust_ok = false; bool rust_ok = false;
try { try {
rust_catch_callback(callback, &rust_ok); rust_catch_callback(throw_cxx_exception, &rust_ok);
assert(false && "unreachable"); assert(false && "unreachable");
} catch (exception e) { } catch (exception e) {
println("caught C++ exception"); println("caught C++ exception");

View file

@ -1,5 +1,5 @@
// Tests that C++ exceptions can unwind through Rust code, run destructors and // 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 // are caught by catch_unwind. Also tests that Rust panics can unwind through
// C++ code. // C++ code.
// For linking libstdc++ on MinGW // For linking libstdc++ on MinGW
@ -17,7 +17,7 @@ impl<'a> Drop for DropCheck<'a> {
} }
extern "C" { extern "C" {
fn throw_cxx_exception(); fn test_cxx_exception();
#[unwind(allowed)] #[unwind(allowed)]
fn cxx_catch_callback(cb: extern "C" fn(), ok: *mut bool); fn cxx_catch_callback(cb: extern "C" fn(), ok: *mut bool);
@ -26,15 +26,12 @@ extern "C" {
#[no_mangle] #[no_mangle]
#[unwind(allowed)] #[unwind(allowed)]
extern "C" fn rust_catch_callback(cb: extern "C" fn(), rust_ok: &mut bool) { 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);
let _drop = DropCheck(rust_ok); cb();
cb(); unreachable!("should have unwound instead of returned");
unreachable!("should have unwound instead of returned");
}));
unreachable!("catch_unwind should not have caught foreign exception");
} }
fn throw_rust_panic() { fn test_rust_panic() {
#[unwind(allowed)] #[unwind(allowed)]
extern "C" fn callback() { extern "C" fn callback() {
println!("throwing rust panic"); println!("throwing rust panic");
@ -60,6 +57,6 @@ fn throw_rust_panic() {
} }
fn main() { fn main() {
unsafe { throw_cxx_exception() }; unsafe { test_cxx_exception() };
throw_rust_panic(); test_rust_panic();
} }

View file

@ -14,3 +14,8 @@ pub fn panic_handler(_: &core::panic::PanicInfo) -> ! {
extern "C" fn __rust_drop_panic() -> ! { extern "C" fn __rust_drop_panic() -> ! {
loop {} loop {}
} }
#[no_mangle]
extern "C" fn __rust_foreign_exception() -> ! {
loop {}
}

View file

@ -17,6 +17,8 @@ const X: () = unimplemented!();
#[lang = "eh_personality"] #[lang = "eh_personality"]
fn eh() {} fn eh() {}
#[lang = "eh_catch_typeinfo"]
static EH_CATCH_TYPEINFO: u8 = 0;
#[panic_handler] #[panic_handler]
fn panic(_info: &PanicInfo) -> ! { fn panic(_info: &PanicInfo) -> ! {

View file

@ -1,41 +1,41 @@
error: 1 positional argument in format string, but no arguments were given 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, "{}",); LL | assert_eq!(1, 1, "{}",);
| ^^ | ^^
error: 1 positional argument in format string, but no arguments were given 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, "{}",); LL | assert_ne!(1, 2, "{}",);
| ^^ | ^^
error: 1 positional argument in format string, but no arguments were given 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, "{}",); LL | debug_assert_eq!(1, 1, "{}",);
| ^^ | ^^
error: 1 positional argument in format string, but no arguments were given 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, "{}",); LL | debug_assert_ne!(1, 2, "{}",);
| ^^ | ^^
error: 1 positional argument in format string, but no arguments were given 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!("{}",); LL | format_args!("{}",);
| ^^ | ^^
error: 1 positional argument in format string, but no arguments were given 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!("{}",); LL | unimplemented!("{}",);
| ^^ | ^^
error: 1 positional argument in format string, but no arguments were given 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, "{}",)?; LL | write!(f, "{}",)?;
| ^^ | ^^

View file

@ -9,6 +9,7 @@
#[cfg(std)] use std::fmt; #[cfg(std)] use std::fmt;
#[cfg(core)] use core::fmt; #[cfg(core)] use core::fmt;
#[cfg(core)] #[lang = "eh_personality"] fn eh_personality() {} #[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 {} } #[cfg(core)] #[lang = "panic_impl"] fn panic_impl(panic: &core::panic::PanicInfo) -> ! { loop {} }
// (see documentation of the similarly-named test in run-pass) // (see documentation of the similarly-named test in run-pass)

View file

@ -1,59 +1,59 @@
error: 1 positional argument in format string, but no arguments were given 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, "{}",); LL | assert_eq!(1, 1, "{}",);
| ^^ | ^^
error: 1 positional argument in format string, but no arguments were given 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, "{}",); LL | assert_ne!(1, 2, "{}",);
| ^^ | ^^
error: 1 positional argument in format string, but no arguments were given 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, "{}",); LL | debug_assert_eq!(1, 1, "{}",);
| ^^ | ^^
error: 1 positional argument in format string, but no arguments were given 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, "{}",); LL | debug_assert_ne!(1, 2, "{}",);
| ^^ | ^^
error: 1 positional argument in format string, but no arguments were given 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!("{}",); LL | eprint!("{}",);
| ^^ | ^^
error: 1 positional argument in format string, but no arguments were given 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!("{}",); LL | format!("{}",);
| ^^ | ^^
error: 1 positional argument in format string, but no arguments were given 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!("{}",); LL | format_args!("{}",);
| ^^ | ^^
error: 1 positional argument in format string, but no arguments were given 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!("{}",); LL | print!("{}",);
| ^^ | ^^
error: 1 positional argument in format string, but no arguments were given 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!("{}",); LL | unimplemented!("{}",);
| ^^ | ^^
error: 1 positional argument in format string, but no arguments were given 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, "{}",)?; LL | write!(f, "{}",)?;
| ^^ | ^^

View file

@ -12,4 +12,5 @@ fn main() {
} }
#[lang = "eh_personality"] extern fn eh_personality() {} #[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 {} } #[lang = "panic_impl"] fn panic_impl(panic: &PanicInfo) -> ! { loop {} }

View file

@ -11,3 +11,5 @@ use core::panic::PanicInfo;
fn panic_impl(info: &PanicInfo) -> ! { loop {} } fn panic_impl(info: &PanicInfo) -> ! { loop {} }
#[lang = "eh_personality"] #[lang = "eh_personality"]
fn eh_personality() {} fn eh_personality() {}
#[lang = "eh_catch_typeinfo"]
static EH_CATCH_TYPEINFO: u8 = 0;

View file

@ -14,6 +14,9 @@ use core::ops::RangeBounds;
#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))]
#[lang = "eh_personality"] #[lang = "eh_personality"]
extern fn 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 // take a reference to any built-in range

View file

@ -1,7 +1,7 @@
error: `#[panic_handler]` function required, but not found error: `#[panic_handler]` function required, but not found
error[E0308]: mismatched types 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); LL | take_range(0..1);
| ^^^^ | ^^^^
@ -13,7 +13,7 @@ LL | take_range(0..1);
found struct `core::ops::Range<{integer}>` found struct `core::ops::Range<{integer}>`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/issue-54505-no-std.rs:29:16 --> $DIR/issue-54505-no-std.rs:32:16
| |
LL | take_range(1..); LL | take_range(1..);
| ^^^ | ^^^
@ -25,7 +25,7 @@ LL | take_range(1..);
found struct `core::ops::RangeFrom<{integer}>` found struct `core::ops::RangeFrom<{integer}>`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/issue-54505-no-std.rs:34:16 --> $DIR/issue-54505-no-std.rs:37:16
| |
LL | take_range(..); LL | take_range(..);
| ^^ | ^^
@ -37,7 +37,7 @@ LL | take_range(..);
found struct `core::ops::RangeFull` found struct `core::ops::RangeFull`
error[E0308]: mismatched types 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); LL | take_range(0..=1);
| ^^^^^ | ^^^^^
@ -49,7 +49,7 @@ LL | take_range(0..=1);
found struct `core::ops::RangeInclusive<{integer}>` found struct `core::ops::RangeInclusive<{integer}>`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/issue-54505-no-std.rs:44:16 --> $DIR/issue-54505-no-std.rs:47:16
| |
LL | take_range(..5); LL | take_range(..5);
| ^^^ | ^^^
@ -61,7 +61,7 @@ LL | take_range(..5);
found struct `core::ops::RangeTo<{integer}>` found struct `core::ops::RangeTo<{integer}>`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/issue-54505-no-std.rs:49:16 --> $DIR/issue-54505-no-std.rs:52:16
| |
LL | take_range(..=42); LL | take_range(..=42);
| ^^^^^ | ^^^^^