aboutsummaryrefslogtreecommitdiff
path: root/docs/history/07-zygote/code/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'docs/history/07-zygote/code/core.c')
-rw-r--r--docs/history/07-zygote/code/core.c276
1 files changed, 276 insertions, 0 deletions
diff --git a/docs/history/07-zygote/code/core.c b/docs/history/07-zygote/code/core.c
new file mode 100644
index 0000000..41930f4
--- /dev/null
+++ b/docs/history/07-zygote/code/core.c
@@ -0,0 +1,276 @@
+/* zygote core: puts the genome (boot loader + assembler, both vm code) into
+ memory and runs it. every block of vm code is translated to arm64 the first
+ time it is reached, straight from live memory. stores that land on translated
+ words throw those translations away, so code that rewrites itself, including
+ the resident assembler, just gets retranslated on the next visit.
+ arm64 only (darwin or linux). ZYTRACE=1 logs translations on stderr. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#ifdef __APPLE__
+#include <pthread.h>
+#include <libkern/OSCacheControl.h>
+#endif
+#include "genome.h"
+
+#ifndef JTYPE
+#define JTYPE char
+#endif
+char Q(void);
+void G(char);
+extern JTYPE J;
+
+#define MW 65536
+#define CODEW (4 << 20) /* 16 MB of code, in instruction words */
+#define MAXB 65536
+#define BLKW 1024 /* longest block, in vm words */
+
+static int m[MW];
+static unsigned char cov[MW]; /* 1 if a live translation covers the word */
+static void *tab[MW]; /* vm address -> translation */
+static struct { int a, b; } bl[MAXB]; /* live translations cover [a,b); a < 0 is dead */
+static int nbl, trace;
+static unsigned *code, *cp, *x_enter, *x_exit, *x_disp, *x_first;
+static long S[9]; /* m, s, r, tab, cov, Q, G, &J, store hook; read by the stubs */
+
+enum { W0 = 0, W1 = 1, X9 = 9, X10, X11, M = 19, SP = 20, RP = 21, TAB = 22, ST = 23 };
+
+static void E(unsigned x) { *cp++ = x; }
+static void li(int d, int v)
+{
+ unsigned u = (unsigned)v;
+ E(0x52800000 | (u & 0xffff) << 5 | d);
+ if (u >> 16) E(0x72a00000 | (u >> 16) << 5 | d);
+}
+static void ldr_post(int t, int n, int i) { E(0xb8400400 | (i & 0x1ff) << 12 | n << 5 | t); }
+static void ldr_pre(int t, int n, int i) { E(0xb8400c00 | (i & 0x1ff) << 12 | n << 5 | t); }
+static void str_pre(int t, int n, int i) { E(0xb8000c00 | (i & 0x1ff) << 12 | n << 5 | t); }
+static void str_post(int t, int n, int i) { E(0xb8000400 | (i & 0x1ff) << 12 | n << 5 | t); }
+static void ldr_u(int t, int n, int o) { E(0xb9400000 | (o / 4) << 10 | n << 5 | t); }
+static void str_u(int t, int n, int o) { E(0xb9000000 | (o / 4) << 10 | n << 5 | t); }
+static void ldrx_u(int t, int n, int o) { E(0xf9400000 | (o / 8) << 10 | n << 5 | t); }
+static void ldr_rr(int t, int n, int r, int ext) { E(0xb8600800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); }
+static void str_rr(int t, int n, int r, int ext) { E(0xb8200800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); }
+static void ldp_post(int t, int u, int n, int i) { E(0x28c00000 | ((i / 4) & 0x7f) << 15 | u << 10 | n << 5 | t); }
+static void op3(unsigned op, int d, int n, int r) { E(op | r << 16 | n << 5 | d); }
+static void addx_ext(int d, int n, int r, int ext) { E(0x8b200000 | r << 16 | ext << 13 | 2 << 10 | n << 5 | d); }
+static void movw(int d, int r) { E(0x2a0003e0 | r << 16 | d); }
+static void blr(int n) { E(0xd63f0000 | n << 5); }
+static void b_to(unsigned *t) { long o = t - cp; E(0x14000000 | (o & 0x3ffffff)); }
+static void cb_fix(unsigned *at, unsigned *to) { *at |= ((to - at) & 0x7ffff) << 5; }
+#define SXTW 6
+#define UXTW 2
+
+static void build_stubs(void)
+{
+ cp = code;
+ x_enter = cp; /* int enter(long *S, void *code) */
+ E(0xa9bc7bfd); /* stp x29, x30, [sp, #-64]! */
+ E(0x910003fd); /* mov x29, sp */
+ E(0xa90153f3); /* stp x19, x20, [sp, #16] */
+ E(0xa9025bf5); /* stp x21, x22, [sp, #32] */
+ E(0xf9001bf7); /* str x23, [sp, #48] */
+ E(0xaa0003f7); /* mov x23, x0 */
+ ldrx_u(M, ST, 0);
+ ldr_u(X9, ST, 8); addx_ext(SP, M, X9, UXTW);
+ ldr_u(X9, ST, 16); addx_ext(RP, M, X9, UXTW);
+ ldrx_u(TAB, ST, 24);
+ E(0xd61f0020); /* br x1 */
+ x_exit = cp; /* w0 = next vm address */
+ op3(0xcb000000, X9, SP, M); E(0xd342fd29); str_u(X9, ST, 8); /* s = (x20 - x19) >> 2 */
+ op3(0xcb000000, X9, RP, M); E(0xd342fd29); str_u(X9, ST, 16);
+ E(0xa94153f3); /* ldp x19, x20, [sp, #16] */
+ E(0xa9425bf5); /* ldp x21, x22, [sp, #32] */
+ E(0xf9401bf7); /* ldr x23, [sp, #48] */
+ E(0xa8c47bfd); /* ldp x29, x30, [sp], #64 */
+ E(0xd65f03c0); /* ret */
+ x_disp = cp; /* w0 = target: chain through tab[] or fall back to c */
+ E(0x7140401f); /* cmp w0, #65536 */
+ E(0x54000002 | ((x_exit - cp) & 0x7ffff) << 5); /* b.hs exit */
+ E(0xf8605ac9); /* ldr x9, [x22, w0, uxtw #3] */
+ E(0xb4000009 | ((x_exit - cp) & 0x7ffff) << 5); /* cbz x9, exit */
+ E(0xd61f0120); /* br x9 */
+ x_first = cp;
+}
+
+static void flush(void)
+{
+ memset(tab, 0, sizeof tab);
+ memset(cov, 0, sizeof cov);
+ nbl = 0;
+ cp = x_first;
+ if (trace) fprintf(stderr, "jit: flushed\n");
+}
+
+static void jitwrite(int on)
+{
+#ifdef __APPLE__
+ pthread_jit_write_protect_np(!on);
+#else
+ (void)on;
+#endif
+}
+
+static void syncicache(unsigned *a, unsigned *b)
+{
+#ifdef __APPLE__
+ sys_icache_invalidate(a, (char *)b - (char *)a);
+#else
+ __builtin___clear_cache((char *)a, (char *)b);
+#endif
+}
+
+/* store hook for words that are covered by a translation */
+static int hook(int a, int v)
+{
+ int j, x, hit = 0;
+ if (m[a] == v) return 0;
+ m[a] = v;
+ for (j = 0; j < nbl; j++)
+ if (bl[j].a >= 0 && bl[j].a <= a && a < bl[j].b) {
+ tab[bl[j].a] = 0;
+ for (x = bl[j].a; x < bl[j].b; x++) cov[x] = 0;
+ if (trace) fprintf(stderr, "jit: dropped %d..%d\n", bl[j].a, bl[j].b - 1);
+ bl[j].a = -1;
+ hit = 1;
+ }
+ for (j = 0; j < nbl; j++)
+ if (bl[j].a >= 0) for (x = bl[j].a; x < bl[j].b; x++) cov[x] = 1;
+ return hit;
+}
+
+static void op(int i, int k) /* everything except control flow */
+{
+ unsigned *a, *b, *c, *z;
+ switch (k) {
+ case 1: ldr_u(X9, SP, 0); ldr_rr(X9, M, X9, SXTW); str_u(X9, SP, 0); return;
+ case 2: /* store: inline unless the target word is translated */
+ ldp_post(X9, X10, SP, 8);
+ E(0x7140413f); /* cmp w9, #65536 */
+ a = cp; E(0x54000002); /* b.hs done */
+ ldrx_u(X11, ST, 32);
+ E(0x3869496b); /* ldrb w11, [x11, w9, uxtw] */
+ b = cp; E(0x3500000b); /* cbnz w11, slow */
+ str_rr(X10, M, X9, UXTW);
+ c = cp; E(0x14000000); /* b done */
+ cb_fix(b, cp); /* slow: hook(a, v) */
+ movw(W0, X9); movw(W1, X10); ldrx_u(X11, ST, 64); blr(X11);
+ z = cp; E(0x34000000); /* cbz w0, done */
+ li(W0, i + 1); b_to(x_exit); /* a translation died: resume at i+1 in c */
+ cb_fix(a, cp); cb_fix(z, cp);
+ *c |= (cp - c) & 0x3ffffff;
+ return;
+ case 3: ldr_u(X9, SP, 0); E(0x9100128a); ldr_rr(X9, X10, X9, SXTW); str_u(X9, SP, 0); return; /* add x10, x20, #4 */
+ case 4: E(0x91001294); return; /* add x20, x20, #4 */
+ case 5: E(0x29402a89); E(0x2900268a); return; /* ldp w9, w10, [x20]; stp w10, w9, [x20] */
+ case 10: ldrx_u(X11, ST, 40); blr(X11); E(0x13001c09); str_pre(X9, SP, -4); return; /* sxtb w9, w0 */
+ case 11:
+ ldrx_u(X9, ST, 56);
+ if (sizeof J == 1) E(0x39400129); else ldr_u(X9, X9, 0); /* ldrb w9, [x9] */
+ str_pre(X9, SP, -4);
+ return;
+ case 12: ldr_post(W0, SP, 4); E(0x13001c00); ldrx_u(X11, ST, 48); blr(X11); return; /* sxtb w0, w0 */
+ }
+ ldr_post(X9, SP, 4);
+ ldr_u(X10, SP, 0);
+ switch (k) {
+ case 13: op3(0x0b000000, X10, X10, X9); break;
+ case 14: op3(0x4b000000, X10, X10, X9); break;
+ case 15: op3(0x1b007c00, X10, X10, X9); break;
+ case 16: op3(0x0a000000, X10, X10, X9); break;
+ case 17: op3(0x2a000000, X10, X10, X9); break;
+ case 18: op3(0x4a000000, X10, X10, X9); break;
+ case 19: op3(0x1ac02800, X10, X10, X9); break;
+ default: E(0x6b09015f); E(0x1a9fa7ea); /* cmp w10, w9; cset w10, lt */
+ }
+ str_u(X10, SP, 0);
+}
+
+static void *translate(int p)
+{
+ unsigned *start;
+ int i = p, n = 0, w, k, t;
+ if (cp - code > CODEW - 65536 || nbl >= MAXB) flush();
+ start = cp;
+ for (;;) {
+ if (i >= MW) { E(0x12800000); b_to(x_exit); break; } /* movn w0, #0: halt */
+ if (n++ >= BLKW) { li(W0, i); b_to(x_disp); break; }
+ w = m[i];
+ cov[i] = 1;
+ if (w & 1 && i + 1 < MW && !(m[i + 1] & 1) && (k = m[i + 1] / 2) >= 6 && k <= 8) {
+ t = (w - 1) / 2; /* literal + call/jmp/jz with a known target */
+ cov[i + 1] = 1;
+ if (k == 8) {
+ unsigned *a;
+ ldr_post(X10, SP, 4);
+ a = cp; E(0x3500000a); /* cbnz w10, fallthrough */
+ li(W0, t); b_to(x_disp);
+ cb_fix(a, cp);
+ i += 2;
+ continue;
+ }
+ li(X10, i + 2);
+ if (k == 6) str_post(X10, RP, 4); else str_u(X10, RP, 0);
+ li(W0, t); b_to(x_disp);
+ i += 2;
+ break;
+ }
+ if (w & 1) { li(X9, (w - 1) / 2); str_pre(X9, SP, -4); i++; continue; }
+ k = w / 2;
+ if (k == 0) { E(0x12800000); b_to(x_exit); i++; break; }
+ if (k < 0 || k == 6 || k == 7) {
+ ldr_post(W0, SP, 4);
+ li(X10, i + 1);
+ if (k == 6) str_post(X10, RP, 4); else str_u(X10, RP, 0);
+ if (k < 0) { li(X11, 7 - k); addx_ext(RP, RP, X11, SXTW); }
+ b_to(x_disp);
+ i++;
+ break;
+ }
+ if (k == 8) { /* jz: taken leaves, not taken keeps going */
+ ldp_post(X9, X10, SP, 8);
+ E(0x3500006a); /* cbnz w10, +3 */
+ movw(W0, X9);
+ b_to(x_disp);
+ i++;
+ continue;
+ }
+ if (k == 9) { ldr_pre(W0, RP, -4); b_to(x_disp); i++; break; }
+ op(i, k);
+ i++;
+ }
+ bl[nbl].a = p;
+ bl[nbl++].b = i;
+ tab[p] = start;
+ syncicache(start, cp);
+ if (trace) fprintf(stderr, "jit: %d..%d -> %ld bytes\n", p, i - 1, (long)((char *)cp - (char *)start));
+ return start;
+}
+
+int main(void)
+{
+ int p = GORG;
+ union { unsigned *u; int (*f)(long *, void *); } enter;
+ trace = getenv("ZYTRACE") != 0;
+#ifdef __APPLE__
+ code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0);
+#else
+ code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+#endif
+ if (code == MAP_FAILED) { perror("mmap"); return 1; }
+ jitwrite(1);
+ build_stubs();
+ jitwrite(0);
+ syncicache(code, cp);
+ memcpy(m + GORG, genome, sizeof genome);
+ S[0] = (long)m; S[1] = 65000; S[2] = 60000; S[3] = (long)tab; S[4] = (long)cov;
+ S[5] = (long)Q; S[6] = (long)G; S[7] = (long)&J; S[8] = (long)hook;
+ enter.u = x_enter;
+ while ((unsigned)p < MW) {
+ void *c = tab[p];
+ if (!c) { jitwrite(1); c = translate(p); jitwrite(0); }
+ p = enter.f(S, c);
+ }
+ return 0;
+}