use stack::Stack;
use std::usize;
use std::mem::transmute;
#[cfg(target_arch = "x86_64")]
use std::simd;
use thunk::Thunk;
use libc;
use sys;
#[derive(Debug)]
pub struct Context {
regs: Box<Registers>,
stack_bounds: Option<(usize, usize)>,
}
pub type InitFn = extern "C" fn(usize, *mut ()) -> !;
impl Context {
pub fn empty() -> Context {
Context {
regs: Box::new(Registers::new()),
stack_bounds: None,
}
}
pub fn new<F>(init: InitFn, arg: usize, start: F, stack: &mut Stack) -> Context
where F: FnOnce() + Send + 'static {
let sp: *const usize = stack.end();
let sp: *mut usize = sp as *mut usize;
let mut regs = Box::new(Registers::new());
initialize_call_frame(&mut regs, init, arg, unsafe { transmute(Box::new(Thunk::new(start))) }, sp);
let stack_base: *const usize = stack.start();
let bounds =
if sp as libc::uintptr_t == stack_base as libc::uintptr_t {
None
} else {
Some((stack_base as usize, sp as usize))
};
return Context {
regs: regs,
stack_bounds: bounds,
}
}
pub fn swap(out_context: &mut Context, in_context: &Context) {
debug!("swapping contexts");
let out_regs: &mut Registers = match out_context {
&mut Context { regs: ref mut r, .. } => r
};
let in_regs: &Registers = match in_context {
&Context { regs: ref r, .. } => r
};
debug!("noting the stack limit and doing raw swap");
unsafe {
match in_context.stack_bounds {
Some((lo, hi)) => sys::stack::record_rust_managed_stack_bounds(lo, hi),
None => sys::stack::record_rust_managed_stack_bounds(0, usize::MAX),
}
rust_swap_registers(out_regs, in_regs)
}
}
}
extern {
fn rust_swap_registers(out_regs: *mut Registers, in_regs: *const Registers);
}
#[cfg(target_arch = "x86")]
#[repr(C)]
#[derive(Debug)]
struct Registers {
eax: u32, ebx: u32, ecx: u32, edx: u32,
ebp: u32, esi: u32, edi: u32, esp: u32,
cs: u16, ds: u16, ss: u16, es: u16, fs: u16, gs: u16,
eflags: u32, eip: u32
}
#[cfg(target_arch = "x86")]
impl Registers {
fn new() -> Registers {
Registers {
eax: 0, ebx: 0, ecx: 0, edx: 0,
ebp: 0, esi: 0, edi: 0, esp: 0,
cs: 0, ds: 0, ss: 0, es: 0, fs: 0, gs: 0,
eflags: 0, eip: 0,
}
}
}
#[cfg(target_arch = "x86")]
fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: usize, thunkptr: *mut (), sp: *mut usize) {
let sp = align_down(sp);
let sp = mut_offset(sp, -4);
unsafe { *mut_offset(sp, 2) = thunkptr as usize };
unsafe { *mut_offset(sp, 1) = arg as usize };
unsafe { *mut_offset(sp, 0) = 0 };
regs.esp = sp as u32;
regs.eip = fptr as u32;
regs.ebp = 0;
}
#[cfg(all(windows, target_arch = "x86_64"))]
#[repr(C)]
#[derive(Debug)]
struct Registers {
gpr: [libc::uintptr_t; 14],
_xmm: [simd::u32x4; 10]
}
#[cfg(all(windows, target_arch = "x86_64"))]
impl Registers {
fn new() -> Registers {
Registers {
gpr: [0; 14],
_xmm: [simd::u32x4(0,0,0,0); 10]
}
}
}
#[cfg(all(not(windows), target_arch = "x86_64"))]
#[repr(C)]
#[derive(Debug)]
struct Registers {
gpr: [libc::uintptr_t; 10],
_xmm: [simd::u32x4; 6]
}
#[cfg(all(not(windows), target_arch = "x86_64"))]
impl Registers {
fn new() -> Registers {
Registers {
gpr: [0; 10],
_xmm: [simd::u32x4(0,0,0,0); 6]
}
}
}
#[cfg(target_arch = "x86_64")]
fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: usize, thunkptr: *mut (), sp: *mut usize) {
extern { fn rust_bootstrap_green_task(); }
static RUSTRT_RSP: usize = 1;
static RUSTRT_IP: usize = 8;
static RUSTRT_RBP: usize = 2;
static RUSTRT_R12: usize = 4;
static RUSTRT_R13: usize = 5;
static RUSTRT_R14: usize = 6;
let sp = align_down(sp);
let sp = mut_offset(sp, -1);
unsafe { *sp = 0; }
debug!("creating call framenn");
debug!("fptr {:#x}", fptr as libc::uintptr_t);
debug!("arg {:#x}", arg);
debug!("sp {:?}", sp);
regs.gpr[RUSTRT_R12] = arg as libc::uintptr_t;
regs.gpr[RUSTRT_R13] = thunkptr as libc::uintptr_t;
regs.gpr[RUSTRT_R14] = fptr as libc::uintptr_t;
regs.gpr[RUSTRT_RSP] = sp as libc::uintptr_t;
regs.gpr[RUSTRT_IP] = rust_bootstrap_green_task as libc::uintptr_t;
regs.gpr[RUSTRT_RBP] = 0;
}
#[cfg(target_arch = "arm")]
#[repr(C)]
#[derive(Debug)]
struct Registers([libc::uintptr_t; 32]);
#[cfg(target_arch = "arm")]
impl Registers {
fn new() -> Registers {
Registers([0; 32])
}
}
#[cfg(target_arch = "arm")]
fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: usize, thunkptr: *mut (), sp: *mut usize) {
extern { fn rust_bootstrap_green_task(); }
let sp = align_down(sp);
let sp = mut_offset(sp, -2);
unsafe { *sp = 0; }
let &mut Registers(ref mut regs) = regs;
regs[0] = arg as libc::uintptr_t; regs[3] = thunkptr as libc::uintptr_t; regs[5] = fptr as libc::uintptr_t; regs[13] = sp as libc::uintptr_t; regs[14] = rust_bootstrap_green_task as libc::uintptr_t; }
#[cfg(any(target_arch = "mips",
target_arch = "mipsel"))]
#[repr(C)]
#[derive(Debug)]
struct Registers([libc::uintptr_t; 32]);
#[cfg(any(target_arch = "mips",
target_arch = "mipsel"))]
impl Registers {
fn new() -> Registers {
Registers([0; 32])
}
}
#[cfg(any(target_arch = "mips",
target_arch = "mipsel"))]
fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: usize, thunkptr: *mut (), sp: *mut usize) {
let sp = align_down(sp);
let sp = mut_offset(sp, -2);
unsafe { *sp = 0; }
let &mut Registers(ref mut regs) = regs;
regs[4] = arg as libc::uintptr_t;
regs[5] = thunkptr as libc::uintptr_t;
regs[29] = sp as libc::uintptr_t;
regs[25] = fptr as libc::uintptr_t;
regs[31] = fptr as libc::uintptr_t;
}
fn align_down(sp: *mut usize) -> *mut usize {
let sp = (sp as usize) & !(16 - 1);
sp as *mut usize
}
#[inline]
pub fn mut_offset<T>(ptr: *mut T, count: isize) -> *mut T {
unsafe { ptr.offset(count) }
}
#[cfg(test)]
mod test {
use thunk::Thunk;
use std::mem::transmute;
use std::sync::mpsc::channel;
use std::rt::util::min_stack;
use std::rt::unwind::try;
use stack::Stack;
use context::Context;
extern "C" fn init_fn(arg: usize, f: *mut ()) -> ! {
let func: Box<Thunk<(), _>> = unsafe { transmute(f) };
if let Err(cause) = unsafe { try(move|| func.invoke(())) } {
error!("Panicked inside: {:?}", cause.downcast::<&str>());
}
let ctx: &Context = unsafe { transmute(arg) };
let mut dummy = Context::empty();
Context::swap(&mut dummy, ctx);
unreachable!();
}
#[test]
fn test_swap_context() {
let mut cur = Context::empty();
let (tx, rx) = channel();
let mut stk = Stack::new(min_stack());
let ctx = Context::new(init_fn, unsafe { transmute(&cur) }, move|| {
tx.send(1).unwrap();
}, &mut stk);
assert!(rx.try_recv().is_err());
Context::swap(&mut cur, &ctx);
assert_eq!(rx.recv().unwrap(), 1);
}
}