Skip to content

Instantly share code, notes, and snippets.

@cfsamson
Last active July 2, 2019 23:08
Show Gist options
  • Save cfsamson/52beafe2d75da54234233b49d439a358 to your computer and use it in GitHub Desktop.
Save cfsamson/52beafe2d75da54234233b49d439a358 to your computer and use it in GitHub Desktop.
Trying to create the minimal example needed for the problem with Crystals windows context switch
STACK_SIZE = 1024*1024*8
class Test
property value : UInt64 = 0
property stack = Array(UInt8).new(STACK_SIZE)
property func : Proc(Void) = ->{}
property rsp : UInt64 = 0
def makecontext(proc : Proc)
@func = proc
s_ptr = @stack.to_unsafe
s_start = s_ptr + (STACK_SIZE - 32)
@rsp = (s_start - 1*8).as(Void*).address
# Our entry
s_start_ptr = s_start.as(UInt64*)
s_start_ptr.value = ->start(Test).pointer.address
# First parameter
first_param = (s_start - 8).as(UInt64*)
first_param.value = self.as(Void*).address
end
def run
@func.call
Process.exit(0)
end
end
def start(f : Test)
f.run
end
@[NoInline]
@[Naked]
def swap(data)
{% if flag?(:win32) %}
asm("
pushq %rcx
movq $0, %rsp
popq %rcx
"
:: "r"(data) )
{% else %}
asm("
pushq %rdi
movq $0, %rsp
popq %rdi
"
:: "r"(data))
{% end %}
end
test = Test.new
test.makecontext(->{ puts "Hello from new stack" })
swap(test.rsp)
#![feature(asm)]
#![feature(naked_functions)]
// # How to build this in Rust
// 1. Install Rust: https://www.rust-lang.org/tools/install
// 2. Install the nightly compiler: `rustup toolchain install nightly`
// 3. Create a new directory and run `cargo init`
// 4. Make sure you use the nightly by running `rustup override set nightly` in the project folder
// 5. Copy this code over to `main.rs`
// 6. Run the program: `cargo run`
const STACK_SIZE: isize = 1024 * 1024 * 8;
struct Test {
stack: Vec<u8>,
func: fn(),
rsp: Option<*const u64>,
}
impl Test {
fn new() -> Self {
Test {
stack: vec![0_u8; STACK_SIZE as usize],
func: || {},
rsp: None,
}
}
fn makecontext(&mut self, proc: fn()) {
self.func = proc;
let s_ptr = self.stack.as_ptr();
let mut s_start = unsafe { s_ptr.offset(STACK_SIZE - 32) as *mut u8 };
self.rsp = Some(unsafe { s_start.offset(-1 * 8) as *const u64 });
unsafe {
// Our entry
std::ptr::write(s_start as *mut u64, start as u64);
// first parameter
let mut first_param = s_start.offset(-8) as *mut u64;
// this hurts to write...
let this = Box::new(self);
std::ptr::write(first_param, Box::into_raw(this) as u64);
}
}
fn run(&self) {
(self.func)();
}
}
fn start(f: *const *const Test) {
unsafe {
(**f).run();
}
std::process::exit(0);
}
#[cfg(target_os = "windows")]
#[inline(never)]
#[naked]
fn swap(data: *const u64) {
unsafe {
asm!("
pushq %rcx
movq $0, %rsp
popq %rcx
"
:: "r"(data) :: "volatile", "alignstack");
}
}
#[cfg(not(target_os = "windows"))]
#[inline(never)]
#[naked]
fn swap(data: *const u64) {
unsafe {
asm!("
pushq %rdi
movq $0, %rsp
popq %rdi
"
:: "r"(data));
}
}
fn main() {
let mut test = Test::new();
test.makecontext(|| println!("Hello from new stack"));
swap(test.rsp.unwrap());
println!("BEFORE: {:?}", test.rsp.unwrap());
}
STACK_SIZE = 1024*1024*8
class Test
property value : UInt64 = 0
property stack = Array(UInt8).new(STACK_SIZE)
property func : Proc(Void) = ->{}
property rsp : UInt64 = 0
property first_param : UInt64 = 0
def makecontext(proc : Proc)
@func = proc
s_ptr = @stack.to_unsafe
s_start = s_ptr + (STACK_SIZE - 32)
@rsp = s_start.as(Void*).address
# Our entry
s_start_ptr = s_start.as(UInt64*)
s_start_ptr.value = ->(f : Test) { f.run }.pointer.address
# First parameter
@first_param = self.as(Void*).address
end
def run
@func.call
Process.exit(0)
end
end
@[NoInline]
@[Naked]
def swap(data, first_param)
{% if flag?(:win32) %}
asm("
movq $0, %rsp
movq $1, %rcx
#movq %rcx, $1
"
:: "r"(data), "r"(first_param))
{% else %}
asm("
movq $0, %rsp
movq $1, %rdi
#movq %rdi, $1
"
:: "r"(data), "r"(first_param))
{% end %}
end
test = Test.new
test.makecontext(->{puts "Hello from new stack"})
swap(test.rsp, test.@first_param)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment