From 442f4a5f2ca6f7f4082c09968e0fd83601a50d2a Mon Sep 17 00:00:00 2001 From: klutzy Date: Mon, 26 Aug 2013 22:01:55 +0900 Subject: [PATCH] Support Win64 context switching This patch saves and restores win64's nonvolatile registers. This patch also saves stack information of thread environment block (TEB), which is at %gs:0x08 and %gs:0x10. --- src/libstd/rt/context.rs | 38 ++++++++++++++++++++++----- src/rt/arch/x86_64/_context.S | 48 +++++++++++++++++++++++++++++++++++ src/rt/arch/x86_64/regs.h | 36 +++++++++++++++++++------- 3 files changed, 106 insertions(+), 16 deletions(-) diff --git a/src/libstd/rt/context.rs b/src/libstd/rt/context.rs index 5aaddc68383..476554bf7f7 100644 --- a/src/libstd/rt/context.rs +++ b/src/libstd/rt/context.rs @@ -47,6 +47,7 @@ impl Context { let fp: *c_void = task_start_wrapper as *c_void; let argp: *c_void = unsafe { transmute::<&~fn(), *c_void>(&*start) }; + let stack_base: *uint = stack.start(); let sp: *uint = stack.end(); let sp: *mut uint = unsafe { transmute_mut_unsafe(sp) }; // Save and then immediately load the current context, @@ -56,7 +57,7 @@ impl Context { swap_registers(transmute_mut_region(&mut *regs), transmute_region(&*regs)); }; - initialize_call_frame(&mut *regs, fp, argp, sp); + initialize_call_frame(&mut *regs, fp, argp, sp, stack_base); return Context { start: Some(start), @@ -107,7 +108,8 @@ fn new_regs() -> ~Registers { } #[cfg(target_arch = "x86")] -fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) { +fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, + sp: *mut uint, _stack_base: *uint) { let sp = align_down(sp); let sp = mut_offset(sp, -4); @@ -123,14 +125,19 @@ fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: regs.ebp = 0; } -#[cfg(target_arch = "x86_64")] +#[cfg(windows, target_arch = "x86_64")] +type Registers = [uint, ..34]; +#[cfg(not(windows), target_arch = "x86_64")] type Registers = [uint, ..22]; -#[cfg(target_arch = "x86_64")] +#[cfg(windows, target_arch = "x86_64")] +fn new_regs() -> ~Registers { ~([0, .. 34]) } +#[cfg(not(windows), target_arch = "x86_64")] fn new_regs() -> ~Registers { ~([0, .. 22]) } #[cfg(target_arch = "x86_64")] -fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) { +fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, + sp: *mut uint, stack_base: *uint) { // Redefinitions from regs.h static RUSTRT_ARG0: uint = 3; @@ -138,6 +145,21 @@ fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: static RUSTRT_IP: uint = 8; static RUSTRT_RBP: uint = 2; + #[cfg(windows)] + fn initialize_tib(regs: &mut Registers, sp: *mut uint, stack_base: *uint) { + // Redefinitions from regs.h + static RUSTRT_ST1: uint = 11; // stack bottom + static RUSTRT_ST2: uint = 12; // stack top + regs[RUSTRT_ST1] = sp as uint; + regs[RUSTRT_ST2] = stack_base as uint; + } + #[cfg(not(windows))] + fn initialize_tib(_: &mut Registers, _: *mut uint, _: *uint) { + } + + // Win64 manages stack range at TIB: %gs:0x08 (top) and %gs:0x10 (bottom) + initialize_tib(regs, sp, stack_base); + let sp = align_down(sp); let sp = mut_offset(sp, -1); @@ -164,7 +186,8 @@ type Registers = [uint, ..32]; fn new_regs() -> ~Registers { ~([0, .. 32]) } #[cfg(target_arch = "arm")] -fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) { +fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, + sp: *mut uint, _stack_base: *uint) { let sp = align_down(sp); // sp of arm eabi is 8-byte aligned let sp = mut_offset(sp, -2); @@ -184,7 +207,8 @@ type Registers = [uint, ..32]; fn new_regs() -> ~Registers { ~([0, .. 32]) } #[cfg(target_arch = "mips")] -fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) { +fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, + sp: *mut uint, _stack_base: *uint) { let sp = align_down(sp); // sp of mips o32 is 8-byte aligned let sp = mut_offset(sp, -2); diff --git a/src/rt/arch/x86_64/_context.S b/src/rt/arch/x86_64/_context.S index a8af27e3e4f..857fe91c914 100644 --- a/src/rt/arch/x86_64/_context.S +++ b/src/rt/arch/x86_64/_context.S @@ -86,16 +86,40 @@ SWAP_REGISTERS: mov %r14, (RUSTRT_R14*8)(ARG0) mov %r15, (RUSTRT_R15*8)(ARG0) +#if defined(__MINGW32__) || defined(_WINDOWS) + mov %rdi, (RUSTRT_RDI*8)(ARG0) + mov %rsi, (RUSTRT_RSI*8)(ARG0) + + // Save stack range + mov %gs:0x08, %r8 + mov %r8, (RUSTRT_ST1*8)(ARG0) + mov %gs:0x10, %r9 + mov %r9, (RUSTRT_ST2*8)(ARG0) +#endif + // Save 0th argument register: mov ARG0, (RUSTRT_ARG0*8)(ARG0) // Save non-volatile XMM registers: +#if defined(__MINGW32__) || defined(_WINDOWS) + movapd %xmm6, (RUSTRT_XMM6*8)(ARG0) + movapd %xmm7, (RUSTRT_XMM7*8)(ARG0) + movapd %xmm8, (RUSTRT_XMM8*8)(ARG0) + movapd %xmm9, (RUSTRT_XMM9*8)(ARG0) + movapd %xmm10, (RUSTRT_XMM10*8)(ARG0) + movapd %xmm11, (RUSTRT_XMM11*8)(ARG0) + movapd %xmm12, (RUSTRT_XMM12*8)(ARG0) + movapd %xmm13, (RUSTRT_XMM13*8)(ARG0) + movapd %xmm14, (RUSTRT_XMM14*8)(ARG0) + movapd %xmm15, (RUSTRT_XMM15*8)(ARG0) +#else movapd %xmm0, (RUSTRT_XMM0*8)(ARG0) movapd %xmm1, (RUSTRT_XMM1*8)(ARG0) movapd %xmm2, (RUSTRT_XMM2*8)(ARG0) movapd %xmm3, (RUSTRT_XMM3*8)(ARG0) movapd %xmm4, (RUSTRT_XMM4*8)(ARG0) movapd %xmm5, (RUSTRT_XMM5*8)(ARG0) +#endif // Restore non-volatile integer registers: // (including RSP) @@ -107,16 +131,40 @@ SWAP_REGISTERS: mov (RUSTRT_R14*8)(ARG1), %r14 mov (RUSTRT_R15*8)(ARG1), %r15 +#if defined(__MINGW32__) || defined(_WINDOWS) + mov (RUSTRT_RDI*8)(ARG1), %rdi + mov (RUSTRT_RSI*8)(ARG1), %rsi + + // Restore stack range + mov (RUSTRT_ST1*8)(ARG1), %r8 + mov %r8, %gs:0x08 + mov (RUSTRT_ST2*8)(ARG1), %r9 + mov %r9, %gs:0x10 +#endif + // Restore 0th argument register: mov (RUSTRT_ARG0*8)(ARG1), ARG0 // Restore non-volatile XMM registers: +#if defined(__MINGW32__) || defined(_WINDOWS) + movapd (RUSTRT_XMM6*8)(ARG1), %xmm6 + movapd (RUSTRT_XMM7*8)(ARG1), %xmm7 + movapd (RUSTRT_XMM8*8)(ARG1), %xmm8 + movapd (RUSTRT_XMM9*8)(ARG1), %xmm9 + movapd (RUSTRT_XMM10*8)(ARG1), %xmm10 + movapd (RUSTRT_XMM11*8)(ARG1), %xmm11 + movapd (RUSTRT_XMM12*8)(ARG1), %xmm12 + movapd (RUSTRT_XMM13*8)(ARG1), %xmm13 + movapd (RUSTRT_XMM14*8)(ARG1), %xmm14 + movapd (RUSTRT_XMM15*8)(ARG1), %xmm15 +#else movapd (RUSTRT_XMM0*8)(ARG1), %xmm0 movapd (RUSTRT_XMM1*8)(ARG1), %xmm1 movapd (RUSTRT_XMM2*8)(ARG1), %xmm2 movapd (RUSTRT_XMM3*8)(ARG1), %xmm3 movapd (RUSTRT_XMM4*8)(ARG1), %xmm4 movapd (RUSTRT_XMM5*8)(ARG1), %xmm5 +#endif // Jump to the instruction pointer // found in regs: diff --git a/src/rt/arch/x86_64/regs.h b/src/rt/arch/x86_64/regs.h index 1aca452df10..cff47ac378a 100644 --- a/src/rt/arch/x86_64/regs.h +++ b/src/rt/arch/x86_64/regs.h @@ -18,15 +18,33 @@ #define RUSTRT_R14 6 #define RUSTRT_R15 7 #define RUSTRT_IP 8 -// Not used, just padding -#define RUSTRT_XXX 9 -#define RUSTRT_XMM0 10 -#define RUSTRT_XMM1 12 -#define RUSTRT_XMM2 14 -#define RUSTRT_XMM3 16 -#define RUSTRT_XMM4 18 -#define RUSTRT_XMM5 20 -#define RUSTRT_MAX 22 +#if defined(__MINGW32__) || defined(_WINDOWS) + #define RUSTRT_RDI 9 + #define RUSTRT_RSI 10 + #define RUSTRT_ST1 11 + #define RUSTRT_ST2 12 + #define RUSTRT_XMM6 14 + #define RUSTRT_XMM7 16 + #define RUSTRT_XMM8 18 + #define RUSTRT_XMM9 20 + #define RUSTRT_XMM10 22 + #define RUSTRT_XMM11 24 + #define RUSTRT_XMM12 26 + #define RUSTRT_XMM13 28 + #define RUSTRT_XMM14 30 + #define RUSTRT_XMM15 32 + #define RUSTRT_MAX 34 +#else + // Not used, just padding + #define RUSTRT_XXX 9 + #define RUSTRT_XMM0 10 + #define RUSTRT_XMM1 12 + #define RUSTRT_XMM2 14 + #define RUSTRT_XMM3 16 + #define RUSTRT_XMM4 18 + #define RUSTRT_XMM5 20 + #define RUSTRT_MAX 22 +#endif // ARG0 is the register in which the first argument goes. // Naturally this depends on your operating system.