mirror of
https://github.com/rust-lang/rust.git
synced 2024-10-30 05:51:58 +00:00
Auto merge of #70212 - Amanieu:catch_foreign, r=Mark-Simulacrum
Abort when foreign exceptions are caught by catch_unwind Prior to this PR, foreign exceptions were not caught by catch_unwind, and instead passed through invisibly. This represented a painful soundness hole in some libraries ([take_mut](https://github.com/Sgeo/take_mut/blob/master/src/lib.rs#L37)), which relied on `catch_unwind` to handle all possible exit paths from a closure. With this PR, foreign exceptions are now caught by `catch_unwind` and will trigger an abort since catching foreign exceptions is currently UB according to the latest proposals by the FFI unwind project group. cc @rust-lang/wg-ffi-unwind
This commit is contained in:
commit
41aaa90c67
@ -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]
|
||||
|
@ -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<EHAction, ()> {
|
||||
pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result<EHAction, ()> {
|
||||
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)
|
||||
|
@ -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<dyn Any + Send> {
|
||||
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<dyn Any + Send>) -> u32 {
|
||||
@ -68,25 +91,16 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> 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,
|
||||
|
@ -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> {
|
||||
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<EHAction, ()> {
|
||||
unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result<EHAction, ()> {
|
||||
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
|
||||
|
@ -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;
|
||||
|
@ -309,15 +309,21 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> 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<dyn Any + Send> {
|
||||
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
|
||||
|
@ -359,6 +359,9 @@ impl<F: Future> Future for AssertUnwindSafe<F> {
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
|
@ -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,
|
||||
|
@ -82,6 +82,7 @@ pub struct CodegenCx<'ll, 'tcx> {
|
||||
pub dbg_cx: Option<debuginfo::CrateDebugContext<'ll, 'tcx>>,
|
||||
|
||||
eh_personality: Cell<Option<&'ll Value>>,
|
||||
eh_catch_typeinfo: Cell<Option<&'ll Value>>,
|
||||
pub rust_try_fn: Cell<Option<&'ll Value>>,
|
||||
|
||||
intrinsics: RefCell<FxHashMap<&'static str, &'ll Value>>,
|
||||
@ -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> {
|
||||
|
@ -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 <stdint.h>
|
||||
//
|
||||
// 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));
|
||||
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());
|
||||
|
||||
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));
|
||||
});
|
||||
@ -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>(
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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 };
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 {}
|
||||
}
|
||||
|
@ -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) -> ! {
|
||||
|
@ -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, "{}",)?;
|
||||
| ^^
|
||||
|
@ -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)
|
||||
|
@ -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, "{}",)?;
|
||||
| ^^
|
||||
|
@ -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 {} }
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
| ^^^^^
|
||||
|
Loading…
Reference in New Issue
Block a user