-
Notifications
You must be signed in to change notification settings - Fork 30
Expand file tree
/
Copy pathx86.c
More file actions
131 lines (112 loc) · 3.57 KB
/
x86.c
File metadata and controls
131 lines (112 loc) · 3.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#define LIBCO_C
#include "libco.h"
#include "settings.h"
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__clang__) || defined(__GNUC__)
#define fastcall __attribute__((fastcall))
#elif defined(_MSC_VER)
#define fastcall __fastcall
#else
#error "libco: please define fastcall macro"
#endif
static thread_local long co_active_buffer[64];
static thread_local cothread_t co_active_handle = 0;
static void (fastcall *co_swap)(cothread_t, cothread_t) = 0;
#ifdef LIBCO_MPROTECT
alignas(4096)
#else
section(text)
#endif
/* ABI: fastcall */
static const unsigned char co_swap_function[4096] = {
0x89, 0x22, /* mov [edx],esp */
0x8b, 0x21, /* mov esp,[ecx] */
0x58, /* pop eax */
0x89, 0x6a, 0x04, /* mov [edx+ 4],ebp */
0x89, 0x72, 0x08, /* mov [edx+ 8],esi */
0x89, 0x7a, 0x0c, /* mov [edx+12],edi */
0x89, 0x5a, 0x10, /* mov [edx+16],ebx */
0x8b, 0x69, 0x04, /* mov ebp,[ecx+ 4] */
0x8b, 0x71, 0x08, /* mov esi,[ecx+ 8] */
0x8b, 0x79, 0x0c, /* mov edi,[ecx+12] */
0x8b, 0x59, 0x10, /* mov ebx,[ecx+16] */
0xff, 0xe0, /* jmp eax */
};
#ifdef _WIN32
/* The macro logic below matches what valgrind.h is able to handle. Although
* there's no Valgrind on Windows, it's possible to run a Windows exe on Linux
* with Wine and Valgrind. See https://wiki.winehq.org/Wine_and_Valgrind. */
#if defined(__GNUC__) || defined(_MSC_VER)
#include "valgrind.h"
#endif
#include <windows.h>
static void co_init(void) {
#ifdef LIBCO_MPROTECT
DWORD old_privileges;
VirtualProtect((void*)co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READ, &old_privileges);
#endif
}
#else
#include "valgrind.h"
#ifdef LIBCO_MPROTECT
#include <unistd.h>
#include <sys/mman.h>
#endif
static void co_init(void) {
#ifdef LIBCO_MPROTECT
unsigned long addr = (unsigned long)co_swap_function;
unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE));
unsigned long size = (addr - base) + sizeof co_swap_function;
mprotect((void*)base, size, PROT_READ | PROT_EXEC);
#endif
}
#endif
static void crash(void) {
LIBCO_ASSERT(0); /* called only if cothread_t entrypoint returns */
}
cothread_t co_active(void) {
if(!co_active_handle) co_active_handle = &co_active_buffer;
return co_active_handle;
}
cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void)) {
cothread_t handle;
if(!co_swap) {
co_init();
co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function;
}
if(!co_active_handle) co_active_handle = &co_active_buffer;
#if defined(__VALGRIND_MAJOR__)
VALGRIND_STACK_REGISTER(memory, (char*)memory + size);
#endif
if((handle = (cothread_t)memory)) {
unsigned long stack_top = (unsigned long)handle + size;
long *p;
stack_top -= 32;
stack_top &= ~((unsigned long) 15);
p = (long*)(stack_top); /* seek to top of stack */
*--p = (long)crash; /* crash if entrypoint returns */
*--p = (long)entrypoint; /* start of function */
*(long*)handle = (long)p; /* stack pointer */
}
return handle;
}
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
void* memory = LIBCO_MALLOC(size);
if(!memory) return (cothread_t)0;
return co_derive(memory, size, entrypoint);
}
void co_delete(cothread_t handle) {
LIBCO_FREE(handle);
}
void co_switch(cothread_t handle) {
register cothread_t co_previous_handle = co_active_handle;
co_swap(co_active_handle = handle, co_previous_handle);
}
int co_serializable(void) {
return 1;
}
#ifdef __cplusplus
}
#endif