Skip to content

Commit 76660fd

Browse files
committed
Make Emscripten unwinding use a valid type_info
This allows catch_panic to ignore C++ exceptions.
1 parent ad61c88 commit 76660fd

File tree

5 files changed

+58
-17
lines changed

5 files changed

+58
-17
lines changed

src/doc/unstable-book/src/language-features/lang-items.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,8 @@ the source code.
252252
- `eh_personality`: `libpanic_unwind/gcc.rs` (GNU)
253253
- `eh_personality`: `libpanic_unwind/seh.rs` (SEH)
254254
- `eh_unwind_resume`: `libpanic_unwind/gcc.rs` (GCC)
255-
- `msvc_try_filter`: `libpanic_unwind/seh.rs` (SEH)
255+
- `eh_catch_typeinfo`: `libpanic_unwind/seh.rs` (SEH)
256+
- `eh_catch_typeinfo`: `libpanic_unwind/emcc.rs` (EMCC)
256257
- `panic`: `libcore/panicking.rs`
257258
- `panic_bounds_check`: `libcore/panicking.rs`
258259
- `panic_impl`: `libcore/panicking.rs`

src/libpanic_unwind/emcc.rs

+42-10
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,48 @@ use alloc::boxed::Box;
1515
use libc::{self, c_int};
1616
use unwind as uw;
1717

18+
// This matches the layout of std::type_info in C++
19+
#[repr(C)]
20+
struct TypeInfo {
21+
vtable: *const usize,
22+
name: *const u8,
23+
}
24+
unsafe impl Sync for TypeInfo {}
25+
26+
extern "C" {
27+
// The leading `\x01` byte here is actually a magical signal to LLVM to
28+
// *not* apply any other mangling like prefixing with a `_` character.
29+
//
30+
// This symbol is the vtable used by C++'s `std::type_info`. Objects of type
31+
// `std::type_info`, type descriptors, have a pointer to this table. Type
32+
// descriptors are referenced by the C++ EH structures defined above and
33+
// that we construct below.
34+
//
35+
// Note that the real size is larger than 3 usize, but we only need our
36+
// vtable to point to the third element.
37+
#[link_name = "\x01_ZTVN10__cxxabiv117__class_type_infoE"]
38+
static CLASS_TYPE_INFO_VTABLE: [usize; 3];
39+
}
40+
41+
// std::type_info for a rust_panic class
42+
#[lang = "eh_catch_typeinfo"]
43+
static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo {
44+
// Normally we would use .as_ptr().add(2) but this doesn't work in a const context.
45+
vtable: unsafe { &CLASS_TYPE_INFO_VTABLE[2] },
46+
// This intentionally doesn't use the normal name mangling scheme because
47+
// we don't want C++ to be able to produce or catch Rust panics.
48+
name: b"rust_panic\0".as_ptr(),
49+
};
50+
1851
pub fn payload() -> *mut u8 {
1952
ptr::null_mut()
2053
}
2154

2255
pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
2356
assert!(!ptr.is_null());
24-
let ex = ptr::read(ptr as *mut _);
25-
__cxa_free_exception(ptr as *mut _);
57+
let adjusted_ptr = __cxa_begin_catch(ptr as *mut libc::c_void);
58+
let ex = ptr::read(adjusted_ptr as *mut _);
59+
__cxa_end_catch();
2660
ex
2761
}
2862

@@ -32,11 +66,8 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
3266
if exception == ptr::null_mut() {
3367
return uw::_URC_FATAL_PHASE1_ERROR as u32;
3468
}
35-
let exception = exception as *mut Box<dyn Any + Send>;
36-
ptr::write(exception, data);
37-
__cxa_throw(exception as *mut _, ptr::null_mut(), ptr::null_mut());
38-
39-
unreachable!()
69+
ptr::write(exception as *mut _, data);
70+
__cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, ptr::null_mut());
4071
}
4172

4273
#[lang = "eh_personality"]
@@ -52,10 +83,11 @@ unsafe extern "C" fn rust_eh_personality(version: c_int,
5283

5384
extern "C" {
5485
fn __cxa_allocate_exception(thrown_size: libc::size_t) -> *mut libc::c_void;
55-
fn __cxa_free_exception(thrown_exception: *mut libc::c_void);
86+
fn __cxa_begin_catch(thrown_exception: *mut libc::c_void) -> *mut libc::c_void;
87+
fn __cxa_end_catch();
5688
fn __cxa_throw(thrown_exception: *mut libc::c_void,
57-
tinfo: *mut libc::c_void,
58-
dest: *mut libc::c_void);
89+
tinfo: *const TypeInfo,
90+
dest: *mut libc::c_void) -> !;
5991
fn __gxx_personality_v0(version: c_int,
6092
actions: uw::_Unwind_Action,
6193
exception_class: uw::_Unwind_Exception_Class,

src/libpanic_unwind/seh.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,13 @@ extern "C" {
213213
static TYPE_INFO_VTABLE: *const u8;
214214
}
215215

216-
// We use #[lang = "msvc_try_filter"] here as this is the type descriptor which
216+
// We use #[lang = "eh_catch_typeinfo"] here as this is the type descriptor which
217217
// we'll use in LLVM's `catchpad` instruction which ends up also being passed as
218218
// an argument to the C++ personality function.
219219
//
220220
// Again, I'm not entirely sure what this is describing, it just seems to work.
221-
#[cfg_attr(not(test), lang = "msvc_try_filter")]
221+
#[cfg_attr(bootstrap, lang = "msvc_try_filter")]
222+
#[cfg_attr(not(any(test, bootstrap)), lang = "eh_catch_typeinfo")]
222223
static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
223224
pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _,
224225
spare: core::ptr::null_mut(),

src/librustc/middle/lang_items.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ language_item_table! {
385385

386386
EhPersonalityLangItem, "eh_personality", eh_personality, Target::Fn;
387387
EhUnwindResumeLangItem, "eh_unwind_resume", eh_unwind_resume, Target::Fn;
388-
MSVCTryFilterLangItem, "msvc_try_filter", msvc_try_filter, Target::Static;
388+
EhCatchTypeinfoLangItem, "eh_catch_typeinfo", eh_catch_typeinfo, Target::Static;
389389

390390
OwnedBoxLangItem, "owned_box", owned_box, Target::Struct;
391391

src/librustc_codegen_llvm/intrinsic.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -899,9 +899,9 @@ fn codegen_msvc_try(
899899
let cs = catchswitch.catch_switch(None, None, 1);
900900
catchswitch.add_handler(cs, catchpad.llbb());
901901

902-
let tydesc = match bx.tcx().lang_items().msvc_try_filter() {
902+
let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() {
903903
Some(did) => bx.get_static(did),
904-
None => bug!("msvc_try_filter not defined"),
904+
None => bug!("eh_catch_typeinfo not defined, but needed for SEH unwinding"),
905905
};
906906
let funclet = catchpad.catch_pad(cs, &[tydesc, bx.const_i32(0), slot]);
907907

@@ -975,7 +975,14 @@ fn codegen_gnu_try(
975975
// rust_try ignores the selector.
976976
let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false);
977977
let vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 1);
978-
catch.add_clause(vals, bx.const_null(bx.type_i8p()));
978+
let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() {
979+
Some(tydesc) => {
980+
let tydesc = bx.get_static(tydesc);
981+
bx.bitcast(tydesc, bx.type_i8p())
982+
}
983+
None => bx.const_null(bx.type_i8p()),
984+
};
985+
catch.add_clause(vals, tydesc);
979986
let ptr = catch.extract_value(vals, 0);
980987
let ptr_align = bx.tcx().data_layout.pointer_align.abi;
981988
let bitcast = catch.bitcast(local_ptr, bx.type_ptr_to(bx.type_i8p()));

0 commit comments

Comments
 (0)