shithub: sl

Download patch

ref: 253107e36cdb715a21c1fc47cd119702f1a11285
parent: 12c9d2fc728b51aa1eb9a70d0d331eb9464912d9
author: spew <spew@cbza.org>
date: Thu Mar 13 20:40:03 EDT 2025

add initial lsd support

--- a/mkfile
+++ b/mkfile
@@ -15,6 +15,7 @@
 	src/sl.h\
 	src/opcodes.h\
 	src/plan9/platform.h\
+	src/plan9/lsd.h\
 
 OFILES=\
 	3rd/brieflz/brieflz.$O\
@@ -45,6 +46,7 @@
 	src/plan9/sl.boot.$O\
 	src/plan9/popcount`{test -f src/plan9/popcount_$objtype.s && echo -n _$objtype}.$O\
 	src/plan9/sys.$O\
+	src/plan9/lsd.$O\
 	src/print.$O\
 	src/ptrhash.$O\
 	src/random.$O\
--- a/src/dos/sys.c
+++ b/src/dos/sys.c
@@ -41,6 +41,9 @@
 	}
 }
 
+void
+sys_init(void){}
+
 static const u8int boot[] = {
 #include "sl.boot.h"
 };
--- a/src/iostream.c
+++ b/src/iostream.c
@@ -6,7 +6,7 @@
 #include "iostream.h"
 
 static sl_v sl_linesym, sl_blocksym, sl_memorysym, sl_nonesym;
-static sl_type *sl_iostreamtype;
+sl_type *sl_iostreamtype;
 
 static void
 print_iostream(sl_v v, sl_ios *f)
--- a/src/iostream.h
+++ b/src/iostream.h
@@ -2,3 +2,4 @@
 int isiostream(sl_v v) sl_purefn;
 sl_v stream_to_string(sl_v *ps);
 void iostream_init(void);
+extern sl_type *sl_iostreamtype;
--- a/src/macos/sys.c
+++ b/src/macos/sys.c
@@ -71,6 +71,9 @@
 	return -1;
 }
 
+void
+sys_init(void){}
+
 char os_version[10];
 
 static const u8int boot[] = {
--- /dev/null
+++ b/src/plan9/lsd.c
@@ -1,0 +1,375 @@
+#include "sl.h"
+#include <bio.h>
+#include <ctype.h>
+#include <mach.h>
+#include "cvalues.h"
+#include "iostream.h"
+
+static char aout[1024];
+static sl_v lsd_gpregsym, lsd_fpregsym, lsd_regsym, lsd_symsym, lsd_framesym;
+static Map* coremap;
+static Fhdr fhdr;
+static sl_ios *proc_stdin;
+
+void
+lsd_init(void)
+{
+	lsd_gpregsym = csymbol(":gpreg");
+	lsd_fpregsym = csymbol(":fpreg");
+	lsd_symsym = csymbol("sym");
+	lsd_regsym = csymbol("reg");
+	lsd_framesym = csymbol("frame");
+}
+
+static Reglist*
+rname(char *name)
+{
+	Reglist *rp;
+
+	for(rp = mach->reglist; rp->rname; rp++)
+	if(strcmp(name, rp->rname) == 0)
+		return rp;
+	return nil;
+}
+
+static uvlong
+rget(Map *map, char *name)
+{
+	Reglist *rp;
+	ulong x;
+	uvlong v;
+	int r;
+
+	rp = rname(name);
+	if(rp == nil)
+		lerrorf(sl_errio, "invalid register name %s", name);
+
+	switch(rp->rformat){
+	default:
+		r = get4(map, rp->roffs, &x);
+		v = x;
+		break;
+	case 'V': case 'W': case 'Y': case 'Z':
+		r = get8(map, rp->roffs, &v);
+		break;
+	}
+	if(r < 0)
+		lerrorf(sl_errio, "could not get register %s: %r", name);
+	return v;
+}
+
+sl_v
+mk_sym(Symbol *s)
+{
+	sl_v v;
+	Rune r;
+	static char b[2];
+
+	b[0] = s->type;
+	chartorune(&r, b);
+	v = alloc_vector(4, 0);
+	sl_gc_handle(&v);
+	vector_elt(v, 0) = lsd_symsym;
+	vector_elt(v, 1) = string_from_cstr(s->name);
+	vector_elt(v, 2) = mk_rune(r);
+	vector_elt(v, 3) = size_wrap(s->value);
+	sl_free_gc_handles(1);
+	return v;
+}
+
+static sl_v
+symslist(int (*loadsym)(Symbol*, int))
+{
+	Symbol s;
+	sl_v syms;
+	int i;
+
+	syms = sl_nil;
+	sl_gc_handle(&syms);
+	for(i = 0; loadsym(&s, i); i++){
+		if(s.name[0] == '.' || strchr(s.name, '$') != nil)
+			continue;
+		syms = mk_cons(mk_sym(&s), syms);
+	}
+	sl_free_gc_handles(1);
+	return syms;
+}
+
+static sl_v
+localslist(Symbol *fn, uvlong sp)
+{
+	sl_v locals;
+	int i;
+	Symbol s;
+
+	locals = sl_nil;
+	sl_gc_handle(&locals);
+	s = *fn;
+	for(i = 0; localsym(&s, i); i++){
+		if(s.name[0] == '.')
+			continue;
+		switch(s.class){
+		case CAUTO:
+			s.value = sp-s.value;
+			break;
+		case CPARAM:
+			s.value = sp+mach->szaddr+s.value;
+			break;
+		}
+		locals = mk_cons(mk_sym(&s), locals);
+	}
+	sl_free_gc_handles(1);
+	return locals;
+}
+
+static sl_v tracelist;
+
+static void
+trlist(Map *map, uvlong retpc, uvlong sp, Symbol *fn)
+{
+	sl_v  v;
+
+	USED(map);
+	v = alloc_vector(5, 0);
+	sl_gc_handle(&v);
+	vector_elt(v, 0) = lsd_framesym;
+	vector_elt(v, 1) = mk_sym(fn);
+	vector_elt(v, 2) = mk_u64(retpc);
+	vector_elt(v, 3) = mk_u64(sp);
+	vector_elt(v, 4) = localslist(fn, sp);
+	sl_free_gc_handles(1);
+	tracelist = mk_cons(v, tracelist);
+}
+
+static void
+load(void)
+{
+	int fd;
+	long nsym;
+	Map *symmap;
+
+	fd = open(aout, OREAD);
+	if(fd < 0)
+		lerrorf(sl_errio, "could not open \"%s\"", aout);
+	if(!crackhdr(fd, &fhdr)){
+		close(fd);
+		lerrorf(sl_errio, "could not decode file header for \"%s\"", aout);
+	}
+	machbytype(fhdr.type);
+	symmap = loadmap(nil, fd, &fhdr);
+	if(symmap == nil){
+		close(fd);
+		lerrorf(sl_errio, "could not load segments for \"%s\"", aout);
+	}
+	nsym = syminit(fd, &fhdr);
+	if(nsym < 0){
+		close(fd);
+		lerrorf(sl_errio, "could not initialize the symbol table for \"%s\"", aout);
+	}
+	close(fd);
+	ios_printf(ios_stdout, "%s:%s\n", aout, fhdr.name);
+}
+
+static void
+freecore(void)
+{
+	int i;
+
+	if(coremap == nil)
+		return;
+	for(i = 0; i < coremap->nsegs; i++)
+	if(coremap->seg[i].inuse && coremap->seg[i].fd >= 0)
+		close(coremap->seg[i].fd);
+	free(coremap);
+	coremap = nil;
+}
+
+static void
+attach(int pid)
+{
+	static char buf[128];
+	int fd;
+
+	if(pid < 0)
+		return;
+
+	snprint(buf, sizeof(buf), "/proc/%d/mem", pid);
+	fd = open(buf, ORDWR);
+	if(fd < 0)
+		lerrorf(sl_errio, "could not open \"%s\"", buf);
+
+	freecore();
+	coremap = attachproc(pid, 0, fd, &fhdr);
+	if(coremap == nil)
+		lerrorf(sl_errio, "could not make coremap: %r");
+}
+
+static int
+newproc(int *outp)
+{
+	int pid, fd, p[2];
+	char buf[128], *argv[2];
+
+	argv[0] = aout;
+	argv[1] = nil;
+	if(pipe(p) < 0)
+		lerrorf(sl_errio, "could not create a pipe");
+	pid = rfork(RFPROC|RFFDG|RFREND|RFNAMEG|RFNOTEG);
+	switch(pid){
+	case -1:
+		lerrorf(sl_errio, "could not fork");
+	case 0:
+		snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
+		fd = open(buf, ORDWR);
+		if(fd < 0)
+			sysfatal("could not open %s: %r", buf);
+		write(fd, "hang", 4);
+		close(fd);
+		close(p[0]);
+		dup(p[1], 0);
+		close(p[1]);
+		exec(argv[0], argv);
+		sysfatal("could not exec %s: %r", aout);
+	}
+	close(p[1]);
+	attach(pid);
+	*outp = p[0];
+	return pid;
+}
+
+BUILTIN("lsd-cleanup", lsd_cleanup)
+{
+	USED(args);
+	argcount(nargs, 0);
+	freecore();
+	return sl_void;
+}
+
+BUILTIN("lsd-load", lsd_load)
+{
+	Reglist *r;
+	sl_v v, registers;
+	int pid, len;
+
+	pid = -1;
+	argcount(nargs, 1);
+	if(sl_unlikely(!sl_isstring(args[0]) && !sl_isnumber(args[0])))
+		type_error("string|number", args[0]);
+
+	if(isfixnum(args[0])){
+		pid = numval(args[0]);
+		snprint(aout, sizeof(aout), "/proc/%d/text", pid);
+	}else{
+		len = cv_len(ptr(args[0]));
+		if(len+1 > sizeof(aout))
+			lerrorf(sl_errio, "path too long");
+		memmove(aout, cvalue_data(args[0]), len);
+	}
+
+	load();
+	attach(pid);
+
+	registers = sl_nil;
+	v = sl_nil;
+	sl_gc_handle(&registers);
+	sl_gc_handle(&v);
+	for(r = mach->reglist; r->rname != nil; r++){
+		v = alloc_vector(5, 0);
+		vector_elt(v, 0) = lsd_regsym;
+		vector_elt(v, 1) = string_from_cstr(r->rname);
+		vector_elt(v, 2) = r->rflags == RINT ? lsd_gpregsym : lsd_fpregsym;
+		vector_elt(v, 3) = size_wrap(r->roffs);
+		switch(r->rformat){
+		default:
+			vector_elt(v, 4) = sl_u32sym;
+			break;
+		case 'V': case 'W': case 'Y': case 'Z':
+			vector_elt(v, 4) = sl_u64sym;
+			break;
+		}
+		set(symbol(r->rname, 1), v);
+		registers = mk_cons(v, registers);
+	}
+	sl_free_gc_handles(2);
+
+	v = alloc_vector(5, 0);
+	sl_gc_handle(&v);
+	vector_elt(v, 0) = fixnum(pid);
+	vector_elt(v, 1) = registers;
+	vector_elt(v, 2) = cvalue_from_ref(sl_stringtype, machdata->bpinst, machdata->bpsize);
+	vector_elt(v, 3) = symslist(textsym);
+	vector_elt(v, 4) = symslist(globalsym);
+	sl_free_gc_handles(1);
+
+	return v;
+}
+
+BUILTIN("lsd-new", lsd_new)
+{
+	sl_v v;
+	int pid, p;
+
+	USED(args);
+	USED(nargs);
+
+	pid = newproc(&p);
+
+	if(proc_stdin != nil){
+		free(proc_stdin->loc.filename);
+		free(proc_stdin);
+	}
+	proc_stdin = MEM_ALLOC(sizeof(*proc_stdin));
+	ios_fd(proc_stdin, p, 0, 0);
+	proc_stdin->loc.filename = MEM_STRDUP("proc-stdin");
+
+	v = alloc_vector(2, 0);
+	sl_gc_handle(&v);
+	vector_elt(v, 0) = fixnum(pid);
+	vector_elt(v, 1) = cvalue_from_ref(sl_iostreamtype, proc_stdin, sizeof(*proc_stdin));
+	sl_free_gc_handles(1);
+	return v;
+}
+
+BUILTIN("lsd-follow", lsd_follow)
+{
+	int n;
+	uvlong addr, f[10], *p;
+	sl_v foll;
+
+	argcount(nargs, 1);
+	if(sl_unlikely(!sl_isnumber(args[0])))
+		type_error("number", args[0]);
+	addr = tosize(args[0]);
+
+	n = (*machdata->foll)(coremap, addr, rget, f);
+	if(n < 0)
+		lerrorf(sl_errio, "follow(%ux): %r", addr);
+
+	foll = sl_nil;
+	sl_gc_handle(&foll);
+	for(p = f; p < f+n; p++)
+		foll = mk_cons(mk_u64(*p), foll);
+	sl_free_gc_handles(1);
+	return foll;
+}
+
+BUILTIN("lsd-ctrace", lsd_ctrace)
+{
+	sl_v *a;
+	uvlong pc, sp, res;
+
+	argcount(nargs, 3);
+	for(a = args; a < args+3; a++)
+	if(sl_unlikely(!sl_isnumber(*a)))
+		type_error("number", *a);
+
+	pc = tosize(args[0]);
+	sp = tosize(args[1]);
+	res = tosize(args[2]);
+	tracelist = sl_nil;
+	sl_gc_handle(&tracelist);
+	if((*machdata->ctrace)(coremap, pc, sp, res, trlist) <= 0)
+		lerrorf(sl_errio, "no stack frame: %r");
+	sl_free_gc_handles(1);
+	return tracelist;
+}
--- /dev/null
+++ b/src/plan9/lsd.h
@@ -1,0 +1,1 @@
+void lsd_init(void);
--- /dev/null
+++ b/src/plan9/lsd.lsp
@@ -1,0 +1,162 @@
+(defstruct reg name type addr size)
+(defstruct sym name type addr)
+(defstruct global text data)
+(defstruct frame loc retpc sp locals)
+
+(def coref NIL)
+(def textf NIL)
+(def regsf NIL)
+(def fpregsf NIL)
+(def proc-stdin NIL)
+(def pids ())
+(def bptbl (table))
+
+(def (procfile s . flags)
+  (let ((path (string "/proc/" pid "/" s)))
+    (apply file (cons path flags))))
+
+(def (writectl msg)
+  (let ((ctlf (procfile 'ctl :write)))
+    (io-write ctlf msg)
+    (io-close ctlf)))
+
+(def (clearnote)
+  (let ((notef (procfile 'note :read)))
+    (io-readall notef)
+    (io-close notef)))
+
+(def (start) (writectl "start"))
+(def (startstop) (writectl "startstop") (clearnote))
+(def (stop) (writectl "stop") (clearnote))
+
+(def (follow addr) (reverse (lsd-follow addr)))
+
+(def (io-pread f off rest)
+  (io-seek f off)
+  (apply io-read (cons f rest)))
+
+(def (io-pwrite f off rest)
+  (io-seek f off)
+  (apply io-write (cons f rest))
+  (io-flush f))
+
+(def (readcore addr . rest)
+  (unless coref (error "not attached to proc"))
+  (io-pread coref addr rest))
+
+(def (readtext addr . rest)
+  (unless textf (error "not attached to proc"))
+  (io-pread textf addr rest))
+
+(def (writecore addr . rest)
+  (unless coref (error "not attached to proc"))
+  (io-pwrite coref addr rest))
+
+(def (readreg reg)
+  (unless regsf (error "not attached to proc"))
+  (let ((f (case (reg-type reg)
+             ((:gpreg) regsf)
+             ((:fpreg) fpregsf))))
+    (io-pread f (reg-addr reg) (list (reg-size reg)))))
+
+(def (readsym sym . rest)
+  (unless coref (error "not attached to proc"))
+  (apply readcore (cons (sym-addr sym) rest)))
+
+(def (bpset loc)
+  (if (< pid 0) (error "no running process"))
+  (let ((addr (cond ((eq? (typeof loc) 'symbol)
+                          (sym-addr (get (global-text globals) loc)))
+                    ((number? loc) (u64 loc))
+                    (else (error "symbol or number")))))
+    (unless (eq? (status) 'Stopped)
+      (begin
+        (princ "Waiting... " status "\n")
+        (stop)))
+    (if (has? bptbl addr)
+        (error "breakpoint already set at " loc))
+    (put! bptbl addr (readcore addr 'byte (length bpinst)))
+    (writecore addr bpinst)))
+
+(def (detach)
+  (if regsf (io-close regsf))
+  (if fpregsf (io-close fpregsf))
+  (if coref (io-close coref))
+  (if textf (io-close textf))
+  (void))
+
+(def (attach)
+  (detach)
+  (set! regsf (procfile 'regs :read :write))
+  (set! fpregsf (procfile 'fpregs :read :write))
+  (set! coref (procfile 'mem :read :write))
+  (set! textf (procfile 'text :read))
+  (void))
+
+(def (new . args)
+  (let ((v (apply lsd-new args)))
+    (if proc-stdin (io-close proc-stdin))
+    (set! bptbl (table))
+    (set! pid (aref v 0))
+    (set! proc-stdin (aref v 1))
+    (attach)
+    (bpset (car (follow (sym-addr (get (global-text globals) 'main)))))
+    (startstop)
+    (set! pids (cons pid pids))
+    pid))
+
+(def (load a)
+  (let* ((v (lsd-load a))
+         (f (λ (sym tbl) (put! tbl (symbol (sym-name sym)) sym)))
+         (text (foldl f (table) (aref v 3)))
+         (data (foldl f (table) (aref v 4))))
+    (set! pid (aref v 0))
+    (set! registers (aref v 1))
+    (set! bpinst (aref v 2))
+    (set! globals (make-global :text text :data data)))
+  (if (>= pid 0) (attach)))
+
+(def (status)
+  (let ((sf (procfile 'status)))
+    (read sf)
+    (read sf)
+    (let ((stat (read sf)))
+      (io-close sf)
+      stat)))
+
+(def tracers (table
+  "amd64" (λ () (lsd-ctrace (readreg PC) (readreg SP) (u64 0)))
+  "arm64" (λ () (lsd-ctrace (readreg PC) (readreg SP) (readreg R30)))))
+
+(def _stk (get tracers (os-getenv "objtype")))
+
+(def (step)
+  (let* ((addr (readreg PC))
+         (on-bp (has? bptbl addr)))
+    (if on-bp (writecore addr (get bptbl addr)))
+    (let* ((f (follow addr))
+           (o (map (λ (a) (readcore a 'byte (length bpinst))) f)))
+      (for-each (λ (a) (writecore a bpinst)) f)
+      (startstop)
+      (map writecore f o)
+      (if on-bp (writecore addr bpinst))
+      (readreg PC))))
+
+(def (cont)
+  (let ((addr (readreg PC)))
+    (if (has? bptbl addr) (step))
+    (startstop)))
+
+(def (at-exit s)
+  (if proc-stdin (io-close proc-stdin))
+  (detach)
+  (lsd-cleanup)
+  (for-each (λ (p) (princ "echo kill > /proc/" p "/ctl\n")) pids))
+
+(add-exit-hook at-exit)
+
+(let* ((proc (cadr *argv*))
+       (pid (string->number proc)))
+  (if pid (load pid) (load proc)))
+
+(repl)
--- a/src/plan9/sys.c
+++ b/src/plan9/sys.c
@@ -1,6 +1,7 @@
 #include "sl.h"
 #include "timefuncs.h"
 #include <tos.h>
+#include "lsd.h"
 
 #define Nsec 1000000000ULL
 
@@ -99,6 +100,12 @@
 	nulldir(&d);
 	d.length = sz;
 	return dirfwstat(f, &d) > 0 ? 0 : -1;
+}
+
+void
+sys_init(void)
+{
+	lsd_init();
 }
 
 extern uchar bootcode[];
--- a/src/posix/sys.c
+++ b/src/posix/sys.c
@@ -101,6 +101,9 @@
 	}
 }
 
+void
+sys_init(void){}
+
 static const u8int boot[] = {
 #include "sl.boot.h"
 };
--- a/src/sl.c
+++ b/src/sl.c
@@ -1317,6 +1317,7 @@
 	table_init();
 	iostream_init();
 	compress_init();
+	sys_init();
 	return 0;
 }
 
--- a/src/sl.h
+++ b/src/sl.h
@@ -441,4 +441,6 @@
 extern sl_type *sl_bytetype, *sl_runetype;
 extern sl_type *sl_stringtype, *sl_runestringtype;
 
+void sys_init(void);
+
 _Noreturn void slmain(const u8int *boot, int bootsz, int argc, char **argv);