more features
diff --git a/src/libmach/cmdline.c b/src/libmach/cmdline.c
new file mode 100644
index 0000000..3d07535
--- /dev/null
+++ b/src/libmach/cmdline.c
@@ -0,0 +1,187 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+
+Fhdr *symhdr;
+Fhdr *corhdr;
+char *symfil;
+char *corfil;
+int corpid;
+Regs *correg;
+Map *symmap;
+Map *cormap;
+
+int
+alldigs(char *s)
+{
+	while(*s){
+		if(*s<'0' || '9'<*s)
+			return 0;
+		s++;
+	}
+	return 1;
+}
+
+/*
+ * attach to arguments in argc, argv
+ */
+int
+attachargs(int argc, char **argv, int omode)
+{
+	int i;
+	Fhdr *hdr;
+	char *s;
+
+	symhdr = nil;
+	corhdr = nil;
+	symfil = nil;
+	corfil = nil;
+	corpid = 0;
+	correg = nil;
+
+	for(i=0; i<argc; i++){
+		if(alldigs(argv[i])){
+			if(corpid){
+				fprint(2, "already have corpid %d; ignoring corpid %d\n", corpid, argv[i]);
+				continue;
+			}
+			if(corhdr){
+				fprint(2, "already have core %s; ignoring corpid %d\n", corfil, corpid);
+				continue;
+			}
+			corpid = atoi(argv[i]);
+			continue;
+		}
+		if((hdr = crackhdr(argv[i], omode)) == nil){
+			fprint(2, "crackhdr %s: %r\n", argv[i]);
+			continue;
+		}
+		fprint(2, "%s: %s %s %s\n", argv[i], hdr->aname, hdr->mname, hdr->fname);
+		if(hdr->ftype == FCORE){
+			if(corpid){
+				fprint(2, "already have corpid %d; ignoring core %s\n", corpid, argv[i]);
+				uncrackhdr(hdr);
+				continue;
+			}
+			if(corhdr){
+				fprint(2, "already have core %s; ignoring core %s\n", corfil, argv[i]);
+				uncrackhdr(hdr);
+				continue;
+			}
+			corhdr = hdr;
+			corfil = argv[i];
+		}else{
+			if(symhdr){
+				fprint(2, "already have text %s; ignoring text %s\n", symfil, argv[i]);
+				uncrackhdr(hdr);
+				continue;
+			}
+			symhdr = hdr;
+			symfil = argv[i];
+		}
+	}
+
+	if(symhdr == nil){
+		symfil = "a.out";	/* default */
+		if(corpid){	/* try from corpid */
+			if((s = proctextfile(corpid)) != nil){
+				fprint(2, "corpid %d: text %s\n", corpid, s);
+				symfil = s;
+			}
+		}
+		if(corhdr){	/* try from core */
+			if(corhdr->txtfil != nil){
+				fprint(2, "core %s: text %s\n", corfil, corhdr->txtfil);
+				symfil = corhdr->txtfil;
+			}
+		}
+		if((symhdr = crackhdr(symfil, omode)) == nil){
+			fprint(2, "crackhdr %s: %r\n", symfil);
+			symfil = nil;
+		}
+	}
+
+	if(symhdr)
+		syminit(symhdr);
+
+	if(!mach)
+		mach = machcpu;
+
+	/*
+	 * Set up maps
+	 */
+	symmap = allocmap();
+	cormap = allocmap();
+	if(symmap == nil || cormap == nil)
+		sysfatal("allocating maps: %r");
+
+	if(symhdr){
+		if(mapfile(symhdr, 0, symmap, nil) < 0)
+			fprint(2, "mapfile %s: %r\n", symfil);
+		mapfile(symhdr, 0, cormap, nil);
+	}
+
+	if(corpid)	
+		attachproc(corpid);
+
+	if(corhdr)
+		attachcore(corhdr);
+
+	return 0;
+}
+
+static int thecorpid;
+static Fhdr *thecorhdr;
+
+static void
+unattach(void)
+{
+	unmapproc(cormap);
+	unmapfile(corhdr, cormap);
+	free(correg);
+	correg = nil;
+	thecorpid = 0;
+	thecorhdr = nil;
+	corpid = 0;
+	corhdr = nil;
+	corfil = nil;
+}
+
+int
+attachproc(int pid)
+{
+	unattach();
+	if(pid == 0)
+		return 0;
+	if(mapproc(pid, cormap, &correg) < 0){
+		fprint(2, "attachproc %d: %r\n", pid);
+		return -1;
+	}
+	thecorpid = pid;
+	corpid = pid;
+	return 0;
+}
+
+int
+attachcore(Fhdr *hdr)
+{
+	unattach();
+	if(corhdr == nil)
+		return 0;
+	if(mapfile(hdr, 0, cormap, &correg) < 0){
+		fprint(2, "attachcore %s: %r\n", hdr->filename);
+		return -1;
+	}
+	thecorhdr = hdr;
+	corhdr = hdr;
+	corfil = hdr->filename;
+	return 0;
+}
+
+int
+attachdynamic(void)
+{
+	elfdl386mapdl();
+	return 0;
+}
+
diff --git a/src/libmach/elfdl386.c b/src/libmach/elfdl386.c
new file mode 100644
index 0000000..104e5a9
--- /dev/null
+++ b/src/libmach/elfdl386.c
@@ -0,0 +1,133 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "elf.h"
+
+/*
+aggr Linkdebug
+{
+	'X' 0 version;
+	'X' 4 map;
+};
+
+aggr Linkmap
+{
+	'X' 0 addr;
+	'X' 4 name;
+	'X' 8 dynsect;
+	'X' 12 next;
+	'X' 16 prev;
+};
+*/
+enum
+{
+	DT_NULL = 0,
+	DT_NEEDED,
+	DT_PLTRRELSZ,
+	DT_PLTGOT,
+	DT_HASH,
+	DT_STRTAB,
+	DT_SYMTAB,
+	DT_RELA,
+	DT_RELASZ = 8,
+	DT_RELAENT,
+	DT_STSZ,
+	DT_SYMENT,
+	DT_INIT,
+	DT_FINI,
+	DT_SONAME,
+	DT_RPATH,
+	DT_SYMBOLIC = 16,
+	DT_REL,
+	DT_RELSZ,
+	DT_RELENT,
+	DT_PLTREL,
+	DT_DEBUG,
+	DT_TEXTREL,
+	DT_JMPREL,
+};
+
+static int
+getstr(Map *map, ulong addr, char *buf, uint nbuf)
+{
+	int i;
+
+	for(i=0; i<nbuf; i++){
+		if(get1(map, addr+i, buf+i, 1) < 0)
+			return -1;
+		if(buf[i] == 0)
+			return 0;
+	}
+	return -1;	/* no nul */
+}
+
+static ulong
+dyninfo(Fhdr *hdr, int x)
+{
+	u32int addr, u;
+
+	if(hdr == nil || (addr = ((Elf*)hdr->elf)->dynamic) == 0){
+		fprint(2, "no hdr/dynamic %p\n", hdr);
+		return 0;
+	}
+	addr += hdr->base;
+
+	while(addr != 0){
+		if(get4(cormap, addr, &u) < 0)
+			return 0;
+		if(u == x){
+			if(get4(cormap, addr+4, &u) < 0)
+				return 0;
+			return u;
+		}
+		addr += 8;
+	}
+	return 0;
+}
+
+void
+elfdl386mapdl(void)
+{
+	int i;
+	Fhdr *hdr;
+	u32int linkdebug, linkmap, name, addr;
+	char buf[1024];
+
+print("elfdl386mapdl\n");
+	if((linkdebug = dyninfo(symhdr, DT_DEBUG)) == 0){
+		fprint(2, "no dt_debug section\n");
+		return;
+	}
+	if(get4(cormap, linkdebug+4, &linkmap) < 0){
+		fprint(2, "get4 linkdebug+4 (0x%lux) failed\n", linkdebug);
+		return;
+	}
+
+	for(i=0; i<100 && linkmap != 0; i++){
+		if(get4(cormap, linkmap, &addr) < 0
+		|| get4(cormap, linkmap+4, &name) < 0
+		|| get4(cormap, linkmap+12, &linkmap) < 0)
+			break;
+
+		if(name
+		&& getstr(cormap, name, buf, sizeof buf) >= 0
+		&& buf[0]
+		&& access(buf, AEXIST) >= 0){
+			if((hdr = crackhdr(buf, OREAD)) == nil)
+				fprint(2, "crackhdr %s: %r\n", buf);
+			else{
+				fprint(2, "%s: %s %s %s\n", buf, hdr->aname, hdr->mname, hdr->fname);
+				hdr->base = addr;
+				if(mapfile(hdr, addr, symmap, nil) < 0)
+					fprint(2, "mapfile %s: %r\n", buf);
+				if(corhdr){
+					unmapfile(corhdr, cormap);
+					mapfile(hdr, addr, cormap, nil);
+				}
+				if(syminit(hdr) < 0)
+					fprint(2, "syminit %s: %\r", buf);
+			}
+		}	
+	}
+}
+
diff --git a/src/libmach/elfnm.c b/src/libmach/elfnm.c
new file mode 100644
index 0000000..9e6d1ea
--- /dev/null
+++ b/src/libmach/elfnm.c
@@ -0,0 +1,38 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include <elf.h>
+
+void
+usage(void)
+{
+	fprint(2, "usage: elfnm file...\n");
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	ElfSym esym;
+	Fhdr *fp;
+	int i, j;
+
+	ARGBEGIN{
+	default:
+		usage();
+	}ARGEND
+
+	if(argc == 0)
+		usage();
+
+	for(i=0; i<argc; i++){
+		if((fp = crackhdr(argv[i], OREAD)) == nil){
+			fprint(2, "%s: %r\n", argv[i]);
+			continue;
+		}
+		for(j=0; elfsym(fp->elf, j, &esym)>=0; j++)
+			print("%s 0x%lux\n", esym.name, esym.value);
+		uncrackhdr(fp);
+	}
+	exits(0);
+}
diff --git a/src/libmach/pthread.c b/src/libmach/pthread.c
new file mode 100644
index 0000000..46eca4f
--- /dev/null
+++ b/src/libmach/pthread.c
@@ -0,0 +1,49 @@
+#include <u.h>
+#include <thread_db.h>
+#include <sys/ptrace.h>
+#include <errno.h>
+#include <sys/procfs.h>	/* psaddr_t */
+#include <libc.h>
+#include <mach.h>
+
+static char *tderrstr[] =
+{
+[TD_OK]			"no error",
+[TD_ERR]		"some error",
+[TD_NOTHR]		"no matching thread found",
+[TD_NOSV]		"no matching synchronization handle found",
+[TD_NOLWP]		"no matching light-weight process found",
+[TD_BADPH]		"invalid process handle",
+[TD_BADTH]		"invalid thread handle",
+[TD_BADSH]		"invalid synchronization handle",
+[TD_BADTA]		"invalid thread agent",
+[TD_BADKEY]		"invalid key",
+[TD_NOMSG]		"no event available",
+[TD_NOFPREGS]	"no floating-point register content available",
+[TD_NOLIBTHREAD]	"application not linked with thread library",
+[TD_NOEVENT]	"requested event is not supported",
+[TD_NOEVENT]	"requested event is not supported",
+[TD_NOCAPAB]	"capability not available",
+[TD_DBERR]		"internal debug library error",
+[TD_NOAPLIC]	"operation is not applicable",
+[TD_NOTSD]		"no thread-specific data available",
+[TD_MALLOC]		"out of memory",
+[TD_PARTIALREG]	"not entire register set was read or written",
+[TD_NOXREGS]	"X register set not available for given threads",
+[TD_TLSDEFER]	"thread has not yet allocated TLS for given module",
+[TD_VERSION]	"version mismatch twixt libpthread and libthread_db",
+[TD_NOTLS]		"there is no TLS segment in the given module",
+};
+
+static char*
+terr(int e)
+{
+	static char buf[50];
+
+	if(e < 0 || e >= nelem(tderrstr) || tderrstr[e] == nil){
+		snprint(buf, sizeof buf, "thread err %d", e);
+		return buf;
+	}
+	return tderrstr[e];
+}
+
diff --git a/src/libmach/t.c b/src/libmach/t.c
new file mode 100644
index 0000000..d11cb7d
--- /dev/null
+++ b/src/libmach/t.c
@@ -0,0 +1,269 @@
+#include <u.h>
+#include <thread_db.h>
+#include <sys/ptrace.h>
+#include <errno.h>
+#include <sys/procfs.h>	/* psaddr_t */
+#include <libc.h>
+#include <mach.h>
+#include "ureg386.h"
+
+int td_get_allthreads(td_thragent_t *ta, td_thrhandle_t **pall);
+
+static char *tderrstr[] =
+{
+[TD_OK]			"no error",
+[TD_ERR]		"some error",
+[TD_NOTHR]		"no matching thread found",
+[TD_NOSV]		"no matching synchronization handle found",
+[TD_NOLWP]		"no matching light-weight process found",
+[TD_BADPH]		"invalid process handle",
+[TD_BADTH]		"invalid thread handle",
+[TD_BADSH]		"invalid synchronization handle",
+[TD_BADTA]		"invalid thread agent",
+[TD_BADKEY]		"invalid key",
+[TD_NOMSG]		"no event available",
+[TD_NOFPREGS]	"no floating-point register content available",
+[TD_NOLIBTHREAD]	"application not linked with thread library",
+[TD_NOEVENT]	"requested event is not supported",
+[TD_NOEVENT]	"requested event is not supported",
+[TD_NOCAPAB]	"capability not available",
+[TD_DBERR]		"internal debug library error",
+[TD_NOAPLIC]	"operation is not applicable",
+[TD_NOTSD]		"no thread-specific data available",
+[TD_MALLOC]		"out of memory",
+[TD_PARTIALREG]	"not entire register set was read or written",
+[TD_NOXREGS]	"X register set not available for given threads",
+[TD_TLSDEFER]	"thread has not yet allocated TLS for given module",
+[TD_VERSION]	"version mismatch twixt libpthread and libthread_db",
+[TD_NOTLS]		"there is no TLS segment in the given module",
+};
+
+static char*
+terr(int e)
+{
+	static char buf[50];
+
+	if(e < 0 || e >= nelem(tderrstr) || tderrstr[e] == nil){
+		snprint(buf, sizeof buf, "thread err %d", e);
+		return buf;
+	}
+	return tderrstr[e];
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: t pid\n");
+	exits("usage");
+}
+
+#define	STRINGSZ	128
+
+/*
+ *	print the value of dot as file:line
+ */
+void
+printsource(long dot)
+{
+	char str[STRINGSZ];
+
+	if (fileline(dot, str, STRINGSZ) >= 0)
+		print("%s", str);
+}
+
+void
+printlocals(Symbol *fn, Regs *regs)
+{
+	int i;
+	u32int v;
+	Symbol s;
+
+	for (i = 0; indexlsym(fn, i, &s)>=0; i++) {
+		if (s.class != CAUTO)
+			continue;
+		if(lget4(cormap, regs, s.loc, &v) >= 0)
+			print("\t%s.%s/\t%#lux\n", fn->name, s.name, v);
+		else
+			print("\t%s.%s/\t?\n", fn->name, s.name);
+	}
+}
+
+void
+printparams(Symbol *fn, Regs *regs)
+{
+	int i;
+	Symbol s;
+	u32int v;
+	int first = 0;
+	ulong pc, sp, bp;
+
+	if(0) print("pc=%lux sp=%lux bp=%lux ", 
+		(rget(regs, "PC", &pc), pc),
+		(rget(regs, "SP", &sp), sp),
+		(rget(regs, "BP", &bp), bp));
+	for (i = 0; indexlsym(fn, i, &s)>=0; i++) {
+		if (s.class != CPARAM)
+			continue;
+		if (first++)
+			print(", ");
+		if(0) print("(%d.%s.%ux.%x)", s.loc.type, s.loc.reg, s.loc.addr, s.loc.offset);
+		if(lget4(cormap, regs, s.loc, &v) >= 0)
+			print("%s=%#lux", s.name, v);
+		else
+			print("%s=?", s.name);
+	}
+}
+
+/*
+ *	callback on stack trace
+ */
+static int
+xtrace(Map *map, Regs *regs, ulong pc, ulong nextpc, Symbol *sym, int depth)
+{
+	char buf[512];
+
+	USED(map);
+	print("\t");
+	if(sym){
+		print("%s(", sym->name);
+		printparams(sym, regs);
+		print(")+0x%ux ", pc-sym->loc.addr);
+	}else
+		print("%#lux ", pc);
+	printsource(pc);
+
+	print(" called from ");
+	symoff(buf, 512, nextpc, CTEXT);
+	print("%s ", buf);
+/*	printsource(nextpc); */
+	print("\n");
+	if(sym)
+		printlocals(sym, regs);
+	return depth<40;
+}
+
+void
+main(int argc, char **argv)
+{
+	struct ps_prochandle p;
+	prgregset_t regs;
+	int e;
+	td_thragent_t *ta;
+	td_thrhandle_t *ts;
+	td_thrinfo_t info;
+	int i, n;
+	Ureg *u;
+	UregRegs r;
+
+	ARGBEGIN{
+	default:
+		usage();
+	}ARGEND
+
+	attachargs(argc, argv, OREAD);
+	attachdynamic();
+
+	if(!corpid && !corhdr)
+		sysfatal("could not attach to process");
+
+	p.pid = corpid;
+	if((e = td_ta_new(&p, &ta)) != TD_OK)
+		sysfatal("td_ta_new: %s", terr(e));
+	if((e = td_ta_get_nthreads(ta, &n)) != TD_OK)
+		sysfatal("td_ta_get_nthreads: %s", terr(e));
+	print("%d threads\n", n);
+
+	if((n = td_get_allthreads(ta, &ts)) < 0)
+		sysfatal("td_get_allthreads: %r");
+	print("%d threads - regs = %p\n", n, regs);
+	for(i=0; i<n; i++){
+		if((e = td_thr_get_info(&ts[i], &info)) != TD_OK)
+			sysfatal("td_thr_get_info: %s", terr(e));
+		print("%d: startfunc=%lux stkbase=%lux pc=%lux sp=%lux lid=%d\n",
+			i, info.ti_startfunc, info.ti_stkbase, info.ti_pc, info.ti_sp, info.ti_lid);	
+		if((e = td_thr_getgregs(&ts[i], regs)) != TD_OK)
+			sysfatal("td_thr_getregs: %s", terr(e));
+		print("%d: pc=%lux sp=%lux gs=%lux\n", i, regs[12], regs[15], regs[10]);
+		if((u = _linux2ureg386((UregLinux386*)regs)) == nil)
+			sysfatal("%r");
+		r.r.rw = _uregrw;
+		r.ureg = (uchar*)u;
+		stacktrace(cormap, &r.r, xtrace);
+	}
+	exits(0);
+}
+
+typedef struct AllThread AllThread;
+struct AllThread
+{
+	td_thrhandle_t *a;
+	int n;
+	int err;
+};
+
+static int
+thritercb(const td_thrhandle_t *th, void *cb)
+{
+	td_thrhandle_t **p;
+	AllThread *a;
+	int n;
+
+	a = cb;
+	if((a->n&(a->n-1)) == 0){
+		if(a->n == 0)
+			n = 1;
+		else
+			n = a->n<<1;
+		if((p = realloc(a->a, n*sizeof a->a[0])) == 0){
+			a->err = -1;
+			return -1;	/* stop iteration */
+		}
+		a->a = p;
+	}
+	a->a[a->n++] = *th;
+	return 0;
+}
+
+int
+td_get_allthreads(td_thragent_t *ta, td_thrhandle_t **pall)
+{
+	int e;
+	AllThread a;
+
+	a.a = nil;
+	a.n = 0;
+	a.err = 0;
+	if((e = td_ta_thr_iter(ta, thritercb, &a, 
+		TD_THR_ANY_STATE,
+		TD_THR_LOWEST_PRIORITY,
+		TD_SIGNO_MASK,
+		TD_THR_ANY_USER_FLAGS)) != TD_OK){
+		werrstr("%s", terr(e));
+		return -1;
+	}
+
+	if(a.err){
+		free(a.a);
+		return -1;
+	}
+
+	*pall = a.a;
+	return a.n;
+}
+
+/* 
+td_err_e td_ta_map_id2thr(const td_thragent_t *ta_p, thread_t tid,td_thrhandle_t *th_p);
+*/
+
+/*
+int
+threadregs(int tid, Regs **rp)
+{
+	check pid
+	look up tid (td_ta_map_id2thr)
+	create Regs with thr handle inside
+	rw function calls thr_getregs and then
+		pulls out the desired register
+}
+
+*/