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(®isters);
+ 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);