shithub: 9ferno

ref: 21336d544ae90e20070fd7c8be67f3f2c9ccbe38
dir: /libinterp/load.c/

View raw version
#include "lib9.h"
#include "isa.h"
#include "interp.h"
#include "raise.h"
#include <kernel.h>

#define DP if(1){}else print
#define DNP if(1){}else print
#define	A(r)	*((Array**)(r))

Module*	modules;
int	dontcompile = 1; /* TODO compiler is broken on amd64 atleast */

static s32
operand(uchar **p)
{
	int c;
	uchar *cp;

	cp = *p;
	c = cp[0];
	switch(c & 0xC0) {
	case 0x00:
		*p = cp+1;
		return c;
	case 0x40:
		*p = cp+1;
		return c|~0x7F;
	case 0x80:
		*p = cp+2;
		if(c & 0x20)
			c |= ~0x3F;
		else
			c &= 0x3F;
		return (c<<8)|cp[1];		
	case 0xC0:
		*p = cp+4;
		if(c & 0x20)
			c |= ~0x3F;
		else
			c &= 0x3F;
		return (c<<24)|(cp[1]<<16)|(cp[2]<<8)|cp[3];		
	}
	return 0;	
}

static s32
disw(uchar **p)
{
	s32 v;
	uchar *c;

	c = *p;
	v  = c[0] << 24;
	v |= c[1] << 16;
	v |= c[2] << 8;
	v |= c[3];
	*p = c + 4;
	return v;
}

double
canontod(u32 v[2])
{
	union { double d; u32 ul[2]; } a;
	/*print("| 0 0x%ux 1 0x%ux |", v[0], v[1]);*/
	a.d = 1.;
	/*print(".");*/
	if(a.ul[0]) {
		a.ul[0] = v[0];
		a.ul[1] = v[1];
	}
	else {
		a.ul[1] = v[0];
		a.ul[0] = v[1];
	}
	/*print(",");*/
	return a.d;
}

Module*
load(char *path)
{
	return readmod(path, nil, 0);
}

int
brpatch(Inst *ip, Module *m)
{
	switch(ip->op) {
	case ICALL:
	case IJMP:
	case IBEQW:
	case IBNEW:
	case IBLTW:
	case IBLEW:
	case IBGTW:
	case IBGEW:
	case IBEQB:
	case IBNEB:
	case IBLTB:
	case IBLEB:
	case IBGTB:
	case IBGEB:
	case IBEQF:
	case IBNEF:
	case IBLTF:
	case IBLEF:
	case IBGTF:
	case IBGEF:
	case IBEQC:
	case IBNEC:
	case IBLTC:
	case IBLEC:
	case IBGTC:
	case IBGEC:
	case IBEQL:
	case IBNEL:
	case IBLTL:
	case IBLEL:
	case IBGTL:
	case IBGEL:
	case ISPAWN:
		if(ip->d.imm < 0 || ip->d.imm >= m->nprog)
			return 0;
		ip->d.imm = (intptr)&m->prog[ip->d.imm];
		break;
	}
	return 1;
}

void
asmstring(intptr offset, uchar* absoluteoffset, String* stored, int len, uchar *s)
{
	uchar *se;
	int c;

	USED(offset);
	DP("\tstring\t@mp+%zd=0x%p,len %d at 0x%p:\"", offset, absoluteoffset, len, stored);
	se = s + len;
	for(; s < se; s++){
		c = *s;
		if(c == '\n')
			DP("\\n");
		else if(c == '\0')
			DP("\\z");
		else if(c == '"')
			DP("\\\"");
		else if(c == '\\')
			DP("\\\\");
		else
			DP("%c", c);
	}
	DP("\"\n");
}

Module*
parsemod(char *path, uchar *code, u32 length, Dir *dir)
{
	Heap *h;
	Inst *ip;
	Type *pt;
	String *s;
	Module *m;
	Array *ary;
	u32 ul[2];
	WORD lo, hi;
	int lsize, id, tnp, tsz, siglen;
	int de, isize, dsize, hsize, dasp;
	uchar *mod, sm, *istream, **isp, *si, *addr, *dastack[DADEPTH], *e, *b;
	Link *l;
	intptr i, n, v, pc, entry, entryt;

	DP("\tsource\t\"%s\"\n", path);
	istream = code;
	isp = &istream;

	m = malloc(sizeof(Module));
	if(m == nil)
		return nil;

	m->dev = dir->dev;
	m->dtype = dir->type;
	m->qid = dir->qid;
	m->mtime = dir->mtime;
	m->origmp = H;
	m->pctab = nil;

	switch(operand(isp)) {
	default:
		kwerrstr("bad magic");
		goto bad;
	case SMAGIC:
		siglen = operand(isp);
		n = length-(*isp-code);
		if(n < 0 || siglen > n){
			kwerrstr("corrupt signature");
			goto bad;
		}
		if(verifysigner(*isp, siglen, *isp+siglen, n-siglen) == 0) {
			kwerrstr("security violation");
			goto bad;
		}
		*isp += siglen;
		break;		
	case XMAGIC:
		if(mustbesigned(path, code, length, dir)){
			kwerrstr("security violation: not signed");
			goto bad;
		}
		break;
	}

	m->rt = operand(isp);
	m->ss = operand(isp);
	isize = operand(isp);
	dsize = operand(isp);
	hsize = operand(isp);
	lsize = operand(isp);
	entry = operand(isp);
	entryt = operand(isp);

	if(isize < 0 || dsize < 0 || hsize < 0 || lsize < 0) {
		kwerrstr("implausible Dis file");
		goto bad;
	}

	DP("parsemod before instructions isize %d dsize %d hsize %d"
		" lsize %d entry 0x%zx entryt 0x%zx sizeof(Inst) %d\n",
		isize, dsize, hsize, lsize, entry, entryt, sizeof(Inst));
	m->nprog = isize;
	m->prog = mallocz(isize*sizeof(Inst), 0);
	if(m->prog == nil) {
		kwerrstr(exNomem);
		goto bad;
	}

	m->ref = 1;
	ip = m->prog;
	for(i = 0; i < isize; i++) {
		ip->op = *istream++;
		ip->add = *istream++;
		ip->reg = 0;
		ip->s.imm = 0;
		ip->d.imm = 0;
		switch(ip->add & ARM) {
		case AXIMM:
		case AXINF:
		case AXINM:
			ip->reg = operand(isp);
		 	break;
		}
		switch(UXSRC(ip->add)) {
		case SRC(AFP):
		case SRC(AMP):	
		case SRC(AIMM):
			ip->s.ind = operand(isp);
			break;
		case SRC(AIND|AFP):
		case SRC(AIND|AMP):
			ip->s.i.f = operand(isp);
			ip->s.i.s = operand(isp);
			break;
		}
		switch(UXDST(ip->add)) {
		case DST(AFP):
		case DST(AMP):	
			ip->d.ind = operand(isp);
			break;
		case DST(AIMM):
			ip->d.ind = operand(isp);
			if(brpatch(ip, m) == 0) {
				kwerrstr("bad branch addr");
				goto bad;
			}
			break;
		case DST(AIND|AFP):
		case DST(AIND|AMP):
			ip->d.i.f = operand(isp);
			ip->d.i.s = operand(isp);
			break;
		}
		if(i % 10 == 0)
			DP("#%p\n", ip);
		DP("	%d %zd %D\n", i, (intptr)ip, ip);
		ip++;		
	}

	DP("\tentry\t0,%d\n",hsize);
	m->ntype = hsize;
	m->type = malloc(hsize*sizeof(Type*));
	if(m->type == nil) {
		kwerrstr(exNomem);
		goto bad;
	}
	for(i = 0; i < hsize; i++) {
		id = operand(isp);
		if(id > hsize) {
			kwerrstr("heap id range");
			goto bad;
		}
		tsz = operand(isp);
		tnp = operand(isp);
		if(tsz < 0 || tnp < 0 || tnp > 128*1024){
			kwerrstr("implausible Dis file");
			goto bad;
		}
		pt = dtype(freeheap, tsz, istream, tnp);
		if(pt == nil) {
			kwerrstr(exNomem);
			goto bad;
		}
		DP("\tdesc\t$%d 0x%p has 0x%p of size %d nptrs %d:\"",
			id, m->type+id, pt, tsz, tnp);
		for(e = istream; e < istream+tnp; e++)
			DP("%.2x", *e);
		DP("\"\n");
		istream += tnp;
		m->type[id] = pt;
	}

	if(dsize != 0) {
		pt = m->type[0];
		if(pt == 0 || pt->size != dsize) {
			kwerrstr("bad desc for mp");
			goto bad;
		}
		h = heapz(pt);
		m->origmp = H2D(uchar*, h);
		DP("\tm->origmp 0x%p belongs to heap at 0x%p, uses type at 0x%p\n",
			m->origmp, h, pt);
	}
	addr = m->origmp;
	dasp = 0;
	DP("\tvar\t@mp, size %d\n", dsize);
	for(;;) {
		sm = *istream++;
		if(sm == 0)
			break;
		n = DLEN(sm);
		if(n == 0)
			n = operand(isp);
		v = operand(isp);
		si = addr + v;
		switch(DTYPE(sm)) {
		default:
			kwerrstr("bad data item");
			goto bad;
		case DEFS:
			s = c2string((char*)istream, n);
			asmstring(v, si, s, n, istream);
			istream += n;
			*(String**)si = s;
			break;
		case DEFB:
			DP("\tbyte\t@mp+%d", v);
			for(i = 0; i < n; i++){
				DP(",%d", *istream & 0xff);
				*si++ = *istream++;
			}
			DP(" n=%d\n", n);
			break;
		case DEFW:
			DP("\tword\t@mp+%d len %d:", v, n);
			for(i = 0; i < n; i++) {
				*(WORD*)si = disw(isp);
				DP(" 0x%zx", *(WORD*)si);
				si += sizeof(WORD);
			}
			DP("\n");
			break;
		case DEFL:
			DP("\tlong\t@mp+%d", v);
			for(i = 0; i < n; i++) {
				hi = disw(isp);
				lo = disw(isp);
				*(LONG*)si = (LONG)hi << 32 | (LONG)(u32)lo;
				DP(",%lld 0x%zx", *(LONG*)si, *(LONG*)si);
				si += sizeof(LONG);
			}
			DP("\n");
			break;
		case DEFF:
			DP("\treal\t@mp+%d", v);
			for(i = 0; i < n; i++) {
				DP(" raw: ");
				for(int j = 0; j<8; j++){
					DP(" 0x%x", ((u8*)isp)[j]);
				}
				ul[0] = disw(isp);
				ul[1] = disw(isp);
				/*print("canontod ul[0] 0x%x ul[1] 0x%x ", ul[0], ul[1]);*/
				*(REAL*)si = canontod(ul);
				/*DP("__");
				DP(",%g", *(REAL*)si);
				DP("--");*/
				si += sizeof(REAL);
			}
			DP("\n");
			break;
		case DEFA:			/* Array */
			DP("\tarray\t@mp+%d", v);
			v = disw(isp);
			if(v < 0 || v > m->ntype) {
				kwerrstr("bad array type");
				goto bad;
			}
			DP(",$%d", v);
			pt = m->type[v];
			v = disw(isp);
			DP(",%d", v);
			h = nheap(sizeof(Array)+(pt->size*v));
			h->t = &Tarray;
			h->t->ref++;
			ary = H2D(Array*, h);
			ary->t = pt;
			ary->len = v;
			ary->root = H;
			ary->data = (uchar*)ary+sizeof(Array);
			memset((void*)ary->data, 0, pt->size*v);
			for(i=(intptr)ary->data;
				i < v;
				i++){
				DP(",%d",*(uchar*)(i+ary));
			}
			DP("\n");
			initarray(pt, ary);
			A(si) = ary;
			break;			
		case DIND:			/* Set index */
			ary = A(si);
			if(ary == H || D2H(ary)->t != &Tarray) {
				kwerrstr("ind not array");
				goto bad;
			}
			DP("\tindir\t@mp+%d", v);
			v = disw(isp);
			if(v > ary->len || v < 0 || dasp >= DADEPTH) {
				kwerrstr("array init range");
				goto bad;
			}
			dastack[dasp++] = addr;
			addr = ary->data+v*ary->t->size;
			DP(",%d,%zd 0x%zx\n",
				v, (intptr)ary->data+v*ary->t->size,
				(intptr)ary->data+v*ary->t->size);
			break;
		case DAPOP:
			if(dasp == 0) {
				kwerrstr("pop range");
				goto bad;
			}
			DP("\tapop\n");
			addr = dastack[--dasp];
			break;
		}
	}
	/*DP("		Initialized origmp\n");
	for(int i = 0; i < m->type[0]->size/(sizeof(intptr)); i++){
		DP("\t\t0x%p\t%zx\n", (intptr*)m->origmp+i, *((intptr*) m->origmp+i));
	}*/
	mod = istream;
	if(memchr(mod, 0, 128) == 0) {
		kwerrstr("bad module name");
		goto bad;
	}
	m->name = strdup((char*)mod);
	if(m->name == nil) {
		kwerrstr(exNomem);
		goto bad;
	}
	DP("\tmodule\t%s\n", m->name);
	while(*istream++)
		;

	l = m->ext = (Link*)malloc((lsize+1)*sizeof(Link));
	if(l == nil){
		kwerrstr(exNomem);
		goto bad;
	}
	for(i = 0; i < lsize; i++, l++) {
		pc = operand(isp);
		de = operand(isp);
		v  = disw(isp);
		pt = nil;
		if(de != -1)
			pt = m->type[de];
		mlink(m, l, istream, v, pc, pt);
		if(de != -1){
			DP("\tlink\tidx %d, type %d size %d np %d ",
				i, de, pt->size, pt->np);
			if(pt->np > 0){
				DP("map");
				for(b = pt->map; b < pt->map+pt->np; b++)
					DP(" %.2x", *b);
				DP(" ");
			}
			DP(", pc %zd, sig 0x%ux,\"%s\"\n",
				pc, v, (char*)istream);
		}else
			DP("\tlink\tidx %d type %d, pc %zd, sig 0x%ux,\"%s\"\n",
				i, de, pc, v, (char*)istream);
		while(*istream++)
			;
	}
	l->name = nil;

	if(m->rt & HASLDT0){
		kwerrstr("obsolete dis");
		goto bad;
	}

	if(m->rt & HASLDT){
		int j, nl;
		Import *i1, **i2;

		nl = operand(isp);
		i2 = m->ldt = (Import**)malloc((nl+1)*sizeof(Import*));
		if(i2 == nil){
			kwerrstr(exNomem);
			goto bad;
		}
		DP("\tldts\t@ldt,%d\n", nl);
		for(i = 0; i < nl; i++, i2++){
			n = operand(isp);
			DP("\text\t@ldts+%d,%d,%zd\n",
				i, n, (intptr)i2-(intptr)m->ldt);
			i1 = *i2 = (Import*)malloc((n+1)*sizeof(Import));
			if(i1 == nil){
				kwerrstr(exNomem);
				goto bad;
			}
			for(j = 0; j < n; j++, i1++){
				i1->sig = disw(isp);
				i1->name = strdup((char*)istream);
				if(i1->name == nil){
					kwerrstr(exNomem);
					goto bad;
				}
				DP("\text\t@ldt+%zd,idx %d, sig 0x%ux,\"%s\"\n",
					(intptr)i1-(intptr)m->ldt,
					j, i1->sig, (char*)istream);
				while(*istream++)
					;
			}
		}
		istream++;
	}

	if(m->rt & HASEXCEPT){
		s32 j, nh, descid;
		Handler *h;
		Except *e;

		nh = operand(isp);
		m->htab = malloc((nh+1)*sizeof(Handler));
		if(m->htab == nil){
			kwerrstr(exNomem);
			goto bad;
		}
		DP("\texceptions\t%d\n", nh);
		h = m->htab;
		for(i = 0; i < nh; i++, h++){
			h->eoff = operand(isp);
			h->pc1 = operand(isp);
			h->pc2 = operand(isp);
			descid = operand(isp);
			if(n != -1)
				h->t = m->type[descid];
			n = operand(isp); /* no of labels */
			h->ne = n>>16;
			n &= 0xffff;
			h->etab = malloc((n+1)*sizeof(Except));
			if(h->etab == nil){
				kwerrstr(exNomem);
				goto bad;
			}
			e = h->etab;
			DP("\texception\t%d: offset %zd pc1 %zd pc2 %zd"
				" desc %d nlab %d ne %zd\n",
				i, h->eoff, h->pc1, h->pc2,
				descid, n, h->ne);
			for(j = 0; j < n; j++, e++){
				e->s = strdup((char*)istream);
				if(e->s == nil){
					kwerrstr(exNomem);
					goto bad;
				}
				while(*istream++)
					;
				e->pc = operand(isp);
				DP("\texctab\t\"%s\", %zd\n", e->s, e->pc);
			}
			e->s = nil;
			e->pc = operand(isp);
			DP("\texctab\t*, %zd\n", e->pc);
		}
		istream++;
	}

	m->entryt = nil;
	m->entry = m->prog;
	if(entry < isize && entryt < hsize) {
		m->entry = &m->prog[entry];
		m->entryt = m->type[entryt];
	}

	if(cflag) {
		if((m->rt&DONTCOMPILE) == 0 && !dontcompile)
			compile(m, isize, nil);
	}
	else
	if(m->rt & MUSTCOMPILE && !dontcompile) {
		if(compile(m, isize, nil) == 0) {
			kwerrstr("compiler required");
			goto bad;
		}
	}

	m->path = strdup(path);
	if(m->path == nil) {
		kwerrstr(exNomem);
		goto bad;
	}
	DP("\tsource\t\"%s\"\n", m->path);
	m->link = modules;
	modules = m;

	return m;
bad:
	destroy(m->origmp);
	freemod(m);
	return nil;
}

Module*
newmod(char *s)
{
	Module *m;

	m = malloc(sizeof(Module));
	if(m == nil)
		error(exNomem);
	m->ref = 1;
	m->path = s;
	m->origmp = H;
	m->name = strdup(s);
	if(m->name == nil) {
		free(m);
		error(exNomem);
	}
	m->link = modules;
	modules = m;
	m->pctab = nil;
	return m;
}

Module*
lookmod(char *s)
{
	Module *m;

	for(m = modules; m != nil; m = m->link)
		if(strcmp(s, m->path) == 0) {
			m->ref++;
			return m;
		}
	return nil;
}

void
freemod(Module *m)
{
	int i;
	Handler *h;
	Except *e;
	Import *i1, **i2;

	if(m->type != nil) {
		for(i = 0; i < m->ntype; i++)
			freetype(m->type[i]);
		free(m->type);
	}
	free(m->name);
	free(m->prog);
	free(m->path);
	free(m->pctab);
	if(m->ldt != nil){
		for(i2 = m->ldt; *i2 != nil; i2++){
			for(i1 = *i2; i1->name != nil; i1++)
				free(i1->name);
			free(*i2);
		}
		free(m->ldt);
	}
	if(m->htab != nil){
		for(h = m->htab; h->etab != nil; h++){
			for(e = h->etab; e->s != nil; e++)
				free(e->s);
			free(h->etab);
		}
		free(m->htab);
	}
	free(m);
}

void
unload(Module *m)
{
	Module **last, *mm;

	m->ref--;
	if(m->ref > 0)
		return;
	if(m->ref == -1)
		abort();

	last = &modules;
	for(mm = modules; mm != nil; mm = mm->link) {
		if(mm == m) {
			*last = m->link;
			break;
		}
		last = &mm->link;
	}

	if(m->rt == DYNMOD)
		freedyncode(m);
	else
		destroy(m->origmp);

	destroylinks(m);

	freemod(m);
}