Browse Source
This commit implements registering unwind information for JIT functions on Windows so that the operating system can both walk and unwind stacks containing JIT frames. Currently this only works with Cranelift as lightbeam does not emit unwind information yet. This commit also resets the stack guard page on Windows for stack overflow exceptions, allowing reliable stack overflow traps. With these changes, all previously disabled test suite tests (not including the multi-value tests) on Windows are now passing. Fixes #291.pull/462/head
Peter Huene
5 years ago
16 changed files with 382 additions and 131 deletions
@ -0,0 +1,134 @@ |
|||
//! Runtime function table.
|
|||
//!
|
|||
//! This module is primarily used to track JIT functions on Windows for stack walking and unwind.
|
|||
|
|||
/// Represents a runtime function table.
|
|||
///
|
|||
/// The runtime function table is not implemented for non-Windows target platforms.
|
|||
#[cfg(not(target_os = "windows"))] |
|||
pub(crate) struct FunctionTable; |
|||
|
|||
#[cfg(not(target_os = "windows"))] |
|||
impl FunctionTable { |
|||
/// Creates a new function table.
|
|||
pub fn new() -> Self { |
|||
Self |
|||
} |
|||
|
|||
/// Returns the number of functions in the table, also referred to as its 'length'.
|
|||
///
|
|||
/// For non-Windows platforms, the table will always be empty.
|
|||
pub fn len(&self) -> usize { |
|||
0 |
|||
} |
|||
|
|||
/// Adds a function to the table based off of the start offset, end offset, and unwind offset.
|
|||
///
|
|||
/// The offsets are from the "module base", which is provided when the table is published.
|
|||
///
|
|||
/// For non-Windows platforms, this is a no-op.
|
|||
pub fn add_function(&mut self, _start: u32, _end: u32, _unwind: u32) {} |
|||
|
|||
/// Publishes the function table using the given base address.
|
|||
///
|
|||
/// A published function table will automatically be deleted when it is dropped.
|
|||
///
|
|||
/// For non-Windows platforms, this is a no-op.
|
|||
pub fn publish(&mut self, _base_address: u64) -> Result<(), String> { |
|||
Ok(()) |
|||
} |
|||
} |
|||
|
|||
/// Represents a runtime function table.
|
|||
///
|
|||
/// This is used to register JIT code with the operating system to enable stack walking and unwinding.
|
|||
#[cfg(all(target_os = "windows", target_arch = "x86_64"))] |
|||
pub(crate) struct FunctionTable { |
|||
functions: Vec<winapi::um::winnt::RUNTIME_FUNCTION>, |
|||
published: bool, |
|||
} |
|||
|
|||
#[cfg(all(target_os = "windows", target_arch = "x86_64"))] |
|||
impl FunctionTable { |
|||
/// Creates a new function table.
|
|||
pub fn new() -> Self { |
|||
Self { |
|||
functions: Vec::new(), |
|||
published: false, |
|||
} |
|||
} |
|||
|
|||
/// Returns the number of functions in the table, also referred to as its 'length'.
|
|||
pub fn len(&self) -> usize { |
|||
self.functions.len() |
|||
} |
|||
|
|||
/// Adds a function to the table based off of the start offset, end offset, and unwind offset.
|
|||
///
|
|||
/// The offsets are from the "module base", which is provided when the table is published.
|
|||
pub fn add_function(&mut self, start: u32, end: u32, unwind: u32) { |
|||
use winapi::um::winnt; |
|||
|
|||
assert!(!self.published, "table has already been published"); |
|||
|
|||
let mut entry = winnt::RUNTIME_FUNCTION::default(); |
|||
|
|||
entry.BeginAddress = start; |
|||
entry.EndAddress = end; |
|||
|
|||
unsafe { |
|||
*entry.u.UnwindInfoAddress_mut() = unwind; |
|||
} |
|||
|
|||
self.functions.push(entry); |
|||
} |
|||
|
|||
/// Publishes the function table using the given base address.
|
|||
///
|
|||
/// A published function table will automatically be deleted when it is dropped.
|
|||
pub fn publish(&mut self, base_address: u64) -> Result<(), String> { |
|||
use winapi::um::winnt; |
|||
|
|||
if self.published { |
|||
return Err("function table was already published".into()); |
|||
} |
|||
|
|||
self.published = true; |
|||
|
|||
if self.functions.is_empty() { |
|||
return Ok(()); |
|||
} |
|||
|
|||
unsafe { |
|||
// Windows heap allocations are 32-bit aligned, but assert just in case
|
|||
assert!( |
|||
(self.functions.as_mut_ptr() as u64) % 4 == 0, |
|||
"function table allocation was not aligned" |
|||
); |
|||
|
|||
if winnt::RtlAddFunctionTable( |
|||
self.functions.as_mut_ptr(), |
|||
self.functions.len() as u32, |
|||
base_address, |
|||
) == 0 |
|||
{ |
|||
return Err("failed to add function table".into()); |
|||
} |
|||
} |
|||
|
|||
Ok(()) |
|||
} |
|||
} |
|||
|
|||
#[cfg(target_os = "windows")] |
|||
impl Drop for FunctionTable { |
|||
fn drop(&mut self) { |
|||
use winapi::um::winnt; |
|||
|
|||
if self.published { |
|||
unsafe { |
|||
winnt::RtlDeleteFunctionTable(self.functions.as_mut_ptr()); |
|||
} |
|||
} |
|||
} |
|||
} |
Loading…
Reference in new issue