copy over x86-specific code
This commit is contained in:
parent
5b3bddcd87
commit
6a0d86c754
|
@ -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)
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
#include "context.h"
|
||||
|
||||
#include "../../rust.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
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
|
|
@ -0,0 +1,62 @@
|
|||
// -*- mode: c++ -*-
|
||||
|
||||
#ifndef CONTEXT_H
|
||||
#define CONTEXT_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef HAVE_VALGRIND
|
||||
#include <valgrind/memcheck.h>
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
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<void *>(top);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue