From 6a0d86c75472e1d8c4ad7bfc7d5e642cec48ff4d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Oct 2011 15:45:17 -0700 Subject: [PATCH] copy over x86-specific code --- src/rt/arch/x64/_context.S | 76 +++++++++++++++++++++++++++++++++++++ src/rt/arch/x64/ccall.S | 22 +++++++++++ src/rt/arch/x64/context.cpp | 73 +++++++++++++++++++++++++++++++++++ src/rt/arch/x64/context.h | 62 ++++++++++++++++++++++++++++++ 4 files changed, 233 insertions(+) create mode 100644 src/rt/arch/x64/_context.S create mode 100644 src/rt/arch/x64/ccall.S create mode 100644 src/rt/arch/x64/context.cpp create mode 100644 src/rt/arch/x64/context.h diff --git a/src/rt/arch/x64/_context.S b/src/rt/arch/x64/_context.S new file mode 100644 index 000000000000..561a250cedb4 --- /dev/null +++ b/src/rt/arch/x64/_context.S @@ -0,0 +1,76 @@ + .text + +/* +Callee save registers: + ebp, ebx, esi, edi + +Caller save registers: + eax, ecx, edx +*/ + +/* + Stores current registers into arg0/RCX and restores + registers found in arg1/RDX. This is used by our + implementation of getcontext. +*/ + +// swap_registers(registers_t *oregs, registers_t *regs) +.globl swap_registers +swap_registers: + // save the old context + movl 4(%esp), %eax + //movl %eax, 0(%eax) + movl %ebx, 4(%eax) + movl %ecx, 8(%eax) + movl %edx, 12(%eax) + movl %ebp, 16(%eax) + movl %esi, 20(%eax) + movl %edi, 24(%eax) + //movl %cs, 32(%eax) + //movl %ds, 34(%eax) + //movl %ss, 36(%eax) + //movl %es, 38(%eax) + //movl %fs, 40(%eax) + //movl %gs, 42(%eax) + + // save the flags + pushf + popl %ecx + movl %ecx, 44(%eax) + + // save the return address as the instruction pointer + // and save the stack pointer of the caller + popl %ecx + movl %esp, 28(%eax) + movl %ecx, 48(%eax) + + // restore the new context + movl 4(%esp), %eax + + movl 4(%eax), %ebx + // save ecx for later... + movl 12(%eax), %edx + movl 16(%eax), %ebp + movl 20(%eax), %esi + movl 24(%eax), %edi + movl 28(%eax), %esp + // We can't actually change this... + //movl 32(%eax), %cs + //movl 34(%eax), %ds + //movl 36(%eax), %ss + //movl 38(%eax), %es + //movl 40(%eax), %fs + //movl 42(%eax), %gs + + // restore the flags + movl 44(%eax), %ecx + push %ecx + popf + + // ok, now we can restore ecx + movl 8(%eax), %ecx + + // Return! + jmp *48(%eax) + + diff --git a/src/rt/arch/x64/ccall.S b/src/rt/arch/x64/ccall.S new file mode 100644 index 000000000000..44d0c2177360 --- /dev/null +++ b/src/rt/arch/x64/ccall.S @@ -0,0 +1,22 @@ + .text + +// upcall_call_c_stack(void (*fn)(), void *new_esp) +// +// Note that we could use |enter| and |leave| but the manuals tell me they're +// slower. +#if defined(__APPLE__) || defined(_WIN32) +.globl _upcall_call_c_stack +_upcall_call_c_stack: +#else +.globl upcall_call_c_stack +upcall_call_c_stack: +#endif + pushl %ebp + movl %esp,%ebp // save esp + movl 8(%esp),%eax // eax = callee + movl 12(%esp),%esp // switch stack + calll *%eax + movl %ebp,%esp // would like to use "leave" but it's slower + popl %ebp + ret + diff --git a/src/rt/arch/x64/context.cpp b/src/rt/arch/x64/context.cpp new file mode 100644 index 000000000000..f5fa3777eec3 --- /dev/null +++ b/src/rt/arch/x64/context.cpp @@ -0,0 +1,73 @@ +#include "context.h" + +#include "../../rust.h" + +#include +#include +#include + +extern "C" uint32_t CDECL swap_registers(registers_t *oregs, + registers_t *regs) + asm ("swap_registers"); + +context::context() +{ + assert((void*)®s == (void*)this); +} + +void context::swap(context &out) +{ + swap_registers(&out.regs, ®s); +} + +void context::call(void *f, void *arg, void *stack) { + // Get the current context, which we will then modify to call the + // given function. + swap(*this); + + // set up the trampoline frame + uint32_t *sp = (uint32_t *)stack; + + // Shift the stack pointer so the alignment works out right. + sp = align_down(sp) - 3; + *--sp = (uint32_t)arg; + *--sp = 0xdeadbeef; + + regs.esp = (uint32_t)sp; + regs.eip = (uint32_t)f; +} + +#if 0 +// This is some useful code to check how the registers struct got +// layed out in memory. +int main() { + registers_t regs; + + printf("Register offsets\n"); + +#define REG(r) \ + printf(" %6s: +%ld\n", #r, (intptr_t)®s.r - (intptr_t)®s); + + REG(eax); + REG(ebx); + REG(ecx); + REG(edx); + REG(ebp); + REG(esi); + REG(edi); + REG(esp); + + REG(cs); + REG(ds); + REG(ss); + REG(es); + REG(fs); + REG(gs); + + REG(eflags); + + REG(eip); + + return 0; +} +#endif diff --git a/src/rt/arch/x64/context.h b/src/rt/arch/x64/context.h new file mode 100644 index 000000000000..131a994ba451 --- /dev/null +++ b/src/rt/arch/x64/context.h @@ -0,0 +1,62 @@ +// -*- mode: c++ -*- + +#ifndef CONTEXT_H +#define CONTEXT_H + +#include +#include +#include + +#ifdef HAVE_VALGRIND +#include +#endif + +template +T align_down(T sp) +{ + // There is no platform we care about that needs more than a + // 16-byte alignment. + return (T)((uint32_t)sp & ~(16 - 1)); +} + +struct registers_t { + // general purpose registers + uint32_t eax, ebx, ecx, edx, ebp, esi, edi, esp; + + // segment registers + uint16_t cs, ds, ss, es, fs, gs; + + uint32_t eflags; + + uint32_t eip; +}; + +class context { +public: + registers_t regs; + + context(); + + context *next; + + void swap(context &out); + void call(void *f, void *arg, void *sp); + void call(void *f, void *sp); + + // Note that this doesn't actually adjust esp. Instead, we adjust esp when + // we actually do the call. This is needed for exception safety -- if the + // function being called causes the task to fail, then we have to avoid + // leaking space on the C stack. + inline void *alloc_stack(size_t nbytes) { + uint32_t bot = regs.esp; + uint32_t top = align_down(bot - nbytes); + +#ifdef HAVE_VALGRIND + (void)VALGRIND_MAKE_MEM_UNDEFINED(top - 4, bot - top + 4); +#endif + + return reinterpret_cast(top); + } +}; + +#endif