Remove morestack support
This commit removes all morestack support from the compiler which entails: * Segmented stacks are no longer emitted in codegen. * We no longer build or distribute libmorestack.a * The `stack_exhausted` lang item is no longer required The only current use of the segmented stack support in LLVM is to detect stack overflow. This is no longer really required, however, because we already have guard pages for all threads and registered signal handlers watching for a segfault on those pages (to print out a stack overflow message). Additionally, major platforms (aka Windows) already don't use morestack. This means that Rust is by default less likely to catch stack overflows because if a function takes up more than one page of stack space it won't hit the guard page. This is what the purpose of morestack was (to catch this case), but it's better served with stack probes which have more cross platform support and no runtime support necessary. Until LLVM supports this for all platform it looks like morestack isn't really buying us much. cc #16012 (still need stack probes) Closes #26458 (a drive-by fix to help diagnostics on stack overflow)
This commit is contained in:
@@ -21,7 +21,6 @@ pub mod io;
|
||||
pub mod poison;
|
||||
pub mod remutex;
|
||||
pub mod rwlock;
|
||||
pub mod stack;
|
||||
pub mod thread;
|
||||
pub mod thread_info;
|
||||
pub mod thread_local;
|
||||
@@ -52,3 +51,7 @@ pub trait IntoInner<Inner> {
|
||||
pub trait FromInner<Inner> {
|
||||
fn from_inner(inner: Inner) -> Self;
|
||||
}
|
||||
|
||||
#[cfg(stage0)]
|
||||
#[lang = "stack_exhausted"]
|
||||
pub fn stack_exhausted() {}
|
||||
|
||||
@@ -1,311 +0,0 @@
|
||||
// Copyright 2013-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.
|
||||
|
||||
//! Rust stack-limit management
|
||||
//!
|
||||
//! Currently Rust uses a segmented-stack-like scheme in order to detect stack
|
||||
//! overflow for rust threads. In this scheme, the prologue of all functions are
|
||||
//! preceded with a check to see whether the current stack limits are being
|
||||
//! exceeded.
|
||||
//!
|
||||
//! This module provides the functionality necessary in order to manage these
|
||||
//! stack limits (which are stored in platform-specific locations). The
|
||||
//! functions here are used at the borders of the thread lifetime in order to
|
||||
//! manage these limits.
|
||||
//!
|
||||
//! This function is an unstable module because this scheme for stack overflow
|
||||
//! detection is not guaranteed to continue in the future. Usage of this module
|
||||
//! is discouraged unless absolutely necessary.
|
||||
|
||||
// iOS related notes
|
||||
//
|
||||
// It is possible to implement it using idea from
|
||||
// http://www.opensource.apple.com/source/Libc/Libc-825.40.1/pthreads/pthread_machdep.h
|
||||
//
|
||||
// In short: _pthread_{get,set}_specific_direct allows extremely fast
|
||||
// access, exactly what is required for segmented stack
|
||||
// There is a pool of reserved slots for Apple internal use (0..119)
|
||||
// First dynamic allocated pthread key starts with 257 (on iOS7)
|
||||
// So using slot 149 should be pretty safe ASSUMING space is reserved
|
||||
// for every key < first dynamic key
|
||||
//
|
||||
// There is also an opportunity to steal keys reserved for Garbage Collection
|
||||
// ranges 80..89 and 110..119, especially considering the fact Garbage Collection
|
||||
// never supposed to work on iOS. But as everybody knows it - there is a chance
|
||||
// that those slots will be re-used, like it happened with key 95 (moved from
|
||||
// JavaScriptCore to CoreText)
|
||||
//
|
||||
// Unfortunately Apple rejected patch to LLVM which generated
|
||||
// corresponding prolog, decision was taken to disable segmented
|
||||
// stack support on iOS.
|
||||
|
||||
pub const RED_ZONE: usize = 20 * 1024;
|
||||
|
||||
/// This function is invoked from rust's current __morestack function. Segmented
|
||||
/// stacks are currently not enabled as segmented stacks, but rather one giant
|
||||
/// stack segment. This means that whenever we run out of stack, we want to
|
||||
/// truly consider it to be stack overflow rather than allocating a new stack.
|
||||
#[cfg(not(test))] // in testing, use the original libstd's version
|
||||
#[lang = "stack_exhausted"]
|
||||
extern fn stack_exhausted() {
|
||||
use intrinsics;
|
||||
|
||||
unsafe {
|
||||
// We're calling this function because the stack just ran out. We need
|
||||
// to call some other rust functions, but if we invoke the functions
|
||||
// right now it'll just trigger this handler being called again. In
|
||||
// order to alleviate this, we move the stack limit to be inside of the
|
||||
// red zone that was allocated for exactly this reason.
|
||||
let limit = get_sp_limit();
|
||||
record_sp_limit(limit - RED_ZONE / 2);
|
||||
|
||||
// This probably isn't the best course of action. Ideally one would want
|
||||
// to unwind the stack here instead of just aborting the entire process.
|
||||
// This is a tricky problem, however. There's a few things which need to
|
||||
// be considered:
|
||||
//
|
||||
// 1. We're here because of a stack overflow, yet unwinding will run
|
||||
// destructors and hence arbitrary code. What if that code overflows
|
||||
// the stack? One possibility is to use the above allocation of an
|
||||
// extra 10k to hope that we don't hit the limit, and if we do then
|
||||
// abort the whole program. Not the best, but kind of hard to deal
|
||||
// with unless we want to switch stacks.
|
||||
//
|
||||
// 2. LLVM will optimize functions based on whether they can unwind or
|
||||
// not. It will flag functions with 'nounwind' if it believes that
|
||||
// the function cannot trigger unwinding, but if we do unwind on
|
||||
// stack overflow then it means that we could unwind in any function
|
||||
// anywhere. We would have to make sure that LLVM only places the
|
||||
// nounwind flag on functions which don't call any other functions.
|
||||
//
|
||||
// 3. The function that overflowed may have owned arguments. These
|
||||
// arguments need to have their destructors run, but we haven't even
|
||||
// begun executing the function yet, so unwinding will not run the
|
||||
// any landing pads for these functions. If this is ignored, then
|
||||
// the arguments will just be leaked.
|
||||
//
|
||||
// Exactly what to do here is a very delicate topic, and is possibly
|
||||
// still up in the air for what exactly to do. Some relevant issues:
|
||||
//
|
||||
// #3555 - out-of-stack failure leaks arguments
|
||||
// #3695 - should there be a stack limit?
|
||||
// #9855 - possible strategies which could be taken
|
||||
// #9854 - unwinding on windows through __morestack has never worked
|
||||
// #2361 - possible implementation of not using landing pads
|
||||
|
||||
::rt::util::report_overflow();
|
||||
|
||||
intrinsics::abort();
|
||||
}
|
||||
}
|
||||
|
||||
// Windows maintains a record of upper and lower stack bounds in the Thread Information
|
||||
// Block (TIB), and some syscalls do check that addresses which are supposed to be in
|
||||
// the stack, indeed lie between these two values.
|
||||
// (See https://github.com/rust-lang/rust/issues/3445#issuecomment-26114839)
|
||||
//
|
||||
// When using Rust-managed stacks (libgreen), we must maintain these values accordingly.
|
||||
// For OS-managed stacks (libnative), we let the OS manage them for us.
|
||||
//
|
||||
// On all other platforms both variants behave identically.
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn record_os_managed_stack_bounds(stack_lo: usize, _stack_hi: usize) {
|
||||
record_sp_limit(stack_lo + RED_ZONE);
|
||||
}
|
||||
|
||||
/// Records the current limit of the stack as specified by `end`.
|
||||
///
|
||||
/// This is stored in an OS-dependent location, likely inside of the thread
|
||||
/// local storage. The location that the limit is stored is a pre-ordained
|
||||
/// location because it's where LLVM has emitted code to check.
|
||||
///
|
||||
/// Note that this cannot be called under normal circumstances. This function is
|
||||
/// changing the stack limit, so upon returning any further function calls will
|
||||
/// possibly be triggering the morestack logic if you're not careful.
|
||||
///
|
||||
/// Also note that this and all of the inside functions are all flagged as
|
||||
/// "inline(always)" because they're messing around with the stack limits. This
|
||||
/// would be unfortunate for the functions themselves to trigger a morestack
|
||||
/// invocation (if they were an actual function call).
|
||||
#[inline(always)]
|
||||
pub unsafe fn record_sp_limit(limit: usize) {
|
||||
return target_record_sp_limit(limit);
|
||||
|
||||
#[cfg(all(target_arch = "x86_64",
|
||||
any(target_os = "macos", target_os = "ios")))]
|
||||
#[inline(always)]
|
||||
unsafe fn target_record_sp_limit(limit: usize) {
|
||||
asm!("movq $$0x60+90*8, %rsi
|
||||
movq $0, %gs:(%rsi)" :: "r"(limit) : "rsi" : "volatile")
|
||||
}
|
||||
#[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)]
|
||||
unsafe fn target_record_sp_limit(limit: usize) {
|
||||
asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile")
|
||||
}
|
||||
#[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)]
|
||||
unsafe fn target_record_sp_limit(_: usize) {
|
||||
}
|
||||
#[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)]
|
||||
unsafe fn target_record_sp_limit(limit: usize) {
|
||||
asm!("movq $0, %fs:24" :: "r"(limit) :: "volatile")
|
||||
}
|
||||
#[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))]
|
||||
#[inline(always)]
|
||||
unsafe fn target_record_sp_limit(limit: usize) {
|
||||
asm!("movq $0, %fs:32" :: "r"(limit) :: "volatile")
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "x86",
|
||||
any(target_os = "macos", target_os = "ios")))]
|
||||
#[inline(always)]
|
||||
unsafe fn target_record_sp_limit(limit: usize) {
|
||||
asm!("movl $$0x48+90*4, %eax
|
||||
movl $0, %gs:(%eax)" :: "r"(limit) : "eax" : "volatile")
|
||||
}
|
||||
#[cfg(all(target_arch = "x86", target_os = "linux"))]
|
||||
#[inline(always)]
|
||||
unsafe fn target_record_sp_limit(limit: usize) {
|
||||
asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile")
|
||||
}
|
||||
#[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)]
|
||||
unsafe fn target_record_sp_limit(_: usize) {
|
||||
}
|
||||
|
||||
// mips, arm - The implementations are a bit big for inline asm!
|
||||
// They can be found in src/rt/arch/$target_arch/record_sp.S
|
||||
#[cfg(any(target_arch = "mips",
|
||||
target_arch = "mipsel",
|
||||
all(target_arch = "arm", not(target_os = "ios"))))]
|
||||
#[inline(always)]
|
||||
unsafe fn target_record_sp_limit(limit: usize) {
|
||||
use libc::c_void;
|
||||
return record_sp_limit(limit as *const c_void);
|
||||
extern {
|
||||
fn record_sp_limit(limit: *const c_void);
|
||||
}
|
||||
}
|
||||
|
||||
// aarch64 - FIXME(AARCH64): missing...
|
||||
// powerpc - FIXME(POWERPC): missing...
|
||||
// arm-ios - iOS segmented stack is disabled for now, see related notes
|
||||
// openbsd/bitrig/netbsd - no segmented stacks.
|
||||
// x86-freebsd - no segmented stacks.
|
||||
#[cfg(any(target_arch = "aarch64",
|
||||
target_arch = "powerpc",
|
||||
all(target_arch = "arm", target_os = "ios"),
|
||||
all(target_arch = "x86", target_os = "freebsd"),
|
||||
target_os = "bitrig",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"))]
|
||||
unsafe fn target_record_sp_limit(_: usize) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The counterpart of the function above, this function will fetch the current
|
||||
/// stack limit stored in TLS.
|
||||
///
|
||||
/// Note that all of these functions are meant to be exact counterparts of their
|
||||
/// brethren above, except that the operands are reversed.
|
||||
///
|
||||
/// As with the setter, this function does not have a __morestack header and can
|
||||
/// therefore be called in a "we're out of stack" situation.
|
||||
#[inline(always)]
|
||||
pub unsafe fn get_sp_limit() -> usize {
|
||||
return target_get_sp_limit();
|
||||
|
||||
#[cfg(all(target_arch = "x86_64",
|
||||
any(target_os = "macos", target_os = "ios")))]
|
||||
#[inline(always)]
|
||||
unsafe fn target_get_sp_limit() -> usize {
|
||||
let limit;
|
||||
asm!("movq $$0x60+90*8, %rsi
|
||||
movq %gs:(%rsi), $0" : "=r"(limit) :: "rsi" : "volatile");
|
||||
return limit;
|
||||
}
|
||||
#[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)]
|
||||
unsafe fn target_get_sp_limit() -> usize {
|
||||
let limit;
|
||||
asm!("movq %fs:112, $0" : "=r"(limit) ::: "volatile");
|
||||
return limit;
|
||||
}
|
||||
#[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)]
|
||||
unsafe fn target_get_sp_limit() -> usize {
|
||||
return 1024;
|
||||
}
|
||||
#[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)]
|
||||
unsafe fn target_get_sp_limit() -> usize {
|
||||
let limit;
|
||||
asm!("movq %fs:24, $0" : "=r"(limit) ::: "volatile");
|
||||
return limit;
|
||||
}
|
||||
#[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))]
|
||||
#[inline(always)]
|
||||
unsafe fn target_get_sp_limit() -> usize {
|
||||
let limit;
|
||||
asm!("movq %fs:32, $0" : "=r"(limit) ::: "volatile");
|
||||
return limit;
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "x86",
|
||||
any(target_os = "macos", target_os = "ios")))]
|
||||
#[inline(always)]
|
||||
unsafe fn target_get_sp_limit() -> usize {
|
||||
let limit;
|
||||
asm!("movl $$0x48+90*4, %eax
|
||||
movl %gs:(%eax), $0" : "=r"(limit) :: "eax" : "volatile");
|
||||
return limit;
|
||||
}
|
||||
#[cfg(all(target_arch = "x86", target_os = "linux"))]
|
||||
#[inline(always)]
|
||||
unsafe fn target_get_sp_limit() -> usize {
|
||||
let limit;
|
||||
asm!("movl %gs:48, $0" : "=r"(limit) ::: "volatile");
|
||||
return limit;
|
||||
}
|
||||
#[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)]
|
||||
unsafe fn target_get_sp_limit() -> usize {
|
||||
return 1024;
|
||||
}
|
||||
|
||||
// mips, arm - The implementations are a bit big for inline asm!
|
||||
// They can be found in src/rt/arch/$target_arch/record_sp.S
|
||||
#[cfg(any(target_arch = "mips",
|
||||
target_arch = "mipsel",
|
||||
all(target_arch = "arm", not(target_os = "ios"))))]
|
||||
#[inline(always)]
|
||||
unsafe fn target_get_sp_limit() -> usize {
|
||||
use libc::c_void;
|
||||
return get_sp_limit() as usize;
|
||||
extern {
|
||||
fn get_sp_limit() -> *const c_void;
|
||||
}
|
||||
}
|
||||
|
||||
// aarch64 - FIXME(AARCH64): missing...
|
||||
// powerpc - FIXME(POWERPC): missing...
|
||||
// arm-ios - no segmented stacks.
|
||||
// openbsd/bitrig/netbsd - no segmented stacks.
|
||||
// x86-freebsd - no segmented stacks..
|
||||
//
|
||||
// This function might be called by runtime though
|
||||
// so it is unsafe to unreachable, let's return a fixed constant.
|
||||
#[cfg(any(target_arch = "aarch64",
|
||||
target_arch = "powerpc",
|
||||
all(target_arch = "arm", target_os = "ios"),
|
||||
all(target_arch = "x86", target_os = "freebsd"),
|
||||
target_os = "bitrig",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"))]
|
||||
#[inline(always)]
|
||||
unsafe fn target_get_sp_limit() -> usize {
|
||||
1024
|
||||
}
|
||||
}
|
||||
@@ -13,15 +13,8 @@ use prelude::v1::*;
|
||||
use alloc::boxed::FnBox;
|
||||
use libc;
|
||||
use sys::stack_overflow;
|
||||
use sys_common::stack;
|
||||
use usize;
|
||||
|
||||
#[no_stack_check]
|
||||
pub unsafe fn start_thread(main: *mut libc::c_void) {
|
||||
// First ensure that we don't trigger __morestack (also why this has a
|
||||
// no_stack_check annotation).
|
||||
stack::record_os_managed_stack_bounds(0, usize::MAX);
|
||||
|
||||
// Next, set up our stack overflow handler which may get triggered if we run
|
||||
// out of stack.
|
||||
let _handler = stack_overflow::Handler::new();
|
||||
|
||||
@@ -40,25 +40,17 @@ impl Drop for Handler {
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"))]
|
||||
mod imp {
|
||||
use sys_common::stack;
|
||||
|
||||
use super::Handler;
|
||||
use rt::util::report_overflow;
|
||||
use mem;
|
||||
use ptr;
|
||||
use intrinsics;
|
||||
use sys::c::{siginfo, sigaction, SIGBUS, SIG_DFL,
|
||||
SA_SIGINFO, SA_ONSTACK, sigaltstack,
|
||||
SIGSTKSZ, sighandler_t, raise};
|
||||
SIGSTKSZ, sighandler_t};
|
||||
use libc;
|
||||
use libc::funcs::posix88::mman::{mmap, munmap};
|
||||
use libc::funcs::posix01::signal::signal;
|
||||
use libc::consts::os::posix88::{SIGSEGV,
|
||||
PROT_READ,
|
||||
PROT_WRITE,
|
||||
MAP_PRIVATE,
|
||||
MAP_ANON,
|
||||
MAP_FAILED};
|
||||
use libc::{SIGSEGV, PROT_READ, PROT_WRITE, MAP_PRIVATE, MAP_ANON};
|
||||
use libc::MAP_FAILED;
|
||||
|
||||
use sys_common::thread_info;
|
||||
|
||||
@@ -66,46 +58,48 @@ mod imp {
|
||||
// This is initialized in init() and only read from after
|
||||
static mut PAGE_SIZE: usize = 0;
|
||||
|
||||
#[no_stack_check]
|
||||
// Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
|
||||
// (unmapped pages) at the end of every thread's stack, so if a thread ends
|
||||
// up running into the guard page it'll trigger this handler. We want to
|
||||
// detect these cases and print out a helpful error saying that the stack
|
||||
// has overflowed. All other signals, however, should go back to what they
|
||||
// were originally supposed to do.
|
||||
//
|
||||
// This handler currently exists purely to print an informative message
|
||||
// whenever a thread overflows its stack. When run the handler always
|
||||
// un-registers itself after running and then returns (to allow the original
|
||||
// signal to be delivered again). By returning we're ensuring that segfaults
|
||||
// do indeed look like segfaults.
|
||||
//
|
||||
// Returning from this kind of signal handler is technically not defined to
|
||||
// work when reading the POSIX spec strictly, but in practice it turns out
|
||||
// many large systems and all implementations allow returning from a signal
|
||||
// handler to work. For a more detailed explanation see the comments on
|
||||
// #26458.
|
||||
unsafe extern fn signal_handler(signum: libc::c_int,
|
||||
info: *mut siginfo,
|
||||
_data: *mut libc::c_void) {
|
||||
|
||||
// We can not return from a SIGSEGV or SIGBUS signal.
|
||||
// See: https://www.gnu.org/software/libc/manual/html_node/Handler-Returns.html
|
||||
|
||||
unsafe fn term(signum: libc::c_int) -> ! {
|
||||
use core::mem::transmute;
|
||||
|
||||
signal(signum, transmute(SIG_DFL));
|
||||
raise(signum);
|
||||
intrinsics::abort();
|
||||
}
|
||||
|
||||
// We're calling into functions with stack checks
|
||||
stack::record_sp_limit(0);
|
||||
|
||||
info: *mut siginfo,
|
||||
_data: *mut libc::c_void) {
|
||||
let guard = thread_info::stack_guard().unwrap_or(0);
|
||||
let addr = (*info).si_addr as usize;
|
||||
|
||||
if guard == 0 || addr < guard - PAGE_SIZE || addr >= guard {
|
||||
term(signum);
|
||||
// If the faulting address is within the guard page, then we print a
|
||||
// message saying so.
|
||||
if guard != 0 && guard - PAGE_SIZE <= addr && addr < guard {
|
||||
report_overflow();
|
||||
}
|
||||
|
||||
report_overflow();
|
||||
// Unregister ourselves by reverting back to the default behavior.
|
||||
let mut action: sigaction = mem::zeroed();
|
||||
action.sa_sigaction = SIG_DFL;
|
||||
sigaction(signum, &action, ptr::null_mut());
|
||||
|
||||
intrinsics::abort()
|
||||
// See comment above for why this function returns.
|
||||
}
|
||||
|
||||
static mut MAIN_ALTSTACK: *mut libc::c_void = 0 as *mut libc::c_void;
|
||||
|
||||
pub unsafe fn init() {
|
||||
let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE);
|
||||
if psize == -1 {
|
||||
panic!("failed to get page size");
|
||||
}
|
||||
|
||||
PAGE_SIZE = psize as usize;
|
||||
PAGE_SIZE = ::sys::os::page_size();
|
||||
|
||||
let mut action: sigaction = mem::zeroed();
|
||||
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
|
||||
@@ -23,7 +23,6 @@ use ptr;
|
||||
use sys::os;
|
||||
use time::Duration;
|
||||
|
||||
use sys_common::stack::RED_ZONE;
|
||||
use sys_common::thread::*;
|
||||
|
||||
pub struct Thread {
|
||||
@@ -43,8 +42,7 @@ impl Thread {
|
||||
let mut attr: libc::pthread_attr_t = mem::zeroed();
|
||||
assert_eq!(pthread_attr_init(&mut attr), 0);
|
||||
|
||||
// Reserve room for the red zone, the runtime's stack of last resort.
|
||||
let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr));
|
||||
let stack_size = cmp::max(stack, min_stack_size(&attr));
|
||||
match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) {
|
||||
0 => {}
|
||||
n => {
|
||||
@@ -72,7 +70,6 @@ impl Thread {
|
||||
Ok(Thread { id: native })
|
||||
};
|
||||
|
||||
#[no_stack_check]
|
||||
extern fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
|
||||
unsafe { start_thread(main); }
|
||||
0 as *mut _
|
||||
|
||||
@@ -78,6 +78,10 @@ pub const PROGRESS_QUIET: libc::DWORD = 3;
|
||||
pub const TOKEN_ADJUST_PRIVILEGES: libc::DWORD = 0x0020;
|
||||
pub const SE_PRIVILEGE_ENABLED: libc::DWORD = 2;
|
||||
|
||||
pub const EXCEPTION_CONTINUE_SEARCH: LONG = 0;
|
||||
pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15;
|
||||
pub const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd;
|
||||
|
||||
#[repr(C)]
|
||||
#[cfg(target_arch = "x86")]
|
||||
pub struct WSADATA {
|
||||
@@ -327,6 +331,24 @@ pub struct REPARSE_MOUNTPOINT_DATA_BUFFER {
|
||||
pub ReparseTarget: libc::WCHAR,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EXCEPTION_RECORD {
|
||||
pub ExceptionCode: DWORD,
|
||||
pub ExceptionFlags: DWORD,
|
||||
pub ExceptionRecord: *mut EXCEPTION_RECORD,
|
||||
pub ExceptionAddress: LPVOID,
|
||||
pub NumberParameters: DWORD,
|
||||
pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS]
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EXCEPTION_POINTERS {
|
||||
pub ExceptionRecord: *mut EXCEPTION_RECORD,
|
||||
pub ContextRecord: LPVOID
|
||||
}
|
||||
|
||||
pub type PVECTORED_EXCEPTION_HANDLER = extern "system"
|
||||
fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG;
|
||||
|
||||
#[link(name = "ws2_32")]
|
||||
#[link(name = "userenv")]
|
||||
@@ -487,6 +509,9 @@ extern "system" {
|
||||
BufferLength: libc::DWORD,
|
||||
PreviousState: PTOKEN_PRIVILEGES,
|
||||
ReturnLength: *mut libc::DWORD) -> libc::BOOL;
|
||||
pub fn AddVectoredExceptionHandler(FirstHandler: ULONG,
|
||||
VectoredHandler: PVECTORED_EXCEPTION_HANDLER)
|
||||
-> LPVOID;
|
||||
}
|
||||
|
||||
// Functions that aren't available on Windows XP, but we still use them and just
|
||||
|
||||
@@ -8,108 +8,44 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#[cfg(stage0)]
|
||||
use core::prelude::v1::*;
|
||||
|
||||
use libc::types::os::arch::extra::{LPVOID, DWORD, LONG};
|
||||
use libc;
|
||||
use mem;
|
||||
use ptr;
|
||||
use libc::{self, LONG};
|
||||
use rt::util::report_overflow;
|
||||
use sys::c;
|
||||
use sys_common::stack;
|
||||
|
||||
pub struct Handler {
|
||||
_data: *mut libc::c_void
|
||||
}
|
||||
pub struct Handler;
|
||||
|
||||
impl Handler {
|
||||
pub unsafe fn new() -> Handler {
|
||||
make_handler()
|
||||
// This API isn't available on XP, so don't panic in that case and just
|
||||
// pray it works out ok.
|
||||
if c::SetThreadStackGuarantee(&mut 0x5000) == 0 {
|
||||
if libc::GetLastError() as u32 != libc::ERROR_CALL_NOT_IMPLEMENTED as u32 {
|
||||
panic!("failed to reserve stack space for exception handling");
|
||||
}
|
||||
}
|
||||
Handler
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Handler {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
// This is initialized in init() and only read from after
|
||||
static mut PAGE_SIZE: usize = 0;
|
||||
|
||||
#[no_stack_check]
|
||||
extern "system" fn vectored_handler(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG {
|
||||
extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS)
|
||||
-> LONG {
|
||||
unsafe {
|
||||
let rec = &(*(*ExceptionInfo).ExceptionRecord);
|
||||
let code = rec.ExceptionCode;
|
||||
|
||||
if code != EXCEPTION_STACK_OVERFLOW {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
if code == c::EXCEPTION_STACK_OVERFLOW {
|
||||
report_overflow();
|
||||
}
|
||||
|
||||
// We're calling into functions with stack checks,
|
||||
// however stack checks by limit should be disabled on Windows
|
||||
stack::record_sp_limit(0);
|
||||
|
||||
report_overflow();
|
||||
|
||||
EXCEPTION_CONTINUE_SEARCH
|
||||
c::EXCEPTION_CONTINUE_SEARCH
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn init() {
|
||||
let mut info = mem::zeroed();
|
||||
libc::GetSystemInfo(&mut info);
|
||||
PAGE_SIZE = info.dwPageSize as usize;
|
||||
|
||||
if AddVectoredExceptionHandler(0, vectored_handler) == ptr::null_mut() {
|
||||
if c::AddVectoredExceptionHandler(0, vectored_handler).is_null() {
|
||||
panic!("failed to install exception handler");
|
||||
}
|
||||
|
||||
mem::forget(make_handler());
|
||||
// Set the thread stack guarantee for the main thread.
|
||||
let _h = Handler::new();
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup() {
|
||||
}
|
||||
|
||||
pub unsafe fn make_handler() -> Handler {
|
||||
// This API isn't available on XP, so don't panic in that case and just pray
|
||||
// it works out ok.
|
||||
if c::SetThreadStackGuarantee(&mut 0x5000) == 0 {
|
||||
if libc::GetLastError() as u32 != libc::ERROR_CALL_NOT_IMPLEMENTED as u32 {
|
||||
panic!("failed to reserve stack space for exception handling");
|
||||
}
|
||||
}
|
||||
|
||||
Handler { _data: 0 as *mut libc::c_void }
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EXCEPTION_RECORD {
|
||||
pub ExceptionCode: DWORD,
|
||||
pub ExceptionFlags: DWORD,
|
||||
pub ExceptionRecord: *mut EXCEPTION_RECORD,
|
||||
pub ExceptionAddress: LPVOID,
|
||||
pub NumberParameters: DWORD,
|
||||
pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS]
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EXCEPTION_POINTERS {
|
||||
pub ExceptionRecord: *mut EXCEPTION_RECORD,
|
||||
pub ContextRecord: LPVOID
|
||||
}
|
||||
|
||||
pub type PVECTORED_EXCEPTION_HANDLER = extern "system"
|
||||
fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG;
|
||||
|
||||
pub type ULONG = libc::c_ulong;
|
||||
|
||||
const EXCEPTION_CONTINUE_SEARCH: LONG = 0;
|
||||
const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15;
|
||||
const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd;
|
||||
|
||||
extern "system" {
|
||||
fn AddVectoredExceptionHandler(FirstHandler: ULONG,
|
||||
VectoredHandler: PVECTORED_EXCEPTION_HANDLER)
|
||||
-> LPVOID;
|
||||
}
|
||||
pub unsafe fn cleanup() {}
|
||||
|
||||
@@ -11,14 +11,12 @@
|
||||
use prelude::v1::*;
|
||||
|
||||
use alloc::boxed::FnBox;
|
||||
use cmp;
|
||||
use io;
|
||||
use libc::{self, c_void, DWORD};
|
||||
use mem;
|
||||
use ptr;
|
||||
use sys::c;
|
||||
use sys::handle::Handle;
|
||||
use sys_common::stack::RED_ZONE;
|
||||
use sys_common::thread::*;
|
||||
use time::Duration;
|
||||
|
||||
@@ -36,11 +34,9 @@ impl Thread {
|
||||
// PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's
|
||||
// just that below a certain threshold you can't do anything useful.
|
||||
// That threshold is application and architecture-specific, however.
|
||||
// For now, the only requirement is that it's big enough to hold the
|
||||
// red zone. Round up to the next 64 kB because that's what the NT
|
||||
// kernel does, might as well make it explicit. With the current
|
||||
// 20 kB red zone, that makes for a 64 kB minimum stack.
|
||||
let stack_size = (cmp::max(stack, RED_ZONE) + 0xfffe) & (-0xfffe - 1);
|
||||
// Round up to the next 64 kB because that's what the NT kernel does,
|
||||
// might as well make it explicit.
|
||||
let stack_size = (stack + 0xfffe) & (!0xfffe);
|
||||
let ret = c::CreateThread(ptr::null_mut(), stack_size as libc::size_t,
|
||||
thread_start, &*p as *const _ as *mut _,
|
||||
0, ptr::null_mut());
|
||||
@@ -52,7 +48,6 @@ impl Thread {
|
||||
Ok(Thread { handle: Handle::new(ret) })
|
||||
};
|
||||
|
||||
#[no_stack_check]
|
||||
extern "system" fn thread_start(main: *mut libc::c_void) -> DWORD {
|
||||
unsafe { start_thread(main); }
|
||||
0
|
||||
|
||||
Reference in New Issue
Block a user