text: #define	EXTERN
  #include	"l.h"
  #include	<ar.h>
  
  #ifndef	DEFAULT
  #define	DEFAULT	'9'
  #endif
  
  char	*noname		= "<none>";
  char	symname[]	= SYMDEF;
  char	thechar		= '5';
  char	*thestring 	= "arm";
  
  /*
   *	-H1 -T0x10005000 -R4		is aif for risc os
   *	-H2 -T4128 -R4096		is plan9 format
   *	-H3 -T0xF0000020 -R4		is NetBSD format
   *	-H4				is IXP1200 (raw)
   *	-H5 -T0xC0008010 -R1024		is ipaq
   */
  
  static int
  isobjfile(char *f)
  {
  	int n, v;
  	Biobuf *b;
  	char buf1[5], buf2[SARMAG];
  
  	b = Bopen(f, OREAD);
  	if(b == nil)
  		return 0;
  	n = Bread(b, buf1, 5);
  	if(n == 5 && (buf1[2] == 1 && buf1[3] == '<' || buf1[3] == 1 && buf1[4] == '<'))
  		v = 1;	/* good enough for our purposes */
  	else{
  		Bseek(b, 0, 0);
  		n = Bread(b, buf2, SARMAG);
  		v = n == SARMAG && strncmp(buf2, ARMAG, SARMAG) == 0;
  	}
  	Bterm(b);
  	return v;
  }
  
  void
  main(int argc, char *argv[])
  {
  	int c;
  	char *a;
  
  	Binit(&bso, 1, OWRITE);
  	srand(time(0));
  	cout = -1;
  	listinit();
  	outfile = 0;
  	nerrors = 0;
  	curtext = P;
  	HEADTYPE = -1;
  	INITTEXT = -1;
  	INITDAT = -1;
  	INITRND = -1;
  	INITENTRY = 0;
  
  	ARGBEGIN {
  	default:
  		c = ARGC();
  		if(c >= 0 && c < sizeof(debug))
  			debug[c]++;
  		break;
  	case 'o':
  		outfile = ARGF();
  		break;
  	case 'E':
  		a = ARGF();
  		if(a)
  			INITENTRY = a;
  		break;
  	case 'T':
  		a = ARGF();
  		if(a)
  			INITTEXT = atolwhex(a);
  		break;
  	case 'D':
  		a = ARGF();
  		if(a)
  			INITDAT = atolwhex(a);
  		break;
  	case 'R':
  		a = ARGF();
  		if(a)
  			INITRND = atolwhex(a);
  		break;
  	case 'H':
  		a = ARGF();
  		if(a)
  			HEADTYPE = atolwhex(a);
  		/* do something about setting INITTEXT */
  		break;
  	case 'x':	/* produce export table */
  		doexp = 1;
  		if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1]))
  			readundefs(ARGF(), SEXPORT);
  		break;
  	case 'u':	/* produce dynamically loadable module */
  		dlm = 1;
  		debug['l']++;
  		if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1]))
  			readundefs(ARGF(), SIMPORT);
  		break;
  	} ARGEND
  
  	USED(argc);
  
  	if(*argv == 0) {
  		diag("usage: 5l [-options] objects");
  		errorexit();
  	}
  	if(!debug['9'] && !debug['U'] && !debug['B'])
  		debug[DEFAULT] = 1;
  	if(HEADTYPE == -1) {
  		if(debug['U'])
  			HEADTYPE = 0;
  		if(debug['B'])
  			HEADTYPE = 1;
  		if(debug['9'])
  			HEADTYPE = 2;
  	}
  	switch(HEADTYPE) {
  	default:
  		diag("unknown -H option");
  		errorexit();
  	case 0:	/* no header */
  		HEADR = 0L;
  		if(INITTEXT == -1)
  			INITTEXT = 0;
  		if(INITDAT == -1)
  			INITDAT = 0;
  		if(INITRND == -1)
  			INITRND = 4;
  		break;
  	case 1:	/* aif for risc os */
  		HEADR = 128L;
  		if(INITTEXT == -1)
  			INITTEXT = 0x10005000 + HEADR;
  		if(INITDAT == -1)
  			INITDAT = 0;
  		if(INITRND == -1)
  			INITRND = 4;
  		break;
  	case 2:	/* plan 9 */
  		HEADR = 32L;
  		if(INITTEXT == -1)
  			INITTEXT = 4128;
  		if(INITDAT == -1)
  			INITDAT = 0;
  		if(INITRND == -1)
  			INITRND = 4096;
  		break;
  	case 3:	/* boot for NetBSD */
  		HEADR = 32L;
  		if(INITTEXT == -1)
  			INITTEXT = 0xF0000020L;
  		if(INITDAT == -1)
  			INITDAT = 0;
  		if(INITRND == -1)
  			INITRND = 4096;
  		break;
  	case 4: /* boot for IXP1200 */
  		HEADR = 0L;
  		if(INITTEXT == -1)
  			INITTEXT = 0x0;
  		if(INITDAT == -1)
  			INITDAT = 0;
  		if(INITRND == -1)
  			INITRND = 4;
  		break;
  	case 5: /* boot for ipaq */
  		HEADR = 16L;
  		if(INITTEXT == -1)
  			INITTEXT = 0xC0008010;
  		if(INITDAT == -1)
  			INITDAT = 0;
  		if(INITRND == -1)
  			INITRND = 1024;
  		break;
  	}
  	if(INITDAT != 0 && INITRND != 0)
  		print("warning: -D0x%lux is ignored because of -R0x%lux\n",
  			INITDAT, INITRND);
  	if(debug['v'])
  		Bprint(&bso, "HEADER = -H0x%d -T0x%lux -D0x%lux -R0x%lux\n",
  			HEADTYPE, INITTEXT, INITDAT, INITRND);
  	Bflush(&bso);
  	zprg.as = AGOK;
  	zprg.scond = 14;
  	zprg.reg = NREG;
  	zprg.from.name = D_NONE;
  	zprg.from.type = D_NONE;
  	zprg.from.reg = NREG;
  	zprg.to = zprg.from;
  	buildop();
  	thumbbuildop();	// could build on demand
  	histgen = 0;
  	textp = P;
  	datap = P;
  	pc = 0;
  	dtype = 4;
  	if(outfile == 0)
  		outfile = "5.out";
  	cout = create(outfile, 1, 0775);
  	if(cout < 0) {
  		diag("%s: cannot create", outfile);
  		errorexit();
  	}
  	nuxiinit();
  
  	version = 0;
  	cbp = buf.cbuf;
  	cbc = sizeof(buf.cbuf);
  	firstp = prg();
  	lastp = firstp;
  
  	if(INITENTRY == 0) {
  		INITENTRY = "_main";
  		if(debug['p'])
  			INITENTRY = "_mainp";
  		if(!debug['l'])
  			lookup(INITENTRY, 0)->type = SXREF;
  	} else
  		lookup(INITENTRY, 0)->type = SXREF;
  
  	while(*argv)
  		objfile(*argv++);
  	if(!debug['l'])
  		loadlib();
  	firstp = firstp->link;
  	if(firstp == P)
  		goto out;
  	if(doexp || dlm){
  		EXPTAB = "_exporttab";
  		zerosig(EXPTAB);
  		zerosig("etext");
  		zerosig("edata");
  		zerosig("end");
  		if(dlm){
  			initdiv();
  			import();
  			HEADTYPE = 2;
  			INITTEXT = INITDAT = 0;
  			INITRND = 8;
  			INITENTRY = EXPTAB;
  		}
  		else
  			divsig();
  		export();
  	}
  	patch();
  	if(debug['p'])
  		if(debug['1'])
  			doprof1();
  		else
  			doprof2();
  	if(debug['u'])
  		reachable();
  	dodata();
  	if(seenthumb && debug['f'])
  		fnptrs();
  	follow();
  	if(firstp == P)
  		goto out;
  	noops();
  	span();
  	asmb();
  	undef();
  
  out:
  	if(debug['c']){
  		thumbcount();
  		print("ARM size = %d\n", armsize);
  	}
  	if(debug['v']) {
  		Bprint(&bso, "%5.2f cpu time\n", cputime());
  		Bprint(&bso, "%ld memory used\n", thunk);
  		Bprint(&bso, "%d sizeof adr\n", sizeof(Adr));
  		Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
  	}
  	Bflush(&bso);
  	errorexit();
  }
  
  void
  loadlib(void)
  {
  	int i;
  	long h;
  	Sym *s;
  
  loop:
  	xrefresolv = 0;
  	for(i=0; i<libraryp; i++) {
  		if(debug['v'])
  			Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i], libraryobj[i]);
  		objfile(library[i]);
  	}
  	if(xrefresolv)
  	for(h=0; h<nelem(hash); h++)
  	for(s = hash[h]; s != S; s = s->link)
  		if(s->type == SXREF)
  			goto loop;
  }
  
  void
  errorexit(void)
  {
  
  	Bflush(&bso);
  	if(nerrors) {
  		if(cout >= 0)
  			remove(outfile);
  		exits("error");
  	}
  	exits(0);
  }
  
  void
  objfile(char *file)
  {
  	long off, esym, cnt, l;
  	int f, work;
  	Sym *s;
  	char magbuf[SARMAG];
  	char name[100], pname[150];
  	struct ar_hdr arhdr;
  	char *e, *start, *stop;
  
  	if(file[0] == '-' && file[1] == 'l') {
  		if(debug['9'])
  			sprint(name, "/%s/lib/lib", thestring);
  		else
  			sprint(name, "/usr/%clib/lib", thechar);
  		strcat(name, file+2);
  		strcat(name, ".a");
  		file = name;
  	}
  	if(debug['v'])
  		Bprint(&bso, "%5.2f ldobj: %s\n", cputime(), file);
  	Bflush(&bso);
  	f = open(file, 0);
  	if(f < 0) {
  		diag("cannot open file: %s", file);
  		errorexit();
  	}
  	l = read(f, magbuf, SARMAG);
  	if(l != SARMAG || strncmp(magbuf, ARMAG, SARMAG)){
  		/* load it as a regular file */
  		l = seek(f, 0L, 2);
  		seek(f, 0L, 0);
  		ldobj(f, l, file);
  		close(f);
  		return;
  	}
  
  	if(debug['v'])
  		Bprint(&bso, "%5.2f ldlib: %s\n", cputime(), file);
  	l = read(f, &arhdr, SAR_HDR);
  	if(l != SAR_HDR) {
  		diag("%s: short read on archive file symbol header", file);
  		goto out;
  	}
  	if(strncmp(arhdr.name, symname, strlen(symname))) {
  		diag("%s: first entry not symbol header", file);
  		goto out;
  	}
  
  	esym = SARMAG + SAR_HDR + atolwhex(arhdr.size);
  	off = SARMAG + SAR_HDR;
  
  	/*
  	 * just bang the whole symbol file into memory
  	 */
  	seek(f, off, 0);
  	cnt = esym - off;
  	start = malloc(cnt + 10);
  	cnt = read(f, start, cnt);
  	if(cnt <= 0){
  		close(f);
  		return;
  	}
  	stop = &start[cnt];
  	memset(stop, 0, 10);
  
  	work = 1;
  	while(work){
  		if(debug['v'])
  			Bprint(&bso, "%5.2f library pass: %s\n", cputime(), file);
  		Bflush(&bso);
  		work = 0;
  		for(e = start; e < stop; e = strchr(e+5, 0) + 1) {
  			s = lookup(e+5, 0);
  			if(s->type != SXREF)
  				continue;
  			sprint(pname, "%s(%s)", file, s->name);
  			if(debug['v'])
  				Bprint(&bso, "%5.2f library: %s\n", cputime(), pname);
  			Bflush(&bso);
  			l = e[1] & 0xff;
  			l |= (e[2] & 0xff) << 8;
  			l |= (e[3] & 0xff) << 16;
  			l |= (e[4] & 0xff) << 24;
  			seek(f, l, 0);
  			l = read(f, &arhdr, SAR_HDR);
  			if(l != SAR_HDR)
  				goto bad;
  			if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag)))
  				goto bad;
  			l = atolwhex(arhdr.size);
  			ldobj(f, l, pname);
  			if(s->type == SXREF) {
  				diag("%s: failed to load: %s", file, s->name);
  				errorexit();
  			}
  			work = 1;
  			xrefresolv = 1;
  		}
  	}
  	return;
  
  bad:
  	diag("%s: bad or out of date archive", file);
  out:
  	close(f);
  }
  
  int
  zaddr(uchar *p, Adr *a, Sym *h[])
  {
  	int i, c;
  	long l;
  	Sym *s;
  	Auto *u;
  
  	c = p[2];
  	if(c < 0 || c > NSYM){
  		print("sym out of range: %d\n", c);
  		p[0] = ALAST+1;
  		return 0;
  	}
  	a->type = p[0];
  	a->reg = p[1];
  	a->sym = h[c];
  	a->name = p[3];
  	c = 4;
  
  	if(a->reg < 0 || a->reg > NREG) {
  		print("register out of range %d\n", a->reg);
  		p[0] = ALAST+1;
  		return 0;	/*  force real diagnostic */
  	}
  
  	if(a->type == D_CONST || a->type == D_OCONST) {
  		if(a->name == D_EXTERN || a->name == D_STATIC) {
  			s = a->sym;
  			if(s != S && (s->type == STEXT || s->type == SLEAF || s->type == SCONST || s->type == SXREF)) {
  				if(0 && !s->fnptr && s->name[0] != '.')
  					print("%s used as function pointer\n", s->name);
  				s->fnptr = 1;	// over the top cos of SXREF
  			}
  		}
  	}
  
  	switch(a->type) {
  	default:
  		print("unknown type %d\n", a->type);
  		p[0] = ALAST+1;
  		return 0;	/*  force real diagnostic */
  
  	case D_NONE:
  	case D_REG:
  	case D_FREG:
  	case D_PSR:
  	case D_FPCR:
  		break;
  
  	case D_REGREG:
  		a->offset = p[4];
  		c++;
  		break;
  
  	case D_BRANCH:
  	case D_OREG:
  	case D_CONST:
  	case D_OCONST:
  	case D_SHIFT:
  		a->offset = p[4] | (p[5]<<8) |
  			(p[6]<<16) | (p[7]<<24);
  		c += 4;
  		break;
  
  	case D_SCONST:
  		while(nhunk < NSNAME)
  			gethunk();
  		a->sval = (char*)hunk;
  		nhunk -= NSNAME;
  		hunk += NSNAME;
  
  		memmove(a->sval, p+4, NSNAME);
  		c += NSNAME;
  		break;
  
  	case D_FCONST:
  		while(nhunk < sizeof(Ieee))
  			gethunk();
  		a->ieee = (Ieee*)hunk;
  		nhunk -= NSNAME;
  		hunk += NSNAME;
  
  		a->ieee->l = p[4] | (p[5]<<8) |
  			(p[6]<<16) | (p[7]<<24);
  		a->ieee->h = p[8] | (p[9]<<8) |
  			(p[10]<<16) | (p[11]<<24);
  		c += 8;
  		break;
  	}
  	s = a->sym;
  	if(s == S)
  		return c;
  	i = a->name;
  	if(i != D_AUTO && i != D_PARAM)
  		return c;
  
  	l = a->offset;
  	for(u=curauto; u; u=u->link)
  		if(u->asym == s)
  		if(u->type == i) {
  			if(u->aoffset > l)
  				u->aoffset = l;
  			return c;
  		}
  
  	while(nhunk < sizeof(Auto))
  		gethunk();
  	u = (Auto*)hunk;
  	nhunk -= sizeof(Auto);
  	hunk += sizeof(Auto);
  
  	u->link = curauto;
  	curauto = u;
  	u->asym = s;
  	u->aoffset = l;
  	u->type = i;
  	return c;
  }
  
  void
  addlib(char *obj)
  {
  	char name[1024], comp[256], *p;
  	int i;
  
  	if(histfrogp <= 0)
  		return;
  
  	if(histfrog[0]->name[1] == '/') {
  		sprint(name, "");
  		i = 1;
  	} else
  	if(histfrog[0]->name[1] == '.') {
  		sprint(name, ".");
  		i = 0;
  	} else {
  		if(debug['9'])
  			sprint(name, "/%s/lib", thestring);
  		else
  			sprint(name, "/usr/%clib", thechar);
  		i = 0;
  	}
  
  	for(; i<histfrogp; i++) {
  		snprint(comp, sizeof comp, histfrog[i]->name+1);
  		for(;;) {
  			p = strstr(comp, "$O");
  			if(p == 0)
  				break;
  			memmove(p+1, p+2, strlen(p+2)+1);
  			p[0] = thechar;
  		}
  		for(;;) {
  			p = strstr(comp, "$M");
  			if(p == 0)
  				break;
  			if(strlen(comp)+strlen(thestring)-2+1 >= sizeof comp) {
  				diag("library component too long");
  				return;
  			}
  			memmove(p+strlen(thestring), p+2, strlen(p+2)+1);
  			memmove(p, thestring, strlen(thestring));
  		}
  		if(strlen(name) + strlen(comp) + 3 >= sizeof(name)) {
  			diag("library component too long");
  			return;
  		}
  		strcat(name, "/");
  		strcat(name, comp);
  	}
  	for(i=0; i<libraryp; i++)
  		if(strcmp(name, library[i]) == 0)
  			return;
  	if(libraryp == nelem(library)){
  		diag("too many autolibs; skipping %s", name);
  		return;
  	}
  
  	p = malloc(strlen(name) + 1);
  	strcpy(p, name);
  	library[libraryp] = p;
  	p = malloc(strlen(obj) + 1);
  	strcpy(p, obj);
  	libraryobj[libraryp] = p;
  	libraryp++;
  }
  
  void
  addhist(long line, int type)
  {
  	Auto *u;
  	Sym *s;
  	int i, j, k;
  
  	u = malloc(sizeof(Auto));
  	s = malloc(sizeof(Sym));
  	s->name = malloc(2*(histfrogp+1) + 1);
  
  	u->asym = s;
  	u->type = type;
  	u->aoffset = line;
  	u->link = curhist;
  	curhist = u;
  
  	j = 1;
  	for(i=0; i<histfrogp; i++) {
  		k = histfrog[i]->value;
  		s->name[j+0] = k>>8;
  		s->name[j+1] = k;
  		j += 2;
  	}
  }
  
  void
  histtoauto(void)
  {
  	Auto *l;
  
  	while(l = curhist) {
  		curhist = l->link;
  		l->link = curauto;
  		curauto = l;
  	}
  }
  
  void
  collapsefrog(Sym *s)
  {
  	int i;
  
  	/*
  	 * bad encoding of path components only allows
  	 * MAXHIST components. if there is an overflow,
  	 * first try to collapse xxx/..
  	 */
  	for(i=1; i<histfrogp; i++)
  		if(strcmp(histfrog[i]->name+1, "..") == 0) {
  			memmove(histfrog+i-1, histfrog+i+1,
  				(histfrogp-i-1)*sizeof(histfrog[0]));
  			histfrogp--;
  			goto out;
  		}
  
  	/*
  	 * next try to collapse .
  	 */
  	for(i=0; i<histfrogp; i++)
  		if(strcmp(histfrog[i]->name+1, ".") == 0) {
  			memmove(histfrog+i, histfrog+i+1,
  				(histfrogp-i-1)*sizeof(histfrog[0]));
  			goto out;
  		}
  
  	/*
  	 * last chance, just truncate from front
  	 */
  	memmove(histfrog+0, histfrog+1,
  		(histfrogp-1)*sizeof(histfrog[0]));
  
  out:
  	histfrog[histfrogp-1] = s;
  }
  
  void
  nopout(Prog *p)
  {
  	p->as = ANOP;
  	p->from.type = D_NONE;
  	p->to.type = D_NONE;
  }
  
  uchar*
  readsome(int f, uchar *buf, uchar *good, uchar *stop, int max)
  {
  	int n;
  
  	n = stop - good;
  	memmove(buf, good, stop - good);
  	stop = buf + n;
  	n = MAXIO - n;
  	if(n > max)
  		n = max;
  	n = read(f, stop, n);
  	if(n <= 0)
  		return 0;
  	return stop + n;
  }
  
  static void puntfp(Prog *);
  
  void
  ldobj(int f, long c, char *pn)
  {
  	long ipc;
  	Prog *p, *t;
  	uchar *bloc, *bsize, *stop;
  	Sym *h[NSYM], *s, *di;
  	int v, o, r, skip;
  	ulong sig;
  	static int files;
  	static char **filen;
  	char **nfilen;
  
  	if((files&15) == 0){
  		nfilen = malloc((files+16)*sizeof(char*));
  		memmove(nfilen, filen, files*sizeof(char*));
  		free(filen);
  		filen = nfilen;
  	}
  	filen[files++] = strdup(pn);
  
  	bsize = buf.xbuf;
  	bloc = buf.xbuf;
  	di = S;
  
  newloop:
  	memset(h, 0, sizeof(h));
  	version++;
  	histfrogp = 0;
  	ipc = pc;
  	skip = 0;
  
  loop:
  	if(c <= 0)
  		goto eof;
  	r = bsize - bloc;
  	if(r < 100 && r < c) {		/* enough for largest prog */
  		bsize = readsome(f, buf.xbuf, bloc, bsize, c);
  		if(bsize == 0)
  			goto eof;
  		bloc = buf.xbuf;
  		goto loop;
  	}
  	o = bloc[0];		/* as */
  	if(o <= AXXX || o >= ALAST) {
  		diag("%s: line %ld: opcode out of range %d", pn, pc-ipc, o);
  		print("	probably not a .5 file\n");
  		errorexit();
  	}
  	if(o == ANAME || o == ASIGNAME) {
  		sig = 0;
  		if(o == ASIGNAME){
  			sig = bloc[1] | (bloc[2]<<8) | (bloc[3]<<16) | (bloc[4]<<24);
  			bloc += 4;
  			c -= 4;
  		}
  		stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
  		if(stop == 0){
  			bsize = readsome(f, buf.xbuf, bloc, bsize, c);
  			if(bsize == 0)
  				goto eof;
  			bloc = buf.xbuf;
  			stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
  			if(stop == 0){
  				fprint(2, "%s: name too long\n", pn);
  				errorexit();
  			}
  		}
  		v = bloc[1];	/* type */
  		o = bloc[2];	/* sym */
  		bloc += 3;
  		c -= 3;
  
  		r = 0;
  		if(v == D_STATIC)
  			r = version;
  		s = lookup((char*)bloc, r);
  		c -= &stop[1] - bloc;
  		bloc = stop + 1;
  
  		if(sig != 0){
  			if(s->sig != 0 && s->sig != sig)
  				diag("incompatible type signatures %lux(%s) and %lux(%s) for %s", s->sig, filen[s->file], sig, pn, s->name);
  			s->sig = sig;
  			s->file = files-1;
  		}
  
  		if(debug['W'])
  			print("	ANAME	%s\n", s->name);
  		h[o] = s;
  		if((v == D_EXTERN || v == D_STATIC) && s->type == 0)
  			s->type = SXREF;
  		if(v == D_FILE) {
  			if(s->type != SFILE) {
  				histgen++;
  				s->type = SFILE;
  				s->value = histgen;
  			}
  			if(histfrogp < MAXHIST) {
  				histfrog[histfrogp] = s;
  				histfrogp++;
  			} else
  				collapsefrog(s);
  		}
  		goto loop;
  	}
  
  	if(nhunk < sizeof(Prog))
  		gethunk();
  	p = (Prog*)hunk;
  	nhunk -= sizeof(Prog);
  	hunk += sizeof(Prog);
  
  	p->as = o;
  	p->scond = bloc[1];
  	p->reg = bloc[2];
  	p->line = bloc[3] | (bloc[4]<<8) | (bloc[5]<<16) | (bloc[6]<<24);
  
  	r = zaddr(bloc+7, &p->from, h) + 7;
  	r += zaddr(bloc+r, &p->to, h);
  	bloc += r;
  	c -= r;
  
  	if(p->reg > NREG)
  		diag("register out of range %d", p->reg);
  
  	p->link = P;
  	p->cond = P;
  
  	if(debug['W'])
  		print("%P\n", p);
  
  	switch(o) {
  	case AHISTORY:
  		if(p->to.offset == -1) {
  			addlib(pn);
  			histfrogp = 0;
  			goto loop;
  		}
  		addhist(p->line, D_FILE);		/* 'z' */
  		if(p->to.offset)
  			addhist(p->to.offset, D_FILE1);	/* 'Z' */
  		histfrogp = 0;
  		goto loop;
  
  	case AEND:
  		histtoauto();
  		if(curtext != P)
  			curtext->to.autom = curauto;
  		curauto = 0;
  		curtext = P;
  		if(c)
  			goto newloop;
  		return;
  
  	case AGLOBL:
  		s = p->from.sym;
  		if(s == S) {
  			diag("GLOBL must have a name\n%P", p);
  			errorexit();
  		}
  		if(s->type == 0 || s->type == SXREF) {
  			s->type = SBSS;
  			s->value = 0;
  		}
  		if(s->type != SBSS) {
  			diag("redefinition: %s\n%P", s->name, p);
  			s->type = SBSS;
  			s->value = 0;
  		}
  		if(p->to.offset > s->value)
  			s->value = p->to.offset;
  		break;
  
  	case ADYNT:
  		if(p->to.sym == S) {
  			diag("DYNT without a sym\n%P", p);
  			break;
  		}
  		di = p->to.sym;
  		p->reg = 4;
  		if(di->type == SXREF) {
  			if(debug['z'])
  				Bprint(&bso, "%P set to %d\n", p, dtype);
  			di->type = SCONST;
  			di->value = dtype;
  			dtype += 4;
  		}
  		if(p->from.sym == S)
  			break;
  
  		p->from.offset = di->value;
  		p->from.sym->type = SDATA;
  		if(curtext == P) {
  			diag("DYNT not in text: %P", p);
  			break;
  		}
  		p->to.sym = curtext->from.sym;
  		p->to.type = D_CONST;
  		p->link = datap;
  		datap = p;
  		break;
  
  	case AINIT:
  		if(p->from.sym == S) {
  			diag("INIT without a sym\n%P", p);
  			break;
  		}
  		if(di == S) {
  			diag("INIT without previous DYNT\n%P", p);
  			break;
  		}
  		p->from.offset = di->value;
  		p->from.sym->type = SDATA;
  		p->link = datap;
  		datap = p;
  		break;
  	
  	case ADATA:
  		if(p->from.sym == S) {
  			diag("DATA without a sym\n%P", p);
  			break;
  		}
  		p->link = datap;
  		datap = p;
  		break;
  
  	case AGOK:
  		diag("unknown opcode\n%P", p);
  		p->pc = pc;
  		pc++;
  		break;
  
  	case ATEXT:
  		setarch(p);
  		setthumb(p);
  		p->align = 4;
  		if(curtext != P) {
  			histtoauto();
  			curtext->to.autom = curauto;
  			curauto = 0;
  		}
  		skip = 0;
  		curtext = p;
  		autosize = (p->to.offset+3L) & ~3L;
  		p->to.offset = autosize;
  		autosize += 4;
  		s = p->from.sym;
  		if(s == S) {
  			diag("TEXT must have a name\n%P", p);
  			errorexit();
  		}
  		if(s->type != 0 && s->type != SXREF) {
  			if(p->reg & DUPOK) {
  				skip = 1;
  				goto casedef;
  			}
  			diag("redefinition: %s\n%P", s->name, p);
  		}
  		s->type = STEXT;
  		s->value = pc;
  		s->thumb = thumb;
  		lastp->link = p;
  		lastp = p;
  		p->pc = pc;
  		pc++;
  		if(textp == P) {
  			textp = p;
  			etextp = p;
  			goto loop;
  		}
  		etextp->cond = p;
  		etextp = p;
  		break;
  
  	case ASUB:
  		if(p->from.type == D_CONST)
  		if(p->from.name == D_NONE)
  		if(p->from.offset < 0) {
  			p->from.offset = -p->from.offset;
  			p->as = AADD;
  		}
  		goto casedef;
  
  	case AADD:
  		if(p->from.type == D_CONST)
  		if(p->from.name == D_NONE)
  		if(p->from.offset < 0) {
  			p->from.offset = -p->from.offset;
  			p->as = ASUB;
  		}
  		goto casedef;
  
  	case AMOVWD:
  	case AMOVWF:
  	case AMOVDW:
  	case AMOVFW:
  	case AMOVFD:
  	case AMOVDF:
  	// case AMOVF:
  	// case AMOVD:
  	case ACMPF:
  	case ACMPD:
  	case AADDF:
  	case AADDD:
  	case ASUBF:
  	case ASUBD:
  	case AMULF:
  	case AMULD:
  	case ADIVF:
  	case ADIVD:
  		if(thumb)
  			puntfp(p);
  		goto casedef;
  
  	case AMOVF:
  		if(thumb)
  			puntfp(p);
  		if(skip)
  			goto casedef;
  
  		if(p->from.type == D_FCONST && chipfloat(p->from.ieee) < 0) {
  			/* size sb 9 max */
  			sprint(literal, "$%lux", ieeedtof(p->from.ieee));
  			s = lookup(literal, 0);
  			if(s->type == 0) {
  				s->type = SBSS;
  				s->value = 4;
  				t = prg();
  				t->as = ADATA;
  				t->line = p->line;
  				t->from.type = D_OREG;
  				t->from.sym = s;
  				t->from.name = D_EXTERN;
  				t->reg = 4;
  				t->to = p->from;
  				t->link = datap;
  				datap = t;
  			}
  			p->from.type = D_OREG;
  			p->from.sym = s;
  			p->from.name = D_EXTERN;
  			p->from.offset = 0;
  		}
  		goto casedef;
  
  	case AMOVD:
  		if(thumb)
  			puntfp(p);
  		if(skip)
  			goto casedef;
  
  		if(p->from.type == D_FCONST && chipfloat(p->from.ieee) < 0) {
  			/* size sb 18 max */
  			sprint(literal, "$%lux.%lux",
  				p->from.ieee->l, p->from.ieee->h);
  			s = lookup(literal, 0);
  			if(s->type == 0) {
  				s->type = SBSS;
  				s->value = 8;
  				t = prg();
  				t->as = ADATA;
  				t->line = p->line;
  				t->from.type = D_OREG;
  				t->from.sym = s;
  				t->from.name = D_EXTERN;
  				t->reg = 8;
  				t->to = p->from;
  				t->link = datap;
  				datap = t;
  			}
  			p->from.type = D_OREG;
  			p->from.sym = s;
  			p->from.name = D_EXTERN;
  			p->from.offset = 0;
  		}
  		goto casedef;
  
  	default:
  	casedef:
  		if(skip)
  			nopout(p);
  
  		if(p->to.type == D_BRANCH)
  			p->to.offset += ipc;
  		lastp->link = p;
  		lastp = p;
  		p->pc = pc;
  		pc++;
  		break;
  	}
  	goto loop;
  
  eof:
  	diag("truncated object file: %s", pn);
  }
  
  Sym*
  lookup(char *symb, int v)
  {
  	Sym *s;
  	char *p;
  	long h;
  	int c, l;
  
  	h = v;
  	for(p=symb; c = *p; p++)
  		h = h+h+h + c;
  	l = (p - symb) + 1;
  	if(h < 0)
  		h = ~h;
  	h %= NHASH;
  	for(s = hash[h]; s != S; s = s->link)
  		if(s->version == v)
  		if(memcmp(s->name, symb, l) == 0)
  			return s;
  
  	while(nhunk < sizeof(Sym))
  		gethunk();
  	s = (Sym*)hunk;
  	nhunk -= sizeof(Sym);
  	hunk += sizeof(Sym);
  
  	s->name = malloc(l);
  	memmove(s->name, symb, l);
  
  	s->link = hash[h];
  	s->type = 0;
  	s->version = v;
  	s->value = 0;
  	s->sig = 0;
  	s->used = s->thumb = s->foreign = s->fnptr = 0;
  	s->use = nil;
  	hash[h] = s;
  	return s;
  }
  
  Prog*
  prg(void)
  {
  	Prog *p;
  
  	while(nhunk < sizeof(Prog))
  		gethunk();
  	p = (Prog*)hunk;
  	nhunk -= sizeof(Prog);
  	hunk += sizeof(Prog);
  
  	*p = zprg;
  	return p;
  }
  
  void
  gethunk(void)
  {
  	char *h;
  	long nh;
  
  	nh = NHUNK;
  	if(thunk >= 5L*NHUNK) {
  		nh = 5L*NHUNK;
  		if(thunk >= 25L*NHUNK)
  			nh = 25L*NHUNK;
  	}
  	h = mysbrk(nh);
  	if(h == (char*)-1) {
  		diag("out of memory");
  		errorexit();
  	}
  	hunk = h;
  	nhunk = nh;
  	thunk += nh;
  }
  
  void
  doprof1(void)
  {
  	Sym *s;
  	long n;
  	Prog *p, *q;
  
  	if(debug['v'])
  		Bprint(&bso, "%5.2f profile 1\n", cputime());
  	Bflush(&bso);
  	s = lookup("__mcount", 0);
  	n = 1;
  	for(p = firstp->link; p != P; p = p->link) {
  		setarch(p);
  		if(p->as == ATEXT) {
  			q = prg();
  			q->line = p->line;
  			q->link = datap;
  			datap = q;
  			q->as = ADATA;
  			q->from.type = D_OREG;
  			q->from.name = D_EXTERN;
  			q->from.offset = n*4;
  			q->from.sym = s;
  			q->reg = 4;
  			q->to = p->from;
  			q->to.type = D_CONST;
  
  			q = prg();
  			q->line = p->line;
  			q->pc = p->pc;
  			q->link = p->link;
  			p->link = q;
  			p = q;
  			p->as = AMOVW;
  			p->from.type = D_OREG;
  			p->from.name = D_EXTERN;
  			p->from.sym = s;
  			p->from.offset = n*4 + 4;
  			p->to.type = D_REG;
  			p->to.reg = thumb ? REGTMPT : REGTMP;
  
  			q = prg();
  			q->line = p->line;
  			q->pc = p->pc;
  			q->link = p->link;
  			p->link = q;
  			p = q;
  			p->as = AADD;
  			p->from.type = D_CONST;
  			p->from.offset = 1;
  			p->to.type = D_REG;
  			p->to.reg = thumb ? REGTMPT : REGTMP;
  
  			q = prg();
  			q->line = p->line;
  			q->pc = p->pc;
  			q->link = p->link;
  			p->link = q;
  			p = q;
  			p->as = AMOVW;
  			p->from.type = D_REG;
  			p->from.reg = thumb ? REGTMPT : REGTMP;
  			p->to.type = D_OREG;
  			p->to.name = D_EXTERN;
  			p->to.sym = s;
  			p->to.offset = n*4 + 4;
  
  			n += 2;
  			continue;
  		}
  	}
  	q = prg();
  	q->line = 0;
  	q->link = datap;
  	datap = q;
  
  	q->as = ADATA;
  	q->from.type = D_OREG;
  	q->from.name = D_EXTERN;
  	q->from.sym = s;
  	q->reg = 4;
  	q->to.type = D_CONST;
  	q->to.offset = n;
  
  	s->type = SBSS;
  	s->value = n*4;
  }
  
  void
  doprof2(void)
  {
  	Sym *s2, *s4;
  	Prog *p, *q, *ps2, *ps4;
  
  	if(debug['v'])
  		Bprint(&bso, "%5.2f profile 2\n", cputime());
  	Bflush(&bso);
  	s2 = lookup("_profin", 0);
  	s4 = lookup("_profout", 0);
  	if(s2->type != STEXT || s4->type != STEXT) {
  		diag("_profin/_profout not defined");
  		return;
  	}
  	ps2 = P;
  	ps4 = P;
  	for(p = firstp; p != P; p = p->link) {
  		setarch(p);
  		if(p->as == ATEXT) {
  			if(p->from.sym == s2) {
  				ps2 = p;
  				p->reg = 1;
  			}
  			if(p->from.sym == s4) {
  				ps4 = p;
  				p->reg = 1;
  			}
  		}
  	}
  	for(p = firstp; p != P; p = p->link) {
  		setarch(p);
  		if(p->as == ATEXT) {
  			if(p->reg & NOPROF) {
  				for(;;) {
  					q = p->link;
  					if(q == P)
  						break;
  					if(q->as == ATEXT)
  						break;
  					p = q;
  				}
  				continue;
  			}
  
  			/*
  			 * BL	profin, R2
  			 */
  			q = prg();
  			q->line = p->line;
  			q->pc = p->pc;
  			q->link = p->link;
  			p->link = q;
  			p = q;
  			p->as = ABL;
  			p->to.type = D_BRANCH;
  			p->cond = ps2;
  			p->to.sym = s2;
  
  			continue;
  		}
  		if(p->as == ARET) {
  			/*
  			 * RET
  			 */
  			q = prg();
  			q->as = ARET;
  			q->from = p->from;
  			q->to = p->to;
  			q->link = p->link;
  			p->link = q;
  
  			/*
  			 * BL	profout
  			 */
  			p->as = ABL;
  			p->from = zprg.from;
  			p->to = zprg.to;
  			p->to.type = D_BRANCH;
  			p->cond = ps4;
  			p->to.sym = s4;
  
  			p = q;
  
  			continue;
  		}
  	}
  }
  
  void
  nuxiinit(void)
  {
  
  	int i, c;
  
  	for(i=0; i<4; i++) {
  		c = find1(0x04030201L, i+1);
  		if(i < 2)
  			inuxi2[i] = c;
  		if(i < 1)
  			inuxi1[i] = c;
  		inuxi4[i] = c;
  		fnuxi4[i] = c;
  		if(!debug['d']){
  			fnuxi8[i] = c;
  			fnuxi8[i+4] = c+4;
  		}
  		else{
  			fnuxi8[i] = c+4;		/* ms word first, then ls, even in little endian mode */
  			fnuxi8[i+4] = c;
  		}
  	}
  	if(debug['v']) {
  		Bprint(&bso, "inuxi = ");
  		for(i=0; i<1; i++)
  			Bprint(&bso, "%d", inuxi1[i]);
  		Bprint(&bso, " ");
  		for(i=0; i<2; i++)
  			Bprint(&bso, "%d", inuxi2[i]);
  		Bprint(&bso, " ");
  		for(i=0; i<4; i++)
  			Bprint(&bso, "%d", inuxi4[i]);
  		Bprint(&bso, "\nfnuxi = ");
  		for(i=0; i<4; i++)
  			Bprint(&bso, "%d", fnuxi4[i]);
  		Bprint(&bso, " ");
  		for(i=0; i<8; i++)
  			Bprint(&bso, "%d", fnuxi8[i]);
  		Bprint(&bso, "\n");
  	}
  	Bflush(&bso);
  }
  
  int
  find1(long l, int c)
  {
  	char *p;
  	int i;
  
  	p = (char*)&l;
  	for(i=0; i<4; i++)
  		if(*p++ == c)
  			return i;
  	return 0;
  }
  
  long
  ieeedtof(Ieee *ieeep)
  {
  	int exp;
  	long v;
  
  	if(ieeep->h == 0)
  		return 0;
  	exp = (ieeep->h>>20) & ((1L<<11)-1L);
  	exp -= (1L<<10) - 2L;
  	v = (ieeep->h & 0xfffffL) << 3;
  	v |= (ieeep->l >> 29) & 0x7L;
  	if((ieeep->l >> 28) & 1) {
  		v++;
  		if(v & 0x800000L) {
  			v = (v & 0x7fffffL) >> 1;
  			exp++;
  		}
  	}
  	if(exp <= -126 || exp >= 130)
  		diag("double fp to single fp overflow");
  	v |= ((exp + 126) & 0xffL) << 23;
  	v |= ieeep->h & 0x80000000L;
  	return v;
  }
  
  double
  ieeedtod(Ieee *ieeep)
  {
  	Ieee e;
  	double fr;
  	int exp;
  
  	if(ieeep->h & (1L<<31)) {
  		e.h = ieeep->h & ~(1L<<31);
  		e.l = ieeep->l;
  		return -ieeedtod(&e);
  	}
  	if(ieeep->l == 0 && ieeep->h == 0)
  		return 0;
  	fr = ieeep->l & ((1L<<16)-1L);
  	fr /= 1L<<16;
  	fr += (ieeep->l>>16) & ((1L<<16)-1L);
  	fr /= 1L<<16;
  	fr += (ieeep->h & (1L<<20)-1L) | (1L<<20);
  	fr /= 1L<<21;
  	exp = (ieeep->h>>20) & ((1L<<11)-1L);
  	exp -= (1L<<10) - 2L;
  	return ldexp(fr, exp);
  }
  
  static void
  puntfp(Prog *p)
  {
  	USED(p);
  	/* floating point - punt for now */
  	curtext->reg = NREG;	/* ARM */
  	curtext->from.sym->thumb = 0;
  	thumb = 0;
  	// print("%s: generating ARM code (contains floating point ops %d)\n", curtext->from.sym->name, p->line);
  }
  
  void
  undefsym(Sym *s)
  {
  	int n;
  
  	n = imports;
  	if(s->value != 0)
  		diag("value != 0 on SXREF");
  	if(n >= 1<<Rindex)
  		diag("import index %d out of range", n);
  	s->value = n<<Roffset;
  	s->type = SUNDEF;
  	imports++;
  }
  
  void
  zerosig(char *sp)
  {
  	Sym *s;
  
  	s = lookup(sp, 0);
  	s->sig = 0;
  }
  
  void
  readundefs(char *f, int t)
  {
  	int i, n;
  	Sym *s;
  	Biobuf *b;
  	char *l, buf[256], *fields[64];
  
  	if(f == nil)
  		return;
  	b = Bopen(f, OREAD);
  	if(b == nil){
  		diag("could not open %s: %r", f);
  		errorexit();
  	}
  	while((l = Brdline(b, '\n')) != nil){
  		n = Blinelen(b);
  		if(n >= sizeof(buf)){
  			diag("%s: line too long", f);
  			errorexit();
  		}
  		memmove(buf, l, n);
  		buf[n-1] = '\0';
  		n = getfields(buf, fields, nelem(fields), 1, " \t\r\n");
  		if(n == nelem(fields)){
  			diag("%s: bad format", f);
  			errorexit();
  		}
  		for(i = 0; i < n; i++){
  			s = lookup(fields[i], 0);
  			s->type = SXREF;
  			s->subtype = t;
  			if(t == SIMPORT)
  				nimports++;
  			else
  				nexports++;
  		}
  	}
  	Bterm(b);
  }
  