-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathstartup.c
252 lines (227 loc) · 8.69 KB
/
startup.c
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/* This is the runtime that hosts natively compiled scheme code.
* It knows how to call the entry point for the compiled scheme code,
* and how to print the resulting scheme expression.
*/
#include <stdio.h> /* printf, perror */
#include <stdlib.h> /* exit */
#include <sys/types.h> /* mmap */
#define _GNU_SOURCE /* MAP_ANONYMOUS is not POSIX */
#include <sys/mman.h> /* mmap, mprotect */
#include <unistd.h> /* getpagesize */
/* Scheme expressions are encoded in machine words, 64 bit "quad words".
* The lower bits of each word are used to encode type information. */
/* Boolean type */
#define bool_f 0x2F /* 00101111 */
#define bool_t 0x6F /* 01101111 */
#define bool_mask 0xBF /* 10111111 */
/* Fixnum type */
#define fx_mask 0x03 /* 00000011 */
#define fx_tag 0x00 /* 00000000 */
#define fx_shift 2
/* Char type */
#define char_mask 0xFF /* 11111111 */
#define char_tag 0x0F /* 00001111 */
#define char_shift 8
/* Special nil object */
#define nil_tag 0x3F /* 00111111 */
/* Other types */
#define obj_mask 0x07 /* 00000111 */
#define pair_tag 0x01 /* 00000001 */
#define closure_tag 0x02 /* 00000010 */
#define symbol_tag 0x03 /* 00000011 */
#define string_tag 0x06 /* 00000110 */
#define vector_tag 0x05 /* 00000101 */
#define car(pair) (* (ptr *) (pair-1))
#define cdr(pair) (* (ptr *) (pair+7))
/* Context is a structure for storing register values between procedure calls.
* See System V AMD64 ABI for details on registers and convention.
* The first six registers, rdi, rsi, rdx, rcx, r8, r9 are used for passing
* arguments to functions.
* The registers rbx, rsp, rbp, r12, r13, r14, r15 must be preserved by the
* called function, the callee.
* A functions return value is stored in rax and rdx.
*/
typedef struct {
void *rax; /* 0 scratch */
void *rbx; /* 8 preserve */
void *rcx; /* 16 scratch */
void *rdx; /* 24 scratch */
void *rdi; /* 32 scratch */
void *rsi; /* 40 scratch */
void *rsp; /* 48 preserve */
void *rbp; /* 56 preserve */
void *r8; /* 64 scratch */
void *r9; /* 72 scratch */
void *r10; /* 80 scratch */
void *r11; /* 88 scratch */
void *r12; /* 96 preserve */
void *r13; /* 104 preserve */
void *r14; /* 112 preserve */
void *r15; /* 120 preserve */
} context;
/* Friendly names for non-printable ASCII codes */
const char *ascii_table[0x40] = {
"nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
"backspace", "tab", "newline", "vt", "np", "return", "so", "si",
"dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
"can", "em", "sub", "esc", "fs", "gs", "rs", "us",
"space"
};
/* A scheme expression is represented by a 8-byte machine word on x86_64 */
typedef unsigned long ptr;
/* Print a scheme expression using the LISP parenthesized notation.
* x Scheme expression, in machine word tagged form
* depth Current depth when recursing through nested pairs/lists
* is_head Non-zero when printing the car of a pair/list, indicates nested pair
*/
static void print_ptr(ptr x, int depth, int is_head) {
if ((x & fx_mask) == fx_tag) {
/* Print the fixnum value */
printf("%d", ((int)x) >> fx_shift);
} else if (x == bool_f) {
/* Print false */
printf("#f");
} else if (x == bool_t) {
/* Print true */
printf("#t");
} else if (x == nil_tag) {
/* Print nil, the empty list */
printf("()");
} else if ((x & char_mask) == char_tag) {
/* Print a single character, or friendly name for non-printable values */
int c = (int)(x >> char_shift);
if (0 <= c && c <= 0x20) {
printf("#\\%s", ascii_table[c]);
} else {
printf("#\\%c", c);
}
} else if ((x & obj_mask) == pair_tag) {
/* Print dotted pair or list. */
ptr _car = car(x);
ptr _cdr = cdr(x);
/* If this is the first pair in a nested pair or list, open parenthesis. */
if (is_head) printf("(");
/* Print the expression stored in the head of pair. */
print_ptr(_car, depth+1, 1);
if (_cdr != nil_tag) {
if ((_cdr & obj_mask) != pair_tag) {
/* Print the rest of dotted pair. */
printf(" . ");
print_ptr(_cdr, depth+1, 0);
printf(")");
} else if ((_cdr & obj_mask) == pair_tag) {
/* Print a space followed by the rest of the list. */
printf(" ");
print_ptr(_cdr, depth+1, 0);
} else {
printf("ERROR"); /* badly formed expression? */
}
} else {
/* The end of list, print a closing parenthesis. */
printf(")");
}
} else if ((x & obj_mask) == vector_tag) {
/* Print a vector. Print each object in the vector. */
ptr *v = (ptr *) (x & ~obj_mask); /* untag vector pointer */
ptr length = v[0]; /* first quad word is length in bytes of vector, untagged */
int i;
length = length >> 3; /* divide by 8 to get number of objects in vector */
printf("#(");
for(i = 0; i < length; i++) {
print_ptr(v[1+i], depth+1, 0);
if (i < (length-1)) printf(" ");
}
printf(")");
} else if ((x & obj_mask) == string_tag ) {
/* Print a string. */
ptr *v = (ptr *) (x & ~obj_mask); /* untag pointer */
ptr length = v[0]; /* first quad word is length in bytes, untagged */
int i;
length = length >> 3; /* divide by 8 to get number of chars in string */
printf("\"");
for(i = 0; i < length; i++) {
int c = (int)(v[1+i] >> char_shift);
if (c=='"') printf("\\\"");
else if (c=='\\') printf("\\\\");
else printf("%c", c);
}
printf("\"");
} else {
/* The type tag is unrecognised. */
printf("#<unknown 0x%08lx>", x);
}
/* If at the outer most depth when nested, finish printing with a newline. */
if (depth == 0) printf("\n");
}
/* Allocate the specified amount in bytes of memory.
* This also reserves extra space, two pages - the page size specified by the
* Operation System. One above and one below the usable memory.
* These two pages are marked for protection so that accidently writing beyond
* the returned memory will cause an immediate fault - this is to avoid
* situations where the program runs past it's allocated space and carries on
* unaware.
*/
static char * allocate_protected_space(int size) {
int page = getpagesize();
int status;
int aligned_size = ((size + page - 1) / page) * page;
char * p = mmap(0, aligned_size + 2 * page,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE,
0, 0);
if (p == MAP_FAILED) {
perror("mmap"); exit(1);
}
status = mprotect(p, page, PROT_NONE);
if (status != 0) {
perror("mprotect"); exit(1);
}
status = mprotect(p + page + aligned_size, page, PROT_NONE);
if (status != 0) {
perror("mprotect"); exit(1);
}
return (p + page);
}
/* Free the allocated memory. */
static void deallocate_protected_space(char *p, int size) {
int page = getpagesize();
int status;
int aligned_size = ((size + page - 1) / page) * page;
status = munmap(p - page, aligned_size + 2 * page);
if (status != 0) {
perror("munmap"); exit(1);
}
}
/* scheme_entry is exported by the compiled scheme code. */
extern long scheme_entry(context *, char *, char *);
/* The execution entry point for a C program. */
int main(int argc, char *argv[]) {
int stack_size = (16 * 4096); /* Holds 8K cells (each cell is 8 bytes). */
int heap_size = (16 * 4096);
/* By convention the stack grows downwards in memory, that is, pushing
* something on the stack decreases the stack index.
* The base of the stack is in fact the top of the allocated space, and
* the top of the stack is the bottom of the allocated space.
*/
char * stack_top = allocate_protected_space(stack_size);
char * stack_base = stack_top + stack_size;
/* The heap grows upwards in memory, towards the stack. */
char * heap_base = allocate_protected_space(heap_size);
/* Storage for the registers to preserve state between function calls. */
context ctxt;
/* Temporary storage for the result of executing the compiled scheme code. */
ptr expr = nil_tag;
/* Run the compiled scheme code.
*
* The System V (Five) AMD64 calling convention means that the arguments to
* scheme_entry are passed in order on the registers:
* rdi (ctxt), rsi (stack_base), rdx (heap_base)
*/
expr = scheme_entry(&ctxt, stack_base, heap_base);
/* Display the result from running the compiled scheme code. */
print_ptr(expr, 0 /* depth */, (expr & obj_mask) == pair_tag /* is_head */ );
/* Free the allocated stack and heap memory. */
deallocate_protected_space(stack_top, stack_size);
deallocate_protected_space(heap_base, heap_size);
return 0;
}