Auto merge of #27210 - vadimcn:win64-eh-pers, r=alexcrichton
After this change, the only remaining symbol we are pulling from libgcc on Win64 is `__chkstk_ms` - the stack probing routine.
This commit is contained in:
commit
ceded6adb3
25 changed files with 671 additions and 241 deletions
|
@ -54,6 +54,7 @@ fn main(argc: isize, argv: *const *const u8) -> isize {
|
||||||
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
||||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||||
|
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
|
||||||
```
|
```
|
||||||
|
|
||||||
Note the use of `abort`: the `exchange_malloc` lang item is assumed to
|
Note the use of `abort`: the `exchange_malloc` lang item is assumed to
|
||||||
|
|
|
@ -39,6 +39,7 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
||||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||||
|
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
|
||||||
# // fn main() {} tricked you, rustdoc!
|
# // fn main() {} tricked you, rustdoc!
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -63,6 +64,7 @@ pub extern fn main(argc: i32, argv: *const *const u8) -> i32 {
|
||||||
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
||||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||||
|
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
|
||||||
# // fn main() {} tricked you, rustdoc!
|
# // fn main() {} tricked you, rustdoc!
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -150,6 +152,7 @@ extern fn panic_fmt(args: &core::fmt::Arguments,
|
||||||
|
|
||||||
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
||||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||||
|
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
|
||||||
# #[start] fn start(argc: isize, argv: *const *const u8) -> isize { 0 }
|
# #[start] fn start(argc: isize, argv: *const *const u8) -> isize { 0 }
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
```
|
```
|
||||||
|
|
|
@ -327,6 +327,7 @@ lets_do_this! {
|
||||||
|
|
||||||
EhPersonalityLangItem, "eh_personality", eh_personality;
|
EhPersonalityLangItem, "eh_personality", eh_personality;
|
||||||
EhPersonalityCatchLangItem, "eh_personality_catch", eh_personality_catch;
|
EhPersonalityCatchLangItem, "eh_personality_catch", eh_personality_catch;
|
||||||
|
EhUnwindResumeLangItem, "eh_unwind_resume", eh_unwind_resume;
|
||||||
MSVCTryFilterLangItem, "msvc_try_filter", msvc_try_filter;
|
MSVCTryFilterLangItem, "msvc_try_filter", msvc_try_filter;
|
||||||
|
|
||||||
ExchangeHeapLangItem, "exchange_heap", exchange_heap;
|
ExchangeHeapLangItem, "exchange_heap", exchange_heap;
|
||||||
|
|
|
@ -45,6 +45,10 @@ pub fn check_crate(krate: &ast::Crate,
|
||||||
if items.eh_personality().is_none() {
|
if items.eh_personality().is_none() {
|
||||||
items.missing.push(lang_items::EhPersonalityLangItem);
|
items.missing.push(lang_items::EhPersonalityLangItem);
|
||||||
}
|
}
|
||||||
|
if sess.target.target.options.custom_unwind_resume &
|
||||||
|
items.eh_unwind_resume().is_none() {
|
||||||
|
items.missing.push(lang_items::EhUnwindResumeLangItem);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut cx = Context { sess: sess, items: items };
|
let mut cx = Context { sess: sess, items: items };
|
||||||
|
@ -122,4 +126,5 @@ weak_lang_items! {
|
||||||
panic_fmt, PanicFmtLangItem, rust_begin_unwind;
|
panic_fmt, PanicFmtLangItem, rust_begin_unwind;
|
||||||
stack_exhausted, StackExhaustedLangItem, rust_stack_exhausted;
|
stack_exhausted, StackExhaustedLangItem, rust_stack_exhausted;
|
||||||
eh_personality, EhPersonalityLangItem, rust_eh_personality;
|
eh_personality, EhPersonalityLangItem, rust_eh_personality;
|
||||||
|
eh_unwind_resume, EhUnwindResumeLangItem, rust_eh_unwind_resume;
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,6 +171,11 @@ pub struct TargetOptions {
|
||||||
/// currently only "gnu" is used to fall into LLVM. Unknown strings cause
|
/// currently only "gnu" is used to fall into LLVM. Unknown strings cause
|
||||||
/// the system linker to be used.
|
/// the system linker to be used.
|
||||||
pub archive_format: String,
|
pub archive_format: String,
|
||||||
|
/// Whether the target uses a custom unwind resumption routine.
|
||||||
|
/// By default LLVM lowers `resume` instructions into calls to `_Unwind_Resume`
|
||||||
|
/// defined in libgcc. If this option is enabled, the target must provide
|
||||||
|
/// `eh_unwind_resume` lang item.
|
||||||
|
pub custom_unwind_resume: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TargetOptions {
|
impl Default for TargetOptions {
|
||||||
|
@ -209,6 +214,7 @@ impl Default for TargetOptions {
|
||||||
pre_link_objects: Vec::new(),
|
pre_link_objects: Vec::new(),
|
||||||
post_link_objects: Vec::new(),
|
post_link_objects: Vec::new(),
|
||||||
archive_format: String::new(),
|
archive_format: String::new(),
|
||||||
|
custom_unwind_resume: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ pub fn target() -> Target {
|
||||||
// On Win64 unwinding is handled by the OS, so we can link libgcc statically.
|
// On Win64 unwinding is handled by the OS, so we can link libgcc statically.
|
||||||
base.pre_link_args.push("-static-libgcc".to_string());
|
base.pre_link_args.push("-static-libgcc".to_string());
|
||||||
base.pre_link_args.push("-m64".to_string());
|
base.pre_link_args.push("-m64".to_string());
|
||||||
|
base.custom_unwind_resume = true;
|
||||||
|
|
||||||
Target {
|
Target {
|
||||||
llvm_target: "x86_64-pc-windows-gnu".to_string(),
|
llvm_target: "x86_64-pc-windows-gnu".to_string(),
|
||||||
|
|
|
@ -2171,6 +2171,12 @@ fn finish_register_fn(ccx: &CrateContext, sym: String, node_id: ast::NodeId,
|
||||||
llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass);
|
llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ccx.tcx().lang_items.eh_unwind_resume() == Some(def) {
|
||||||
|
llvm::SetLinkage(llfn, llvm::ExternalLinkage);
|
||||||
|
if ccx.use_dll_storage_attrs() {
|
||||||
|
llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
|
|
|
@ -846,6 +846,8 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
|
||||||
|
|
||||||
debug!("get_or_create_landing_pad");
|
debug!("get_or_create_landing_pad");
|
||||||
|
|
||||||
|
self.inject_unwind_resume_hook();
|
||||||
|
|
||||||
// Check if a landing pad block exists; if not, create one.
|
// Check if a landing pad block exists; if not, create one.
|
||||||
{
|
{
|
||||||
let mut scopes = self.scopes.borrow_mut();
|
let mut scopes = self.scopes.borrow_mut();
|
||||||
|
|
|
@ -561,6 +561,55 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// By default, LLVM lowers `resume` instructions into calls to `_Unwind_Resume`
|
||||||
|
/// defined in libgcc, however, unlike personality routines, there is no easy way to
|
||||||
|
/// override that symbol. This method injects a local-scoped `_Unwind_Resume` function
|
||||||
|
/// which immediately defers to the user-defined `eh_unwind_resume` lang item.
|
||||||
|
pub fn inject_unwind_resume_hook(&self) {
|
||||||
|
let ccx = self.ccx;
|
||||||
|
if !ccx.sess().target.target.options.custom_unwind_resume ||
|
||||||
|
ccx.unwind_resume_hooked().get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_resume = match ccx.tcx().lang_items.eh_unwind_resume() {
|
||||||
|
Some(did) => callee::trans_fn_ref(ccx, did, ExprId(0), &self.param_substs).val,
|
||||||
|
None => {
|
||||||
|
let fty = Type::variadic_func(&[], &Type::void(self.ccx));
|
||||||
|
declare::declare_cfn(self.ccx, "rust_eh_unwind_resume", fty,
|
||||||
|
self.ccx.tcx().mk_nil())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let resume_type = Type::func(&[Type::i8(ccx).ptr_to()], &Type::void(ccx));
|
||||||
|
let old_resume = llvm::LLVMAddFunction(ccx.llmod(),
|
||||||
|
"_Unwind_Resume\0".as_ptr() as *const _,
|
||||||
|
resume_type.to_ref());
|
||||||
|
llvm::SetLinkage(old_resume, llvm::InternalLinkage);
|
||||||
|
let llbb = llvm::LLVMAppendBasicBlockInContext(ccx.llcx(),
|
||||||
|
old_resume,
|
||||||
|
"\0".as_ptr() as *const _);
|
||||||
|
let builder = ccx.builder();
|
||||||
|
builder.position_at_end(llbb);
|
||||||
|
builder.call(new_resume, &[llvm::LLVMGetFirstParam(old_resume)], None);
|
||||||
|
builder.unreachable(); // it should never return
|
||||||
|
|
||||||
|
// Until DwarfEHPrepare pass has run, _Unwind_Resume is not referenced by any live code
|
||||||
|
// and is subject to dead code elimination. Here we add _Unwind_Resume to @llvm.globals
|
||||||
|
// to prevent that.
|
||||||
|
let i8p_ty = Type::i8p(ccx);
|
||||||
|
let used_ty = Type::array(&i8p_ty, 1);
|
||||||
|
let used = llvm::LLVMAddGlobal(ccx.llmod(), used_ty.to_ref(),
|
||||||
|
"llvm.used\0".as_ptr() as *const _);
|
||||||
|
let old_resume = llvm::LLVMConstBitCast(old_resume, i8p_ty.to_ref());
|
||||||
|
llvm::LLVMSetInitializer(used, C_array(i8p_ty, &[old_resume]));
|
||||||
|
llvm::SetLinkage(used, llvm::AppendingLinkage);
|
||||||
|
llvm::LLVMSetSection(used, "llvm.metadata\0".as_ptr() as *const _)
|
||||||
|
}
|
||||||
|
ccx.unwind_resume_hooked().set(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Basic block context. We create a block context for each basic block
|
// Basic block context. We create a block context for each basic block
|
||||||
|
|
|
@ -146,6 +146,7 @@ pub struct LocalCrateContext<'tcx> {
|
||||||
|
|
||||||
eh_personality: RefCell<Option<ValueRef>>,
|
eh_personality: RefCell<Option<ValueRef>>,
|
||||||
rust_try_fn: RefCell<Option<ValueRef>>,
|
rust_try_fn: RefCell<Option<ValueRef>>,
|
||||||
|
unwind_resume_hooked: Cell<bool>,
|
||||||
|
|
||||||
intrinsics: RefCell<FnvHashMap<&'static str, ValueRef>>,
|
intrinsics: RefCell<FnvHashMap<&'static str, ValueRef>>,
|
||||||
|
|
||||||
|
@ -466,6 +467,7 @@ impl<'tcx> LocalCrateContext<'tcx> {
|
||||||
dbg_cx: dbg_cx,
|
dbg_cx: dbg_cx,
|
||||||
eh_personality: RefCell::new(None),
|
eh_personality: RefCell::new(None),
|
||||||
rust_try_fn: RefCell::new(None),
|
rust_try_fn: RefCell::new(None),
|
||||||
|
unwind_resume_hooked: Cell::new(false),
|
||||||
intrinsics: RefCell::new(FnvHashMap()),
|
intrinsics: RefCell::new(FnvHashMap()),
|
||||||
n_llvm_insns: Cell::new(0),
|
n_llvm_insns: Cell::new(0),
|
||||||
trait_cache: RefCell::new(FnvHashMap()),
|
trait_cache: RefCell::new(FnvHashMap()),
|
||||||
|
@ -735,6 +737,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
|
||||||
&self.local.rust_try_fn
|
&self.local.rust_try_fn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unwind_resume_hooked<'a>(&'a self) -> &'a Cell<bool> {
|
||||||
|
&self.local.unwind_resume_hooked
|
||||||
|
}
|
||||||
|
|
||||||
fn intrinsics<'a>(&'a self) -> &'a RefCell<FnvHashMap<&'static str, ValueRef>> {
|
fn intrinsics<'a>(&'a self) -> &'a RefCell<FnvHashMap<&'static str, ValueRef>> {
|
||||||
&self.local.intrinsics
|
&self.local.intrinsics
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ use llvm::{SequentiallyConsistent, Acquire, Release, AtomicXchg, ValueRef, TypeK
|
||||||
use middle::subst;
|
use middle::subst;
|
||||||
use middle::subst::FnSpace;
|
use middle::subst::FnSpace;
|
||||||
use trans::adt;
|
use trans::adt;
|
||||||
|
use trans::attributes;
|
||||||
use trans::base::*;
|
use trans::base::*;
|
||||||
use trans::build::*;
|
use trans::build::*;
|
||||||
use trans::callee;
|
use trans::callee;
|
||||||
|
@ -1159,26 +1160,14 @@ fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||||
// of exceptions (e.g. the normal semantics of LLVM's landingpad and invoke
|
// of exceptions (e.g. the normal semantics of LLVM's landingpad and invoke
|
||||||
// instructions).
|
// instructions).
|
||||||
//
|
//
|
||||||
// This translation is a little surprising for two reasons:
|
// This translation is a little surprising because
|
||||||
//
|
// we always call a shim function instead of inlining the call to `invoke`
|
||||||
// 1. We always call a shim function instead of inlining the call to `invoke`
|
|
||||||
// manually here. This is done because in LLVM we're only allowed to have one
|
// manually here. This is done because in LLVM we're only allowed to have one
|
||||||
// personality per function definition. The call to the `try` intrinsic is
|
// personality per function definition. The call to the `try` intrinsic is
|
||||||
// being inlined into the function calling it, and that function may already
|
// being inlined into the function calling it, and that function may already
|
||||||
// have other personality functions in play. By calling a shim we're
|
// have other personality functions in play. By calling a shim we're
|
||||||
// guaranteed that our shim will have the right personality function.
|
// guaranteed that our shim will have the right personality function.
|
||||||
//
|
//
|
||||||
// 2. Instead of making one shim (explained above), we make two shims! The
|
|
||||||
// reason for this has to do with the technical details about the
|
|
||||||
// implementation of unwinding in the runtime, but the tl;dr; is that the
|
|
||||||
// outer shim's personality function says "catch rust exceptions" and the
|
|
||||||
// inner shim's landing pad will not `resume` the exception being thrown.
|
|
||||||
// This means that the outer shim's landing pad is never run and the inner
|
|
||||||
// shim's return value is the return value of the whole call.
|
|
||||||
//
|
|
||||||
// The double-shim aspect is currently done for implementation ease on the
|
|
||||||
// runtime side of things, and more info can be found in
|
|
||||||
// src/libstd/rt/unwind/gcc.rs.
|
|
||||||
fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||||
func: ValueRef,
|
func: ValueRef,
|
||||||
data: ValueRef,
|
data: ValueRef,
|
||||||
|
@ -1188,38 +1177,52 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||||
let ccx = bcx.ccx();
|
let ccx = bcx.ccx();
|
||||||
let dloc = DebugLoc::None;
|
let dloc = DebugLoc::None;
|
||||||
|
|
||||||
// Type indicator for the exception being thrown, not entirely sure
|
// Translates the shims described above:
|
||||||
// what's going on here but it's what all the examples in LLVM use.
|
//
|
||||||
let lpad_ty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)],
|
// bcx:
|
||||||
false);
|
// invoke %func(%args...) normal %normal unwind %catch
|
||||||
|
//
|
||||||
|
// normal:
|
||||||
|
// ret null
|
||||||
|
//
|
||||||
|
// catch:
|
||||||
|
// (ptr, _) = landingpad
|
||||||
|
// ret ptr
|
||||||
|
|
||||||
// Define the "inner try" shim
|
let rust_try = declare::define_internal_rust_fn(ccx, "__rust_try", try_fn_ty);
|
||||||
let rust_try_inner = declare::define_internal_rust_fn(ccx,
|
attributes::emit_uwtable(rust_try, true);
|
||||||
"__rust_try_inner",
|
|
||||||
try_fn_ty);
|
|
||||||
trans_rust_try(ccx, rust_try_inner, lpad_ty, bcx.fcx.eh_personality(),
|
|
||||||
output, dloc, &mut |bcx, then, catch| {
|
|
||||||
let func = llvm::get_param(rust_try_inner, 0);
|
|
||||||
let data = llvm::get_param(rust_try_inner, 1);
|
|
||||||
Invoke(bcx, func, &[data], then.llbb, catch.llbb, None, dloc);
|
|
||||||
C_null(Type::i8p(ccx))
|
|
||||||
});
|
|
||||||
|
|
||||||
// Define the "outer try" shim.
|
|
||||||
let rust_try = declare::define_internal_rust_fn(ccx, "__rust_try",
|
|
||||||
try_fn_ty);
|
|
||||||
let catch_pers = match bcx.tcx().lang_items.eh_personality_catch() {
|
let catch_pers = match bcx.tcx().lang_items.eh_personality_catch() {
|
||||||
Some(did) => callee::trans_fn_ref(ccx, did, ExprId(0),
|
Some(did) => callee::trans_fn_ref(ccx, did, ExprId(0),
|
||||||
bcx.fcx.param_substs).val,
|
bcx.fcx.param_substs).val,
|
||||||
None => bcx.tcx().sess.bug("eh_personality_catch not defined"),
|
None => bcx.tcx().sess.bug("eh_personality_catch not defined"),
|
||||||
};
|
};
|
||||||
trans_rust_try(ccx, rust_try, lpad_ty, catch_pers, output, dloc,
|
|
||||||
&mut |bcx, then, catch| {
|
let (fcx, block_arena);
|
||||||
|
block_arena = TypedArena::new();
|
||||||
|
fcx = new_fn_ctxt(ccx, rust_try, ast::DUMMY_NODE_ID, false,
|
||||||
|
output, ccx.tcx().mk_substs(Substs::trans_empty()),
|
||||||
|
None, &block_arena);
|
||||||
|
let bcx = init_function(&fcx, true, output);
|
||||||
|
let then = bcx.fcx.new_temp_block("then");
|
||||||
|
let catch = bcx.fcx.new_temp_block("catch");
|
||||||
|
|
||||||
let func = llvm::get_param(rust_try, 0);
|
let func = llvm::get_param(rust_try, 0);
|
||||||
let data = llvm::get_param(rust_try, 1);
|
let data = llvm::get_param(rust_try, 1);
|
||||||
Invoke(bcx, rust_try_inner, &[func, data], then.llbb, catch.llbb,
|
Invoke(bcx, func, &[data], then.llbb, catch.llbb, None, dloc);
|
||||||
None, dloc)
|
Ret(then, C_null(Type::i8p(ccx)), dloc);
|
||||||
});
|
|
||||||
|
// 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. rust_try ignores the selector.
|
||||||
|
let lpad_ty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)],
|
||||||
|
false);
|
||||||
|
let vals = LandingPad(catch, lpad_ty, catch_pers, 1);
|
||||||
|
AddClause(catch, vals, C_null(Type::i8p(ccx)));
|
||||||
|
let ptr = ExtractValue(catch, vals, 0);
|
||||||
|
Ret(catch, ptr, dloc);
|
||||||
|
fcx.cleanup();
|
||||||
|
|
||||||
return rust_try
|
return rust_try
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1228,68 +1231,9 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||||
let ret = Call(bcx, llfn, &[func, data], None, dloc);
|
let ret = Call(bcx, llfn, &[func, data], None, dloc);
|
||||||
Store(bcx, ret, dest);
|
Store(bcx, ret, dest);
|
||||||
return bcx;
|
return bcx;
|
||||||
|
|
||||||
// Translates both the inner and outer shims described above. The only
|
|
||||||
// difference between these two is the function invoked and the personality
|
|
||||||
// involved, so a common routine is shared.
|
|
||||||
//
|
|
||||||
// bcx:
|
|
||||||
// invoke %func(%args...) normal %normal unwind %unwind
|
|
||||||
//
|
|
||||||
// normal:
|
|
||||||
// ret null
|
|
||||||
//
|
|
||||||
// unwind:
|
|
||||||
// (ptr, _) = landingpad
|
|
||||||
// br (ptr != null), done, reraise
|
|
||||||
//
|
|
||||||
// done:
|
|
||||||
// ret ptr
|
|
||||||
//
|
|
||||||
// reraise:
|
|
||||||
// resume
|
|
||||||
//
|
|
||||||
// Note that the branch checking for `null` here isn't actually necessary,
|
|
||||||
// it's just an unfortunate hack to make sure that LLVM doesn't optimize too
|
|
||||||
// much. If this were not present, then LLVM would correctly deduce that our
|
|
||||||
// inner shim should be tagged with `nounwind` (as it catches all
|
|
||||||
// exceptions) and then the outer shim's `invoke` will be translated to just
|
|
||||||
// a simple call, destroying that entry for the personality function.
|
|
||||||
//
|
|
||||||
// To ensure that both shims always have an `invoke` this check against null
|
|
||||||
// confuses LLVM enough to the point that it won't infer `nounwind` and
|
|
||||||
// we'll proceed as normal.
|
|
||||||
fn trans_rust_try<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|
||||||
llfn: ValueRef,
|
|
||||||
lpad_ty: Type,
|
|
||||||
personality: ValueRef,
|
|
||||||
output: ty::FnOutput<'tcx>,
|
|
||||||
dloc: DebugLoc,
|
|
||||||
invoke: &mut FnMut(Block, Block, Block) -> ValueRef) {
|
|
||||||
let (fcx, block_arena);
|
|
||||||
block_arena = TypedArena::new();
|
|
||||||
fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, false,
|
|
||||||
output, ccx.tcx().mk_substs(Substs::trans_empty()),
|
|
||||||
None, &block_arena);
|
|
||||||
let bcx = init_function(&fcx, true, output);
|
|
||||||
let then = bcx.fcx.new_temp_block("then");
|
|
||||||
let catch = bcx.fcx.new_temp_block("catch");
|
|
||||||
let reraise = bcx.fcx.new_temp_block("reraise");
|
|
||||||
let catch_return = bcx.fcx.new_temp_block("catch-return");
|
|
||||||
|
|
||||||
let invoke_ret = invoke(bcx, then, catch);
|
|
||||||
Ret(then, invoke_ret, dloc);
|
|
||||||
let vals = LandingPad(catch, lpad_ty, personality, 1);
|
|
||||||
AddClause(catch, vals, C_null(Type::i8p(ccx)));
|
|
||||||
let ptr = ExtractValue(catch, vals, 0);
|
|
||||||
let valid = ICmp(catch, llvm::IntNE, ptr, C_null(Type::i8p(ccx)), dloc);
|
|
||||||
CondBr(catch, valid, catch_return.llbb, reraise.llbb, dloc);
|
|
||||||
Ret(catch_return, ptr, dloc);
|
|
||||||
Resume(reraise, vals);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to generate the `Ty` associated with `rust_Try`
|
// Helper to generate the `Ty` associated with `rust_try`
|
||||||
fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
|
fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
|
||||||
f: &mut FnMut(Ty<'tcx>,
|
f: &mut FnMut(Ty<'tcx>,
|
||||||
ty::FnOutput<'tcx>) -> ValueRef)
|
ty::FnOutput<'tcx>) -> ValueRef)
|
||||||
|
@ -1299,8 +1243,7 @@ fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
|
||||||
return llfn
|
return llfn
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define the types up front for the signatures of the rust_try and
|
// Define the type up front for the signature of the rust_try function.
|
||||||
// rust_try_inner functions.
|
|
||||||
let tcx = ccx.tcx();
|
let tcx = ccx.tcx();
|
||||||
let i8p = tcx.mk_mut_ptr(tcx.types.i8);
|
let i8p = tcx.mk_mut_ptr(tcx.types.i8);
|
||||||
let fn_ty = tcx.mk_bare_fn(ty::BareFnTy {
|
let fn_ty = tcx.mk_bare_fn(ty::BareFnTy {
|
||||||
|
|
159
src/libstd/rt/dwarf/eh.rs
Normal file
159
src/libstd/rt/dwarf/eh.rs
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
//! Parsing of GCC-style Language-Specific Data Area (LSDA)
|
||||||
|
//! For details see:
|
||||||
|
//! http://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html
|
||||||
|
//! http://mentorembedded.github.io/cxx-abi/exceptions.pdf
|
||||||
|
//! http://www.airs.com/blog/archives/460
|
||||||
|
//! http://www.airs.com/blog/archives/464
|
||||||
|
//!
|
||||||
|
//! A reference implementation may be found in the GCC source tree
|
||||||
|
//! (<root>/libgcc/unwind-c.c as of this writing)
|
||||||
|
|
||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
use prelude::v1::*;
|
||||||
|
use rt::dwarf::DwarfReader;
|
||||||
|
use core::mem;
|
||||||
|
|
||||||
|
pub const DW_EH_PE_omit : u8 = 0xFF;
|
||||||
|
pub const DW_EH_PE_absptr : u8 = 0x00;
|
||||||
|
|
||||||
|
pub const DW_EH_PE_uleb128 : u8 = 0x01;
|
||||||
|
pub const DW_EH_PE_udata2 : u8 = 0x02;
|
||||||
|
pub const DW_EH_PE_udata4 : u8 = 0x03;
|
||||||
|
pub const DW_EH_PE_udata8 : u8 = 0x04;
|
||||||
|
pub const DW_EH_PE_sleb128 : u8 = 0x09;
|
||||||
|
pub const DW_EH_PE_sdata2 : u8 = 0x0A;
|
||||||
|
pub const DW_EH_PE_sdata4 : u8 = 0x0B;
|
||||||
|
pub const DW_EH_PE_sdata8 : u8 = 0x0C;
|
||||||
|
|
||||||
|
pub const DW_EH_PE_pcrel : u8 = 0x10;
|
||||||
|
pub const DW_EH_PE_textrel : u8 = 0x20;
|
||||||
|
pub const DW_EH_PE_datarel : u8 = 0x30;
|
||||||
|
pub const DW_EH_PE_funcrel : u8 = 0x40;
|
||||||
|
pub const DW_EH_PE_aligned : u8 = 0x50;
|
||||||
|
|
||||||
|
pub const DW_EH_PE_indirect : u8 = 0x80;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct EHContext {
|
||||||
|
pub ip: usize, // Current instruction pointer
|
||||||
|
pub func_start: usize, // Address of the current function
|
||||||
|
pub text_start: usize, // Address of the code section
|
||||||
|
pub data_start: usize, // Address of the data section
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn find_landing_pad(lsda: *const u8, context: &EHContext)
|
||||||
|
-> Option<usize> {
|
||||||
|
if lsda.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let func_start = context.func_start;
|
||||||
|
let mut reader = DwarfReader::new(lsda);
|
||||||
|
|
||||||
|
let start_encoding = reader.read::<u8>();
|
||||||
|
// base address for landing pad offsets
|
||||||
|
let lpad_base = if start_encoding != DW_EH_PE_omit {
|
||||||
|
read_encoded_pointer(&mut reader, context, start_encoding)
|
||||||
|
} else {
|
||||||
|
func_start
|
||||||
|
};
|
||||||
|
|
||||||
|
let ttype_encoding = reader.read::<u8>();
|
||||||
|
if ttype_encoding != DW_EH_PE_omit {
|
||||||
|
// Rust doesn't analyze exception types, so we don't care about the type table
|
||||||
|
reader.read_uleb128();
|
||||||
|
}
|
||||||
|
|
||||||
|
let call_site_encoding = reader.read::<u8>();
|
||||||
|
let call_site_table_length = reader.read_uleb128();
|
||||||
|
let action_table = reader.ptr.offset(call_site_table_length as isize);
|
||||||
|
// Return addresses point 1 byte past the call instruction, which could
|
||||||
|
// be in the next IP range.
|
||||||
|
let ip = context.ip-1;
|
||||||
|
|
||||||
|
while reader.ptr < action_table {
|
||||||
|
let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding);
|
||||||
|
let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding);
|
||||||
|
let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding);
|
||||||
|
let cs_action = reader.read_uleb128();
|
||||||
|
// Callsite table is sorted by cs_start, so if we've passed the ip, we
|
||||||
|
// may stop searching.
|
||||||
|
if ip < func_start + cs_start {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if ip < func_start + cs_start + cs_len {
|
||||||
|
if cs_lpad != 0 {
|
||||||
|
return Some(lpad_base + cs_lpad);
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// IP range not found: gcc's C++ personality calls terminate() here,
|
||||||
|
// however the rest of the languages treat this the same as cs_lpad == 0.
|
||||||
|
// We follow this suit.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn round_up(unrounded: usize, align: usize) -> usize {
|
||||||
|
assert!(align.is_power_of_two());
|
||||||
|
(unrounded + align - 1) & !(align - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn read_encoded_pointer(reader: &mut DwarfReader,
|
||||||
|
context: &EHContext,
|
||||||
|
encoding: u8) -> usize {
|
||||||
|
assert!(encoding != DW_EH_PE_omit);
|
||||||
|
|
||||||
|
// DW_EH_PE_aligned implies it's an absolute pointer value
|
||||||
|
if encoding == DW_EH_PE_aligned {
|
||||||
|
reader.ptr = round_up(reader.ptr as usize,
|
||||||
|
mem::size_of::<usize>()) as *const u8;
|
||||||
|
return reader.read::<usize>();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = match encoding & 0x0F {
|
||||||
|
DW_EH_PE_absptr => reader.read::<usize>(),
|
||||||
|
DW_EH_PE_uleb128 => reader.read_uleb128() as usize,
|
||||||
|
DW_EH_PE_udata2 => reader.read::<u16>() as usize,
|
||||||
|
DW_EH_PE_udata4 => reader.read::<u32>() as usize,
|
||||||
|
DW_EH_PE_udata8 => reader.read::<u64>() as usize,
|
||||||
|
DW_EH_PE_sleb128 => reader.read_sleb128() as usize,
|
||||||
|
DW_EH_PE_sdata2 => reader.read::<i16>() as usize,
|
||||||
|
DW_EH_PE_sdata4 => reader.read::<i32>() as usize,
|
||||||
|
DW_EH_PE_sdata8 => reader.read::<i64>() as usize,
|
||||||
|
_ => panic!()
|
||||||
|
};
|
||||||
|
|
||||||
|
result += match encoding & 0x70 {
|
||||||
|
DW_EH_PE_absptr => 0,
|
||||||
|
// relative to address of the encoded value, despite the name
|
||||||
|
DW_EH_PE_pcrel => reader.ptr as usize,
|
||||||
|
DW_EH_PE_textrel => { assert!(context.text_start != 0);
|
||||||
|
context.text_start },
|
||||||
|
DW_EH_PE_datarel => { assert!(context.data_start != 0);
|
||||||
|
context.data_start },
|
||||||
|
DW_EH_PE_funcrel => { assert!(context.func_start != 0);
|
||||||
|
context.func_start },
|
||||||
|
_ => panic!()
|
||||||
|
};
|
||||||
|
|
||||||
|
if encoding & DW_EH_PE_indirect != 0 {
|
||||||
|
result = *(result as *const usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
107
src/libstd/rt/dwarf/mod.rs
Normal file
107
src/libstd/rt/dwarf/mod.rs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
//! Utilities for parsing DWARF-encoded data streams.
|
||||||
|
//! See http://www.dwarfstd.org,
|
||||||
|
//! DWARF-4 standard, Section 7 - "Data Representation"
|
||||||
|
|
||||||
|
// This module is used only by x86_64-pc-windows-gnu for now, but we
|
||||||
|
// are compiling it everywhere to avoid regressions.
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
pub mod eh;
|
||||||
|
|
||||||
|
use prelude::v1::*;
|
||||||
|
use core::mem;
|
||||||
|
|
||||||
|
pub struct DwarfReader {
|
||||||
|
pub ptr : *const u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C,packed)]
|
||||||
|
struct Unaligned<T>(T);
|
||||||
|
|
||||||
|
impl DwarfReader {
|
||||||
|
|
||||||
|
pub fn new(ptr : *const u8) -> DwarfReader {
|
||||||
|
DwarfReader {
|
||||||
|
ptr : ptr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DWARF streams are packed, so e.g. a u32 would not necessarily be aligned
|
||||||
|
// on a 4-byte boundary. This may cause problems on platforms with strict
|
||||||
|
// alignment requirements. By wrapping data in a "packed" struct, we are
|
||||||
|
// telling the backend to generate "misalignment-safe" code.
|
||||||
|
pub unsafe fn read<T:Copy>(&mut self) -> T {
|
||||||
|
let Unaligned(result) = *(self.ptr as *const Unaligned<T>);
|
||||||
|
self.ptr = self.ptr.offset(mem::size_of::<T>() as isize);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable
|
||||||
|
// Length Data".
|
||||||
|
pub unsafe fn read_uleb128(&mut self) -> u64 {
|
||||||
|
let mut shift : usize = 0;
|
||||||
|
let mut result : u64 = 0;
|
||||||
|
let mut byte : u8;
|
||||||
|
loop {
|
||||||
|
byte = self.read::<u8>();
|
||||||
|
result |= ((byte & 0x7F) as u64) << shift;
|
||||||
|
shift += 7;
|
||||||
|
if byte & 0x80 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn read_sleb128(&mut self) -> i64 {
|
||||||
|
let mut shift : usize = 0;
|
||||||
|
let mut result : u64 = 0;
|
||||||
|
let mut byte : u8;
|
||||||
|
loop {
|
||||||
|
byte = self.read::<u8>();
|
||||||
|
result |= ((byte & 0x7F) as u64) << shift;
|
||||||
|
shift += 7;
|
||||||
|
if byte & 0x80 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// sign-extend
|
||||||
|
if shift < 8 * mem::size_of::<u64>() && (byte & 0x40) != 0 {
|
||||||
|
result |= (!0 as u64) << shift;
|
||||||
|
}
|
||||||
|
result as i64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dwarf_reader() {
|
||||||
|
let encoded: &[u8] = &[1,
|
||||||
|
2, 3,
|
||||||
|
4, 5, 6, 7,
|
||||||
|
0xE5, 0x8E, 0x26,
|
||||||
|
0x9B, 0xF1, 0x59,
|
||||||
|
0xFF, 0xFF];
|
||||||
|
|
||||||
|
let mut reader = DwarfReader::new(encoded.as_ptr());
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
assert!(reader.read::<u8>() == u8::to_be(1u8));
|
||||||
|
assert!(reader.read::<u16>() == u16::to_be(0x0203));
|
||||||
|
assert!(reader.read::<u32>() == u32::to_be(0x04050607));
|
||||||
|
|
||||||
|
assert!(reader.read_uleb128() == 624485);
|
||||||
|
assert!(reader.read_sleb128() == -624485);
|
||||||
|
|
||||||
|
assert!(reader.read::<i8>() == i8::to_be(-1));
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ pub enum _Unwind_Action {
|
||||||
|
|
||||||
#[cfg(target_arch = "arm")]
|
#[cfg(target_arch = "arm")]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub enum _Unwind_State {
|
pub enum _Unwind_State {
|
||||||
_US_VIRTUAL_UNWIND_FRAME = 0,
|
_US_VIRTUAL_UNWIND_FRAME = 0,
|
||||||
_US_UNWIND_FRAME_STARTING = 1,
|
_US_UNWIND_FRAME_STARTING = 1,
|
||||||
|
@ -46,6 +47,7 @@ pub enum _Unwind_State {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub enum _Unwind_Reason_Code {
|
pub enum _Unwind_Reason_Code {
|
||||||
_URC_NO_REASON = 0,
|
_URC_NO_REASON = 0,
|
||||||
_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
|
_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
|
||||||
|
|
|
@ -18,7 +18,7 @@ macro_rules! rterrln {
|
||||||
::rt::util::dumb_print(format_args!(concat!($fmt, "\n")))
|
::rt::util::dumb_print(format_args!(concat!($fmt, "\n")))
|
||||||
} );
|
} );
|
||||||
($fmt:expr, $($arg:expr),*) => ( {
|
($fmt:expr, $($arg:expr),*) => ( {
|
||||||
::rt::util::dumb_print(format_args!(concat!($fmt, "\n"), $($arg)*))
|
::rt::util::dumb_print(format_args!(concat!($fmt, "\n"), $($arg),*))
|
||||||
} )
|
} )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ macro_rules! rtdebug {
|
||||||
} );
|
} );
|
||||||
($str:expr, $($arg:expr),*) => ( {
|
($str:expr, $($arg:expr),*) => ( {
|
||||||
if cfg!(rtdebug) {
|
if cfg!(rtdebug) {
|
||||||
rterrln!($str, $($arg)*)
|
rterrln!($str, $($arg),*)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,8 @@ pub mod args;
|
||||||
mod at_exit_imp;
|
mod at_exit_imp;
|
||||||
mod libunwind;
|
mod libunwind;
|
||||||
|
|
||||||
|
mod dwarf;
|
||||||
|
|
||||||
/// The default error code of the rust runtime if the main thread panics instead
|
/// The default error code of the rust runtime if the main thread panics instead
|
||||||
/// of exiting cleanly.
|
/// of exiting cleanly.
|
||||||
pub const DEFAULT_ERROR_CODE: isize = 101;
|
pub const DEFAULT_ERROR_CODE: isize = 101;
|
||||||
|
|
|
@ -74,14 +74,7 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class {
|
||||||
// so the behavior of __gcc_personality_v0 is perfectly adequate there, and
|
// so the behavior of __gcc_personality_v0 is perfectly adequate there, and
|
||||||
// - rust_eh_personality_catch, used only by rust_try(), which always catches.
|
// - rust_eh_personality_catch, used only by rust_try(), which always catches.
|
||||||
//
|
//
|
||||||
// Note, however, that for implementation simplicity, rust_eh_personality_catch
|
// See also: rustc_trans::trans::intrinsic::trans_gnu_try
|
||||||
// lacks code to install a landing pad, so in order to obtain exception object
|
|
||||||
// pointer (which it needs to return upstream), rust_try() employs another trick:
|
|
||||||
// it calls into the nested rust_try_inner(), whose landing pad does not resume
|
|
||||||
// unwinds. Instead, it extracts the exception pointer and performs a "normal"
|
|
||||||
// return.
|
|
||||||
//
|
|
||||||
// See also: rt/rust_try.ll
|
|
||||||
|
|
||||||
#[cfg(all(not(target_arch = "arm"),
|
#[cfg(all(not(target_arch = "arm"),
|
||||||
not(all(windows, target_arch = "x86_64")),
|
not(all(windows, target_arch = "x86_64")),
|
||||||
|
@ -118,11 +111,11 @@ pub mod eabi {
|
||||||
#[lang = "eh_personality_catch"]
|
#[lang = "eh_personality_catch"]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern fn rust_eh_personality_catch(
|
pub extern fn rust_eh_personality_catch(
|
||||||
_version: c_int,
|
version: c_int,
|
||||||
actions: uw::_Unwind_Action,
|
actions: uw::_Unwind_Action,
|
||||||
_exception_class: uw::_Unwind_Exception_Class,
|
exception_class: uw::_Unwind_Exception_Class,
|
||||||
_ue_header: *mut uw::_Unwind_Exception,
|
ue_header: *mut uw::_Unwind_Exception,
|
||||||
_context: *mut uw::_Unwind_Context
|
context: *mut uw::_Unwind_Context
|
||||||
) -> uw::_Unwind_Reason_Code
|
) -> uw::_Unwind_Reason_Code
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -130,7 +123,10 @@ pub mod eabi {
|
||||||
uw::_URC_HANDLER_FOUND // catch!
|
uw::_URC_HANDLER_FOUND // catch!
|
||||||
}
|
}
|
||||||
else { // cleanup phase
|
else { // cleanup phase
|
||||||
uw::_URC_INSTALL_CONTEXT
|
unsafe {
|
||||||
|
__gcc_personality_v0(version, actions, exception_class, ue_header,
|
||||||
|
context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,11 +167,11 @@ pub mod eabi {
|
||||||
#[lang = "eh_personality_catch"]
|
#[lang = "eh_personality_catch"]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern fn rust_eh_personality_catch(
|
pub extern fn rust_eh_personality_catch(
|
||||||
_version: c_int,
|
version: c_int,
|
||||||
actions: uw::_Unwind_Action,
|
actions: uw::_Unwind_Action,
|
||||||
_exception_class: uw::_Unwind_Exception_Class,
|
exception_class: uw::_Unwind_Exception_Class,
|
||||||
_ue_header: *mut uw::_Unwind_Exception,
|
ue_header: *mut uw::_Unwind_Exception,
|
||||||
_context: *mut uw::_Unwind_Context
|
context: *mut uw::_Unwind_Context
|
||||||
) -> uw::_Unwind_Reason_Code
|
) -> uw::_Unwind_Reason_Code
|
||||||
{
|
{
|
||||||
if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
|
if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
|
||||||
|
@ -183,8 +179,8 @@ pub mod eabi {
|
||||||
}
|
}
|
||||||
else { // cleanup phase
|
else { // cleanup phase
|
||||||
unsafe {
|
unsafe {
|
||||||
__gcc_personality_sj0(_version, actions, _exception_class, _ue_header,
|
__gcc_personality_sj0(version, actions, exception_class, ue_header,
|
||||||
_context)
|
context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,8 +218,8 @@ pub mod eabi {
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern fn rust_eh_personality_catch(
|
pub extern fn rust_eh_personality_catch(
|
||||||
state: uw::_Unwind_State,
|
state: uw::_Unwind_State,
|
||||||
_ue_header: *mut uw::_Unwind_Exception,
|
ue_header: *mut uw::_Unwind_Exception,
|
||||||
_context: *mut uw::_Unwind_Context
|
context: *mut uw::_Unwind_Context
|
||||||
) -> uw::_Unwind_Reason_Code
|
) -> uw::_Unwind_Reason_Code
|
||||||
{
|
{
|
||||||
if (state as c_int & uw::_US_ACTION_MASK as c_int)
|
if (state as c_int & uw::_US_ACTION_MASK as c_int)
|
||||||
|
@ -231,112 +227,9 @@ pub mod eabi {
|
||||||
uw::_URC_HANDLER_FOUND // catch!
|
uw::_URC_HANDLER_FOUND // catch!
|
||||||
}
|
}
|
||||||
else { // cleanup phase
|
else { // cleanup phase
|
||||||
uw::_URC_INSTALL_CONTEXT
|
unsafe {
|
||||||
|
__gcc_personality_v0(state, ue_header, context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx)
|
|
||||||
//
|
|
||||||
// This looks a bit convoluted because rather than implementing a native SEH
|
|
||||||
// handler, GCC reuses the same personality routine as for the other
|
|
||||||
// architectures by wrapping it with an "API translator" layer
|
|
||||||
// (_GCC_specific_handler).
|
|
||||||
|
|
||||||
#[cfg(all(windows, target_arch = "x86_64", not(test)))]
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[allow(non_camel_case_types, non_snake_case)]
|
|
||||||
pub mod eabi {
|
|
||||||
pub use self::EXCEPTION_DISPOSITION::*;
|
|
||||||
use rt::libunwind as uw;
|
|
||||||
use libc::{c_void, c_int};
|
|
||||||
|
|
||||||
// Fake definitions; these are actually complicated structs,
|
|
||||||
// but we don't use the contents here.
|
|
||||||
pub type EXCEPTION_RECORD = c_void;
|
|
||||||
pub type CONTEXT = c_void;
|
|
||||||
pub type DISPATCHER_CONTEXT = c_void;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum EXCEPTION_DISPOSITION {
|
|
||||||
ExceptionContinueExecution,
|
|
||||||
ExceptionContinueSearch,
|
|
||||||
ExceptionNestedException,
|
|
||||||
ExceptionCollidedUnwind
|
|
||||||
}
|
|
||||||
|
|
||||||
type _Unwind_Personality_Fn =
|
|
||||||
extern fn(
|
|
||||||
version: c_int,
|
|
||||||
actions: uw::_Unwind_Action,
|
|
||||||
exception_class: uw::_Unwind_Exception_Class,
|
|
||||||
ue_header: *mut uw::_Unwind_Exception,
|
|
||||||
context: *mut uw::_Unwind_Context
|
|
||||||
) -> uw::_Unwind_Reason_Code;
|
|
||||||
|
|
||||||
extern {
|
|
||||||
fn __gcc_personality_seh0(
|
|
||||||
exceptionRecord: *mut EXCEPTION_RECORD,
|
|
||||||
establisherFrame: *mut c_void,
|
|
||||||
contextRecord: *mut CONTEXT,
|
|
||||||
dispatcherContext: *mut DISPATCHER_CONTEXT
|
|
||||||
) -> EXCEPTION_DISPOSITION;
|
|
||||||
|
|
||||||
fn _GCC_specific_handler(
|
|
||||||
exceptionRecord: *mut EXCEPTION_RECORD,
|
|
||||||
establisherFrame: *mut c_void,
|
|
||||||
contextRecord: *mut CONTEXT,
|
|
||||||
dispatcherContext: *mut DISPATCHER_CONTEXT,
|
|
||||||
personality: _Unwind_Personality_Fn
|
|
||||||
) -> EXCEPTION_DISPOSITION;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[lang = "eh_personality"]
|
|
||||||
#[no_mangle]
|
|
||||||
extern fn rust_eh_personality(
|
|
||||||
exceptionRecord: *mut EXCEPTION_RECORD,
|
|
||||||
establisherFrame: *mut c_void,
|
|
||||||
contextRecord: *mut CONTEXT,
|
|
||||||
dispatcherContext: *mut DISPATCHER_CONTEXT
|
|
||||||
) -> EXCEPTION_DISPOSITION
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
__gcc_personality_seh0(exceptionRecord, establisherFrame,
|
|
||||||
contextRecord, dispatcherContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[lang = "eh_personality_catch"]
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern fn rust_eh_personality_catch(
|
|
||||||
exceptionRecord: *mut EXCEPTION_RECORD,
|
|
||||||
establisherFrame: *mut c_void,
|
|
||||||
contextRecord: *mut CONTEXT,
|
|
||||||
dispatcherContext: *mut DISPATCHER_CONTEXT
|
|
||||||
) -> EXCEPTION_DISPOSITION
|
|
||||||
{
|
|
||||||
extern fn inner(
|
|
||||||
_version: c_int,
|
|
||||||
actions: uw::_Unwind_Action,
|
|
||||||
_exception_class: uw::_Unwind_Exception_Class,
|
|
||||||
_ue_header: *mut uw::_Unwind_Exception,
|
|
||||||
_context: *mut uw::_Unwind_Context
|
|
||||||
) -> uw::_Unwind_Reason_Code
|
|
||||||
{
|
|
||||||
if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
|
|
||||||
uw::_URC_HANDLER_FOUND // catch!
|
|
||||||
}
|
|
||||||
else { // cleanup phase
|
|
||||||
uw::_URC_INSTALL_CONTEXT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
_GCC_specific_handler(exceptionRecord, establisherFrame,
|
|
||||||
contextRecord, dispatcherContext,
|
|
||||||
inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
|
//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
|
||||||
//! documents linked from it.
|
//! documents linked from it.
|
||||||
//! These are also good reads:
|
//! These are also good reads:
|
||||||
//! http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/
|
//! http://mentorembedded.github.io/cxx-abi/abi-eh.html
|
||||||
//! http://monoinfinito.wordpress.com/series/exception-handling-in-c/
|
//! http://monoinfinito.wordpress.com/series/exception-handling-in-c/
|
||||||
//! http://www.airs.com/blog/index.php?s=exception+frames
|
//! http://www.airs.com/blog/index.php?s=exception+frames
|
||||||
//!
|
//!
|
||||||
|
@ -76,9 +76,20 @@ use sys_common::mutex::Mutex;
|
||||||
// The actual unwinding implementation is cfg'd here, and we've got two current
|
// The actual unwinding implementation is cfg'd here, and we've got two current
|
||||||
// implementations. One goes through SEH on Windows and the other goes through
|
// implementations. One goes through SEH on Windows and the other goes through
|
||||||
// libgcc via the libunwind-like API.
|
// libgcc via the libunwind-like API.
|
||||||
#[cfg(target_env = "msvc")] #[path = "seh.rs"] #[doc(hidden)]
|
|
||||||
|
// *-pc-windows-msvc
|
||||||
|
#[cfg(all(windows, target_env = "msvc"))]
|
||||||
|
#[path = "seh.rs"] #[doc(hidden)]
|
||||||
pub mod imp;
|
pub mod imp;
|
||||||
#[cfg(not(target_env = "msvc"))] #[path = "gcc.rs"] #[doc(hidden)]
|
|
||||||
|
// x86_64-pc-windows-gnu
|
||||||
|
#[cfg(all(windows, target_arch="x86_64", target_env="gnu"))]
|
||||||
|
#[path = "seh64_gnu.rs"] #[doc(hidden)]
|
||||||
|
pub mod imp;
|
||||||
|
|
||||||
|
// i686-pc-windows-gnu and all others
|
||||||
|
#[cfg(any(unix, all(windows, target_arch="x86", target_env="gnu")))]
|
||||||
|
#[path = "gcc.rs"] #[doc(hidden)]
|
||||||
pub mod imp;
|
pub mod imp;
|
||||||
|
|
||||||
pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: u32);
|
pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: u32);
|
||||||
|
|
227
src/libstd/rt/unwind/seh64_gnu.rs
Normal file
227
src/libstd/rt/unwind/seh64_gnu.rs
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
//! Unwinding implementation of top of native Win64 SEH,
|
||||||
|
//! however the unwind handler data (aka LSDA) uses GCC-compatible encoding.
|
||||||
|
|
||||||
|
#![allow(bad_style)]
|
||||||
|
#![allow(private_no_mangle_fns)]
|
||||||
|
|
||||||
|
use prelude::v1::*;
|
||||||
|
|
||||||
|
use any::Any;
|
||||||
|
use self::EXCEPTION_DISPOSITION::*;
|
||||||
|
use rt::dwarf::eh;
|
||||||
|
use core::mem;
|
||||||
|
use core::ptr;
|
||||||
|
use simd;
|
||||||
|
use libc::{c_void, c_ulonglong, DWORD, LPVOID};
|
||||||
|
type ULONG_PTR = c_ulonglong;
|
||||||
|
|
||||||
|
// Define our exception codes:
|
||||||
|
// according to http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx,
|
||||||
|
// [31:30] = 3 (error), 2 (warning), 1 (info), 0 (success)
|
||||||
|
// [29] = 1 (user-defined)
|
||||||
|
// [28] = 0 (reserved)
|
||||||
|
// we define bits:
|
||||||
|
// [24:27] = type
|
||||||
|
// [0:23] = magic
|
||||||
|
const ETYPE: DWORD = 0b1110_u32 << 28;
|
||||||
|
const MAGIC: DWORD = 0x525354; // "RST"
|
||||||
|
|
||||||
|
const RUST_PANIC: DWORD = ETYPE | (1 << 24) | MAGIC;
|
||||||
|
|
||||||
|
const EXCEPTION_NONCONTINUABLE: DWORD = 0x1; // Noncontinuable exception
|
||||||
|
const EXCEPTION_UNWINDING: DWORD = 0x2; // Unwind is in progress
|
||||||
|
const EXCEPTION_EXIT_UNWIND: DWORD = 0x4; // Exit unwind is in progress
|
||||||
|
const EXCEPTION_STACK_INVALID: DWORD = 0x8; // Stack out of limits or unaligned
|
||||||
|
const EXCEPTION_NESTED_CALL: DWORD = 0x10; // Nested exception handler call
|
||||||
|
const EXCEPTION_TARGET_UNWIND: DWORD = 0x20; // Target unwind in progress
|
||||||
|
const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call
|
||||||
|
const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING |
|
||||||
|
EXCEPTION_EXIT_UNWIND |
|
||||||
|
EXCEPTION_TARGET_UNWIND |
|
||||||
|
EXCEPTION_COLLIDED_UNWIND;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct EXCEPTION_RECORD {
|
||||||
|
ExceptionCode: DWORD,
|
||||||
|
ExceptionFlags: DWORD,
|
||||||
|
ExceptionRecord: *const EXCEPTION_RECORD,
|
||||||
|
ExceptionAddress: LPVOID,
|
||||||
|
NumberParameters: DWORD,
|
||||||
|
ExceptionInformation: [ULONG_PTR; 15],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CONTEXT = c_void;
|
||||||
|
pub type UNWIND_HISTORY_TABLE = c_void;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct RUNTIME_FUNCTION {
|
||||||
|
BeginAddress: DWORD,
|
||||||
|
EndAddress: DWORD,
|
||||||
|
UnwindData: DWORD,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct DISPATCHER_CONTEXT {
|
||||||
|
ControlPc: LPVOID,
|
||||||
|
ImageBase: LPVOID,
|
||||||
|
FunctionEntry: *const RUNTIME_FUNCTION,
|
||||||
|
EstablisherFrame: LPVOID,
|
||||||
|
TargetIp: LPVOID,
|
||||||
|
ContextRecord: *const CONTEXT,
|
||||||
|
LanguageHandler: LPVOID,
|
||||||
|
HandlerData: *const u8,
|
||||||
|
HistoryTable: *const UNWIND_HISTORY_TABLE,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum EXCEPTION_DISPOSITION {
|
||||||
|
ExceptionContinueExecution,
|
||||||
|
ExceptionContinueSearch,
|
||||||
|
ExceptionNestedException,
|
||||||
|
ExceptionCollidedUnwind
|
||||||
|
}
|
||||||
|
|
||||||
|
// From kernel32.dll
|
||||||
|
extern "system" {
|
||||||
|
fn RaiseException(dwExceptionCode: DWORD,
|
||||||
|
dwExceptionFlags: DWORD,
|
||||||
|
nNumberOfArguments: DWORD,
|
||||||
|
lpArguments: *const ULONG_PTR);
|
||||||
|
|
||||||
|
fn RtlUnwindEx(TargetFrame: LPVOID,
|
||||||
|
TargetIp: LPVOID,
|
||||||
|
ExceptionRecord: *const EXCEPTION_RECORD,
|
||||||
|
ReturnValue: LPVOID,
|
||||||
|
OriginalContext: *const CONTEXT,
|
||||||
|
HistoryTable: *const UNWIND_HISTORY_TABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct PanicData {
|
||||||
|
data: Box<Any + Send + 'static>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
|
||||||
|
let panic_ctx = Box::new(PanicData { data: data });
|
||||||
|
let params = [Box::into_raw(panic_ctx) as ULONG_PTR];
|
||||||
|
rtdebug!("panic: ctx={:X}", params[0]);
|
||||||
|
RaiseException(RUST_PANIC,
|
||||||
|
EXCEPTION_NONCONTINUABLE,
|
||||||
|
params.len() as DWORD,
|
||||||
|
¶ms as *const ULONG_PTR);
|
||||||
|
rtabort!("could not unwind stack");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
|
||||||
|
rtdebug!("cleanup: ctx={:X}", ptr as usize);
|
||||||
|
let panic_ctx = Box::from_raw(ptr as *mut PanicData);
|
||||||
|
return panic_ctx.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SEH doesn't support resuming unwinds after calling a landing pad like
|
||||||
|
// libunwind does. For this reason, MSVC compiler outlines landing pads into
|
||||||
|
// separate functions that can be called directly from the personality function
|
||||||
|
// but are nevertheless able to find and modify stack frame of the "parent"
|
||||||
|
// function.
|
||||||
|
//
|
||||||
|
// Since this cannot be done with libdwarf-style landing pads,
|
||||||
|
// rust_eh_personality instead catches RUST_PANICs, runs the landing pad, then
|
||||||
|
// reraises the exception.
|
||||||
|
//
|
||||||
|
// Note that it makes certain assumptions about the exception:
|
||||||
|
//
|
||||||
|
// 1. That RUST_PANIC is non-continuable, so no lower stack frame may choose to
|
||||||
|
// resume execution.
|
||||||
|
// 2. That the first parameter of the exception is a pointer to an extra data
|
||||||
|
// area (PanicData).
|
||||||
|
// Since these assumptions do not generally hold true for foreign exceptions
|
||||||
|
// (system faults, C++ exceptions, etc), we make no attempt to invoke our
|
||||||
|
// landing pads (and, thus, destructors!) for anything other than RUST_PANICs.
|
||||||
|
// This is considered acceptable, because the behavior of throwing exceptions
|
||||||
|
// through a C ABI boundary is undefined.
|
||||||
|
|
||||||
|
#[lang = "eh_personality_catch"]
|
||||||
|
#[cfg(not(test))]
|
||||||
|
unsafe extern fn rust_eh_personality_catch(
|
||||||
|
exceptionRecord: *mut EXCEPTION_RECORD,
|
||||||
|
establisherFrame: LPVOID,
|
||||||
|
contextRecord: *mut CONTEXT,
|
||||||
|
dispatcherContext: *mut DISPATCHER_CONTEXT
|
||||||
|
) -> EXCEPTION_DISPOSITION
|
||||||
|
{
|
||||||
|
rust_eh_personality(exceptionRecord, establisherFrame,
|
||||||
|
contextRecord, dispatcherContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[lang = "eh_personality"]
|
||||||
|
#[cfg(not(test))]
|
||||||
|
unsafe extern fn rust_eh_personality(
|
||||||
|
exceptionRecord: *mut EXCEPTION_RECORD,
|
||||||
|
establisherFrame: LPVOID,
|
||||||
|
contextRecord: *mut CONTEXT,
|
||||||
|
dispatcherContext: *mut DISPATCHER_CONTEXT
|
||||||
|
) -> EXCEPTION_DISPOSITION
|
||||||
|
{
|
||||||
|
let er = &*exceptionRecord;
|
||||||
|
let dc = &*dispatcherContext;
|
||||||
|
rtdebug!("rust_eh_personality: code={:X}, flags={:X}, frame={:X}, ip={:X}",
|
||||||
|
er.ExceptionCode, er.ExceptionFlags,
|
||||||
|
establisherFrame as usize, dc.ControlPc as usize);
|
||||||
|
|
||||||
|
if er.ExceptionFlags & EXCEPTION_UNWIND == 0 { // we are in the dispatch phase
|
||||||
|
if er.ExceptionCode == RUST_PANIC {
|
||||||
|
if let Some(lpad) = find_landing_pad(dc) {
|
||||||
|
rtdebug!("unwinding to landing pad {:X}", lpad);
|
||||||
|
|
||||||
|
RtlUnwindEx(establisherFrame,
|
||||||
|
lpad as LPVOID,
|
||||||
|
exceptionRecord,
|
||||||
|
er.ExceptionInformation[0] as LPVOID, // pointer to PanicData
|
||||||
|
contextRecord,
|
||||||
|
dc.HistoryTable);
|
||||||
|
rtabort!("could not unwind");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExceptionContinueSearch
|
||||||
|
}
|
||||||
|
|
||||||
|
// The `resume` instruction, found at the end of the landing pads, and whose job
|
||||||
|
// is to resume stack unwinding, is typically lowered by LLVM into a call to
|
||||||
|
// `_Unwind_Resume` routine. To avoid confusion with the same symbol exported
|
||||||
|
// from libgcc, we redirect it to `rust_eh_unwind_resume`.
|
||||||
|
// Since resolution of this symbol is done by the linker, `rust_eh_unwind_resume`
|
||||||
|
// must be marked `pub` + `#[no_mangle]`. (Can we make it a lang item?)
|
||||||
|
|
||||||
|
#[lang = "eh_unwind_resume"]
|
||||||
|
#[cfg(not(test))]
|
||||||
|
unsafe extern fn rust_eh_unwind_resume(panic_ctx: LPVOID) {
|
||||||
|
rtdebug!("rust_eh_unwind_resume: ctx={:X}", panic_ctx as usize);
|
||||||
|
let params = [panic_ctx as ULONG_PTR];
|
||||||
|
RaiseException(RUST_PANIC,
|
||||||
|
EXCEPTION_NONCONTINUABLE,
|
||||||
|
params.len() as DWORD,
|
||||||
|
¶ms as *const ULONG_PTR);
|
||||||
|
rtabort!("could not resume unwind");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn find_landing_pad(dc: &DISPATCHER_CONTEXT) -> Option<usize> {
|
||||||
|
let eh_ctx = eh::EHContext {
|
||||||
|
ip: dc.ControlPc as usize,
|
||||||
|
func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize,
|
||||||
|
text_start: dc.ImageBase as usize,
|
||||||
|
data_start: 0
|
||||||
|
};
|
||||||
|
eh::find_landing_pad(dc.HandlerData, &eh_ctx)
|
||||||
|
}
|
|
@ -21,6 +21,9 @@ extern fn stack_exhausted() {}
|
||||||
#[lang = "eh_personality"]
|
#[lang = "eh_personality"]
|
||||||
extern fn eh_personality() {}
|
extern fn eh_personality() {}
|
||||||
|
|
||||||
|
#[lang = "eh_unwind_resume"]
|
||||||
|
extern fn eh_unwind_resume() {}
|
||||||
|
|
||||||
#[lang = "panic_fmt"]
|
#[lang = "panic_fmt"]
|
||||||
extern fn rust_begin_unwind(msg: core::fmt::Arguments, file: &'static str,
|
extern fn rust_begin_unwind(msg: core::fmt::Arguments, file: &'static str,
|
||||||
line: u32) -> ! {
|
line: u32) -> ! {
|
||||||
|
|
|
@ -23,4 +23,5 @@ fn main() {
|
||||||
|
|
||||||
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
||||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||||
|
#[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {}
|
||||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
-include ../tools.mk
|
-include ../tools.mk
|
||||||
|
|
||||||
ifndef IS_WINDOWS
|
|
||||||
EXTRAFLAGS := $(EXTRACFLAGS)
|
EXTRAFLAGS := $(EXTRACFLAGS)
|
||||||
endif
|
|
||||||
|
|
||||||
# FIXME: ignore freebsd
|
# FIXME: ignore freebsd
|
||||||
ifneq ($(shell uname),FreeBSD)
|
ifneq ($(shell uname),FreeBSD)
|
||||||
|
|
|
@ -19,4 +19,5 @@ pub extern fn bar() {}
|
||||||
|
|
||||||
#[lang = "stack_exhausted"] fn stack_exhausted() {}
|
#[lang = "stack_exhausted"] fn stack_exhausted() {}
|
||||||
#[lang = "eh_personality"] fn eh_personality() {}
|
#[lang = "eh_personality"] fn eh_personality() {}
|
||||||
|
#[lang = "eh_unwind_resume"] fn eh_unwind_resume() {}
|
||||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||||
|
|
|
@ -19,4 +19,6 @@ pub extern fn foo() {}
|
||||||
|
|
||||||
#[lang = "stack_exhausted"] fn stack_exhausted() {}
|
#[lang = "stack_exhausted"] fn stack_exhausted() {}
|
||||||
#[lang = "eh_personality"] fn eh_personality() {}
|
#[lang = "eh_personality"] fn eh_personality() {}
|
||||||
|
#[lang = "eh_unwind_resume"] fn eh_unwind_resume() {}
|
||||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ extern "rust-intrinsic" { fn transmute<T, U>(t: T) -> U; }
|
||||||
|
|
||||||
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
||||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||||
|
#[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {}
|
||||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||||
|
|
||||||
#[start]
|
#[start]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue