diff options
| author | Aaron Turon <aturon@mozilla.com> | 2014-11-23 19:21:17 -0800 |
|---|---|---|
| committer | Aaron Turon <aturon@mozilla.com> | 2014-12-18 23:31:34 -0800 |
| commit | 2b3477d373603527d23cc578f3737857b7b253d7 (patch) | |
| tree | 56022ebf11d5d27a6ef15f15d00d014a84a35837 /src/libstd/sys/windows | |
| parent | 840de072085df360733c48396224e9966e2dc72c (diff) | |
| download | rust-2b3477d373603527d23cc578f3737857b7b253d7.tar.gz rust-2b3477d373603527d23cc578f3737857b7b253d7.zip | |
libs: merge librustrt into libstd
This commit merges the `rustrt` crate into `std`, undoing part of the facade. This merger continues the paring down of the runtime system. Code relying on the public API of `rustrt` will break; some of this API is now available through `std::rt`, but is likely to change and/or be removed very soon. [breaking-change]
Diffstat (limited to 'src/libstd/sys/windows')
| -rw-r--r-- | src/libstd/sys/windows/backtrace.rs | 371 | ||||
| -rw-r--r-- | src/libstd/sys/windows/mod.rs | 3 | ||||
| -rw-r--r-- | src/libstd/sys/windows/stack_overflow.rs | 120 | ||||
| -rw-r--r-- | src/libstd/sys/windows/thread.rs | 95 | ||||
| -rw-r--r-- | src/libstd/sys/windows/thread_local.rs | 6 |
5 files changed, 592 insertions, 3 deletions
diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs new file mode 100644 index 00000000000..833b69d6cbe --- /dev/null +++ b/src/libstd/sys/windows/backtrace.rs @@ -0,0 +1,371 @@ +// Copyright 2014 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. +/// As always, windows has something very different than unix, we mainly want +/// to avoid having to depend too much on libunwind for windows. +/// +/// If you google around, you'll find a fair bit of references to built-in +/// functions to get backtraces on windows. It turns out that most of these are +/// in an external library called dbghelp. I was unable to find this library +/// via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent +/// of it. +/// +/// You'll also find that there's a function called CaptureStackBackTrace +/// mentioned frequently (which is also easy to use), but sadly I didn't have a +/// copy of that function in my mingw install (maybe it was broken?). Instead, +/// this takes the route of using StackWalk64 in order to walk the stack. + +use c_str::CString; +use intrinsics; +use io::{IoResult, Writer}; +use libc; +use mem; +use ops::Drop; +use option::{Some, None}; +use path::Path; +use result::{Ok, Err}; +use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; +use slice::SliceExt; +use str::StrPrelude; +use dynamic_lib::DynamicLibrary; + +use sys_common::backtrace::*; + +#[allow(non_snake_case)] +extern "system" { + fn GetCurrentProcess() -> libc::HANDLE; + fn GetCurrentThread() -> libc::HANDLE; + fn RtlCaptureContext(ctx: *mut arch::CONTEXT); +} + +type SymFromAddrFn = + extern "system" fn(libc::HANDLE, u64, *mut u64, + *mut SYMBOL_INFO) -> libc::BOOL; +type SymInitializeFn = + extern "system" fn(libc::HANDLE, *mut libc::c_void, + libc::BOOL) -> libc::BOOL; +type SymCleanupFn = + extern "system" fn(libc::HANDLE) -> libc::BOOL; + +type StackWalk64Fn = + extern "system" fn(libc::DWORD, libc::HANDLE, libc::HANDLE, + *mut STACKFRAME64, *mut arch::CONTEXT, + *mut libc::c_void, *mut libc::c_void, + *mut libc::c_void, *mut libc::c_void) -> libc::BOOL; + +const MAX_SYM_NAME: uint = 2000; +const IMAGE_FILE_MACHINE_I386: libc::DWORD = 0x014c; +const IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200; +const IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664; + +#[repr(C)] +struct SYMBOL_INFO { + SizeOfStruct: libc::c_ulong, + TypeIndex: libc::c_ulong, + Reserved: [u64, ..2], + Index: libc::c_ulong, + Size: libc::c_ulong, + ModBase: u64, + Flags: libc::c_ulong, + Value: u64, + Address: u64, + Register: libc::c_ulong, + Scope: libc::c_ulong, + Tag: libc::c_ulong, + NameLen: libc::c_ulong, + MaxNameLen: libc::c_ulong, + // note that windows has this as 1, but it basically just means that + // the name is inline at the end of the struct. For us, we just bump + // the struct size up to MAX_SYM_NAME. + Name: [libc::c_char, ..MAX_SYM_NAME], +} + + +#[repr(C)] +enum ADDRESS_MODE { + AddrMode1616, + AddrMode1632, + AddrModeReal, + AddrModeFlat, +} + +struct ADDRESS64 { + Offset: u64, + Segment: u16, + Mode: ADDRESS_MODE, +} + +struct STACKFRAME64 { + AddrPC: ADDRESS64, + AddrReturn: ADDRESS64, + AddrFrame: ADDRESS64, + AddrStack: ADDRESS64, + AddrBStore: ADDRESS64, + FuncTableEntry: *mut libc::c_void, + Params: [u64, ..4], + Far: libc::BOOL, + Virtual: libc::BOOL, + Reserved: [u64, ..3], + KdHelp: KDHELP64, +} + +struct KDHELP64 { + Thread: u64, + ThCallbackStack: libc::DWORD, + ThCallbackBStore: libc::DWORD, + NextCallback: libc::DWORD, + FramePointer: libc::DWORD, + KiCallUserMode: u64, + KeUserCallbackDispatcher: u64, + SystemRangeStart: u64, + KiUserExceptionDispatcher: u64, + StackBase: u64, + StackLimit: u64, + Reserved: [u64, ..5], +} + +#[cfg(target_arch = "x86")] +mod arch { + use libc; + + const MAXIMUM_SUPPORTED_EXTENSION: uint = 512; + + #[repr(C)] + pub struct CONTEXT { + ContextFlags: libc::DWORD, + Dr0: libc::DWORD, + Dr1: libc::DWORD, + Dr2: libc::DWORD, + Dr3: libc::DWORD, + Dr6: libc::DWORD, + Dr7: libc::DWORD, + FloatSave: FLOATING_SAVE_AREA, + SegGs: libc::DWORD, + SegFs: libc::DWORD, + SegEs: libc::DWORD, + SegDs: libc::DWORD, + Edi: libc::DWORD, + Esi: libc::DWORD, + Ebx: libc::DWORD, + Edx: libc::DWORD, + Ecx: libc::DWORD, + Eax: libc::DWORD, + Ebp: libc::DWORD, + Eip: libc::DWORD, + SegCs: libc::DWORD, + EFlags: libc::DWORD, + Esp: libc::DWORD, + SegSs: libc::DWORD, + ExtendedRegisters: [u8, ..MAXIMUM_SUPPORTED_EXTENSION], + } + + #[repr(C)] + pub struct FLOATING_SAVE_AREA { + ControlWord: libc::DWORD, + StatusWord: libc::DWORD, + TagWord: libc::DWORD, + ErrorOffset: libc::DWORD, + ErrorSelector: libc::DWORD, + DataOffset: libc::DWORD, + DataSelector: libc::DWORD, + RegisterArea: [u8, ..80], + Cr0NpxState: libc::DWORD, + } + + pub fn init_frame(frame: &mut super::STACKFRAME64, + ctx: &CONTEXT) -> libc::DWORD { + frame.AddrPC.Offset = ctx.Eip as u64; + frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrStack.Offset = ctx.Esp as u64; + frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrFrame.Offset = ctx.Ebp as u64; + frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; + super::IMAGE_FILE_MACHINE_I386 + } +} + +#[cfg(target_arch = "x86_64")] +mod arch { + use libc::{c_longlong, c_ulonglong}; + use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG}; + use simd; + + #[repr(C)] + pub struct CONTEXT { + _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte + P1Home: DWORDLONG, + P2Home: DWORDLONG, + P3Home: DWORDLONG, + P4Home: DWORDLONG, + P5Home: DWORDLONG, + P6Home: DWORDLONG, + + ContextFlags: DWORD, + MxCsr: DWORD, + + SegCs: WORD, + SegDs: WORD, + SegEs: WORD, + SegFs: WORD, + SegGs: WORD, + SegSs: WORD, + EFlags: DWORD, + + Dr0: DWORDLONG, + Dr1: DWORDLONG, + Dr2: DWORDLONG, + Dr3: DWORDLONG, + Dr6: DWORDLONG, + Dr7: DWORDLONG, + + Rax: DWORDLONG, + Rcx: DWORDLONG, + Rdx: DWORDLONG, + Rbx: DWORDLONG, + Rsp: DWORDLONG, + Rbp: DWORDLONG, + Rsi: DWORDLONG, + Rdi: DWORDLONG, + R8: DWORDLONG, + R9: DWORDLONG, + R10: DWORDLONG, + R11: DWORDLONG, + R12: DWORDLONG, + R13: DWORDLONG, + R14: DWORDLONG, + R15: DWORDLONG, + + Rip: DWORDLONG, + + FltSave: FLOATING_SAVE_AREA, + + VectorRegister: [M128A, .. 26], + VectorControl: DWORDLONG, + + DebugControl: DWORDLONG, + LastBranchToRip: DWORDLONG, + LastBranchFromRip: DWORDLONG, + LastExceptionToRip: DWORDLONG, + LastExceptionFromRip: DWORDLONG, + } + + #[repr(C)] + pub struct M128A { + _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte + Low: c_ulonglong, + High: c_longlong + } + + #[repr(C)] + pub struct FLOATING_SAVE_AREA { + _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte + _Dummy: [u8, ..512] // FIXME: Fill this out + } + + pub fn init_frame(frame: &mut super::STACKFRAME64, + ctx: &CONTEXT) -> DWORD { + frame.AddrPC.Offset = ctx.Rip as u64; + frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrStack.Offset = ctx.Rsp as u64; + frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrFrame.Offset = ctx.Rbp as u64; + frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; + super::IMAGE_FILE_MACHINE_AMD64 + } +} + +#[repr(C)] +struct Cleanup { + handle: libc::HANDLE, + SymCleanup: SymCleanupFn, +} + +impl Drop for Cleanup { + fn drop(&mut self) { (self.SymCleanup)(self.handle); } +} + +pub fn write(w: &mut Writer) -> IoResult<()> { + // According to windows documentation, all dbghelp functions are + // single-threaded. + static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + let _g = unsafe { LOCK.lock() }; + + // Open up dbghelp.dll, we don't link to it explicitly because it can't + // always be found. Additionally, it's nice having fewer dependencies. + let path = Path::new("dbghelp.dll"); + let lib = match DynamicLibrary::open(Some(&path)) { + Ok(lib) => lib, + Err(..) => return Ok(()), + }; + + macro_rules! sym( ($e:expr, $t:ident) => (unsafe { + match lib.symbol($e) { + Ok(f) => mem::transmute::<*mut u8, $t>(f), + Err(..) => return Ok(()) + } + }) ) + + // Fetch the symbols necessary from dbghelp.dll + let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn); + let SymInitialize = sym!("SymInitialize", SymInitializeFn); + let SymCleanup = sym!("SymCleanup", SymCleanupFn); + let StackWalk64 = sym!("StackWalk64", StackWalk64Fn); + + // Allocate necessary structures for doing the stack walk + let process = unsafe { GetCurrentProcess() }; + let thread = unsafe { GetCurrentThread() }; + let mut context: arch::CONTEXT = unsafe { intrinsics::init() }; + unsafe { RtlCaptureContext(&mut context); } + let mut frame: STACKFRAME64 = unsafe { intrinsics::init() }; + let image = arch::init_frame(&mut frame, &context); + + // Initialize this process's symbols + let ret = SymInitialize(process, 0 as *mut libc::c_void, libc::TRUE); + if ret != libc::TRUE { return Ok(()) } + let _c = Cleanup { handle: process, SymCleanup: SymCleanup }; + + // And now that we're done with all the setup, do the stack walking! + let mut i = 0i; + try!(write!(w, "stack backtrace:\n")); + while StackWalk64(image, process, thread, &mut frame, &mut context, + 0 as *mut libc::c_void, + 0 as *mut libc::c_void, + 0 as *mut libc::c_void, + 0 as *mut libc::c_void) == libc::TRUE{ + let addr = frame.AddrPC.Offset; + if addr == frame.AddrReturn.Offset || addr == 0 || + frame.AddrReturn.Offset == 0 { break } + + i += 1; + try!(write!(w, " {:2}: {:#2$x}", i, addr, HEX_WIDTH)); + let mut info: SYMBOL_INFO = unsafe { intrinsics::init() }; + info.MaxNameLen = MAX_SYM_NAME as libc::c_ulong; + // the struct size in C. the value is different to + // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81) + // due to struct alignment. + info.SizeOfStruct = 88; + + let mut displacement = 0u64; + let ret = SymFromAddr(process, addr as u64, &mut displacement, + &mut info); + + if ret == libc::TRUE { + try!(write!(w, " - ")); + let cstr = unsafe { CString::new(info.Name.as_ptr(), false) }; + let bytes = cstr.as_bytes(); + match cstr.as_str() { + Some(s) => try!(demangle(w, s)), + None => try!(w.write(bytes[..bytes.len()-1])), + } + } + try!(w.write(&['\n' as u8])); + } + + Ok(()) +} diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index d22d4e0f534..6924687d8c4 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -35,6 +35,7 @@ macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => ( }; ) } +pub mod backtrace; pub mod c; pub mod ext; pub mod condvar; @@ -46,7 +47,9 @@ pub mod pipe; pub mod process; pub mod rwlock; pub mod sync; +pub mod stack_overflow; pub mod tcp; +pub mod thread; pub mod thread_local; pub mod timer; pub mod tty; diff --git a/src/libstd/sys/windows/stack_overflow.rs b/src/libstd/sys/windows/stack_overflow.rs new file mode 100644 index 00000000000..e3d96a054f4 --- /dev/null +++ b/src/libstd/sys/windows/stack_overflow.rs @@ -0,0 +1,120 @@ +// Copyright 2014 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. + +use rt::local::Local; +use rt::task::Task; +use rt::util::report_overflow; +use core::prelude::*; +use ptr; +use mem; +use libc; +use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL}; +use sys_common::stack; + +pub struct Handler { + _data: *mut libc::c_void +} + +impl Handler { + pub unsafe fn new() -> Handler { + make_handler() + } +} + +impl Drop for Handler { + fn drop(&mut self) {} +} + +// get_task_info is called from an exception / signal handler. +// It returns the guard page of the current task or 0 if that +// guard page doesn't exist. None is returned if there's currently +// no local task. +unsafe fn get_task_guard_page() -> Option<uint> { + let task: Option<*mut Task> = Local::try_unsafe_borrow(); + task.map(|task| (&*task).stack_guard().unwrap_or(0)) +} + +// This is initialized in init() and only read from after +static mut PAGE_SIZE: uint = 0; + +#[no_stack_check] +extern "system" fn vectored_handler(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG { + unsafe { + let rec = &(*(*ExceptionInfo).ExceptionRecord); + let code = rec.ExceptionCode; + + if code != EXCEPTION_STACK_OVERFLOW { + return EXCEPTION_CONTINUE_SEARCH; + } + + // We're calling into functions with stack checks, + // however stack checks by limit should be disabled on Windows + stack::record_sp_limit(0); + + if get_task_guard_page().is_some() { + report_overflow(); + } + + EXCEPTION_CONTINUE_SEARCH + } +} + +pub unsafe fn init() { + let mut info = mem::zeroed(); + libc::GetSystemInfo(&mut info); + PAGE_SIZE = info.dwPageSize as uint; + + if AddVectoredExceptionHandler(0, vectored_handler) == ptr::null_mut() { + panic!("failed to install exception handler"); + } + + mem::forget(make_handler()); +} + +pub unsafe fn cleanup() { +} + +pub unsafe fn make_handler() -> Handler { + if SetThreadStackGuarantee(&mut 0x5000) == 0 { + panic!("failed to reserve stack space for exception handling"); + } + + Handler { _data: 0i as *mut libc::c_void } +} + +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] +} + +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: uint = 15; +const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd; + +extern "system" { + fn AddVectoredExceptionHandler(FirstHandler: ULONG, + VectoredHandler: PVECTORED_EXCEPTION_HANDLER) + -> LPVOID; + fn SetThreadStackGuarantee(StackSizeInBytes: *mut ULONG) -> BOOL; +} diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs new file mode 100644 index 00000000000..00f1e9767f5 --- /dev/null +++ b/src/libstd/sys/windows/thread.rs @@ -0,0 +1,95 @@ +// Copyright 2014 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. + +use core::prelude::*; + +use boxed::Box; +use cmp; +use mem; +use ptr; +use libc; +use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL, + LPVOID, DWORD, LPDWORD, HANDLE}; +use sys_common::stack::RED_ZONE; +use sys_common::thread::*; + +pub type rust_thread = HANDLE; +pub type rust_thread_return = DWORD; + +pub type StartFn = extern "system" fn(*mut libc::c_void) -> rust_thread_return; + +#[no_stack_check] +pub extern "system" fn thread_start(main: *mut libc::c_void) -> rust_thread_return { + return start_thread(main); +} + +pub mod guard { + pub unsafe fn main() -> uint { + 0 + } + + pub unsafe fn current() -> uint { + 0 + } + + pub unsafe fn init() { + } +} + +pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread { + let arg: *mut libc::c_void = mem::transmute(p); + // FIXME On UNIX, we guard against stack sizes that are too small but + // that's because pthreads enforces that stacks are at least + // 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); + let ret = CreateThread(ptr::null_mut(), stack_size as libc::size_t, + thread_start, arg, 0, ptr::null_mut()); + + if ret as uint == 0 { + // be sure to not leak the closure + let _p: Box<proc():Send> = mem::transmute(arg); + panic!("failed to spawn native thread: {}", ret); + } + return ret; +} + +pub unsafe fn join(native: rust_thread) { + use libc::consts::os::extra::INFINITE; + WaitForSingleObject(native, INFINITE); +} + +pub unsafe fn detach(native: rust_thread) { + assert!(libc::CloseHandle(native) != 0); +} + +pub unsafe fn yield_now() { + // This function will return 0 if there are no other threads to execute, + // but this also means that the yield was useless so this isn't really a + // case that needs to be worried about. + SwitchToThread(); +} + +#[allow(non_snake_case)] +extern "system" { + fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, + dwStackSize: SIZE_T, + lpStartAddress: StartFn, + lpParameter: LPVOID, + dwCreationFlags: DWORD, + lpThreadId: LPDWORD) -> HANDLE; + fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; + fn SwitchToThread() -> BOOL; +} diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs index 969b322af99..6c8d9639d5c 100644 --- a/src/libstd/sys/windows/thread_local.rs +++ b/src/libstd/sys/windows/thread_local.rs @@ -13,8 +13,8 @@ use prelude::*; use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL}; use mem; -use rustrt; -use rustrt::exclusive::Exclusive; +use rt; +use rt::exclusive::Exclusive; use sync::{ONCE_INIT, Once}; pub type Key = DWORD; @@ -131,7 +131,7 @@ fn init_dtors() { DTORS = mem::transmute(dtors); } - rustrt::at_exit(move|| unsafe { + rt::at_exit(move|| unsafe { mem::transmute::<_, Box<Exclusive<Vec<(Key, Dtor)>>>>(DTORS); DTORS = 0 as *mut _; }); |
