diff options
Diffstat (limited to 'src/cmd')
221 files changed, 22731 insertions, 12800 deletions
diff --git a/src/cmd/5a/Makefile b/src/cmd/5a/Makefile index f01b017da..f4463c97b 100644 --- a/src/cmd/5a/Makefile +++ b/src/cmd/5a/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 5a\ +TARG=5a HFILES=\ a.h\ @@ -15,26 +15,11 @@ HFILES=\ OFILES=\ y.tab.$O\ lex.$O\ -# ../5l/enam.$O\ + ../5l/enam.$O\ YFILES=\ a.y\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lbio -l9 - -$(OFILES): $(HFILES) +include ../../Make.ccmd lex.$O: ../cc/macbody ../cc/lexbody - -y.tab.h: $(YFILES) - bison -y $(YFLAGS) $(YFILES) - -y.tab.c: y.tab.h - test -f y.tab.c && touch y.tab.c - -clean: - rm -f *.$O $(TARG) *.5 enam.c 5.out a.out y.tab.h y.tab.c - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) diff --git a/src/cmd/5a/a.h b/src/cmd/5a/a.h index 6cd5af8c6..bc4f433e1 100644 --- a/src/cmd/5a/a.h +++ b/src/cmd/5a/a.h @@ -1,7 +1,7 @@ // Inferno utils/5a/a.h // http://code.google.com/p/inferno-os/source/browse/utils/5a/a.h // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -148,6 +148,7 @@ EXTERN int pass; EXTERN char* pathname; EXTERN int32 pc; EXTERN int peekc; +EXTERN int32 stmtline; EXTERN int sym; EXTERN char* symb; EXTERN int thechar; @@ -157,7 +158,7 @@ EXTERN Biobuf obuf; void* alloc(int32); void* allocn(void*, int32, int32); -void ensuresymb(int32); +void ensuresymb(int32); void errorexit(void); void pushio(void); void newio(void); diff --git a/src/cmd/5a/a.y b/src/cmd/5a/a.y index bb30ac698..b39c916ab 100644 --- a/src/cmd/5a/a.y +++ b/src/cmd/5a/a.y @@ -1,7 +1,7 @@ // Inferno utils/5a/a.y // http://code.google.com/p/inferno-os/source/browse/utils/5a/a.y // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -63,7 +63,11 @@ %type <gen> imm ximm name oreg ireg nireg ioreg imsr %% prog: -| prog line +| prog + { + stmtline = lineno; + } + line line: LLAB ':' diff --git a/src/cmd/5a/lex.c b/src/cmd/5a/lex.c index 2cc0993e4..b36094a78 100644 --- a/src/cmd/5a/lex.c +++ b/src/cmd/5a/lex.c @@ -1,7 +1,7 @@ // Inferno utils/5a/lex.c // http://code.google.com/p/inferno-os/source/browse/utils/5a/lex.c // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -658,10 +658,10 @@ jackpot: Bputc(&obuf, a); Bputc(&obuf, scond); Bputc(&obuf, reg); - Bputc(&obuf, lineno); - Bputc(&obuf, lineno>>8); - Bputc(&obuf, lineno>>16); - Bputc(&obuf, lineno>>24); + Bputc(&obuf, stmtline); + Bputc(&obuf, stmtline>>8); + Bputc(&obuf, stmtline>>16); + Bputc(&obuf, stmtline>>24); zaddr(g1, sf); zaddr(g2, st); diff --git a/src/cmd/5c/Makefile b/src/cmd/5c/Makefile index b534206f3..70b614e8a 100644 --- a/src/cmd/5c/Makefile +++ b/src/cmd/5c/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 5c\ +TARG=5c HFILES=\ gc.h\ @@ -26,18 +26,9 @@ OFILES=\ ../5l/enam.$O\ LIB=\ - ../cc/cc.a$O + ../cc/cc.a\ -$(TARG): $(OFILES) $(LIB) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lbio -l9 - -$(OFILES): $(HFILES) - -clean: - rm -f *.$O $(TARG) *.5 enam.c 5.out a.out - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +include ../../Make.ccmd %.$O: ../cc/%.c - $(CC) $(CFLAGS) -c -I. -o $@ ../cc/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../cc/$*.c diff --git a/src/cmd/5c/list.c b/src/cmd/5c/list.c index c792c130d..ab0fae83c 100644 --- a/src/cmd/5c/list.c +++ b/src/cmd/5c/list.c @@ -59,7 +59,7 @@ Bconv(Fmt *fp) if(str[0]) strcat(str, " "); if(var[i].sym == S) { - sprint(ss, "$%ld", var[i].offset); + sprint(ss, "$%d", var[i].offset); s = ss; } else s = var[i].sym->name; @@ -204,7 +204,7 @@ Dconv(Fmt *fp) break; case D_BRANCH: - sprint(str, "%ld(PC)", a->offset-pc); + sprint(str, "%d(PC)", a->offset-pc); break; case D_FCONST: @@ -307,7 +307,7 @@ Nconv(Fmt *fp) a = va_arg(fp->args, Adr*); s = a->sym; if(s == S) { - sprint(str, "%ld", a->offset); + sprint(str, "%d", a->offset); goto out; } switch(a->name) { @@ -316,23 +316,23 @@ Nconv(Fmt *fp) break; case D_NONE: - sprint(str, "%ld", a->offset); + sprint(str, "%d", a->offset); break; case D_EXTERN: - sprint(str, "%s+%ld(SB)", s->name, a->offset); + sprint(str, "%s+%d(SB)", s->name, a->offset); break; case D_STATIC: - sprint(str, "%s<>+%ld(SB)", s->name, a->offset); + sprint(str, "%s<>+%d(SB)", s->name, a->offset); break; case D_AUTO: - sprint(str, "%s-%ld(SP)", s->name, -a->offset); + sprint(str, "%s-%d(SP)", s->name, -a->offset); break; case D_PARAM: - sprint(str, "%s+%ld(FP)", s->name, a->offset); + sprint(str, "%s+%d(FP)", s->name, a->offset); break; } out: diff --git a/src/cmd/5c/mul.c b/src/cmd/5c/mul.c index 121f67d5b..ff50c4845 100644 --- a/src/cmd/5c/mul.c +++ b/src/cmd/5c/mul.c @@ -115,7 +115,7 @@ mulcon0(int32 v) if(docode(hintab[g].hint, m->code, 1, 0)) return m; - print("multiply table failure %ld\n", v); + print("multiply table failure %d\n", v); m->code[0] = 0; return 0; @@ -132,7 +132,7 @@ no: if(gen1(g)) { if(docode(hint, m->code, 1, 0)) return m; - print("multiply table failure %ld\n", v); + print("multiply table failure %d\n", v); break; } } diff --git a/src/cmd/5c/peep.c b/src/cmd/5c/peep.c index 4959d414d..8945ee732 100644 --- a/src/cmd/5c/peep.c +++ b/src/cmd/5c/peep.c @@ -1324,8 +1324,6 @@ predicable(Prog *p) || p->as == ASIGNAME || p->as == ATEXT || p->as == AWORD - || p->as == ADYNT - || p->as == AINIT || p->as == ABCASE || p->as == ACASE) return 0; diff --git a/src/cmd/5c/reg.c b/src/cmd/5c/reg.c index f2d38d519..8c9794418 100644 --- a/src/cmd/5c/reg.c +++ b/src/cmd/5c/reg.c @@ -309,7 +309,7 @@ loop2: if(debug['R'] && debug['v']) { print("\nprop structure:\n"); for(r = firstr; r != R; r = r->link) { - print("%ld:%P", r->loop, r->prog); + print("%d:%P", r->loop, r->prog); for(z=0; z<BITS; z++) bit.b[z] = r->set.b[z] | r->refahead.b[z] | r->calahead.b[z] | @@ -955,7 +955,7 @@ paint1(Reg *r, int bn) if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) { change -= CLOAD * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tld %B $%d\n", r->loop, + print("%d%P\td %B $%d\n", r->loop, r->prog, blsh(bn), change); } for(;;) { @@ -965,21 +965,21 @@ paint1(Reg *r, int bn) if(r->use1.b[z] & bb) { change += CREF * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tu1 %B $%d\n", r->loop, + print("%d%P\tu1 %B $%d\n", r->loop, p, blsh(bn), change); } if((r->use2.b[z]|r->set.b[z]) & bb) { change += CREF * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tu2 %B $%d\n", r->loop, + print("%d%P\tu2 %B $%d\n", r->loop, p, blsh(bn), change); } if(STORE(r) & r->regdiff.b[z] & bb) { change -= CLOAD * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tst %B $%d\n", r->loop, + print("%d%P\tst %B $%d\n", r->loop, p, blsh(bn), change); } diff --git a/src/cmd/5c/swt.c b/src/cmd/5c/swt.c index cefbf53d9..43eb73c94 100644 --- a/src/cmd/5c/swt.c +++ b/src/cmd/5c/swt.c @@ -47,7 +47,7 @@ swit1(C1 *q, int nc, int32 def, Node *n) if(nc < 5) { for(i=0; i<nc; i++) { if(debug['W']) - print("case = %.8lux\n", q->val); + print("case = %.8ux\n", q->val); gopcode(OEQ, nodconst(q->val), n, Z); patch(p, q->label); q++; @@ -60,7 +60,7 @@ swit1(C1 *q, int nc, int32 def, Node *n) i = nc / 2; r = q+i; if(debug['W']) - print("case > %.8lux\n", r->val); + print("case > %.8ux\n", r->val); gopcode(OGT, nodconst(r->val), n, Z); sp = p; gopcode(OEQ, nodconst(r->val), n, Z); /* just gen the B.EQ */ @@ -68,7 +68,7 @@ swit1(C1 *q, int nc, int32 def, Node *n) swit1(q, i, def, n); if(debug['W']) - print("case < %.8lux\n", r->val); + print("case < %.8ux\n", r->val); patch(sp, pc); swit1(r+1, nc-i-1, def, n); return; @@ -81,7 +81,7 @@ direct: patch(p, def); for(i=0; i<nc; i++) { if(debug['W']) - print("case = %.8lux\n", q->val); + print("case = %.8ux\n", q->val); while(q->val != v) { nextpc(); p->as = ABCASE; @@ -227,7 +227,7 @@ mulcon(Node *n, Node *nn) return 0; } if(debug['M'] && debug['v']) - print("%L multiply: %ld\n", n->lineno, v); + print("%L multiply: %d\n", n->lineno, v); memmove(code, m->code, sizeof(m->code)); code[sizeof(m->code)] = 0; @@ -606,7 +606,7 @@ zaddr(char *bp, Adr *a, int s) } int32 -align(int32 i, Type *t, int op) +align(int32 i, Type *t, int op, int32 *maxalign) { int32 o; Type *v; @@ -620,7 +620,9 @@ align(int32 i, Type *t, int op) break; case Asu2: /* padding at end of a struct */ - w = SZ_LONG; + w = *maxalign; + if(w < 1) + w = 1; if(packflg) w = packflg; break; @@ -628,10 +630,16 @@ align(int32 i, Type *t, int op) case Ael1: /* initial align of struct element */ for(v=t; v->etype==TARRAY; v=v->link) ; - w = ewidth[v->etype]; - if(w <= 0 || w >= SZ_LONG) - w = SZ_LONG; - if(packflg) + if(v->etype == TSTRUCT || v->etype == TUNION) + w = v->align; + else { + w = ewidth[v->etype]; + if(w == 8) + w = 4; + } + if(w < 1 || w > SZ_LONG) + fatal(Z, "align"); + if(packflg) w = packflg; break; @@ -641,8 +649,8 @@ align(int32 i, Type *t, int op) case Aarg0: /* initial passbyptr argument in arg list */ if(typesuv[t->etype]) { - o = align(o, types[TIND], Aarg1); - o = align(o, types[TIND], Aarg2); + o = align(o, types[TIND], Aarg1, nil); + o = align(o, types[TIND], Aarg2, nil); } break; @@ -661,14 +669,16 @@ align(int32 i, Type *t, int op) break; case Aaut3: /* total align of automatic */ - o = align(o, t, Ael2); - o = align(o, t, Ael1); + o = align(o, t, Ael2, nil); + o = align(o, t, Ael1, nil); w = SZ_LONG; /* because of a pun in cc/dcl.c:contig() */ break; } o = xround(o, w); + if(maxalign != nil && *maxalign < w) + *maxalign = w; if(debug['A']) - print("align %s %ld %T = %ld\n", bnames[op], i, t, o); + print("align %s %d %T = %d\n", bnames[op], i, t, o); return o; } diff --git a/src/cmd/5c/txt.c b/src/cmd/5c/txt.c index 9dac0312f..0f17cea89 100644 --- a/src/cmd/5c/txt.c +++ b/src/cmd/5c/txt.c @@ -388,7 +388,7 @@ err: void regsalloc(Node *n, Node *nn) { - cursafe = align(cursafe, nn->type, Aaut3); + cursafe = align(cursafe, nn->type, Aaut3, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); *n = *nodsafe; n->xoffset = -(stkoff + cursafe); @@ -402,22 +402,22 @@ regaalloc1(Node *n, Node *nn) { nodreg(n, nn, REGARG); reg[REGARG]++; - curarg = align(curarg, nn->type, Aarg1); - curarg = align(curarg, nn->type, Aarg2); + curarg = align(curarg, nn->type, Aarg1, nil); + curarg = align(curarg, nn->type, Aarg2, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); } void regaalloc(Node *n, Node *nn) { - curarg = align(curarg, nn->type, Aarg1); + curarg = align(curarg, nn->type, Aarg1, nil); *n = *nn; n->op = OINDREG; n->reg = REGSP; n->xoffset = curarg + SZ_LONG; n->complex = 0; n->addable = 20; - curarg = align(curarg, nn->type, Aarg2); + curarg = align(curarg, nn->type, Aarg2, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); } @@ -580,7 +580,8 @@ void gmove(Node *f, Node *t) { int ft, tt, a; - Node nod; + Node nod, nod1; + Prog *p1; ft = f->type->etype; tt = t->type->etype; @@ -709,21 +710,53 @@ gmove(Node *f, Node *t) } break; case TUINT: - case TINT: case TULONG: + if(tt == TFLOAT || tt == TDOUBLE) { + // ugly and probably longer than necessary, + // but vfp has a single instruction for this, + // so hopefully it won't last long. + // + // tmp = f + // tmp1 = tmp & 0x80000000 + // tmp ^= tmp1 + // t = float(int32(tmp)) + // if(tmp1) + // t += 2147483648. + // + regalloc(&nod, f, Z); + regalloc(&nod1, f, Z); + gins(AMOVW, f, &nod); + gins(AMOVW, &nod, &nod1); + gins(AAND, nodconst(0x80000000), &nod1); + gins(AEOR, &nod1, &nod); + if(tt == TFLOAT) + gins(AMOVWF, &nod, t); + else + gins(AMOVWD, &nod, t); + gins(ACMP, nodconst(0), Z); + raddr(&nod1, p); + gins(ABEQ, Z, Z); + regfree(&nod); + regfree(&nod1); + p1 = p; + regalloc(&nod, t, Z); + gins(AMOVF, nodfconst(2147483648.), &nod); + gins(AADDF, &nod, t); + regfree(&nod); + patch(p1, pc); + return; + } + // fall through + + case TINT: case TLONG: case TIND: switch(tt) { case TDOUBLE: - case TVLONG: gins(AMOVWD, f, t); - if(ft == TULONG) { - } return; case TFLOAT: gins(AMOVWF, f, t); - if(ft == TULONG) { - } return; case TINT: case TUINT: @@ -741,7 +774,6 @@ gmove(Node *f, Node *t) case TSHORT: switch(tt) { case TDOUBLE: - case TVLONG: regalloc(&nod, f, Z); gins(AMOVH, f, &nod); gins(AMOVWD, &nod, t); @@ -771,7 +803,6 @@ gmove(Node *f, Node *t) case TUSHORT: switch(tt) { case TDOUBLE: - case TVLONG: regalloc(&nod, f, Z); gins(AMOVHU, f, &nod); gins(AMOVWD, &nod, t); @@ -801,7 +832,6 @@ gmove(Node *f, Node *t) case TCHAR: switch(tt) { case TDOUBLE: - case TVLONG: regalloc(&nod, f, Z); gins(AMOVB, f, &nod); gins(AMOVWD, &nod, t); @@ -831,7 +861,6 @@ gmove(Node *f, Node *t) case TUCHAR: switch(tt) { case TDOUBLE: - case TVLONG: regalloc(&nod, f, Z); gins(AMOVBU, f, &nod); gins(AMOVWD, &nod, t); diff --git a/src/cmd/5g/Makefile b/src/cmd/5g/Makefile index 123af19cd..6873fbc68 100644 --- a/src/cmd/5g/Makefile +++ b/src/cmd/5g/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 5g +TARG=5g HFILES=\ ../gc/go.h\ @@ -21,18 +21,15 @@ OFILES=\ ggen.$O\ gsubr.$O\ cgen.$O\ - cgen64.$O + cgen64.$O\ + cplx.$O\ + reg.$O\ + peep.$O\ LIB=\ - ../gc/gc.a$O + ../gc/gc.a\ -$(TARG): $(OFILES) $(LIB) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lbio -l9 -lm +include ../../Make.ccmd -$(OFILES): $(HFILES) - -clean: - rm -f *.o $(TARG) *.5 enam.c 5.out a.out - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +%.$O: ../gc/%.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../gc/$*.c diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c index 8072c3ceb..1328f4be6 100644 --- a/src/cmd/5g/cgen.c +++ b/src/cmd/5g/cgen.c @@ -4,33 +4,6 @@ #include "gg.h" -void -mgen(Node *n, Node *n1, Node *rg) -{ - n1->ostk = 0; - n1->op = OEMPTY; - - if(n->addable) { - *n1 = *n; - n1->ostk = 0; - if(n1->op == OREGISTER || n1->op == OINDREG) - reg[n->val.u.reg]++; - return; - } - if(n->type->width > widthptr) - tempname(n1, n->type); - else - regalloc(n1, n->type, rg); - cgen(n, n1); -} - -void -mfree(Node *n) -{ - if(n->op == OREGISTER) - regfree(n); -} - /* * generate: * res = n; @@ -55,12 +28,6 @@ cgen(Node *n, Node *res) if(res == N || res->type == T) fatal("cgen: res nil"); - // TODO compile complex - if(n != N && n->type != T && iscomplex[n->type->etype]) - return; - if(res != N && res->type != T && iscomplex[res->type->etype]) - return; - while(n->op == OCONVNOP) n = n->left; @@ -80,6 +47,7 @@ cgen(Node *n, Node *res) goto ret; } + // update addressability for string, slice // can't do in walk because n->left->addable // changes if n->left is an escaping local variable. @@ -96,7 +64,9 @@ cgen(Node *n, Node *res) // if both are addressable, move if(n->addable && res->addable) { - if (is64(n->type) || is64(res->type) || n->op == OREGISTER || res->op == OREGISTER) { + if(is64(n->type) || is64(res->type) || + n->op == OREGISTER || res->op == OREGISTER || + iscomplex[n->type->etype] || iscomplex[res->type->etype]) { gmove(n, res); } else { regalloc(&n1, n->type, N); @@ -126,8 +96,13 @@ cgen(Node *n, Node *res) return; } + if(complexop(n, res)) { + complexgen(n, res); + return; + } + // if n is sudoaddable generate addr and move - if (!is64(n->type) && !is64(res->type)) { + if (!is64(n->type) && !is64(res->type) && !iscomplex[n->type->etype] && !iscomplex[res->type->etype]) { a = optoas(OAS, n->type); if(sudoaddable(a, n, &addr, &w)) { if (res->op != OREGISTER) { @@ -195,8 +170,8 @@ cgen(Node *n, Node *res) case OREAL: case OIMAG: case OCMPLX: - // TODO compile complex - return; + fatal("unexpected complex"); + break; // these call bgen to get a bool value case OOROR: @@ -269,10 +244,26 @@ cgen(Node *n, Node *res) cgen(nl, res); break; } - - mgen(nl, &n1, res); - gmove(&n1, res); - mfree(&n1); + if(nl->addable && !is64(nl->type)) { + regalloc(&n1, nl->type, res); + gmove(nl, &n1); + } else { + if(n->type->width > widthptr || is64(nl->type) || isfloat[nl->type->etype]) + tempname(&n1, nl->type); + else + regalloc(&n1, nl->type, res); + cgen(nl, &n1); + } + if(n->type->width > widthptr || is64(n->type) || isfloat[n->type->etype]) + tempname(&n2, n->type); + else + regalloc(&n2, n->type, N); + gmove(&n1, &n2); + gmove(&n2, res); + if(n1.op == OREGISTER) + regfree(&n1); + if(n2.op == OREGISTER) + regfree(&n2); break; case ODOT: @@ -461,6 +452,41 @@ ret: } /* + * generate array index into res. + * n might be any size; res is 32-bit. + * returns Prog* to patch to panic call. + */ +Prog* +cgenindex(Node *n, Node *res) +{ + Node tmp, lo, hi, zero, n1, n2; + + if(!is64(n->type)) { + cgen(n, res); + return nil; + } + + tempname(&tmp, types[TINT64]); + cgen(n, &tmp); + split64(&tmp, &lo, &hi); + gmove(&lo, res); + if(debug['B']) { + splitclean(); + return nil; + } + regalloc(&n1, types[TINT32], N); + regalloc(&n2, types[TINT32], N); + nodconst(&zero, types[TINT32], 0); + gmove(&hi, &n1); + gmove(&zero, &n2); + gcmp(ACMP, &n1, &n2); + regfree(&n2); + regfree(&n1); + splitclean(); + return gbranch(ABNE, T); +} + +/* * generate: * res = &n; */ @@ -469,10 +495,10 @@ agen(Node *n, Node *res) { Node *nl, *nr; Node n1, n2, n3, n4, n5, tmp; - Prog *p1; + Prog *p1, *p2; uint32 w; uint64 v; - Type *t; + int r; if(debug['g']) { dump("\nagen-res", res); @@ -504,7 +530,22 @@ agen(Node *n, Node *res) break; case OCALLMETH: - cgen_callmeth(n, 0); + case OCALLFUNC: + // Release res so that it is available for cgen_call. + // Pick it up again after the call. + r = -1; + if(n->ullman >= UINF) { + if(res->op == OREGISTER || res->op == OINDREG) { + r = res->val.u.reg; + reg[r]--; + } + } + if(n->op == OCALLMETH) + cgen_callmeth(n, 0); + else + cgen_call(n, 0); + if(r >= 0) + reg[r]++; cgen_aret(n, res); break; @@ -513,36 +554,36 @@ agen(Node *n, Node *res) cgen_aret(n, res); break; - case OCALLFUNC: - cgen_call(n, 0); - cgen_aret(n, res); - break; - case OINDEX: - // TODO(rsc): uint64 indices + p2 = nil; // to be patched to panicindex. w = n->type->width; if(nr->addable) { - agenr(nl, &n3, res); - if(!isconst(nr, CTINT)) { + if(!isconst(nr, CTINT)) tempname(&tmp, types[TINT32]); - cgen(nr, &tmp); + if(!isconst(nl, CTSTR)) + agenr(nl, &n3, res); + if(!isconst(nr, CTINT)) { + p2 = cgenindex(nr, &tmp); regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); } } else if(nl->addable) { if(!isconst(nr, CTINT)) { tempname(&tmp, types[TINT32]); - cgen(nr, &tmp); + p2 = cgenindex(nr, &tmp); regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); } - regalloc(&n3, types[tptr], res); - agen(nl, &n3); + if(!isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + agen(nl, &n3); + } } else { tempname(&tmp, types[TINT32]); - cgen(nr, &tmp); + p2 = cgenindex(nr, &tmp); nr = &tmp; - agenr(nl, &n3, res); + if(!isconst(nl, CTSTR)) + agenr(nl, &n3, res); regalloc(&n1, tmp.type, N); gins(optoas(OAS, tmp.type), &tmp, &n1); } @@ -556,9 +597,10 @@ agen(Node *n, Node *res) // constant index if(isconst(nr, CTINT)) { + if(isconst(nl, CTSTR)) + fatal("constant string constant index"); v = mpgetfix(nr->val.u.xval); - if(isslice(nl->type)) { - + if(isslice(nl->type) || nl->type->etype == TSTRING) { if(!debug['B'] && !n->etype) { n1 = n3; n1.op = OINDREG; @@ -582,13 +624,6 @@ agen(Node *n, Node *res) n1.type = types[tptr]; n1.xoffset = Array_array; gmove(&n1, &n3); - } else - if(!debug['B'] && !n->etype) { - if(v < 0) - yyerror("out of bounds on array"); - else - if(v >= nl->type->bound) - yyerror("out of bounds on array"); } nodconst(&n2, types[tptr], v*w); @@ -602,19 +637,17 @@ agen(Node *n, Node *res) break; } - // type of the index - t = types[TUINT32]; - if(issigned[n1.type->etype]) - t = types[TINT32]; - - regalloc(&n2, t, &n1); // i + regalloc(&n2, types[TINT32], &n1); // i gmove(&n1, &n2); regfree(&n1); if(!debug['B'] && !n->etype) { // check bounds regalloc(&n4, types[TUINT32], N); - if(isslice(nl->type)) { + if(isconst(nl, CTSTR)) { + nodconst(&n1, types[TUINT32], nl->val.u.sval->len); + gmove(&n1, &n4); + } else if(isslice(nl->type) || nl->type->etype == TSTRING) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; @@ -627,11 +660,18 @@ agen(Node *n, Node *res) gcmp(optoas(OCMP, types[TUINT32]), &n2, &n4); regfree(&n4); p1 = gbranch(optoas(OLT, types[TUINT32]), T); + if(p2) + patch(p2, pc); ginscall(panicindex, 0); patch(p1, pc); } - - if(isslice(nl->type)) { + + if(isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + p1 = gins(AMOVW, N, &n3); + datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from); + p1->from.type = D_CONST; + } else if(isslice(nl->type) || nl->type->etype == TSTRING) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; @@ -653,10 +693,10 @@ agen(Node *n, Node *res) else if(w == 8) gshift(AADD, &n2, SHIFT_LL, 3, &n3); } else { - regalloc(&n4, t, N); - nodconst(&n1, t, w); + regalloc(&n4, types[TUINT32], N); + nodconst(&n1, types[TUINT32], w); gmove(&n1, &n4); - gins(optoas(OMUL, t), &n4, &n2); + gins(optoas(OMUL, types[TUINT32]), &n4, &n2); gins(optoas(OADD, types[tptr]), &n2, &n3); regfree(&n4); gmove(&n3, res); @@ -769,12 +809,8 @@ igen(Node *n, Node *a, Node *res) void agenr(Node *n, Node *a, Node *res) { - Node n1; - - tempname(&n1, types[tptr]); - agen(n, &n1); regalloc(a, types[tptr], res); - gmove(&n1, a); + agen(n, a); } /* @@ -796,15 +832,12 @@ bgen(Node *n, int true, Prog *to) if(n == N) n = nodbool(1); + if(n->ninit != nil) + genlist(n->ninit); + nl = n->left; nr = n->right; - // TODO compile complex - if(nl != N && nl->type != T && iscomplex[nl->type->etype]) - return; - if(nr != N && nr->type != T && iscomplex[nr->type->etype]) - return; - if(n->type == T) { convlit(&n, types[TBOOL]); if(n->type == T) @@ -925,6 +958,7 @@ bgen(Node *n, int true, Prog *to) goto ret; } a = brcom(a); + true = !true; } // make simplest on right @@ -985,6 +1019,11 @@ bgen(Node *n, int true, Prog *to) break; } + if(iscomplex[nl->type->etype]) { + complexbool(a, nl, nr, true, to); + break; + } + if(is64(nr->type)) { if(!nl->addable) { tempname(&n1, nl->type); @@ -1031,7 +1070,16 @@ bgen(Node *n, int true, Prog *to) cgen(nr, &n2); gcmp(optoas(OCMP, nr->type), &n1, &n2); - patch(gbranch(a, nr->type), to); + if(isfloat[nl->type->etype]) { + p1 = gbranch(ABVS, nr->type); + patch(gbranch(a, nr->type), to); + if(n->op == ONE) + patch(p1, to); + else + patch(p1, pc); + } else { + patch(gbranch(a, nr->type), to); + } regfree(&n1); regfree(&n2); @@ -1088,7 +1136,7 @@ stkof(Node *n) t = structfirst(&flist, getoutarg(t)); if(t != T) - return t->width; + return t->width + 4; // correct for LR break; } diff --git a/src/cmd/5g/cgen64.c b/src/cmd/5g/cgen64.c index a22f4a548..716ec5ed5 100644 --- a/src/cmd/5g/cgen64.c +++ b/src/cmd/5g/cgen64.c @@ -439,12 +439,12 @@ olsh_break: p3 = gbranch(ABLO, T); // shift == 32 + p1 = gins(AMOVW, &bh, &al); + p1->scond = C_SCOND_EQ; if(bh.type->etype == TINT32) p1 = gshift(AMOVW, &bh, SHIFT_AR, 31, &ah); else - p1 = gins(AEOR, &al, &al); - p1->scond = C_SCOND_EQ; - p1 = gins(AMOVW, &bh, &al); + p1 = gins(AEOR, &ah, &ah); p1->scond = C_SCOND_EQ; p4 = gbranch(ABEQ, T); diff --git a/src/cmd/5g/galign.c b/src/cmd/5g/galign.c index 76affbf00..9c8760aea 100644 --- a/src/cmd/5g/galign.c +++ b/src/cmd/5g/galign.c @@ -25,7 +25,6 @@ Typedef typedefs[] = void betypeinit(void) { - maxround = 4; widthptr = 4; zprog.link = P; diff --git a/src/cmd/5g/gg.h b/src/cmd/5g/gg.h index c62efeb6c..603c09fc8 100644 --- a/src/cmd/5g/gg.h +++ b/src/cmd/5g/gg.h @@ -33,12 +33,13 @@ struct Addr struct Prog { - short as; // opcode + short as; // opcode uint32 loc; // pc offset in this func uint32 lineno; // source line that generated this Addr from; // src address - Addr to; // dst address + Addr to; // dst address Prog* link; // next instruction in this func + void* regp; // points to enclosing Reg struct char reg; // doubles as width in DATA op uchar scond; }; @@ -90,6 +91,7 @@ void ginscall(Node*, int); * cgen */ void agen(Node*, Node*); +Prog* cgenindex(Node *, Node *); void igen(Node*, Node*, Node*); void agenr(Node *n, Node *a, Node *res); vlong fieldoffset(Type*, Node*); diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c index 3243bb863..42a89415d 100644 --- a/src/cmd/5g/ggen.c +++ b/src/cmd/5g/ggen.c @@ -61,11 +61,14 @@ compile(Node *fn) pl = newplist(); pl->name = curfn->nname; + + setlineno(curfn); nodconst(&nod1, types[TINT32], 0); ptxt = gins(ATEXT, curfn->nname, &nod1); afunclit(&ptxt->from); + ginit(); genlist(curfn->enter); pret = nil; @@ -78,6 +81,7 @@ compile(Node *fn) } genlist(curfn->nbody); + gclean(); checklabels(); if(nerrors != 0) goto ret; @@ -87,29 +91,32 @@ compile(Node *fn) if(pret) patch(pret, pc); + ginit(); + if(hasdefer) + ginscall(deferreturn, 0); if(curfn->exit) genlist(curfn->exit); + gclean(); if(nerrors != 0) goto ret; - if(hasdefer) - ginscall(deferreturn, 0); + if(curfn->endlineno) + lineno = curfn->endlineno; pc->as = ARET; // overwrite AEND pc->lineno = lineno; - /* TODO(kaib): Add back register optimizations - if(!debug['N'] || debug['R'] || debug['P']) + if(!debug['N'] || debug['R'] || debug['P']) { regopt(ptxt); - */ + } // fill in argument size ptxt->to.type = D_CONST2; ptxt->reg = 0; // flags - ptxt->to.offset2 = rnd(curfn->type->argwid, maxround); + ptxt->to.offset2 = rnd(curfn->type->argwid, widthptr); // fill in final stack size if(stksize > maxstksize) maxstksize = stksize; - ptxt->to.offset = rnd(maxstksize+maxarg, maxround); + ptxt->to.offset = rnd(maxstksize+maxarg, widthptr); maxstksize = 0; if(debug['f']) @@ -203,6 +210,7 @@ ginscall(Node *f, int proc) void cgen_callinter(Node *n, Node *res, int proc) { + int r; Node *i, *f; Node tmpi, nodo, nodr, nodsp; @@ -216,6 +224,14 @@ cgen_callinter(Node *n, Node *res, int proc) i = i->left; // interface + // Release res register during genlist and cgen, + // which might have their own function calls. + r = -1; + if(res != N && (res->op == OREGISTER || res->op == OINDREG)) { + r = res->val.u.reg; + reg[r]--; + } + if(!i->addable) { tempname(&tmpi, i->type); cgen(i, &tmpi); @@ -223,6 +239,8 @@ cgen_callinter(Node *n, Node *res, int proc) } genlist(n->list); // args + if(r >= 0) + reg[r]++; regalloc(&nodr, types[tptr], res); regalloc(&nodo, types[tptr], &nodr); @@ -427,10 +445,10 @@ cgen_asop(Node *n) case OOR: a = optoas(n->etype, nl->type); if(nl->addable) { - regalloc(&n2, nl->type, N); regalloc(&n3, nr->type, N); - cgen(nl, &n2); cgen(nr, &n3); + regalloc(&n2, nl->type, N); + cgen(nl, &n2); gins(a, &n3, &n2); cgen(&n2, nl); regfree(&n2); @@ -439,13 +457,14 @@ cgen_asop(Node *n) } if(nr->ullman < UINF) if(sudoaddable(a, nl, &addr, &w)) { + w = optoas(OAS, nl->type); regalloc(&n2, nl->type, N); - regalloc(&n3, nr->type, N); - p1 = gins(AMOVW, N, &n2); + p1 = gins(w, N, &n2); p1->from = addr; + regalloc(&n3, nr->type, N); cgen(nr, &n3); gins(a, &n3, &n2); - p1 = gins(AMOVW, &n2, N); + p1 = gins(w, &n2, N); p1->to = addr; regfree(&n2); regfree(&n3); @@ -544,7 +563,7 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) cgen(nl, &n1); sc = mpgetfix(nr->val.u.xval); if(sc == 0) { - return; + // nothing to do } else if(sc >= nl->type->width*8) { if(op == ORSH && issigned[nl->type->etype]) gshift(AMOVW, &n1, SHIFT_AR, w, &n1); @@ -682,6 +701,29 @@ regcmp(const void *va, const void *vb) static Prog* throwpc; +// We're only going to bother inlining if we can +// convert all the arguments to 32 bits safely. Can we? +static int +fix64(NodeList *nn, int n) +{ + NodeList *l; + Node *r; + int i; + + l = nn; + for(i=0; i<n; i++) { + r = l->n->right; + if(is64(r->type) && !smallintconst(r)) { + if(r->op == OCONV) + r = r->left; + if(is64(r->type)) + return 0; + } + l = l->next; + } + return 1; +} + void getargs(NodeList *nn, Node *reg, int n) { @@ -710,7 +752,7 @@ getargs(NodeList *nn, Node *reg, int n) void cmpandthrow(Node *nl, Node *nr) { - vlong cl, cr; + vlong cl; Prog *p1; int op; Node *c, n1, n2; @@ -720,17 +762,8 @@ cmpandthrow(Node *nl, Node *nr) cl = mpgetfix(nl->val.u.xval); if(cl == 0) return; - if(smallintconst(nr)) { - cr = mpgetfix(nr->val.u.xval); - if(cl > cr) { - if(throwpc == nil) { - throwpc = pc; - ginscall(panicslice, 0); - } else - patch(gbranch(AB, T), throwpc); - } + if(smallintconst(nr)) return; - } // put the constant on the right op = brrev(op); @@ -794,6 +827,8 @@ cgen_inline(Node *n, Node *res) goto no; if(!n->left->addable) goto no; + if(n->left->sym == S) + goto no; if(n->left->sym->pkg != runtimepkg) goto no; if(strcmp(n->left->sym->name, "slicearray") == 0) @@ -811,6 +846,8 @@ cgen_inline(Node *n, Node *res) slicearray: if(!sleasy(res)) goto no; + if(!fix64(n->list, 5)) + goto no; getargs(n->list, nodes, 5); // if(hb[3] > nel[1]) goto throw @@ -902,6 +939,8 @@ slicearray: return 1; sliceslice: + if(!fix64(n->list, narg)) + goto no; ntemp.op = OXXX; if(!sleasy(n->list->n->right)) { Node *n0; diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c index c4564ed66..bf59534b9 100644 --- a/src/cmd/5g/gobj.c +++ b/src/cmd/5g/gobj.c @@ -633,10 +633,10 @@ dsymptr(Sym *s, int off, Sym *x, int xoff) void -genembedtramp(Type *rcvr, Type *method, Sym *newnam) +genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface) { // TODO(kaib): re-implement genembedtramp - genwrapper(rcvr, method, newnam); + genwrapper(rcvr, method, newnam, iface); /* Sym *e; int c, d, o; @@ -705,7 +705,7 @@ out: p->to.type = D_OREG; p->to.reg = NREG; p->to.name = D_EXTERN; - p->to.sym = methodsym(method->sym, ptrto(f->type)); + p->to.sym = methodsym(method->sym, ptrto(f->type), 0); //print("4. %P\n", p); pc->as = ARET; // overwrite AEND diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c index 700602c35..133a21b3e 100644 --- a/src/cmd/5g/gsubr.c +++ b/src/cmd/5g/gsubr.c @@ -197,9 +197,50 @@ afunclit(Addr *a) } } +static int resvd[] = +{ + 9, // reserved for m + 10, // reserved for g +}; + +void +ginit(void) +{ + int i; + + for(i=0; i<nelem(reg); i++) + reg[i] = 0; + for(i=0; i<nelem(resvd); i++) + reg[resvd[i]]++; +} + +void +gclean(void) +{ + int i; + + for(i=0; i<nelem(resvd); i++) + reg[resvd[i]]--; + + for(i=0; i<nelem(reg); i++) + if(reg[i]) + yyerror("reg %R left allocated\n", i); +} + int32 anyregalloc(void) { + int i, j; + + for(i=0; i<nelem(reg); i++) { + if(reg[i] == 0) + goto ok; + for(j=0; j<nelem(resvd); j++) + if(resvd[j] == i) + goto ok; + return 1; + ok:; + } return 0; } @@ -264,6 +305,11 @@ regalloc(Node *n, Type *t, Node *o) goto out; yyerror("out of floating point registers"); goto err; + + case TCOMPLEX64: + case TCOMPLEX128: + tempname(n, t); + return; } yyerror("regalloc: unknown type %T", t); @@ -293,6 +339,8 @@ regfree(Node *n) print("regalloc fix %d float %d\n", fixfree, floatfree); } + if(n->op == ONAME && iscomplex[n->type->etype]) + return; if(n->op != OREGISTER && n->op != OINDREG) fatal("regfree: not a register"); i = n->val.u.reg; @@ -498,6 +546,11 @@ gmove(Node *f, Node *t) tt = simsimtype(t->type); cvt = t->type; + if(iscomplex[ft] || iscomplex[tt]) { + complexmove(f, t); + return; + } + // cannot have two memory operands; // except 64-bit, which always copies via registers anyway. if(!is64(f->type) && !is64(t->type) && ismem(f) && ismem(t)) @@ -715,56 +768,109 @@ gmove(Node *f, Node *t) * float to integer */ case CASE(TFLOAT32, TINT8): - case CASE(TFLOAT32, TINT16): - case CASE(TFLOAT32, TINT32): case CASE(TFLOAT32, TUINT8): + case CASE(TFLOAT32, TINT16): case CASE(TFLOAT32, TUINT16): + case CASE(TFLOAT32, TINT32): case CASE(TFLOAT32, TUINT32): - fa = AMOVF; - a = AMOVFW; - ta = AMOVW; - goto fltconv; +// case CASE(TFLOAT32, TUINT64): case CASE(TFLOAT64, TINT8): - case CASE(TFLOAT64, TINT16): - case CASE(TFLOAT64, TINT32): case CASE(TFLOAT64, TUINT8): + case CASE(TFLOAT64, TINT16): case CASE(TFLOAT64, TUINT16): + case CASE(TFLOAT64, TINT32): case CASE(TFLOAT64, TUINT32): - fa = AMOVD; - a = AMOVDW; +// case CASE(TFLOAT64, TUINT64): + fa = AMOVF; + a = AMOVFW; + if(ft == TFLOAT64) { + fa = AMOVD; + a = AMOVDW; + } ta = AMOVW; - goto fltconv; + switch(tt) { + case TINT8: + ta = AMOVB; + break; + case TUINT8: + ta = AMOVBU; + break; + case TINT16: + ta = AMOVH; + break; + case TUINT16: + ta = AMOVHU; + break; + } - case CASE(TFLOAT32, TUINT64): - case CASE(TFLOAT64, TUINT64): - fatal("gmove TFLOAT, UINT64 not implemented"); + regalloc(&r1, types[ft], f); + regalloc(&r2, types[tt], t); + gins(fa, f, &r1); // load to fpu + p1 = gins(a, &r1, &r1); // convert to w + switch(tt) { + case TUINT8: + case TUINT16: + case TUINT32: + p1->scond |= C_UBIT; + } + gins(AMOVW, &r1, &r2); // copy to cpu + gins(ta, &r2, t); // store + regfree(&r1); + regfree(&r2); return; /* * integer to float */ case CASE(TINT8, TFLOAT32): - case CASE(TINT16, TFLOAT32): - case CASE(TINT32, TFLOAT32): case CASE(TUINT8, TFLOAT32): + case CASE(TINT16, TFLOAT32): case CASE(TUINT16, TFLOAT32): + case CASE(TINT32, TFLOAT32): case CASE(TUINT32, TFLOAT32): - fa = AMOVW; - a = AMOVWF; - ta = AMOVF; - goto fltconv; - case CASE(TINT8, TFLOAT64): - case CASE(TINT16, TFLOAT64): - case CASE(TINT32, TFLOAT64): case CASE(TUINT8, TFLOAT64): + case CASE(TINT16, TFLOAT64): case CASE(TUINT16, TFLOAT64): + case CASE(TINT32, TFLOAT64): case CASE(TUINT32, TFLOAT64): fa = AMOVW; - a = AMOVWD; - ta = AMOVD; - goto fltconv; + switch(ft) { + case TINT8: + fa = AMOVB; + break; + case TUINT8: + fa = AMOVBU; + break; + case TINT16: + fa = AMOVH; + break; + case TUINT16: + fa = AMOVHU; + break; + } + a = AMOVWF; + ta = AMOVF; + if(tt == TFLOAT64) { + a = AMOVWD; + ta = AMOVD; + } + regalloc(&r1, types[ft], f); + regalloc(&r2, types[tt], t); + gins(fa, f, &r1); // load to cpu + gins(AMOVW, &r1, &r2); // copy to fpu + p1 = gins(a, &r2, &r2); // convert + switch(ft) { + case TUINT8: + case TUINT16: + case TUINT32: + p1->scond |= C_UBIT; + } + gins(ta, &r2, t); // store + regfree(&r1); + regfree(&r2); + return; case CASE(TUINT64, TFLOAT32): case CASE(TUINT64, TFLOAT64): @@ -831,16 +937,6 @@ trunc64: splitclean(); return; -fltconv: - regalloc(&r1, types[ft], f); - regalloc(&r2, types[tt], t); - gins(fa, f, &r1); - gins(a, &r1, &r2); - gins(ta, &r2, t); - regfree(&r1); - regfree(&r2); - return; - fatal: // should not happen fatal("gmove %N -> %N", f, t); @@ -951,7 +1047,7 @@ gshift(int as, Node *lhs, int32 stype, int32 sval, Node *rhs) { Prog *p; - if (sval <= 0 || sval > 32) + if(sval <= 0 || sval > 32) fatal("bad shift value: %d", sval); sval = sval&0x1f; @@ -983,7 +1079,7 @@ checkoffset(Addr *a, int canemitcode) if(a->offset < unmappedzero) return; if(!canemitcode) - fatal("checkoffset %#llx, cannot emit code", a->offset); + fatal("checkoffset %#x, cannot emit code", a->offset); // cannot rely on unmapped nil page at 0 to catch // reference with large offset. instead, emit explicit @@ -1014,7 +1110,7 @@ naddr(Node *n, Addr *a, int canemitcode) break; case OREGISTER: - if (n->val.u.reg <= REGALLOC_RMAX) { + if(n->val.u.reg <= REGALLOC_RMAX) { a->type = D_REG; a->reg = n->val.u.reg; } else { @@ -1314,15 +1410,21 @@ optoas(int op, Type *t) case CASE(OAS, TBOOL): case CASE(OAS, TINT8): - case CASE(OAS, TUINT8): a = AMOVB; break; + case CASE(OAS, TUINT8): + a = AMOVBU; + break; + case CASE(OAS, TINT16): - case CASE(OAS, TUINT16): a = AMOVH; break; + case CASE(OAS, TUINT16): + a = AMOVHU; + break; + case CASE(OAS, TINT32): case CASE(OAS, TUINT32): case CASE(OAS, TPTR32): @@ -1554,7 +1656,7 @@ sudoaddable(int as, Node *n, Addr *a, int *w) int64 v; Node n1, n2, n3, n4, *nn, *l, *r; Node *reg, *reg1; - Prog *p1; + Prog *p1, *p2; Type *t; if(n->type == T) @@ -1579,6 +1681,8 @@ sudoaddable(int as, Node *n, Addr *a, int *w) goto odot; case OINDEX: + if(n->left->type->etype == TSTRING) + return 0; cleani += 2; reg = &clean[cleani-1]; reg1 = &clean[cleani-2]; @@ -1692,8 +1796,8 @@ oindex: if(issigned[r->type->etype]) t = types[TINT32]; regalloc(reg1, t, N); - regalloc(&n3, r->type, reg1); - cgen(r, &n3); + regalloc(&n3, types[TINT32], reg1); + p2 = cgenindex(r, &n3); gmove(&n3, reg1); regfree(&n3); @@ -1734,6 +1838,8 @@ oindex: gcmp(optoas(OCMP, types[TUINT32]), reg1, &n3); regfree(&n3); p1 = gbranch(optoas(OLT, types[TUINT32]), T); + if(p2) + patch(p2, pc); ginscall(panicindex, 0); patch(p1, pc); } @@ -1746,14 +1852,20 @@ oindex: gmove(&n2, reg); } - if (*w == 1) + switch(*w) { + case 1: gins(AADD, reg1, reg); - else if(*w == 2) + break; + case 2: gshift(AADD, reg1, SHIFT_LL, 1, reg); - else if(*w == 4) + break; + case 4: gshift(AADD, reg1, SHIFT_LL, 2, reg); - else if(*w == 8) + break; + case 8: gshift(AADD, reg1, SHIFT_LL, 3, reg); + break; + } naddr(reg1, a, 1); a->type = D_OREG; @@ -1799,19 +1911,6 @@ oindex_const: n1.type = types[tptr]; n1.xoffset = Array_array; gmove(&n1, reg); - - } else - if(!debug['B']) { - if(v < 0) { - yyerror("out of bounds on array"); - } else - if(o & OPtrto) { - if(v >= l->type->type->bound) - yyerror("out of bounds on array"); - } else - if(v >= l->type->bound) { - yyerror("out of bounds on array"); - } } n2 = *reg; diff --git a/src/cmd/5g/list.c b/src/cmd/5g/list.c index 19027829c..ce74d6478 100644 --- a/src/cmd/5g/list.c +++ b/src/cmd/5g/list.c @@ -49,28 +49,28 @@ listinit(void) int Pconv(Fmt *fp) { - char str[STRINGSZ]; + char str[STRINGSZ], str1[STRINGSZ]; Prog *p; p = va_arg(fp->args, Prog*); sconsize = 8; switch(p->as) { default: + snprint(str1, sizeof(str1), "%A%C", p->as, p->scond); if(p->reg == NREG) - snprint(str, sizeof(str), "%.4ld (%L) %-7A%C %D,%D", - p->loc, p->lineno, p->as, p->scond, &p->from, &p->to); - else if (p->from.type != D_FREG) - snprint(str, sizeof(str), "%.4ld (%L) %-7A%C %D,R%d,%D", - p->loc, p->lineno, p->as, p->scond, &p->from, p->reg, &p->to); + snprint(str, sizeof(str), "%.4d (%L) %-7s %D,%D", + p->loc, p->lineno, str1, &p->from, &p->to); else - snprint(str, sizeof(str), "%.4ld (%L) %-7A%C %D,F%d,%D", + if (p->from.type != D_FREG) { + snprint(str, sizeof(str), "%.4d (%L) %-7s %D,R%d,%D", + p->loc, p->lineno, str1, &p->from, p->reg, &p->to); + } else + snprint(str, sizeof(str), "%.4d (%L) %-7A%C %D,F%d,%D", p->loc, p->lineno, p->as, p->scond, &p->from, p->reg, &p->to); break; case ADATA: - case AINIT: - case ADYNT: - snprint(str, sizeof(str), "%.4ld (%L) %-7A %D/%d,%D", + snprint(str, sizeof(str), "%.4d (%L) %-7A %D/%d,%D", p->loc, p->lineno, p->as, &p->from, p->reg, &p->to); break; } @@ -154,10 +154,16 @@ Dconv(Fmt *fp) break; case D_BRANCH: - if(a->sym != S) - sprint(str, "%s+%d(APC)", a->sym->name, a->offset); - else - sprint(str, "%d(APC)", a->offset); + if(a->branch == P || a->branch->loc == 0) { + if(a->sym != S) + sprint(str, "%s+%d(APC)", a->sym->name, a->offset); + else + sprint(str, "%d(APC)", a->offset); + } else + if(a->sym != S) + sprint(str, "%s+%d(APC)", a->sym->name, a->branch->loc); + else + sprint(str, "%d(APC)", a->branch->loc); break; case D_FCONST: diff --git a/src/cmd/5g/opt.h b/src/cmd/5g/opt.h index 1b0366290..9a4e17571 100644 --- a/src/cmd/5g/opt.h +++ b/src/cmd/5g/opt.h @@ -128,7 +128,7 @@ Reg* rega(void); int rcmp(const void*, const void*); void regopt(Prog*); void addmove(Reg*, int, int, int); -Bits mkvar(Reg*, Adr*); +Bits mkvar(Reg *r, Adr *a, int); void prop(Reg*, Bits, Bits); void loopit(Reg*, int32); void synch(Reg*, Bits); diff --git a/src/cmd/5g/peep.c b/src/cmd/5g/peep.c new file mode 100644 index 000000000..32333e8a9 --- /dev/null +++ b/src/cmd/5g/peep.c @@ -0,0 +1,1511 @@ +// Inferno utils/5c/peep.c +// http://code.google.com/p/inferno-os/source/browse/utils/5g/peep.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#include "gg.h" +#include "opt.h" + +int xtramodes(Reg*, Adr*); +int shiftprop(Reg *r); +void constprop(Adr *c1, Adr *v1, Reg *r); +void predicate(void); +int copyau1(Prog *p, Adr *v); +int isdconst(Addr *a); + +void +peep(void) +{ + Reg *r, *r1, *r2; + Prog *p, *p1; + int t; +/* + * complete R structure + */ + t = 0; + for(r=firstr; r!=R; r=r1) { + r1 = r->link; + if(r1 == R) + break; + p = r->prog->link; + while(p != r1->prog) + switch(p->as) { + default: + r2 = rega(); + r->link = r2; + r2->link = r1; + + r2->prog = p; + r2->p1 = r; + r->s1 = r2; + r2->s1 = r1; + r1->p1 = r2; + + r = r2; + t++; + + case ADATA: + case AGLOBL: + case ANAME: + case ASIGNAME: + p = p->link; + } + } + +loop1: + t = 0; + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + switch(p->as) { + case ASLL: + case ASRL: + case ASRA: + /* + * elide shift into D_SHIFT operand of subsequent instruction + */ + if(shiftprop(r)) { + excise(r); + t++; + break; + } + break; + + case AMOVW: + case AMOVF: + case AMOVD: + if(!regtyp(&p->to)) + break; + if(isdconst(&p->from)) { + constprop(&p->from, &p->to, r->s1); + break; + } + if(!regtyp(&p->from)) + break; + if(p->from.type != p->to.type) + break; + if(copyprop(r)) { + excise(r); + t++; + break; + } + if(subprop(r) && copyprop(r)) { + excise(r); + t++; + break; + } + } + } + if(t) + goto loop1; + /* + * look for MOVB x,R; MOVB R,R + */ + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + switch(p->as) { + default: + continue; + case AEOR: + /* + * EOR -1,x,y => MVN x,y + */ + if(isdconst(&p->from) && p->from.offset == -1) { + p->as = AMVN; + p->from.type = D_REG; + if(p->reg != NREG) + p->from.reg = p->reg; + else + p->from.reg = p->to.reg; + p->reg = NREG; + } + continue; + case AMOVH: + case AMOVHU: + case AMOVB: + case AMOVBU: + if(p->to.type != D_REG) + continue; + break; + } + r1 = r->link; + if(r1 == R) + continue; + p1 = r1->prog; + if(p1->as != p->as) + continue; + if(p1->from.type != D_REG || p1->from.reg != p->to.reg) + continue; + if(p1->to.type != D_REG || p1->to.reg != p->to.reg) + continue; + excise(r1); + } + + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + switch(p->as) { + case AMOVW: + case AMOVB: + case AMOVBU: + if(p->from.type == D_OREG && p->from.offset == 0) + xtramodes(r, &p->from); + else if(p->to.type == D_OREG && p->to.offset == 0) + xtramodes(r, &p->to); + else + continue; + break; + case ACMP: + /* + * elide CMP $0,x if calculation of x can set condition codes + */ + if(isdconst(&p->from) || p->from.offset != 0) + continue; + r2 = r->s1; + if(r2 == R) + continue; + t = r2->prog->as; + switch(t) { + default: + continue; + case ABEQ: + case ABNE: + case ABMI: + case ABPL: + break; + case ABGE: + t = ABPL; + break; + case ABLT: + t = ABMI; + break; + case ABHI: + t = ABNE; + break; + case ABLS: + t = ABEQ; + break; + } + r1 = r; + do + r1 = uniqp(r1); + while (r1 != R && r1->prog->as == ANOP); + if(r1 == R) + continue; + p1 = r1->prog; + if(p1->to.type != D_REG) + continue; + if(p1->to.reg != p->reg) + if(!(p1->as == AMOVW && p1->from.type == D_REG && p1->from.reg == p->reg)) + continue; + switch(p1->as) { + default: + continue; + case AMOVW: + if(p1->from.type != D_REG) + continue; + case AAND: + case AEOR: + case AORR: + case ABIC: + case AMVN: + case ASUB: + case ARSB: + case AADD: + case AADC: + case ASBC: + case ARSC: + break; + } + p1->scond |= C_SBIT; + r2->prog->as = t; + excise(r); + continue; + } + } + + predicate(); +} + +Reg* +uniqp(Reg *r) +{ + Reg *r1; + + r1 = r->p1; + if(r1 == R) { + r1 = r->p2; + if(r1 == R || r1->p2link != R) + return R; + } else + if(r->p2 != R) + return R; + return r1; +} + +Reg* +uniqs(Reg *r) +{ + Reg *r1; + + r1 = r->s1; + if(r1 == R) { + r1 = r->s2; + if(r1 == R) + return R; + } else + if(r->s2 != R) + return R; + return r1; +} + +int +regtyp(Adr *a) +{ + + if(a->type == D_REG) + return 1; + if(a->type == D_FREG) + return 1; + return 0; +} + +/* + * the idea is to substitute + * one register for another + * from one MOV to another + * MOV a, R0 + * ADD b, R0 / no use of R1 + * MOV R0, R1 + * would be converted to + * MOV a, R1 + * ADD b, R1 + * MOV R1, R0 + * hopefully, then the former or latter MOV + * will be eliminated by copy propagation. + */ +int +subprop(Reg *r0) +{ + Prog *p; + Adr *v1, *v2; + Reg *r; + int t; + + p = r0->prog; + v1 = &p->from; + if(!regtyp(v1)) + return 0; + v2 = &p->to; + if(!regtyp(v2)) + return 0; + for(r=uniqp(r0); r!=R; r=uniqp(r)) { + if(uniqs(r) == R) + break; + p = r->prog; + switch(p->as) { + case ABL: + return 0; + + case ACMP: + case ACMN: + case AADD: + case ASUB: + case ARSB: + case ASLL: + case ASRL: + case ASRA: + case AORR: + case AAND: + case AEOR: + case AMUL: + case ADIV: + case ADIVU: + + case ACMPF: + case ACMPD: + case AADDD: + case AADDF: + case ASUBD: + case ASUBF: + case AMULD: + case AMULF: + case ADIVD: + case ADIVF: + if(p->to.type == v1->type) + if(p->to.reg == v1->reg) { + if(p->reg == NREG) + p->reg = p->to.reg; + goto gotit; + } + break; + + case AMOVF: + case AMOVD: + case AMOVW: + if(p->to.type == v1->type) + if(p->to.reg == v1->reg) + goto gotit; + break; + + case AMOVM: + t = 1<<v2->reg; + if((p->from.type == D_CONST && (p->from.offset&t)) || + (p->to.type == D_CONST && (p->to.offset&t))) + return 0; + break; + } + if(copyau(&p->from, v2) || + copyau1(p, v2) || + copyau(&p->to, v2)) + break; + if(copysub(&p->from, v1, v2, 0) || + copysub1(p, v1, v2, 0) || + copysub(&p->to, v1, v2, 0)) + break; + } + return 0; + +gotit: + copysub(&p->to, v1, v2, 1); + if(debug['P']) { + print("gotit: %D->%D\n%P", v1, v2, r->prog); + if(p->from.type == v2->type) + print(" excise"); + print("\n"); + } + for(r=uniqs(r); r!=r0; r=uniqs(r)) { + p = r->prog; + copysub(&p->from, v1, v2, 1); + copysub1(p, v1, v2, 1); + copysub(&p->to, v1, v2, 1); + if(debug['P']) + print("%P\n", r->prog); + } + t = v1->reg; + v1->reg = v2->reg; + v2->reg = t; + if(debug['P']) + print("%P last\n", r->prog); + return 1; +} + +/* + * The idea is to remove redundant copies. + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * use v2 return fail + * ----------------- + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * set v2 return success + */ +int +copyprop(Reg *r0) +{ + Prog *p; + Adr *v1, *v2; + Reg *r; + + p = r0->prog; + v1 = &p->from; + v2 = &p->to; + if(copyas(v1, v2)) + return 1; + for(r=firstr; r!=R; r=r->link) + r->active = 0; + return copy1(v1, v2, r0->s1, 0); +} + +int +copy1(Adr *v1, Adr *v2, Reg *r, int f) +{ + int t; + Prog *p; + + if(r->active) { + if(debug['P']) + print("act set; return 1\n"); + return 1; + } + r->active = 1; + if(debug['P']) + print("copy %D->%D f=%d\n", v1, v2, f); + for(; r != R; r = r->s1) { + p = r->prog; + if(debug['P']) + print("%P", p); + if(!f && uniqp(r) == R) { + f = 1; + if(debug['P']) + print("; merge; f=%d", f); + } + t = copyu(p, v2, A); + switch(t) { + case 2: /* rar, cant split */ + if(debug['P']) + print("; %Drar; return 0\n", v2); + return 0; + + case 3: /* set */ + if(debug['P']) + print("; %Dset; return 1\n", v2); + return 1; + + case 1: /* used, substitute */ + case 4: /* use and set */ + if(f) { + if(!debug['P']) + return 0; + if(t == 4) + print("; %Dused+set and f=%d; return 0\n", v2, f); + else + print("; %Dused and f=%d; return 0\n", v2, f); + return 0; + } + if(copyu(p, v2, v1)) { + if(debug['P']) + print("; sub fail; return 0\n"); + return 0; + } + if(debug['P']) + print("; sub%D/%D", v2, v1); + if(t == 4) { + if(debug['P']) + print("; %Dused+set; return 1\n", v2); + return 1; + } + break; + } + if(!f) { + t = copyu(p, v1, A); + if(!f && (t == 2 || t == 3 || t == 4)) { + f = 1; + if(debug['P']) + print("; %Dset and !f; f=%d", v1, f); + } + } + if(debug['P']) + print("\n"); + if(r->s2) + if(!copy1(v1, v2, r->s2, f)) + return 0; + } + return 1; +} + +/* + * The idea is to remove redundant constants. + * $c1->v1 + * ($c1->v2 s/$c1/v1)* + * set v1 return + * The v1->v2 should be eliminated by copy propagation. + */ +void +constprop(Adr *c1, Adr *v1, Reg *r) +{ + Prog *p; + + if(debug['P']) + print("constprop %D->%D\n", c1, v1); + for(; r != R; r = r->s1) { + p = r->prog; + if(debug['P']) + print("%P", p); + if(uniqp(r) == R) { + if(debug['P']) + print("; merge; return\n"); + return; + } + if(p->as == AMOVW && copyas(&p->from, c1)) { + if(debug['P']) + print("; sub%D/%D", &p->from, v1); + p->from = *v1; + } else if(copyu(p, v1, A) > 1) { + if(debug['P']) + print("; %Dset; return\n", v1); + return; + } + if(debug['P']) + print("\n"); + if(r->s2) + constprop(c1, v1, r->s2); + } +} + +/* + * ASLL x,y,w + * .. (not use w, not set x y w) + * AXXX w,a,b (a != w) + * .. (not use w) + * (set w) + * ----------- changed to + * .. + * AXXX (x<<y),a,b + * .. + */ +#define FAIL(msg) { if(debug['P']) print("\t%s; FAILURE\n", msg); return 0; } +int +shiftprop(Reg *r) +{ + Reg *r1; + Prog *p, *p1, *p2; + int n, o; + Adr a; + + p = r->prog; + if(p->to.type != D_REG) + FAIL("BOTCH: result not reg"); + n = p->to.reg; + a = zprog.from; + if(p->reg != NREG && p->reg != p->to.reg) { + a.type = D_REG; + a.reg = p->reg; + } + if(debug['P']) + print("shiftprop\n%P", p); + r1 = r; + for(;;) { + /* find first use of shift result; abort if shift operands or result are changed */ + r1 = uniqs(r1); + if(r1 == R) + FAIL("branch"); + if(uniqp(r1) == R) + FAIL("merge"); + p1 = r1->prog; + if(debug['P']) + print("\n%P", p1); + switch(copyu(p1, &p->to, A)) { + case 0: /* not used or set */ + if((p->from.type == D_REG && copyu(p1, &p->from, A) > 1) || + (a.type == D_REG && copyu(p1, &a, A) > 1)) + FAIL("args modified"); + continue; + case 3: /* set, not used */ + FAIL("BOTCH: noref"); + } + break; + } + /* check whether substitution can be done */ + switch(p1->as) { + default: + FAIL("non-dpi"); + case AAND: + case AEOR: + case AADD: + case AADC: + case AORR: + case ASUB: + case ARSB: + case ASBC: + case ARSC: + if(p1->reg == n || (p1->reg == NREG && p1->to.type == D_REG && p1->to.reg == n)) { + if(p1->from.type != D_REG) + FAIL("can't swap"); + p1->reg = p1->from.reg; + p1->from.reg = n; + switch(p1->as) { + case ASUB: + p1->as = ARSB; + break; + case ARSB: + p1->as = ASUB; + break; + case ASBC: + p1->as = ARSC; + break; + case ARSC: + p1->as = ASBC; + break; + } + if(debug['P']) + print("\t=>%P", p1); + } + case ABIC: + case ACMP: + case ACMN: + if(p1->reg == n) + FAIL("can't swap"); + if(p1->reg == NREG && p1->to.reg == n) + FAIL("shift result used twice"); + case AMVN: + if(p1->from.type == D_SHIFT) + FAIL("shift result used in shift"); + if(p1->from.type != D_REG || p1->from.reg != n) + FAIL("BOTCH: where is it used?"); + break; + } + /* check whether shift result is used subsequently */ + p2 = p1; + if(p1->to.reg != n) + for (;;) { + r1 = uniqs(r1); + if(r1 == R) + FAIL("inconclusive"); + p1 = r1->prog; + if(debug['P']) + print("\n%P", p1); + switch(copyu(p1, &p->to, A)) { + case 0: /* not used or set */ + continue; + case 3: /* set, not used */ + break; + default:/* used */ + FAIL("reused"); + } + break; + } + + /* make the substitution */ + p2->from.type = D_SHIFT; + p2->from.reg = NREG; + o = p->reg; + if(o == NREG) + o = p->to.reg; + + switch(p->from.type){ + case D_CONST: + o |= (p->from.offset&0x1f)<<7; + break; + case D_REG: + o |= (1<<4) | (p->from.reg<<8); + break; + } + switch(p->as){ + case ASLL: + o |= 0<<5; + break; + case ASRL: + o |= 1<<5; + break; + case ASRA: + o |= 2<<5; + break; + } + p2->from.offset = o; + if(debug['P']) + print("\t=>%P\tSUCCEED\n", p2); + return 1; +} + +Reg* +findpre(Reg *r, Adr *v) +{ + Reg *r1; + + for(r1=uniqp(r); r1!=R; r=r1,r1=uniqp(r)) { + if(uniqs(r1) != r) + return R; + switch(copyu(r1->prog, v, A)) { + case 1: /* used */ + case 2: /* read-alter-rewrite */ + return R; + case 3: /* set */ + case 4: /* set and used */ + return r1; + } + } + return R; +} + +Reg* +findinc(Reg *r, Reg *r2, Adr *v) +{ + Reg *r1; + Prog *p; + + + for(r1=uniqs(r); r1!=R && r1!=r2; r=r1,r1=uniqs(r)) { + if(uniqp(r1) != r) + return R; + switch(copyu(r1->prog, v, A)) { + case 0: /* not touched */ + continue; + case 4: /* set and used */ + p = r1->prog; + if(p->as == AADD) + if(isdconst(&p->from)) + if(p->from.offset > -4096 && p->from.offset < 4096) + return r1; + default: + return R; + } + } + return R; +} + +int +nochange(Reg *r, Reg *r2, Prog *p) +{ + Adr a[3]; + int i, n; + + if(r == r2) + return 1; + n = 0; + if(p->reg != NREG && p->reg != p->to.reg) { + a[n].type = D_REG; + a[n++].reg = p->reg; + } + switch(p->from.type) { + case D_SHIFT: + a[n].type = D_REG; + a[n++].reg = p->from.offset&0xf; + case D_REG: + a[n].type = D_REG; + a[n++].reg = p->from.reg; + } + if(n == 0) + return 1; + for(; r!=R && r!=r2; r=uniqs(r)) { + p = r->prog; + for(i=0; i<n; i++) + if(copyu(p, &a[i], A) > 1) + return 0; + } + return 1; +} + +int +findu1(Reg *r, Adr *v) +{ + for(; r != R; r = r->s1) { + if(r->active) + return 0; + r->active = 1; + switch(copyu(r->prog, v, A)) { + case 1: /* used */ + case 2: /* read-alter-rewrite */ + case 4: /* set and used */ + return 1; + case 3: /* set */ + return 0; + } + if(r->s2) + if (findu1(r->s2, v)) + return 1; + } + return 0; +} + +int +finduse(Reg *r, Adr *v) +{ + Reg *r1; + + for(r1=firstr; r1!=R; r1=r1->link) + r1->active = 0; + return findu1(r, v); +} + +int +xtramodes(Reg *r, Adr *a) +{ + Reg *r1, *r2, *r3; + Prog *p, *p1; + Adr v; + + p = r->prog; + if(debug['h'] && p->as == AMOVB && p->from.type == D_OREG) /* byte load */ + return 0; + v = *a; + v.type = D_REG; + r1 = findpre(r, &v); + if(r1 != R) { + p1 = r1->prog; + if(p1->to.type == D_REG && p1->to.reg == v.reg) + switch(p1->as) { + case AADD: + if(p1->from.type == D_REG || + (p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 && + (p->as != AMOVB || (a == &p->from && (p1->from.offset&~0xf) == 0))) || + (p1->from.type == D_CONST && + p1->from.offset > -4096 && p1->from.offset < 4096)) + if(nochange(uniqs(r1), r, p1)) { + if(a != &p->from || v.reg != p->to.reg) + if (finduse(r->s1, &v)) { + if(p1->reg == NREG || p1->reg == v.reg) + /* pre-indexing */ + p->scond |= C_WBIT; + else return 0; + } + switch (p1->from.type) { + case D_REG: + /* register offset */ + a->type = D_SHIFT; + a->offset = p1->from.reg; + break; + case D_SHIFT: + /* scaled register offset */ + a->type = D_SHIFT; + case D_CONST: + /* immediate offset */ + a->offset = p1->from.offset; + break; + } + if(p1->reg != NREG) + a->reg = p1->reg; + excise(r1); + return 1; + } + break; + case AMOVW: + if(p1->from.type == D_REG) + if((r2 = findinc(r1, r, &p1->from)) != R) { + for(r3=uniqs(r2); r3->prog->as==ANOP; r3=uniqs(r3)) + ; + if(r3 == r) { + /* post-indexing */ + p1 = r2->prog; + a->reg = p1->to.reg; + a->offset = p1->from.offset; + p->scond |= C_PBIT; + if(!finduse(r, &r1->prog->to)) + excise(r1); + excise(r2); + return 1; + } + } + break; + } + } + if(a != &p->from || a->reg != p->to.reg) + if((r1 = findinc(r, R, &v)) != R) { + /* post-indexing */ + p1 = r1->prog; + a->offset = p1->from.offset; + p->scond |= C_PBIT; + excise(r1); + return 1; + } + return 0; +} + +/* + * return + * 1 if v only used (and substitute), + * 2 if read-alter-rewrite + * 3 if set + * 4 if set and used + * 0 otherwise (not touched) + */ +int +copyu(Prog *p, Adr *v, Adr *s) +{ + + switch(p->as) { + + default: + if(debug['P']) + print(" (?)"); + return 2; + + case AMOVM: + if(v->type != D_REG) + return 0; + if(p->from.type == D_CONST) { /* read reglist, read/rar */ + if(s != A) { + if(p->from.offset&(1<<v->reg)) + return 1; + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyau(&p->to, v)) { + if(p->scond&C_WBIT) + return 2; + return 1; + } + if(p->from.offset&(1<<v->reg)) + return 1; + } else { /* read/rar, write reglist */ + if(s != A) { + if(p->to.offset&(1<<v->reg)) + return 1; + if(copysub(&p->from, v, s, 1)) + return 1; + return 0; + } + if(copyau(&p->from, v)) { + if(p->scond&C_WBIT) + return 2; + if(p->to.offset&(1<<v->reg)) + return 4; + return 1; + } + if(p->to.offset&(1<<v->reg)) + return 3; + } + return 0; + + case ANOP: /* read, write */ + case AMOVW: + case AMOVF: + case AMOVD: + case AMOVH: + case AMOVHU: + case AMOVB: + case AMOVBU: + case AMOVDW: + case AMOVWD: + case AMOVFD: + case AMOVDF: + if(p->scond&(C_WBIT|C_PBIT)) + if(v->type == D_REG) { + if(p->from.type == D_OREG || p->from.type == D_SHIFT) { + if(p->from.reg == v->reg) + return 2; + } else { + if(p->to.reg == v->reg) + return 2; + } + } + if(s != A) { + if(copysub(&p->from, v, s, 1)) + return 1; + if(!copyas(&p->to, v)) + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyas(&p->to, v)) { + if(copyau(&p->from, v)) + return 4; + return 3; + } + if(copyau(&p->from, v)) + return 1; + if(copyau(&p->to, v)) + return 1; + return 0; + + case AADD: /* read, read, write */ + case ASUB: + case ARSB: + case ASLL: + case ASRL: + case ASRA: + case AORR: + case AAND: + case AEOR: + case AMUL: + case ADIV: + case ADIVU: + case AADDF: + case AADDD: + case ASUBF: + case ASUBD: + case AMULF: + case AMULD: + case ADIVF: + case ADIVD: + + case ACMPF: + case ACMPD: + case ACMP: + case ACMN: + case ACASE: + if(s != A) { + if(copysub(&p->from, v, s, 1)) + return 1; + if(copysub1(p, v, s, 1)) + return 1; + if(!copyas(&p->to, v)) + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyas(&p->to, v)) { + if(p->reg == NREG) + p->reg = p->to.reg; + if(copyau(&p->from, v)) + return 4; + if(copyau1(p, v)) + return 4; + return 3; + } + if(copyau(&p->from, v)) + return 1; + if(copyau1(p, v)) + return 1; + if(copyau(&p->to, v)) + return 1; + return 0; + + case ABEQ: /* read, read */ + case ABNE: + case ABCS: + case ABHS: + case ABCC: + case ABLO: + case ABMI: + case ABPL: + case ABVS: + case ABVC: + case ABHI: + case ABLS: + case ABGE: + case ABLT: + case ABGT: + case ABLE: + if(s != A) { + if(copysub(&p->from, v, s, 1)) + return 1; + return copysub1(p, v, s, 1); + } + if(copyau(&p->from, v)) + return 1; + if(copyau1(p, v)) + return 1; + return 0; + + case AB: /* funny */ + if(s != A) { + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyau(&p->to, v)) + return 1; + return 0; + + case ARET: /* funny */ + if(v->type == D_REG) + if(v->reg == REGRET) + return 2; + if(v->type == D_FREG) + if(v->reg == FREGRET) + return 2; + + case ABL: /* funny */ + if(v->type == D_REG) { + if(v->reg <= REGEXT && v->reg > exregoffset) + return 2; + if(v->reg == REGARG) + return 2; + } + if(v->type == D_FREG) + if(v->reg <= FREGEXT && v->reg > exfregoffset) + return 2; + + if(s != A) { + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyau(&p->to, v)) + return 4; + return 3; + + case ATEXT: /* funny */ + if(v->type == D_REG) + if(v->reg == REGARG) + return 3; + return 0; + } + return 0; +} + +int +a2type(Prog *p) +{ + + switch(p->as) { + + case ACMP: + case ACMN: + + case AADD: + case ASUB: + case ARSB: + case ASLL: + case ASRL: + case ASRA: + case AORR: + case AAND: + case AEOR: + case AMUL: + case ADIV: + case ADIVU: + return D_REG; + + case ACMPF: + case ACMPD: + + case AADDF: + case AADDD: + case ASUBF: + case ASUBD: + case AMULF: + case AMULD: + case ADIVF: + case ADIVD: + return D_FREG; + } + return D_NONE; +} + +/* + * direct reference, + * could be set/use depending on + * semantics + */ +int +copyas(Adr *a, Adr *v) +{ + + if(regtyp(v)) { + if(a->type == v->type) + if(a->reg == v->reg) + return 1; + } else + if(v->type == D_CONST) { /* for constprop */ + if(a->type == v->type) + if(a->name == v->name) + if(a->sym == v->sym) + if(a->reg == v->reg) + if(a->offset == v->offset) + return 1; + } + return 0; +} + +/* + * either direct or indirect + */ +int +copyau(Adr *a, Adr *v) +{ + + if(copyas(a, v)) + return 1; + if(v->type == D_REG) { + if(a->type == D_CONST && a->reg != NREG) { + if(v->reg == a->reg) + return 1; + } else + if(a->type == D_OREG) { + if(v->reg == a->reg) + return 1; + } else + if(a->type == D_REGREG) { + if(v->reg == a->reg) + return 1; + if(a->offset == v->reg) + return 1; + } else + if(a->type == D_SHIFT) { + if((a->offset&0xf) == v->reg) + return 1; + if((a->offset&(1<<4)) && (a->offset>>8) == v->reg) + return 1; + } + } + return 0; +} + +int +copyau1(Prog *p, Adr *v) +{ + + if(regtyp(v)) { + if(a2type(p) == v->type) + if(p->reg == v->reg) { + if(a2type(p) != v->type) + print("botch a2type %P\n", p); + return 1; + } + } + return 0; +} + +/* + * substitute s for v in a + * return failure to substitute + */ +int +copysub(Adr *a, Adr *v, Adr *s, int f) +{ + + if(f) + if(copyau(a, v)) { + if(a->type == D_SHIFT) { + if((a->offset&0xf) == v->reg) + a->offset = (a->offset&~0xf)|s->reg; + if((a->offset&(1<<4)) && (a->offset>>8) == v->reg) + a->offset = (a->offset&~(0xf<<8))|(s->reg<<8); + } else + if(a->type == D_REGREG) { + if(a->offset == v->reg) + a->offset = s->reg; + if(a->reg == v->reg) + a->reg = s->reg; + } else + a->reg = s->reg; + } + return 0; +} + +int +copysub1(Prog *p1, Adr *v, Adr *s, int f) +{ + + if(f) + if(copyau1(p1, v)) + p1->reg = s->reg; + return 0; +} + +struct { + int opcode; + int notopcode; + int scond; + int notscond; +} predinfo[] = { + { ABEQ, ABNE, 0x0, 0x1, }, + { ABNE, ABEQ, 0x1, 0x0, }, + { ABCS, ABCC, 0x2, 0x3, }, + { ABHS, ABLO, 0x2, 0x3, }, + { ABCC, ABCS, 0x3, 0x2, }, + { ABLO, ABHS, 0x3, 0x2, }, + { ABMI, ABPL, 0x4, 0x5, }, + { ABPL, ABMI, 0x5, 0x4, }, + { ABVS, ABVC, 0x6, 0x7, }, + { ABVC, ABVS, 0x7, 0x6, }, + { ABHI, ABLS, 0x8, 0x9, }, + { ABLS, ABHI, 0x9, 0x8, }, + { ABGE, ABLT, 0xA, 0xB, }, + { ABLT, ABGE, 0xB, 0xA, }, + { ABGT, ABLE, 0xC, 0xD, }, + { ABLE, ABGT, 0xD, 0xC, }, +}; + +typedef struct { + Reg *start; + Reg *last; + Reg *end; + int len; +} Joininfo; + +enum { + Join, + Split, + End, + Branch, + Setcond, + Toolong +}; + +enum { + Falsecond, + Truecond, + Delbranch, + Keepbranch +}; + +int +isbranch(Prog *p) +{ + return (ABEQ <= p->as) && (p->as <= ABLE); +} + +int +predicable(Prog *p) +{ + switch(p->as) { + case ANOP: + case AXXX: + case ADATA: + case AGLOBL: + case AGOK: + case AHISTORY: + case ANAME: + case ASIGNAME: + case ATEXT: + case AWORD: + case ABCASE: + case ACASE: + return 0; + } + if(isbranch(p)) + return 0; + return 1; +} + +/* + * Depends on an analysis of the encodings performed by 5l. + * These seem to be all of the opcodes that lead to the "S" bit + * being set in the instruction encodings. + * + * C_SBIT may also have been set explicitly in p->scond. + */ +int +modifiescpsr(Prog *p) +{ + switch(p->as) { + case ATST: + case ATEQ: + case ACMN: + case ACMP: + case AMULU: + case ADIVU: + case AMUL: + case ADIV: + case AMOD: + case AMODU: + case ABL: + return 1; + } + if(p->scond & C_SBIT) + return 1; + return 0; +} + +/* + * Find the maximal chain of instructions starting with r which could + * be executed conditionally + */ +int +joinsplit(Reg *r, Joininfo *j) +{ + j->start = r; + j->last = r; + j->len = 0; + do { + if (r->p2 && (r->p1 || r->p2->p2link)) { + j->end = r; + return Join; + } + if (r->s1 && r->s2) { + j->end = r; + return Split; + } + j->last = r; + if (r->prog->as != ANOP) + j->len++; + if (!r->s1 && !r->s2) { + j->end = r->link; + return End; + } + if (r->s2) { + j->end = r->s2; + return Branch; + } + if (modifiescpsr(r->prog)) { + j->end = r->s1; + return Setcond; + } + r = r->s1; + } while (j->len < 4); + j->end = r; + return Toolong; +} + +Reg* +successor(Reg *r) +{ + if(r->s1) + return r->s1; + else + return r->s2; +} + +void +applypred(Reg *rstart, Joininfo *j, int cond, int branch) +{ + int pred; + Reg *r; + + if(j->len == 0) + return; + if(cond == Truecond) + pred = predinfo[rstart->prog->as - ABEQ].scond; + else + pred = predinfo[rstart->prog->as - ABEQ].notscond; + + for(r = j->start;; r = successor(r)) { + if (r->prog->as == AB) { + if (r != j->last || branch == Delbranch) + excise(r); + else { + if (cond == Truecond) + r->prog->as = predinfo[rstart->prog->as - ABEQ].opcode; + else + r->prog->as = predinfo[rstart->prog->as - ABEQ].notopcode; + } + } + else + if (predicable(r->prog)) + r->prog->scond = (r->prog->scond&~C_SCOND)|pred; + if (r->s1 != r->link) { + r->s1 = r->link; + r->link->p1 = r; + } + if (r == j->last) + break; + } +} + +void +predicate(void) +{ + Reg *r; + int t1, t2; + Joininfo j1, j2; + + for(r=firstr; r!=R; r=r->link) { + if (isbranch(r->prog)) { + t1 = joinsplit(r->s1, &j1); + t2 = joinsplit(r->s2, &j2); + if(j1.last->link != j2.start) + continue; + if(j1.end == j2.end) + if((t1 == Branch && (t2 == Join || t2 == Setcond)) || + (t2 == Join && (t1 == Join || t1 == Setcond))) { + applypred(r, &j1, Falsecond, Delbranch); + applypred(r, &j2, Truecond, Delbranch); + excise(r); + continue; + } + if(t1 == End || t1 == Branch) { + applypred(r, &j1, Falsecond, Keepbranch); + excise(r); + continue; + } + } + } +} + +int +isdconst(Addr *a) +{ + if(a->type == D_CONST && a->reg == NREG) + return 1; + return 0; +} diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c new file mode 100644 index 000000000..5011e75cc --- /dev/null +++ b/src/cmd/5g/reg.c @@ -0,0 +1,1329 @@ +// Inferno utils/5c/reg.c +// http://code.google.com/p/inferno-os/source/browse/utils/5g/reg.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#include "gg.h" +#include "opt.h" + +#define P2R(p) (Reg*)(p->reg) + + void addsplits(void); + int noreturn(Prog *p); +static int first = 0; + +Reg* +rega(void) +{ + Reg *r; + + r = freer; + if(r == R) { + r = mal(sizeof(*r)); + } else + freer = r->link; + + *r = zreg; + return r; +} + +int +rcmp(const void *a1, const void *a2) +{ + Rgn *p1, *p2; + int c1, c2; + + p1 = (Rgn*)a1; + p2 = (Rgn*)a2; + c1 = p2->cost; + c2 = p1->cost; + if(c1 -= c2) + return c1; + return p2->varno - p1->varno; +} + +static void +setoutvar(void) +{ + Type *t; + Node *n; + Addr a; + Iter save; + Bits bit; + int z; + + t = structfirst(&save, getoutarg(curfn->type)); + while(t != T) { + n = nodarg(t, 1); + a = zprog.from; + naddr(n, &a, 0); + bit = mkvar(R, &a, 0); + for(z=0; z<BITS; z++) + ovar.b[z] |= bit.b[z]; + t = structnext(&save); + } +//if(bany(b)) +//print("ovars = %Q\n", &ovar); +} + +void +excise(Reg *r) +{ + Prog *p; + + p = r->prog; + p->as = ANOP; + p->scond = zprog.scond; + p->from = zprog.from; + p->to = zprog.to; + p->reg = zprog.reg; +} + +static void +setaddrs(Bits bit) +{ + int i, n; + Var *v; + Sym *s; + + while(bany(&bit)) { + // convert each bit to a variable + i = bnum(bit); + s = var[i].sym; + n = var[i].name; + bit.b[i/32] &= ~(1L<<(i%32)); + + // disable all pieces of that variable + for(i=0; i<nvar; i++) { + v = var+i; + if(v->sym == s && v->name == n) + v->addr = 2; + } + } +} + +void +regopt(Prog *firstp) +{ + Reg *r, *r1; + Prog *p; + int i, z, nr; + uint32 vreg; + Bits bit; + +return; // disabled for the moment + if(first == 0) { + fmtinstall('Q', Qconv); + } + first++; + + if(debug['K']) { + if(first != 20) + return; +// debug['R'] = 2; +// debug['P'] = 2; + print("optimizing %S\n", curfn->nname->sym); + } + + // count instructions + nr = 0; + for(p=firstp; p!=P; p=p->link) + nr++; + + // if too big dont bother + if(nr >= 10000) { +// print("********** %S is too big (%d)\n", curfn->nname->sym, nr); + return; + } + + r1 = R; + firstr = R; + lastr = R; + nvar = 0; + regbits = 0; + for(z=0; z<BITS; z++) { + externs.b[z] = 0; + params.b[z] = 0; + consts.b[z] = 0; + addrs.b[z] = 0; + ovar.b[z] = 0; + } + + // build list of return variables + setoutvar(); + + /* + * pass 1 + * build aux data structure + * allocate pcs + * find use and set of variables + */ + nr = 0; + for(p=firstp; p != P; p = p->link) { + switch(p->as) { + case ADATA: + case AGLOBL: + case ANAME: + case ASIGNAME: + continue; + } + r = rega(); + nr++; + if(firstr == R) { + firstr = r; + lastr = r; + } else { + lastr->link = r; + r->p1 = lastr; + lastr->s1 = r; + lastr = r; + } + r->prog = p; + p->regp = r; + + r1 = r->p1; + if(r1 != R) { + switch(r1->prog->as) { + case ARET: + case AB: + case ARFE: + r->p1 = R; + r1->s1 = R; + } + } + + /* + * left side always read + */ + bit = mkvar(r, &p->from, p->as==AMOVW); + for(z=0; z<BITS; z++) + r->use1.b[z] |= bit.b[z]; + + /* + * right side depends on opcode + */ + bit = mkvar(r, &p->to, 0); + if(bany(&bit)) + switch(p->as) { + default: + yyerror("reg: unknown op: %A", p->as); + break; + + /* + * right side write + */ + case ANOP: + case AMOVB: + case AMOVBU: + case AMOVH: + case AMOVHU: + case AMOVW: + case AMOVF: + case AMOVD: + for(z=0; z<BITS; z++) + r->set.b[z] |= bit.b[z]; + break; + + /* + * funny + */ + case ABL: + for(z=0; z<BITS; z++) + addrs.b[z] |= bit.b[z]; + break; + } + + if(p->as == AMOVM) { + z = p->to.offset; + if(p->from.type == D_CONST) + z = p->from.offset; + for(i=0; z; i++) { + if(z&1) + regbits |= RtoB(i); + z >>= 1; + } + } + } + if(firstr == R) + return; + + /* + * pass 2 + * turn branch references to pointers + * build back pointers + */ + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + if(p->to.type == D_BRANCH) { + if(p->to.branch == P) + fatal("pnil %P", p); + r1 = p->to.branch->regp; + if(r1 == R) + fatal("rnil %P", p); + if(r1 == r) { + //fatal("ref to self %P", p); + continue; + } + r->s2 = r1; + r->p2link = r1->p2; + r1->p2 = r; + } + } + if(debug['R']) { + p = firstr->prog; + print("\n%L %D\n", p->lineno, &p->from); + } + + /* + * pass 2.5 + * find looping structure + */ + for(r = firstr; r != R; r = r->link) + r->active = 0; + change = 0; + loopit(firstr, nr); + + /* + * pass 3 + * iterate propagating usage + * back until flow graph is complete + */ +loop1: + change = 0; + for(r = firstr; r != R; r = r->link) + r->active = 0; + for(r = firstr; r != R; r = r->link) + if(r->prog->as == ARET) + prop(r, zbits, zbits); +loop11: + /* pick up unreachable code */ + i = 0; + for(r = firstr; r != R; r = r1) { + r1 = r->link; + if(r1 && r1->active && !r->active) { + prop(r, zbits, zbits); + i = 1; + } + } + if(i) + goto loop11; + if(change) + goto loop1; + + + /* + * pass 4 + * iterate propagating register/variable synchrony + * forward until graph is complete + */ +loop2: + change = 0; + for(r = firstr; r != R; r = r->link) + r->active = 0; + synch(firstr, zbits); + if(change) + goto loop2; + + addsplits(); + + if(debug['R'] > 1) { + print("\nprop structure:\n"); + for(r = firstr; r != R; r = r->link) { + print("%d:%P", r->loop, r->prog); + for(z=0; z<BITS; z++) { + bit.b[z] = r->set.b[z] | + r->refahead.b[z] | r->calahead.b[z] | + r->refbehind.b[z] | r->calbehind.b[z] | + r->use1.b[z] | r->use2.b[z]; + } + + if(bany(&bit)) { + print("\t"); + if(bany(&r->use1)) + print(" u1=%Q", r->use1); + if(bany(&r->use2)) + print(" u2=%Q", r->use2); + if(bany(&r->set)) + print(" st=%Q", r->set); + if(bany(&r->refahead)) + print(" ra=%Q", r->refahead); + if(bany(&r->calahead)) + print(" ca=%Q", r->calahead); + if(bany(&r->refbehind)) + print(" rb=%Q", r->refbehind); + if(bany(&r->calbehind)) + print(" cb=%Q", r->calbehind); + } + print("\n"); + } + } + + /* + * pass 5 + * isolate regions + * calculate costs (paint1) + */ + r = firstr; + if(r) { + for(z=0; z<BITS; z++) + bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) & + ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]); + if(bany(&bit) & !r->refset) { + // should never happen - all variables are preset + if(debug['w']) + print("%L: used and not set: %Q\n", r->prog->lineno, bit); + r->refset = 1; + } + } + + for(r = firstr; r != R; r = r->link) + r->act = zbits; + rgp = region; + nregion = 0; + for(r = firstr; r != R; r = r->link) { + for(z=0; z<BITS; z++) + bit.b[z] = r->set.b[z] & + ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]); + if(bany(&bit) && !r->refset) { + if(debug['w']) + print("%L: set and not used: %Q\n", r->prog->lineno, bit); + r->refset = 1; + excise(r); + } + for(z=0; z<BITS; z++) + bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]); + while(bany(&bit)) { + i = bnum(bit); + rgp->enter = r; + rgp->varno = i; + change = 0; + if(debug['R'] > 1) + print("\n"); + paint1(r, i); + bit.b[i/32] &= ~(1L<<(i%32)); + if(change <= 0) { + if(debug['R']) + print("%L $%d: %Q\n", + r->prog->lineno, change, blsh(i)); + continue; + } + rgp->cost = change; + nregion++; + if(nregion >= NRGN) { + if(debug['R'] > 1) + print("too many regions\n"); + goto brk; + } + rgp++; + } + } +brk: + qsort(region, nregion, sizeof(region[0]), rcmp); + + /* + * pass 6 + * determine used registers (paint2) + * replace code (paint3) + */ + rgp = region; + for(i=0; i<nregion; i++) { + bit = blsh(rgp->varno); + vreg = paint2(rgp->enter, rgp->varno); + vreg = allreg(vreg, rgp); + if(debug['R']) { + if(rgp->regno >= NREG) + print("%L $%d F%d: %Q\n", + rgp->enter->prog->lineno, + rgp->cost, + rgp->regno-NREG, + bit); + else + print("%L $%d R%d: %Q\n", + rgp->enter->prog->lineno, + rgp->cost, + rgp->regno, + bit); + } + if(rgp->regno != 0) + paint3(rgp->enter, rgp->varno, vreg, rgp->regno); + rgp++; + } + /* + * pass 7 + * peep-hole on basic block + */ + if(!debug['R'] || debug['P']) { +// peep(); + } + + /* + * last pass + * eliminate nops + * free aux structures + */ + for(p = firstp; p != P; p = p->link) { + while(p->link != P && p->link->as == ANOP) + p->link = p->link->link; + if(p->to.type == D_BRANCH) + while(p->to.branch != P && p->to.branch->as == ANOP) + p->to.branch = p->to.branch->link; + } + if(r1 != R) { + r1->link = freer; + freer = firstr; + } +} + +void +addsplits(void) +{ + Reg *r, *r1; + int z, i; + Bits bit; + + for(r = firstr; r != R; r = r->link) { + if(r->loop > 1) + continue; + if(r->prog->as == ABL) + continue; + for(r1 = r->p2; r1 != R; r1 = r1->p2link) { + if(r1->loop <= 1) + continue; + for(z=0; z<BITS; z++) + bit.b[z] = r1->calbehind.b[z] & + (r->refahead.b[z] | r->use1.b[z] | r->use2.b[z]) & + ~(r->calahead.b[z] & addrs.b[z]); + while(bany(&bit)) { + i = bnum(bit); + bit.b[i/32] &= ~(1L << (i%32)); + } + } + } +} + +/* + * add mov b,rn + * just after r + */ +void +addmove(Reg *r, int bn, int rn, int f) +{ + Prog *p, *p1; + Adr *a; + Var *v; + + p1 = mal(sizeof(*p1)); + *p1 = zprog; + p = r->prog; + + p1->link = p->link; + p->link = p1; + p1->lineno = p->lineno; + + v = var + bn; + + a = &p1->to; + a->sym = v->sym; + a->name = v->name; + a->offset = v->offset; + a->etype = v->etype; + a->type = D_OREG; + if(a->etype == TARRAY || a->sym == S) + a->type = D_CONST; + + switch(v->etype) { + default: + print("What is this %E\n", v->etype); + + case TINT32: + case TUINT32: + case TPTR32: + case TBOOL: + p1->as = AMOVW; + break; + case TINT8: + case TUINT8: + p1->as = AMOVB; + break; + case TINT16: + case TUINT16: + p1->as = AMOVH; + break; + case TFLOAT32: + p1->as = AMOVF; + break; + case TFLOAT64: + p1->as = AMOVD; + break; + } + + p1->from.type = D_REG; + p1->from.reg = rn; + if(rn >= NREG) { + p1->from.type = D_FREG; + p1->from.reg = rn-NREG; + } + if(!f) { + p1->from = *a; + *a = zprog.from; + a->type = D_REG; + a->reg = rn; + if(rn >= NREG) { + a->type = D_FREG; + a->reg = rn-NREG; + } + if(v->etype == TUINT8) + p1->as = AMOVBU; + if(v->etype == TUINT16) + p1->as = AMOVHU; + } + if(debug['R']) + print("%P\t.a%P\n", p, p1); +} + +static int +overlap(int32 o1, int w1, int32 o2, int w2) +{ + int32 t1, t2; + + t1 = o1+w1; + t2 = o2+w2; + + if(!(t1 > o2 && t2 > o1)) + return 0; + + return 1; +} + +Bits +mkvar(Reg *r, Adr *a, int docon) +{ + Var *v; + int i, t, n, et, z, w, flag; + int32 o; + Bits bit; + Sym *s; + + // mark registers used + t = a->type; + n = D_NONE; + + switch(t) { + default: + print("type %d %d %D\n", t, a->name, a); + goto none; + + case D_CONST: + if(a->reg != NREG) + r->regu |= RtoB(a->reg); + // fallthrough + + case D_NONE: + case D_FCONST: + case D_BRANCH: + goto none; + + case D_REGREG: + if(a->offset != NREG) + r->regu |= RtoB(a->offset); + // fallthrough + + case D_REG: + case D_SHIFT: + case D_OREG: + if(a->reg != NREG) + r->regu |= RtoB(a->reg); + break; + + case D_FREG: + if(a->reg != NREG) + r->regu |= FtoB(a->reg); + break; + } + + switch(a->name) { + default: + goto none; + + case D_EXTERN: + case D_STATIC: + case D_AUTO: + case D_PARAM: + n = a->name; + break; + } + + flag = 0; +// if(a->pun) +// flag = 1; + + s = a->sym; + if(s == S) + goto none; + if(s->name[0] == '.') + goto none; + et = a->etype; + o = a->offset; + w = a->width; + + for(i=0; i<nvar; i++) { + v = var+i; + if(v->sym == s && v->name == n) { + if(v->offset == o) + if(v->etype == et) + if(v->width == w) + if(!flag) + return blsh(i); + + // if they overlaps, disable both + if(overlap(v->offset, v->width, o, w)) { + v->addr = 1; + flag = 1; + } + } + } + + switch(et) { + case 0: + case TFUNC: + case TARRAY: + case TSTRING: + goto none; + } + + if(nvar >= NVAR) { + if(debug['w'] > 1 && s) + fatal("variable not optimized: %D", a); + goto none; + } + + i = nvar; + nvar++; + v = var+i; + v->sym = s; + v->offset = o; + v->name = n; +// v->gotype = a->gotype; + v->etype = et; + v->width = w; + v->addr = flag; // funny punning + + if(debug['R']) + print("bit=%2d et=%E pun=%d %D\n", i, et, flag, a); + +out: + bit = blsh(i); + if(n == D_EXTERN || n == D_STATIC) + for(z=0; z<BITS; z++) + externs.b[z] |= bit.b[z]; + if(n == D_PARAM) + for(z=0; z<BITS; z++) + params.b[z] |= bit.b[z]; + +// if(t == D_CONST) { +// if(s == S) { +// for(z=0; z<BITS; z++) +// consts.b[z] |= bit.b[z]; +// return bit; +// } +// if(et != TARRAY) +// for(z=0; z<BITS; z++) +// addrs.b[z] |= bit.b[z]; +// for(z=0; z<BITS; z++) +// params.b[z] |= bit.b[z]; +// return bit; +// } +// if(t != D_OREG) +// goto none; + + return bit; + +none: + return zbits; +} + +void +prop(Reg *r, Bits ref, Bits cal) +{ + Reg *r1, *r2; + int z; + + for(r1 = r; r1 != R; r1 = r1->p1) { + for(z=0; z<BITS; z++) { + ref.b[z] |= r1->refahead.b[z]; + if(ref.b[z] != r1->refahead.b[z]) { + r1->refahead.b[z] = ref.b[z]; + change++; + } + cal.b[z] |= r1->calahead.b[z]; + if(cal.b[z] != r1->calahead.b[z]) { + r1->calahead.b[z] = cal.b[z]; + change++; + } + } + switch(r1->prog->as) { + case ABL: + if(noreturn(r1->prog)) + break; + for(z=0; z<BITS; z++) { + cal.b[z] |= ref.b[z] | externs.b[z]; + ref.b[z] = 0; + } + break; + + case ATEXT: + for(z=0; z<BITS; z++) { + cal.b[z] = 0; + ref.b[z] = 0; + } + break; + + case ARET: + for(z=0; z<BITS; z++) { + cal.b[z] = externs.b[z] | ovar.b[z]; + ref.b[z] = 0; + } + break; + } + for(z=0; z<BITS; z++) { + ref.b[z] = (ref.b[z] & ~r1->set.b[z]) | + r1->use1.b[z] | r1->use2.b[z]; + cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]); + r1->refbehind.b[z] = ref.b[z]; + r1->calbehind.b[z] = cal.b[z]; + } + if(r1->active) + break; + r1->active = 1; + } + for(; r != r1; r = r->p1) + for(r2 = r->p2; r2 != R; r2 = r2->p2link) + prop(r2, r->refbehind, r->calbehind); +} + +/* + * find looping structure + * + * 1) find reverse postordering + * 2) find approximate dominators, + * the actual dominators if the flow graph is reducible + * otherwise, dominators plus some other non-dominators. + * See Matthew S. Hecht and Jeffrey D. Ullman, + * "Analysis of a Simple Algorithm for Global Data Flow Problems", + * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts, + * Oct. 1-3, 1973, pp. 207-217. + * 3) find all nodes with a predecessor dominated by the current node. + * such a node is a loop head. + * recursively, all preds with a greater rpo number are in the loop + */ +int32 +postorder(Reg *r, Reg **rpo2r, int32 n) +{ + Reg *r1; + + r->rpo = 1; + r1 = r->s1; + if(r1 && !r1->rpo) + n = postorder(r1, rpo2r, n); + r1 = r->s2; + if(r1 && !r1->rpo) + n = postorder(r1, rpo2r, n); + rpo2r[n] = r; + n++; + return n; +} + +int32 +rpolca(int32 *idom, int32 rpo1, int32 rpo2) +{ + int32 t; + + if(rpo1 == -1) + return rpo2; + while(rpo1 != rpo2){ + if(rpo1 > rpo2){ + t = rpo2; + rpo2 = rpo1; + rpo1 = t; + } + while(rpo1 < rpo2){ + t = idom[rpo2]; + if(t >= rpo2) + fatal("bad idom"); + rpo2 = t; + } + } + return rpo1; +} + +int +doms(int32 *idom, int32 r, int32 s) +{ + while(s > r) + s = idom[s]; + return s == r; +} + +int +loophead(int32 *idom, Reg *r) +{ + int32 src; + + src = r->rpo; + if(r->p1 != R && doms(idom, src, r->p1->rpo)) + return 1; + for(r = r->p2; r != R; r = r->p2link) + if(doms(idom, src, r->rpo)) + return 1; + return 0; +} + +void +loopmark(Reg **rpo2r, int32 head, Reg *r) +{ + if(r->rpo < head || r->active == head) + return; + r->active = head; + r->loop += LOOP; + if(r->p1 != R) + loopmark(rpo2r, head, r->p1); + for(r = r->p2; r != R; r = r->p2link) + loopmark(rpo2r, head, r); +} + +void +loopit(Reg *r, int32 nr) +{ + Reg *r1; + int32 i, d, me; + + if(nr > maxnr) { + rpo2r = mal(nr * sizeof(Reg*)); + idom = mal(nr * sizeof(int32)); + maxnr = nr; + } + d = postorder(r, rpo2r, 0); + if(d > nr) + fatal("too many reg nodes"); + nr = d; + for(i = 0; i < nr / 2; i++){ + r1 = rpo2r[i]; + rpo2r[i] = rpo2r[nr - 1 - i]; + rpo2r[nr - 1 - i] = r1; + } + for(i = 0; i < nr; i++) + rpo2r[i]->rpo = i; + + idom[0] = 0; + for(i = 0; i < nr; i++){ + r1 = rpo2r[i]; + me = r1->rpo; + d = -1; + if(r1->p1 != R && r1->p1->rpo < me) + d = r1->p1->rpo; + for(r1 = r1->p2; r1 != nil; r1 = r1->p2link) + if(r1->rpo < me) + d = rpolca(idom, d, r1->rpo); + idom[i] = d; + } + + for(i = 0; i < nr; i++){ + r1 = rpo2r[i]; + r1->loop++; + if(r1->p2 != R && loophead(idom, r1)) + loopmark(rpo2r, i, r1); + } +} + +void +synch(Reg *r, Bits dif) +{ + Reg *r1; + int z; + + for(r1 = r; r1 != R; r1 = r1->s1) { + for(z=0; z<BITS; z++) { + dif.b[z] = (dif.b[z] & + ~(~r1->refbehind.b[z] & r1->refahead.b[z])) | + r1->set.b[z] | r1->regdiff.b[z]; + if(dif.b[z] != r1->regdiff.b[z]) { + r1->regdiff.b[z] = dif.b[z]; + change++; + } + } + if(r1->active) + break; + r1->active = 1; + for(z=0; z<BITS; z++) + dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]); + if(r1->s2 != R) + synch(r1->s2, dif); + } +} + +uint32 +allreg(uint32 b, Rgn *r) +{ + Var *v; + int i; + + v = var + r->varno; + r->regno = 0; + switch(v->etype) { + + default: + fatal("unknown etype %d/%E", bitno(b), v->etype); + break; + + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TINT: + case TUINT: + case TUINTPTR: + case TBOOL: + case TPTR32: + i = BtoR(~b); + if(i && r->cost >= 0) { + r->regno = i; + return RtoB(i); + } + break; + + case TFLOAT32: + case TFLOAT64: + case TFLOAT: + i = BtoF(~b); + if(i && r->cost >= 0) { + r->regno = i+NREG; + return FtoB(i); + } + break; + + case TINT64: + case TUINT64: + case TPTR64: + case TINTER: + case TSTRUCT: + case TARRAY: + break; + } + return 0; +} + +void +paint1(Reg *r, int bn) +{ + Reg *r1; + Prog *p; + int z; + uint32 bb; + + z = bn/32; + bb = 1L<<(bn%32); + if(r->act.b[z] & bb) + return; + for(;;) { + if(!(r->refbehind.b[z] & bb)) + break; + r1 = r->p1; + if(r1 == R) + break; + if(!(r1->refahead.b[z] & bb)) + break; + if(r1->act.b[z] & bb) + break; + r = r1; + } + + if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) { + change -= CLOAD * r->loop; + if(debug['R'] > 1) + print("%d%P\td %Q $%d\n", r->loop, + r->prog, blsh(bn), change); + } + for(;;) { + r->act.b[z] |= bb; + p = r->prog; + + if(r->use1.b[z] & bb) { + change += CREF * r->loop; + if(debug['R'] > 1) + print("%d%P\tu1 %Q $%d\n", r->loop, + p, blsh(bn), change); + } + + if((r->use2.b[z]|r->set.b[z]) & bb) { + change += CREF * r->loop; + if(debug['R'] > 1) + print("%d%P\tu2 %Q $%d\n", r->loop, + p, blsh(bn), change); + } + + if(STORE(r) & r->regdiff.b[z] & bb) { + change -= CLOAD * r->loop; + if(debug['R'] > 1) + print("%d%P\tst %Q $%d\n", r->loop, + p, blsh(bn), change); + } + + if(r->refbehind.b[z] & bb) + for(r1 = r->p2; r1 != R; r1 = r1->p2link) + if(r1->refahead.b[z] & bb) + paint1(r1, bn); + + if(!(r->refahead.b[z] & bb)) + break; + r1 = r->s2; + if(r1 != R) + if(r1->refbehind.b[z] & bb) + paint1(r1, bn); + r = r->s1; + if(r == R) + break; + if(r->act.b[z] & bb) + break; + if(!(r->refbehind.b[z] & bb)) + break; + } +} + +uint32 +paint2(Reg *r, int bn) +{ + Reg *r1; + int z; + uint32 bb, vreg; + + z = bn/32; + bb = 1L << (bn%32); + vreg = regbits; + if(!(r->act.b[z] & bb)) + return vreg; + for(;;) { + if(!(r->refbehind.b[z] & bb)) + break; + r1 = r->p1; + if(r1 == R) + break; + if(!(r1->refahead.b[z] & bb)) + break; + if(!(r1->act.b[z] & bb)) + break; + r = r1; + } + for(;;) { + r->act.b[z] &= ~bb; + + vreg |= r->regu; + + if(r->refbehind.b[z] & bb) + for(r1 = r->p2; r1 != R; r1 = r1->p2link) + if(r1->refahead.b[z] & bb) + vreg |= paint2(r1, bn); + + if(!(r->refahead.b[z] & bb)) + break; + r1 = r->s2; + if(r1 != R) + if(r1->refbehind.b[z] & bb) + vreg |= paint2(r1, bn); + r = r->s1; + if(r == R) + break; + if(!(r->act.b[z] & bb)) + break; + if(!(r->refbehind.b[z] & bb)) + break; + } + return vreg; +} + +void +paint3(Reg *r, int bn, int32 rb, int rn) +{ + Reg *r1; + Prog *p; + int z; + uint32 bb; + + z = bn/32; + bb = 1L << (bn%32); + if(r->act.b[z] & bb) + return; + for(;;) { + if(!(r->refbehind.b[z] & bb)) + break; + r1 = r->p1; + if(r1 == R) + break; + if(!(r1->refahead.b[z] & bb)) + break; + if(r1->act.b[z] & bb) + break; + r = r1; + } + + if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) + addmove(r, bn, rn, 0); + for(;;) { + r->act.b[z] |= bb; + p = r->prog; + + if(r->use1.b[z] & bb) { + if(debug['R']) + print("%P", p); + addreg(&p->from, rn); + if(debug['R']) + print("\t.c%P\n", p); + } + if((r->use2.b[z]|r->set.b[z]) & bb) { + if(debug['R']) + print("%P", p); + addreg(&p->to, rn); + if(debug['R']) + print("\t.c%P\n", p); + } + + if(STORE(r) & r->regdiff.b[z] & bb) + addmove(r, bn, rn, 1); + r->regu |= rb; + + if(r->refbehind.b[z] & bb) + for(r1 = r->p2; r1 != R; r1 = r1->p2link) + if(r1->refahead.b[z] & bb) + paint3(r1, bn, rb, rn); + + if(!(r->refahead.b[z] & bb)) + break; + r1 = r->s2; + if(r1 != R) + if(r1->refbehind.b[z] & bb) + paint3(r1, bn, rb, rn); + r = r->s1; + if(r == R) + break; + if(r->act.b[z] & bb) + break; + if(!(r->refbehind.b[z] & bb)) + break; + } +} + +void +addreg(Adr *a, int rn) +{ + + a->sym = 0; + a->name = D_NONE; + a->type = D_REG; + a->reg = rn; + if(rn >= NREG) { + a->type = D_FREG; + a->reg = rn-NREG; + } +} + +/* + * bit reg + * 0 R0 + * 1 R1 + * ... ... + * 10 R10 + */ +int32 +RtoB(int r) +{ + + if(r < 2 || r >= REGTMP-2) // excluded R9 and R10 for m and g + return 0; + return 1L << r; +} + +int +BtoR(int32 b) +{ + b &= 0x01fcL; // excluded R9 and R10 for m and g + if(b == 0) + return 0; + return bitno(b); +} + +/* + * bit reg + * 18 F2 + * 19 F3 + * ... ... + * 23 F7 + */ +int32 +FtoB(int f) +{ + + if(f < 2 || f > NFREG-1) + return 0; + return 1L << (f + 16); +} + +int +BtoF(int32 b) +{ + + b &= 0xfc0000L; + if(b == 0) + return 0; + return bitno(b) - 16; +} + +static Sym* symlist[10]; + +int +noreturn(Prog *p) +{ + Sym *s; + int i; + + if(symlist[0] == S) { + symlist[0] = pkglookup("panicindex", runtimepkg); + symlist[1] = pkglookup("panicslice", runtimepkg); + symlist[2] = pkglookup("throwinit", runtimepkg); + symlist[3] = pkglookup("panic", runtimepkg); + } + + s = p->to.sym; + if(s == S) + return 0; + for(i=0; symlist[i]!=S; i++) + if(s == symlist[i]) + return 1; + return 0; +} diff --git a/src/cmd/5l/5.out.h b/src/cmd/5l/5.out.h index c06441c1c..a25c0f71d 100644 --- a/src/cmd/5l/5.out.h +++ b/src/cmd/5l/5.out.h @@ -58,6 +58,7 @@ #define NFREG 8 #define FREGRET 0 #define FREGEXT 7 +#define FREGTMP 15 /* compiler allocates register variables F0 up */ /* compiler allocates external registers F7 down */ @@ -158,8 +159,8 @@ enum as ARET, ATEXT, AWORD, - ADYNT, - AINIT, + ADYNT_, + AINIT_, ABCASE, ACASE, @@ -188,7 +189,7 @@ enum as #define C_PBIT (1<<5) #define C_WBIT (1<<6) #define C_FBIT (1<<7) /* psr flags-only */ -#define C_UBIT (1<<7) /* up bit */ +#define C_UBIT (1<<7) /* up bit, unsigned bit */ #define C_SCOND_EQ 0 #define C_SCOND_NE 1 @@ -246,6 +247,7 @@ enum as /* internal only */ #define D_SIZE (D_NONE+40) +#define D_PCREL (D_NONE+41) /* * this is the ranlib header diff --git a/src/cmd/5l/Makefile b/src/cmd/5l/Makefile index b9780f098..71798724b 100644 --- a/src/cmd/5l/Makefile +++ b/src/cmd/5l/Makefile @@ -2,24 +2,29 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 5l\ +TARG=5l OFILES=\ asm.$O\ + data.$O\ elf.$O\ enam.$O\ + ldelf.$O\ + ldmacho.$O\ lib.$O\ list.$O\ noop.$O\ obj.$O\ optab.$O\ pass.$O\ + prof.$O\ thumb.$O\ softfloat.$O\ span.$O\ + symtab.$O\ go.$O\ HFILES=\ @@ -27,19 +32,12 @@ HFILES=\ ../5l/5.out.h\ ../ld/elf.h\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lbio -l9 - -$(OFILES): $(HFILES) +include ../../Make.ccmd enam.c: 5.out.h sh mkenam -clean: - rm -f *.o $(TARG) *.5 enam.c 5.out a.out - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +CLEANFILES+=enam.c %.$O: ../ld/%.c - $(CC) $(CFLAGS) -c -I. ../ld/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. ../ld/$*.c diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index 45e6e734f..7ceea59b6 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Writing object files. + #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" @@ -50,127 +52,11 @@ entryvalue(void) s = lookup(a, 0); if(s->type == 0) return INITTEXT; - switch(s->type) { - case STEXT: - case SLEAF: - break; - case SDATA: - if(dlm) - return s->value+INITDAT; - default: + if(s->type != STEXT) diag("entry not text: %s", s->name); - } return s->value; } -vlong -addstring(Sym *s, char *str) -{ - int n, m; - vlong r; - Prog *p; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - n = strlen(str)+1; - while(n > 0) { - m = n; - if(m > NSNAME) - m = NSNAME; - p = newdata(s, s->value, m, D_EXTERN); - p->to.type = D_SCONST; - p->to.sval = mal(NSNAME); - memmove(p->to.sval, str, m); - s->value += m; - str += m; - n -= m; - } - return r; -} - -vlong -adduintxx(Sym *s, uint64 v, int wid) -{ - vlong r; - Prog *p; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, wid, D_EXTERN); - s->value += wid; - p->to.type = D_CONST; - p->to.offset = v; - return r; -} - -vlong -adduint8(Sym *s, uint8 v) -{ - return adduintxx(s, v, 1); -} - -vlong -adduint16(Sym *s, uint16 v) -{ - return adduintxx(s, v, 2); -} - -vlong -adduint32(Sym *s, uint32 v) -{ - return adduintxx(s, v, 4); -} - -vlong -adduint64(Sym *s, uint64 v) -{ - return adduintxx(s, v, 8); -} - -vlong -addaddr(Sym *s, Sym *t) -{ - vlong r; - Prog *p; - enum { Ptrsize = 4 }; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, Ptrsize, D_EXTERN); - s->value += Ptrsize; - p->to.type = D_ADDR; - p->to.index = D_EXTERN; - p->to.offset = 0; - p->to.sym = t; - return r; -} - -vlong -addsize(Sym *s, Sym *t) -{ - vlong r; - Prog *p; - enum { Ptrsize = 4 }; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, Ptrsize, D_EXTERN); - s->value += Ptrsize; - p->to.type = D_SIZE; - p->to.index = D_EXTERN; - p->to.offset = 0; - p->to.sym = t; - return r; -} - enum { ElfStrEmpty, ElfStrInterp, @@ -188,6 +74,8 @@ enum { ElfStrGosymtab, ElfStrGopclntab, ElfStrShstrtab, + ElfStrRelPlt, + ElfStrPlt, NElfStr }; @@ -209,21 +97,67 @@ needlib(char *name) return 0; } +int nelfsym = 1; + +void +adddynrel(Sym *s, Reloc *r) +{ + diag("adddynrel: unsupported binary format"); +} + +void +adddynsym(Sym *s) +{ + diag("adddynsym: not implemented"); +} + +static void +elfsetupplt(void) +{ + // TODO +} + +int +archreloc(Reloc *r, Sym *s, vlong *val) +{ + return -1; +} + +void +adddynlib(char *lib) +{ + Sym *s; + + if(!needlib(lib)) + return; + + if(iself) { + s = lookup(".dynstr", 0); + if(s->size == 0) + addstring(s, ""); + elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib)); + } else { + diag("adddynlib: unsupported binary format"); + } +} + void doelf(void) { - Sym *s, *shstrtab, *dynamic, *dynstr, *d; - int h, nsym, t; + Sym *s, *shstrtab, *dynstr; if(!iself) return; /* predefine strings we need for section headers */ shstrtab = lookup(".shstrtab", 0); + shstrtab->type = SELFDATA; shstrtab->reachable = 1; + elfstr[ElfStrEmpty] = addstring(shstrtab, ""); elfstr[ElfStrText] = addstring(shstrtab, ".text"); elfstr[ElfStrData] = addstring(shstrtab, ".data"); + addstring(shstrtab, ".rodata"); elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); if(!debug['s']) { elfstr[ElfStrGosymcounts] = addstring(shstrtab, ".gosymcounts"); @@ -241,6 +175,8 @@ doelf(void) elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym"); elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr"); elfstr[ElfStrRel] = addstring(shstrtab, ".rel"); + elfstr[ElfStrRelPlt] = addstring(shstrtab, ".rel.plt"); + elfstr[ElfStrPlt] = addstring(shstrtab, ".plt"); /* interpreter string */ s = lookup(".interp", 0); @@ -257,7 +193,8 @@ doelf(void) s = lookup(".dynstr", 0); s->type = SELFDATA; s->reachable = 1; - addstring(s, ""); + if(s->size == 0) + addstring(s, ""); dynstr = s; /* relocation table */ @@ -269,92 +206,35 @@ doelf(void) s = lookup(".got", 0); s->reachable = 1; s->type = SELFDATA; + + /* hash */ + s = lookup(".hash", 0); + s->reachable = 1; + s->type = SELFDATA; - /* got.plt - ??? */ + /* got.plt */ s = lookup(".got.plt", 0); s->reachable = 1; - s->type = SELFDATA; + s->type = SDATA; // writable, so not SELFDATA - /* hash */ - s = lookup(".hash", 0); + s = lookup(".plt", 0); + s->reachable = 1; + s->type = SELFDATA; + + s = lookup(".rel.plt", 0); s->reachable = 1; s->type = SELFDATA; + + elfsetupplt(); /* define dynamic elf table */ s = lookup(".dynamic", 0); s->reachable = 1; s->type = SELFDATA; - dynamic = s; - - /* - * relocation entries for dynimp symbols - */ - nsym = 1; // sym 0 is reserved - for(h=0; h<NHASH; h++) { - for(s=hash[h]; s!=S; s=s->link) { - if(!s->reachable || (s->type != SDATA && s->type != SBSS) || s->dynimpname == nil) - continue; - - if(!s->dynexport) { - d = lookup(".rel", 0); - addaddr(d, s); - adduint32(d, ELF32_R_INFO(nsym, R_ARM_ABS32)); - } - - nsym++; - - d = lookup(".dynsym", 0); - adduint32(d, addstring(lookup(".dynstr", 0), s->dynimpname)); - /* value */ - if(!s->dynexport) - adduint32(d, 0); - else - addaddr(d, s); - - /* size of object */ - adduint32(d, 0); - - /* type */ - t = STB_GLOBAL << 4; - if(s->dynexport && s->type == STEXT) - t |= STT_FUNC; - else - t |= STT_OBJECT; - adduint8(d, t); - - /* reserved */ - adduint8(d, 0); - - /* section where symbol is defined */ - if(!s->dynexport) - adduint16(d, SHN_UNDEF); - else { - switch(s->type) { - default: - case STEXT: - t = 9; - break; - case SDATA: - t = 10; - break; - case SBSS: - t = 11; - break; - } - adduint16(d, t); - } - - if(!s->dynexport && needlib(s->dynimplib)) - elfwritedynent(dynamic, DT_NEEDED, addstring(dynstr, s->dynimplib)); - } - } - - elfdynhash(nsym); /* * .dynamic table */ - s = dynamic; elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); @@ -365,6 +245,10 @@ doelf(void) elfwritedynent(s, DT_RELENT, ELF32RELSIZE); if(rpath) elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); + elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); + elfwritedynent(s, DT_PLTREL, DT_REL); + elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rel.plt", 0)); + elfwritedynentsym(s, DT_JMPREL, lookup(".rel.plt", 0)); elfwritedynent(s, DT_NULL, 0); } } @@ -372,16 +256,18 @@ doelf(void) vlong datoff(vlong addr) { - if(addr >= INITDAT) - return addr - INITDAT + rnd(HEADR+textsize, INITRND); - diag("datoff %#llx", addr); + if(addr >= segdata.vaddr) + return addr - segdata.vaddr + segdata.fileoff; + if(addr >= segtext.vaddr) + return addr - segtext.vaddr + segtext.fileoff; + diag("datoff %#x", addr); return 0; } void shsym(Elf64_Shdr *sh, Sym *s) { - sh->addr = s->value + INITDAT; + sh->addr = symaddr(s); sh->off = datoff(sh->addr); sh->size = s->size; } @@ -400,103 +286,47 @@ phsh(Elf64_Phdr *ph, Elf64_Shdr *sh) void asmb(void) { - Prog *p; - int32 t, etext; + int32 t; int a, dynsym; - uint32 va, fo, w, symo, startva; - uint32 symdatva = SYMDATVA; + uint32 va, fo, w, startva; int strtabsize; - Optab *o; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; + Section *sect; strtabsize = 0; - symo = 0; if(debug['v']) - Bprint(&bso, "%5.2f asm\n", cputime()); + Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); - OFFSET = HEADR; - seek(cout, OFFSET, 0); - pc = INITTEXT; - for(p = firstp; p != P; p = p->link) { - setarch(p); - if(p->as == ATEXT) { - curtext = p; - autosize = p->to.offset + 4; - } - if(p->pc != pc) { - diag("phase error %lux sb %lux", - p->pc, pc); - if(!debug['a']) - prasm(curp); - pc = p->pc; - } - curp = p; - o = oplook(p); /* could probably avoid this call */ - if(thumb) - thumbasmout(p, o); - else - asmout(p, o); - pc += o->size; - } - while(pc-INITTEXT < textsize) { - cput(0); - pc++; - } - if(debug['a']) - Bprint(&bso, "\n"); - Bflush(&bso); - cflush(); + sect = segtext.sect; + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + codeblk(sect->vaddr, sect->len); - /* output strings in text segment */ - etext = INITTEXT + textsize; - for(t = pc; t < etext; t += sizeof(buf)-100) { - if(etext-t > sizeof(buf)-100) - datblk(t, sizeof(buf)-100, 1); - else - datblk(t, etext-t, 1); - } + /* output read-only data in text segment */ + sect = segtext.sect->next; + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + datblk(sect->vaddr, sect->len); - /* output section header strings */ - curtext = P; - switch(HEADTYPE) { - case 0: - case 1: - case 2: - case 5: - OFFSET = HEADR+textsize; - seek(cout, OFFSET, 0); - break; - case 3: - OFFSET = rnd(HEADR+textsize, 4096); - seek(cout, OFFSET, 0); - break; - case 6: - OFFSET = rnd(HEADR+textsize, INITRND); - seek(cout, OFFSET, 0); - break; - } - if(dlm){ - char buf[8]; + if(debug['v']) + Bprint(&bso, "%5.2f datblk\n", cputime()); + Bflush(&bso); - write(cout, buf, INITDAT-textsize); - textsize = INITDAT; - } - for(t = 0; t < datsize; t += sizeof(buf)-100) { - if(datsize-t > sizeof(buf)-100) - datblk(t, sizeof(buf)-100, 0); - else - datblk(t, datsize-t, 0); - } - cflush(); + seek(cout, segdata.fileoff, 0); + datblk(segdata.vaddr, segdata.filelen); + + /* output read-only data in text segment */ + sect = segtext.sect->next; + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + datblk(sect->vaddr, sect->len); /* output symbol table */ symsize = 0; lcsize = 0; if(!debug['s']) { + // TODO: rationalize if(debug['v']) Bprint(&bso, "%5.2f sym\n", cputime()); Bflush(&bso); @@ -508,45 +338,24 @@ asmb(void) debug['s'] = 1; break; case 2: - OFFSET = HEADR+textsize+datsize; + OFFSET = HEADR+textsize+segdata.filelen; seek(cout, OFFSET, 0); break; case 3: - OFFSET += rnd(datsize, 4096); + OFFSET += rnd(segdata.filelen, 4096); seek(cout, OFFSET, 0); break; case 6: - symo = rnd(HEADR+textsize, INITRND)+datsize+strtabsize; - symo = rnd(symo, INITRND); - seek(cout, symo + 8, 0); + OFFSET += segdata.filelen; + seek(cout, rnd(OFFSET, INITRND), 0); break; } if(!debug['s']) - asmsym(); - if(debug['v']) - Bprint(&bso, "%5.2f pc\n", cputime()); - Bflush(&bso); - if(!debug['s']) - asmlc(); - if(!debug['s']) asmthumbmap(); - if(dlm) - asmdyn(); - if(!debug['s']) - strnput("", INITRND-(8+symsize+lcsize)%INITRND); - cflush(); - seek(cout, symo, 0); - lputl(symsize); - lputl(lcsize); - cflush(); - } - else if(dlm){ - seek(cout, HEADR+textsize+datsize, 0); - asmdyn(); cflush(); } - curtext = P; + cursym = nil; if(debug['v']) Bprint(&bso, "%5.2f header\n", cputime()); Bflush(&bso); @@ -568,10 +377,10 @@ asmb(void) lputl(0xef000011); /* SWI - exit code */ lputl(textsize+HEADR); /* text size */ - lputl(datsize); /* data size */ + lputl(segdata.filelen); /* data size */ lputl(0); /* sym size */ - lputl(bsssize); /* bss size */ + lputl(segdata.len - segdata.filelen); /* bss size */ lputl(0); /* sym type */ lputl(INITTEXT-HEADR); /* text addr */ lputl(0); /* workspace - ignored */ @@ -586,13 +395,10 @@ asmb(void) lputl(0xe1a0f00e); /* B (R14) - zero init return */ break; case 2: /* plan 9 */ - if(dlm) - lput(0x80000000|0x647); /* magic */ - else - lput(0x647); /* magic */ + lput(0x647); /* magic */ lput(textsize); /* sizes */ - lput(datsize); - lput(bsssize); + lput(segdata.filelen); + lput(segdata.len - segdata.filelen); lput(symsize); /* nsyms */ lput(entryvalue()); /* va of entry */ lput(0L); @@ -601,8 +407,8 @@ asmb(void) case 3: /* boot for NetBSD */ lput((143<<16)|0413); /* magic */ lputl(rnd(HEADR+textsize, 4096)); - lputl(rnd(datsize, 4096)); - lputl(bsssize); + lputl(rnd(segdata.filelen, 4096)); + lputl(segdata.len - segdata.filelen); lputl(symsize); /* nsyms */ lputl(entryvalue()); /* va of entry */ lputl(0L); @@ -650,41 +456,8 @@ asmb(void) phsh(ph, sh); } - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_X+PF_R; - ph->vaddr = va; - ph->paddr = va; - ph->off = fo; - ph->filesz = w; - ph->memsz = w; - ph->align = INITRND; - - fo = rnd(fo+w, INITRND); - va = rnd(va+w, INITRND); - w = datsize; - - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_W+PF_R; - ph->off = fo; - ph->vaddr = va; - ph->paddr = va; - ph->filesz = w; - ph->memsz = w+bsssize; - ph->align = INITRND; - - if(!debug['s']) { - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_R; - ph->off = symo; - ph->vaddr = symdatva; - ph->paddr = symdatva; - ph->filesz = rnd(8+symsize+lcsize, INITRND); - ph->memsz = rnd(8+symsize+lcsize, INITRND); - ph->align = INITRND; - } + elfphload(&segtext); + elfphload(&segdata); /* Dynamic linking sections */ if (!debug['d']) { /* -d suppresses dynamic loader format */ @@ -766,81 +539,23 @@ asmb(void) ph->flags = PF_W+PF_R; ph->align = 4; - fo = ELFRESERVE; - va = startva + fo; - w = textsize; - - /* - * The alignments are bigger than they really need - * to be here, but they are necessary to keep the - * arm strip from moving everything around. - */ - - sh = newElfShdr(elfstr[ElfStrText]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_EXECINSTR; - sh->addr = va; - sh->off = fo; - sh->size = w; - sh->addralign = ELFRESERVE; - - fo = rnd(fo+w, INITRND); - va = rnd(va+w, INITRND); - w = datsize; - - sh = newElfShdr(elfstr[ElfStrData]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_WRITE+SHF_ALLOC; - sh->addr = va + elfdatsize; - sh->off = fo + elfdatsize; - sh->size = w - elfdatsize; - sh->addralign = INITRND; - - fo += w; - va += w; - w = bsssize; - - sh = newElfShdr(elfstr[ElfStrBss]); - sh->type = SHT_NOBITS; - sh->flags = SHF_WRITE+SHF_ALLOC; - sh->addr = va; - sh->off = fo; - sh->size = w; - sh->addralign = 4; + for(sect=segtext.sect; sect!=nil; sect=sect->next) + elfshbits(sect); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfshbits(sect); if (!debug['s']) { - fo = symo; - w = 8; - sh = newElfShdr(elfstr[ElfStrGosymtab]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; - sh->addralign = INITRND; - sh->addr = symdatva; - - fo += w; - w = symsize; - - sh = newElfShdr(elfstr[ElfStrGosymtab]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; sh->addralign = 1; - sh->addr = symdatva + 8; - - fo += w; - w = lcsize; + shsym(sh, lookup("symtab", 0)); sh = newElfShdr(elfstr[ElfStrGopclntab]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; sh->addralign = 1; - sh->addr = symdatva + 8 + lcsize; + shsym(sh, lookup("pclntab", 0)); } sh = newElfShstrtab(elfstr[ElfStrShstrtab]); @@ -879,27 +594,16 @@ asmb(void) } cflush(); if(debug['c']){ - print("textsize=%ld\n", textsize); - print("datsize=%ld\n", datsize); - print("bsssize=%ld\n", bsssize); - print("symsize=%ld\n", symsize); - print("lcsize=%ld\n", lcsize); - print("total=%ld\n", textsize+datsize+bsssize+symsize+lcsize); + print("textsize=%d\n", textsize); + print("datsize=%d\n", segdata.filelen); + print("bsssize=%d\n", segdata.len - segdata.filelen); + print("symsize=%d\n", symsize); + print("lcsize=%d\n", lcsize); + print("total=%d\n", textsize+segdata.len+symsize+lcsize); } } void -strnput(char *s, int n) -{ - for(; *s; s++){ - cput(*s); - n--; - } - for(; n > 0; n--) - cput(0); -} - -void cput(int c) { cbp[0] = c; @@ -987,7 +691,7 @@ cflush(void) /* no bug if cbc < 0 since obuf(cbuf) followed by ibuf in buf! */ n = sizeof(buf.cbuf) - cbc; if(n) - write(cout, buf.cbuf, n); + ewrite(cout, buf.cbuf, n); cbp = buf.cbuf; cbc = sizeof(buf.cbuf); } @@ -996,223 +700,16 @@ void nopstat(char *f, Count *c) { if(c->outof) - Bprint(&bso, "%s delay %ld/%ld (%.2f)\n", f, + Bprint(&bso, "%s delay %d/%d (%.2f)\n", f, c->outof - c->count, c->outof, (double)(c->outof - c->count)/c->outof); } -void -asmsym(void) -{ - Prog *p; - Auto *a; - Sym *s; - int h; - - s = lookup("etext", 0); - if(s->type == STEXT) - putsymb(s->name, 'T', s->value, s->version); - - for(h=0; h<NHASH; h++) - for(s=hash[h]; s!=S; s=s->link) - switch(s->type) { - case SCONST: - putsymb(s->name, 'D', s->value, s->version); - continue; - - case SDATA: - case SELFDATA: - putsymb(s->name, 'D', s->value+INITDAT, s->version); - continue; - - case SBSS: - putsymb(s->name, 'B', s->value+INITDAT, s->version); - continue; - - case SFIXED: - putsymb(s->name, 'B', s->value, s->version); - continue; - - case SSTRING: - putsymb(s->name, 'T', s->value, s->version); - continue; - - case SFILE: - putsymb(s->name, 'f', s->value, s->version); - continue; - } - - for(p=textp; p!=P; p=p->cond) { - s = p->from.sym; - if(s->type != STEXT && s->type != SLEAF) - continue; - - /* filenames first */ - for(a=p->to.autom; a; a=a->link) - if(a->type == D_FILE) - putsymb(a->asym->name, 'z', a->aoffset, 0); - else - if(a->type == D_FILE1) - putsymb(a->asym->name, 'Z', a->aoffset, 0); - - if(!s->reachable) - continue; - - if(s->type == STEXT) - putsymb(s->name, 'T', s->value, s->version); - else - putsymb(s->name, 'L', s->value, s->version); - - /* frame, auto and param after */ - putsymb(".frame", 'm', p->to.offset+4, 0); - for(a=p->to.autom; a; a=a->link) - if(a->type == D_AUTO) - putsymb(a->asym->name, 'a', -a->aoffset, 0); - else - if(a->type == D_PARAM) - putsymb(a->asym->name, 'p', a->aoffset, 0); - } - if(debug['v'] || debug['n']) - Bprint(&bso, "symsize = %lud\n", symsize); - Bflush(&bso); -} - -void -putsymb(char *s, int t, int32 v, int ver) -{ - int i, f; - - if(t == 'f') - s++; - lput(v); - if(ver) - t += 'a' - 'A'; - cput(t+0x80); /* 0x80 is variable length */ - - if(t == 'Z' || t == 'z') { - cput(s[0]); - for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) { - cput(s[i]); - cput(s[i+1]); - } - cput(0); - cput(0); - i++; - } - else { - for(i=0; s[i]; i++) - cput(s[i]); - cput(0); - } - // TODO(rsc): handle go parameter - lput(0); - - symsize += 4 + 1 + i + 1 + 4; - - if(debug['n']) { - if(t == 'z' || t == 'Z') { - Bprint(&bso, "%c %.8lux ", t, v); - for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) { - f = ((s[i]&0xff) << 8) | (s[i+1]&0xff); - Bprint(&bso, "/%x", f); - } - Bprint(&bso, "\n"); - return; - } - if(ver) - Bprint(&bso, "%c %.8lux %s<%d>\n", t, v, s, ver); - else - Bprint(&bso, "%c %.8lux %s\n", t, v, s); - } -} - -#define MINLC 4 -void -asmlc(void) -{ - int32 oldpc, oldlc; - Prog *p; - int32 v, s; - - oldpc = INITTEXT; - oldlc = 0; - for(p = firstp; p != P; p = p->link) { - setarch(p); - if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) { - if(p->as == ATEXT) - curtext = p; - if(debug['L']) - Bprint(&bso, "%6lux %P\n", - p->pc, p); - continue; - } - if(debug['L']) - Bprint(&bso, "\t\t%6ld", lcsize); - v = (p->pc - oldpc) / MINLC; - while(v) { - s = 127; - if(v < 127) - s = v; - cput(s+128); /* 129-255 +pc */ - if(debug['L']) - Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128); - v -= s; - lcsize++; - } - s = p->line - oldlc; - oldlc = p->line; - oldpc = p->pc + MINLC; - if(s > 64 || s < -64) { - cput(0); /* 0 vv +lc */ - cput(s>>24); - cput(s>>16); - cput(s>>8); - cput(s); - if(debug['L']) { - if(s > 0) - Bprint(&bso, " lc+%ld(%d,%ld)\n", - s, 0, s); - else - Bprint(&bso, " lc%ld(%d,%ld)\n", - s, 0, s); - Bprint(&bso, "%6lux %P\n", - p->pc, p); - } - lcsize += 5; - continue; - } - if(s > 0) { - cput(0+s); /* 1-64 +lc */ - if(debug['L']) { - Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s); - Bprint(&bso, "%6lux %P\n", - p->pc, p); - } - } else { - cput(64-s); /* 65-128 -lc */ - if(debug['L']) { - Bprint(&bso, " lc%ld(%ld)\n", s, 64-s); - Bprint(&bso, "%6lux %P\n", - p->pc, p); - } - } - lcsize++; - } - while(lcsize & 1) { - s = 129; - cput(s); - lcsize++; - } - if(debug['v'] || debug['L']) - Bprint(&bso, "lcsize = %ld\n", lcsize); - Bflush(&bso); -} - static void outt(int32 f, int32 l) { if(debug['L']) - Bprint(&bso, "tmap: %lux-%lux\n", f, l); + Bprint(&bso, "tmap: %ux-%ux\n", f, l); lput(f); lput(l); } @@ -1227,186 +724,46 @@ asmthumbmap(void) return; pc = 0; lastt = -1; - for(p = firstp; p != P; p = p->link){ + for(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; pc = p->pc - INITTEXT; - if(p->as == ATEXT){ - setarch(p); - if(thumb){ - if(p->from.sym->foreign){ // 8 bytes of ARM first - if(lastt >= 0){ - outt(lastt, pc-1); - lastt = -1; - } - pc += 8; - } - if(lastt < 0) - lastt = pc; - } - else{ - if(p->from.sym->foreign){ // 4 bytes of THUMB first - if(lastt < 0) - lastt = pc; - pc += 4; - } + setarch(p); + if(thumb){ + if(p->from.sym->foreign){ // 8 bytes of ARM first if(lastt >= 0){ outt(lastt, pc-1); lastt = -1; } + pc += 8; } + if(lastt < 0) + lastt = pc; } - } - if(lastt >= 0) - outt(lastt, pc+1); -} - -void -datblk(int32 s, int32 n, int str) -{ - Sym *v; - Prog *p; - char *cast; - int32 a, l, fl, j, d; - int i, c; - - memset(buf.dbuf, 0, n+100); - for(p = datap; p != P; p = p->link) { - if(str != (p->from.sym->type == SSTRING)) - continue; - curp = p; - a = p->from.sym->value + p->from.offset; - l = a - s; - c = p->reg; - i = 0; - if(l < 0) { - if(l+c <= 0) - continue; - while(l < 0) { - l++; - i++; - } - } - if(l >= n) - continue; - if(p->as != AINIT && p->as != ADYNT) { - for(j=l+(c-i)-1; j>=l; j--) - if(buf.dbuf[j]) { - print("%P\n", p); - diag("multiple initialization"); - break; - } - } - switch(p->to.type) { - default: - diag("unknown mode in initialization%P", p); - break; - - case D_FCONST: - switch(c) { - default: - case 4: - fl = ieeedtof(p->to.ieee); - cast = (char*)&fl; - for(; i<c; i++) { - buf.dbuf[l] = cast[fnuxi4[i]]; - l++; - } - break; - case 8: - cast = (char*)p->to.ieee; - for(; i<c; i++) { - buf.dbuf[l] = cast[fnuxi8[i]]; - l++; - } - break; - } - break; - - case D_SCONST: - for(; i<c; i++) { - buf.dbuf[l] = p->to.sval[i]; - l++; - } - break; - - case D_CONST: - d = p->to.offset; - v = p->to.sym; - if(v) { - switch(v->type) { - case SUNDEF: - ckoff(v, d); - d += v->value; - break; - case STEXT: - case SLEAF: - d += v->value; -#ifdef CALLEEBX - d += fnpinc(v); -#else - if(v->thumb) - d++; // T bit -#endif - break; - case SSTRING: - d += v->value; - break; - case SDATA: - case SBSS: - if(p->to.type == D_SIZE) - d += v->size; - else - d += v->value + INITDAT; - break; - } - if(dlm) - dynreloc(v, a+INITDAT, 1); - } - cast = (char*)&d; - switch(c) { - default: - diag("bad nuxi %d %d%P", c, i, curp); - break; - case 1: - for(; i<c; i++) { - buf.dbuf[l] = cast[inuxi1[i]]; - l++; - } - break; - case 2: - for(; i<c; i++) { - buf.dbuf[l] = cast[inuxi2[i]]; - l++; - } - break; - case 4: - for(; i<c; i++) { - buf.dbuf[l] = cast[inuxi4[i]]; - l++; - } - break; - } - break; - - case D_SBIG: - if(debug['a'] && i == 0) { - Bprint(&bso, "\t%P\n", curp); + else{ + if(p->from.sym->foreign){ // 4 bytes of THUMB first + if(lastt < 0) + lastt = pc; + pc += 4; } - for(; i<c; i++) { - buf.dbuf[l] = p->to.sbig[i]; - l++; + if(lastt >= 0){ + outt(lastt, pc-1); + lastt = -1; } - break; } + if(cursym->next == nil) + for(; p != P; p = p->link) + pc = p->pc = INITTEXT; } - write(cout, buf.dbuf, n); + if(lastt >= 0) + outt(lastt, pc+1); } void -asmout(Prog *p, Optab *o) +asmout(Prog *p, Optab *o, int32 *out) { int32 o1, o2, o3, o4, o5, o6, v; int r, rf, rt, rt2; - Sym *s; + Reloc *rel; PP = p; o1 = 0; @@ -1416,7 +773,7 @@ PP = p; o5 = 0; o6 = 0; armsize += o->size; -if(debug['P']) print("%ulx: %P type %d\n", (uint32)(p->pc), p, o->type); +if(debug['P']) print("%ux: %P type %d\n", (uint32)(p->pc), p, o->type); switch(o->type) { default: diag("unknown asm %d", o->type); @@ -1424,7 +781,7 @@ if(debug['P']) print("%ulx: %P type %d\n", (uint32)(p->pc), p, o->type); break; case 0: /* pseudo ops */ -if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym->name, p->from.sym->thumb, p->from.sym->foreign, p->from.sym->fnptr, p->from.sym->used); +if(debug['G']) print("%ux: %s: arm %d %d %d\n", (uint32)(p->pc), p->from.sym->name, p->from.sym->thumb, p->from.sym->foreign, p->from.sym->fnptr); break; case 1: /* op R,[R],R */ @@ -1485,14 +842,7 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym case 5: /* bra s */ v = -8; - if(p->cond == UP) { - s = p->to.sym; - if(s->type != SUNDEF) - diag("bad branch sym type"); - v = (uint32)s->value >> (Roffset-2); - dynreloc(s, p->pc, 0); - } - else if(p->cond != P) + if(p->cond != P) v = (p->cond->pc - pc) - 8; #ifdef CALLEEBX if(p->as == ABL) @@ -1553,18 +903,17 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym break; case 11: /* word */ - switch(aclass(&p->to)) { - case C_LCON: - if(!dlm) - break; - if(p->to.name != D_EXTERN && p->to.name != D_STATIC) - break; - case C_ADDR: - if(p->to.sym->type == SUNDEF) - ckoff(p->to.sym, p->to.offset); - dynreloc(p->to.sym, p->pc, 1); - } + aclass(&p->to); o1 = instoffset; + if(p->to.sym != S) { + rel = addrel(cursym); + rel->off = pc - cursym->value; + rel->siz = 4; + rel->type = D_ADDR; + rel->sym = p->to.sym; + rel->add = p->to.offset; + o1 = 0; + } break; case 12: /* movw $lcon, reg */ @@ -1890,38 +1239,15 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym case 54: /* floating point arith */ o1 = oprrr(p->as, p->scond); - if(p->from.type == D_FCONST) { - rf = chipfloat(p->from.ieee); - if(rf < 0){ - diag("invalid floating-point immediate\n%P", p); - rf = 0; - } - rf |= (1<<3); - } else - rf = p->from.reg; + rf = p->from.reg; rt = p->to.reg; r = p->reg; - if(p->to.type == D_NONE) - rt = 0; /* CMP[FD] */ - else if(o1 & (1<<15)) - r = 0; /* monadic */ - else if(r == NREG) + if(r == NREG) { r = rt; - o1 |= rf | (r<<16) | (rt<<12); - break; - - case 55: /* floating point fix and float */ - o1 = oprrr(p->as, p->scond); - rf = p->from.reg; - rt = p->to.reg; - if(p->to.type == D_NONE){ - rt = 0; - diag("to.type==D_NONE (asm/fp)"); + if(p->as == AMOVF || p->as == AMOVD) + r = 0; } - if(p->from.type == D_REG) - o1 |= (rf<<12) | (rt<<16); - else - o1 |= rf | (rt<<12); + o1 |= rf | (r<<16) | (rt<<12); break; case 56: /* move to FP[CS]R */ @@ -1983,11 +1309,8 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym break; case 63: /* bcase */ - if(p->cond != P) { + if(p->cond != P) o1 = p->cond->pc; - if(dlm) - dynreloc(S, p->pc, 1); - } break; /* reloc ops */ @@ -2114,7 +1437,7 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym if(p->to.sym->thumb) v |= 1; // T bit o1 = olr(8, REGPC, REGTMP, p->scond&C_SCOND); // mov 8(PC), Rtmp - o2 = oprrr(AADD, p->scond) | immrot(8) | (REGPC<<16) | (REGLINK<<12); // add 8,PC, LR + o2 = oprrr(AADD, p->scond) | immrot(8) | (REGPC<<16) | (REGLINK<<12); // add 8,PC, LR o3 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | REGTMP; // bx Rtmp o4 = opbra(AB, 14); // B over o6 o5 = v; @@ -2132,7 +1455,7 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym o1 |= immrot(instoffset); o1 |= p->to.reg << 16; o1 |= REGTMP << 12; - o2 = oprrr(AADD, p->scond) | immrot(0) | (REGPC<<16) | (REGLINK<<12); // mov PC, LR + o2 = oprrr(AADD, p->scond) | immrot(0) | (REGPC<<16) | (REGLINK<<12); // mov PC, LR o3 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | REGTMP; // BX Rtmp break; case 76: /* bx O(R) when returning from fn*/ @@ -2164,35 +1487,114 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym o1 |= p->to.reg << 12; o1 |= (p->scond & C_SCOND) << 28; break; + case 80: /* fmov zfcon,freg */ + if((p->scond & C_SCOND) != C_SCOND_NONE) + diag("floating point cannot be conditional"); // cant happen + o1 = 0xf3000110; // EOR 64 + + // always clears the double float register + r = p->to.reg; + o1 |= r << 0; + o1 |= r << 12; + o1 |= r << 16; + break; + case 81: /* fmov sfcon,freg */ + o1 = 0x0eb00a00; // VMOV imm 32 + if(p->as == AMOVD) + o1 = 0xeeb00b00; // VMOV imm 64 + o1 |= (p->scond & C_SCOND) << 28; + o1 |= p->to.reg << 12; + v = chipfloat(&p->from.ieee); + o1 |= (v&0xf) << 0; + o1 |= (v&0xf0) << 12; + break; + case 82: /* fcmp freg,freg, */ + o1 = oprrr(p->as, p->scond); + o1 |= (p->reg<<12) | (p->from.reg<<0); + o2 = 0x0ef1fa10; // VMRS R15 + o2 |= (p->scond & C_SCOND) << 28; + break; + case 83: /* fcmp freg,, */ + o1 = oprrr(p->as, p->scond); + o1 |= (p->from.reg<<12) | (1<<16); + o2 = 0x0ef1fa10; // VMRS R15 + o2 |= (p->scond & C_SCOND) << 28; + break; + case 84: /* movfw freg,freg - truncate float-to-fix */ + o1 = oprrr(p->as, p->scond); + o1 |= (p->from.reg<<0); + o1 |= (p->to.reg<<12); + break; + case 85: /* movwf freg,freg - fix-to-float */ + o1 = oprrr(p->as, p->scond); + o1 |= (p->from.reg<<0); + o1 |= (p->to.reg<<12); + break; + case 86: /* movfw freg,reg - truncate float-to-fix */ + // macro for movfw freg,FTMP; movw FTMP,reg + o1 = oprrr(p->as, p->scond); + o1 |= (p->from.reg<<0); + o1 |= (FREGTMP<<12); + o2 = oprrr(AMOVFW+AEND, p->scond); + o2 |= (FREGTMP<<16); + o2 |= (p->to.reg<<12); + break; + case 87: /* movwf reg,freg - fix-to-float */ + // macro for movw reg,FTMP; movwf FTMP,freg + o1 = oprrr(AMOVWF+AEND, p->scond); + o1 |= (p->from.reg<<12); + o1 |= (FREGTMP<<16); + o2 = oprrr(p->as, p->scond); + o2 |= (FREGTMP<<0); + o2 |= (p->to.reg<<12); + break; + case 88: /* movw reg,freg */ + o1 = oprrr(AMOVWF+AEND, p->scond); + o1 |= (p->from.reg<<12); + o1 |= (p->to.reg<<16); + break; + case 89: /* movw freg,reg */ + o1 = oprrr(AMOVFW+AEND, p->scond); + o1 |= (p->from.reg<<16); + o1 |= (p->to.reg<<12); + break; } + + out[0] = o1; + out[1] = o2; + out[2] = o3; + out[3] = o4; + out[4] = o5; + out[5] = o6; + return; v = p->pc; switch(o->size) { default: if(debug['a']) - Bprint(&bso, " %.8lux:\t\t%P\n", v, p); + Bprint(&bso, " %.8ux:\t\t%P\n", v, p); break; case 4: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p); + Bprint(&bso, " %.8ux: %.8ux\t%P\n", v, o1, p); lputl(o1); break; case 8: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", v, o1, o2, p); + Bprint(&bso, " %.8ux: %.8ux %.8ux%P\n", v, o1, o2, p); lputl(o1); lputl(o2); break; case 12: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux%P\n", v, o1, o2, o3, p); + Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux%P\n", v, o1, o2, o3, p); lputl(o1); lputl(o2); lputl(o3); break; case 16: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux%P\n", + Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux %.8ux%P\n", v, o1, o2, o3, o4, p); lputl(o1); lputl(o2); @@ -2201,7 +1603,7 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym break; case 20: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux%P\n", + Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux%P\n", v, o1, o2, o3, o4, o5, p); lputl(o1); lputl(o2); @@ -2211,7 +1613,7 @@ if(debug['G']) print("%ulx: %s: arm %d %d %d %d\n", (uint32)(p->pc), p->from.sym break; case 24: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux%P\n", + Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux%P\n", v, o1, o2, o3, o4, o5, o6, p); lputl(o1); lputl(o2); @@ -2262,26 +1664,51 @@ oprrr(int a, int sc) case ASRA: return o | (0xd<<21) | (2<<5); case ASWI: return o | (0xf<<24); - case AADDD: return o | (0xe<<24) | (0x0<<20) | (1<<8) | (1<<7); - case AADDF: return o | (0xe<<24) | (0x0<<20) | (1<<8); - case AMULD: return o | (0xe<<24) | (0x1<<20) | (1<<8) | (1<<7); - case AMULF: return o | (0xe<<24) | (0x1<<20) | (1<<8); - case ASUBD: return o | (0xe<<24) | (0x2<<20) | (1<<8) | (1<<7); - case ASUBF: return o | (0xe<<24) | (0x2<<20) | (1<<8); - case ADIVD: return o | (0xe<<24) | (0x4<<20) | (1<<8) | (1<<7); - case ADIVF: return o | (0xe<<24) | (0x4<<20) | (1<<8); - case ACMPD: - case ACMPF: return o | (0xe<<24) | (0x9<<20) | (0xF<<12) | (1<<8) | (1<<4); /* arguably, ACMPF should expand to RNDF, CMPD */ - - case AMOVF: - case AMOVDF: return o | (0xe<<24) | (0x0<<20) | (1<<15) | (1<<8); - case AMOVD: - case AMOVFD: return o | (0xe<<24) | (0x0<<20) | (1<<15) | (1<<8) | (1<<7); - - case AMOVWF: return o | (0xe<<24) | (0<<20) | (1<<8) | (1<<4); - case AMOVWD: return o | (0xe<<24) | (0<<20) | (1<<8) | (1<<4) | (1<<7); - case AMOVFW: return o | (0xe<<24) | (1<<20) | (1<<8) | (1<<4); - case AMOVDW: return o | (0xe<<24) | (1<<20) | (1<<8) | (1<<4) | (1<<7); + case AADDD: return o | (0xe<<24) | (0x3<<20) | (0xb<<8) | (0<<4); + case AADDF: return o | (0xe<<24) | (0x3<<20) | (0xa<<8) | (0<<4); + case ASUBD: return o | (0xe<<24) | (0x3<<20) | (0xb<<8) | (4<<4); + case ASUBF: return o | (0xe<<24) | (0x3<<20) | (0xa<<8) | (4<<4); + case AMULD: return o | (0xe<<24) | (0x2<<20) | (0xb<<8) | (0<<4); + case AMULF: return o | (0xe<<24) | (0x2<<20) | (0xa<<8) | (0<<4); + case ADIVD: return o | (0xe<<24) | (0x8<<20) | (0xb<<8) | (0<<4); + case ADIVF: return o | (0xe<<24) | (0x8<<20) | (0xa<<8) | (0<<4); + case ACMPD: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xb<<8) | (0xc<<4); + case ACMPF: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xa<<8) | (0xc<<4); + + case AMOVF: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xa<<8) | (4<<4); + case AMOVD: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xb<<8) | (4<<4); + + case AMOVDF: return o | (0xe<<24) | (0xb<<20) | (7<<16) | (0xa<<8) | (0xc<<4) | + (1<<8); // dtof + case AMOVFD: return o | (0xe<<24) | (0xb<<20) | (7<<16) | (0xa<<8) | (0xc<<4) | + (0<<8); // dtof + + case AMOVWF: + if((sc & C_UBIT) == 0) + o |= 1<<7; /* signed */ + return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | + (0<<18) | (0<<8); // toint, double + case AMOVWD: + if((sc & C_UBIT) == 0) + o |= 1<<7; /* signed */ + return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | + (0<<18) | (1<<8); // toint, double + + case AMOVFW: + if((sc & C_UBIT) == 0) + o |= 1<<16; /* signed */ + return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | + (1<<18) | (0<<8) | (1<<7); // toint, double, trunc + case AMOVDW: + if((sc & C_UBIT) == 0) + o |= 1<<16; /* signed */ + return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | + (1<<18) | (1<<8) | (1<<7); // toint, double, trunc + + case AMOVWF+AEND: // copy WtoF + return o | (0xe<<24) | (0x0<<20) | (0xb<<8) | (1<<4); + case AMOVFW+AEND: // copy FtoW + return o | (0xe<<24) | (0x1<<20) | (0xb<<8) | (1<<4); } diag("bad rrr %d", a); prasm(curp); @@ -2337,12 +1764,13 @@ olr(int32 v, int b, int r, int sc) o |= 1 << 23; if(sc & C_WBIT) o |= 1 << 21; - o |= (0x1<<26) | (1<<20); + o |= (1<<26) | (1<<20); if(v < 0) { + if(sc & C_UBIT) diag(".U on neg offset"); v = -v; o ^= 1 << 23; } - if(v >= (1<<12)) + if(v >= (1<<12) || v < 0) diag("literal span too large: %d (R%d)\n%P", v, b, PP); o |= v; o |= b << 16; @@ -2367,7 +1795,7 @@ olhr(int32 v, int b, int r, int sc) v = -v; o ^= 1 << 23; } - if(v >= (1<<8)) + if(v >= (1<<8) || v < 0) diag("literal span too large: %d (R%d)\n%P", v, b, PP); o |= (v&0xf)|((v>>4)<<8)|(1<<22); o |= b << 16; @@ -2434,25 +1862,25 @@ ofsr(int a, int r, int32 v, int b, int sc, Prog *p) o |= 1 << 24; if(sc & C_WBIT) o |= 1 << 21; - o |= (6<<25) | (1<<24) | (1<<23); + o |= (6<<25) | (1<<24) | (1<<23) | (10<<8); if(v < 0) { v = -v; o ^= 1 << 23; } if(v & 3) diag("odd offset for floating point op: %d\n%P", v, p); - else if(v >= (1<<10)) + else + if(v >= (1<<10) || v < 0) diag("literal span too large: %d\n%P", v, p); o |= (v>>2) & 0xFF; o |= b << 16; o |= r << 12; - o |= 1 << 8; switch(a) { default: diag("bad fst %A", a); case AMOVD: - o |= 1<<15; + o |= 1 << 8; case AMOVF: break; } @@ -2481,28 +1909,104 @@ omvl(Prog *p, Adr *a, int dr) return o1; } -static Ieee chipfloats[] = { - {0x00000000, 0x00000000}, /* 0 */ - {0x00000000, 0x3ff00000}, /* 1 */ - {0x00000000, 0x40000000}, /* 2 */ - {0x00000000, 0x40080000}, /* 3 */ - {0x00000000, 0x40100000}, /* 4 */ - {0x00000000, 0x40140000}, /* 5 */ - {0x00000000, 0x3fe00000}, /* .5 */ - {0x00000000, 0x40240000}, /* 10 */ -}; +int +chipzero(Ieee *e) +{ + if(e->l != 0 || e->h != 0) + return -1; + return 0; +} int chipfloat(Ieee *e) { - Ieee *p; int n; + ulong h; - for(n = sizeof(chipfloats)/sizeof(chipfloats[0]); --n >= 0;){ - p = &chipfloats[n]; - if(p->l == e->l && p->h == e->h) - return n; - } + if(e->l != 0 || (e->h&0xffff) != 0) + goto no; + h = e->h & 0x7fc00000; + if(h != 0x40000000 && h != 0x3fc00000) + goto no; + n = 0; + + // sign bit (a) + if(e->h & 0x80000000) + n |= 1<<7; + + // exp sign bit (b) + if(h == 0x3fc00000) + n |= 1<<6; + + // rest of exp and mantissa (cd-efgh) + n |= (e->h >> 16) & 0x3f; + +//print("match %.8lux %.8lux %d\n", e->l, e->h, n); + return n; + +no: return -1; } + +void +genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) +{ + Auto *a; + Sym *s; + int h; + + s = lookup("etext", 0); + if(s->type == STEXT) + put(s, s->name, 'T', s->value, s->size, s->version, 0); + + for(h=0; h<NHASH; h++) { + for(s=hash[h]; s!=S; s=s->hash) { + switch(s->type) { + case SCONST: + case SRODATA: + case SDATA: + case SELFDATA: + if(!s->reachable) + continue; + put(s, s->name, 'D', s->value, s->size, s->version, s->gotype); + continue; + + case SBSS: + if(!s->reachable) + continue; + put(s, s->name, 'B', s->value, s->size, s->version, s->gotype); + continue; + + case SFILE: + put(nil, s->name, 'f', s->value, 0, s->version, 0); + continue; + } + } + } + + for(s = textp; s != nil; s = s->next) { + /* filenames first */ + for(a=s->autom; a; a=a->link) + if(a->type == D_FILE) + put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); + else + if(a->type == D_FILE1) + put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); + + put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); + + /* frame, auto and param after */ + put(nil, ".frame", 'm', s->text->to.offset+4, 0, 0, 0); + + for(a=s->autom; a; a=a->link) + if(a->type == D_AUTO) + put(nil, a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype); + else + if(a->type == D_PARAM) + put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); + } + if(debug['v'] || debug['n']) + Bprint(&bso, "symsize = %ud\n", symsize); + Bflush(&bso); +} diff --git a/src/cmd/5l/doc.go b/src/cmd/5l/doc.go index b09995d71..6f7408116 100644 --- a/src/cmd/5l/doc.go +++ b/src/cmd/5l/doc.go @@ -20,8 +20,8 @@ Original options are listed in the link above. Options new in this version: --L dir1,dir2,.. - Search for libraries (package files) in the comma-separated list of directories. +-L dir1 -L dir2 + Search for libraries (package files) in dir1, dir2, etc. The default is the single location $GOROOT/pkg/$GOOS_arm. -r dir1:dir2:... Set the dynamic linker search path when using ELF. diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h index c6659cfab..4e7ccea88 100644 --- a/src/cmd/5l/l.h +++ b/src/cmd/5l/l.h @@ -45,20 +45,21 @@ enum /* do not undefine this - code will be removed eventually */ #define CALLEEBX +#define dynptrsize 0 + typedef struct Adr Adr; typedef struct Sym Sym; typedef struct Autom Auto; typedef struct Prog Prog; +typedef struct Reloc Reloc; typedef struct Optab Optab; typedef struct Oprang Oprang; typedef uchar Opcross[32][2][32]; typedef struct Count Count; -typedef struct Use Use; #define P ((Prog*)0) #define S ((Sym*)0) -#define U ((Use*)0) -#define TNAME (curtext&&curtext->from.sym?curtext->from.sym->name:noname) +#define TNAME (cursym?cursym->name:noname) struct Adr { @@ -66,14 +67,10 @@ struct Adr { int32 u0offset; char* u0sval; - Ieee* u0ieee; + Ieee u0ieee; char* u0sbig; } u0; - union - { - Auto* u1autom; - Sym* u1sym; - } u1; + Sym* sym; char type; uchar index; // not used on arm, required by ld/go.c char reg; @@ -85,11 +82,18 @@ struct Adr #define offset u0.u0offset #define sval u0.u0sval +#define scon sval #define ieee u0.u0ieee #define sbig u0.u0sbig -#define autom u1.u1autom -#define sym u1.u1sym +struct Reloc +{ + int32 off; + uchar siz; + int16 type; + int32 add; + Sym* sym; +}; struct Prog { @@ -112,35 +116,51 @@ struct Prog uchar reg; uchar align; }; + #define regused u0.u0regused #define forwd u0.u0forwd +#define datasize reg +#define textflag reg struct Sym { - char *name; + char* name; short type; short version; - short become; - short frame; - uchar subtype; uchar dupok; uchar reachable; uchar dynexport; + uchar leaf; + int32 dynid; + int32 plt; + int32 got; int32 value; int32 sig; int32 size; - uchar used; + uchar special; uchar thumb; // thumb code uchar foreign; // called by arm if thumb, by thumb if arm uchar fnptr; // used as fn ptr - Use* use; - Sym* link; - Prog* text; - Prog* data; + Sym* hash; // in hash table + Sym* next; // in text or data list + Sym* sub; // in SSUB list + Sym* outer; // container of sub Sym* gotype; char* file; char* dynimpname; char* dynimplib; + + // STEXT + Auto* autom; + Prog* text; + + // SDATA, SBSS + uchar* p; + int32 np; + int32 maxp; + Reloc* r; + int32 nr; + int32 maxr; }; #define SIGNINTERN (1729*325*1729) @@ -174,34 +194,24 @@ struct Count int32 count; int32 outof; }; -struct Use -{ - Prog* p; /* use */ - Prog* ct; /* curtext */ - Use* link; -}; enum { Sxxx, - + + /* order here is order in output file */ STEXT = 1, + SRODATA, + SELFDATA, SDATA, SBSS, - SDATA1, + SXREF, - SLEAF, SFILE, SCONST, - SSTRING, - SUNDEF, - SREMOVED, + SDYNIMPORT, - SIMPORT, - SEXPORT, - - SFIXED, - SELFDATA, + SSUB = 1<<8, LFROM = 1<<0, LTO = 1<<1, @@ -221,7 +231,9 @@ enum C_SCON, /* 0xffff */ C_BCON, /* thumb */ C_LCON, - C_FCON, + C_ZFCON, + C_SFCON, + C_LFCON, C_GCON, /* thumb */ C_RACON, @@ -229,9 +241,6 @@ enum C_LACON, C_GACON, /* thumb */ - C_RECON, - C_LECON, - C_SBRA, C_LBRA, C_GBRA, /* thumb */ @@ -242,12 +251,6 @@ enum C_SAUTO, /* -0xfff to 0xfff */ C_LAUTO, - C_HEXT, - C_FEXT, - C_HFEXT, - C_SEXT, - C_LEXT, - C_HOREG, C_FOREG, C_HFOREG, @@ -262,7 +265,7 @@ enum C_HREG, C_OFFPC, /* thumb */ - C_ADDR, /* relocatable address */ + C_ADDR, /* reference to relocatable address */ C_GOK, @@ -271,7 +274,6 @@ enum LABEL = 1<<1, LEAF = 1<<2, - BIG = (1<<12)-4, STRINGSZ = 200, NHASH = 10007, NHUNK = 100000, @@ -279,9 +281,7 @@ enum NENT = 100, MAXIO = 8192, MAXHIST = 20, /* limit of path elements for history symbols */ - - Roffset = 22, /* no. bits for offset in relocation address */ - Rindex = 10, /* no. bits for index in relocation address */ + MINLC = 4, }; EXTERN union @@ -310,23 +310,18 @@ EXTERN int32 INITTEXT; /* text location */ EXTERN char* INITENTRY; /* entry point */ EXTERN int32 autosize; EXTERN Biobuf bso; -EXTERN int32 bsssize; EXTERN int cbc; EXTERN uchar* cbp; EXTERN int cout; EXTERN Auto* curauto; EXTERN Auto* curhist; EXTERN Prog* curp; -EXTERN Prog* curtext; -EXTERN Prog* datap; -EXTERN int32 datsize; +EXTERN Sym* cursym; +EXTERN Sym* datap; EXTERN int32 elfdatsize; EXTERN char debug[128]; -EXTERN Prog* edatap; -EXTERN Prog* etextp; -EXTERN Prog* firstp; +EXTERN Sym* etextp; EXTERN char* noname; -EXTERN int xrefresolv; EXTERN Prog* lastp; EXTERN int32 lcsize; EXTERN char literal[32]; @@ -341,7 +336,7 @@ EXTERN uchar repop[ALAST]; EXTERN char* rpath; EXTERN uint32 stroffset; EXTERN int32 symsize; -EXTERN Prog* textp; +EXTERN Sym* textp; EXTERN int32 textsize; EXTERN int version; EXTERN char xcmp[C_GOK+1][C_GOK+1]; @@ -352,14 +347,6 @@ EXTERN int thumb; EXTERN int seenthumb; EXTERN int armsize; -EXTERN int doexp, dlm; -EXTERN int imports, nimports; -EXTERN int exports, nexports; -EXTERN char* EXPTAB; -EXTERN Prog undefp; - -#define UP (&undefp) - extern char* anames[]; extern Optab optab[]; extern Optab thumboptab[]; @@ -384,6 +371,7 @@ EXTERN Prog* prog_modu; int Aconv(Fmt*); int Cconv(Fmt*); int Dconv(Fmt*); +int Iconv(Fmt*); int Nconv(Fmt*); int Oconv(Fmt*); int Pconv(Fmt*); @@ -393,36 +381,29 @@ int thumbaclass(Adr*, Prog*); void addhist(int32, int); Prog* appendp(Prog*); void asmb(void); -void asmdyn(void); -void asmlc(void); void asmthumbmap(void); -void asmout(Prog*, Optab*); +void asmout(Prog*, Optab*, int32*); void thumbasmout(Prog*, Optab*); -void asmsym(void); int32 atolwhex(char*); Prog* brloop(Prog*); void buildop(void); void thumbbuildop(void); void buildrep(int, int); void cflush(void); -void ckoff(Sym*, int32); +int chipzero(Ieee*); int chipfloat(Ieee*); int cmp(int, int); int compound(Prog*); double cputime(void); -void datblk(int32, int32, int); void diag(char*, ...); void divsig(void); void dodata(void); void doprof1(void); void doprof2(void); -void dynreloc(Sym*, int32, int); int32 entryvalue(void); void exchange(Prog*); -void export(void); void follow(void); void hputl(int); -void import(void); int isnop(Prog*); void listinit(void); Sym* lookup(char*, int); @@ -430,10 +411,8 @@ void cput(int); void hput(int32); void lput(int32); void lputl(int32); -void mkfwd(void); void* mysbrk(uint32); void names(void); -Prog* newdata(Sym *s, int o, int w, int t); void nocache(Prog*); int ocmp(const void*, const void*); int32 opirr(int); @@ -454,18 +433,17 @@ void prasm(Prog*); void prepend(Prog*, Prog*); Prog* prg(void); int pseudo(Prog*); -void putsymb(char*, int, int32, int); int32 regoff(Adr*); int relinv(int); int32 rnd(int32, int32); void softfloat(void); void span(void); void strnput(char*, int); +int32 symaddr(Sym*); void undef(void); void wput(int32); void wputl(ushort w); void xdefine(char*, int, int32); -void xfol(Prog*); void noops(void); int32 immrot(uint32); int32 immaddr(int32); @@ -475,7 +453,6 @@ int isbranch(Prog*); int fnpinc(Sym *); int fninc(Sym *); void thumbcount(void); -void reachable(void); void fnptrs(void); void doelf(void); diff --git a/src/cmd/5l/list.c b/src/cmd/5l/list.c index 9cbb5501c..b4df89587 100644 --- a/src/cmd/5l/list.c +++ b/src/cmd/5l/list.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Printing. + #include "l.h" #include "../ld/lib.h" @@ -42,6 +44,7 @@ listinit(void) fmtinstall('S', Sconv); fmtinstall('N', Nconv); fmtinstall('O', Oconv); // C_type constants + fmtinstall('I', Iconv); } void @@ -53,7 +56,6 @@ prasm(Prog *p) int Pconv(Fmt *fp) { - char str[STRINGSZ], *s; Prog *p; int a; @@ -62,42 +64,41 @@ Pconv(Fmt *fp) a = p->as; switch(a) { default: - s = str; - s += sprint(s, "(%d)", p->line); + fmtprint(fp, "(%d)", p->line); if(p->reg == NREG) - sprint(s, " %A%C %D,%D", + fmtprint(fp, " %A%C %D,%D", a, p->scond, &p->from, &p->to); else if(p->from.type != D_FREG) - sprint(s, " %A%C %D,R%d,%D", + fmtprint(fp, " %A%C %D,R%d,%D", a, p->scond, &p->from, p->reg, &p->to); else - sprint(s, " %A%C %D,F%d,%D", + fmtprint(fp, " %A%C %D,F%d,%D", a, p->scond, &p->from, p->reg, &p->to); break; case ASWPW: case ASWPBU: - sprint(str, "(%d) %A%C R%d,%D,%D", + fmtprint(fp, "(%d) %A%C R%d,%D,%D", p->line, a, p->scond, p->reg, &p->from, &p->to); break; case ADATA: - case AINIT: - case ADYNT: - sprint(str, "(%d) %A%C %D/%d,%D", + case AINIT_: + case ADYNT_: + fmtprint(fp, "(%d) %A%C %D/%d,%D", p->line, a, p->scond, &p->from, p->reg, &p->to); break; case AWORD: - sprint(str, "WORD %x", p->to.offset); + fmtprint(fp, "(%d) WORD %D", p->line, &p->to); break; case ADWORD: - sprint(str, "DWORD %x %x", p->from.offset, p->to.offset); + fmtprint(fp, "(%d) DWORD %D %D", p->line, &p->from, &p->to); break; } - return fmtstrcpy(fp, str); + return 0; } int @@ -164,98 +165,98 @@ Dconv(Fmt *fp) switch(a->type) { default: - sprint(str, "GOK-type(%d)", a->type); + snprint(str, sizeof str, "GOK-type(%d)", a->type); break; case D_NONE: str[0] = 0; if(a->name != D_NONE || a->reg != NREG || a->sym != S) - sprint(str, "%N(R%d)(NONE)", a, a->reg); + snprint(str, sizeof str, "%N(R%d)(NONE)", a, a->reg); break; case D_CONST: if(a->reg == NREG) - sprint(str, "$%N", a); + snprint(str, sizeof str, "$%N", a); else - sprint(str, "$%N(R%d)", a, a->reg); + snprint(str, sizeof str, "$%N(R%d)", a, a->reg); break; case D_CONST2: - sprint(str, "$%d-%d", a->offset, a->offset2); + snprint(str, sizeof str, "$%d-%d", a->offset, a->offset2); break; case D_SHIFT: v = a->offset; op = "<<>>->@>" + (((v>>5) & 3) << 1); if(v & (1<<4)) - sprint(str, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15); + snprint(str, sizeof str, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15); else - sprint(str, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31); + snprint(str, sizeof str, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31); if(a->reg != NREG) - sprint(str+strlen(str), "(R%d)", a->reg); + seprint(str+strlen(str), str+sizeof str, "(R%d)", a->reg); break; case D_OCONST: - sprint(str, "$*$%N", a); + snprint(str, sizeof str, "$*$%N", a); if(a->reg != NREG) - sprint(str, "%N(R%d)(CONST)", a, a->reg); + snprint(str, sizeof str, "%N(R%d)(CONST)", a, a->reg); break; case D_OREG: if(a->reg != NREG) - sprint(str, "%N(R%d)", a, a->reg); + snprint(str, sizeof str, "%N(R%d)", a, a->reg); else - sprint(str, "%N", a); + snprint(str, sizeof str, "%N", a); break; case D_REG: - sprint(str, "R%d", a->reg); + snprint(str, sizeof str, "R%d", a->reg); if(a->name != D_NONE || a->sym != S) - sprint(str, "%N(R%d)(REG)", a, a->reg); + snprint(str, sizeof str, "%N(R%d)(REG)", a, a->reg); break; case D_REGREG: - sprint(str, "(R%d,R%d)", a->reg, (int)a->offset); + snprint(str, sizeof str, "(R%d,R%d)", a->reg, (int)a->offset); if(a->name != D_NONE || a->sym != S) - sprint(str, "%N(R%d)(REG)", a, a->reg); + snprint(str, sizeof str, "%N(R%d)(REG)", a, a->reg); break; case D_FREG: - sprint(str, "F%d", a->reg); + snprint(str, sizeof str, "F%d", a->reg); if(a->name != D_NONE || a->sym != S) - sprint(str, "%N(R%d)(REG)", a, a->reg); + snprint(str, sizeof str, "%N(R%d)(REG)", a, a->reg); break; case D_PSR: switch(a->reg) { case 0: - sprint(str, "CPSR"); + snprint(str, sizeof str, "CPSR"); break; case 1: - sprint(str, "SPSR"); + snprint(str, sizeof str, "SPSR"); break; default: - sprint(str, "PSR%d", a->reg); + snprint(str, sizeof str, "PSR%d", a->reg); break; } if(a->name != D_NONE || a->sym != S) - sprint(str, "%N(PSR%d)(REG)", a, a->reg); + snprint(str, sizeof str, "%N(PSR%d)(REG)", a, a->reg); break; case D_FPCR: switch(a->reg){ case 0: - sprint(str, "FPSR"); + snprint(str, sizeof str, "FPSR"); break; case 1: - sprint(str, "FPCR"); + snprint(str, sizeof str, "FPCR"); break; default: - sprint(str, "FCR%d", a->reg); + snprint(str, sizeof str, "FCR%d", a->reg); break; } if(a->name != D_NONE || a->sym != S) - sprint(str, "%N(FCR%d)(REG)", a, a->reg); + snprint(str, sizeof str, "%N(FCR%d)(REG)", a, a->reg); break; @@ -263,22 +264,22 @@ Dconv(Fmt *fp) if(curp->cond != P) { v = curp->cond->pc; if(a->sym != S) - sprint(str, "%s+%.5lux(BRANCH)", a->sym->name, v); + snprint(str, sizeof str, "%s+%.5ux(BRANCH)", a->sym->name, v); else - sprint(str, "%.5lux(BRANCH)", v); + snprint(str, sizeof str, "%.5ux(BRANCH)", v); } else if(a->sym != S) - sprint(str, "%s+%d(APC)", a->sym->name, a->offset); + snprint(str, sizeof str, "%s+%d(APC)", a->sym->name, a->offset); else - sprint(str, "%d(APC)", a->offset); + snprint(str, sizeof str, "%d(APC)", a->offset); break; case D_FCONST: - sprint(str, "$%e", ieeedtod(a->ieee)); + snprint(str, sizeof str, "$%e", ieeedtod(&a->ieee)); break; case D_SCONST: - sprint(str, "$\"%S\"", a->sval); + snprint(str, sizeof str, "$\"%S\"", a->sval); break; } return fmtstrcpy(fp, str); @@ -374,15 +375,45 @@ Sconv(Fmt *fp) return fmtstrcpy(fp, str); } +int +Iconv(Fmt *fp) +{ + int i, n; + uint32 *p; + char *s; + Fmt fmt; + + n = fp->prec; + fp->prec = 0; + if(!(fp->flags&FmtPrec) || n < 0) + return fmtstrcpy(fp, "%I"); + fp->flags &= ~FmtPrec; + p = va_arg(fp->args, uint32*); + + // format into temporary buffer and + // call fmtstrcpy to handle padding. + fmtstrinit(&fmt); + for(i=0; i<n/4; i++) { + if(i > 0) + fmtprint(&fmt, " "); + fmtprint(&fmt, "%.8ux", *p++); + } + s = fmtstrflush(&fmt); + fmtstrcpy(fp, s); + free(s); + return 0; +} + static char* cnames[] = { [C_ADDR] = "C_ADDR", [C_BCON] = "C_BCON", [C_FAUTO] = "C_FAUTO", - [C_FCON] = "C_FCON", + [C_ZFCON] = "C_SFCON", + [C_SFCON] = "C_SFCON", + [C_LFCON] = "C_LFCON", [C_FCR] = "C_FCR", - [C_FEXT] = "C_FEXT", [C_FOREG] = "C_FOREG", [C_FREG] = "C_FREG", [C_GACON] = "C_GACON", @@ -391,9 +422,7 @@ cnames[] = [C_GOK] = "C_GOK", [C_GOREG] = "C_GOREG", [C_HAUTO] = "C_HAUTO", - [C_HEXT] = "C_HEXT", [C_HFAUTO] = "C_HFAUTO", - [C_HFEXT] = "C_HFEXT", [C_HFOREG] = "C_HFOREG", [C_HOREG] = "C_HOREG", [C_HREG] = "C_HREG", @@ -401,8 +430,6 @@ cnames[] = [C_LAUTO] = "C_LAUTO", [C_LBRA] = "C_LBRA", [C_LCON] = "C_LCON", - [C_LECON] = "C_LECON", - [C_LEXT] = "C_LEXT", [C_LOREG] = "C_LOREG", [C_NCON] = "C_NCON", [C_NONE] = "C_NONE", @@ -411,7 +438,6 @@ cnames[] = [C_PSR] = "C_PSR", [C_RACON] = "C_RACON", [C_RCON] = "C_RCON", - [C_RECON] = "C_RECON", [C_REG] = "C_REG", [C_REGREG] = "C_REGREG", [C_ROREG] = "C_ROREG", @@ -419,7 +445,6 @@ cnames[] = [C_SAUTO] = "C_SAUTO", [C_SBRA] = "C_SBRA", [C_SCON] = "C_SCON", - [C_SEXT] = "C_SEXT", [C_SHIFT] = "C_SHIFT", [C_SOREG] = "C_SOREG", [C_SP] = "C_SP", @@ -443,19 +468,22 @@ Oconv(Fmt *fp) void diag(char *fmt, ...) { - char buf[STRINGSZ], *tn; + char buf[STRINGSZ], *tn, *sep; va_list arg; - tn = "??none??"; - if(curtext != P && curtext->from.sym != S) - tn = curtext->from.sym->name; + tn = ""; + sep = ""; + if(cursym != S) { + tn = cursym->name; + sep = ": "; + } va_start(arg, fmt); vseprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); - print("%s: %s\n", tn, buf); + print("%s%s%s\n", tn, sep, buf); nerrors++; - if(nerrors > 10) { + if(nerrors > 20) { print("too many errors\n"); errorexit(); } diff --git a/src/cmd/5l/noop.c b/src/cmd/5l/noop.c index 41d235a09..5def0d3f1 100644 --- a/src/cmd/5l/noop.c +++ b/src/cmd/5l/noop.c @@ -28,12 +28,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Code transformations. + #include "l.h" +#include "../ld/lib.h" // see ../../runtime/proc.c:/StackGuard enum { StackBig = 4096, + StackSmall = 128, }; static Sym* sym_div; @@ -110,14 +114,12 @@ void noops(void) { Prog *p, *q, *q1, *q2; - int o, curframe, curbecome, maxbecome, foreign; + int o, foreign; Prog *pmorestack; Sym *symmorestack; /* * find leaf subroutines - * become sizes - * frame sizes * strip NOPs * expand RET * expand BECOME pseudo @@ -127,780 +129,662 @@ noops(void) Bprint(&bso, "%5.2f noops\n", cputime()); Bflush(&bso); - pmorestack = P; symmorestack = lookup("runtime.morestack", 0); - - if(symmorestack->type == STEXT) - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - if(p->from.sym == symmorestack) { - pmorestack = p; - p->reg |= NOSPLIT; - break; - } - } + if(symmorestack->type != STEXT) { + diag("runtime·morestack not defined"); + errorexit(); } - // TODO(kaib): make lack of morestack an error -// if(pmorestack == P) -// diag("runtime·morestack not defined"); - - curframe = 0; - curbecome = 0; - maxbecome = 0; - curtext = 0; + pmorestack = symmorestack->text; + pmorestack->reg |= NOSPLIT; q = P; - for(p = firstp; p != P; p = p->link) { - setarch(p); - - /* find out how much arg space is used in this TEXT */ - if(p->to.type == D_OREG && p->to.reg == REGSP) - if(p->to.offset > curframe) - curframe = p->to.offset; - - switch(p->as) { - case ATEXT: - if(curtext && curtext->from.sym) { - curtext->from.sym->frame = curframe; - curtext->from.sym->become = curbecome; - if(curbecome > maxbecome) - maxbecome = curbecome; - } - curframe = 0; - curbecome = 0; - - p->mark |= LEAF; - curtext = p; - break; - - case ARET: - /* special form of RET is BECOME */ - if(p->from.type == D_CONST) - if(p->from.offset > curbecome) - curbecome = p->from.offset; - break; - - case ADIV: - case ADIVU: - case AMOD: - case AMODU: - q = p; - if(prog_div == P) - initdiv(); - if(curtext != P) - curtext->mark &= ~LEAF; - setdiv(p->as); - continue; - - case ANOP: - q1 = p->link; - q->link = q1; /* q is non-nop */ - q1->mark |= p->mark; - continue; - - case ABL: - case ABX: - if(curtext != P) - curtext->mark &= ~LEAF; - - case ABCASE: - case AB: - - case ABEQ: - case ABNE: - case ABCS: - case ABHS: - case ABCC: - case ABLO: - case ABMI: - case ABPL: - case ABVS: - case ABVC: - case ABHI: - case ABLS: - case ABGE: - case ABLT: - case ABGT: - case ABLE: - - q1 = p->cond; - if(q1 != P) { - while(q1->as == ANOP) { - q1 = q1->link; - p->cond = q1; - } - } - break; - } - q = p; - } - - if(curtext && curtext->from.sym) { - curtext->from.sym->frame = curframe; - curtext->from.sym->become = curbecome; - if(curbecome > maxbecome) - maxbecome = curbecome; - } - - if(debug['b']) - print("max become = %d\n", maxbecome); - xdefine("ALEFbecome", STEXT, maxbecome); - - curtext = 0; - for(p = firstp; p != P; p = p->link) { - setarch(p); - switch(p->as) { - case ATEXT: - curtext = p; - break; - case ABL: - // case ABX: - if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) { - o = maxbecome - curtext->from.sym->frame; - if(o <= 0) - break; - /* calling a become or calling a variable */ - if(p->to.sym == S || p->to.sym->become) { - curtext->to.offset += o; - if(debug['b']) { - curp = p; - print("%D calling %D increase %d\n", - &curtext->from, &p->to, o); + for(cursym = textp; cursym != nil; cursym = cursym->next) { + for(p = cursym->text; p != P; p = p->link) { + setarch(p); + + switch(p->as) { + case ATEXT: + p->mark |= LEAF; + break; + + case ARET: + break; + + case ADIV: + case ADIVU: + case AMOD: + case AMODU: + q = p; + if(prog_div == P) + initdiv(); + cursym->text->mark &= ~LEAF; + setdiv(p->as); + continue; + + case ANOP: + q1 = p->link; + q->link = q1; /* q is non-nop */ + if(q1 != P) + q1->mark |= p->mark; + continue; + + case ABL: + case ABX: + cursym->text->mark &= ~LEAF; + + case ABCASE: + case AB: + + case ABEQ: + case ABNE: + case ABCS: + case ABHS: + case ABCC: + case ABLO: + case ABMI: + case ABPL: + case ABVS: + case ABVC: + case ABHI: + case ABLS: + case ABGE: + case ABLT: + case ABGT: + case ABLE: + q1 = p->cond; + if(q1 != P) { + while(q1->as == ANOP) { + q1 = q1->link; + p->cond = q1; } } + break; } - break; + q = p; } } - for(p = firstp; p != P; p = p->link) { - setarch(p); - o = p->as; - switch(o) { - case ATEXT: - curtext = p; - autosize = p->to.offset + 4; - if(autosize <= 4) - if(curtext->mark & LEAF) { - p->to.offset = -4; - autosize = 0; - } - - if(!autosize && !(curtext->mark & LEAF)) { - if(debug['v']) - Bprint(&bso, "save suppressed in: %s\n", - curtext->from.sym->name); - Bflush(&bso); - curtext->mark |= LEAF; - } + for(cursym = textp; cursym != nil; cursym = cursym->next) { + for(p = cursym->text; p != P; p = p->link) { + setarch(p); + o = p->as; + switch(o) { + case ATEXT: + autosize = p->to.offset + 4; + if(autosize <= 4) + if(cursym->text->mark & LEAF) { + p->to.offset = -4; + autosize = 0; + } + + if(!autosize && !(cursym->text->mark & LEAF)) { + if(debug['v']) + Bprint(&bso, "save suppressed in: %s\n", + cursym->name); + Bflush(&bso); + cursym->text->mark |= LEAF; + } #ifdef CALLEEBX - if(p->from.sym->foreign){ - if(thumb) - // don't allow literal pool to seperate these - p = adword(0xe28f7001, 0xe12fff17, p); // arm add 1, pc, r7 and bx r7 - // p = aword(0xe12fff17, aword(0xe28f7001, p)); // arm add 1, pc, r7 and bx r7 - else - p = aword(0x4778, p); // thumb bx pc and 2 bytes padding - } + if(p->from.sym->foreign){ + if(thumb) + // don't allow literal pool to seperate these + p = adword(0xe28f7001, 0xe12fff17, p); // arm add 1, pc, r7 and bx r7 + // p = aword(0xe12fff17, aword(0xe28f7001, p)); // arm add 1, pc, r7 and bx r7 + else + p = aword(0x4778, p); // thumb bx pc and 2 bytes padding + } #endif - if(curtext->mark & LEAF) { - if(curtext->from.sym) - curtext->from.sym->type = SLEAF; - if(!autosize) + if(cursym->text->mark & LEAF) { + cursym->leaf = 1; + if(!autosize) + break; + } + + if(thumb){ + if(!(p->reg & NOSPLIT)) + diag("stack splitting not supported in thumb"); + if(!(cursym->text->mark & LEAF)){ + q = movrr(nil, REGLINK, REGTMPT-1, p); + p->link = q; + q1 = prg(); + q1->as = AMOVW; + q1->line = p->line; + q1->from.type = D_REG; + q1->from.reg = REGTMPT-1; + q1->to.type = D_OREG; + q1->to.name = D_NONE; + q1->to.reg = REGSP; + q1->to.offset = 0; + q1->link = q->link; + q->link = q1; + } + if(autosize){ + q2 = prg(); + q2->as = ASUB; + q2->line = p->line; + q2->from.type = D_CONST; + q2->from.offset = autosize; + q2->to.type = D_REG; + q2->to.reg = REGSP; + q2->link = p->link; + p->link = q2; + } break; - } - - if(thumb){ - if(!(p->reg & NOSPLIT)) - diag("stack splitting not supported in thumb"); - if(!(curtext->mark & LEAF)){ - q = movrr(nil, REGLINK, REGTMPT-1, p); - p->link = q; + } + + if(p->reg & NOSPLIT) { q1 = prg(); q1->as = AMOVW; + q1->scond |= C_WBIT; q1->line = p->line; q1->from.type = D_REG; - q1->from.reg = REGTMPT-1; + q1->from.reg = REGLINK; q1->to.type = D_OREG; - q1->to.name = D_NONE; + q1->to.offset = -autosize; q1->to.reg = REGSP; - q1->to.offset = 0; - q1->link = q->link; - q->link = q1; - } - if(autosize){ - q2 = prg(); - q2->as = ASUB; - q2->line = p->line; - q2->from.type = D_CONST; - q2->from.offset = autosize; - q2->to.type = D_REG; - q2->to.reg = REGSP; - q2->link = p->link; - p->link = q2; + q1->link = p->link; + p->link = q1; + } else if (autosize < StackBig) { + // split stack check for small functions + // MOVW g_stackguard(g), R1 + // CMP R1, $-autosize(SP) + // MOVW.LO $autosize, R1 + // MOVW.LO $args, R2 + // MOVW.LO R14, R3 + // BL.LO runtime.morestack(SB) // modifies LR + // MOVW.W R14,$-autosize(SP) + + // TODO(kaib): add more trampolines + // TODO(kaib): put stackguard in register + // TODO(kaib): add support for -K and underflow detection + + // MOVW g_stackguard(g), R1 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_OREG; + p->from.reg = REGG; + p->to.type = D_REG; + p->to.reg = 1; + + if(autosize < StackSmall) { + // CMP R1, SP + p = appendp(p); + p->as = ACMP; + p->from.type = D_REG; + p->from.reg = 1; + p->reg = REGSP; + } else { + // MOVW $-autosize(SP), R2 + // CMP R1, R2 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.reg = REGSP; + p->from.offset = -autosize; + p->to.type = D_REG; + p->to.reg = 2; + + p = appendp(p); + p->as = ACMP; + p->from.type = D_REG; + p->from.reg = 1; + p->reg = 2; + } + + // MOVW.LO $autosize, R1 + p = appendp(p); + p->as = AMOVW; + p->scond = C_SCOND_LO; + p->from.type = D_CONST; + p->from.offset = 0; + p->to.type = D_REG; + p->to.reg = 1; + + // MOVW.LO $args +4, R2 + // also need to store the extra 4 bytes. + p = appendp(p); + p->as = AMOVW; + p->scond = C_SCOND_LO; + p->from.type = D_CONST; + p->from.offset = ((cursym->text->to.offset2 + 3) & ~3) + 4; + p->to.type = D_REG; + p->to.reg = 2; + + // MOVW.LO R14, R3 + p = appendp(p); + p->as = AMOVW; + p->scond = C_SCOND_LO; + p->from.type = D_REG; + p->from.reg = REGLINK; + p->to.type = D_REG; + p->to.reg = 3; + + // BL.LO runtime.morestack(SB) // modifies LR + p = appendp(p); + p->as = ABL; + p->scond = C_SCOND_LO; + p->to.type = D_BRANCH; + p->to.sym = symmorestack; + p->cond = pmorestack; + + // MOVW.W R14,$-autosize(SP) + p = appendp(p); + p->as = AMOVW; + p->scond |= C_WBIT; + p->from.type = D_REG; + p->from.reg = REGLINK; + p->to.type = D_OREG; + p->to.offset = -autosize; + p->to.reg = REGSP; + } else { // > StackBig + // MOVW $autosize, R1 + // MOVW $args, R2 + // MOVW R14, R3 + // BL runtime.morestack(SB) // modifies LR + // MOVW.W R14,$-autosize(SP) + + // MOVW $autosize, R1 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.offset = autosize; + p->to.type = D_REG; + p->to.reg = 1; + + // MOVW $args +4, R2 + // also need to store the extra 4 bytes. + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.offset = ((cursym->text->to.offset2 + 3) & ~3) + 4; + p->to.type = D_REG; + p->to.reg = 2; + + // MOVW R14, R3 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_REG; + p->from.reg = REGLINK; + p->to.type = D_REG; + p->to.reg = 3; + + // BL runtime.morestack(SB) // modifies LR + p = appendp(p); + p->as = ABL; + p->to.type = D_BRANCH; + p->to.sym = symmorestack; + p->cond = pmorestack; + + // MOVW.W R14,$-autosize(SP) + p = appendp(p); + p->as = AMOVW; + p->scond |= C_WBIT; + p->from.type = D_REG; + p->from.reg = REGLINK; + p->to.type = D_OREG; + p->to.offset = -autosize; + p->to.reg = REGSP; } break; - } - - if(p->reg & NOSPLIT) { - q1 = prg(); - q1->as = AMOVW; - q1->scond |= C_WBIT; - q1->line = p->line; - q1->from.type = D_REG; - q1->from.reg = REGLINK; - q1->to.type = D_OREG; - q1->to.offset = -autosize; - q1->to.reg = REGSP; - q1->link = p->link; - p->link = q1; - } else if (autosize < StackBig) { - // split stack check for small functions - // MOVW g_stackguard(g), R1 - // CMP R1, $-autosize(SP) - // MOVW.LO $autosize, R1 - // MOVW.LO $args, R2 - // MOVW.LO R14, R3 - // BL.LO runtime.morestack(SB) // modifies LR - // MOVW.W R14,$-autosize(SP) - - // TODO(kaib): add more trampolines - // TODO(kaib): put stackguard in register - // TODO(kaib): add support for -K and underflow detection - - // MOVW g_stackguard(g), R1 - p = appendp(p); - p->as = AMOVW; - p->from.type = D_OREG; - p->from.reg = REGG; - p->to.type = D_REG; - p->to.reg = 1; - - // CMP R1, $-autosize(SP) - p = appendp(p); - p->as = ACMP; - p->from.type = D_REG; - p->from.reg = 1; - p->from.offset = -autosize; - p->reg = REGSP; - - // MOVW.LO $autosize, R1 - p = appendp(p); - p->as = AMOVW; - p->scond = C_SCOND_LO; - p->from.type = D_CONST; - p->from.offset = 0; - p->to.type = D_REG; - p->to.reg = 1; - - // MOVW.LO $args +4, R2 - // also need to store the extra 4 bytes. - p = appendp(p); - p->as = AMOVW; - p->scond = C_SCOND_LO; - p->from.type = D_CONST; - p->from.offset = ((curtext->to.offset2 + 3) & ~3) + 4; - p->to.type = D_REG; - p->to.reg = 2; - - // MOVW.LO R14, R3 - p = appendp(p); - p->as = AMOVW; - p->scond = C_SCOND_LO; - p->from.type = D_REG; - p->from.reg = REGLINK; - p->to.type = D_REG; - p->to.reg = 3; - - // BL.LO runtime.morestack(SB) // modifies LR - p = appendp(p); - p->as = ABL; - p->scond = C_SCOND_LO; - p->to.type = D_BRANCH; - p->to.sym = symmorestack; - p->cond = pmorestack; - - // MOVW.W R14,$-autosize(SP) - p = appendp(p); - p->as = AMOVW; - p->scond |= C_WBIT; - p->from.type = D_REG; - p->from.reg = REGLINK; - p->to.type = D_OREG; - p->to.offset = -autosize; - p->to.reg = REGSP; - } else { // > StackBig - // MOVW $autosize, R1 - // MOVW $args, R2 - // MOVW R14, R3 - // BL runtime.morestack(SB) // modifies LR - // MOVW.W R14,$-autosize(SP) - - // MOVW $autosize, R1 - p = appendp(p); - p->as = AMOVW; - p->from.type = D_CONST; - p->from.offset = autosize; - p->to.type = D_REG; - p->to.reg = 1; - - // MOVW $args +4, R2 - // also need to store the extra 4 bytes. - p = appendp(p); - p->as = AMOVW; - p->from.type = D_CONST; - p->from.offset = ((curtext->to.offset2 + 3) & ~3) + 4; - p->to.type = D_REG; - p->to.reg = 2; - - // MOVW R14, R3 - p = appendp(p); - p->as = AMOVW; - p->from.type = D_REG; - p->from.reg = REGLINK; - p->to.type = D_REG; - p->to.reg = 3; - - // BL runtime.morestack(SB) // modifies LR - p = appendp(p); - p->as = ABL; - p->to.type = D_BRANCH; - p->to.sym = symmorestack; - p->cond = pmorestack; - - // MOVW.W R14,$-autosize(SP) - p = appendp(p); - p->as = AMOVW; - p->scond |= C_WBIT; - p->from.type = D_REG; - p->from.reg = REGLINK; - p->to.type = D_OREG; - p->to.offset = -autosize; - p->to.reg = REGSP; - } - break; - - case ARET: - nocache(p); - foreign = seenthumb && curtext->from.sym != S && (curtext->from.sym->foreign || curtext->from.sym->fnptr); -// print("%s %d %d\n", curtext->from.sym->name, curtext->from.sym->foreign, curtext->from.sym->fnptr); - if(p->from.type == D_CONST) - goto become; - if(curtext->mark & LEAF) { - if(!autosize) { - if(thumb){ - p = fnret(p, REGLINK, foreign, p); + + case ARET: + nocache(p); + foreign = seenthumb && (cursym->foreign || cursym->fnptr); +// print("%s %d %d\n", cursym->name, cursym->foreign, cursym->fnptr); + if(cursym->text->mark & LEAF) { + if(!autosize) { + if(thumb){ + p = fnret(p, REGLINK, foreign, p); + break; + } +// if(foreign) print("ABXRET 1 %s\n", cursym->name); + p->as = foreign ? ABXRET : AB; + p->from = zprg.from; + p->to.type = D_OREG; + p->to.offset = 0; + p->to.reg = REGLINK; break; } -// if(foreign) print("ABXRET 1 %s\n", curtext->from.sym->name); - p->as = foreign ? ABXRET : AB; - p->from = zprg.from; - p->to.type = D_OREG; - p->to.offset = 0; - p->to.reg = REGLINK; - break; } - } - if(thumb){ - if(curtext->mark & LEAF){ - if(autosize){ - p->as = AADD; - p->from.type = D_CONST; - p->from.offset = autosize; + if(thumb){ + if(cursym->text->mark & LEAF){ + if(autosize){ + p->as = AADD; + p->from.type = D_CONST; + p->from.offset = autosize; + p->to.type = D_REG; + p->to.reg = REGSP; + q = nil; + } + else + q = p; + q = fnret(q, REGLINK, foreign, p); + if(q != p) + p->link = q; + } + else{ + p->as = AMOVW; + p->from.type = D_OREG; + p->from.name = D_NONE; + p->from.reg = REGSP; + p->from.offset = 0; p->to.type = D_REG; - p->to.reg = REGSP; - q = nil; + p->to.reg = REGTMPT-1; + if(autosize){ + q = prg(); + q->as = AADD; + q->from.type = D_CONST; + q->from.offset = autosize; + q->to.type = D_REG; + q->to.reg = REGSP; + q->link = p->link; + p->link = q; + } + else + q = p; + q1 = fnret(nil, REGTMPT-1, foreign, p); + q1->link = q->link; + q->link = q1; } - else - q = p; - q = fnret(q, REGLINK, foreign, p); - if(q != p) - p->link = q; + break; } - else{ + if(foreign) { +// if(foreign) print("ABXRET 3 %s\n", cursym->name); +#define R 1 p->as = AMOVW; p->from.type = D_OREG; p->from.name = D_NONE; p->from.reg = REGSP; p->from.offset = 0; p->to.type = D_REG; - p->to.reg = REGTMPT-1; - if(autosize){ - q = prg(); - q->as = AADD; - q->from.type = D_CONST; - q->from.offset = autosize; - q->to.type = D_REG; - q->to.reg = REGSP; - q->link = p->link; - p->link = q; - } - else - q = p; - q1 = fnret(nil, REGTMPT-1, foreign, p); + p->to.reg = R; + q = prg(); + q->as = AADD; + q->scond = p->scond; + q->line = p->line; + q->from.type = D_CONST; + q->from.offset = autosize; + q->to.type = D_REG; + q->to.reg = REGSP; + q->link = p->link; + p->link = q; + q1 = prg(); + q1->as = ABXRET; + q1->scond = p->scond; + q1->line = p->line; + q1->to.type = D_OREG; + q1->to.offset = 0; + q1->to.reg = R; q1->link = q->link; q->link = q1; +#undef R + } + else { + p->as = AMOVW; + p->scond |= C_PBIT; + p->from.type = D_OREG; + p->from.offset = autosize; + p->from.reg = REGSP; + p->to.type = D_REG; + p->to.reg = REGPC; } break; - } - if(foreign) { -// if(foreign) print("ABXRET 3 %s\n", curtext->from.sym->name); -#define R 1 + + case ADIV: + case ADIVU: + case AMOD: + case AMODU: + if(debug['M']) + break; + if(p->from.type != D_REG) + break; + if(p->to.type != D_REG) + break; + q1 = p; + + /* MOV a,4(SP) */ + q = prg(); + q->link = p->link; + p->link = q; + p = q; + p->as = AMOVW; - p->from.type = D_OREG; - p->from.name = D_NONE; - p->from.reg = REGSP; - p->from.offset = 0; - p->to.type = D_REG; - p->to.reg = R; + p->line = q1->line; + p->from.type = D_REG; + p->from.reg = q1->from.reg; + p->to.type = D_OREG; + p->to.reg = REGSP; + p->to.offset = 4; + + /* MOV b,REGTMP */ q = prg(); - q->as = AADD; - q->scond = p->scond; - q->line = p->line; - q->from.type = D_CONST; - q->from.offset = autosize; - q->to.type = D_REG; - q->to.reg = REGSP; q->link = p->link; p->link = q; - q1 = prg(); - q1->as = ABXRET; - q1->scond = p->scond; - q1->line = p->line; - q1->to.type = D_OREG; - q1->to.offset = 0; - q1->to.reg = R; - q1->link = q->link; - q->link = q1; -#undef R - } - else { + p = q; + p->as = AMOVW; - p->scond |= C_PBIT; - p->from.type = D_OREG; - p->from.offset = autosize; - p->from.reg = REGSP; + p->line = q1->line; + p->from.type = D_REG; + p->from.reg = q1->reg; + if(q1->reg == NREG) + p->from.reg = q1->to.reg; p->to.type = D_REG; - p->to.reg = REGPC; - } - break; - - become: - if(foreign){ - diag("foreign become - help"); - break; - } - if(thumb){ - diag("thumb become - help"); - break; - } - print("arm become\n"); - if(curtext->mark & LEAF) { - - if(!autosize) { - p->as = AB; - p->from = zprg.from; + p->to.reg = prog_div->from.sym->thumb ? REGTMPT : REGTMP; + p->to.offset = 0; + + /* CALL appropriate */ + q = prg(); + q->link = p->link; + p->link = q; + p = q; + +#ifdef CALLEEBX + p->as = ABL; +#else + if(prog_div->from.sym->thumb) + p->as = thumb ? ABL : ABX; + else + p->as = thumb ? ABX : ABL; +#endif + p->line = q1->line; + p->to.type = D_BRANCH; + p->cond = p; + switch(o) { + case ADIV: + p->cond = prog_div; + p->to.sym = sym_div; + break; + case ADIVU: + p->cond = prog_divu; + p->to.sym = sym_divu; + break; + case AMOD: + p->cond = prog_mod; + p->to.sym = sym_mod; + break; + case AMODU: + p->cond = prog_modu; + p->to.sym = sym_modu; break; } - } - q = prg(); - q->scond = p->scond; - q->line = p->line; - q->as = AB; - q->from = zprg.from; - q->to = p->to; - q->cond = p->cond; - q->link = p->link; - p->link = q; - if(thumb){ - q1 = prg(); - q1->line = p->line; - q1->as = AADD; - q1->from.type = D_CONST; - q1->from.offset = autosize; - q1->to.type = D_REG; - q1->to.reg = REGSP; + + /* MOV REGTMP, b */ + q = prg(); + q->link = p->link; + p->link = q; + p = q; + p->as = AMOVW; - p->line = p->line; - p->from.type = D_OREG; - p->from.name = D_NONE; - p->from.reg = REGSP; + p->line = q1->line; + p->from.type = D_REG; + p->from.reg = prog_div->from.sym->thumb ? REGTMPT : REGTMP; p->from.offset = 0; p->to.type = D_REG; - p->to.reg = REGTMPT-1; - q1->link = q; - p->link = q1; - q2 = movrr(nil, REGTMPT-1, REGLINK, p); - q2->link = q; - q1->link = q2; - break; - } - p->as = AMOVW; - p->scond |= C_PBIT; - p->from = zprg.from; - p->from.type = D_OREG; - p->from.offset = autosize; - p->from.reg = REGSP; - p->to = zprg.to; - p->to.type = D_REG; - p->to.reg = REGLINK; - - break; - - case ADIV: - case ADIVU: - case AMOD: - case AMODU: - if(debug['M']) - break; - if(p->from.type != D_REG) - break; - if(p->to.type != D_REG) - break; - q1 = p; - - /* MOV a,4(SP) */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - - p->as = AMOVW; - p->line = q1->line; - p->from.type = D_REG; - p->from.reg = q1->from.reg; - p->to.type = D_OREG; - p->to.reg = REGSP; - p->to.offset = 4; - - /* MOV b,REGTMP */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - - p->as = AMOVW; - p->line = q1->line; - p->from.type = D_REG; - p->from.reg = q1->reg; - if(q1->reg == NREG) - p->from.reg = q1->to.reg; - p->to.type = D_REG; - p->to.reg = prog_div != UP && prog_div->from.sym->thumb ? REGTMPT : REGTMP; - p->to.offset = 0; - - /* CALL appropriate */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - -#ifdef CALLEEBX - p->as = ABL; -#else - if(prog_div != UP && prog_div->from.sym->thumb) - p->as = thumb ? ABL : ABX; - else - p->as = thumb ? ABX : ABL; -#endif - p->line = q1->line; - p->to.type = D_BRANCH; - p->cond = p; - switch(o) { - case ADIV: - p->cond = prog_div; - p->to.sym = sym_div; - break; - case ADIVU: - p->cond = prog_divu; - p->to.sym = sym_divu; - break; - case AMOD: - p->cond = prog_mod; - p->to.sym = sym_mod; + p->to.reg = q1->to.reg; + + /* ADD $8,SP */ + q = prg(); + q->link = p->link; + p->link = q; + p = q; + + p->as = AADD; + p->from.type = D_CONST; + p->from.reg = NREG; + p->from.offset = 8; + p->reg = NREG; + p->to.type = D_REG; + p->to.reg = REGSP; + + /* SUB $8,SP */ + q1->as = ASUB; + q1->from.type = D_CONST; + q1->from.offset = 8; + q1->from.reg = NREG; + q1->reg = NREG; + q1->to.type = D_REG; + q1->to.reg = REGSP; + break; - case AMODU: - p->cond = prog_modu; - p->to.sym = sym_modu; + case AMOVW: + if(thumb){ + Adr *a = &p->from; + + if(a->type == D_CONST && ((a->name == D_NONE && a->reg == REGSP) || a->name == D_AUTO || a->name == D_PARAM) && (a->offset & 3)) + diag("SP offset not multiple of 4"); + } break; - } - - /* MOV REGTMP, b */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - - p->as = AMOVW; - p->line = q1->line; - p->from.type = D_REG; - p->from.reg = prog_div != UP && prog_div->from.sym->thumb ? REGTMPT : REGTMP; - p->from.offset = 0; - p->to.type = D_REG; - p->to.reg = q1->to.reg; - - /* ADD $8,SP */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - - p->as = AADD; - p->from.type = D_CONST; - p->from.reg = NREG; - p->from.offset = 8; - p->reg = NREG; - p->to.type = D_REG; - p->to.reg = REGSP; - - /* SUB $8,SP */ - q1->as = ASUB; - q1->from.type = D_CONST; - q1->from.offset = 8; - q1->from.reg = NREG; - q1->reg = NREG; - q1->to.type = D_REG; - q1->to.reg = REGSP; - - break; - case AMOVW: - if(thumb){ - Adr *a = &p->from; - - if(a->type == D_CONST && ((a->name == D_NONE && a->reg == REGSP) || a->name == D_AUTO || a->name == D_PARAM) && (a->offset & 3)) - diag("SP offset not multiple of 4"); - } - break; - case AMOVB: - case AMOVBU: - case AMOVH: - case AMOVHU: - if(thumb){ - if(p->from.type == D_OREG && (p->from.name == D_AUTO || p->from.name == D_PARAM || (p->from.name == D_CONST && p->from.reg == REGSP))){ - q = prg(); - *q = *p; - if(p->from.name == D_AUTO) - q->from.offset += autosize; - else if(p->from.name == D_PARAM) - q->from.offset += autosize+4; - q->from.name = D_NONE; - q->from.reg = REGTMPT; - p = movrr(p, REGSP, REGTMPT, p); - q->link = p->link; - p->link = q; + case AMOVB: + case AMOVBU: + case AMOVH: + case AMOVHU: + if(thumb){ + if(p->from.type == D_OREG && (p->from.name == D_AUTO || p->from.name == D_PARAM || (p->from.name == D_CONST && p->from.reg == REGSP))){ + q = prg(); + *q = *p; + if(p->from.name == D_AUTO) + q->from.offset += autosize; + else if(p->from.name == D_PARAM) + q->from.offset += autosize+4; + q->from.name = D_NONE; + q->from.reg = REGTMPT; + p = movrr(p, REGSP, REGTMPT, p); + q->link = p->link; + p->link = q; + } + if(p->to.type == D_OREG && (p->to.name == D_AUTO || p->to.name == D_PARAM || (p->to.name == D_CONST && p->to.reg == REGSP))){ + q = prg(); + *q = *p; + if(p->to.name == D_AUTO) + q->to.offset += autosize; + else if(p->to.name == D_PARAM) + q->to.offset += autosize+4; + q->to.name = D_NONE; + q->to.reg = REGTMPT; + p = movrr(p, REGSP, REGTMPT, p); + q->link = p->link; + p->link = q; + if(q->to.offset < 0 || q->to.offset > 255){ // complicated + p->to.reg = REGTMPT+1; // mov sp, r8 + q1 = prg(); + q1->line = p->line; + q1->as = AMOVW; + q1->from.type = D_CONST; + q1->from.offset = q->to.offset; + q1->to.type = D_REG; + q1->to.reg = REGTMPT; // mov $o, r7 + p->link = q1; + q1->link = q; + q1 = prg(); + q1->line = p->line; + q1->as = AADD; + q1->from.type = D_REG; + q1->from.reg = REGTMPT+1; + q1->to.type = D_REG; + q1->to.reg = REGTMPT; // add r8, r7 + p->link->link = q1; + q1->link = q; + q->to.offset = 0; // mov* r, 0(r7) + /* phew */ + } + } } - if(p->to.type == D_OREG && (p->to.name == D_AUTO || p->to.name == D_PARAM || (p->to.name == D_CONST && p->to.reg == REGSP))){ - q = prg(); - *q = *p; - if(p->to.name == D_AUTO) - q->to.offset += autosize; - else if(p->to.name == D_PARAM) - q->to.offset += autosize+4; - q->to.name = D_NONE; - q->to.reg = REGTMPT; - p = movrr(p, REGSP, REGTMPT, p); - q->link = p->link; - p->link = q; - if(q->to.offset < 0 || q->to.offset > 255){ // complicated - p->to.reg = REGTMPT+1; // mov sp, r8 - q1 = prg(); - q1->line = p->line; - q1->as = AMOVW; - q1->from.type = D_CONST; - q1->from.offset = q->to.offset; - q1->to.type = D_REG; - q1->to.reg = REGTMPT; // mov $o, r7 - p->link = q1; - q1->link = q; - q1 = prg(); - q1->line = p->line; - q1->as = AADD; - q1->from.type = D_REG; - q1->from.reg = REGTMPT+1; - q1->to.type = D_REG; - q1->to.reg = REGTMPT; // add r8, r7 - p->link->link = q1; - q1->link = q; - q->to.offset = 0; // mov* r, 0(r7) - /* phew */ + break; + case AMOVM: + if(thumb){ + if(p->from.type == D_OREG){ + if(p->from.offset == 0) + p->from.type = D_REG; + else + diag("non-zero AMOVM offset"); + } + else if(p->to.type == D_OREG){ + if(p->to.offset == 0) + p->to.type = D_REG; + else + diag("non-zero AMOVM offset"); } } - } - break; - case AMOVM: - if(thumb){ - if(p->from.type == D_OREG){ - if(p->from.offset == 0) + break; + case AB: + if(thumb && p->to.type == D_OREG){ + if(p->to.offset == 0){ + p->as = AMOVW; p->from.type = D_REG; - else - diag("non-zero AMOVM offset"); - } - else if(p->to.type == D_OREG){ - if(p->to.offset == 0) + p->from.reg = p->to.reg; p->to.type = D_REG; - else - diag("non-zero AMOVM offset"); - } - } - break; - case AB: - if(thumb && p->to.type == D_OREG){ - if(p->to.offset == 0){ - p->as = AMOVW; - p->from.type = D_REG; - p->from.reg = p->to.reg; - p->to.type = D_REG; - p->to.reg = REGPC; - } - else{ - p->as = AADD; - p->from.type = D_CONST; - p->from.offset = p->to.offset; - p->reg = p->to.reg; - p->to.type = D_REG; - p->to.reg = REGTMPT-1; - q = prg(); - q->as = AMOVW; - q->line = p->line; - q->from.type = D_REG; - q->from.reg = REGTMPT-1; - q->to.type = D_REG; - q->to.reg = REGPC; - q->link = p->link; - p->link = q; + p->to.reg = REGPC; + } + else{ + p->as = AADD; + p->from.type = D_CONST; + p->from.offset = p->to.offset; + p->reg = p->to.reg; + p->to.type = D_REG; + p->to.reg = REGTMPT-1; + q = prg(); + q->as = AMOVW; + q->line = p->line; + q->from.type = D_REG; + q->from.reg = REGTMPT-1; + q->to.type = D_REG; + q->to.reg = REGPC; + q->link = p->link; + p->link = q; + } } - } - if(seenthumb && !thumb && p->to.type == D_OREG && p->to.reg == REGLINK){ - // print("warn %s: b (R%d) assuming a return\n", curtext->from.sym->name, p->to.reg); - p->as = ABXRET; - } - break; - case ABL: - case ABX: - if(thumb && p->to.type == D_OREG){ - if(p->to.offset == 0){ - p->as = o; - p->from.type = D_NONE; - p->to.type = D_REG; + if(seenthumb && !thumb && p->to.type == D_OREG && p->to.reg == REGLINK){ + // print("warn %s: b (R%d) assuming a return\n", cursym->name, p->to.reg); + p->as = ABXRET; } - else{ - p->as = AADD; - p->from.type = D_CONST; - p->from.offset = p->to.offset; - p->reg = p->to.reg; - p->to.type = D_REG; - p->to.reg = REGTMPT-1; - q = prg(); - q->as = o; - q->line = p->line; - q->from.type = D_NONE; - q->to.type = D_REG; - q->to.reg = REGTMPT-1; - q->link = p->link; - p->link = q; + break; + case ABL: + case ABX: + if(thumb && p->to.type == D_OREG){ + if(p->to.offset == 0){ + p->as = o; + p->from.type = D_NONE; + p->to.type = D_REG; + } + else{ + p->as = AADD; + p->from.type = D_CONST; + p->from.offset = p->to.offset; + p->reg = p->to.reg; + p->to.type = D_REG; + p->to.reg = REGTMPT-1; + q = prg(); + q->as = o; + q->line = p->line; + q->from.type = D_NONE; + q->to.type = D_REG; + q->to.reg = REGTMPT-1; + q->link = p->link; + p->link = q; + } } + break; } - break; } } } @@ -911,12 +795,9 @@ sigdiv(char *n) Sym *s; s = lookup(n, 0); - if(s->type == STEXT){ + if(s->type == STEXT) if(s->sig == 0) s->sig = SIGNINTERN; - } - else if(s->type == 0 || s->type == SXREF) - s->type = SUNDEF; } void @@ -928,25 +809,10 @@ divsig(void) sigdiv("_modu"); } -static void -sdiv(Sym *s) -{ - if(s->type == 0 || s->type == SXREF){ - /* undefsym(s); */ - s->type = SXREF; - if(s->sig == 0) - s->sig = SIGNINTERN; - s->subtype = SIMPORT; - } - else if(s->type != STEXT) - diag("undefined: %s", s->name); -} - void initdiv(void) { Sym *s2, *s3, *s4, *s5; - Prog *p; if(prog_div != P) return; @@ -954,38 +820,25 @@ initdiv(void) sym_divu = s3 = lookup("_divu", 0); sym_mod = s4 = lookup("_mod", 0); sym_modu = s5 = lookup("_modu", 0); - if(dlm) { - sdiv(s2); if(s2->type == SXREF) prog_div = UP; - sdiv(s3); if(s3->type == SXREF) prog_divu = UP; - sdiv(s4); if(s4->type == SXREF) prog_mod = UP; - sdiv(s5); if(s5->type == SXREF) prog_modu = UP; - } - for(p = firstp; p != P; p = p->link) - if(p->as == ATEXT) { - if(p->from.sym == s2) - prog_div = p; - if(p->from.sym == s3) - prog_divu = p; - if(p->from.sym == s4) - prog_mod = p; - if(p->from.sym == s5) - prog_modu = p; - } + prog_div = s2->text; + prog_divu = s3->text; + prog_mod = s4->text; + prog_modu = s5->text; if(prog_div == P) { diag("undefined: %s", s2->name); - prog_div = curtext; + prog_div = cursym->text; } if(prog_divu == P) { diag("undefined: %s", s3->name); - prog_divu = curtext; + prog_divu = cursym->text; } if(prog_mod == P) { diag("undefined: %s", s4->name); - prog_mod = curtext; + prog_mod = cursym->text; } if(prog_modu == P) { diag("undefined: %s", s5->name); - prog_modu = curtext; + prog_modu = cursym->text; } } @@ -1000,7 +853,7 @@ setdiv(int as) case AMOD: p = prog_mod; break; case AMODU: p = prog_modu; break; } - if(p != UP && thumb != p->from.sym->thumb) + if(thumb != p->from.sym->thumb) p->from.sym->foreign = 1; } diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c index e3597e040..cb9ad9805 100644 --- a/src/cmd/5l/obj.c +++ b/src/cmd/5l/obj.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Reading object files. + #define EXTERN #include "l.h" #include "../ld/lib.h" @@ -50,28 +52,6 @@ char *thestring = "arm"; * -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; -} - static char* linkername[] = { @@ -94,7 +74,6 @@ main(int argc, char *argv[]) cout = -1; listinit(); nerrors = 0; - curtext = P; outfile = "5.out"; HEADTYPE = -1; INITTEXT = -1; @@ -231,10 +210,10 @@ main(int argc, char *argv[]) break; } if(INITDAT != 0 && INITRND != 0) - print("warning: -D0x%lux is ignored because of -R0x%lux\n", + print("warning: -D0x%ux is ignored because of -R0x%ux\n", INITDAT, INITRND); if(debug['v']) - Bprint(&bso, "HEADER = -H0x%d -T0x%lux -D0x%lux -R0x%lux\n", + Bprint(&bso, "HEADER = -H0x%d -T0x%ux -D0x%ux -R0x%ux\n", HEADTYPE, INITTEXT, INITDAT, INITRND); Bflush(&bso); zprg.as = AGOK; @@ -247,9 +226,6 @@ main(int argc, char *argv[]) buildop(); thumbbuildop(); // could build on demand histgen = 0; - textp = P; - datap = P; - edatap = P; pc = 0; dtype = 4; nuxiinit(); @@ -257,8 +233,6 @@ main(int argc, char *argv[]) version = 0; cbp = buf.cbuf; cbc = sizeof(buf.cbuf); - firstp = prg(); - lastp = firstp; addlibpath("command line", "command line", argv[0], "main"); loadlib(); @@ -268,52 +242,30 @@ main(int argc, char *argv[]) for(i=0; i<nelem(linkername); i++) mark(lookup(linkername[i], 0)); deadcode(); - - 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(); + if(textp == nil) { + diag("no code"); + errorexit(); } + patch(); if(debug['p']) if(debug['1']) doprof1(); else doprof2(); - if(debug['u']) - reachable(); doelf(); - dodata(); - if(seenthumb && debug['f']) - fnptrs(); follow(); - if(firstp == P) { - diag("no code"); - errorexit(); - } softfloat(); noops(); span(); + pclntab(); + symtab(); + dodata(); + address(); + reloc(); asmb(); undef(); -out: if(debug['c']){ thumbcount(); print("ARM size = %d\n", armsize); @@ -327,7 +279,7 @@ out: errorexit(); } -void +static void zaddr(Biobuf *f, Adr *a, Sym *h[]) { int i, c; @@ -355,7 +307,7 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) 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(s != S && (s->type == STEXT || 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 @@ -398,9 +350,8 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) break; case D_FCONST: - a->ieee = mal(sizeof(Ieee)); - a->ieee->l = Bget4(f); - a->ieee->h = Bget4(f); + a->ieee.l = Bget4(f); + a->ieee.h = Bget4(f); break; } s = a->sym; @@ -441,7 +392,7 @@ void ldobj1(Biobuf *f, char *pkg, int64 len, char *pn) { int32 ipc; - Prog *p, *t; + Prog *p; Sym *h[NSYM], *s, *di; int v, o, r, skip; uint32 sig; @@ -449,7 +400,9 @@ ldobj1(Biobuf *f, char *pkg, int64 len, char *pn) int ntext; int32 eof; char src[1024], *x; + Prog *lastp; + lastp = nil; ntext = 0; eof = Boffset(f) + len; di = S; @@ -499,13 +452,17 @@ loop: if(sig != 0){ if(s->sig != 0 && s->sig != sig) - diag("incompatible type signatures %lux(%s) and %lux(%s) for %s", s->sig, s->file, sig, pn, s->name); + diag("incompatible type signatures %ux(%s) and %ux(%s) for %s", s->sig, s->file, sig, pn, s->name); s->sig = sig; s->file = pn; } if(debug['W']) print(" ANAME %s\n", s->name); + if(o < 0 || o >= nelem(h)) { + fprint(2, "%s: mangled input file\n", pn); + errorexit(); + } h[o] = s; if((v == D_EXTERN || v == D_STATIC) && s->type == 0) s->type = SXREF; @@ -533,8 +490,8 @@ loop: zaddr(f, &p->from, h); zaddr(f, &p->to, h); - if(p->reg > NREG) - diag("register out of range %d", p->reg); + if(p->as != ATEXT && p->as != AGLOBL && p->reg > NREG) + diag("register out of range %A %d", p->as, p->reg); p->link = P; p->cond = P; @@ -559,10 +516,10 @@ loop: case AEND: histtoauto(); - if(curtext != P) - curtext->to.autom = curauto; + if(cursym != nil && cursym->text) + cursym->autom = curauto; curauto = 0; - curtext = P; + cursym = nil; if(Boffset(f) == eof) return; goto newloop; @@ -582,98 +539,31 @@ loop: s->type = SBSS; s->value = 0; } - if(p->to.offset > s->value) - s->value = p->to.offset; + if(p->to.offset > s->size) + s->size = p->to.offset; if(p->reg & DUPOK) s->dupok = 1; break; - case ADYNT: - s = p->from.sym; - 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(s == S) - break; - - p->from.offset = di->value; - s->type = SDATA; - if(curtext == P) { - diag("DYNT not in text: %P", p); - break; - } - p->to.sym = curtext->from.sym; - p->to.type = D_CONST; - if(s != S) { - p->dlink = s->data; - s->data = p; - } - if(edatap == P) - datap = p; - else - edatap->link = p; - edatap = p; - break; - - case AINIT: - s = p->from.sym; - if(s == 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; - s->type = SDATA; - if(s != S) { - p->dlink = s->data; - s->data = p; - } - if(edatap == P) - datap = p; - else - edatap->link = p; - edatap = p; - break; - case ADATA: // Assume that AGLOBL comes after ADATA. // If we've seen an AGLOBL that said this sym was DUPOK, // ignore any more ADATA we see, which must be // redefinitions. s = p->from.sym; - if(s != S && s->dupok) { + if(s->dupok) { if(debug['v']) Bprint(&bso, "skipping %s in %s: dupok\n", s->name, pn); goto loop; } - if(s != S) { - p->dlink = s->data; - s->data = p; - if(s->file == nil) - s->file = pn; - else if(s->file != pn) { - diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); - errorexit(); - } + if(s->file == nil) + s->file = pn; + else if(s->file != pn) { + diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); + errorexit(); } - if(edatap == P) - datap = p; - else - edatap->link = p; - edatap = p; + savedata(s, p); + unmal(p, sizeof *p); break; case AGOK: @@ -683,31 +573,24 @@ loop: break; case ATEXT: - s = p->from.sym; - if(ntext++ == 0 && s->type != 0 && s->type != SXREF) { - /* redefinition, so file has probably been seen before */ - if(debug['v']) - Bprint(&bso, "skipping: %s: redefinition: %s", pn, s->name); - return; - } - setarch(p); - setthumb(p); - p->align = 4; - if(curtext != P) { + if(cursym != nil && cursym->text) { histtoauto(); - curtext->to.autom = curauto; + cursym->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(); } + cursym = s; + if(ntext++ == 0 && s->type != 0 && s->type != SXREF) { + /* redefinition, so file has probably been seen before */ + if(debug['v']) + Bprint(&bso, "skipping: %s: redefinition: %s", pn, s->name); + return; + } + skip = 0; if(s->type != 0 && s->type != SXREF) { if(p->reg & DUPOK) { skip = 1; @@ -715,21 +598,24 @@ loop: } diag("redefinition: %s\n%P", s->name, p); } + if(etextp) + etextp->next = s; + else + textp = s; + etextp = s; + setarch(p); + setthumb(p); + p->align = 4; + autosize = (p->to.offset+3L) & ~3L; + p->to.offset = autosize; + autosize += 4; s->type = STEXT; s->text = p; 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: @@ -778,27 +664,15 @@ loop: if(skip) goto casedef; - if(p->from.type == D_FCONST && chipfloat(p->from.ieee) < 0) { + if(p->from.type == D_FCONST && chipfloat(&p->from.ieee) < 0 && + (chipzero(&p->from.ieee) < 0 || (p->scond & C_SCOND) != C_SCOND_NONE)) { /* size sb 9 max */ - sprint(literal, "$%lux", ieeedtof(p->from.ieee)); + sprint(literal, "$%ux", 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; - if(edatap == P) - datap = t; - else - edatap->link = t; - edatap = t; - t->link = P; + adduint32(s, ieeedtof(&p->from.ieee)); + s->reachable = 0; } p->from.type = D_OREG; p->from.sym = s; @@ -813,28 +687,17 @@ loop: if(skip) goto casedef; - if(p->from.type == D_FCONST && chipfloat(p->from.ieee) < 0) { + if(p->from.type == D_FCONST && chipfloat(&p->from.ieee) < 0 && + (chipzero(&p->from.ieee) < 0 || (p->scond & C_SCOND) != C_SCOND_NONE)) { /* size sb 18 max */ - sprint(literal, "$%lux.%lux", - p->from.ieee->l, p->from.ieee->h); + sprint(literal, "$%ux.%ux", + 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; - if(edatap == P) - datap = t; - else - edatap->link = t; - edatap = t; - t->link = P; + adduint32(s, p->from.ieee.l); + adduint32(s, p->from.ieee.h); + s->reachable = 0; } p->from.type = D_OREG; p->from.sym = s; @@ -847,13 +710,17 @@ loop: casedef: if(skip) nopout(p); - + p->pc = pc; + pc++; if(p->to.type == D_BRANCH) p->to.offset += ipc; + if(lastp == nil) { + if(p->as != ANOP) + diag("unexpected instruction: %P", p); + break; + } lastp->link = p; lastp = p; - p->pc = pc; - pc++; break; } goto loop; @@ -872,191 +739,13 @@ prg(void) return p; } -void -doprof1(void) -{ - Sym *s; - int32 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; - } - } -} - static void puntfp(Prog *p) { USED(p); /* floating point - punt for now */ - curtext->reg = NREG; /* ARM */ - curtext->from.sym->thumb = 0; + cursym->text->reg = NREG; /* ARM */ + cursym->thumb = 0; thumb = 0; // print("%s: generating ARM code (contains floating point ops %d)\n", curtext->from.sym->name, p->line); } diff --git a/src/cmd/5l/optab.c b/src/cmd/5l/optab.c index 92fe12fc2..96b216837 100644 --- a/src/cmd/5l/optab.c +++ b/src/cmd/5l/optab.c @@ -34,8 +34,6 @@ Optab optab[] = { /* struct Optab: OPCODE, from, prog->reg, to, type,size,param,flag */ - { ATEXT, C_LEXT, C_NONE, C_LCON, 0, 0, 0 }, - { ATEXT, C_LEXT, C_REG, C_LCON, 0, 0, 0 }, { ATEXT, C_ADDR, C_NONE, C_LCON, 0, 0, 0 }, { ATEXT, C_ADDR, C_REG, C_LCON, 0, 0, 0 }, @@ -56,7 +54,6 @@ Optab optab[] = { AMVN, C_SHIFT,C_NONE, C_REG, 3, 4, 0 }, { ACMP, C_SHIFT,C_REG, C_NONE, 3, 4, 0 }, - { AMOVW, C_RECON,C_NONE, C_REG, 4, 4, REGSB }, { AMOVW, C_RACON,C_NONE, C_REG, 4, 4, REGSP }, { AB, C_NONE, C_NONE, C_SBRA, 5, 4, 0, LPOOL }, @@ -81,7 +78,6 @@ Optab optab[] = { AWORD, C_NONE, C_NONE, C_LCON, 11, 4, 0 }, { AWORD, C_NONE, C_NONE, C_GCON, 11, 4, 0 }, - { AWORD, C_NONE, C_NONE, C_LEXT, 11, 4, 0 }, { AWORD, C_NONE, C_NONE, C_ADDR, 11, 4, 0 }, { AMOVW, C_NCON, C_NONE, C_REG, 12, 4, 0 }, @@ -109,85 +105,64 @@ Optab optab[] = { AMULL, C_REG, C_REG, C_REGREG, 17, 4, 0 }, - { AMOVW, C_REG, C_NONE, C_SEXT, 20, 4, REGSB }, { AMOVW, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, { AMOVW, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, - { AMOVB, C_REG, C_NONE, C_SEXT, 20, 4, REGSB }, { AMOVB, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, { AMOVB, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, - { AMOVBU, C_REG, C_NONE, C_SEXT, 20, 4, REGSB }, { AMOVBU, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, { AMOVBU, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, - { AMOVW, C_SEXT, C_NONE, C_REG, 21, 4, REGSB }, { AMOVW, C_SAUTO,C_NONE, C_REG, 21, 4, REGSP }, { AMOVW, C_SOREG,C_NONE, C_REG, 21, 4, 0 }, - { AMOVBU, C_SEXT, C_NONE, C_REG, 21, 4, REGSB }, { AMOVBU, C_SAUTO,C_NONE, C_REG, 21, 4, REGSP }, { AMOVBU, C_SOREG,C_NONE, C_REG, 21, 4, 0 }, - { AMOVB, C_SEXT, C_NONE, C_REG, 22, 12, REGSB }, { AMOVB, C_SAUTO,C_NONE, C_REG, 22, 12, REGSP }, { AMOVB, C_SOREG,C_NONE, C_REG, 22, 12, 0 }, - { AMOVH, C_SEXT, C_NONE, C_REG, 22, 12, REGSB }, { AMOVH, C_SAUTO,C_NONE, C_REG, 22, 12, REGSP }, { AMOVH, C_SOREG,C_NONE, C_REG, 22, 12, 0 }, - { AMOVHU, C_SEXT, C_NONE, C_REG, 22, 12, REGSB }, { AMOVHU, C_SAUTO,C_NONE, C_REG, 22, 12, REGSP }, { AMOVHU, C_SOREG,C_NONE, C_REG, 22, 12, 0 }, - { AMOVH, C_REG, C_NONE, C_SEXT, 23, 12, REGSB }, { AMOVH, C_REG, C_NONE, C_SAUTO, 23, 12, REGSP }, { AMOVH, C_REG, C_NONE, C_SOREG, 23, 12, 0 }, - { AMOVHU, C_REG, C_NONE, C_SEXT, 23, 12, REGSB }, { AMOVHU, C_REG, C_NONE, C_SAUTO, 23, 12, REGSP }, { AMOVHU, C_REG, C_NONE, C_SOREG, 23, 12, 0 }, - { AMOVW, C_REG, C_NONE, C_LEXT, 30, 8, REGSB, LTO }, { AMOVW, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, { AMOVW, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, { AMOVW, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO }, - { AMOVB, C_REG, C_NONE, C_LEXT, 30, 8, REGSB, LTO }, { AMOVB, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, { AMOVB, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, { AMOVB, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO }, - { AMOVBU, C_REG, C_NONE, C_LEXT, 30, 8, REGSB, LTO }, { AMOVBU, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, { AMOVBU, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, { AMOVBU, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO }, - { AMOVW, C_LEXT, C_NONE, C_REG, 31, 8, REGSB, LFROM }, { AMOVW, C_LAUTO,C_NONE, C_REG, 31, 8, REGSP, LFROM }, { AMOVW, C_LOREG,C_NONE, C_REG, 31, 8, 0, LFROM }, { AMOVW, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM }, - { AMOVBU, C_LEXT, C_NONE, C_REG, 31, 8, REGSB, LFROM }, { AMOVBU, C_LAUTO,C_NONE, C_REG, 31, 8, REGSP, LFROM }, { AMOVBU, C_LOREG,C_NONE, C_REG, 31, 8, 0, LFROM }, { AMOVBU, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM }, - { AMOVB, C_LEXT, C_NONE, C_REG, 32, 16, REGSB, LFROM }, { AMOVB, C_LAUTO,C_NONE, C_REG, 32, 16, REGSP, LFROM }, { AMOVB, C_LOREG,C_NONE, C_REG, 32, 16, 0, LFROM }, { AMOVB, C_ADDR, C_NONE, C_REG, 66, 16, 0, LFROM }, - { AMOVH, C_LEXT, C_NONE, C_REG, 32, 16, REGSB, LFROM }, { AMOVH, C_LAUTO,C_NONE, C_REG, 32, 16, REGSP, LFROM }, { AMOVH, C_LOREG,C_NONE, C_REG, 32, 16, 0, LFROM }, { AMOVH, C_ADDR, C_NONE, C_REG, 66, 16, 0, LFROM }, - { AMOVHU, C_LEXT, C_NONE, C_REG, 32, 16, REGSB, LFROM }, { AMOVHU, C_LAUTO,C_NONE, C_REG, 32, 16, REGSP, LFROM }, { AMOVHU, C_LOREG,C_NONE, C_REG, 32, 16, 0, LFROM }, { AMOVHU, C_ADDR, C_NONE, C_REG, 66, 16, 0, LFROM }, - { AMOVH, C_REG, C_NONE, C_LEXT, 33, 24, REGSB, LTO }, { AMOVH, C_REG, C_NONE, C_LAUTO, 33, 24, REGSP, LTO }, { AMOVH, C_REG, C_NONE, C_LOREG, 33, 24, 0, LTO }, { AMOVH, C_REG, C_NONE, C_ADDR, 67, 24, 0, LTO }, - { AMOVHU, C_REG, C_NONE, C_LEXT, 33, 24, REGSB, LTO }, { AMOVHU, C_REG, C_NONE, C_LAUTO, 33, 24, REGSP, LTO }, { AMOVHU, C_REG, C_NONE, C_LOREG, 33, 24, 0, LTO }, { AMOVHU, C_REG, C_NONE, C_ADDR, 67, 24, 0, LTO }, - { AMOVW, C_LECON,C_NONE, C_REG, 34, 8, REGSB, LFROM }, { AMOVW, C_LACON,C_NONE, C_REG, 34, 8, REGSP, LFROM }, { AMOVW, C_PSR, C_NONE, C_REG, 35, 4, 0 }, @@ -201,19 +176,15 @@ Optab optab[] = { ARFE, C_NONE, C_NONE, C_NONE, 41, 4, 0 }, - { AMOVF, C_FREG, C_NONE, C_FEXT, 50, 4, REGSB }, { AMOVF, C_FREG, C_NONE, C_FAUTO, 50, 4, REGSP }, { AMOVF, C_FREG, C_NONE, C_FOREG, 50, 4, 0 }, - { AMOVF, C_FEXT, C_NONE, C_FREG, 51, 4, REGSB }, { AMOVF, C_FAUTO,C_NONE, C_FREG, 51, 4, REGSP }, { AMOVF, C_FOREG,C_NONE, C_FREG, 51, 4, 0 }, - { AMOVF, C_FREG, C_NONE, C_LEXT, 52, 12, REGSB, LTO }, { AMOVF, C_FREG, C_NONE, C_LAUTO, 52, 12, REGSP, LTO }, { AMOVF, C_FREG, C_NONE, C_LOREG, 52, 12, 0, LTO }, - { AMOVF, C_LEXT, C_NONE, C_FREG, 53, 12, REGSB, LFROM }, { AMOVF, C_LAUTO,C_NONE, C_FREG, 53, 12, REGSP, LFROM }, { AMOVF, C_LOREG,C_NONE, C_FREG, 53, 12, 0, LFROM }, @@ -222,17 +193,8 @@ Optab optab[] = { AADDF, C_FREG, C_NONE, C_FREG, 54, 4, 0 }, { AADDF, C_FREG, C_REG, C_FREG, 54, 4, 0 }, - { AADDF, C_FCON, C_NONE, C_FREG, 54, 4, 0 }, - { AADDF, C_FCON, C_REG, C_FREG, 54, 4, 0 }, - { AMOVF, C_FCON, C_NONE, C_FREG, 54, 4, 0 }, { AMOVF, C_FREG, C_NONE, C_FREG, 54, 4, 0 }, - { ACMPF, C_FREG, C_REG, C_NONE, 54, 4, 0 }, - { ACMPF, C_FCON, C_REG, C_NONE, 54, 4, 0 }, - - { AMOVFW, C_FREG, C_NONE, C_REG, 55, 4, 0 }, - { AMOVFW, C_REG, C_NONE, C_FREG, 55, 4, 0 }, - { AMOVW, C_REG, C_NONE, C_FCR, 56, 4, 0 }, { AMOVW, C_FCR, C_NONE, C_REG, 57, 4, 0 }, @@ -248,41 +210,46 @@ Optab optab[] = { ACASE, C_REG, C_NONE, C_NONE, 62, 4, 0 }, { ABCASE, C_NONE, C_NONE, C_SBRA, 63, 4, 0 }, - { AMOVH, C_REG, C_NONE, C_HEXT, 70, 4, REGSB, V4 }, { AMOVH, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, V4 }, { AMOVH, C_REG, C_NONE, C_HOREG, 70, 4, 0, V4 }, - { AMOVHU, C_REG, C_NONE, C_HEXT, 70, 4, REGSB, V4 }, { AMOVHU, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, V4 }, { AMOVHU, C_REG, C_NONE, C_HOREG, 70, 4, 0, V4 }, - { AMOVB, C_HEXT, C_NONE, C_REG, 71, 4, REGSB, V4 }, { AMOVB, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, V4 }, { AMOVB, C_HOREG,C_NONE, C_REG, 71, 4, 0, V4 }, - { AMOVH, C_HEXT, C_NONE, C_REG, 71, 4, REGSB, V4 }, { AMOVH, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, V4 }, { AMOVH, C_HOREG,C_NONE, C_REG, 71, 4, 0, V4 }, - { AMOVHU, C_HEXT, C_NONE, C_REG, 71, 4, REGSB, V4 }, { AMOVHU, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, V4 }, { AMOVHU, C_HOREG,C_NONE, C_REG, 71, 4, 0, V4 }, - { AMOVH, C_REG, C_NONE, C_LEXT, 72, 8, REGSB, LTO|V4 }, { AMOVH, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO|V4 }, { AMOVH, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO|V4 }, - { AMOVHU, C_REG, C_NONE, C_LEXT, 72, 8, REGSB, LTO|V4 }, { AMOVHU, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO|V4 }, { AMOVHU, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO|V4 }, - { AMOVB, C_LEXT, C_NONE, C_REG, 73, 8, REGSB, LFROM|V4 }, { AMOVB, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM|V4 }, { AMOVB, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM|V4 }, - { AMOVH, C_LEXT, C_NONE, C_REG, 73, 8, REGSB, LFROM|V4 }, { AMOVH, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM|V4 }, { AMOVH, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM|V4 }, - { AMOVHU, C_LEXT, C_NONE, C_REG, 73, 8, REGSB, LFROM|V4 }, { AMOVHU, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM|V4 }, { AMOVHU, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM|V4 }, { ALDREX, C_SOREG,C_NONE, C_REG, 77, 4, 0 }, { ASTREX, C_SOREG,C_REG, C_REG, 78, 4, 0 }, + { AMOVF, C_ZFCON,C_NONE, C_FREG, 80, 4, 0 }, + { AMOVF, C_SFCON,C_NONE, C_FREG, 81, 4, 0 }, + + { ACMPF, C_FREG, C_REG, C_NONE, 82, 8, 0 }, + { ACMPF, C_FREG, C_NONE, C_NONE, 83, 8, 0 }, + + { AMOVFW, C_FREG, C_NONE, C_FREG, 84, 4, 0 }, + { AMOVWF, C_FREG, C_NONE, C_FREG, 85, 4, 0 }, + + { AMOVFW, C_FREG, C_NONE, C_REG, 86, 8, 0 }, + { AMOVWF, C_REG, C_NONE, C_FREG, 87, 8, 0 }, + + { AMOVW, C_REG, C_NONE, C_FREG, 88, 4, 0 }, + { AMOVW, C_FREG, C_NONE, C_REG, 89, 4, 0 }, + { AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0 }, }; diff --git a/src/cmd/5l/pass.c b/src/cmd/5l/pass.c index 06b1792b4..e16b34171 100644 --- a/src/cmd/5l/pass.c +++ b/src/cmd/5l/pass.c @@ -28,147 +28,12 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Code and data passes. + #include "l.h" #include "../ld/lib.h" -void -dodata(void) -{ - int i, t; - Sym *s; - Prog *p; - int32 orig, v; - - if(debug['v']) - Bprint(&bso, "%5.2f dodata\n", cputime()); - Bflush(&bso); - for(p = datap; p != P; p = p->link) { - s = p->from.sym; - if(p->as == ADYNT || p->as == AINIT) - s->value = dtype; - if(s->type == SBSS) - s->type = SDATA; - if(s->type != SDATA && s->type != SELFDATA) - diag("initialize non-data (%d): %s\n%P", - s->type, s->name, p); - v = p->from.offset + p->reg; - if(v > s->value) - diag("initialize bounds (%ld/%ld): %s\n%P", - v, s->value, s->name, p); - if((s->type == SBSS || s->type == SDATA) && (p->to.type == D_CONST || p->to.type == D_OCONST) && (p->to.name == D_EXTERN || p->to.name == D_STATIC)){ - s = p->to.sym; - if(s != S && (s->type == STEXT || s->type == SLEAF || s->type == SCONST || s->type == SXREF)) - s->fnptr = 1; - } - } - - if(debug['t']) { - /* - * pull out string constants - */ - for(p = datap; p != P; p = p->link) { - s = p->from.sym; - if(p->to.type == D_SCONST) - s->type = SSTRING; - } - } - - /* - * pass 0 - * assign elf data - must be segregated from real data - */ - orig = 0; - for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) { - if(!s->reachable || s->type != SELFDATA) - continue; - v = s->value; - while(v & 3) - v++; - s->size = v; - s->value = orig; - orig += v; - } - elfdatsize = orig; - - /* - * pass 1 - * assign 'small' variables to data segment - * (rational is that data segment is more easily - * addressed through offset on R12) - */ - for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) { - t = s->type; - if(t != SDATA && t != SBSS) - continue; - v = s->value; - if(v == 0) { - diag("%s: no size", s->name); - v = 1; - } - while(v & 3) - v++; - s->size = v; - s->value = v; - if(v > MINSIZ) - continue; - s->value = orig; - orig += v; - s->type = SDATA1; - } - - /* - * pass 2 - * assign large 'data' variables to data segment - */ - for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) { - t = s->type; - if(t != SDATA) { - if(t == SDATA1) - s->type = SDATA; - continue; - } - v = s->value; - s->size = v; - s->value = orig; - orig += v; - } - - while(orig & 7) - orig++; - datsize = orig; - - /* - * pass 3 - * everything else to bss segment - */ - for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) { - if(s->type != SBSS) - continue; - v = s->value; - s->size = v; - s->value = orig; - orig += v; - } - while(orig & 7) - orig++; - bsssize = orig-datsize; - - xdefine("setR12", SDATA, 0L+BIG); - xdefine("bdata", SDATA, 0L); - xdefine("data", SBSS, 0); - xdefine("edata", SDATA, datsize); - xdefine("end", SBSS, datsize+bsssize); - xdefine("etext", STEXT, 0L); - - if(debug['s']) - xdefine("symdat", SFIXED, 0); - else - xdefine("symdat", SFIXED, SYMDATVA); -} +static void xfol(Prog*, Prog**); void undef(void) @@ -177,7 +42,7 @@ undef(void) Sym *s; for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) + for(s = hash[i]; s != S; s = s->hash) if(s->type == SXREF) diag("%s: not defined", s->name); } @@ -223,20 +88,23 @@ relinv(int a) void follow(void) { + Prog *firstp, *lastp; + if(debug['v']) Bprint(&bso, "%5.2f follow\n", cputime()); Bflush(&bso); - firstp = prg(); - lastp = firstp; - xfol(textp); - - firstp = firstp->link; - lastp->link = P; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + firstp = prg(); + lastp = firstp; + xfol(cursym->text, &lastp); + lastp->link = nil; + cursym->text = firstp->link; + } } -void -xfol(Prog *p) +static void +xfol(Prog *p, Prog **last) { Prog *q, *r; int a, i; @@ -246,12 +114,6 @@ loop: return; setarch(p); a = p->as; - if(a == ATEXT) - curtext = p; - if(!curtext->from.sym->reachable) { - p = p->cond; - goto loop; - } if(a == AB) { q = p->cond; if(q != P && q->as != ATEXT) { @@ -263,7 +125,7 @@ loop: } if(p->mark & FOLL) { for(i=0,q=p; i<4; i++,q=q->link) { - if(q == lastp) + if(q == *last || q == nil) break; a = q->as; if(a == ANOP) { @@ -272,7 +134,7 @@ loop: } if(a == AB || (a == ARET && q->scond == 14) || a == ARFE) goto copy; - if(!q->cond || (q->cond->mark&FOLL)) + if(q->cond == P || (q->cond->mark&FOLL)) continue; if(a != ABEQ && a != ABNE) continue; @@ -285,12 +147,12 @@ loop: r->mark |= FOLL; if(p != q) { p = p->link; - lastp->link = r; - lastp = r; + (*last)->link = r; + *last = r; continue; } - lastp->link = r; - lastp = r; + (*last)->link = r; + *last = r; if(a == AB || (a == ARET && q->scond == 14) || a == ARFE) return; r->as = ABNE; @@ -299,7 +161,7 @@ loop: r->cond = p->link; r->link = p->cond; if(!(r->link->mark&FOLL)) - xfol(r->link); + xfol(r->link, last); if(!(r->cond->mark&FOLL)) print("cant happen 2\n"); return; @@ -315,8 +177,8 @@ loop: p = q; } p->mark |= FOLL; - lastp->link = p; - lastp = p; + (*last)->link = p; + *last = p; if(a == AB || (a == ARET && p->scond == 14) || a == ARFE){ return; } @@ -329,7 +191,7 @@ loop: p->link = p->cond; p->cond = q; } - xfol(p->link); + xfol(p->link, last); q = brchain(p->cond); if(q == P) q = p->cond; @@ -349,7 +211,7 @@ patch(void) { int32 c, vexit; Prog *p, *q; - Sym *s, *s1; + Sym *s; int a; if(debug['v']) @@ -358,113 +220,71 @@ patch(void) mkfwd(); s = lookup("exit", 0); vexit = s->value; - for(p = firstp; p != P; p = p->link) { - setarch(p); - a = p->as; - if(a == ATEXT) - curtext = p; - if(seenthumb && a == ABL){ - // if((s = p->to.sym) != S && (s1 = curtext->from.sym) != S) - // print("%s calls %s\n", s1->name, s->name); - if((s = p->to.sym) != S && (s1 = curtext->from.sym) != S && s->thumb != s1->thumb) - s->foreign = 1; - } - if((a == ABL || a == ABX || a == AB || a == ARET) && - p->to.type != D_BRANCH && p->to.sym != S) { - s = p->to.sym; - switch(s->type) { - default: - diag("undefined: %s", s->name); - s->type = STEXT; - s->value = vexit; - continue; // avoid more error messages - case STEXT: - p->to.offset = s->value; - p->to.type = D_BRANCH; - break; - case SUNDEF: - if(p->as != ABL) - diag("help: SUNDEF in AB || ARET"); - p->to.offset = 0; - p->to.type = D_BRANCH; - p->cond = UP; - break; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + for(p = cursym->text; p != P; p = p->link) { + setarch(p); + a = p->as; + if(seenthumb && a == ABL){ + // if((s = p->to.sym) != S && (s1 = curtext->from.sym) != S) + // print("%s calls %s\n", s1->name, s->name); + if((s = p->to.sym) != S && s->thumb != cursym->thumb) + s->foreign = 1; } - } - if(p->to.type != D_BRANCH || p->cond == UP) - continue; - c = p->to.offset; - for(q = firstp; q != P;) { - if(q->forwd != P) - if(c >= q->forwd->pc) { - q = q->forwd; + if((a == ABL || a == ABX || a == AB || a == ARET) && + p->to.type != D_BRANCH && p->to.sym != S) { + s = p->to.sym; + switch(s->type) { + default: + diag("undefined: %s", s->name); + s->type = STEXT; + s->value = vexit; + continue; // avoid more error messages + case STEXT: + p->to.offset = s->value; + p->to.type = D_BRANCH; + break; + } + } + if(p->to.type != D_BRANCH) continue; + c = p->to.offset; + for(q = textp->text; q != P;) { + if(c == q->pc) + break; + if(q->forwd != P && c >= q->forwd->pc) + q = q->forwd; + else + q = q->link; } - if(c == q->pc) - break; - q = q->link; - } - if(q == P) { - diag("branch out of range %ld\n%P", c, p); - p->to.type = D_NONE; + if(q == P) { + diag("branch out of range %d\n%P", c, p); + p->to.type = D_NONE; + } + p->cond = q; } - p->cond = q; } - for(p = firstp; p != P; p = p->link) { - setarch(p); - a = p->as; - if(p->as == ATEXT) - curtext = p; - if(seenthumb && a == ABL) { + for(cursym = textp; cursym != nil; cursym = cursym->next) { + for(p = cursym->text; p != P; p = p->link) { + setarch(p); + a = p->as; + if(seenthumb && a == ABL) { #ifdef CALLEEBX - if(0) - {} + if(0) + {} #else - if((s = p->to.sym) != S && (s->foreign || s->fnptr)) - p->as = ABX; + if((s = p->to.sym) != S && (s->foreign || s->fnptr)) + p->as = ABX; #endif - else if(p->to.type == D_OREG) - p->as = ABX; - } - if(p->cond != P && p->cond != UP) { - p->cond = brloop(p->cond); - if(p->cond != P) - if(p->to.type == D_BRANCH) - p->to.offset = p->cond->pc; - } - } -} - -#define LOG 5 -void -mkfwd(void) -{ - Prog *p; - int32 dwn[LOG], cnt[LOG], i; - Prog *lst[LOG]; - - for(i=0; i<LOG; i++) { - if(i == 0) - cnt[i] = 1; else - cnt[i] = LOG * cnt[i-1]; - dwn[i] = 1; - lst[i] = P; - } - i = 0; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - i--; - if(i < 0) - i = LOG-1; - p->forwd = P; - dwn[i]--; - if(dwn[i] <= 0) { - dwn[i] = cnt[i]; - if(lst[i] != P) - lst[i]->forwd = p; - lst[i] = p; + else if(p->to.type == D_OREG) + p->as = ABX; + } + if(p->cond != P) { + p->cond = brloop(p->cond); + if(p->cond != P) + if(p->to.type == D_BRANCH) + p->to.offset = p->cond->pc; + } } } } @@ -543,462 +363,3 @@ rnd(int32 v, int32 r) v -= c; return v; } - -#define Reachable(n) if((s = lookup(n, 0)) != nil) s->used++ - -static void -rused(Adr *a) -{ - Sym *s = a->sym; - - if(s == S) - return; - if(a->type == D_OREG || a->type == D_OCONST || a->type == D_CONST){ - if(a->name == D_EXTERN || a->name == D_STATIC){ - if(s->used == 0) - s->used = 1; - } - } - else if(a->type == D_BRANCH){ - if(s->used == 0) - s->used = 1; - } -} - -void -reachable() -{ - Prog *p, *prev, *prevt, *nextt, *q; - Sym *s, *s0; - int i, todo; - char *a; - - Reachable("_div"); - Reachable("_divu"); - Reachable("_mod"); - Reachable("_modu"); - a = INITENTRY; - if(*a >= '0' && *a <= '9') - return; - s = lookup(a, 0); - if(s == nil) - return; - if(s->type == 0){ - s->used = 1; // to stop asm complaining - for(p = firstp; p != P && p->as != ATEXT; p = p->link) - ; - if(p == nil) - return; - s = p->from.sym; - } - s->used = 1; - do{ - todo = 0; - for(p = firstp; p != P; p = p->link){ - if(p->as == ATEXT && (s0 = p->from.sym)->used == 1){ - todo = 1; - for(q = p->link; q != P && q->as != ATEXT; q = q->link){ - rused(&q->from); - rused(&q->to); - } - s0->used = 2; - } - } - for(p = datap; p != P; p = p->link){ - if((s0 = p->from.sym)->used == 1){ - todo = 1; - for(q = p; q != P; q = q->link){ // data can be scattered - if(q->from.sym == s0) - rused(&q->to); - } - s0->used = 2; - } - } - }while(todo); - prev = nil; - prevt = nextt = nil; - for(p = firstp; p != P; ){ - if(p->as == ATEXT){ - prevt = nextt; - nextt = p; - } - if(p->as == ATEXT && (s0 = p->from.sym)->used == 0){ - s0->type = SREMOVED; - for(q = p->link; q != P && q->as != ATEXT; q = q->link) - ; - if(q != p->cond) - diag("bad ptr in reachable()"); - if(prev == nil) - firstp = q; - else - prev->link = q; - if(q == nil) - lastp = prev; - if(prevt == nil) - textp = q; - else - prevt->cond = q; - if(q == nil) - etextp = prevt; - nextt = prevt; - if(debug['V']) - print("%s unused\n", s0->name); - p = q; - } - else{ - prev = p; - p = p->link; - } - } - prevt = nil; - for(p = datap; p != nil; ){ - if((s0 = p->from.sym)->used == 0){ - s0->type = SREMOVED; - prev = prevt; - for(q = p; q != nil; q = q->link){ - if(q->from.sym == s0){ - if(prev == nil) - datap = q->link; - else - prev->link = q->link; - } - else - prev = q; - } - if(debug['V']) - print("%s unused (data)\n", s0->name); - p = prevt->link; - } - else{ - prevt = p; - p = p->link; - } - } - for(i=0; i<NHASH; i++){ - for(s = hash[i]; s != S; s = s->link){ - if(s->used == 0) - s->type = SREMOVED; - } - } -} - -static void -fused(Adr *a, Prog *p, Prog *ct) -{ - Sym *s = a->sym; - Use *u; - - if(s == S) - return; - if(a->type == D_OREG || a->type == D_OCONST || a->type == D_CONST){ - if(a->name == D_EXTERN || a->name == D_STATIC){ - u = malloc(sizeof(Use)); - u->p = p; - u->ct = ct; - u->link = s->use; - s->use = u; - } - } - else if(a->type == D_BRANCH){ - u = malloc(sizeof(Use)); - u->p = p; - u->ct = ct; - u->link = s->use; - s->use = u; - } -} - -static int -ckfpuse(Prog *p, Prog *ct, Sym *fp, Sym *r) -{ - int reg; - - USED(fp); - USED(ct); - if(p->from.sym == r && p->as == AMOVW && (p->from.type == D_CONST || p->from.type == D_OREG) && p->reg == NREG && p->to.type == D_REG){ - reg = p->to.reg; - for(p = p->link; p != P && p->as != ATEXT; p = p->link){ - if((p->as == ABL || p->as == ABX) && p->to.type == D_OREG && p->to.reg == reg) - return 1; - if(!debug['F'] && (isbranch(p) || p->as == ARET)){ - // print("%s: branch %P in %s\n", fp->name, p, ct->from.sym->name); - return 0; - } - if((p->from.type == D_REG || p->from.type == D_OREG) && p->from.reg == reg){ - if(!debug['F'] && p->to.type != D_REG){ - // print("%s: store %P in %s\n", fp->name, p, ct->from.sym->name); - return 0; - } - reg = p->to.reg; - } - } - } - // print("%s: no MOVW O(R), R\n", fp->name); - return debug['F']; -} - -static void -setfpuse(Prog *p, Sym *fp, Sym *r) -{ - int reg; - - if(p->from.sym == r && p->as == AMOVW && (p->from.type == D_CONST || p->from.type == D_OREG) && p->reg == NREG && p->to.type == D_REG){ - reg = p->to.reg; - for(p = p->link; p != P && p->as != ATEXT; p = p->link){ - if((p->as == ABL || p->as == ABX) && p->to.type == D_OREG && p->to.reg == reg){ - fp->fnptr = 0; - p->as = ABL; // safe to do so -// print("simplified %s call\n", fp->name); - break; - } - if(!debug['F'] && (isbranch(p) || p->as == ARET)) - diag("bad setfpuse call"); - if((p->from.type == D_REG || p->from.type == D_OREG) && p->from.reg == reg){ - if(!debug['F'] && p->to.type != D_REG) - diag("bad setfpuse call"); - reg = p->to.reg; - } - } - } -} - -static int -cksymuse(Sym *s, int t) -{ - Prog *p; - - for(p = datap; p != P; p = p->link){ - if(p->from.sym == s && p->to.sym != nil && strcmp(p->to.sym->name, ".string") != 0 && p->to.sym->thumb != t){ - // print("%s %s %d %d ", p->from.sym->name, p->to.sym->name, p->to.sym->thumb, t); - return 0; - } - } - return 1; -} - -/* check the use of s at the given point */ -static int -ckuse(Sym *s, Sym *s0, Use *u) -{ - Sym *s1; - - s1 = u->p->from.sym; -// print("ckuse %s %s %s\n", s->name, s0->name, s1 ? s1->name : "nil"); - if(u->ct == nil){ /* in data area */ - if(s0 == s && !cksymuse(s1, s0->thumb)){ - // print("%s: cksymuse fails\n", s0->name); - return 0; - } - for(u = s1->use; u != U; u = u->link) - if(!ckuse(s1, s0, u)) - return 0; - } - else{ /* in text area */ - if(u->ct->from.sym->thumb != s0->thumb){ - // print("%s(%d): foreign call %s(%d)\n", s0->name, s0->thumb, u->ct->from.sym->name, u->ct->from.sym->thumb); - return 0; - } - return ckfpuse(u->p, u->ct, s0, s); - } - return 1; -} - -static void -setuse(Sym *s, Sym *s0, Use *u) -{ - Sym *s1; - - s1 = u->p->from.sym; - if(u->ct == nil){ /* in data area */ - for(u = s1->use; u != U; u = u->link) - setuse(s1, s0, u); - } - else{ /* in text area */ - setfpuse(u->p, s0, s); - } -} - -/* detect BX O(R) which can be done as BL O(R) */ -void -fnptrs() -{ - int i; - Sym *s; - Prog *p; - Use *u; - - for(i=0; i<NHASH; i++){ - for(s = hash[i]; s != S; s = s->link){ - if(s->fnptr && (s->type == STEXT || s->type == SLEAF || s->type == SCONST)){ - // print("%s : fnptr %d %d\n", s->name, s->thumb, s->foreign); - } - } - } - /* record use of syms */ - for(p = firstp; p != P; p = p->link){ - if(p->as == ATEXT) - curtext = p; - else{ - fused(&p->from, p, curtext); - fused(&p->to, p, curtext); - } - } - for(p = datap; p != P; p = p->link) - fused(&p->to, p, nil); - - /* now look for fn ptrs */ - for(i=0; i<NHASH; i++){ - for(s = hash[i]; s != S; s = s->link){ - if(s->fnptr && (s->type == STEXT || s->type == SLEAF || s->type == SCONST)){ - for(u = s->use; u != U; u = u->link){ - if(!ckuse(s, s, u)) - break; - } - if(u == U){ // can simplify - for(u = s->use; u != U; u = u->link) - setuse(s, s, u); - } - } - } - } - - /* now free Use structures */ -} - -void -import(void) -{ - int i; - Sym *s; - - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type == SXREF && (nimports == 0 || s->subtype == SIMPORT)){ - undefsym(s); - Bprint(&bso, "IMPORT: %s sig=%lux v=%ld\n", s->name, s->sig, s->value); - } -} - -void -ckoff(Sym *s, int32 v) -{ - if(v < 0 || v >= 1<<Roffset) - diag("relocation offset %ld for %s out of range", v, s->name); -} - -Prog* -newdata(Sym *s, int o, int w, int t) -{ - Prog *p; - - p = prg(); - p->link = datap; - datap = p; - p->as = ADATA; - p->reg = w; - p->from.type = D_OREG; - p->from.name = t; - p->from.sym = s; - p->from.offset = o; - p->to.type = D_CONST; - p->to.name = D_NONE; - s->data = p; - return p; -} - -void -export(void) -{ - int i, j, n, off, nb, sv, ne; - Sym *s, *et, *str, **esyms; - Prog *p; - char buf[NSNAME], *t; - - n = 0; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT)) - n++; - esyms = malloc(n*sizeof(Sym*)); - ne = n; - n = 0; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT)) - esyms[n++] = s; - for(i = 0; i < ne-1; i++) - for(j = i+1; j < ne; j++) - if(strcmp(esyms[i]->name, esyms[j]->name) > 0){ - s = esyms[i]; - esyms[i] = esyms[j]; - esyms[j] = s; - } - - nb = 0; - off = 0; - et = lookup(EXPTAB, 0); - if(et->type != 0 && et->type != SXREF) - diag("%s already defined", EXPTAB); - et->type = SDATA; - str = lookup(".string", 0); - if(str->type == 0) - str->type = SDATA; - sv = str->value; - for(i = 0; i < ne; i++){ - s = esyms[i]; - Bprint(&bso, "EXPORT: %s sig=%lux t=%d\n", s->name, s->sig, s->type); - - /* signature */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.offset = s->sig; - - /* address */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.name = D_EXTERN; - p->to.sym = s; - - /* string */ - t = s->name; - n = strlen(t)+1; - for(;;){ - buf[nb++] = *t; - sv++; - if(nb >= NSNAME){ - p = newdata(str, sv-NSNAME, NSNAME, D_STATIC); - p->to.type = D_SCONST; - p->to.sval = malloc(NSNAME); - memmove(p->to.sval, buf, NSNAME); - nb = 0; - } - if(*t++ == 0) - break; - } - - /* name */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.name = D_STATIC; - p->to.sym = str; - p->to.offset = sv-n; - } - - if(nb > 0){ - p = newdata(str, sv-nb, nb, D_STATIC); - p->to.type = D_SCONST; - p->to.sval = malloc(NSNAME); - memmove(p->to.sval, buf, nb); - } - - for(i = 0; i < 3; i++){ - newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - } - et->value = off; - if(sv == 0) - sv = 1; - str->value = sv; - exports = ne; - free(esyms); -} diff --git a/src/cmd/5l/prof.c b/src/cmd/5l/prof.c new file mode 100644 index 000000000..ad115a8ca --- /dev/null +++ b/src/cmd/5l/prof.c @@ -0,0 +1,214 @@ +// Inferno utils/5l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Profiling. + +#include "l.h" +#include "../ld/lib.h" + +void +doprof1(void) +{ +#if 0 // TODO(rsc) + Sym *s; + int32 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; +#endif +} + +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(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; + setarch(p); + if(cursym == s2) { + ps2 = p; + p->reg = 1; + } + if(cursym == s4) { + ps4 = p; + p->reg = 1; + } + } + for(cursym = textp; cursym != nil; cursym = cursym->next) + for(p = cursym->text; 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; + } + } +} diff --git a/src/cmd/5l/softfloat.c b/src/cmd/5l/softfloat.c index 82874ee1c..fd66b0969 100644 --- a/src/cmd/5l/softfloat.c +++ b/src/cmd/5l/softfloat.c @@ -5,67 +5,82 @@ #define EXTERN #include "l.h" +// Software floating point. + void -softfloat() +softfloat(void) { Prog *p, *next, *psfloat; Sym *symsfloat; int wasfloat; - + + if(!debug['F']) + return; + symsfloat = lookup("_sfloat", 0); psfloat = P; if(symsfloat->type == STEXT) - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - if(p->from.sym == symsfloat) { - psfloat = p; - break; - } - } - } + psfloat = symsfloat->text; + + for(cursym = textp; cursym != nil; cursym = cursym->next) { + wasfloat = 0; + for(p = cursym->text; p != P; p = p->link) + if(p->cond != P) + p->cond->mark |= LABEL; + for(p = cursym->text; p != P; p = p->link) { + switch(p->as) { + case AMOVW: + if(p->to.type == D_FREG || p->from.type == D_FREG) + goto soft; + goto notsoft; + + 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: + goto soft; - wasfloat = 0; - p = firstp; - for(p = firstp; p != P; p = p->link) { - switch(p->as) { - 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 (psfloat == P) - diag("floats used with _sfloat not defined"); - if (!wasfloat) { - next = prg(); - *next = *p; + default: + goto notsoft; - // BL _sfloat(SB) - *p = zprg; - p->link = next; - p->as = ABL; - p->to.type = D_BRANCH; - p->to.sym = symsfloat; - p->cond = psfloat; + soft: + if (psfloat == P) + diag("floats used with _sfloat not defined"); + if (!wasfloat || (p->mark&LABEL)) { + next = prg(); + *next = *p; + + // BL _sfloat(SB) + *p = zprg; + p->link = next; + p->as = ABL; + p->to.type = D_BRANCH; + p->to.sym = symsfloat; + p->cond = psfloat; + + p = next; + wasfloat = 1; + } + break; - p = next; - wasfloat = 1; + notsoft: + wasfloat = 0; } - break; - default: - wasfloat = 0; } } } diff --git a/src/cmd/5l/span.c b/src/cmd/5l/span.c index a97af07f9..be0f5e8b3 100644 --- a/src/cmd/5l/span.c +++ b/src/cmd/5l/span.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Instruction layout. + #include "l.h" #include "../ld/lib.h" @@ -52,9 +54,9 @@ ispad(Prog *p) { if(p->as != AMOVW) return 0; - if(p->from.type != D_REG || p->from.reg != REGSB) + if(p->from.type != D_REG || p->from.reg != REGTMP) return 0; - if(p->to.type != D_REG || p->to.reg != REGSB) + if(p->to.type != D_REG || p->to.reg != REGTMP) return 0; return 1; } @@ -100,7 +102,7 @@ fnpinc(Sym *s) if(!debug['f']) diag("fnptr == 0 in fnpinc"); if(s->foreign) - diag("bad usage in fnpinc %s %d %d %d", s->name, s->used, s->foreign, s->thumb); + diag("bad usage in fnpinc %s %d %d", s->name, s->foreign, s->thumb); return 0; } /* 0, 1, 2, 3 squared */ @@ -119,9 +121,9 @@ pad(Prog *p, int pc) q->as = AMOVW; q->line = p->line; q->from.type = D_REG; - q->from.reg = REGSB; + q->from.reg = REGTMP; q->to.type = D_REG; - q->to.reg = REGSB; + q->to.reg = REGTMP; q->pc = pc; q->link = p->link; return q; @@ -132,7 +134,7 @@ scan(Prog *op, Prog *p, int c) { Prog *q; - for(q = op->link; q != p; q = q->link){ + for(q = op->link; q != p && q != P; q = q->link){ q->pc = c; c += oplook(q)->size; nocache(q); @@ -163,11 +165,12 @@ void span(void) { Prog *p, *op; - Sym *setext, *s; Optab *o; - int m, bflag, i; - int32 c, otxt, v; + int m, bflag, i, v; + int32 c, otxt, out[6]; int lastthumb = -1; + Section *sect; + uchar *bp; if(debug['v']) Bprint(&bso, "%5.2f span\n", cputime()); @@ -176,61 +179,65 @@ span(void) bflag = 0; c = INITTEXT; op = nil; + p = nil; otxt = c; - for(p = firstp; p != P; op = p, p = p->link) { + for(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; setarch(p); p->pc = c; - o = oplook(p); - m = o->size; - // must check literal pool here in case p generates many instructions - if(blitrl){ - if(thumb && isbranch(p)) - pool.extra += brextra(p); - if(checkpool(op, p->as == ACASE ? casesz(p) : m)) - c = p->pc = scan(op, p, c); - } - if(m == 0) { - if(p->as == ATEXT) { - if(blitrl && lastthumb != -1 && lastthumb != thumb){ // flush literal pool - if(flushpool(op, 0, 1)) - c = p->pc = scan(op, p, c); - } - lastthumb = thumb; - curtext = p; - autosize = p->to.offset + 4; - if(p->from.sym != S) - p->from.sym->value = c; - /* need passes to resolve branches */ - if(c-otxt >= 1L<<17) - bflag = 1; - otxt = c; - if(thumb && blitrl) + cursym->value = c; + + lastthumb = thumb; + autosize = p->to.offset + 4; + if(p->from.sym != S) + p->from.sym->value = c; + /* need passes to resolve branches */ + if(c-otxt >= 1L<<17) + bflag = 1; + otxt = c; + if(thumb && blitrl) + pool.extra += brextra(p); + + for(op = p, p = p->link; p != P; op = p, p = p->link) { + curp = p; + setarch(p); + p->pc = c; + o = oplook(p); + m = o->size; + // must check literal pool here in case p generates many instructions + if(blitrl){ + if(thumb && isbranch(p)) pool.extra += brextra(p); + if(checkpool(op, p->as == ACASE ? casesz(p) : m)) + c = p->pc = scan(op, p, c); + } + if(m == 0) { + diag("zero-width instruction\n%P", p); continue; } - diag("zero-width instruction\n%P", p); - continue; - } - switch(o->flag & (LFROM|LTO|LPOOL)) { - case LFROM: - addpool(p, &p->from); - break; - case LTO: - addpool(p, &p->to); - break; - case LPOOL: - if ((p->scond&C_SCOND) == 14) + switch(o->flag & (LFROM|LTO|LPOOL)) { + case LFROM: + addpool(p, &p->from); + break; + case LTO: + addpool(p, &p->to); + break; + case LPOOL: + if ((p->scond&C_SCOND) == 14) + flushpool(p, 0, 0); + break; + } + if(p->as==AMOVW && p->to.type==D_REG && p->to.reg==REGPC && (p->scond&C_SCOND) == 14) flushpool(p, 0, 0); - break; + c += m; } - if(p->as==AMOVW && p->to.type==D_REG && p->to.reg==REGPC && (p->scond&C_SCOND) == 14) - flushpool(p, 0, 0); - c += m; - if(blitrl && p->link == P){ - if(thumb && isbranch(p)) - pool.extra += brextra(p); - checkpool(p, 0); + if(blitrl){ + if(thumb && isbranch(op)) + pool.extra += brextra(op); + if(checkpool(op, 0)) + c = scan(op, P, c); } + cursym->size = c - cursym->value; } /* @@ -244,48 +251,52 @@ span(void) Bprint(&bso, "%5.2f span1\n", cputime()); bflag = 0; c = INITTEXT; - for(p = firstp; p != P; p = p->link) { - setarch(p); - p->pc = c; - if(thumb && isbranch(p)) - nocache(p); - o = oplook(p); -/* very larg branches - if(o->type == 6 && p->cond) { - otxt = p->cond->pc - c; - if(otxt < 0) - otxt = -otxt; - if(otxt >= (1L<<17) - 10) { - q = prg(); - q->link = p->link; - p->link = q; - q->as = AB; - q->to.type = D_BRANCH; - q->cond = p->cond; - p->cond = q; - q = prg(); - q->link = p->link; - p->link = q; - q->as = AB; - q->to.type = D_BRANCH; - q->cond = q->link->link; - bflag = 1; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + cursym->value = c; + for(p = cursym->text; p != P; p = p->link) { + curp = p; + setarch(p); + p->pc = c; + if(thumb && isbranch(p)) + nocache(p); + o = oplook(p); +/* very large branches + if(o->type == 6 && p->cond) { + otxt = p->cond->pc - c; + if(otxt < 0) + otxt = -otxt; + if(otxt >= (1L<<17) - 10) { + q = prg(); + q->link = p->link; + p->link = q; + q->as = AB; + q->to.type = D_BRANCH; + q->cond = p->cond; + p->cond = q; + q = prg(); + q->link = p->link; + p->link = q; + q->as = AB; + q->to.type = D_BRANCH; + q->cond = q->link->link; + bflag = 1; + } } - } */ - m = o->size; - if(m == 0) { - if(p->as == ATEXT) { - curtext = p; - autosize = p->to.offset + 4; - if(p->from.sym != S) - p->from.sym->value = c; + m = o->size; + if(m == 0) { + if(p->as == ATEXT) { + autosize = p->to.offset + 4; + if(p->from.sym != S) + p->from.sym->value = c; + continue; + } + diag("zero-width instruction\n%P", p); continue; } - diag("zero-width instruction\n%P", p); - continue; + c += m; } - c += m; + cursym->size = c - cursym->value; } } @@ -304,107 +315,94 @@ span(void) c = INITTEXT; oop = op = nil; again = 0; - for(p = firstp; p != P; oop = op, op = p, p = p->link){ - setarch(p); - if(p->pc != c) - again = 1; - p->pc = c; - if(thumb && isbranch(p)) - nocache(p); - o = oplook(p); - m = o->size; - if(passes == 1 && thumb && isbranch(p)){ // start conservative so unneeded alignment is not added - if(p->as == ABL) - m = 4; - else - m = 2; - p->align = 0; - } - if(p->align){ - if((p->align == 4 && (c&3)) || (p->align == 2 && !(c&3))){ - if(ispad(op)){ - oop->link = p; - op = oop; - c -= 2; - p->pc = c; - } - else{ - op->link = pad(op, c); - op = op->link; - c += 2; - p->pc = c; - } + for(cursym = textp; cursym != nil; cursym = cursym->next) { + cursym->value = c; + for(p = cursym->text; p != P; oop = op, op = p, p = p->link) { + curp = p; + setarch(p); + if(p->pc != c) again = 1; + p->pc = c; + if(thumb && isbranch(p)) + nocache(p); + o = oplook(p); + m = o->size; + if(passes == 1 && thumb && isbranch(p)){ // start conservative so unneeded alignment is not added + if(p->as == ABL) + m = 4; + else + m = 2; + p->align = 0; } - } - if(m == 0) { - if(p->as == ATEXT) { - curtext = p; - autosize = p->to.offset + 4; - if(p->from.sym != S) - p->from.sym->value = c; - continue; + if(p->align){ + if((p->align == 4 && (c&3)) || (p->align == 2 && !(c&3))){ + if(ispad(op)){ + oop->link = p; + op = oop; + c -= 2; + p->pc = c; + } + else{ + op->link = pad(op, c); + op = op->link; + c += 2; + p->pc = c; + } + again = 1; + } + } + if(m == 0) { + if(p->as == ATEXT) { + autosize = p->to.offset + 4; + if(p->from.sym != S) + p->from.sym->value = c; + continue; + } } + c += m; } - c += m; + cursym->size = c - cursym->value; } if(c != lastc || again){ lastc = c; goto loop; } } - - if(0 && seenthumb){ // rm redundant padding - obsolete - int d; - - op = nil; - d = 0; - for(p = firstp; p != P; op = p, p = p->link){ - p->pc -= d; - if(p->as == ATEXT){ - if(p->from.sym != S) - p->from.sym->value -= d; -// if(p->from.sym != S) print("%s %ux %d %d %d\n", p->from.sym->name ? p->from.sym->name : "?", p->from.sym->value, p->from.sym->thumb, p->from.sym->foreign, p->from.sym->fnptr); - } - if(ispad(p) && p->link != P && ispad(p->link)){ - op->link = p->link->link; - d += 4; - p = op; + c = rnd(c, 8); + + /* + * lay out the code. all the pc-relative code references, + * even cross-function, are resolved now; + * only data references need to be relocated. + * with more work we could leave cross-function + * code references to be relocated too, and then + * perhaps we'd be able to parallelize the span loop above. + */ + for(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; + setarch(p); + autosize = p->to.offset + 4; + symgrow(cursym, cursym->size); + + bp = cursym->p; + for(p = p->link; p != P; p = p->link) { + curp = p; + pc = p->pc; + curp = p; + o = oplook(p); + asmout(p, o, out); + for(i=0; i<o->size/4; i++) { + v = out[i]; + *bp++ = v; + *bp++ = v>>8; + *bp++ = v>>16; + *bp++ = v>>24; } } - // print("%d bytes removed (padding)\n", d); - c -= d; } - - if(debug['t']) { - /* - * add strings to text segment - */ - c = rnd(c, 8); - for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) { - if(s->type != SSTRING) - continue; - v = s->value; - while(v & 3) - v++; - s->value = c; - c += v; - } - } - - c = rnd(c, 8); - - setext = lookup("etext", 0); - if(setext != S) { - setext->value = c; - textsize = c - INITTEXT; - } - if(INITRND) - INITDAT = rnd(c, INITRND); - if(debug['v']) - Bprint(&bso, "tsize = %lux\n", textsize); - Bflush(&bso); + sect = addsection(&segtext, ".text", 05); + sect->vaddr = INITTEXT; + sect->len = c - INITTEXT; } /* @@ -437,7 +435,7 @@ flushpool(Prog *p, int skip, int force) if(blitrl) { if(skip){ - if(0 && skip==1)print("note: flush literal pool at %lux: len=%lud ref=%lux\n", p->pc+4, pool.size, pool.start); + if(0 && skip==1)print("note: flush literal pool at %ux: len=%ud ref=%ux\n", p->pc+4, pool.size, pool.start); q = prg(); q->as = AB; q->to.type = D_BRANCH; @@ -523,10 +521,10 @@ xdefine(char *p, int t, int32 v) Sym *s; s = lookup(p, 0); - if(s->type == 0 || s->type == SXREF) { - s->type = t; - s->value = v; - } + s->type = t; + s->value = v; + s->reachable = 1; + s->special = 1; } int32 @@ -583,6 +581,38 @@ immhalf(int32 v) return 0; } +int32 +symaddr(Sym *s) +{ + int32 v; + + v = s->value; + switch(s->type) { + default: + diag("unexpected type %d in symaddr(%s)", s->type, s->name); + return 0; + + case STEXT: +/* TODO(rsc): what is this for? +#ifdef CALLEEBX + v += fnpinc(s); +#else + if(s->thumb) + v++; // T bit +#endif +*/ + break; + + case SELFDATA: + case SRODATA: + case SDATA: + case SBSS: + case SCONST: + break; + } + return v; +} + int aclass(Adr *a) { @@ -619,36 +649,9 @@ aclass(Adr *a) } s = a->sym; t = s->type; - if(t == 0 || t == SXREF) { - diag("undefined external: %s in %s", - s->name, TNAME); - s->type = SDATA; - } - if(dlm) { - switch(t) { - default: - instoffset = s->value + a->offset + INITDAT; - break; - case SUNDEF: - case STEXT: - case SCONST: - case SLEAF: - case SSTRING: - instoffset = s->value + a->offset; - break; - } - return C_ADDR; - } - instoffset = s->value + a->offset - BIG; - t = immaddr(instoffset); - if(t) { - if(immhalf(instoffset)) - return immfloat(t) ? C_HFEXT : C_HEXT; - if(immfloat(t)) - return C_FEXT; - return C_SEXT; - } - return C_LEXT; + instoffset = 0; // s.b. unused but just in case + return C_ADDR; + case D_AUTO: instoffset = autosize + a->offset; t = immaddr(instoffset); @@ -703,32 +706,17 @@ aclass(Adr *a) case D_STATIC: s = a->sym; t = s->type; - if(t == 0 || t == SXREF) { - diag("undefined external: %s in %s", - s->name, TNAME); - s->type = SDATA; - } - if(s->type == SFIXED) { - instoffset = s->value + a->offset; - return C_LCON; - } - instoffset = s->value + a->offset + INITDAT; - if(s->type == STEXT || s->type == SLEAF || s->type == SUNDEF) { - instoffset = s->value + a->offset; -#ifdef CALLEEBX - instoffset += fnpinc(s); -#else - if(s->thumb) - instoffset++; // T bit -#endif - return C_LCON; - } - return C_LCON; + instoffset = 0; // s.b. unused but just in case + return C_ADDR; } return C_GOK; case D_FCONST: - return C_FCON; + if(chipzero(&a->ieee) >= 0) + return C_ZFCON; + if(chipfloat(&a->ieee) >= 0) + return C_SFCON; + return C_LFCON; case D_CONST: case D_CONST2: @@ -753,37 +741,7 @@ aclass(Adr *a) if(s == S) break; t = s->type; - switch(t) { - case 0: - case SXREF: - diag("undefined external: %s in %s", - s->name, TNAME); - s->type = SDATA; - break; - case SFIXED: - instoffset = s->value + a->offset; - return C_LCON; - case SUNDEF: - case STEXT: - case SSTRING: - case SCONST: - case SLEAF: - instoffset = s->value + a->offset; -#ifdef CALLEEBX - instoffset += fnpinc(s); -#else - if(s->thumb) - instoffset++; // T bit -#endif - return C_LCON; - } - if(!dlm) { - instoffset = s->value + a->offset - BIG; - t = immrot(instoffset); - if(t && instoffset != 0) - return C_RECON; - } - instoffset = s->value + a->offset + INITDAT; + instoffset = 0; // s.b. unused but just in case return C_LCON; case D_AUTO: @@ -895,21 +853,11 @@ cmp(int a, int b) if(b == C_RACON) return 1; break; - case C_LECON: - if(b == C_RECON) + case C_LFCON: + if(b == C_ZFCON || b == C_SFCON) return 1; break; - case C_HFEXT: - return b == C_HEXT || b == C_FEXT; - case C_FEXT: - case C_HEXT: - return b == C_HFEXT; - case C_SEXT: - return cmp(C_HFEXT, b); - case C_LEXT: - return cmp(C_SEXT, b); - case C_HFAUTO: return b == C_HAUTO || b == C_FAUTO; case C_FAUTO: @@ -1091,17 +1039,20 @@ buildop(void) break; case AMOVFW: - oprange[AMOVWF] = oprange[r]; - oprange[AMOVWD] = oprange[r]; oprange[AMOVDW] = oprange[r]; break; + case AMOVWF: + oprange[AMOVWD] = oprange[r]; + break; + case AMULL: oprange[AMULA] = oprange[r]; oprange[AMULAL] = oprange[r]; oprange[AMULLU] = oprange[r]; oprange[AMULALU] = oprange[r]; break; + case ALDREX: case ASTREX: break; @@ -1146,159 +1097,3 @@ buildrep(int x, int as) oprange[as].start = 0; } */ - -enum{ - ABSD = 0, - ABSU = 1, - RELD = 2, - RELU = 3, -}; - -int modemap[4] = { 0, 1, -1, 2, }; - -typedef struct Reloc Reloc; - -struct Reloc -{ - int n; - int t; - uchar *m; - uint32 *a; -}; - -Reloc rels; - -static void -grow(Reloc *r) -{ - int t; - uchar *m, *nm; - uint32 *a, *na; - - t = r->t; - r->t += 64; - m = r->m; - a = r->a; - r->m = nm = malloc(r->t*sizeof(uchar)); - r->a = na = malloc(r->t*sizeof(uint32)); - memmove(nm, m, t*sizeof(uchar)); - memmove(na, a, t*sizeof(uint32)); - free(m); - free(a); -} - -void -dynreloc(Sym *s, int32 v, int abs) -{ - int i, k, n; - uchar *m; - uint32 *a; - Reloc *r; - - if(v&3) - diag("bad relocation address"); - v >>= 2; - if(s != S && s->type == SUNDEF) - k = abs ? ABSU : RELU; - else - k = abs ? ABSD : RELD; - /* Bprint(&bso, "R %s a=%ld(%lx) %d\n", s->name, a, a, k); */ - k = modemap[k]; - r = &rels; - n = r->n; - if(n >= r->t) - grow(r); - m = r->m; - a = r->a; - for(i = n; i > 0; i--){ - if(v < a[i-1]){ /* happens occasionally for data */ - m[i] = m[i-1]; - a[i] = a[i-1]; - } - else - break; - } - m[i] = k; - a[i] = v; - r->n++; -} - -static int -sput(char *s) -{ - char *p; - - p = s; - while(*s) - cput(*s++); - cput(0); - return s-p+1; -} - -void -asmdyn() -{ - int i, n, t, c; - Sym *s; - uint32 la, ra, *a; - vlong off; - uchar *m; - Reloc *r; - - cflush(); - off = seek(cout, 0, 1); - lput(0); - t = 0; - lput(imports); - t += 4; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->type == SUNDEF){ - lput(s->sig); - t += 4; - t += sput(s->name); - } - - la = 0; - r = &rels; - n = r->n; - m = r->m; - a = r->a; - lput(n); - t += 4; - for(i = 0; i < n; i++){ - ra = *a-la; - if(*a < la) - diag("bad relocation order"); - if(ra < 256) - c = 0; - else if(ra < 65536) - c = 1; - else - c = 2; - cput((c<<6)|*m++); - t++; - if(c == 0){ - cput(ra); - t++; - } - else if(c == 1){ - wput(ra); - t += 2; - } - else{ - lput(ra); - t += 4; - } - la = *a++; - } - - cflush(); - seek(cout, off, 0); - lput(t); - - if(debug['v']){ - Bprint(&bso, "import table entries = %d\n", imports); - Bprint(&bso, "export table entries = %d\n", exports); - } -} diff --git a/src/cmd/5l/thumb.c b/src/cmd/5l/thumb.c index 97292f640..b2ba630c3 100644 --- a/src/cmd/5l/thumb.c +++ b/src/cmd/5l/thumb.c @@ -203,9 +203,8 @@ thumbaclass(Adr *a, Prog *p) a->sym->name, TNAME); a->sym->type = SDATA; } - instoffset = a->sym->value + a->offset + INITDAT; - return C_LEXT; /* INITDAT unknown at this stage */ - // return immacon(instoffset, p, C_SEXT, C_LEXT); + instoffset = a->sym->value + a->offset; + return C_ADDR; /* INITDAT unknown at this stage */ case D_AUTO: instoffset = autosize + a->offset; return immauto(instoffset, p); @@ -235,8 +234,8 @@ thumbaclass(Adr *a, Prog *p) s->name, TNAME); s->type = SDATA; } - instoffset = s->value + a->offset + INITDAT; - if(s->type == STEXT || s->type == SLEAF){ + instoffset = s->value + a->offset; + if(s->type == STEXT){ instoffset = s->value + a->offset; #ifdef CALLEEBX instoffset += fnpinc(s); @@ -252,7 +251,7 @@ thumbaclass(Adr *a, Prog *p) return C_GOK; case D_FCONST: diag("D_FCONST in thumaclass"); - return C_FCON; + return C_LFCON; case D_CONST: switch(a->name) { case D_NONE: @@ -275,7 +274,6 @@ thumbaclass(Adr *a, Prog *p) break; case SCONST: case STEXT: - case SLEAF: instoffset = s->value + a->offset; #ifdef CALLEEBX instoffset += fnpinc(s); @@ -285,7 +283,7 @@ thumbaclass(Adr *a, Prog *p) #endif return C_LCON; } - instoffset = s->value + a->offset + INITDAT; + instoffset = s->value + a->offset; return C_LCON; /* INITDAT unknown at this stage */ // return immcon(instoffset, p); case D_AUTO: @@ -358,8 +356,8 @@ thumbaclass(Adr *a, Prog *p) // as a1 a2 a3 type size param lit vers Optab thumboptab[] = { - { ATEXT, C_LEXT, C_NONE, C_LCON, 0, 0, 0 }, - { ATEXT, C_LEXT, C_REG, C_LCON, 0, 0, 0 }, + { ATEXT, C_ADDR, C_NONE, C_LCON, 0, 0, 0 }, + { ATEXT, C_ADDR, C_REG, C_LCON, 0, 0, 0 }, { AMVN, C_REG, C_NONE, C_REG, 1, 2, 0 }, { ASRL, C_REG, C_NONE, C_REG, 1, 2, 0 }, { ACMP, C_REG, C_REG, C_NONE, 1, 2, 0 }, @@ -412,37 +410,27 @@ Optab thumboptab[] = { ASWI, C_NONE, C_NONE, C_LCON, 16, 2, 0 }, { AWORD, C_NONE, C_NONE, C_LCON, 17, 4, 0 }, { AWORD, C_NONE, C_NONE, C_GCON, 17, 4, 0 }, - { AWORD, C_NONE, C_NONE, C_LEXT, 17, 4, 0 }, + { AWORD, C_NONE, C_NONE, C_ADDR, 17, 4, 0 }, { ADWORD, C_LCON, C_NONE, C_LCON, 50, 8, 0 }, { AMOVW, C_SAUTO, C_NONE, C_REG, 18, 2, REGSP }, { AMOVW, C_LAUTO, C_NONE, C_REG, 33, 6, 0, LFROM }, // { AMOVW, C_OFFPC, C_NONE, C_REG, 18, 2, REGPC, LFROM }, - { AMOVW, C_SEXT, C_NONE, C_REG, 30, 4, 0 }, { AMOVW, C_SOREG, C_NONE, C_REG, 19, 2, 0 }, - { AMOVHU, C_SEXT, C_NONE, C_REG, 30, 4, 0 }, { AMOVHU, C_SOREG, C_NONE, C_REG, 19, 2, 0 }, - { AMOVBU, C_SEXT, C_NONE, C_REG, 30, 4, 0 }, { AMOVBU, C_SOREG, C_NONE, C_REG, 19, 2, 0 }, { AMOVW, C_REG, C_NONE, C_SAUTO, 20, 2, 0 }, { AMOVW, C_REG, C_NONE, C_LAUTO, 34, 6, 0, LTO }, - { AMOVW, C_REG, C_NONE, C_SEXT, 31, 4, 0 }, { AMOVW, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVH, C_REG, C_NONE, C_SEXT, 31, 4, 0 }, { AMOVH, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVB, C_REG, C_NONE, C_SEXT, 31, 4, 0 }, { AMOVB, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVHU, C_REG, C_NONE, C_SEXT, 31, 4, 0 }, { AMOVHU, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVBU, C_REG, C_NONE, C_SEXT, 31, 4, 0 }, { AMOVBU, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, { AMOVW, C_REG, C_NONE, C_REG, 22, 2, 0 }, { AMOVB, C_REG, C_NONE, C_REG, 23, 4, 0 }, { AMOVH, C_REG, C_NONE, C_REG, 23, 4, 0 }, { AMOVBU, C_REG, C_NONE, C_REG, 23, 4, 0 }, { AMOVHU, C_REG, C_NONE, C_REG, 23, 4, 0 }, - { AMOVH, C_SEXT, C_NONE, C_REG, 32, 6, 0 }, { AMOVH, C_SOREG, C_NONE, C_REG, 24, 4, 0 }, - { AMOVB, C_SEXT, C_NONE, C_REG, 32, 6, 0 }, { AMOVB, C_SOREG, C_NONE, C_REG, 24, 4, 0 }, { AMOVW, C_SACON, C_NONE, C_REG, 25, 2, 0 }, { AMOVW, C_LACON, C_NONE, C_REG, 35, 4, 0 }, @@ -469,16 +457,16 @@ Optab thumboptab[] = { AMOVB, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, { AMOVHU, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, { AMOVBU, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, - { AMOVW, C_LEXT, C_NONE, C_REG, 30, 4, 0, LFROM }, - { AMOVH, C_LEXT, C_NONE, C_REG, 32, 6, 0, LFROM }, - { AMOVB, C_LEXT, C_NONE, C_REG, 32, 6, 0, LFROM }, - { AMOVHU, C_LEXT, C_NONE, C_REG, 30, 4, 0, LFROM }, - { AMOVBU, C_LEXT, C_NONE, C_REG, 30, 4, 0, LFROM }, - { AMOVW, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO }, - { AMOVH, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO }, - { AMOVB, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO }, - { AMOVHU, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO }, - { AMOVBU, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO }, + { AMOVW, C_ADDR, C_NONE, C_REG, 30, 4, 0, LFROM }, + { AMOVH, C_ADDR, C_NONE, C_REG, 32, 6, 0, LFROM }, + { AMOVB, C_ADDR, C_NONE, C_REG, 32, 6, 0, LFROM }, + { AMOVHU, C_ADDR, C_NONE, C_REG, 30, 4, 0, LFROM }, + { AMOVBU, C_ADDR, C_NONE, C_REG, 30, 4, 0, LFROM }, + { AMOVW, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, + { AMOVH, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, + { AMOVB, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, + { AMOVHU, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, + { AMOVBU, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, { AXXX, C_NONE, C_NONE, C_NONE, 0, 2, 0 }, }; @@ -683,7 +671,7 @@ thumbasmout(Prog *p, Optab *o) rt = p->to.reg; r = p->reg; o1 = o2 = o3 = o4 = o5 = o6 = o7 = 0; -if(debug['P']) print("%ulx: %P type %d %d\n", (uint32)(p->pc), p, o->type, p->align); +if(debug['P']) print("%ux: %P type %d %d\n", (uint32)(p->pc), p, o->type, p->align); opcount[o->type] += o->size; switch(o->type) { default: @@ -691,7 +679,7 @@ if(debug['P']) print("%ulx: %P type %d %d\n", (uint32)(p->pc), p, o->type, p->al prasm(p); break; case 0: /* pseudo ops */ -if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); +if(debug['G']) print("%ux: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); break; case 1: /* op R, -, R or op R, R, - */ o1 = thumboprr(p->as); @@ -981,6 +969,7 @@ if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); } break; case 30: /* AMOVW... *addr, R */ + diag("likely broken"); // does this still refer to SB? thumbaclass(&p->from, p); o1 = mv(p, rt, instoffset); // MOV addr, rtmp o2 = thumbopmv(p->as, 1); @@ -988,6 +977,7 @@ if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); o2 |= (rt<<3) | rt; // MOV* 0(rtmp), R break; case 31: /* AMOVW... R, *addr */ + diag("likely broken"); // does this still refer to SB? thumbaclass(&p->to, p); o1 = mv(p, REGTMPT, instoffset); o2 = thumbopmv(p->as, 0); @@ -1162,29 +1152,29 @@ if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); switch(o->size) { default: if(debug['a']) - Bprint(&bso, " %.8lux:\t\t%P\n", v, p); + Bprint(&bso, " %.8ux:\t\t%P\n", v, p); break; case 2: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p); + Bprint(&bso, " %.8ux: %.8ux\t%P\n", v, o1, p); hputl(o1); break; case 4: if(debug['a']) - Bprint(&bso, " %.8lux: %.8lux %.8lux\t%P\n", v, o1, o2, p); + Bprint(&bso, " %.8ux: %.8ux %.8ux\t%P\n", v, o1, o2, p); hputl(o1); hputl(o2); break; case 6: if(debug['a']) - Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, p); + Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, p); hputl(o1); hputl(o2); hputl(o3); break; case 8: if(debug['a']) - Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, p); + Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, p); hputl(o1); hputl(o2); hputl(o3); @@ -1192,7 +1182,7 @@ if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); break; case 10: if(debug['a']) - Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, o5, p); + Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, o5, p); hputl(o1); hputl(o2); hputl(o3); @@ -1201,7 +1191,7 @@ if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); break; case 12: if(debug['a']) - Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, o5, o6, p); + Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, o5, o6, p); hputl(o1); hputl(o2); hputl(o3); @@ -1211,7 +1201,7 @@ if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); break; case 14: if(debug['a']) - Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, o5, o6, o7, p); + Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, o5, o6, o7, p); hputl(o1); hputl(o2); hputl(o3); @@ -1223,12 +1213,12 @@ if(debug['G']) print("%ulx: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); } if(debug['G']){ if(o->type == 17){ - print("%lx: word %ld\n", p->pc, (o2<<16)+o1); + print("%x: word %d\n", p->pc, (o2<<16)+o1); return; } if(o->type == 50){ - print("%lx: word %ld\n", p->pc, (o2<<16)+o1); - print("%lx: word %ld\n", p->pc, (o4<<16)+o3); + print("%x: word %d\n", p->pc, (o2<<16)+o1); + print("%x: word %d\n", p->pc, (o4<<16)+o3); return; } if(o->size > 0) dis(o1, p->pc); diff --git a/src/cmd/6a/Makefile b/src/cmd/6a/Makefile index 21d824708..30180bd24 100644 --- a/src/cmd/6a/Makefile +++ b/src/cmd/6a/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 6a\ +TARG=6a HFILES=\ a.h\ @@ -20,21 +20,6 @@ OFILES=\ YFILES=\ a.y\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lbio -l9 - -$(OFILES): $(HFILES) +include ../../Make.ccmd lex.$O: ../cc/macbody ../cc/lexbody - -y.tab.h: $(YFILES) - bison -y $(YFLAGS) $(YFILES) - -y.tab.c: y.tab.h - test -f y.tab.c && touch y.tab.c - -clean: - rm -f *.$O $(TARG) *.6 enam.c 6.out a.out y.tab.h y.tab.c - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) diff --git a/src/cmd/6a/a.h b/src/cmd/6a/a.h index a713acc9f..9030081ca 100644 --- a/src/cmd/6a/a.h +++ b/src/cmd/6a/a.h @@ -1,7 +1,7 @@ // Inferno utils/6a/a.h // http://code.google.com/p/inferno-os/source/browse/utils/6a/a.h // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -50,7 +50,7 @@ typedef struct Ref Ref; typedef struct Gen Gen; typedef struct Io Io; typedef struct Hist Hist; -typedef struct Gen2 Gen2; +typedef struct Gen2 Gen2; #define MAXALIGN 7 #define FPCHIP 1 @@ -161,6 +161,7 @@ EXTERN int pass; EXTERN char* pathname; EXTERN int32 pc; EXTERN int peekc; +EXTERN int32 stmtline; EXTERN int sym; EXTERN char* symb; EXTERN int thechar; @@ -168,9 +169,9 @@ EXTERN char* thestring; EXTERN int32 thunk; EXTERN Biobuf obuf; -void* alloc(int32); +void* alloc(int32); void* allocn(void*, int32, int32); -void ensuresymb(int32); +void ensuresymb(int32); void errorexit(void); void pushio(void); void newio(void); diff --git a/src/cmd/6a/a.y b/src/cmd/6a/a.y index 804f638a0..770f676fe 100644 --- a/src/cmd/6a/a.y +++ b/src/cmd/6a/a.y @@ -1,7 +1,7 @@ // Inferno utils/6a/a.y // http://code.google.com/p/inferno-os/source/browse/utils/6a/a.y // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -60,7 +60,11 @@ %type <gen2> spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 spec9 %% prog: -| prog line +| prog + { + stmtline = lineno; + } + line line: LLAB ':' @@ -453,6 +457,12 @@ omem: $$.type = D_INDIR+D_SP; $$.offset = $1; } +| con '(' LSREG ')' + { + $$ = nullgen; + $$.type = D_INDIR+$3; + $$.offset = $1; + } | con '(' LLREG '*' con ')' { $$ = nullgen; diff --git a/src/cmd/6a/lex.c b/src/cmd/6a/lex.c index 81273b297..1b8bb6344 100644 --- a/src/cmd/6a/lex.c +++ b/src/cmd/6a/lex.c @@ -1,7 +1,7 @@ // Inferno utils/6a/lex.c // http://code.google.com/p/inferno-os/source/browse/utils/6a/lex.c // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -1260,10 +1260,10 @@ jackpot: } Bputc(&obuf, a); Bputc(&obuf, a>>8); - Bputc(&obuf, lineno); - Bputc(&obuf, lineno>>8); - Bputc(&obuf, lineno>>16); - Bputc(&obuf, lineno>>24); + Bputc(&obuf, stmtline); + Bputc(&obuf, stmtline>>8); + Bputc(&obuf, stmtline>>16); + Bputc(&obuf, stmtline>>24); zaddr(&g2->from, sf); zaddr(&g2->to, st); diff --git a/src/cmd/6c/Makefile b/src/cmd/6c/Makefile index 53a8e80e6..484e16def 100644 --- a/src/cmd/6c/Makefile +++ b/src/cmd/6c/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 6c\ +TARG=6c HFILES=\ gc.h\ @@ -28,18 +28,9 @@ OFILES=\ ../6l/enam.$O\ LIB=\ - ../cc/cc.a$O + ../cc/cc.a\ -$(TARG): $(OFILES) $(LIB) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lbio -l9 - -$(OFILES): $(HFILES) - -clean: - rm -f *.$O $(TARG) *.6 enam.c 6.out a.out - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +include ../../Make.ccmd %.$O: ../cc/%.c - $(CC) $(CFLAGS) -c -I. -o $@ ../cc/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../cc/$*.c diff --git a/src/cmd/6c/cgen.c b/src/cmd/6c/cgen.c index 39452c989..90394884f 100644 --- a/src/cmd/6c/cgen.c +++ b/src/cmd/6c/cgen.c @@ -57,6 +57,12 @@ cgen(Node *n, Node *nn) l = n->left; r = n->right; o = n->op; + + if(n->op == OEXREG || (nn != Z && nn->op == OEXREG)) { + gmove(n, nn); + return; + } + if(n->addable >= INDEXED) { if(nn == Z) { switch(o) { @@ -1922,7 +1928,7 @@ vaddr(Node *n, int a) int32 hi64v(Node *n) { - if(align(0, types[TCHAR], Aarg1)) /* isbigendian */ + if(align(0, types[TCHAR], Aarg1, nil)) /* isbigendian */ return (int32)(n->vconst) & ~0L; else return (int32)((uvlong)n->vconst>>32) & ~0L; @@ -1931,7 +1937,7 @@ hi64v(Node *n) int32 lo64v(Node *n) { - if(align(0, types[TCHAR], Aarg1)) /* isbigendian */ + if(align(0, types[TCHAR], Aarg1, nil)) /* isbigendian */ return (int32)((uvlong)n->vconst>>32) & ~0L; else return (int32)(n->vconst) & ~0L; diff --git a/src/cmd/6c/div.c b/src/cmd/6c/div.c index d67ef8df7..bad6c5e27 100644 --- a/src/cmd/6c/div.c +++ b/src/cmd/6c/div.c @@ -120,7 +120,7 @@ sdivgen(Node *l, Node *r, Node *ax, Node *dx) if(c < 0) c = -c; a = sdiv(c, &m, &s); -//print("a=%d i=%ld s=%d m=%lux\n", a, (long)r->vconst, s, m); +//print("a=%d i=%d s=%d m=%ux\n", a, (long)r->vconst, s, m); gins(AMOVL, nodconst(m), ax); gins(AIMULL, l, Z); gins(AMOVL, l, ax); @@ -141,7 +141,7 @@ udivgen(Node *l, Node *r, Node *ax, Node *dx) Node nod; a = udiv(r->vconst, &m, &s, &t); -//print("a=%ud i=%ld p=%d s=%d m=%lux\n", a, (long)r->vconst, t, s, m); +//print("a=%ud i=%d p=%d s=%d m=%ux\n", a, (long)r->vconst, t, s, m); if(t != 0) { gins(AMOVL, l, ax); gins(ASHRL, nodconst(t), ax); diff --git a/src/cmd/6c/list.c b/src/cmd/6c/list.c index ba517ca0a..4293203c0 100644 --- a/src/cmd/6c/list.c +++ b/src/cmd/6c/list.c @@ -78,22 +78,23 @@ Pconv(Fmt *fp) p = va_arg(fp->args, Prog*); switch(p->as) { case ADATA: - sprint(str, " %A %D/%d,%D", - p->as, &p->from, p->from.scale, &p->to); + sprint(str, "(%L) %A %D/%d,%D", + p->lineno, p->as, &p->from, p->from.scale, &p->to); break; case ATEXT: if(p->from.scale) { - sprint(str, " %A %D,%d,%lD", - p->as, &p->from, p->from.scale, &p->to); + sprint(str, "(%L) %A %D,%d,%lD", + p->lineno, p->as, &p->from, p->from.scale, &p->to); break; } - sprint(str, " %A %D,%lD", - p->as, &p->from, &p->to); + sprint(str, "(%L) %A %D,%lD", + p->lineno, p->as, &p->from, &p->to); break; default: - sprint(str, " %A %D,%lD", p->as, &p->from, &p->to); + sprint(str, "(%L) %A %D,%lD", + p->lineno, p->as, &p->from, &p->to); break; } return fmtstrcpy(fp, str); diff --git a/src/cmd/6c/mul.c b/src/cmd/6c/mul.c index eefeacaed..ab6883e7a 100644 --- a/src/cmd/6c/mul.c +++ b/src/cmd/6c/mul.c @@ -355,7 +355,7 @@ mulgen1(uint32 v, Node *n) mulparam(v, p); found: -// print("v=%.lx a=%d n=%d s=%d g=%d o=%d \n", p->value, p->alg, p->neg, p->shift, p->arg, p->off); +// print("v=%.x a=%d n=%d s=%d g=%d o=%d \n", p->value, p->alg, p->neg, p->shift, p->arg, p->off); if(p->alg < 0) return 0; diff --git a/src/cmd/6c/peep.c b/src/cmd/6c/peep.c index 01793bfc5..13fd25e73 100644 --- a/src/cmd/6c/peep.c +++ b/src/cmd/6c/peep.c @@ -797,8 +797,6 @@ copyu(Prog *p, Adr *v, Adr *s) return 3; case ACALL: /* funny */ - if(REGEXT && v->type <= REGEXT && v->type > exregoffset) - return 2; if(REGARG >= 0 && v->type == REGARG) return 2; diff --git a/src/cmd/6c/reg.c b/src/cmd/6c/reg.c index 431501202..996128f75 100644 --- a/src/cmd/6c/reg.c +++ b/src/cmd/6c/reg.c @@ -448,7 +448,7 @@ regopt(Prog *p) if(debug['R'] && debug['v']) { print("\nlooping structure:\n"); for(r = firstr; r != R; r = r->link) { - print("%ld:%P", r->loop, r->prog); + print("%d:%P", r->loop, r->prog); for(z=0; z<BITS; z++) bit.b[z] = r->use1.b[z] | r->use2.b[z] | @@ -1113,7 +1113,7 @@ paint1(Reg *r, int bn) if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) { change -= CLOAD * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tld %B $%d\n", r->loop, + print("%d%P\td %B $%d\n", r->loop, r->prog, blsh(bn), change); } for(;;) { @@ -1123,21 +1123,21 @@ paint1(Reg *r, int bn) if(r->use1.b[z] & bb) { change += CREF * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tu1 %B $%d\n", r->loop, + print("%d%P\tu1 %B $%d\n", r->loop, p, blsh(bn), change); } if((r->use2.b[z]|r->set.b[z]) & bb) { change += CREF * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tu2 %B $%d\n", r->loop, + print("%d%P\tu2 %B $%d\n", r->loop, p, blsh(bn), change); } if(STORE(r) & r->regdiff.b[z] & bb) { change -= CLOAD * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tst %B $%d\n", r->loop, + print("%d%P\tst %B $%d\n", r->loop, p, blsh(bn), change); } @@ -1174,7 +1174,7 @@ regset(Reg *r, uint32 bb) while(b = bb & ~(bb-1)) { v.type = b & 0xFFFF? BtoR(b): BtoF(b); if(v.type == 0) - diag(Z, "zero v.type for %#lux", b); + diag(Z, "zero v.type for %#ux", b); c = copyu(r->prog, &v, A); if(c == 3) set |= b; diff --git a/src/cmd/6c/sgen.c b/src/cmd/6c/sgen.c index b8247a1b7..42045f8fa 100644 --- a/src/cmd/6c/sgen.c +++ b/src/cmd/6c/sgen.c @@ -131,6 +131,10 @@ xcom(Node *n) n->addable = 11; break; + case OEXREG: + n->addable = 0; + break; + case OREGISTER: n->addable = 12; break; diff --git a/src/cmd/6c/swt.c b/src/cmd/6c/swt.c index 668a1fdbc..47975a0c8 100644 --- a/src/cmd/6c/swt.c +++ b/src/cmd/6c/swt.c @@ -503,7 +503,7 @@ zaddr(Biobuf *b, Adr *a, int s) } int32 -align(int32 i, Type *t, int op) +align(int32 i, Type *t, int op, int32 *maxalign) { int32 o; Type *v; @@ -517,7 +517,9 @@ align(int32 i, Type *t, int op) break; case Asu2: /* padding at end of a struct */ - w = SZ_VLONG; + w = *maxalign; + if(w < 1) + w = 1; if(packflg) w = packflg; break; @@ -525,10 +527,13 @@ align(int32 i, Type *t, int op) case Ael1: /* initial align of struct element */ for(v=t; v->etype==TARRAY; v=v->link) ; - w = ewidth[v->etype]; - if(w <= 0 || w >= SZ_VLONG) - w = SZ_VLONG; - if(packflg) + if(v->etype == TSTRUCT || v->etype == TUNION) + w = v->align; + else + w = ewidth[v->etype]; + if(w < 1 || w > SZ_VLONG) + fatal(Z, "align"); + if(packflg) w = packflg; break; @@ -538,8 +543,8 @@ align(int32 i, Type *t, int op) case Aarg0: /* initial passbyptr argument in arg list */ if(typesu[t->etype]) { - o = align(o, types[TIND], Aarg1); - o = align(o, types[TIND], Aarg2); + o = align(o, types[TIND], Aarg1, nil); + o = align(o, types[TIND], Aarg2, nil); } break; @@ -560,13 +565,15 @@ align(int32 i, Type *t, int op) break; case Aaut3: /* total align of automatic */ - o = align(o, t, Ael1); - o = align(o, t, Ael2); + o = align(o, t, Ael1, nil); + o = align(o, t, Ael2, nil); break; } o = xround(o, w); + if(maxalign && *maxalign < w) + *maxalign = w; if(debug['A']) - print("align %s %ld %T = %ld\n", bnames[op], i, t, o); + print("align %s %d %T = %d\n", bnames[op], i, t, o); return o; } diff --git a/src/cmd/6c/txt.c b/src/cmd/6c/txt.c index f96c40f8e..a78ba227b 100644 --- a/src/cmd/6c/txt.c +++ b/src/cmd/6c/txt.c @@ -38,8 +38,7 @@ ginit(void) thechar = '6'; thestring = "amd64"; - exregoffset = REGEXT; - exfregoffset = FREGEXT; + dodefine("_64BIT"); listinit(); nstring = 0; mnstring = 0; @@ -425,7 +424,7 @@ err: void regsalloc(Node *n, Node *nn) { - cursafe = align(cursafe, nn->type, Aaut3); + cursafe = align(cursafe, nn->type, Aaut3, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); *n = *nodsafe; n->xoffset = -(stkoff + cursafe); @@ -441,22 +440,22 @@ regaalloc1(Node *n, Node *nn) diag(n, "regaalloc1 and REGARG<0"); nodreg(n, nn, REGARG); reg[REGARG]++; - curarg = align(curarg, nn->type, Aarg1); - curarg = align(curarg, nn->type, Aarg2); + curarg = align(curarg, nn->type, Aarg1, nil); + curarg = align(curarg, nn->type, Aarg2, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); } void regaalloc(Node *n, Node *nn) { - curarg = align(curarg, nn->type, Aarg1); + curarg = align(curarg, nn->type, Aarg1, nil); *n = *nn; n->op = OINDREG; n->reg = REGSP; n->xoffset = curarg; n->complex = 0; n->addable = 20; - curarg = align(curarg, nn->type, Aarg2); + curarg = align(curarg, nn->type, Aarg2, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); } @@ -491,6 +490,10 @@ naddr(Node *n, Adr *a) a->sym = S; break; + case OEXREG: + a->type = D_INDIR + D_GS; + a->offset = n->reg - 1; + break; case OIND: naddr(n->left, a); @@ -1502,11 +1505,11 @@ exreg(Type *t) int32 o; if(typechlpv[t->etype]) { - if(exregoffset <= REGEXT-4) + if(exregoffset >= 64) return 0; o = exregoffset; - exregoffset--; - return o; + exregoffset += 8; + return o+1; // +1 to avoid 0 == failure; naddr's case OEXREG will subtract 1. } return 0; } diff --git a/src/cmd/6g/Makefile b/src/cmd/6g/Makefile index 712cfc60c..023f5d111 100644 --- a/src/cmd/6g/Makefile +++ b/src/cmd/6g/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 6g +TARG=6g HFILES=\ ../gc/go.h\ @@ -26,18 +26,9 @@ OFILES=\ ../6l/enam.$O\ LIB=\ - ../gc/gc.a$O + ../gc/gc.a\ -$(TARG): $(OFILES) $(LIB) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lbio -l9 -lm - -$(OFILES): $(HFILES) - -clean: - rm -f $(TARG) enam.c 6.out a.out *.$O *.6 - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +include ../../Make.ccmd %.$O: ../gc/%.c - $(CC) $(CFLAGS) -c -I. -o $@ ../gc/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../gc/$*.c diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c index aacc0d06f..d4d22fd61 100644 --- a/src/cmd/6g/cgen.c +++ b/src/cmd/6g/cgen.c @@ -418,7 +418,7 @@ void agen(Node *n, Node *res) { Node *nl, *nr; - Node n1, n2, n3, tmp, n4; + Node n1, n2, n3, tmp, n4, n5; Prog *p1; uint32 w; uint64 v; @@ -477,8 +477,10 @@ agen(Node *n, Node *res) regalloc(&n1, nr->type, N); cgen(nr, &n1); } - regalloc(&n3, types[tptr], res); - agen(nl, &n3); + if(!isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + agen(nl, &n3); + } goto index; } tempname(&tmp, nr->type); @@ -486,8 +488,10 @@ agen(Node *n, Node *res) nr = &tmp; irad: - regalloc(&n3, types[tptr], res); - agen(nl, &n3); + if(!isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + agen(nl, &n3); + } if(!isconst(nr, CTINT)) { regalloc(&n1, nr->type, N); cgen(nr, &n1); @@ -501,7 +505,7 @@ agen(Node *n, Node *res) // explicit check for nil if array is large enough // that we might derive too big a pointer. - if(!isslice(nl->type) && nl->type->width >= unmappedzero) { + if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) { regalloc(&n4, types[tptr], &n3); gmove(&n3, &n4); n4.op = OINDREG; @@ -516,15 +520,16 @@ agen(Node *n, Node *res) // constant index if(isconst(nr, CTINT)) { + if(isconst(nl, CTSTR)) + fatal("constant string constant index"); // front end should handle v = mpgetfix(nr->val.u.xval); - if(isslice(nl->type)) { - + if(isslice(nl->type) || nl->type->etype == TSTRING) { if(!debug['B'] && !n->etype) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; n1.xoffset = Array_nel; - nodconst(&n2, types[TUINT64], v); + nodconst(&n2, types[TUINT32], v); gins(optoas(OCMP, types[TUINT32]), &n1, &n2); p1 = gbranch(optoas(OGT, types[TUINT32]), T); ginscall(panicindex, 0); @@ -536,18 +541,9 @@ agen(Node *n, Node *res) n1.type = types[tptr]; n1.xoffset = Array_array; gmove(&n1, &n3); - } else - if(!debug['B'] && !n->etype) { - if(v < 0) - yyerror("out of bounds on array"); - else - if(v >= nl->type->bound) - yyerror("out of bounds on array"); } - nodconst(&n2, types[tptr], v*w); - gins(optoas(OADD, types[tptr]), &n2, &n3); - + ginscon(optoas(OADD, types[tptr]), v*w, &n3); gmove(&n3, res); regfree(&n3); break; @@ -564,20 +560,43 @@ agen(Node *n, Node *res) if(!debug['B'] && !n->etype) { // check bounds - if(isslice(nl->type)) { + n5.op = OXXX; + t = types[TUINT32]; + if(is64(nr->type)) + t = types[TUINT64]; + if(isconst(nl, CTSTR)) { + nodconst(&n1, t, nl->val.u.sval->len); + } else if(isslice(nl->type) || nl->type->etype == TSTRING) { n1 = n3; n1.op = OINDREG; - n1.type = types[tptr]; + n1.type = types[TUINT32]; n1.xoffset = Array_nel; - } else - nodconst(&n1, types[TUINT64], nl->type->bound); - gins(optoas(OCMP, types[TUINT32]), &n2, &n1); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); + if(is64(nr->type)) { + regalloc(&n5, t, N); + gmove(&n1, &n5); + n1 = n5; + } + } else { + nodconst(&n1, t, nl->type->bound); + } + gins(optoas(OCMP, t), &n2, &n1); + p1 = gbranch(optoas(OLT, t), T); + if(n5.op != OXXX) + regfree(&n5); ginscall(panicindex, 0); patch(p1, pc); } - if(isslice(nl->type)) { + if(isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + p1 = gins(ALEAQ, N, &n3); + datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from); + p1->from.scale = 1; + p1->from.index = n2.val.u.reg; + goto indexdone; + } + + if(isslice(nl->type) || nl->type->etype == TSTRING) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; @@ -591,12 +610,12 @@ agen(Node *n, Node *res) p1->from.index = p1->from.type; p1->from.type = p1->to.type + D_INDIR; } else { - nodconst(&n1, t, w); - gins(optoas(OMUL, t), &n1, &n2); + ginscon(optoas(OMUL, t), w, &n2); gins(optoas(OADD, types[tptr]), &n2, &n3); gmove(&n3, res); } + indexdone: gmove(&n3, res); regfree(&n2); regfree(&n3); @@ -616,10 +635,8 @@ agen(Node *n, Node *res) fatal("agen: bad ONAME class %#x", n->class); } cgen(n->heapaddr, res); - if(n->xoffset != 0) { - nodconst(&n1, types[TINT64], n->xoffset); - gins(optoas(OADD, types[tptr]), &n1, res); - } + if(n->xoffset != 0) + ginscon(optoas(OADD, types[tptr]), n->xoffset, res); break; case OIND: @@ -628,10 +645,8 @@ agen(Node *n, Node *res) case ODOT: agen(nl, res); - if(n->xoffset != 0) { - nodconst(&n1, types[TINT64], n->xoffset); - gins(optoas(OADD, types[tptr]), &n1, res); - } + if(n->xoffset != 0) + ginscon(optoas(OADD, types[tptr]), n->xoffset, res); break; case ODOTPTR: @@ -648,8 +663,7 @@ agen(Node *n, Node *res) gins(ATESTB, nodintconst(0), &n1); regfree(&n1); } - nodconst(&n1, types[TINT64], n->xoffset); - gins(optoas(OADD, types[tptr]), &n1, res); + ginscon(optoas(OADD, types[tptr]), n->xoffset, res); } break; } @@ -694,6 +708,9 @@ bgen(Node *n, int true, Prog *to) if(n == N) n = nodbool(1); + if(n->ninit != nil) + genlist(n->ninit); + nl = n->left; nr = n->right; @@ -1006,6 +1023,10 @@ sgen(Node *n, Node *ns, int32 w) if(w < 0) fatal("sgen copy %d", w); + if(w == 16) + if(componentgen(n, ns)) + return; + // offset on the stack osrc = stkof(n); odst = stkof(ns); @@ -1099,3 +1120,163 @@ sgen(Node *n, Node *ns, int32 w) restx(&nodr, &oldr); restx(&cx, &oldcx); } + +static int +cadable(Node *n) +{ + if(!n->addable) { + // dont know how it happens, + // but it does + return 0; + } + + switch(n->op) { + case ONAME: + return 1; + } + return 0; +} + +/* + * copy a structure component by component + * return 1 if can do, 0 if cant. + * nr is N for copy zero + */ +int +componentgen(Node *nr, Node *nl) +{ + Node nodl, nodr; + int freel, freer; + + freel = 0; + freer = 0; + + switch(nl->type->etype) { + default: + goto no; + + case TARRAY: + if(!isslice(nl->type)) + goto no; + case TSTRING: + case TINTER: + break; + } + + nodl = *nl; + if(!cadable(nl)) { + if(nr == N || !cadable(nr)) + goto no; + igen(nl, &nodl, N); + freel = 1; + } + + if(nr != N) { + nodr = *nr; + if(!cadable(nr)) { + igen(nr, &nodr, N); + freer = 1; + } + } + + switch(nl->type->etype) { + case TARRAY: + if(!isslice(nl->type)) + goto no; + + nodl.xoffset += Array_array; + nodl.type = ptrto(nl->type->type); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = types[TUINT32]; + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_cap-Array_nel; + nodl.type = types[TUINT32]; + + if(nr != N) { + nodr.xoffset += Array_cap-Array_nel; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + goto yes; + + case TSTRING: + nodl.xoffset += Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = types[TUINT32]; + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + goto yes; + + case TINTER: + nodl.xoffset += Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + goto yes; + + case TSTRUCT: + goto no; + } + +no: + if(freer) + regfree(&nodr); + if(freel) + regfree(&nodl); + return 0; + +yes: + if(freer) + regfree(&nodr); + if(freel) + regfree(&nodl); + return 1; +} diff --git a/src/cmd/6g/galign.c b/src/cmd/6g/galign.c index 68647e21b..bdfc9947e 100644 --- a/src/cmd/6g/galign.c +++ b/src/cmd/6g/galign.c @@ -25,7 +25,6 @@ Typedef typedefs[] = void betypeinit(void) { - maxround = 8; widthptr = 8; zprog.link = P; diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h index 353a86dcd..7efb2c252 100644 --- a/src/cmd/6g/gg.h +++ b/src/cmd/6g/gg.h @@ -99,6 +99,7 @@ void cgen_aret(Node*, Node*); int cgen_inline(Node*, Node*); void restx(Node*, Node*); void savex(int, Node*, Node*, Node*, Type*); +int componentgen(Node*, Node*); /* * gsubr.c @@ -122,6 +123,7 @@ Node* nodarg(Type*, int); void nodreg(Node*, Type*, int); void nodindreg(Node*, Type*, int); void gconreg(int, vlong, int); +void ginscon(int, vlong, Node*); void buildtxt(void); Plist* newplist(void); int isfat(Type*); diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c index 59a6d529d..d9fa1793c 100644 --- a/src/cmd/6g/ggen.c +++ b/src/cmd/6g/ggen.c @@ -62,6 +62,8 @@ compile(Node *fn) pl = newplist(); pl->name = curfn->nname; + setlineno(curfn); + nodconst(&nod1, types[TINT32], 0); ptxt = gins(ATEXT, curfn->nname, &nod1); afunclit(&ptxt->from); @@ -83,6 +85,8 @@ compile(Node *fn) checklabels(); if(nerrors != 0) goto ret; + if(curfn->endlineno) + lineno = curfn->endlineno; if(curfn->type->outtuple != 0) ginscall(throwreturn, 0); @@ -90,13 +94,13 @@ compile(Node *fn) if(pret) patch(pret, pc); ginit(); + if(hasdefer) + ginscall(deferreturn, 0); if(curfn->exit) genlist(curfn->exit); gclean(); if(nerrors != 0) goto ret; - if(hasdefer) - ginscall(deferreturn, 0); pc->as = ARET; // overwrite AEND pc->lineno = lineno; @@ -105,11 +109,11 @@ compile(Node *fn) } // fill in argument size - ptxt->to.offset = rnd(curfn->type->argwid, maxround); + ptxt->to.offset = rnd(curfn->type->argwid, widthptr); // fill in final stack size ptxt->to.offset <<= 32; - ptxt->to.offset |= rnd(stksize+maxarg, maxround); + ptxt->to.offset |= rnd(stksize+maxarg, widthptr); if(debug['f']) frame(0); @@ -1041,7 +1045,12 @@ clearfat(Node *nl) if(debug['g']) dump("\nclearfat", nl); + w = nl->type->width; + if(w == 16) + if(componentgen(N, nl)) + return; + c = w % 8; // bytes q = w / 8; // quads @@ -1115,44 +1124,64 @@ getargs(NodeList *nn, Node *reg, int n) void cmpandthrow(Node *nl, Node *nr) { - vlong cl, cr; + vlong cl; Prog *p1; int op; Node *c; + Type *t; + Node n1; + + if(nl->op == OCONV && is64(nl->type)) + nl = nl->left; + if(nr->op == OCONV && is64(nr->type)) + nr = nr->left; op = OLE; if(smallintconst(nl)) { cl = mpgetfix(nl->val.u.xval); if(cl == 0) return; - if(smallintconst(nr)) { - cr = mpgetfix(nr->val.u.xval); - if(cl > cr) { - if(throwpc == nil) { - throwpc = pc; - ginscall(panicslice, 0); - } else - patch(gbranch(AJMP, T), throwpc); - } + if(smallintconst(nr)) return; - } - // put the constant on the right op = brrev(op); c = nl; nl = nr; nr = c; } - - gins(optoas(OCMP, types[TUINT32]), nl, nr); + if(is64(nr->type) && smallintconst(nr)) + nr->type = types[TUINT32]; + + n1.op = OXXX; + t = types[TUINT32]; + if(nl->type->width != t->width || nr->type->width != t->width) { + if((is64(nl->type) && nl->op != OLITERAL) || (is64(nr->type) && nr->op != OLITERAL)) + t = types[TUINT64]; + + // Check if we need to use a temporary. + // At least one of the arguments is 32 bits + // (the len or cap) so one temporary suffices. + if(nl->type->width != t->width && nl->op != OLITERAL) { + regalloc(&n1, t, nl); + gmove(nl, &n1); + nl = &n1; + } else if(nr->type->width != t->width && nr->op != OLITERAL) { + regalloc(&n1, t, nr); + gmove(nr, &n1); + nr = &n1; + } + } + gins(optoas(OCMP, t), nl, nr); + if(n1.op != OXXX) + regfree(&n1); if(throwpc == nil) { - p1 = gbranch(optoas(op, types[TUINT32]), T); + p1 = gbranch(optoas(op, t), T); throwpc = pc; ginscall(panicslice, 0); patch(p1, pc); } else { op = brcom(op); - p1 = gbranch(optoas(op, types[TUINT32]), T); + p1 = gbranch(optoas(op, t), T); patch(p1, throwpc); } } @@ -1183,6 +1212,8 @@ cgen_inline(Node *n, Node *res) goto no; if(!n->left->addable) goto no; + if(n->left->sym == S) + goto no; if(n->left->sym->pkg != runtimepkg) goto no; if(strcmp(n->left->sym->name, "slicearray") == 0) @@ -1261,10 +1292,8 @@ slicearray: if(smallintconst(&nodes[2]) && smallintconst(&nodes[4])) { v = mpgetfix(nodes[2].val.u.xval) * mpgetfix(nodes[4].val.u.xval); - if(v != 0) { - nodconst(&n1, types[tptr], v); - gins(optoas(OADD, types[tptr]), &n1, &nodes[0]); - } + if(v != 0) + ginscon(optoas(OADD, types[tptr]), v, &nodes[0]); } else { regalloc(&n1, types[tptr], &nodes[2]); gmove(&nodes[2], &n1); @@ -1310,6 +1339,7 @@ sliceslice: // if(lb[1] > old.nel[0]) goto throw; n2 = nodes[0]; n2.xoffset += Array_nel; + n2.type = types[TUINT32]; cmpandthrow(&nodes[1], &n2); // ret.nel = old.nel[0]-lb[1]; @@ -1329,6 +1359,7 @@ sliceslice: // if(hb[2] > old.cap[0]) goto throw; n2 = nodes[0]; n2.xoffset += Array_cap; + n2.type = types[TUINT32]; cmpandthrow(&nodes[2], &n2); // if(lb[1] > hb[2]) goto throw; @@ -1376,10 +1407,8 @@ sliceslice: gins(optoas(OAS, types[tptr]), &n2, &n1); v = mpgetfix(nodes[1].val.u.xval) * mpgetfix(nodes[3].val.u.xval); - if(v != 0) { - nodconst(&n2, types[tptr], v); - gins(optoas(OADD, types[tptr]), &n2, &n1); - } + if(v != 0) + ginscon(optoas(OADD, types[tptr]), v, &n1); } else { gmove(&nodes[1], &n1); if(!smallintconst(&nodes[3]) || mpgetfix(nodes[3].val.u.xval) != 1) diff --git a/src/cmd/6g/gobj.c b/src/cmd/6g/gobj.c index 7c05054b7..b667ae48a 100644 --- a/src/cmd/6g/gobj.c +++ b/src/cmd/6g/gobj.c @@ -633,7 +633,7 @@ dsymptr(Sym *s, int off, Sym *x, int xoff) } void -genembedtramp(Type *rcvr, Type *method, Sym *newnam) +genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface) { Sym *e; int c, d, o, mov, add, loaded; @@ -732,7 +732,7 @@ out: p = pc; gins(AJMP, N, N); p->to.type = D_EXTERN; - p->to.sym = methodsym(method->sym, ptrto(f->type)); + p->to.sym = methodsym(method->sym, ptrto(f->type), 0); //print("6. %P\n", p); pc->as = ARET; // overwrite AEND diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c index 52ff6fdea..ebb61ea94 100644 --- a/src/cmd/6g/gsubr.c +++ b/src/cmd/6g/gsubr.c @@ -147,6 +147,8 @@ ggloblnod(Node *nam, int32 width) p->to.sym = S; p->to.type = D_CONST; p->to.offset = width; + if(nam->readonly) + p->from.scale = RODATA; } void @@ -163,6 +165,7 @@ ggloblsym(Sym *s, int32 width, int dupok) p->to.offset = width; if(dupok) p->from.scale = DUPOK; + p->from.scale |= RODATA; } int @@ -427,11 +430,33 @@ fatal("shouldnt be used"); void gconreg(int as, vlong c, int reg) { - Node n1, n2; + Node nr; + + nodreg(&nr, types[TINT64], reg); + ginscon(as, c, &nr); +} + +/* + * generate + * as $c, n + */ +void +ginscon(int as, vlong c, Node *n2) +{ + Node n1, ntmp; nodconst(&n1, types[TINT64], c); - nodreg(&n2, types[TINT64], reg); - gins(as, &n1, &n2); + + if(as != AMOVQ && (c < -1LL<<31 || c >= 1LL<<31)) { + // cannot have 64-bit immediokate in ADD, etc. + // instead, MOV into register first. + regalloc(&ntmp, types[TINT64], N); + gins(AMOVQ, &n1, &ntmp); + gins(as, &ntmp, n2); + regfree(&ntmp); + return; + } + gins(as, &n1, n2); } #define CASE(a,b) (((a)<<16)|((b)<<0)) @@ -1683,12 +1708,28 @@ optoas(int op, Type *t) enum { - ODynam = 1<<0, + ODynam = 1<<0, + OAddable = 1<<1, }; static Node clean[20]; static int cleani = 0; +int +xgen(Node *n, Node *a, int o) +{ + regalloc(a, types[tptr], N); + + if(o & ODynam) + if(n->addable) + if(n->op != OINDREG) + if(n->op != OREGISTER) + return 1; + + agen(n, a); + return 0; +} + void sudoclean(void) { @@ -1716,7 +1757,7 @@ sudoaddable(int as, Node *n, Addr *a) int o, i, w; int oary[10]; int64 v; - Node n1, n2, n3, *nn, *l, *r; + Node n1, n2, n3, n4, *nn, *l, *r; Node *reg, *reg1; Prog *p1; Type *t; @@ -1743,6 +1784,8 @@ sudoaddable(int as, Node *n, Addr *a) goto odot; case OINDEX: + if(n->left->type->etype == TSTRING) + return 0; goto oindex; } return 0; @@ -1820,7 +1863,7 @@ oindex: if(l->type->etype != TARRAY) fatal("not ary"); if(l->type->bound < 0) - o += ODynam; + o |= ODynam; w = n->type->width; if(isconst(r, CTINT)) @@ -1836,9 +1879,6 @@ oindex: break; } -// if(sudoaddable(as, l, a)) -// goto oindex_sudo; - cleani += 2; reg = &clean[cleani-1]; reg1 = &clean[cleani-2]; @@ -1847,8 +1887,8 @@ oindex: // load the array (reg) if(l->ullman > r->ullman) { - regalloc(reg, types[tptr], N); - agen(l, reg); + if(xgen(l, reg, o)) + o |= OAddable; } // load the index (reg1) @@ -1863,49 +1903,89 @@ oindex: // load the array (reg) if(l->ullman <= r->ullman) { - regalloc(reg, types[tptr], N); - agen(l, reg); + if(xgen(l, reg, o)) + o |= OAddable; } if(!(o & ODynam) && l->type->width >= unmappedzero && l->op == OIND) { // cannot rely on page protections to // catch array ptr == 0, so dereference. n2 = *reg; + n2.xoffset = 0; n2.op = OINDREG; n2.type = types[TUINT8]; - n2.xoffset = 0; gins(ATESTB, nodintconst(0), &n2); } // check bounds if(!debug['B'] && !n->etype) { + // check bounds + n4.op = OXXX; + t = types[TUINT32]; if(o & ODynam) { - n2 = *reg; - n2.op = OINDREG; - n2.type = types[tptr]; - n2.xoffset = Array_nel; + if(o & OAddable) { + n2 = *l; + n2.xoffset += Array_nel; + n2.type = types[TUINT32]; + if(is64(r->type)) { + t = types[TUINT64]; + regalloc(&n4, t, N); + gmove(&n2, &n4); + n2 = n4; + } + } else { + n2 = *reg; + n2.xoffset = Array_nel; + n2.op = OINDREG; + n2.type = types[TUINT32]; + if(is64(r->type)) { + t = types[TUINT64]; + regalloc(&n4, t, N); + gmove(&n2, &n4); + n2 = n4; + } + } } else { + if(is64(r->type)) + t = types[TUINT64]; nodconst(&n2, types[TUINT64], l->type->bound); } - gins(optoas(OCMP, types[TUINT32]), reg1, &n2); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); + gins(optoas(OCMP, t), reg1, &n2); + p1 = gbranch(optoas(OLT, t), T); + if(n4.op != OXXX) + regfree(&n4); ginscall(panicindex, 0); patch(p1, pc); } if(o & ODynam) { - n2 = *reg; - n2.op = OINDREG; - n2.type = types[tptr]; - n2.xoffset = Array_array; - gmove(&n2, reg); + if(o & OAddable) { + n2 = *l; + n2.xoffset += Array_array; + n2.type = types[TUINT64]; + gmove(&n2, reg); + } else { + n2 = *reg; + n2.xoffset = Array_array; + n2.op = OINDREG; + n2.type = types[tptr]; + gmove(&n2, reg); + } } - naddr(reg1, a, 1); - a->offset = 0; - a->scale = w; - a->index = a->type; - a->type = reg->val.u.reg + D_INDIR; + if(o & OAddable) { + naddr(reg1, a, 1); + a->offset = 0; + a->scale = w; + a->index = a->type; + a->type = reg->val.u.reg + D_INDIR; + } else { + naddr(reg1, a, 1); + a->offset = 0; + a->scale = w; + a->index = a->type; + a->type = reg->val.u.reg + D_INDIR; + } goto yes; @@ -1915,15 +1995,6 @@ oindex_const: // can multiply by width statically v = mpgetfix(r->val.u.xval); - if(!debug['B'] && (o & ODynam) == 0) { - // array indexed by a constant bounds check - if(v < 0) { - yyerror("out of bounds on array"); - } else - if(v >= l->type->bound) { - yyerror("out of bounds on array"); - } - } if(sudoaddable(as, l, a)) goto oindex_const_sudo; diff --git a/src/cmd/6g/list.c b/src/cmd/6g/list.c index 9194b1dab..c8077c97a 100644 --- a/src/cmd/6g/list.c +++ b/src/cmd/6g/list.c @@ -47,24 +47,28 @@ Pconv(Fmt *fp) { char str[STRINGSZ]; Prog *p; + char scale[40]; p = va_arg(fp->args, Prog*); sconsize = 8; + scale[0] = '\0'; + if(p->from.scale != 0 && (p->as == AGLOBL || p->as == ATEXT)) + snprint(scale, sizeof scale, "%d,", p->from.scale); switch(p->as) { default: - snprint(str, sizeof(str), "%.4ld (%L) %-7A %D,%D", - p->loc, p->lineno, p->as, &p->from, &p->to); + snprint(str, sizeof(str), "%.4d (%L) %-7A %D,%s%D", + p->loc, p->lineno, p->as, &p->from, scale, &p->to); break; case ADATA: sconsize = p->from.scale; - snprint(str, sizeof(str), "%.4ld (%L) %-7A %D/%d,%D", + snprint(str, sizeof(str), "%.4d (%L) %-7A %D/%d,%D", p->loc, p->lineno, p->as, &p->from, sconsize, &p->to); break; case ATEXT: - snprint(str, sizeof(str), "%.4ld (%L) %-7A %D,%lD", - p->loc, p->lineno, p->as, &p->from, &p->to); + snprint(str, sizeof(str), "%.4d (%L) %-7A %D,%s%lD", + p->loc, p->lineno, p->as, &p->from, scale, &p->to); break; } return fmtstrcpy(fp, str); @@ -104,7 +108,7 @@ Dconv(Fmt *fp) if(a->branch == nil) snprint(str, sizeof(str), "<nil>"); else - snprint(str, sizeof(str), "%ld", a->branch->loc); + snprint(str, sizeof(str), "%d", a->branch->loc); break; case D_EXTERN: @@ -127,7 +131,7 @@ Dconv(Fmt *fp) if(fp->flags & FmtLong) { d1 = a->offset & 0xffffffffLL; d2 = (a->offset>>32) & 0xffffffffLL; - snprint(str, sizeof(str), "$%lud-%lud", (ulong)d1, (ulong)d2); + snprint(str, sizeof(str), "$%ud-%ud", (ulong)d1, (ulong)d2); break; } snprint(str, sizeof(str), "$%lld", a->offset); diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c index e92740e04..464627066 100644 --- a/src/cmd/6g/reg.c +++ b/src/cmd/6g/reg.c @@ -682,17 +682,17 @@ brk: print("\nstats\n"); if(ostats.ncvtreg) - print(" %4ld cvtreg\n", ostats.ncvtreg); + print(" %4d cvtreg\n", ostats.ncvtreg); if(ostats.nspill) - print(" %4ld spill\n", ostats.nspill); + print(" %4d spill\n", ostats.nspill); if(ostats.nreload) - print(" %4ld reload\n", ostats.nreload); + print(" %4d reload\n", ostats.nreload); if(ostats.ndelmov) - print(" %4ld delmov\n", ostats.ndelmov); + print(" %4d delmov\n", ostats.ndelmov); if(ostats.nvar) - print(" %4ld delmov\n", ostats.nvar); + print(" %4d delmov\n", ostats.nvar); if(ostats.naddr) - print(" %4ld delmov\n", ostats.naddr); + print(" %4d delmov\n", ostats.naddr); memset(&ostats, 0, sizeof(ostats)); } @@ -1268,7 +1268,7 @@ regset(Reg *r, uint32 bb) while(b = bb & ~(bb-1)) { v.type = b & 0xFFFF? BtoR(b): BtoF(b); if(v.type == 0) - fatal("zero v.type for %#lux", b); + fatal("zero v.type for %#ux", b); c = copyu(r->prog, &v, A); if(c == 3) set |= b; @@ -1486,7 +1486,7 @@ dumpone(Reg *r) int z; Bits bit; - print("%ld:%P", r->loop, r->prog); + print("%d:%P", r->loop, r->prog); for(z=0; z<BITS; z++) bit.b[z] = r->set.b[z] | @@ -1535,14 +1535,14 @@ dumpit(char *str, Reg *r0) if(r1 != R) { print(" pred:"); for(; r1 != R; r1 = r1->p2link) - print(" %.4lud", r1->prog->loc); + print(" %.4ud", r1->prog->loc); print("\n"); } // r1 = r->s1; // if(r1 != R) { // print(" succ:"); // for(; r1 != R; r1 = r1->s1) -// print(" %.4lud", r1->prog->loc); +// print(" %.4ud", r1->prog->loc); // print("\n"); // } } diff --git a/src/cmd/6l/6.out.h b/src/cmd/6l/6.out.h index ca5e485c0..709f82ccc 100644 --- a/src/cmd/6l/6.out.h +++ b/src/cmd/6l/6.out.h @@ -33,6 +33,7 @@ #define NOPROF (1<<0) #define DUPOK (1<<1) #define NOSPLIT (1<<2) +#define RODATA (1<<3) /* * amd64 @@ -389,8 +390,8 @@ enum as AEND, - ADYNT, - AINIT, + ADYNT_, + AINIT_, ASIGNAME, @@ -823,6 +824,7 @@ enum D_INDIR, /* additive */ D_SIZE = D_INDIR + D_INDIR, /* 6l internal */ + D_PCREL, T_TYPE = 1<<0, T_INDEX = 1<<1, diff --git a/src/cmd/6l/Makefile b/src/cmd/6l/Makefile index 72bde4465..fba1b42ae 100644 --- a/src/cmd/6l/Makefile +++ b/src/cmd/6l/Makefile @@ -2,23 +2,29 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 6l\ +TARG=6l OFILES=\ asm.$O\ + data.$O\ + dwarf.$O\ elf.$O\ enam.$O\ go.$O\ + ldelf.$O\ + ldmacho.$O\ lib.$O\ list.$O\ macho.$O\ obj.$O\ optab.$O\ pass.$O\ + prof.$O\ span.$O\ + symtab.$O\ HFILES=\ l.h\ @@ -26,20 +32,14 @@ HFILES=\ ../ld/lib.h\ ../ld/elf.h\ ../ld/macho.h\ + ../ld/dwarf.h\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lbio -l9 - -$(OFILES): $(HFILES) +include ../../Make.ccmd enam.c: 6.out.h sh mkenam -clean: - rm -f *.$O $(TARG) *.6 enam.c 6.out a.out - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +CLEANFILES+=enam.c %.$O: ../ld/%.c - $(CC) $(CFLAGS) -c -I. ../ld/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. ../ld/$*.c diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index b45557ebe..9726d227c 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -28,9 +28,12 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Writing object files. + #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" +#include "../ld/dwarf.h" #include "../ld/macho.h" #define Dbufslop 100 @@ -41,7 +44,6 @@ char linuxdynld[] = "/lib64/ld-linux-x86-64.so.2"; char freebsddynld[] = "/libexec/ld-elf.so.1"; char zeroes[32]; -Prog* datsort(Prog *l); vlong entryvalue(void) @@ -55,15 +57,8 @@ entryvalue(void) s = lookup(a, 0); if(s->type == 0) return INITTEXT; - switch(s->type) { - case STEXT: - break; - case SDATA: - if(dlm) - return s->value+INITDAT; - default: + if(s->type != STEXT) diag("entry not text: %s", s->name); - } return s->value; } @@ -113,130 +108,13 @@ vputl(uint64 v) lputl(v>>32); } -void -strnput(char *s, int n) -{ - int i; - - for(i=0; i<n; i++) { - cput(*s); - if(*s != 0) - s++; - } -} - -vlong -addstring(Sym *s, char *str) -{ - int n, m; - vlong r; - Prog *p; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - n = strlen(str)+1; - while(n > 0) { - m = n; - if(m > sizeof(p->to.scon)) - m = sizeof(p->to.scon); - p = newdata(s, s->value, m, D_EXTERN); - p->to.type = D_SCONST; - memmove(p->to.scon, str, m); - s->value += m; - str += m; - n -= m; - } - return r; -} - -vlong -adduintxx(Sym *s, uint64 v, int wid) -{ - vlong r; - Prog *p; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, wid, D_EXTERN); - s->value += wid; - p->to.type = D_CONST; - p->to.offset = v; - return r; -} - -vlong -adduint8(Sym *s, uint8 v) -{ - return adduintxx(s, v, 1); -} - -vlong -adduint16(Sym *s, uint16 v) -{ - return adduintxx(s, v, 2); -} - -vlong -adduint32(Sym *s, uint32 v) -{ - return adduintxx(s, v, 4); -} - -vlong -adduint64(Sym *s, uint64 v) -{ - return adduintxx(s, v, 8); -} - -vlong -addaddr(Sym *s, Sym *t) -{ - vlong r; - Prog *p; - enum { Ptrsize = 8 }; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, Ptrsize, D_EXTERN); - s->value += Ptrsize; - p->to.type = D_ADDR; - p->to.index = D_EXTERN; - p->to.offset = 0; - p->to.sym = t; - return r; -} - -vlong -addsize(Sym *s, Sym *t) -{ - vlong r; - Prog *p; - enum { Ptrsize = 8 }; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, Ptrsize, D_EXTERN); - s->value += Ptrsize; - p->to.type = D_SIZE; - p->to.index = D_EXTERN; - p->to.offset = 0; - p->to.sym = t; - return r; -} - vlong datoff(vlong addr) { - if(addr >= INITDAT) - return addr - INITDAT + rnd(HEADR+textsize, INITRND); + if(addr >= segdata.vaddr) + return addr - segdata.vaddr + segdata.fileoff; + if(addr >= segtext.vaddr) + return addr - segtext.vaddr + segtext.fileoff; diag("datoff %#llx", addr); return 0; } @@ -260,6 +138,8 @@ enum { ElfStrShstrtab, ElfStrSymtab, ElfStrStrtab, + ElfStrRelaPlt, + ElfStrPlt, NElfStr }; @@ -281,29 +161,461 @@ needlib(char *name) return 0; } +int nelfsym = 1; + +static void addpltsym(Sym*); +static void addgotsym(Sym*); + +void +adddynrel(Sym *s, Reloc *r) +{ + Sym *targ, *rela, *got; + + targ = r->sym; + cursym = s; + + switch(r->type) { + default: + if(r->type >= 256) { + diag("unexpected relocation type %d", r->type); + return; + } + break; + + // Handle relocations found in ELF object files. + case 256 + R_X86_64_PC32: + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ->name); + if(targ->type == 0 || targ->type == SXREF) + diag("unknown symbol %s in pcrel", targ->name); + r->type = D_PCREL; + r->add += 4; + return; + + case 256 + R_X86_64_PLT32: + r->type = D_PCREL; + r->add += 4; + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add += targ->plt; + } + return; + + case 256 + R_X86_64_GOTPCREL: + if(targ->dynimpname == nil || targ->dynexport) { + // have symbol + // turn MOVQ of GOT entry into LEAQ of symbol itself + if(r->off < 2 || s->p[r->off-2] != 0x8b) { + diag("unexpected GOT_LOAD reloc for non-dynamic symbol %s", targ->name); + return; + } + s->p[r->off-2] = 0x8d; + r->type = D_PCREL; + r->add += 4; + return; + } + addgotsym(targ); + r->type = D_PCREL; + r->sym = lookup(".got", 0); + r->add += 4; + r->add += targ->got; + return; + + case 256 + R_X86_64_64: + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected R_X86_64_64 relocation for dynamic symbol %s", targ->name); + r->type = D_ADDR; + return; + + // Handle relocations found in Mach-O object files. + case 512 + MACHO_X86_64_RELOC_UNSIGNED*2 + 0: + case 512 + MACHO_X86_64_RELOC_SIGNED*2 + 0: + case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 0: + // TODO: What is the difference between all these? + r->type = D_ADDR; + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected reloc for dynamic symbol %s", targ->name); + return; + + case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 1: + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = targ->plt; + r->type = D_PCREL; + return; + } + // fall through + case 512 + MACHO_X86_64_RELOC_UNSIGNED*2 + 1: + case 512 + MACHO_X86_64_RELOC_SIGNED*2 + 1: + case 512 + MACHO_X86_64_RELOC_SIGNED_1*2 + 1: + case 512 + MACHO_X86_64_RELOC_SIGNED_2*2 + 1: + case 512 + MACHO_X86_64_RELOC_SIGNED_4*2 + 1: + r->type = D_PCREL; + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected pc-relative reloc for dynamic symbol %s", targ->name); + return; + + case 512 + MACHO_X86_64_RELOC_GOT_LOAD*2 + 1: + if(targ->dynimpname == nil || targ->dynexport) { + // have symbol + // turn MOVQ of GOT entry into LEAQ of symbol itself + if(r->off < 2 || s->p[r->off-2] != 0x8b) { + diag("unexpected GOT_LOAD reloc for non-dynamic symbol %s", targ->name); + return; + } + s->p[r->off-2] = 0x8d; + r->type = D_PCREL; + return; + } + // fall through + case 512 + MACHO_X86_64_RELOC_GOT*2 + 1: + if(targ->dynimpname == nil || targ->dynexport) + diag("unexpected GOT reloc for non-dynamic symbol %s", targ->name); + addgotsym(targ); + r->type = D_PCREL; + r->sym = lookup(".got", 0); + r->add += targ->got; + return; + } + + // Handle references to ELF symbols from our own object files. + if(targ->dynimpname == nil || targ->dynexport) + return; + + switch(r->type) { + case D_PCREL: + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = targ->plt; + return; + + case D_ADDR: + if(s->type != SDATA) + break; + if(iself) { + adddynsym(targ); + rela = lookup(".rela", 0); + addaddrplus(rela, s, r->off); + if(r->siz == 8) + adduint64(rela, ELF64_R_INFO(targ->dynid, R_X86_64_64)); + else + adduint64(rela, ELF64_R_INFO(targ->dynid, R_X86_64_32)); + adduint64(rela, r->add); + r->type = 256; // ignore during relocsym + return; + } + if(HEADTYPE == 6 && s->size == PtrSize && r->off == 0) { + // Mach-O relocations are a royal pain to lay out. + // They use a compact stateful bytecode representation + // that is too much bother to deal with. + // Instead, interpret the C declaration + // void *_Cvar_stderr = &stderr; + // as making _Cvar_stderr the name of a GOT entry + // for stderr. This is separate from the usual GOT entry, + // just in case the C code assigns to the variable, + // and of course it only works for single pointers, + // but we only need to support cgo and that's all it needs. + adddynsym(targ); + got = lookup(".got", 0); + s->type = got->type | SSUB; + s->outer = got; + s->sub = got->sub; + got->sub = s; + s->value = got->size; + adduint64(got, 0); + adduint32(lookup(".linkedit.got", 0), targ->dynid); + r->type = 256; // ignore during relocsym + return; + } + break; + } + + cursym = s; + diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); +} + +int +archreloc(Reloc *r, Sym *s, vlong *val) +{ + return -1; +} + +static void +elfsetupplt(void) +{ + Sym *plt, *got; + + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + if(plt->size == 0) { + // pushq got+8(IP) + adduint8(plt, 0xff); + adduint8(plt, 0x35); + addpcrelplus(plt, got, 8); + + // jmpq got+16(IP) + adduint8(plt, 0xff); + adduint8(plt, 0x25); + addpcrelplus(plt, got, 16); + + // nopl 0(AX) + adduint32(plt, 0x00401f0f); + + // assume got->size == 0 too + addaddrplus(got, lookup(".dynamic", 0), 0); + adduint64(got, 0); + adduint64(got, 0); + } +} + +static void +addpltsym(Sym *s) +{ + if(s->plt >= 0) + return; + + adddynsym(s); + + if(iself) { + Sym *plt, *got, *rela; + + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + rela = lookup(".rela.plt", 0); + if(plt->size == 0) + elfsetupplt(); + + // jmpq *got+size(IP) + adduint8(plt, 0xff); + adduint8(plt, 0x25); + addpcrelplus(plt, got, got->size); + + // add to got: pointer to current pos in plt + addaddrplus(got, plt, plt->size); + + // pushq $x + adduint8(plt, 0x68); + adduint32(plt, (got->size-24-8)/8); + + // jmpq .plt + adduint8(plt, 0xe9); + adduint32(plt, -(plt->size+4)); + + // rela + addaddrplus(rela, got, got->size-8); + adduint64(rela, ELF64_R_INFO(s->dynid, R_X86_64_JMP_SLOT)); + adduint64(rela, 0); + + s->plt = plt->size - 16; + } else if(HEADTYPE == 6) { // Mach-O + // To do lazy symbol lookup right, we're supposed + // to tell the dynamic loader which library each + // symbol comes from and format the link info + // section just so. I'm too lazy (ha!) to do that + // so for now we'll just use non-lazy pointers, + // which don't need to be told which library to use. + // + // http://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html + // has details about what we're avoiding. + + Sym *plt; + + addgotsym(s); + plt = lookup(".plt", 0); + + adduint32(lookup(".linkedit.plt", 0), s->dynid); + + // jmpq *got+size(IP) + s->plt = plt->size; + + adduint8(plt, 0xff); + adduint8(plt, 0x25); + addpcrelplus(plt, lookup(".got", 0), s->got); + } else { + diag("addpltsym: unsupported binary format"); + } +} + +static void +addgotsym(Sym *s) +{ + Sym *got, *rela; + + if(s->got >= 0) + return; + + adddynsym(s); + got = lookup(".got", 0); + s->got = got->size; + adduint64(got, 0); + + if(iself) { + rela = lookup(".rela", 0); + addaddrplus(rela, got, s->got); + adduint64(rela, ELF64_R_INFO(s->dynid, R_X86_64_GLOB_DAT)); + adduint64(rela, 0); + } else if(HEADTYPE == 6) { // Mach-O + adduint32(lookup(".linkedit.got", 0), s->dynid); + } else { + diag("addgotsym: unsupported binary format"); + } +} + +void +adddynsym(Sym *s) +{ + Sym *d, *str; + int t; + char *name; + + if(s->dynid >= 0) + return; + + if(s->dynimpname == nil) + diag("adddynsym: no dynamic name for %s", s->name); + + if(iself) { + s->dynid = nelfsym++; + + d = lookup(".dynsym", 0); + name = s->dynimpname; + if(name == nil) + name = s->name; + adduint32(d, addstring(lookup(".dynstr", 0), name)); + /* type */ + t = STB_GLOBAL << 4; + if(s->dynexport && s->type == STEXT) + t |= STT_FUNC; + else + t |= STT_OBJECT; + adduint8(d, t); + + /* reserved */ + adduint8(d, 0); + + /* section where symbol is defined */ + if(!s->dynexport && s->dynimpname != nil) + adduint16(d, SHN_UNDEF); + else { + switch(s->type) { + default: + case STEXT: + t = 11; + break; + case SRODATA: + t = 12; + break; + case SDATA: + t = 13; + break; + case SBSS: + t = 14; + break; + } + adduint16(d, t); + } + + /* value */ + if(s->type == SDYNIMPORT) + adduint64(d, 0); + else + addaddr(d, s); + + /* size of object */ + adduint64(d, 0); + + if(!s->dynexport && s->dynimplib && needlib(s->dynimplib)) { + elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, + addstring(lookup(".dynstr", 0), s->dynimplib)); + } + } else if(HEADTYPE == 6) { + // Mach-o symbol nlist64 + d = lookup(".dynsym", 0); + name = s->dynimpname; + if(name == nil) + name = s->name; + s->dynid = d->size/16; + // darwin still puts _ prefixes on all C symbols + str = lookup(".dynstr", 0); + adduint32(d, str->size); + adduint8(str, '_'); + addstring(str, name); + if(s->type == SDYNIMPORT) { + adduint8(d, 0x01); // type - N_EXT - external symbol + adduint8(d, 0); // section + } else { + adduint8(d, 0x0f); + switch(s->type) { + default: + case STEXT: + adduint8(d, 1); + break; + case SDATA: + adduint8(d, 2); + break; + case SBSS: + adduint8(d, 4); + break; + } + } + adduint16(d, 0); // desc + if(s->type == SDYNIMPORT) + adduint64(d, 0); // value + else + addaddr(d, s); + } else { + diag("adddynsym: unsupported binary format"); + } +} + +void +adddynlib(char *lib) +{ + Sym *s; + + if(!needlib(lib)) + return; + + if(iself) { + s = lookup(".dynstr", 0); + if(s->size == 0) + addstring(s, ""); + elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib)); + } else if(HEADTYPE == 6) { // Mach-O + machoadddynlib(lib); + } else { + diag("adddynlib: unsupported binary format"); + } +} + void doelf(void) { - Sym *s, *shstrtab, *dynamic, *dynstr, *d; - int h, nsym, t; + Sym *s, *shstrtab, *dynstr; if(HEADTYPE != 7 && HEADTYPE != 9) return; /* predefine strings we need for section headers */ shstrtab = lookup(".shstrtab", 0); + shstrtab->type = SELFDATA; + shstrtab->reachable = 1; + elfstr[ElfStrEmpty] = addstring(shstrtab, ""); elfstr[ElfStrText] = addstring(shstrtab, ".text"); elfstr[ElfStrData] = addstring(shstrtab, ".data"); elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); + addstring(shstrtab, ".elfdata"); + addstring(shstrtab, ".rodata"); if(!debug['s']) { elfstr[ElfStrGosymcounts] = addstring(shstrtab, ".gosymcounts"); elfstr[ElfStrGosymtab] = addstring(shstrtab, ".gosymtab"); elfstr[ElfStrGopclntab] = addstring(shstrtab, ".gopclntab"); - if(debug['e']) { - elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab"); - elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab"); - } + elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab"); + elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab"); + dwarfaddshstrings(shstrtab); } elfstr[ElfStrShstrtab] = addstring(shstrtab, ".shstrtab"); @@ -316,19 +628,21 @@ doelf(void) elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym"); elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr"); elfstr[ElfStrRela] = addstring(shstrtab, ".rela"); + elfstr[ElfStrRelaPlt] = addstring(shstrtab, ".rela.plt"); + elfstr[ElfStrPlt] = addstring(shstrtab, ".plt"); /* dynamic symbol table - first entry all zeros */ s = lookup(".dynsym", 0); s->type = SELFDATA; s->reachable = 1; - s->value += ELF64SYMSIZE; + s->size += ELF64SYMSIZE; /* dynamic string table */ s = lookup(".dynstr", 0); s->type = SELFDATA; s->reachable = 1; - s->value += ELF64SYMSIZE; - addstring(s, ""); + if(s->size == 0) + addstring(s, ""); dynstr = s; /* relocation table */ @@ -339,15 +653,24 @@ doelf(void) /* global offset table */ s = lookup(".got", 0); s->reachable = 1; + s->type = SDATA; // writable, so not SELFDATA + + /* hash */ + s = lookup(".hash", 0); + s->reachable = 1; s->type = SELFDATA; - /* got.plt - ??? */ s = lookup(".got.plt", 0); s->reachable = 1; + s->type = SDATA; // writable, not SELFDATA + + s = lookup(".plt", 0); + s->reachable = 1; s->type = SELFDATA; - /* hash */ - s = lookup(".hash", 0); + elfsetupplt(); + + s = lookup(".rela.plt", 0); s->reachable = 1; s->type = SELFDATA; @@ -355,78 +678,10 @@ doelf(void) s = lookup(".dynamic", 0); s->reachable = 1; s->type = SELFDATA; - dynamic = s; - - /* - * relocation entries for dynimport symbols - */ - nsym = 1; // sym 0 is reserved - for(h=0; h<NHASH; h++) { - for(s=hash[h]; s!=S; s=s->link) { - if(!s->reachable || (s->type != STEXT && s->type != SDATA && s->type != SBSS) || s->dynimpname == nil) - continue; - - if(!s->dynexport) { - d = lookup(".rela", 0); - addaddr(d, s); - adduint64(d, ELF64_R_INFO(nsym, R_X86_64_64)); - adduint64(d, 0); - } - - nsym++; - - d = lookup(".dynsym", 0); - adduint32(d, addstring(lookup(".dynstr", 0), s->dynimpname)); - /* type */ - t = STB_GLOBAL << 4; - if(s->dynexport && s->type == STEXT) - t |= STT_FUNC; - else - t |= STT_OBJECT; - adduint8(d, t); - - /* reserved */ - adduint8(d, 0); - - /* section where symbol is defined */ - if(!s->dynexport) - adduint16(d, SHN_UNDEF); - else { - switch(s->type) { - default: - case STEXT: - t = 9; - break; - case SDATA: - t = 10; - break; - case SBSS: - t = 11; - break; - } - adduint16(d, t); - } - - /* value */ - if(!s->dynexport) - adduint64(d, 0); - else - addaddr(d, s); - - /* size of object */ - adduint64(d, 0); - - if(!s->dynexport && needlib(s->dynimplib)) - elfwritedynent(dynamic, DT_NEEDED, addstring(dynstr, s->dynimplib)); - } - } - - elfdynhash(nsym); /* * .dynamic table */ - s = dynamic; elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE); @@ -437,6 +692,11 @@ doelf(void) elfwritedynent(s, DT_RELAENT, ELF64RELASIZE); if(rpath) elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); + + elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); + elfwritedynent(s, DT_PLTREL, DT_RELA); + elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rela.plt", 0)); + elfwritedynentsym(s, DT_JMPREL, lookup(".rela.plt", 0)); elfwritedynent(s, DT_NULL, 0); } } @@ -463,15 +723,13 @@ phsh(ElfPhdr *ph, ElfShdr *sh) void asmb(void) { - Prog *p; - int32 v, magic; + int32 magic; int a, dynsym; - uchar *op1; vlong vl, va, startva, fo, w, symo, elfsymo, elfstro, elfsymsize, machlink; - vlong symdatva = SYMDATVA; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; + Section *sect; if(debug['v']) Bprint(&bso, "%5.2f asmb\n", cputime()); @@ -481,101 +739,51 @@ asmb(void) elfsymsize = 0; elfstro = 0; elfsymo = 0; - seek(cout, HEADR, 0); - pc = INITTEXT; - curp = firstp; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - if(p->pc != pc) { - if(!debug['a']) - print("%P\n", curp); - diag("phase error %llux sb %llux in %s", p->pc, pc, TNAME); - pc = p->pc; - } - curp = p; - asmins(p); - a = (andptr - and); - if(cbc < a) - cflush(); - if(debug['a']) { - Bprint(&bso, pcstr, pc); - for(op1 = and; op1 < andptr; op1++) - Bprint(&bso, "%.2ux", *op1); - for(; op1 < and+Maxand; op1++) - Bprint(&bso, " "); - Bprint(&bso, "%P\n", curp); - } - if(dlm) { - if(p->as == ATEXT) - reloca = nil; - else if(reloca != nil) - diag("reloc failure: %P", curp); - } - memmove(cbp, and, a); - cbp += a; - pc += a; - cbc -= a; - } - cflush(); + + if(debug['v']) + Bprint(&bso, "%5.2f codeblk\n", cputime()); + Bflush(&bso); + + sect = segtext.sect; + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + codeblk(sect->vaddr, sect->len); + + /* output read-only data in text segment */ + sect = segtext.sect->next; + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + datblk(sect->vaddr, sect->len); + + if(debug['v']) + Bprint(&bso, "%5.2f datblk\n", cputime()); + Bflush(&bso); + seek(cout, segdata.fileoff, 0); + datblk(segdata.vaddr, segdata.filelen); + + machlink = 0; + if(HEADTYPE == 6) + machlink = domacholink(); switch(HEADTYPE) { default: - diag("unknown header type %ld", HEADTYPE); + diag("unknown header type %d", HEADTYPE); case 2: case 5: - seek(cout, HEADR+textsize, 0); break; case 6: debug['8'] = 1; /* 64-bit addresses */ - v = HEADR+textsize; - seek(cout, v, 0); - v = rnd(v, 4096) - v; - while(v > 0) { - cput(0); - v--; - } - cflush(); break; - case 7: case 9: debug['8'] = 1; /* 64-bit addresses */ - v = rnd(HEADR+textsize, INITRND); - seek(cout, v, 0); - /* index of elf text section; needed by asmelfsym, double-checked below */ - /* debug['d'] causes 8 extra sections before the .text section */ + /* !debug['d'] causes extra sections before the .text section */ elftextsh = 1; if(!debug['d']) - elftextsh += 8; + elftextsh += 10; break; } - if(debug['v']) - Bprint(&bso, "%5.2f datblk\n", cputime()); - Bflush(&bso); - - if(dlm){ - char buf[8]; - - write(cout, buf, INITDAT-textsize); - textsize = INITDAT; - } - - datap = datsort(datap); - for(v = 0; v < datsize; v += sizeof(buf)-Dbufslop) { - if(datsize-v > sizeof(buf)-Dbufslop) - datblk(v, sizeof(buf)-Dbufslop); - else - datblk(v, datsize-v); - } - - machlink = 0; - if(HEADTYPE == 6) - machlink = domacholink(); - symsize = 0; spsize = 0; lcsize = 0; @@ -589,14 +797,14 @@ asmb(void) case 2: case 5: debug['s'] = 1; - symo = HEADR+textsize+datsize; + symo = HEADR+segtext.len+segdata.filelen; break; case 6: - symo = rnd(HEADR+textsize, INITRND)+rnd(datsize, INITRND)+machlink; + symo = rnd(HEADR+segtext.len, INITRND)+rnd(segdata.filelen, INITRND)+machlink; break; case 7: case 9: - symo = rnd(HEADR+textsize, INITRND)+datsize; + symo = rnd(HEADR+segtext.len, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); break; } @@ -608,8 +816,6 @@ asmb(void) * line number table */ seek(cout, symo+8, 0); - if(!debug['s']) - asmsym(); if(debug['v']) Bprint(&bso, "%5.2f sp\n", cputime()); Bflush(&bso); @@ -617,30 +823,26 @@ asmb(void) Bprint(&bso, "%5.2f pc\n", cputime()); Bflush(&bso); if(!debug['s']) - asmlc(); - if(dlm) - asmdyn(); - if(!debug['s']) strnput("", INITRND-(8+symsize+lcsize)%INITRND); cflush(); seek(cout, symo, 0); lputl(symsize); lputl(lcsize); cflush(); - if(!debug['s'] && debug['e']) { + if(!debug['s']) { elfsymo = symo+8+symsize+lcsize; seek(cout, elfsymo, 0); - asmelfsym(); + asmelfsym64(); cflush(); elfstro = seek(cout, 0, 1); elfsymsize = elfstro - elfsymo; - write(cout, elfstrdat, elfstrsize); - } - } else - if(dlm){ - seek(cout, HEADR+textsize+datsize, 0); - asmdyn(); - cflush(); + ewrite(cout, elfstrdat, elfstrsize); + + if(debug['v']) + Bprint(&bso, "%5.2f dwarf\n", cputime()); + + dwarfemitdebugsections(); + } } if(debug['v']) @@ -652,12 +854,10 @@ asmb(void) case 2: /* plan9 */ magic = 4*26*26+7; magic |= 0x00008000; /* fat header */ - if(dlm) - magic |= 0x80000000; /* dlm */ lputb(magic); /* magic */ - lputb(textsize); /* sizes */ - lputb(datsize); - lputb(bsssize); + lputb(segtext.filelen); /* sizes */ + lputb(segdata.filelen); + lputb(segdata.len - segdata.filelen); lputb(symsize); /* nsyms */ vl = entryvalue(); lputb(PADDR(vl)); /* va of entry */ @@ -667,19 +867,17 @@ asmb(void) break; case 3: /* plan9 */ magic = 4*26*26+7; - if(dlm) - magic |= 0x80000000; lputb(magic); /* magic */ - lputb(textsize); /* sizes */ - lputb(datsize); - lputb(bsssize); + lputb(segtext.filelen); /* sizes */ + lputb(segdata.filelen); + lputb(segdata.len - segdata.filelen); lputb(symsize); /* nsyms */ lputb(entryvalue()); /* va of entry */ lputb(spsize); /* sp offsets */ lputb(lcsize); /* line offsets */ break; case 6: - asmbmacho(symdatva, symo); + asmbmacho(); break; case 7: case 9: @@ -689,7 +887,7 @@ asmb(void) fo = HEADR; startva = INITTEXT - HEADR; va = startva + fo; - w = textsize; + w = segtext.filelen; /* This null SHdr must appear before all others */ sh = newElfShdr(elfstr[ElfStrEmpty]); @@ -724,41 +922,8 @@ asmb(void) phsh(ph, sh); } - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_X+PF_R; - ph->vaddr = va - fo; - ph->paddr = va - fo; - ph->off = 0; - ph->filesz = w + fo; - ph->memsz = w + fo; - ph->align = INITRND; - - fo = rnd(fo+w, INITRND); - va = rnd(va+w, INITRND); - w = datsize; - - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_W+PF_R; - ph->off = fo; - ph->vaddr = va; - ph->paddr = va; - ph->filesz = w; - ph->memsz = w+bsssize; - ph->align = INITRND; - - if(!debug['s']) { - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_R; - ph->off = symo; - ph->vaddr = symdatva; - ph->paddr = symdatva; - ph->filesz = rnd(8+symsize+lcsize, INITRND); - ph->memsz = rnd(8+symsize+lcsize, INITRND); - ph->align = INITRND; - } + elfphload(&segtext); + elfphload(&segdata); /* Dynamic linking sections */ if (!debug['d']) { /* -d suppresses dynamic loader format */ @@ -776,7 +941,7 @@ asmb(void) sh->entsize = 8; sh->addralign = 8; shsym(sh, lookup(".got.plt", 0)); - + dynsym = eh->shnum; sh = newElfShdr(elfstr[ElfStrDynsym]); sh->type = SHT_DYNSYM; @@ -793,6 +958,22 @@ asmb(void) sh->addralign = 1; shsym(sh, lookup(".dynstr", 0)); + sh = newElfShdr(elfstr[ElfStrRelaPlt]); + sh->type = SHT_RELA; + sh->flags = SHF_ALLOC; + sh->entsize = ELF64RELASIZE; + sh->addralign = 8; + sh->link = dynsym; + sh->info = eh->shnum; // .plt + shsym(sh, lookup(".rela.plt", 0)); + + sh = newElfShdr(elfstr[ElfStrPlt]); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_EXECINSTR; + sh->entsize = 16; + sh->addralign = 4; + shsym(sh, lookup(".plt", 0)); + sh = newElfShdr(elfstr[ElfStrHash]); sh->type = SHT_HASH; sh->flags = SHF_ALLOC; @@ -821,6 +1002,17 @@ asmb(void) ph->type = PT_DYNAMIC; ph->flags = PF_R + PF_W; phsh(ph, sh); + + /* + * Thread-local storage segment (really just size). + */ + if(tlsoffset != 0) { + ph = newElfPhdr(); + ph->type = PT_TLS; + ph->flags = PF_R; + ph->memsz = -tlsoffset; + ph->align = 8; + } } ph = newElfPhdr(); @@ -828,93 +1020,41 @@ asmb(void) ph->flags = PF_W+PF_R; ph->align = 8; - fo = ELFRESERVE; - va = startva + fo; - w = textsize; - if(elftextsh != eh->shnum) diag("elftextsh = %d, want %d", elftextsh, eh->shnum); - sh = newElfShdr(elfstr[ElfStrText]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_EXECINSTR; - sh->addr = va; - sh->off = fo; - sh->size = w; - sh->addralign = 8; - - fo = rnd(fo+w, INITRND); - va = rnd(va+w, INITRND); - w = datsize; - - sh = newElfShdr(elfstr[ElfStrData]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_WRITE+SHF_ALLOC; - sh->addr = va + elfdatsize; - sh->off = fo + elfdatsize; - sh->size = w - elfdatsize; - sh->addralign = 8; - - fo += w; - va += w; - w = bsssize; - - sh = newElfShdr(elfstr[ElfStrBss]); - sh->type = SHT_NOBITS; - sh->flags = SHF_WRITE+SHF_ALLOC; - sh->addr = va; - sh->off = fo; - sh->size = w; - sh->addralign = 8; + for(sect=segtext.sect; sect!=nil; sect=sect->next) + elfshbits(sect); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfshbits(sect); if (!debug['s']) { - fo = symo; - w = 8; - - sh = newElfShdr(elfstr[ElfStrGosymcounts]); + sh = newElfShdr(elfstr[ElfStrGosymtab]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; sh->addralign = 1; - sh->addr = symdatva; - - fo += w; - w = symsize; + shsym(sh, lookup("symtab", 0)); - sh = newElfShdr(elfstr[ElfStrGosymtab]); + sh = newElfShdr(elfstr[ElfStrGopclntab]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; sh->addralign = 1; - sh->addr = symdatva + 8; + shsym(sh, lookup("pclntab", 0)); - fo += w; - w = lcsize; + sh = newElfShdr(elfstr[ElfStrSymtab]); + sh->type = SHT_SYMTAB; + sh->off = elfsymo; + sh->size = elfsymsize; + sh->addralign = 8; + sh->entsize = 24; + sh->link = eh->shnum; // link to strtab - sh = newElfShdr(elfstr[ElfStrGopclntab]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; + sh = newElfShdr(elfstr[ElfStrStrtab]); + sh->type = SHT_STRTAB; + sh->off = elfstro; + sh->size = elfstrsize; sh->addralign = 1; - sh->addr = symdatva + 8 + symsize; - - if(debug['e']) { - sh = newElfShdr(elfstr[ElfStrSymtab]); - sh->type = SHT_SYMTAB; - sh->off = elfsymo; - sh->size = elfsymsize; - sh->addralign = 8; - sh->entsize = 24; - sh->link = eh->shnum; // link to strtab - - sh = newElfShdr(elfstr[ElfStrStrtab]); - sh->type = SHT_STRTAB; - sh->off = elfstro; - sh->size = elfstrsize; - sh->addralign = 1; - } + + dwarfaddelfheaders(); } sh = newElfShstrtab(elfstr[ElfStrShstrtab]); @@ -961,354 +1101,91 @@ cflush(void) n = sizeof(buf.cbuf) - cbc; if(n) - write(cout, buf.cbuf, n); + ewrite(cout, buf.cbuf, n); cbp = buf.cbuf; cbc = sizeof(buf.cbuf); } -void -outa(int n, uchar *cast, uchar *map, vlong l) -{ - int i, j; - - Bprint(&bso, pcstr, l); - for(i=0; i<n; i++) { - j = i; - if(map != nil) - j = map[j]; - Bprint(&bso, "%.2ux", cast[j]); - } - for(; i<Maxand; i++) - Bprint(&bso, " "); - Bprint(&bso, "%P\n", curp); -} - -/* - * divide-and-conquer list-link - * sort of Prog* structures. - * Used for the data block. - */ -int -datcmp(Prog *p1, Prog *p2) +/* Current position in file */ +vlong +cpos(void) { - vlong v1, v2; - - v1 = p1->from.offset; - v2 = p2->from.offset; - if(v1 > v2) - return +1; - if(v1 < v2) - return -1; - return 0; + return seek(cout, 0, 1) + sizeof(buf.cbuf) - cbc; } -Prog* -dsort(Prog *l) -{ - Prog *l1, *l2, *le; - - if(l == 0 || l->link == 0) - return l; - - l1 = l; - l2 = l; - for(;;) { - l2 = l2->link; - if(l2 == 0) - break; - l2 = l2->link; - if(l2 == 0) - break; - l1 = l1->link; - } - - l2 = l1->link; - l1->link = 0; - l1 = dsort(l); - l2 = dsort(l2); - - /* set up lead element */ - if(datcmp(l1, l2) < 0) { - l = l1; - l1 = l1->link; - } else { - l = l2; - l2 = l2->link; - } - le = l; - - for(;;) { - if(l1 == 0) { - while(l2) { - le->link = l2; - le = l2; - l2 = l2->link; - } - le->link = 0; - break; - } - if(l2 == 0) { - while(l1) { - le->link = l1; - le = l1; - l1 = l1->link; - } - break; - } - if(datcmp(l1, l2) < 0) { - le->link = l1; - le = l1; - l1 = l1->link; - } else { - le->link = l2; - le = l2; - l2 = l2->link; - } - } - le->link = 0; - return l; -} - -static Prog *datp; - -Prog* -datsort(Prog *l) +vlong +rnd(vlong v, vlong r) { - Prog *p; - Adr *a; + vlong c; - for(p = l; p != P; p = p->link) { - a = &p->from; - a->offset += a->sym->value; - } - datp = dsort(l); - return datp; + if(r <= 0) + return v; + v += r - 1; + c = v % r; + if(c < 0) + c += r; + v -= c; + return v; } void -datblk(int32 s, int32 n) +genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) { - Prog *p; - uchar *cast; - int32 l, fl, j; - vlong o; - int i, c; - Adr *a; - - for(p = datp; p != P; p = p->link) { - a = &p->from; - l = a->offset - s; - if(l+a->scale < 0) - continue; - datp = p; - break; - } - - memset(buf.dbuf, 0, n+Dbufslop); - for(p = datp; p != P; p = p->link) { - a = &p->from; - - l = a->offset - s; - if(l >= n) - break; - - c = a->scale; - i = 0; - if(l < 0) { - if(l+c <= 0) + Auto *a; + Sym *s; + int h; + + for(h=0; h<NHASH; h++) { + for(s=hash[h]; s!=S; s=s->hash) { + switch(s->type&~SSUB) { + case SCONST: + case SRODATA: + case SDATA: + case SELFDATA: + case SMACHOGOT: + if(!s->reachable) + continue; + put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); continue; - i = -l; - l = 0; - } - - curp = p; - if(!a->sym->reachable) - diag("unreachable symbol in datblk - %s", a->sym->name); - if(a->sym->type == SMACHO) - continue; - if(p->as != AINIT && p->as != ADYNT) { - for(j=l+(c-i)-1; j>=l; j--) - if(buf.dbuf[j]) { - print("%P\n", p); - diag("multiple initialization for %d %d", s, j); - break; - } - } - - switch(p->to.type) { - case D_FCONST: - switch(c) { - default: - case 4: - fl = ieeedtof(&p->to.ieee); - cast = (uchar*)&fl; - for(; i<c; i++) { - buf.dbuf[l] = cast[fnuxi4[i]]; - l++; - } - break; - case 8: - cast = (uchar*)&p->to.ieee; - for(; i<c; i++) { - buf.dbuf[l] = cast[fnuxi8[i]]; - l++; - } - break; - } - break; - - case D_SCONST: - for(; i<c; i++) { - buf.dbuf[l] = p->to.scon[i]; - l++; - } - break; + case SBSS: + if(!s->reachable) + continue; + put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype); + continue; - default: - o = p->to.offset; - if(p->to.type == D_SIZE) - o += p->to.sym->size; - if(p->to.type == D_ADDR) { - if(p->to.index != D_STATIC && p->to.index != D_EXTERN) - diag("DADDR type%P", p); - if(p->to.sym) { - if(p->to.sym->type == SUNDEF) - ckoff(p->to.sym, o); - if(p->to.sym->type == Sxxx) { - curtext = p; // show useful name in diag's output - diag("missing symbol %s", p->to.sym->name); - } - o += p->to.sym->value; - if(p->to.sym->type != STEXT && p->to.sym->type != SUNDEF) - o += INITDAT; - if(dlm) - dynreloc(p->to.sym, l+s+INITDAT, 1); - } - } - fl = o; - cast = (uchar*)&fl; - switch(c) { - default: - diag("bad nuxi %d %d\n%P", c, i, curp); - break; - case 1: - for(; i<c; i++) { - buf.dbuf[l] = cast[inuxi1[i]]; - l++; - } - break; - case 2: - for(; i<c; i++) { - buf.dbuf[l] = cast[inuxi2[i]]; - l++; - } - break; - case 4: - for(; i<c; i++) { - buf.dbuf[l] = cast[inuxi4[i]]; - l++; - } - break; - case 8: - cast = (uchar*)&o; - for(; i<c; i++) { - buf.dbuf[l] = cast[inuxi8[i]]; - l++; - } - break; + case SFILE: + put(nil, s->name, 'f', s->value, 0, s->version, 0); + continue; } - break; } } - write(cout, buf.dbuf, n); - if(!debug['a']) - return; - - /* - * a second pass just to print the asm - */ - for(p = datap; p != P; p = p->link) { - a = &p->from; - - l = a->offset - s; - if(l >= n) - continue; - - c = a->scale; - i = 0; - if(l < 0) - continue; - - if(a->sym->type == SMACHO) + for(s = textp; s != nil; s = s->next) { + if(s->text == nil) continue; - switch(p->to.type) { - case D_FCONST: - switch(c) { - default: - case 4: - fl = ieeedtof(&p->to.ieee); - cast = (uchar*)&fl; - outa(c, cast, fnuxi4, l+s+INITDAT); - break; - case 8: - cast = (uchar*)&p->to.ieee; - outa(c, cast, fnuxi8, l+s+INITDAT); - break; - } - break; - - case D_SCONST: - outa(c, (uchar*)p->to.scon, nil, l+s+INITDAT); - break; - - default: - o = p->to.offset; - if(p->to.type == D_SIZE) - o += p->to.sym->size; - if(p->to.type == D_ADDR) { - if(p->to.sym) { - o += p->to.sym->value; - if(p->to.sym->type != STEXT && p->to.sym->type != SUNDEF) - o += INITDAT; - } - } - fl = o; - cast = (uchar*)&fl; - switch(c) { - case 1: - outa(c, cast, inuxi1, l+s+INITDAT); - break; - case 2: - outa(c, cast, inuxi2, l+s+INITDAT); - break; - case 4: - outa(c, cast, inuxi4, l+s+INITDAT); - break; - case 8: - cast = (uchar*)&o; - outa(c, cast, inuxi8, l+s+INITDAT); - break; - } - break; - } + /* filenames first */ + for(a=s->autom; a; a=a->link) + if(a->type == D_FILE) + put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); + else + if(a->type == D_FILE1) + put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); + + put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); + + /* frame, auto and param after */ + put(nil, ".frame", 'm', s->text->to.offset+8, 0, 0, 0); + + for(a=s->autom; a; a=a->link) + if(a->type == D_AUTO) + put(nil, a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype); + else + if(a->type == D_PARAM) + put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); } + if(debug['v'] || debug['n']) + Bprint(&bso, "symsize = %ud\n", symsize); + Bflush(&bso); } - -vlong -rnd(vlong v, vlong r) -{ - vlong c; - - if(r <= 0) - return v; - v += r - 1; - c = v % r; - if(c < 0) - c += r; - v -= c; - return v; -} - diff --git a/src/cmd/6l/doc.go b/src/cmd/6l/doc.go index a74e9b5c0..501317f36 100644 --- a/src/cmd/6l/doc.go +++ b/src/cmd/6l/doc.go @@ -32,8 +32,8 @@ Options new in this version: Write Apple Mach-O binaries (default when $GOOS is darwin) -H7 Write Linux ELF binaries (default when $GOOS is linux) --L dir1,dir2,.. - Search for libraries (package files) in the comma-separated list of directories. +-L dir1 -L dir2 + Search for libraries (package files) in dir1, dir2, etc. The default is the single location $GOROOT/pkg/$GOOS_amd64. -r dir1:dir2:... Set the dynamic linker search path when using ELF. diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h index 3db0b450a..1c52ea89d 100644 --- a/src/cmd/6l/l.h +++ b/src/cmd/6l/l.h @@ -44,7 +44,7 @@ enum #define P ((Prog*)0) #define S ((Sym*)0) -#define TNAME (curtext?curtext->from.sym->name:noname) +#define TNAME (cursym?cursym->name:noname) #define cput(c)\ { *cbp++ = c;\ if(--cbc <= 0)\ @@ -56,6 +56,7 @@ typedef struct Sym Sym; typedef struct Auto Auto; typedef struct Optab Optab; typedef struct Movtab Movtab; +typedef struct Reloc Reloc; struct Adr { @@ -67,11 +68,7 @@ struct Adr Ieee u0ieee; char *u0sbig; } u0; - union - { - Auto* u1autom; - Sym* u1sym; - } u1; + Sym* sym; short type; char index; char scale; @@ -83,18 +80,25 @@ struct Adr #define ieee u0.u0ieee #define sbig u0.u0sbig -#define autom u1.u1autom -#define sym u1.u1sym +struct Reloc +{ + int32 off; + uchar siz; + int32 type; + int64 add; + Sym* sym; +}; struct Prog { Adr from; Adr to; - Prog *forwd; + Prog* forwd; + Prog* comefrom; Prog* link; - Prog* dlink; Prog* pcond; /* work on this */ vlong pc; + int32 spadj; int32 line; short as; char ft; /* oclass cache */ @@ -102,9 +106,12 @@ struct Prog uchar mark; /* work on these */ uchar back; - char width; /* fake for DATA */ + char width; /* fake for DATA */ char mode; /* 16, 32, or 64 */ }; +#define datasize from.scale +#define textflag from.scale + struct Auto { Sym* asym; @@ -115,25 +122,39 @@ struct Auto }; struct Sym { - char *name; + char* name; short type; short version; - short become; - short frame; - uchar subtype; uchar dupok; uchar reachable; uchar dynexport; + uchar special; + int32 dynid; + int32 sig; + int32 plt; + int32 got; + Sym* hash; // in hash table + Sym* next; // in text or data list + Sym* sub; // in SSUB list + Sym* outer; // container of sub vlong value; vlong size; - int32 sig; - Sym* link; - Prog* text; - Prog* data; Sym* gotype; char* file; char* dynimpname; char* dynimplib; + + // STEXT + Auto* autom; + Prog* text; + + // SDATA, SBSS + uchar* p; + int32 np; + int32 maxp; + Reloc* r; + int32 nr; + int32 maxr; }; struct Optab { @@ -154,21 +175,25 @@ struct Movtab enum { Sxxx, + + /* order here is order in output file */ STEXT = 1, + SELFDATA, + SMACHOPLT, + SRODATA, SDATA, + SMACHOGOT, SBSS, - SDATA1, + SXREF, + SMACHODYNSTR, + SMACHODYNSYM, + SMACHOINDIRECTPLT, + SMACHOINDIRECTGOT, SFILE, SCONST, - SUNDEF, - - SIMPORT, - SEXPORT, - - SMACHO, - SFIXED, - SELFDATA, + SDYNIMPORT, + SSUB = 1<<8, NHASH = 10007, NHUNK = 100000, @@ -274,8 +299,6 @@ enum Rxx = 1<<1, /* extend sib index */ Rxb = 1<<0, /* extend modrm r/m, sib base, or opcode reg */ - Roffset = 22, /* no. bits for offset in relocation address */ - Rindex = 10, /* no. bits for index in relocation address */ Maxand = 10, /* in -a output width of the byte codes */ }; @@ -300,35 +323,30 @@ EXTERN union EXTERN int32 HEADR; EXTERN int32 HEADTYPE; -EXTERN vlong INITDAT; EXTERN int32 INITRND; EXTERN vlong INITTEXT; +EXTERN vlong INITDAT; EXTERN char* INITENTRY; /* entry point */ EXTERN Biobuf bso; -EXTERN int32 bsssize; EXTERN int cbc; EXTERN char* cbp; EXTERN char* pcstr; EXTERN Auto* curauto; EXTERN Auto* curhist; EXTERN Prog* curp; -EXTERN Prog* curtext; -EXTERN Prog* datap; -EXTERN Prog* edatap; -EXTERN vlong datsize; +EXTERN Sym* cursym; +EXTERN Sym* datap; EXTERN vlong elfdatsize; EXTERN char debug[128]; EXTERN char literal[32]; -EXTERN Prog* etextp; -EXTERN Prog* firstp; -EXTERN int xrefresolv; +EXTERN Sym* textp; +EXTERN Sym* etextp; EXTERN char ycover[Ymax*Ymax]; EXTERN uchar* andptr; EXTERN uchar* rexptr; EXTERN uchar and[30]; EXTERN int reg[D_NONE]; EXTERN int regrex[D_NONE+1]; -EXTERN Prog* lastp; EXTERN int32 lcsize; EXTERN int nerrors; EXTERN char* noname; @@ -338,8 +356,7 @@ EXTERN char* rpath; EXTERN int32 spsize; EXTERN Sym* symlist; EXTERN int32 symsize; -EXTERN Prog* textp; -EXTERN vlong textsize; +EXTERN int tlsoffset; EXTERN int version; EXTERN Prog zprg; EXTERN int dtype; @@ -347,45 +364,29 @@ EXTERN char* paramspace; EXTERN Sym* adrgotype; // type symbol on last Adr read EXTERN Sym* fromgotype; // type symbol on last p->from read -EXTERN Adr* reloca; -EXTERN int doexp; // export table -EXTERN int dlm; // dynamically loadable module -EXTERN int imports, nimports; -EXTERN int exports, nexports; -EXTERN char* EXPTAB; -EXTERN Prog undefp; EXTERN vlong textstksiz; EXTERN vlong textarg; extern char thechar; -EXTERN int dynptrsize; EXTERN int elfstrsize; EXTERN char* elfstrdat; EXTERN int elftextsh; -#define UP (&undefp) - extern Optab optab[]; extern Optab* opindex[]; extern char* anames[]; int Aconv(Fmt*); int Dconv(Fmt*); +int Iconv(Fmt*); int Pconv(Fmt*); int Rconv(Fmt*); int Sconv(Fmt*); void addhist(int32, int); void addstackmark(void); Prog* appendp(Prog*); -vlong addstring(Sym*, char*); -vlong adduint32(Sym*, uint32); -vlong adduint64(Sym*, uint64); -vlong addaddr(Sym*, Sym*); -vlong addsize(Sym*, Sym*); void asmb(void); void asmdyn(void); void asmins(Prog*); -void asmlc(void); -void asmsp(void); void asmsym(void); void asmelfsym(void); vlong atolwhex(char*); @@ -393,35 +394,29 @@ Prog* brchain(Prog*); Prog* brloop(Prog*); void buildop(void); void cflush(void); -void ckoff(Sym*, int32); Prog* copyp(Prog*); +vlong cpos(void); double cputime(void); void datblk(int32, int32); void deadcode(void); void diag(char*, ...); -void dobss(void); void dodata(void); void doelf(void); -void doinit(void); void domacho(void); void doprof1(void); void doprof2(void); void dostkoff(void); -void dynreloc(Sym*, uint32, int); vlong entryvalue(void); -void export(void); void follow(void); void gethunk(void); void gotypestrings(void); -void import(void); void listinit(void); Sym* lookup(char*, int); void lputb(int32); void lputl(int32); +void instinit(void); void main(int, char*[]); -void mkfwd(void); void* mysbrk(uint32); -Prog* newdata(Sym*, int, int, int); Prog* newtext(Prog*, Sym*); void nopout(Prog*); int opsize(Prog*); @@ -429,19 +424,14 @@ void patch(void); Prog* prg(void); void parsetextconst(vlong); int relinv(int); -int32 reuse(Prog*, Sym*); vlong rnd(vlong, vlong); void span(void); -void strnput(char*, int); void undef(void); -vlong vaddr(Adr*); vlong symaddr(Sym*); void vputl(uint64); void wputb(uint16); void wputl(uint16); void xdefine(char*, int, vlong); -void xfol(Prog*); -void zaddr(Biobuf*, Adr*, Sym*[]); void machseg(char*, vlong, vlong, vlong, vlong, uint32, uint32, uint32, uint32); void machsymseg(uint32, uint32); @@ -460,3 +450,9 @@ uint32 machheadr(void); #pragma varargck type "R" int #pragma varargck type "A" int #pragma varargck argpos diag 1 + +/* Used by ../ld/dwarf.c */ +enum +{ + DWARFREGSP = 7 +}; diff --git a/src/cmd/6l/list.c b/src/cmd/6l/list.c index 195e11d1d..f39efa2e8 100644 --- a/src/cmd/6l/list.c +++ b/src/cmd/6l/list.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Printing. + #include "l.h" #include "../ld/lib.h" @@ -42,46 +44,36 @@ listinit(void) fmtinstall('D', Dconv); fmtinstall('S', Sconv); fmtinstall('P', Pconv); + fmtinstall('I', Iconv); } int Pconv(Fmt *fp) { - char str[STRINGSZ], str1[STRINGSZ]; Prog *p; p = va_arg(fp->args, Prog*); - if(p == P) - return fmtstrcpy(fp, "<P>"); - bigP = p; - - snprint(str1, sizeof(str1), "(%ld)", p->line); switch(p->as) { case ATEXT: if(p->from.scale) { - snprint(str, sizeof(str), "%-7s %-7A %D,%d,%lD", - str1, p->as, &p->from, p->from.scale, &p->to); + fmtprint(fp, "(%d) %A %D,%d,%D", + p->line, p->as, &p->from, p->from.scale, &p->to); break; } - snprint(str, sizeof(str), "%-7s %-7A %D,%lD", - str1, p->as, &p->from, &p->to); - break; - default: - snprint(str, sizeof(str), "%-7s %-7A %D,%D", - str1, p->as, &p->from, &p->to); + fmtprint(fp, "(%d) %A %D,%D", + p->line, p->as, &p->from, &p->to); break; - case ADATA: - case AINIT: - case ADYNT: - snprint(str, sizeof(str), "%-7s %-7A %D/%d,%D", - str1, p->as, &p->from, p->from.scale, &p->to); + case AINIT_: + case ADYNT_: + fmtprint(fp, "(%d) %A %D/%d,%D", + p->line, p->as, &p->from, p->from.scale, &p->to); break; } bigP = P; - return fmtstrcpy(fp, str); + return 0; } int @@ -187,7 +179,7 @@ Dconv(Fmt *fp) break; case D_FCONST: - snprint(str, sizeof(str), "$(%.8lux,%.8lux)", a->ieee.h, a->ieee.l); + snprint(str, sizeof(str), "$(%.8ux,%.8ux)", a->ieee.h, a->ieee.l); break; case D_SCONST: @@ -402,19 +394,48 @@ Sconv(Fmt *fp) return fmtstrcpy(fp, str); } +int +Iconv(Fmt *fp) +{ + int i, n; + uchar *p; + char *s; + Fmt fmt; + + n = fp->prec; + fp->prec = 0; + if(!(fp->flags&FmtPrec) || n < 0) + return fmtstrcpy(fp, "%I"); + fp->flags &= ~FmtPrec; + p = va_arg(fp->args, uchar*); + + // format into temporary buffer and + // call fmtstrcpy to handle padding. + fmtstrinit(&fmt); + for(i=0; i<n; i++) + fmtprint(&fmt, "%.2ux", *p++); + s = fmtstrflush(&fmt); + fmtstrcpy(fp, s); + free(s); + return 0; +} + void diag(char *fmt, ...) { - char buf[STRINGSZ], *tn; + char buf[STRINGSZ], *tn, *sep; va_list arg; - tn = "??none??"; - if(curtext != P && curtext->from.sym != S) - tn = curtext->from.sym->name; + tn = ""; + sep = ""; + if(cursym != S) { + tn = cursym->name; + sep = ": "; + } va_start(arg, fmt); vseprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); - print("%s: %s\n", tn, buf); + print("%s%s%s\n", tn, sep, buf); nerrors++; if(nerrors > 20) { diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c index 5a4b6a3fc..96d78c3b9 100644 --- a/src/cmd/6l/obj.c +++ b/src/cmd/6l/obj.c @@ -28,11 +28,14 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Reading object files. + #define EXTERN #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" #include "../ld/macho.h" +#include "../ld/dwarf.h" #include <ar.h> char *noname = "<none>"; @@ -51,28 +54,6 @@ char* paramspace = "FP"; * options used: 189BLQSWabcjlnpsvz */ -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 usage(void) { @@ -83,7 +64,7 @@ usage(void) void main(int argc, char *argv[]) { - int i, c; + int c; Binit(&bso, 1, OWRITE); cout = -1; @@ -187,6 +168,11 @@ main(int argc, char *argv[]) INITRND = 4096; break; case 6: /* apple MACH */ + /* + * OS X system constant - offset from 0(GS) to our TLS. + * Explained in ../../libcgo/darwin_amd64.c. + */ + tlsoffset = 0x8a0; machoinit(); HEADR = MACHORESERVE; if(INITRND == -1) @@ -198,6 +184,13 @@ main(int argc, char *argv[]) break; case 7: /* elf64 executable */ case 9: /* freebsd */ + /* + * ELF uses TLS offset negative from FS. + * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS). + * Also known to ../../pkg/runtime/linux/amd64/sys.s + * and ../../libcgo/linux_amd64.s. + */ + tlsoffset = -16; elfinit(); HEADR = ELFRESERVE; if(INITTEXT == -1) @@ -209,117 +202,13 @@ main(int argc, char *argv[]) break; } if(INITDAT != 0 && INITRND != 0) - print("warning: -D0x%llux is ignored because of -R0x%lux\n", + print("warning: -D0x%llux is ignored because of -R0x%ux\n", INITDAT, INITRND); if(debug['v']) - Bprint(&bso, "HEADER = -H%ld -T0x%llux -D0x%llux -R0x%lux\n", + Bprint(&bso, "HEADER = -H%d -T0x%llux -D0x%llux -R0x%ux\n", HEADTYPE, INITTEXT, INITDAT, INITRND); Bflush(&bso); - for(i=1; optab[i].as; i++) { - c = optab[i].as; - if(opindex[c] != nil) { - diag("phase error in optab: %d (%A)", i, c); - errorexit(); - } - opindex[c] = &optab[i]; - } - - for(i=0; i<Ymax; i++) - ycover[i*Ymax + i] = 1; - - ycover[Yi0*Ymax + Yi8] = 1; - ycover[Yi1*Ymax + Yi8] = 1; - - ycover[Yi0*Ymax + Ys32] = 1; - ycover[Yi1*Ymax + Ys32] = 1; - ycover[Yi8*Ymax + Ys32] = 1; - - ycover[Yi0*Ymax + Yi32] = 1; - ycover[Yi1*Ymax + Yi32] = 1; - ycover[Yi8*Ymax + Yi32] = 1; - ycover[Ys32*Ymax + Yi32] = 1; - - ycover[Yi0*Ymax + Yi64] = 1; - ycover[Yi1*Ymax + Yi64] = 1; - ycover[Yi8*Ymax + Yi64] = 1; - ycover[Ys32*Ymax + Yi64] = 1; - ycover[Yi32*Ymax + Yi64] = 1; - - ycover[Yal*Ymax + Yrb] = 1; - ycover[Ycl*Ymax + Yrb] = 1; - ycover[Yax*Ymax + Yrb] = 1; - ycover[Ycx*Ymax + Yrb] = 1; - ycover[Yrx*Ymax + Yrb] = 1; - ycover[Yrl*Ymax + Yrb] = 1; - - ycover[Ycl*Ymax + Ycx] = 1; - - ycover[Yax*Ymax + Yrx] = 1; - ycover[Ycx*Ymax + Yrx] = 1; - - ycover[Yax*Ymax + Yrl] = 1; - ycover[Ycx*Ymax + Yrl] = 1; - ycover[Yrx*Ymax + Yrl] = 1; - - ycover[Yf0*Ymax + Yrf] = 1; - - ycover[Yal*Ymax + Ymb] = 1; - ycover[Ycl*Ymax + Ymb] = 1; - ycover[Yax*Ymax + Ymb] = 1; - ycover[Ycx*Ymax + Ymb] = 1; - ycover[Yrx*Ymax + Ymb] = 1; - ycover[Yrb*Ymax + Ymb] = 1; - ycover[Yrl*Ymax + Ymb] = 1; - ycover[Ym*Ymax + Ymb] = 1; - - ycover[Yax*Ymax + Yml] = 1; - ycover[Ycx*Ymax + Yml] = 1; - ycover[Yrx*Ymax + Yml] = 1; - ycover[Yrl*Ymax + Yml] = 1; - ycover[Ym*Ymax + Yml] = 1; - - ycover[Yax*Ymax + Ymm] = 1; - ycover[Ycx*Ymax + Ymm] = 1; - ycover[Yrx*Ymax + Ymm] = 1; - ycover[Yrl*Ymax + Ymm] = 1; - ycover[Ym*Ymax + Ymm] = 1; - ycover[Ymr*Ymax + Ymm] = 1; - - ycover[Yax*Ymax + Yxm] = 1; - ycover[Ycx*Ymax + Yxm] = 1; - ycover[Yrx*Ymax + Yxm] = 1; - ycover[Yrl*Ymax + Yxm] = 1; - ycover[Ym*Ymax + Yxm] = 1; - ycover[Yxr*Ymax + Yxm] = 1; - - for(i=0; i<D_NONE; i++) { - reg[i] = -1; - if(i >= D_AL && i <= D_R15B) { - reg[i] = (i-D_AL) & 7; - if(i >= D_SPB && i <= D_DIB) - regrex[i] = 0x40; - if(i >= D_R8B && i <= D_R15B) - regrex[i] = Rxr | Rxx | Rxb; - } - if(i >= D_AH && i<= D_BH) - reg[i] = 4 + ((i-D_AH) & 7); - if(i >= D_AX && i <= D_R15) { - reg[i] = (i-D_AX) & 7; - if(i >= D_R8) - regrex[i] = Rxr | Rxx | Rxb; - } - if(i >= D_F0 && i <= D_F0+7) - reg[i] = (i-D_F0) & 7; - if(i >= D_M0 && i <= D_M0+7) - reg[i] = (i-D_M0) & 7; - if(i >= D_X0 && i <= D_X0+15) { - reg[i] = (i-D_X0) & 7; - if(i >= D_X0+8) - regrex[i] = Rxr | Rxx | Rxb; - } - if(i >= D_CR+8 && i <= D_CR+15) - regrex[i] = Rxr; - } + instinit(); zprg.link = P; zprg.pcond = P; @@ -334,50 +223,20 @@ main(int argc, char *argv[]) pcstr = "%.6llux "; nuxiinit(); histgen = 0; - textp = P; - datap = P; - edatap = P; pc = 0; dtype = 4; version = 0; cbp = buf.cbuf; cbc = sizeof(buf.cbuf); - firstp = prg(); - lastp = firstp; addlibpath("command line", "command line", argv[0], "main"); loadlib(); - deadcode(); - - firstp = firstp->link; - if(firstp == P) - errorexit(); - - if(doexp || dlm){ - EXPTAB = "_exporttab"; - zerosig(EXPTAB); - zerosig("etext"); - zerosig("edata"); - zerosig("end"); - if(dlm){ - import(); - HEADTYPE = 2; - INITTEXT = 0; - INITDAT = 0; - INITRND = 8; - INITENTRY = EXPTAB; - } - export(); - } - patch(); follow(); doelf(); if(HEADTYPE == 6) domacho(); - dodata(); - dobss(); dostkoff(); paramspace = "SP"; /* (FP) now (SP) on output */ if(debug['p']) @@ -386,12 +245,18 @@ main(int argc, char *argv[]) else doprof2(); span(); - doinit(); + addexport(); + textaddress(); + pclntab(); + symtab(); + dodata(); + address(); + reloc(); asmb(); undef(); if(debug['v']) { Bprint(&bso, "%5.2f cpu time\n", cputime()); - Bprint(&bso, "%ld symbols\n", nsymbol); + Bprint(&bso, "%d symbols\n", nsymbol); Bprint(&bso, "%d sizeof adr\n", sizeof(Adr)); Bprint(&bso, "%d sizeof prog\n", sizeof(Prog)); } @@ -400,8 +265,19 @@ main(int argc, char *argv[]) errorexit(); } -void -zaddr(Biobuf *f, Adr *a, Sym *h[]) +static Sym* +zsym(char *pn, Biobuf *f, Sym *h[]) +{ + int o; + + o = Bgetc(f); + if(o < 0 || o >= NSYM || h[o] == nil) + mangle(pn); + return h[o]; +} + +static void +zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) { int t; int32 l; @@ -425,7 +301,7 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) } a->sym = S; if(t & T_SYM) - a->sym = h[Bgetc(f)]; + a->sym = zsym(pn, f, h); a->type = D_NONE; if(t & T_FCONST) { a->ieee.l = Bget4(f); @@ -438,16 +314,17 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) } if(t & T_TYPE) a->type = Bgetc(f); + if(a->type < 0 || a->type >= D_SIZE) + mangle(pn); adrgotype = S; if(t & T_GOTYPE) - adrgotype = h[Bgetc(f)]; + adrgotype = zsym(pn, f, h); s = a->sym; - if(s == S) - return; - t = a->type; + if(t == D_INDIR+D_GS) + a->offset += tlsoffset; if(t != D_AUTO && t != D_PARAM) { - if(adrgotype) + if(s && adrgotype) s->gotype = adrgotype; return; } @@ -484,7 +361,7 @@ void ldobj1(Biobuf *f, char *pkg, int64 len, char *pn) { vlong ipc; - Prog *p, *t; + Prog *p; int v, o, r, skip, mode; Sym *h[NSYM], *s, *di; uint32 sig; @@ -492,7 +369,9 @@ ldobj1(Biobuf *f, char *pkg, int64 len, char *pn) int ntext; vlong eof; char src[1024]; + Prog *lastp; + lastp = nil; ntext = 0; eof = Boffset(f) + len; di = S; @@ -549,7 +428,7 @@ loop: if(sig != 0){ if(s->sig != 0 && s->sig != sig) diag("incompatible type signatures" - "%lux(%s) and %lux(%s) for %s", + "%ux(%s) and %ux(%s) for %s", s->sig, s->file, sig, pn, s->name); s->sig = sig; s->file = pn; @@ -557,6 +436,8 @@ loop: if(debug['W']) print(" ANAME %s\n", s->name); + if(o < 0 || o >= nelem(h)) + mangle(pn); h[o] = s; if((v == D_EXTERN || v == D_STATIC) && s->type == 0) s->type = SXREF; @@ -571,6 +452,7 @@ loop: histfrogp++; } else collapsefrog(s); + dwarfaddfrag(s->value, s->name); } goto loop; } @@ -582,9 +464,18 @@ loop: p->mode = mode; p->ft = 0; p->tt = 0; - zaddr(f, &p->from, h); + zaddr(pn, f, &p->from, h); fromgotype = adrgotype; - zaddr(f, &p->to, h); + zaddr(pn, f, &p->to, h); + + switch(p->as) { + case ATEXT: + case ADATA: + case AGLOBL: + if(p->from.sym == S) + mangle(pn); + break; + } if(debug['W']) print("%P\n", p); @@ -606,10 +497,10 @@ loop: case AEND: histtoauto(); - if(curtext != P) - curtext->to.autom = curauto; + if(cursym != nil && cursym->text) + cursym->autom = curauto; curauto = 0; - curtext = P; + cursym = nil; if(Boffset(f) == eof) return; goto newloop; @@ -618,89 +509,41 @@ loop: s = p->from.sym; if(s->type == 0 || s->type == SXREF) { s->type = SBSS; - s->value = 0; + s->size = 0; } - if(s->type != SBSS) { + if(s->type != SBSS && !s->dupok) { diag("%s: redefinition: %s in %s", pn, s->name, TNAME); s->type = SBSS; - s->value = 0; + s->size = 0; } - if(p->to.offset > s->value) - s->value = p->to.offset; + if(p->to.offset > s->size) + s->size = p->to.offset; if(p->from.scale & DUPOK) s->dupok = 1; + if(p->from.scale & RODATA) + s->type = SRODATA; goto loop; - case ADYNT: - if(p->to.sym == S) { - diag("DYNT without a sym\n%P", p); - break; - } - di = p->to.sym; - p->from.scale = 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_ADDR; - p->to.index = D_EXTERN; - goto data; - - 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; - goto data; - case ADATA: - data: // Assume that AGLOBL comes after ADATA. // If we've seen an AGLOBL that said this sym was DUPOK, // ignore any more ADATA we see, which must be // redefinitions. s = p->from.sym; - if(s != S && s->dupok) { + if(s->dupok) { // if(debug['v']) // Bprint(&bso, "skipping %s in %s: dupok\n", s->name, pn); goto loop; } - if(s != S) { - p->dlink = s->data; - s->data = p; - if(s->file == nil) - s->file = pn; - else if(s->file != pn) { - diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); - errorexit(); - } + if(s->file == nil) + s->file = pn; + else if(s->file != pn) { + diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); + errorexit(); } - if(edatap == P) - datap = p; - else - edatap->link = p; - edatap = p; - p->link = P; + savedata(s, p); + unmal(p, sizeof *p); goto loop; case AGOK: @@ -710,23 +553,29 @@ loop: case ATEXT: s = p->from.sym; + if(s->text != nil) { + diag("%s: %s: redefinition", pn, s->name); + return; + } if(ntext++ == 0 && s->type != 0 && s->type != SXREF) { /* redefinition, so file has probably been seen before */ if(debug['v']) Bprint(&bso, "skipping: %s: redefinition: %s", pn, s->name); return; } - if(curtext != P) { + if(cursym != nil && cursym->text) { histtoauto(); - curtext->to.autom = curauto; + cursym->autom = curauto; curauto = 0; } skip = 0; - curtext = p; - if(s == S) { - diag("%s: no TEXT symbol: %P", pn, p); - errorexit(); - } + if(etextp) + etextp->next = s; + else + textp = s; + etextp = s; + s->text = p; + cursym = s; if(s->type != 0 && s->type != SXREF) { if(p->from.scale & DUPOK) { skip = 1; @@ -739,7 +588,10 @@ loop: diag("%s: type mismatch for %s", pn, s->name); s->gotype = fromgotype; } - newtext(p, s); + s->type = STEXT; + s->value = pc; + lastp = p; + p->pc = pc++; goto loop; case AMODE: @@ -772,24 +624,12 @@ loop: goto casdef; if(p->from.type == D_FCONST) { /* size sb 9 max */ - sprint(literal, "$%lux", ieeedtof(&p->from.ieee)); + sprint(literal, "$%ux", 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_EXTERN; - t->from.sym = s; - t->from.scale = 4; - t->to = p->from; - if(edatap == P) - datap = t; - else - edatap->link = t; - edatap = t; - t->link = P; + s->type = SDATA; + adduint32(s, ieeedtof(&p->from.ieee)); + s->reachable = 0; } p->from.type = D_EXTERN; p->from.sym = s; @@ -817,25 +657,14 @@ loop: goto casdef; if(p->from.type == D_FCONST) { /* size sb 18 max */ - sprint(literal, "$%lux.%lux", + sprint(literal, "$%ux.%ux", 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_EXTERN; - t->from.sym = s; - t->from.scale = 8; - t->to = p->from; - if(edatap == P) - datap = t; - else - edatap->link = t; - edatap = t; - t->link = P; + s->type = SDATA; + adduint32(s, p->from.ieee.l); + adduint32(s, p->from.ieee.h); + s->reachable = 0; } p->from.type = D_EXTERN; p->from.sym = s; @@ -847,13 +676,18 @@ loop: default: if(skip) nopout(p); + p->pc = pc; + pc++; if(p->to.type == D_BRANCH) p->to.offset += ipc; + if(lastp == nil) { + if(p->as != ANOP) + diag("unexpected instruction: %P", p); + goto loop; + } lastp->link = p; lastp = p; - p->pc = pc; - pc++; goto loop; } goto loop; @@ -895,154 +729,3 @@ appendp(Prog *q) p->mode = q->mode; return p; } - -void -doprof1(void) -{ - Sym *s; - int32 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) { - if(p->as == ATEXT) { - q = prg(); - q->line = p->line; - q->link = datap; - datap = q; - q->as = ADATA; - q->from.type = D_EXTERN; - q->from.offset = n*4; - q->from.sym = s; - q->from.scale = 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 = AADDL; - p->from.type = D_CONST; - p->from.offset = 1; - p->to.type = 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_EXTERN; - q->from.sym = s; - q->from.scale = 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) { - if(p->as == ATEXT) { - if(p->from.sym == s2) { - p->from.scale = 1; - ps2 = p; - } - if(p->from.sym == s4) { - p->from.scale = 1; - ps4 = p; - } - } - } - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - curtext = p; - - if(p->from.scale & NOPROF) { /* dont profile */ - for(;;) { - q = p->link; - if(q == P) - break; - if(q->as == ATEXT) - break; - p = q; - } - continue; - } - - /* - * JMPL profin - */ - q = prg(); - q->line = p->line; - q->pc = p->pc; - q->link = p->link; - p->link = q; - p = q; - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = 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; - - /* - * JAL profout - */ - p->as = ACALL; - p->from = zprg.from; - p->to = zprg.to; - p->to.type = D_BRANCH; - p->pcond = ps4; - p->to.sym = s4; - - p = q; - - continue; - } - } -} - diff --git a/src/cmd/6l/optab.c b/src/cmd/6l/optab.c index c729f0e23..6cc50313e 100644 --- a/src/cmd/6l/optab.c +++ b/src/cmd/6l/optab.c @@ -783,7 +783,7 @@ Optab optab[] = { AMOVBWSX, ymb_rl, Pq, 0xbe }, { AMOVBWZX, ymb_rl, Pq, 0xb6 }, { AMOVO, yxmov, Pe, 0x6f,0x7f }, - { AMOVOU, yxmov, Pf2, 0x6f,0x7f }, + { AMOVOU, yxmov, Pf3, 0x6f,0x7f }, { AMOVHLPS, yxr, Pm, 0x12 }, { AMOVHPD, yxmov, Pe, 0x16,0x17 }, { AMOVHPS, yxmov, Pm, 0x16,0x17 }, @@ -907,9 +907,9 @@ Optab optab[] = { APOPQ, ypopl, Py, 0x58,0x8f,(00) }, { APOPW, ypopl, Pe, 0x58,0x8f,(00) }, { APOR, ymm, Py, 0xeb,Pe,0xeb }, - { APSADBW, yxm, Pw, Pe,0xf6 }, + { APSADBW, yxm, Pq, 0xf6 }, { APSHUFHW, yxshuf, Pf3, 0x70 }, - { APSHUFL, yxm, Pw, Pe,0x70 }, + { APSHUFL, yxshuf, Pq, 0x70 }, { APSHUFLW, yxshuf, Pf2, 0x70 }, { APSHUFW, ymshuf, Pm, 0x70 }, { APSLLO, ypsdq, Pq, 0x73,(07) }, diff --git a/src/cmd/6l/pass.c b/src/cmd/6l/pass.c index f86942926..5c4ed00a6 100644 --- a/src/cmd/6l/pass.c +++ b/src/cmd/6l/pass.c @@ -28,9 +28,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Code and data passes. + #include "l.h" #include "../ld/lib.h" +static void xfol(Prog*, Prog**); + // see ../../runtime/proc.c:/StackGuard enum { @@ -38,157 +42,6 @@ enum StackBig = 4096, }; -void -dodata(void) -{ - int i; - Sym *s; - Prog *p; - int32 t, u; - - if(debug['v']) - Bprint(&bso, "%5.2f dodata\n", cputime()); - Bflush(&bso); - for(p = datap; p != P; p = p->link) { - curtext = p; // for diag messages - s = p->from.sym; - if(p->as == ADYNT || p->as == AINIT) - s->value = dtype; - if(s->type == SBSS) - s->type = SDATA; - if(s->type != SDATA && s->type != SELFDATA) - diag("initialize non-data (%d): %s\n%P", - s->type, s->name, p); - t = p->from.offset + p->width; - if(t > s->value) - diag("initialize bounds (%lld): %s\n%P", - s->value, s->name, p); - } - - /* allocate elf guys - must be segregated from real data */ - datsize = 0; - for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) { - if(!s->reachable) - continue; - if(s->type != SELFDATA) - continue; - t = rnd(s->value, 8); - s->size = t; - s->value = datsize; - datsize += t; - } - elfdatsize = datsize; - - /* allocate small guys */ - for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) { - if(!s->reachable) - continue; - if(s->type != SDATA) - if(s->type != SBSS) - continue; - t = s->value; - if(t == 0 && s->name[0] != '.') { - diag("%s: no size", s->name); - t = 1; - } - t = rnd(t, 4); - s->value = t; - if(t > MINSIZ) - continue; - if(t >= 8) - datsize = rnd(datsize, 8); - s->size = t; - s->value = datsize; - datsize += t; - s->type = SDATA1; - } - - /* allocate the rest of the data */ - for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) { - if(!s->reachable) - continue; - if(s->type != SDATA) { - if(s->type == SDATA1) - s->type = SDATA; - continue; - } - t = s->value; - if(t >= 8) - datsize = rnd(datsize, 8); - s->size = t; - s->value = datsize; - datsize += t; - } - if(datsize) - datsize = rnd(datsize, 8); - - if(debug['j']) { - /* - * pad data with bss that fits up to next - * 8k boundary, then push data to 8k - */ - u = rnd(datsize, 8192); - u -= datsize; - for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) { - if(!s->reachable) - continue; - if(s->type != SBSS) - continue; - t = s->value; - if(t > u) - continue; - u -= t; - s->size = t; - s->value = datsize; - s->type = SDATA; - datsize += t; - } - datsize += u; - } -} - -void -dobss(void) -{ - int i; - Sym *s; - int32 t; - - if(dynptrsize > 0) { - /* dynamic pointer section between data and bss */ - datsize = rnd(datsize, 8); - } - - /* now the bss */ - bsssize = 0; - for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) { - if(!s->reachable) - continue; - if(s->type != SBSS) - continue; - t = s->value; - s->size = t; - if(t >= 8) - bsssize = rnd(bsssize, 8); - s->value = bsssize + dynptrsize + datsize; - bsssize += t; - } - - xdefine("data", SBSS, 0); - xdefine("edata", SBSS, datsize); - xdefine("end", SBSS, dynptrsize + bsssize + datsize); - - if(debug['s']) - xdefine("symdat", SFIXED, 0); - else - xdefine("symdat", SFIXED, SYMDATVA); -} - Prog* brchain(Prog *p) { @@ -205,19 +58,61 @@ brchain(Prog *p) void follow(void) { + Prog *firstp, *lastp; if(debug['v']) Bprint(&bso, "%5.2f follow\n", cputime()); Bflush(&bso); - firstp = prg(); - lastp = firstp; - xfol(textp); - lastp->link = P; - firstp = firstp->link; + + for(cursym = textp; cursym != nil; cursym = cursym->next) { + firstp = prg(); + lastp = firstp; + xfol(cursym->text, &lastp); + lastp->link = nil; + cursym->text = firstp->link; + } } -void -xfol(Prog *p) +static int +nofollow(int a) +{ + switch(a) { + case AJMP: + case ARET: + case AIRETL: + case AIRETQ: + case AIRETW: + case ARETFL: + case ARETFQ: + case ARETFW: + return 1; + } + return 0; +} + +static int +pushpop(int a) +{ + switch(a) { + case APUSHL: + case APUSHFL: + case APUSHQ: + case APUSHFQ: + case APUSHW: + case APUSHFW: + case APOPL: + case APOPFL: + case APOPQ: + case APOPFQ: + case APOPW: + case APOPFW: + return 1; + } + return 0; +} + +static void +xfol(Prog *p, Prog **last) { Prog *q; int i; @@ -226,55 +121,31 @@ xfol(Prog *p) loop: if(p == P) return; - if(p->as == ATEXT) - curtext = p; - if(!curtext->from.sym->reachable) { - p = p->pcond; - goto loop; - } if(p->as == AJMP) if((q = p->pcond) != P && q->as != ATEXT) { + /* mark instruction as done and continue layout at target of jump */ p->mark = 1; p = q; if(p->mark == 0) goto loop; } if(p->mark) { - /* copy up to 4 instructions to avoid branch */ + /* + * p goes here, but already used it elsewhere. + * copy up to 4 instructions or else branch to other copy. + */ for(i=0,q=p; i<4; i++,q=q->link) { if(q == P) break; - if(q == lastp) + if(q == *last) break; a = q->as; if(a == ANOP) { i--; continue; } - switch(a) { - case AJMP: - case ARET: - case AIRETL: - case AIRETQ: - case AIRETW: - case ARETFL: - case ARETFQ: - case ARETFW: - - case APUSHL: - case APUSHFL: - case APUSHQ: - case APUSHFQ: - case APUSHW: - case APUSHFW: - case APOPL: - case APOPFL: - case APOPQ: - case APOPFQ: - case APOPW: - case APOPFW: - goto brk; - } + if(nofollow(a) || pushpop(a)) + break; // NOTE(rsc): arm does goto copy if(q->pcond == P || q->pcond->mark) continue; if(a == ACALL || a == ALOOP) @@ -287,8 +158,8 @@ loop: q = copyp(p); p = p->link; q->mark = 1; - lastp->link = q; - lastp = q; + (*last)->link = q; + *last = q; if(q->as != a || q->pcond == P || q->pcond->mark) continue; @@ -296,14 +167,13 @@ loop: p = q->pcond; q->pcond = q->link; q->link = p; - xfol(q->link); + xfol(q->link, last); p = q->link; if(p->mark) return; goto loop; } } /* */ - brk:; q = prg(); q->as = AJMP; q->line = p->line; @@ -312,15 +182,22 @@ loop: q->pcond = p; p = q; } + + /* emit p */ p->mark = 1; - lastp->link = p; - lastp = p; + (*last)->link = p; + *last = p; a = p->as; - if(a == AJMP || a == ARET || a == AIRETL || a == AIRETQ || a == AIRETW || - a == ARETFL || a == ARETFQ || a == ARETFW) + + /* continue loop with what comes after p */ + if(nofollow(a)) return; - if(p->pcond != P) - if(a != ACALL) { + if(p->pcond != P && a != ACALL) { + /* + * some kind of conditional branch. + * recurse to follow one path. + * continue loop on the other. + */ q = brchain(p->link); if(q != P && q->mark) if(a != ALOOP) { @@ -328,7 +205,7 @@ loop: p->link = p->pcond; p->pcond = q; } - xfol(p->link); + xfol(p->link, last); q = brchain(p->pcond); if(q->mark) { p->pcond = q; @@ -376,32 +253,11 @@ relinv(int a) case AJOC: return AJOS; } diag("unknown relation: %s in %s", anames[a], TNAME); + errorexit(); return a; } void -doinit(void) -{ - Sym *s; - Prog *p; - int x; - - for(p = datap; p != P; p = p->link) { - x = p->to.type; - if(x != D_EXTERN && x != D_STATIC) - continue; - s = p->to.sym; - if(s->type == 0 || s->type == SXREF) - diag("undefined %s initializer of %s", - s->name, p->from.sym->name); - p->to.offset += s->value; - p->to.type = D_CONST; - if(s->type == SDATA || s->type == SBSS) - p->to.offset += INITDAT; - } -} - -void patch(void) { int32 c; @@ -419,58 +275,58 @@ patch(void) s = lookup("exit", 0); vexit = s->value; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; + for(cursym = textp; cursym != nil; cursym = cursym->next) + for(p = cursym->text; p != P; p = p->link) { + if(HEADTYPE == 7 || HEADTYPE == 9) { + // ELF uses FS instead of GS. + if(p->from.type == D_INDIR+D_GS) + p->from.type = D_INDIR+D_FS; + if(p->to.type == D_INDIR+D_GS) + p->to.type = D_INDIR+D_FS; + } if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH)) { s = p->to.sym; if(s) { if(debug['c']) Bprint(&bso, "%s calls %s\n", TNAME, s->name); - switch(s->type) { - default: + if((s->type&~SSUB) != STEXT) { /* diag prints TNAME first */ diag("undefined: %s", s->name); s->type = STEXT; s->value = vexit; continue; // avoid more error messages - case STEXT: - p->to.offset = s->value; - break; - case SUNDEF: - p->pcond = UP; - p->to.offset = 0; - break; } + if(s->text == nil) + continue; p->to.type = D_BRANCH; + p->to.offset = s->text->pc; + p->pcond = s->text; + continue; } } - if(p->to.type != D_BRANCH || p->pcond == UP) + if(p->to.type != D_BRANCH) continue; c = p->to.offset; - for(q = firstp; q != P;) { - if(q->forwd != P) - if(c >= q->forwd->pc) { - q = q->forwd; - continue; - } + for(q = cursym->text; q != P;) { if(c == q->pc) break; - q = q->link; + if(q->forwd != P && c >= q->forwd->pc) + q = q->forwd; + else + q = q->link; } if(q == P) { - diag("branch out of range in %s\n%P [%s]", - TNAME, p, p->to.sym ? p->to.sym->name : "<nil>"); + diag("branch out of range in %s (%#ux)\n%P [%s]", + TNAME, c, p, p->to.sym ? p->to.sym->name : "<nil>"); p->to.type = D_NONE; } p->pcond = q; } - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; + for(cursym = textp; cursym != nil; cursym = cursym->next) + for(p = cursym->text; p != P; p = p->link) { p->mark = 0; /* initialization for follow */ - if(p->pcond != P && p->pcond != UP) { + if(p->pcond != P) { p->pcond = brloop(p->pcond); if(p->pcond != P) if(p->to.type == D_BRANCH) @@ -479,40 +335,6 @@ patch(void) } } -#define LOG 5 -void -mkfwd(void) -{ - Prog *p; - int i; - int32 dwn[LOG], cnt[LOG]; - Prog *lst[LOG]; - - for(i=0; i<LOG; i++) { - if(i == 0) - cnt[i] = 1; else - cnt[i] = LOG * cnt[i-1]; - dwn[i] = 1; - lst[i] = P; - } - i = 0; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - i--; - if(i < 0) - i = LOG-1; - p->forwd = P; - dwn[i]--; - if(dwn[i] <= 0) { - dwn[i] = cnt[i]; - if(lst[i] != P) - lst[i]->forwd = p; - lst[i] = p; - } - } -} - Prog* brloop(Prog *p) { @@ -553,378 +375,275 @@ dostkoff(void) { Prog *p, *q, *q1; int32 autoffset, deltasp; - int a, f, curframe, curbecome, maxbecome, pcsize; + int a, pcsize; uint32 moreconst1, moreconst2, i; for(i=0; i<nelem(morename); i++) { symmorestack[i] = lookup(morename[i], 0); - pmorestack[i] = P; - } - - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - for(i=0; i<nelem(morename); i++) { - if(p->from.sym == symmorestack[i]) { - pmorestack[i] = p; - break; - } - } - } - } - - for(i=0; i<nelem(morename); i++) { - if(pmorestack[i] == P) - diag("morestack trampoline not defined"); - } - - curframe = 0; - curbecome = 0; - maxbecome = 0; - curtext = 0; - for(p = firstp; p != P; p = p->link) { - - /* find out how much arg space is used in this TEXT */ - if(p->to.type == (D_INDIR+D_SP)) - if(p->to.offset > curframe) - curframe = p->to.offset; - - switch(p->as) { - case ATEXT: - if(curtext && curtext->from.sym) { - curtext->from.sym->frame = curframe; - curtext->from.sym->become = curbecome; - if(curbecome > maxbecome) - maxbecome = curbecome; - } - curframe = 0; - curbecome = 0; - - curtext = p; - break; - - case ARET: - /* special form of RET is BECOME */ - if(p->from.type == D_CONST) - if(p->from.offset > curbecome) - curbecome = p->from.offset; - break; - } - } - if(curtext && curtext->from.sym) { - curtext->from.sym->frame = curframe; - curtext->from.sym->become = curbecome; - if(curbecome > maxbecome) - maxbecome = curbecome; - } - - if(debug['b']) - print("max become = %d\n", maxbecome); - xdefine("ALEFbecome", STEXT, maxbecome); - - curtext = 0; - for(p = firstp; p != P; p = p->link) { - switch(p->as) { - case ATEXT: - curtext = p; - break; - case ACALL: - if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) { - f = maxbecome - curtext->from.sym->frame; - if(f <= 0) - break; - /* calling a become or calling a variable */ - if(p->to.sym == S || p->to.sym->become) { - curtext->to.offset += f; - if(debug['b']) { - curp = p; - print("%D calling %D increase %d\n", - &curtext->from, &p->to, f); - } - } - } - break; - } + if(symmorestack[i]->type != STEXT) + diag("morestack trampoline not defined - %s", morename[i]); + pmorestack[i] = symmorestack[i]->text; } autoffset = 0; deltasp = 0; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - curtext = p; - parsetextconst(p->to.offset); - autoffset = textstksiz; - if(autoffset < 0) - autoffset = 0; - - q = P; - q1 = P; - if((p->from.scale & NOSPLIT) && autoffset >= StackSmall) - diag("nosplit func likely to overflow stack"); - - if(!(p->from.scale & NOSPLIT)) { - if(debug['K']) { - // 6l -K means check not only for stack - // overflow but stack underflow. - // On underflow, INT 3 (breakpoint). - // Underflow itself is rare but this also - // catches out-of-sync stack guard info - - p = appendp(p); - p->as = ACMPQ; - p->from.type = D_INDIR+D_R15; - p->from.offset = 8; - p->to.type = D_SP; - - p = appendp(p); - p->as = AJHI; - p->to.type = D_BRANCH; - p->to.offset = 4; - q1 = p; - - p = appendp(p); - p->as = AINT; - p->from.type = D_CONST; - p->from.offset = 3; - } - - if(autoffset < StackBig) { // do we need to call morestack? - if(autoffset <= StackSmall) { - // small stack - p = appendp(p); - p->as = ACMPQ; - p->from.type = D_SP; - p->to.type = D_INDIR+D_R15; - if(q1) { - q1->pcond = p; - q1 = P; - } - } else { - // large stack - p = appendp(p); - p->as = ALEAQ; - p->from.type = D_INDIR+D_SP; - p->from.offset = -(autoffset-StackSmall); - p->to.type = D_AX; - if(q1) { - q1->pcond = p; - q1 = P; - } + for(cursym = textp; cursym != nil; cursym = cursym->next) { + if(cursym->text == nil || cursym->text->link == nil) + continue; - p = appendp(p); - p->as = ACMPQ; - p->from.type = D_AX; - p->to.type = D_INDIR+D_R15; - } + p = cursym->text; + parsetextconst(p->to.offset); + autoffset = textstksiz; + if(autoffset < 0) + autoffset = 0; + + q = P; + q1 = P; + if((p->from.scale & NOSPLIT) && autoffset >= StackSmall) + diag("nosplit func likely to overflow stack"); + + if(!(p->from.scale & NOSPLIT)) { + p = appendp(p); // load g into CX + p->as = AMOVQ; + if(HEADTYPE == 7 || HEADTYPE == 9) // ELF uses FS + p->from.type = D_INDIR+D_FS; + else + p->from.type = D_INDIR+D_GS; + p->from.offset = tlsoffset+0; + p->to.type = D_CX; + + if(debug['K']) { + // 6l -K means check not only for stack + // overflow but stack underflow. + // On underflow, INT 3 (breakpoint). + // Underflow itself is rare but this also + // catches out-of-sync stack guard info - // common - p = appendp(p); - p->as = AJHI; - p->to.type = D_BRANCH; - p->to.offset = 4; - q = p; - } + p = appendp(p); + p->as = ACMPQ; + p->from.type = D_INDIR+D_CX; + p->from.offset = 8; + p->to.type = D_SP; - /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */ - moreconst1 = 0; - if(autoffset+160 > 4096) - moreconst1 = (autoffset+160) & ~7LL; - moreconst2 = textarg; + p = appendp(p); + p->as = AJHI; + p->to.type = D_BRANCH; + p->to.offset = 4; + q1 = p; - // 4 varieties varieties (const1==0 cross const2==0) - // and 6 subvarieties of (const1==0 and const2!=0) p = appendp(p); - if(moreconst1 == 0 && moreconst2 == 0) { - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack[0]; - p->to.sym = symmorestack[0]; - if(q1) { - q1->pcond = p; - q1 = P; - } - } else - if(moreconst1 != 0 && moreconst2 == 0) { - p->as = AMOVL; - p->from.type = D_CONST; - p->from.offset = moreconst1; - p->to.type = D_AX; - if(q1) { - q1->pcond = p; - q1 = P; - } + p->as = AINT; + p->from.type = D_CONST; + p->from.offset = 3; - p = appendp(p); - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack[1]; - p->to.sym = symmorestack[1]; - } else - if(moreconst1 == 0 && moreconst2 <= 48 && moreconst2%8 == 0) { - i = moreconst2/8 + 3; - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack[i]; - p->to.sym = symmorestack[i]; - if(q1) { - q1->pcond = p; - q1 = P; - } - } else - if(moreconst1 == 0 && moreconst2 != 0) { - p->as = AMOVL; - p->from.type = D_CONST; - p->from.offset = moreconst2; - p->to.type = D_AX; - if(q1) { - q1->pcond = p; - q1 = P; - } + p = appendp(p); + p->as = ANOP; + q1->pcond = p; + q1 = P; + } + if(autoffset < StackBig) { // do we need to call morestack? + if(autoffset <= StackSmall) { + // small stack p = appendp(p); - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack[2]; - p->to.sym = symmorestack[2]; + p->as = ACMPQ; + p->from.type = D_SP; + p->to.type = D_INDIR+D_CX; } else { - p->as = AMOVQ; - p->from.type = D_CONST; - p->from.offset = (uint64)moreconst2 << 32; - p->from.offset |= moreconst1; + // large stack + p = appendp(p); + p->as = ALEAQ; + p->from.type = D_INDIR+D_SP; + p->from.offset = -(autoffset-StackSmall); p->to.type = D_AX; - if(q1) { - q1->pcond = p; - q1 = P; - } p = appendp(p); - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = pmorestack[3]; - p->to.sym = symmorestack[3]; + p->as = ACMPQ; + p->from.type = D_AX; + p->to.type = D_INDIR+D_CX; } - } - - if(q != P) - q->pcond = p->link; - if(autoffset) { + // common p = appendp(p); - p->as = AADJSP; - p->from.type = D_CONST; - p->from.offset = autoffset; - if(q != P) - q->pcond = p; + p->as = AJHI; + p->to.type = D_BRANCH; + p->to.offset = 4; + q = p; } - deltasp = autoffset; - if(debug['K'] > 1 && autoffset) { - // 6l -KK means double-check for stack overflow - // even after calling morestack and even if the - // function is marked as nosplit. - p = appendp(p); - p->as = AMOVQ; - p->from.type = D_INDIR+D_R15; - p->from.offset = 0; - p->to.type = D_BX; + /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */ + moreconst1 = 0; + if(autoffset+160+textarg > 4096) + moreconst1 = (autoffset+160) & ~7LL; + moreconst2 = textarg; - p = appendp(p); - p->as = ASUBQ; + // 4 varieties varieties (const1==0 cross const2==0) + // and 6 subvarieties of (const1==0 and const2!=0) + p = appendp(p); + if(moreconst1 == 0 && moreconst2 == 0) { + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack[0]; + p->to.sym = symmorestack[0]; + } else + if(moreconst1 != 0 && moreconst2 == 0) { + p->as = AMOVL; p->from.type = D_CONST; - p->from.offset = StackSmall+32; - p->to.type = D_BX; - - p = appendp(p); - p->as = ACMPQ; - p->from.type = D_SP; - p->to.type = D_BX; + p->from.offset = moreconst1; + p->to.type = D_AX; p = appendp(p); - p->as = AJHI; + p->as = ACALL; p->to.type = D_BRANCH; - q1 = p; + p->pcond = pmorestack[1]; + p->to.sym = symmorestack[1]; + } else + if(moreconst1 == 0 && moreconst2 <= 48 && moreconst2%8 == 0) { + i = moreconst2/8 + 3; + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack[i]; + p->to.sym = symmorestack[i]; + } else + if(moreconst1 == 0 && moreconst2 != 0) { + p->as = AMOVL; + p->from.type = D_CONST; + p->from.offset = moreconst2; + p->to.type = D_AX; p = appendp(p); - p->as = AINT; + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack[2]; + p->to.sym = symmorestack[2]; + } else { + p->as = AMOVQ; p->from.type = D_CONST; - p->from.offset = 3; + p->from.offset = (uint64)moreconst2 << 32; + p->from.offset |= moreconst1; + p->to.type = D_AX; p = appendp(p); - p->as = ANOP; - q1->pcond = p; - q1 = P; + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack[3]; + p->to.sym = symmorestack[3]; } } - pcsize = p->mode/8; - a = p->from.type; - if(a == D_AUTO) - p->from.offset += deltasp; - if(a == D_PARAM) - p->from.offset += deltasp + pcsize; - a = p->to.type; - if(a == D_AUTO) - p->to.offset += deltasp; - if(a == D_PARAM) - p->to.offset += deltasp + pcsize; - switch(p->as) { - default: - continue; - case APUSHL: - case APUSHFL: - deltasp += 4; - continue; - case APUSHQ: - case APUSHFQ: - deltasp += 8; - continue; - case APUSHW: - case APUSHFW: - deltasp += 2; - continue; - case APOPL: - case APOPFL: - deltasp -= 4; - continue; - case APOPQ: - case APOPFQ: - deltasp -= 8; - continue; - case APOPW: - case APOPFW: - deltasp -= 2; - continue; - case ARET: - break; - } - - if(autoffset != deltasp) - diag("unbalanced PUSH/POP"); - if(p->from.type == D_CONST) - goto become; + if(q != P) + q->pcond = p->link; if(autoffset) { + p = appendp(p); p->as = AADJSP; p->from.type = D_CONST; - p->from.offset = -autoffset; + p->from.offset = autoffset; + p->spadj = autoffset; + if(q != P) + q->pcond = p; + } + deltasp = autoffset; + if(debug['K'] > 1 && autoffset) { + // 6l -KK means double-check for stack overflow + // even after calling morestack and even if the + // function is marked as nosplit. p = appendp(p); - p->as = ARET; - } - continue; + p->as = AMOVQ; + p->from.type = D_INDIR+D_CX; + p->from.offset = 0; + p->to.type = D_BX; - become: - q = p; - p = appendp(p); - p->as = AJMP; - p->to = q->to; - p->pcond = q->pcond; + p = appendp(p); + p->as = ASUBQ; + p->from.type = D_CONST; + p->from.offset = StackSmall+32; + p->to.type = D_BX; - q->as = AADJSP; - q->from = zprg.from; - q->from.type = D_CONST; - q->from.offset = -autoffset; - q->to = zprg.to; - continue; + p = appendp(p); + p->as = ACMPQ; + p->from.type = D_SP; + p->to.type = D_BX; + + p = appendp(p); + p->as = AJHI; + p->to.type = D_BRANCH; + q1 = p; + + p = appendp(p); + p->as = AINT; + p->from.type = D_CONST; + p->from.offset = 3; + + p = appendp(p); + p->as = ANOP; + q1->pcond = p; + q1 = P; + } + + for(; p != P; p = p->link) { + pcsize = p->mode/8; + a = p->from.type; + if(a == D_AUTO) + p->from.offset += deltasp; + if(a == D_PARAM) + p->from.offset += deltasp + pcsize; + a = p->to.type; + if(a == D_AUTO) + p->to.offset += deltasp; + if(a == D_PARAM) + p->to.offset += deltasp + pcsize; + + switch(p->as) { + default: + continue; + case APUSHL: + case APUSHFL: + deltasp += 4; + p->spadj = 4; + continue; + case APUSHQ: + case APUSHFQ: + deltasp += 8; + p->spadj = 8; + continue; + case APUSHW: + case APUSHFW: + deltasp += 2; + p->spadj = 2; + continue; + case APOPL: + case APOPFL: + deltasp -= 4; + p->spadj = -4; + continue; + case APOPQ: + case APOPFQ: + deltasp -= 8; + p->spadj = -8; + continue; + case APOPW: + case APOPFW: + deltasp -= 2; + p->spadj = -2; + continue; + case ARET: + break; + } + + if(autoffset != deltasp) + diag("unbalanced PUSH/POP"); + + if(autoffset) { + p->as = AADJSP; + p->from.type = D_CONST; + p->from.offset = -autoffset; + p->spadj = -autoffset; + p = appendp(p); + p->as = ARET; + } + } } } @@ -975,180 +694,7 @@ undef(void) Sym *s; for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) + for(s = hash[i]; s != S; s = s->hash) if(s->type == SXREF) diag("%s: not defined", s->name); } - -void -import(void) -{ - int i; - Sym *s; - - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type == SXREF && (nimports == 0 || s->subtype == SIMPORT)){ - if(s->value != 0) - diag("value != 0 on SXREF"); - undefsym(s); - Bprint(&bso, "IMPORT: %s sig=%lux v=%lld\n", s->name, s->sig, s->value); - if(debug['S']) - s->sig = 0; - } -} - -void -ckoff(Sym *s, int32 v) -{ - if(v < 0 || v >= 1<<Roffset) - diag("relocation offset %ld for %s out of range", v, s->name); -} - -Prog* -newdata(Sym *s, int o, int w, int t) -{ - Prog *p; - - p = prg(); - if(edatap == P) - datap = p; - else - edatap->link = p; - edatap = p; - p->as = ADATA; - p->width = w; - p->from.scale = w; - p->from.type = t; - p->from.sym = s; - p->from.offset = o; - p->to.type = D_CONST; - p->dlink = s->data; - s->data = p; - return p; -} - -Prog* -newtext(Prog *p, Sym *s) -{ - if(p == P) { - p = prg(); - p->as = ATEXT; - p->from.sym = s; - } - s->type = STEXT; - s->text = p; - s->value = pc; - lastp->link = p; - lastp = p; - p->pc = pc++; - if(textp == P) - textp = p; - else - etextp->pcond = p; - etextp = p; - return p; -} - -void -export(void) -{ - int i, j, n, off, nb, sv, ne; - Sym *s, *et, *str, **esyms; - Prog *p; - char buf[NSNAME], *t; - - n = 0; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type != SXREF && - s->type != SUNDEF && - (nexports == 0 || s->subtype == SEXPORT)) - n++; - esyms = mal(n*sizeof(Sym*)); - ne = n; - n = 0; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type != SXREF && - s->type != SUNDEF && - (nexports == 0 || s->subtype == SEXPORT)) - esyms[n++] = s; - for(i = 0; i < ne-1; i++) - for(j = i+1; j < ne; j++) - if(strcmp(esyms[i]->name, esyms[j]->name) > 0){ - s = esyms[i]; - esyms[i] = esyms[j]; - esyms[j] = s; - } - - nb = 0; - off = 0; - et = lookup(EXPTAB, 0); - if(et->type != 0 && et->type != SXREF) - diag("%s already defined", EXPTAB); - et->type = SDATA; - str = lookup(".string", 0); - if(str->type == 0) - str->type = SDATA; - sv = str->value; - for(i = 0; i < ne; i++){ - s = esyms[i]; - if(debug['S']) - s->sig = 0; - /* Bprint(&bso, "EXPORT: %s sig=%lux t=%d\n", s->name, s->sig, s->type); */ - - /* signature */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.offset = s->sig; - - /* address */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.type = D_ADDR; - p->to.index = D_EXTERN; - p->to.sym = s; - - /* string */ - t = s->name; - n = strlen(t)+1; - for(;;){ - buf[nb++] = *t; - sv++; - if(nb >= NSNAME){ - p = newdata(str, sv-NSNAME, NSNAME, D_STATIC); - p->to.type = D_SCONST; - memmove(p->to.scon, buf, NSNAME); - nb = 0; - } - if(*t++ == 0) - break; - } - - /* name */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.type = D_ADDR; - p->to.index = D_STATIC; - p->to.sym = str; - p->to.offset = sv-n; - } - - if(nb > 0){ - p = newdata(str, sv-nb, nb, D_STATIC); - p->to.type = D_SCONST; - memmove(p->to.scon, buf, nb); - } - - for(i = 0; i < 3; i++){ - newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - } - et->value = off; - if(sv == 0) - sv = 1; - str->value = sv; - exports = ne; - free(esyms); -} diff --git a/src/cmd/6l/prof.c b/src/cmd/6l/prof.c new file mode 100644 index 000000000..25992a40b --- /dev/null +++ b/src/cmd/6l/prof.c @@ -0,0 +1,171 @@ +// Inferno utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Profiling. + +#include "l.h" +#include "../ld/lib.h" + +void +doprof1(void) +{ +#if 0 + Sym *s; + int32 n; + Prog *p, *q; + + if(debug['v']) + Bprint(&bso, "%5.2f profile 1\n", cputime()); + Bflush(&bso); + s = lookup("__mcount", 0); + n = 1; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; + q = prg(); + q->line = p->line; + q->link = datap; + datap = q; + q->as = ADATA; + q->from.type = D_EXTERN; + q->from.offset = n*4; + q->from.sym = s; + q->from.scale = 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 = AADDL; + p->from.type = D_CONST; + p->from.offset = 1; + p->to.type = D_EXTERN; + p->to.sym = s; + p->to.offset = n*4 + 4; + + n += 2; + } + q = prg(); + q->line = 0; + q->link = datap; + datap = q; + + q->as = ADATA; + q->from.type = D_EXTERN; + q->from.sym = s; + q->from.scale = 4; + q->to.type = D_CONST; + q->to.offset = n; + + s->type = SBSS; + s->size = n*4; +#endif +} + +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(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; + if(p->from.sym == s2) { + p->from.scale = 1; + ps2 = p; + } + if(p->from.sym == s4) { + p->from.scale = 1; + ps4 = p; + } + } + for(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; + + if(p->from.scale & NOPROF) /* dont profile */ + continue; + + /* + * JMPL profin + */ + q = prg(); + q->line = p->line; + q->pc = p->pc; + q->link = p->link; + p->link = q; + p = q; + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = ps2; + p->to.sym = s2; + + for(; p; p=p->link) { + 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; + + /* + * JAL profout + */ + p->as = ACALL; + p->from = zprg.from; + p->to = zprg.to; + p->to.type = D_BRANCH; + p->pcond = ps4; + p->to.sym = s4; + + p = q; + } + } + } +} diff --git a/src/cmd/6l/span.c b/src/cmd/6l/span.c index 15f931bcb..5251f19bb 100644 --- a/src/cmd/6l/span.c +++ b/src/cmd/6l/span.c @@ -28,34 +28,33 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Instruction layout. + #include "l.h" #include "../ld/lib.h" -#include "../ld/elf.h" static int rexflag; static int asmode; +static vlong vaddr(Adr*, Reloc*); void -span(void) +span1(Sym *s) { Prog *p, *q; - int32 v; - vlong c, idat; - int m, n, again; - - xdefine("etext", STEXT, 0L); - idat = INITDAT; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - n = 0; - if(p->to.type == D_BRANCH) - if(p->pcond == P) - p->pcond = p; - if((q = p->pcond) != P) - if(q->back != 2) - n = 1; - p->back = n; + int32 c, v, loop; + uchar *bp; + int n, m, i; + + cursym = s; + + if(s->p != nil) + return; + + for(p = s->text; p != P; p = p->link) { + p->back = 2; // use short branches first time through + if((q = p->pcond) != P && (q->back & 2)) + p->back |= 1; // backward jump + if(p->as == AADJSP) { p->to.type = D_SP; v = -p->from.offset; @@ -70,378 +69,253 @@ span(void) p->as = ANOP; } } + n = 0; + do { + loop = 0; + memset(s->r, 0, s->nr*sizeof s->r[0]); + s->nr = 0; + s->np = 0; + c = 0; + for(p = s->text; p != P; p = p->link) { + p->pc = c; + + // process forward jumps to p + for(q = p->comefrom; q != P; q = q->forwd) { + v = p->pc - (q->pc + q->mark); + if(q->back & 2) { // short + if(v > 127) { + loop++; + q->back ^= 2; + } + s->p[q->pc+1] = v; + } else { + bp = s->p + q->pc + q->mark - 4; + *bp++ = v; + *bp++ = v>>8; + *bp++ = v>>16; + *bp++ = v>>24; + } + } + p->comefrom = P; -start: - if(debug['v']) - Bprint(&bso, "%5.2f span\n", cputime()); - Bflush(&bso); - c = INITTEXT; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - if(p->to.type == D_BRANCH) - if(p->back) - p->pc = c; - asmins(p); - p->pc = c; - m = andptr-and; - p->mark = m; - c += m; - } - -loop: - n++; - if(debug['v']) - Bprint(&bso, "%5.2f span %d\n", cputime(), n); - Bflush(&bso); - if(n > 50) { - print("span must be looping\n"); - errorexit(); - } - again = 0; - c = INITTEXT; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - if(p->to.type == D_BRANCH || p->back & 0100) { - if(p->back) - p->pc = c; asmins(p); + p->pc = c; m = andptr-and; - if(m != p->mark) { - p->mark = m; - again++; - } + symgrow(s, p->pc+m); + memmove(s->p+p->pc, and, m); + p->mark = m; + c += m; } - p->pc = c; - c += p->mark; - } - if(again) { - textsize = c; - goto loop; - } - if(INITRND) { - INITDAT = rnd(c, INITRND); - if(INITDAT != idat) { - idat = INITDAT; - goto start; + if(++n > 20) { + diag("span must be looping"); + errorexit(); + } + } while(loop); + s->size = c; + + if(debug['a'] > 1) { + print("span1 %s %lld (%d tries)\n %.6ux", s->name, s->size, n, 0); + for(i=0; i<s->np; i++) { + print(" %.2ux", s->p[i]); + if(i%16 == 15) + print("\n %.6ux", i+1); + } + if(i%16) + print("\n"); + + for(i=0; i<s->nr; i++) { + Reloc *r; + + r = &s->r[i]; + print(" rel %#.4ux/%d %s%+lld\n", r->off, r->siz, r->sym->name, r->add); } } - xdefine("etext", STEXT, c); - if(debug['v']) - Bprint(&bso, "etext = %llux\n", c); - Bflush(&bso); - for(p = textp; p != P; p = p->pcond) - p->from.sym->value = p->pc; - textsize = c - INITTEXT; } void -xdefine(char *p, int t, vlong v) +span(void) { - Sym *s; + Prog *p, *q; + int32 v; + int n; - s = lookup(p, 0); - if(s->type == 0 || s->type == SXREF) { - s->type = t; - s->value = v; - } - if(s->type == STEXT && s->value == 0) - s->value = v; -} + if(debug['v']) + Bprint(&bso, "%5.2f span\n", cputime()); -void -putsymb(char *s, int t, vlong v, vlong size, int ver, Sym *go) -{ - int i, f, l; - vlong gv; - - if(t == 'f') - s++; - l = 4; - if(!debug['8']){ - lputb(v>>32); - l = 8; - } - lputb(v); - if(ver) - t += 'a' - 'A'; - cput(t+0x80); /* 0x80 is variable length */ - - if(t == 'Z' || t == 'z') { - cput(s[0]); - for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) { - cput(s[i]); - cput(s[i+1]); - } - cput(0); - cput(0); - i++; - } - else { - for(i=0; s[i]; i++) - cput(s[i]); - cput(0); - } - gv = 0; - if(go) { - if(!go->reachable) - diag("unreachable type %s", go->name); - gv = go->value+INITDAT; - } - if(l == 8) - lputb(gv>>32); - lputb(gv); - symsize += l + 1 + i+1 + l; - - if(debug['n']) { - if(t == 'z' || t == 'Z') { - Bprint(&bso, "%c %.8llux ", t, v); - for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) { - f = ((s[i]&0xff) << 8) | (s[i+1]&0xff); - Bprint(&bso, "/%x", f); + // NOTE(rsc): If we get rid of the globals we should + // be able to parallelize these iterations. + for(cursym = textp; cursym != nil; cursym = cursym->next) { + if(cursym->p != nil) + continue; + // TODO: move into span1 + for(p = cursym->text; p != P; p = p->link) { + n = 0; + if(p->to.type == D_BRANCH) + if(p->pcond == P) + p->pcond = p; + if((q = p->pcond) != P) + if(q->back != 2) + n = 1; + p->back = n; + if(p->as == AADJSP) { + p->to.type = D_SP; + v = -p->from.offset; + p->from.offset = v; + p->as = p->mode != 64? AADDL: AADDQ; + if(v < 0) { + p->as = p->mode != 64? ASUBL: ASUBQ; + v = -v; + p->from.offset = v; + } + if(v == 0) + p->as = ANOP; } - Bprint(&bso, "\n"); - return; } - if(ver) - Bprint(&bso, "%c %.8llux %s<%d> %s (%.8llux)\n", t, v, s, ver, go ? go->name : "", gv); - else - Bprint(&bso, "%c %.8llux %s %s (%.8llux)\n", t, v, s, go ? go->name : "", gv); + span1(cursym); } } void -genasmsym(void (*put)(char*, int, vlong, vlong, int, Sym*)) +xdefine(char *p, int t, vlong v) { - Prog *p; - Auto *a; Sym *s; - int h; - - s = lookup("etext", 0); - if(s->type == STEXT) - put(s->name, 'T', s->value, s->size, s->version, 0); - - for(h=0; h<NHASH; h++) { - for(s=hash[h]; s!=S; s=s->link) { - switch(s->type) { - case SCONST: - if(!s->reachable) - continue; - put(s->name, 'D', s->value, s->size, s->version, s->gotype); - continue; - - case SDATA: - case SELFDATA: - if(!s->reachable) - continue; - put(s->name, 'D', s->value+INITDAT, s->size, s->version, s->gotype); - continue; - - case SMACHO: - if(!s->reachable) - continue; - put(s->name, 'D', s->value+INITDAT+datsize+bsssize, s->size, s->version, s->gotype); - continue; - - case SBSS: - if(!s->reachable) - continue; - put(s->name, 'B', s->value+INITDAT, s->size, s->version, s->gotype); - continue; - - case SFIXED: - put(s->name, 'B', s->value, s->size, s->version, s->gotype); - continue; - - case SFILE: - put(s->name, 'f', s->value, 0, s->version, 0); - continue; - } - } - } - - for(p = textp; p != P; p = p->pcond) { - s = p->from.sym; - if(s->type != STEXT) - continue; - /* filenames first */ - for(a=p->to.autom; a; a=a->link) - if(a->type == D_FILE) - put(a->asym->name, 'z', a->aoffset, 0, 0, 0); - else - if(a->type == D_FILE1) - put(a->asym->name, 'Z', a->aoffset, 0, 0, 0); - - if(!s->reachable) - continue; - put(s->name, 'T', s->value, s->size, s->version, s->gotype); - - /* frame, auto and param after */ - put(".frame", 'm', p->to.offset+8, 0, 0, 0); - - for(a=p->to.autom; a; a=a->link) - if(a->type == D_AUTO) - put(a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype); - else - if(a->type == D_PARAM) - put(a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); - } - if(debug['v'] || debug['n']) - Bprint(&bso, "symsize = %lud\n", symsize); - Bflush(&bso); + s = lookup(p, 0); + s->type = t; + s->value = v; + s->reachable = 1; + s->special = 1; } void -asmsym(void) +instinit(void) { - genasmsym(putsymb); -} + int c, i; -char *elfstrdat; -int elfstrsize; -int maxelfstr; - -int -putelfstr(char *s) -{ - int off, n; - - if(elfstrsize == 0 && s[0] != 0) { - // first entry must be empty string - putelfstr(""); - } - - n = strlen(s)+1; - if(elfstrsize+n > maxelfstr) { - maxelfstr = 2*(elfstrsize+n+(1<<20)); - elfstrdat = realloc(elfstrdat, maxelfstr); - } - off = elfstrsize; - elfstrsize += n; - memmove(elfstrdat+off, s, n); - return off; -} - -void -putelfsymb(char *s, int t, vlong addr, vlong size, int ver, Sym *go) -{ - int bind, type, shndx, stroff; - - bind = STB_GLOBAL; - switch(t) { - default: - return; - case 'T': - type = STT_FUNC; - shndx = elftextsh + 0; - break; - case 'D': - type = STT_OBJECT; - shndx = elftextsh + 1; - break; - case 'B': - type = STT_OBJECT; - shndx = elftextsh + 2; - break; + for(i=1; optab[i].as; i++) { + c = optab[i].as; + if(opindex[c] != nil) { + diag("phase error in optab: %d (%A)", i, c); + errorexit(); + } + opindex[c] = &optab[i]; } - - stroff = putelfstr(s); - lputl(stroff); // string - cput((bind<<4)|(type&0xF)); - cput(0); - wputl(shndx); - vputl(addr); - vputl(size); -} -void -asmelfsym(void) -{ - genasmsym(putelfsymb); -} - -void -asmlc(void) -{ - vlong oldpc; - Prog *p; - int32 oldlc, v, s; - - oldpc = INITTEXT; - oldlc = 0; - for(p = firstp; p != P; p = p->link) { - if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) { - if(p->as == ATEXT) - curtext = p; - if(debug['O']) - Bprint(&bso, "%6llux %P\n", - p->pc, p); - continue; + for(i=0; i<Ymax; i++) + ycover[i*Ymax + i] = 1; + + ycover[Yi0*Ymax + Yi8] = 1; + ycover[Yi1*Ymax + Yi8] = 1; + + ycover[Yi0*Ymax + Ys32] = 1; + ycover[Yi1*Ymax + Ys32] = 1; + ycover[Yi8*Ymax + Ys32] = 1; + + ycover[Yi0*Ymax + Yi32] = 1; + ycover[Yi1*Ymax + Yi32] = 1; + ycover[Yi8*Ymax + Yi32] = 1; + ycover[Ys32*Ymax + Yi32] = 1; + + ycover[Yi0*Ymax + Yi64] = 1; + ycover[Yi1*Ymax + Yi64] = 1; + ycover[Yi8*Ymax + Yi64] = 1; + ycover[Ys32*Ymax + Yi64] = 1; + ycover[Yi32*Ymax + Yi64] = 1; + + ycover[Yal*Ymax + Yrb] = 1; + ycover[Ycl*Ymax + Yrb] = 1; + ycover[Yax*Ymax + Yrb] = 1; + ycover[Ycx*Ymax + Yrb] = 1; + ycover[Yrx*Ymax + Yrb] = 1; + ycover[Yrl*Ymax + Yrb] = 1; + + ycover[Ycl*Ymax + Ycx] = 1; + + ycover[Yax*Ymax + Yrx] = 1; + ycover[Ycx*Ymax + Yrx] = 1; + + ycover[Yax*Ymax + Yrl] = 1; + ycover[Ycx*Ymax + Yrl] = 1; + ycover[Yrx*Ymax + Yrl] = 1; + + ycover[Yf0*Ymax + Yrf] = 1; + + ycover[Yal*Ymax + Ymb] = 1; + ycover[Ycl*Ymax + Ymb] = 1; + ycover[Yax*Ymax + Ymb] = 1; + ycover[Ycx*Ymax + Ymb] = 1; + ycover[Yrx*Ymax + Ymb] = 1; + ycover[Yrb*Ymax + Ymb] = 1; + ycover[Yrl*Ymax + Ymb] = 1; + ycover[Ym*Ymax + Ymb] = 1; + + ycover[Yax*Ymax + Yml] = 1; + ycover[Ycx*Ymax + Yml] = 1; + ycover[Yrx*Ymax + Yml] = 1; + ycover[Yrl*Ymax + Yml] = 1; + ycover[Ym*Ymax + Yml] = 1; + + ycover[Yax*Ymax + Ymm] = 1; + ycover[Ycx*Ymax + Ymm] = 1; + ycover[Yrx*Ymax + Ymm] = 1; + ycover[Yrl*Ymax + Ymm] = 1; + ycover[Ym*Ymax + Ymm] = 1; + ycover[Ymr*Ymax + Ymm] = 1; + + ycover[Yax*Ymax + Yxm] = 1; + ycover[Ycx*Ymax + Yxm] = 1; + ycover[Yrx*Ymax + Yxm] = 1; + ycover[Yrl*Ymax + Yxm] = 1; + ycover[Ym*Ymax + Yxm] = 1; + ycover[Yxr*Ymax + Yxm] = 1; + + for(i=0; i<D_NONE; i++) { + reg[i] = -1; + if(i >= D_AL && i <= D_R15B) { + reg[i] = (i-D_AL) & 7; + if(i >= D_SPB && i <= D_DIB) + regrex[i] = 0x40; + if(i >= D_R8B && i <= D_R15B) + regrex[i] = Rxr | Rxx | Rxb; } - if(debug['O']) - Bprint(&bso, "\t\t%6ld", lcsize); - v = (p->pc - oldpc) / MINLC; - while(v) { - s = 127; - if(v < 127) - s = v; - cput(s+128); /* 129-255 +pc */ - if(debug['O']) - Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128); - v -= s; - lcsize++; + if(i >= D_AH && i<= D_BH) + reg[i] = 4 + ((i-D_AH) & 7); + if(i >= D_AX && i <= D_R15) { + reg[i] = (i-D_AX) & 7; + if(i >= D_R8) + regrex[i] = Rxr | Rxx | Rxb; } - s = p->line - oldlc; - oldlc = p->line; - oldpc = p->pc + MINLC; - if(s > 64 || s < -64) { - cput(0); /* 0 vv +lc */ - cput(s>>24); - cput(s>>16); - cput(s>>8); - cput(s); - if(debug['O']) { - if(s > 0) - Bprint(&bso, " lc+%ld(%d,%ld)\n", - s, 0, s); - else - Bprint(&bso, " lc%ld(%d,%ld)\n", - s, 0, s); - Bprint(&bso, "%6llux %P\n", - p->pc, p); - } - lcsize += 5; - continue; + if(i >= D_F0 && i <= D_F0+7) + reg[i] = (i-D_F0) & 7; + if(i >= D_M0 && i <= D_M0+7) + reg[i] = (i-D_M0) & 7; + if(i >= D_X0 && i <= D_X0+15) { + reg[i] = (i-D_X0) & 7; + if(i >= D_X0+8) + regrex[i] = Rxr | Rxx | Rxb; } - if(s > 0) { - cput(0+s); /* 1-64 +lc */ - if(debug['O']) { - Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s); - Bprint(&bso, "%6llux %P\n", - p->pc, p); - } - } else { - cput(64-s); /* 65-128 -lc */ - if(debug['O']) { - Bprint(&bso, " lc%ld(%ld)\n", s, 64-s); - Bprint(&bso, "%6llux %P\n", - p->pc, p); - } - } - lcsize++; + if(i >= D_CR+8 && i <= D_CR+15) + regrex[i] = Rxr; } - while(lcsize & 1) { - s = 129; - cput(s); - lcsize++; +} + +int +prefixof(Adr *a) +{ + switch(a->type) { + case D_INDIR+D_CS: + return 0x2e; + case D_INDIR+D_DS: + return 0x3e; + case D_INDIR+D_ES: + return 0x26; + case D_INDIR+D_FS: + return 0x64; + case D_INDIR+D_GS: + return 0x65; } - if(debug['v'] || debug['O']) - Bprint(&bso, "lcsize = %ld\n", lcsize); - Bflush(&bso); + return 0; } int @@ -641,11 +515,11 @@ oclass(Adr *a) } void -asmidx(Adr *a, int base) +asmidx(int scale, int index, int base) { int i; - switch(a->index) { + switch(index) { default: goto bad; @@ -670,10 +544,10 @@ asmidx(Adr *a, int base) case D_BP: case D_SI: case D_DI: - i = reg[(int)a->index] << 3; + i = reg[index] << 3; break; } - switch(a->scale) { + switch(scale) { default: goto bad; case 1: @@ -719,7 +593,7 @@ bas: *andptr++ = i; return; bad: - diag("asmidx: bad address %D", a); + diag("asmidx: bad address %d/%d/%d", scale, index, base); *andptr++ = 0; return; } @@ -727,10 +601,6 @@ bad: static void put4(int32 v) { - if(dlm && curp != P && reloca != nil){ - dynreloc(reloca->sym, curp->pc + andptr - &and[0], 1); - reloca = nil; - } andptr[0] = v; andptr[1] = v>>8; andptr[2] = v>>16; @@ -739,12 +609,25 @@ put4(int32 v) } static void -put8(vlong v) +relput4(Prog *p, Adr *a) { - if(dlm && curp != P && reloca != nil){ - dynreloc(reloca->sym, curp->pc + andptr - &and[0], 1); /* TO DO */ - reloca = nil; + vlong v; + Reloc rel, *r; + + v = vaddr(a, &rel); + if(rel.siz != 0) { + if(rel.siz != 4) + diag("bad reloc"); + r = addrel(cursym); + *r = rel; + r->off = p->pc + andptr - and; } + put4(v); +} + +static void +put8(vlong v) +{ andptr[0] = v; andptr[1] = v>>8; andptr[2] = v>>16; @@ -756,24 +639,41 @@ put8(vlong v) andptr += 8; } +/* +static void +relput8(Prog *p, Adr *a) +{ + vlong v; + Reloc rel, *r; + + v = vaddr(a, &rel); + if(rel.siz != 0) { + r = addrel(cursym); + *r = rel; + r->siz = 8; + r->off = p->pc + andptr - and; + } + put8(v); +} +*/ + vlong symaddr(Sym *s) { - Adr a; - - a.type = D_ADDR; - a.index = D_EXTERN; - a.offset = 0; - a.sym = s; - return vaddr(&a); + if(!s->reachable) + diag("unreachable symbol in symaddr - %s", s->name); + return s->value; } -vlong -vaddr(Adr *a) +static vlong +vaddr(Adr *a, Reloc *r) { int t; vlong v; Sym *s; + + if(r != nil) + memset(r, 0, sizeof *r); t = a->type; v = a->offset; @@ -783,34 +683,18 @@ vaddr(Adr *a) case D_STATIC: case D_EXTERN: s = a->sym; - if(s != nil) { - if(dlm && curp != P) - reloca = a; - switch(s->type) { - case SUNDEF: - ckoff(s, v); - case STEXT: - case SCONST: - if(!s->reachable) - diag("unreachable symbol in vaddr - %s", s->name); - if((uvlong)s->value < (uvlong)INITTEXT) - v += INITTEXT; /* TO DO */ - v += s->value; - break; - case SFIXED: - v += s->value; - break; - case SMACHO: - if(!s->reachable) - sysfatal("unreachable symbol in vaddr - %s", s->name); - v += INITDAT + datsize + s->value; - break; - default: - if(!s->reachable) - diag("unreachable symbol in vaddr - %s", s->name); - v += INITDAT + s->value; - } + if(!s->reachable) + diag("unreachable symbol in vaddr - %s", s->name); + if(r == nil) { + diag("need reloc for %D", a); + errorexit(); } + r->type = D_ADDR; + r->siz = 4; // TODO: 8 for external symbols + r->off = -1; // caller must fill in + r->sym = s; + r->add = v; + v = 0; } return v; } @@ -819,55 +703,51 @@ static void asmandsz(Adr *a, int r, int rex, int m64) { int32 v; - int t; - Adr aa; + int t, scale; + Reloc rel; rex &= (0x40 | Rxr); v = a->offset; t = a->type; + rel.siz = 0; if(a->index != D_NONE) { - if(t >= D_INDIR) { - t -= D_INDIR; - rexflag |= (regrex[(int)a->index] & Rxx) | (regrex[t] & Rxb) | rex; - if(t == D_NONE) { - *andptr++ = (0 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - put4(v); - return; - } - if(v == 0 && t != D_BP && t != D_R13) { - *andptr++ = (0 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - return; - } - if(v >= -128 && v < 128) { - *andptr++ = (1 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - *andptr++ = v; - return; + if(t < D_INDIR) { + switch(t) { + default: + goto bad; + case D_STATIC: + case D_EXTERN: + t = D_NONE; + v = vaddr(a, &rel); + break; + case D_AUTO: + case D_PARAM: + t = D_SP; + break; } - *andptr++ = (2 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - put4(v); + } else + t -= D_INDIR; + rexflag |= (regrex[(int)a->index] & Rxx) | (regrex[t] & Rxb) | rex; + if(t == D_NONE) { + *andptr++ = (0 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + goto putrelv; return; } - switch(t) { - default: - goto bad; - case D_STATIC: - case D_EXTERN: - aa.type = D_NONE+D_INDIR; - break; - case D_AUTO: - case D_PARAM: - aa.type = D_SP+D_INDIR; - break; + if(v == 0 && rel.siz == 0 && t != D_BP && t != D_R13) { + *andptr++ = (0 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + return; } - aa.offset = vaddr(a); - aa.index = a->index; - aa.scale = a->scale; - asmandsz(&aa, r, rex, m64); - return; + if(v >= -128 && v < 128 && rel.siz == 0) { + *andptr++ = (1 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + *andptr++ = v; + return; + } + *andptr++ = (2 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + goto putrelv; } if(t >= D_AL && t <= D_X0+15) { if(v) @@ -876,72 +756,84 @@ asmandsz(Adr *a, int r, int rex, int m64) rexflag |= (regrex[t] & (0x40 | Rxb)) | rex; return; } - if(t >= D_INDIR) { + + scale = a->scale; + if(t < D_INDIR) { + switch(a->type) { + default: + goto bad; + case D_STATIC: + case D_EXTERN: + t = D_NONE; + v = vaddr(a, &rel); + break; + case D_AUTO: + case D_PARAM: + t = D_SP; + break; + } + scale = 1; + } else t -= D_INDIR; - rexflag |= (regrex[t] & Rxb) | rex; - if(t == D_NONE) { - if(asmode != 64){ - *andptr++ = (0 << 6) | (5 << 0) | (r << 3); - put4(v); - return; - } - /* temporary */ - *andptr++ = (0 << 6) | (4 << 0) | (r << 3); /* sib present */ - *andptr++ = (0 << 6) | (4 << 3) | (5 << 0); /* DS:d32 */ - put4(v); + + rexflag |= (regrex[t] & Rxb) | rex; + if(t == D_NONE || (D_CS <= t && t <= D_GS)) { + if(asmode != 64){ + *andptr++ = (0 << 6) | (5 << 0) | (r << 3); + goto putrelv; + } + /* temporary */ + *andptr++ = (0 << 6) | (4 << 0) | (r << 3); /* sib present */ + *andptr++ = (0 << 6) | (4 << 3) | (5 << 0); /* DS:d32 */ + goto putrelv; + } + if(t == D_SP || t == D_R12) { + if(v == 0) { + *andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); + asmidx(scale, D_NONE, t); return; } - if(t == D_SP || t == D_R12) { - if(v == 0) { - *andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); - asmidx(a, t); - return; - } - if(v >= -128 && v < 128) { - *andptr++ = (1 << 6) | (reg[t] << 0) | (r << 3); - asmidx(a, t); - *andptr++ = v; - return; - } - *andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3); - asmidx(a, t); - put4(v); + if(v >= -128 && v < 128) { + *andptr++ = (1 << 6) | (reg[t] << 0) | (r << 3); + asmidx(scale, D_NONE, t); + *andptr++ = v; return; } - if(t >= D_AX && t <= D_R15) { - if(v == 0 && t != D_BP && t != D_R13) { - *andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); - return; - } - if(v >= -128 && v < 128) { - andptr[0] = (1 << 6) | (reg[t] << 0) | (r << 3); - andptr[1] = v; - andptr += 2; - return; - } - *andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3); - put4(v); + *andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3); + asmidx(scale, D_NONE, t); + goto putrelv; + } + if(t >= D_AX && t <= D_R15) { + if(v == 0 && t != D_BP && t != D_R13) { + *andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); return; } - goto bad; + if(v >= -128 && v < 128) { + andptr[0] = (1 << 6) | (reg[t] << 0) | (r << 3); + andptr[1] = v; + andptr += 2; + return; + } + *andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3); + goto putrelv; } - switch(a->type) { - default: - goto bad; - case D_STATIC: - case D_EXTERN: - aa.type = D_NONE+D_INDIR; - break; - case D_AUTO: - case D_PARAM: - aa.type = D_SP+D_INDIR; - break; + goto bad; + +putrelv: + if(rel.siz != 0) { + Reloc *r; + + if(rel.siz != 4) { + diag("bad rel"); + goto bad; + } + r = addrel(cursym); + *r = rel; + r->off = curp->pc + andptr - and; } - aa.index = D_NONE; - aa.scale = 1; - aa.offset = vaddr(a); - asmandsz(&aa, r, rex, m64); + put4(v); return; + bad: diag("asmand: bad address %D", a); return; @@ -1173,14 +1065,25 @@ doasm(Prog *p) Prog *q, pp; uchar *t; Movtab *mo; - int z, op, ft, tt, xo, l; + int z, op, ft, tt, xo, l, pre; vlong v; + Reloc rel, *r; + Adr *a; + + curp = p; // TODO o = opindex[p->as]; if(o == nil) { diag("asmins: missing op %P", p); return; } + + pre = prefixof(&p->from); + if(pre) + *andptr++ = pre; + pre = prefixof(&p->to); + if(pre) + *andptr++ = pre; if(p->ft == 0) p->ft = oclass(&p->from); @@ -1244,7 +1147,7 @@ found: diag("asmins: illegal in %d-bit mode: %P", p->mode, p); break; } - v = vaddr(&p->from); + op = o->op[z]; if(op == 0x0f) { *andptr++ = op; @@ -1350,64 +1253,74 @@ found: break; case Zm_ibo: - v = vaddr(&p->to); *andptr++ = op; asmando(&p->from, o->op[z+1]); - *andptr++ = v; + *andptr++ = vaddr(&p->to, nil); break; case Zibo_m: *andptr++ = op; asmando(&p->to, o->op[z+1]); - *andptr++ = v; + *andptr++ = vaddr(&p->from, nil); break; case Zibo_m_xm: z = mediaop(o, op, t[3], z); asmando(&p->to, o->op[z+1]); - *andptr++ = v; + *andptr++ = vaddr(&p->from, nil); break; case Z_ib: - v = vaddr(&p->to); case Zib_: + if(t[2] == Zib_) + a = &p->from; + else + a = &p->to; *andptr++ = op; - *andptr++ = v; + *andptr++ = vaddr(a, nil); break; case Zib_rp: rexflag |= regrex[p->to.type] & (Rxb|0x40); *andptr++ = op + reg[p->to.type]; - *andptr++ = v; + *andptr++ = vaddr(&p->from, nil); break; case Zil_rp: rexflag |= regrex[p->to.type] & Rxb; *andptr++ = op + reg[p->to.type]; if(o->prefix == Pe) { + v = vaddr(&p->from, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, &p->from); break; case Zo_iw: *andptr++ = op; if(p->from.type != D_NONE){ + v = vaddr(&p->from, nil); *andptr++ = v; *andptr++ = v>>8; } break; case Ziq_rp: + v = vaddr(&p->from, &rel); l = v>>32; - if(l == 0){ + if(l == 0 && rel.siz != 8){ //p->mark |= 0100; //print("zero: %llux %P\n", v, p); rexflag &= ~(0x40|Rxw); rexflag |= regrex[p->to.type] & Rxb; *andptr++ = 0xb8 + reg[p->to.type]; + if(rel.type != 0) { + r = addrel(cursym); + *r = rel; + r->off = p->pc + andptr - and; + } put4(v); }else if(l == -1 && (v&((uvlong)1<<31))!=0){ /* sign extend */ //p->mark |= 0100; @@ -1419,6 +1332,11 @@ found: //print("all: %llux %P\n", v, p); rexflag |= regrex[p->to.type] & Rxb; *andptr++ = op + reg[p->to.type]; + if(rel.type != 0) { + r = addrel(cursym); + *r = rel; + r->off = p->pc + andptr - and; + } put8(v); } break; @@ -1426,53 +1344,54 @@ found: case Zib_rr: *andptr++ = op; asmand(&p->to, &p->to); - *andptr++ = v; + *andptr++ = vaddr(&p->from, nil); break; case Z_il: - v = vaddr(&p->to); case Zil_: - *andptr++ = op; - if(o->prefix == Pe) { - *andptr++ = v; - *andptr++ = v>>8; - } + if(t[2] == Zil_) + a = &p->from; else - put4(v); - break; - - case Zm_ilo: - v = vaddr(&p->to); + a = &p->to; *andptr++ = op; - asmando(&p->from, o->op[z+1]); if(o->prefix == Pe) { + v = vaddr(a, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, a); break; + case Zm_ilo: case Zilo_m: *andptr++ = op; - asmando(&p->to, o->op[z+1]); + if(t[2] == Zilo_m) { + a = &p->from; + asmando(&p->to, o->op[z+1]); + } else { + a = &p->to; + asmando(&p->from, o->op[z+1]); + } if(o->prefix == Pe) { + v = vaddr(a, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, a); break; case Zil_rr: *andptr++ = op; asmand(&p->to, &p->to); if(o->prefix == Pe) { + v = vaddr(&p->from, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, &p->from); break; case Z_rp: @@ -1490,74 +1409,132 @@ found: asmand(&p->to, &p->to); break; - case Zbr: + case Zcall: q = p->pcond; - if(q) { - v = q->pc - p->pc - 2; - if(v >= -128 && v <= 127) { - *andptr++ = op; - *andptr++ = v; - } else { - v -= 6-2; - *andptr++ = 0x0f; - *andptr++ = o->op[z+1]; - *andptr++ = v; - *andptr++ = v>>8; - *andptr++ = v>>16; - *andptr++ = v>>24; - } + if(q == nil) { + diag("call without target"); + errorexit(); + } + if(q->as != ATEXT) { + // Could handle this case by making D_PCREL + // record the Prog* instead of the Sym*, but let's + // wait until the need arises. + diag("call of non-TEXT %P", q); + errorexit(); } + *andptr++ = op; + r = addrel(cursym); + r->off = p->pc + andptr - and; + r->sym = q->from.sym; + r->type = D_PCREL; + r->siz = 4; + put4(0); break; - case Zcall: + case Zbr: + case Zjmp: + // TODO: jump across functions needs reloc q = p->pcond; - if(q) { - v = q->pc - p->pc - 5; - if(dlm && curp != P && p->to.sym->type == SUNDEF){ - /* v = 0 - p->pc - 5; */ - v = 0; - ckoff(p->to.sym, v); - v += p->to.sym->value; - dynreloc(p->to.sym, p->pc+1, 0); + if(q == nil) { + diag("jmp/branch without target"); + errorexit(); + } + if(q->as == ATEXT) { + if(t[2] == Zbr) { + diag("branch to ATEXT"); + errorexit(); } - *andptr++ = op; - *andptr++ = v; - *andptr++ = v>>8; - *andptr++ = v>>16; - *andptr++ = v>>24; + *andptr++ = o->op[z+1]; + r = addrel(cursym); + r->off = p->pc + andptr - and; + r->sym = q->from.sym; + r->type = D_PCREL; + r->siz = 4; + put4(0); + break; } - break; + // Assumes q is in this function. + // TODO: Check in input, preserve in brchain. - case Zjmp: - q = p->pcond; - if(q) { - v = q->pc - p->pc - 2; - if(v >= -128 && v <= 127) { + // Fill in backward jump now. + if(p->back & 1) { + v = q->pc - (p->pc + 2); + if(v >= -128) { *andptr++ = op; *andptr++ = v; } else { v -= 5-2; + if(t[2] == Zbr) { + *andptr++ = 0x0f; + v--; + } *andptr++ = o->op[z+1]; *andptr++ = v; *andptr++ = v>>8; *andptr++ = v>>16; *andptr++ = v>>24; } + break; + } + + // Annotate target; will fill in later. + p->forwd = q->comefrom; + q->comefrom = p; + if(p->back & 2) { // short + *andptr++ = op; + *andptr++ = 0; + } else { + if(t[2] == Zbr) + *andptr++ = 0x0f; + *andptr++ = o->op[z+1]; + *andptr++ = 0; + *andptr++ = 0; + *andptr++ = 0; + *andptr++ = 0; } break; + +/* + v = q->pc - p->pc - 2; + if((v >= -128 && v <= 127) || p->pc == -1 || q->pc == -1) { + *andptr++ = op; + *andptr++ = v; + } else { + v -= 5-2; + if(t[2] == Zbr) { + *andptr++ = 0x0f; + v--; + } + *andptr++ = o->op[z+1]; + *andptr++ = v; + *andptr++ = v>>8; + *andptr++ = v>>16; + *andptr++ = v>>24; + } +*/ + break; case Zloop: q = p->pcond; - if(q) { - v = q->pc - p->pc - 2; - if(v < -128 && v > 127) - diag("loop too far: %P", p); - *andptr++ = op; - *andptr++ = v; + if(q == nil) { + diag("loop without target"); + errorexit(); } + v = q->pc - p->pc - 2; + if(v < -128 && v > 127) + diag("loop too far: %P", p); + *andptr++ = op; + *andptr++ = v; break; case Zbyte: + v = vaddr(&p->from, &rel); + if(rel.siz != 0) { + rel.siz = op; + r = addrel(cursym); + *r = rel; + r->off = p->pc + andptr - and; + } *andptr++ = v; if(op > 1) { *andptr++ = v>>8; @@ -1730,6 +1707,7 @@ void asmins(Prog *p) { int n, np, c; + Reloc *r; rexflag = 0; andptr = and; @@ -1739,7 +1717,7 @@ asmins(Prog *p) /* * as befits the whole approach of the architecture, * the rex prefix must appear before the first opcode byte - * (and thus after any 66/67/f2/f3 prefix bytes, but + * (and thus after any 66/67/f2/f3/26/2e/3e prefix bytes, but * before the 0f opcode escape!), or it might be ignored. * note that the handbook often misleadingly shows 66/f2/f3 in `opcode'. */ @@ -1748,164 +1726,16 @@ asmins(Prog *p) n = andptr - and; for(np = 0; np < n; np++) { c = and[np]; - if(c != 0x66 && c != 0xf2 && c != 0xf3 && c != 0x67) + if(c != 0xf2 && c != 0xf3 && (c < 0x64 || c > 0x67) && c != 0x2e && c != 0x3e && c != 0x26) break; } + for(r=cursym->r+cursym->nr; r-- > cursym->r; ) { + if(r->off < p->pc) + break; + r->off++; + } memmove(and+np+1, and+np, n-np); and[np] = 0x40 | rexflag; andptr++; } } - -enum{ - ABSD = 0, - ABSU = 1, - RELD = 2, - RELU = 3, -}; - -int modemap[4] = { 0, 1, -1, 2, }; - -typedef struct Reloc Reloc; - -struct Reloc -{ - int n; - int t; - uchar *m; - uint32 *a; -}; - -Reloc rels; - -static void -grow(Reloc *r) -{ - int t; - uchar *m, *nm; - uint32 *a, *na; - - t = r->t; - r->t += 64; - m = r->m; - a = r->a; - r->m = nm = mal(r->t*sizeof(uchar)); - r->a = na = mal(r->t*sizeof(uint32)); - memmove(nm, m, t*sizeof(uchar)); - memmove(na, a, t*sizeof(uint32)); - free(m); - free(a); -} - -void -dynreloc(Sym *s, uint32 v, int abs) -{ - int i, k, n; - uchar *m; - uint32 *a; - Reloc *r; - - if(s->type == SUNDEF) - k = abs ? ABSU : RELU; - else - k = abs ? ABSD : RELD; - /* Bprint(&bso, "R %s a=%ld(%lx) %d\n", s->name, v, v, k); */ - k = modemap[k]; - r = &rels; - n = r->n; - if(n >= r->t) - grow(r); - m = r->m; - a = r->a; - for(i = n; i > 0; i--){ - if(v < a[i-1]){ /* happens occasionally for data */ - m[i] = m[i-1]; - a[i] = a[i-1]; - } - else - break; - } - m[i] = k; - a[i] = v; - r->n++; -} - -static int -sput(char *s) -{ - char *p; - - p = s; - while(*s) - cput(*s++); - cput(0); - return s-p+1; -} - -void -asmdyn() -{ - int i, n, t, c; - Sym *s; - uint32 la, ra, *a; - vlong off; - uchar *m; - Reloc *r; - - cflush(); - off = seek(cout, 0, 1); - lputb(0); - t = 0; - lputb(imports); - t += 4; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->type == SUNDEF){ - lputb(s->sig); - t += 4; - t += sput(s->name); - } - - la = 0; - r = &rels; - n = r->n; - m = r->m; - a = r->a; - lputb(n); - t += 4; - for(i = 0; i < n; i++){ - ra = *a-la; - if(*a < la) - diag("bad relocation order"); - if(ra < 256) - c = 0; - else if(ra < 65536) - c = 1; - else - c = 2; - cput((c<<6)|*m++); - t++; - if(c == 0){ - cput(ra); - t++; - } - else if(c == 1){ - wputb(ra); - t += 2; - } - else{ - lputb(ra); - t += 4; - } - la = *a++; - } - - cflush(); - seek(cout, off, 0); - lputb(t); - - if(debug['v']){ - Bprint(&bso, "import table entries = %d\n", imports); - Bprint(&bso, "export table entries = %d\n", exports); - } -} diff --git a/src/cmd/8a/Makefile b/src/cmd/8a/Makefile index beb575544..78d361dbd 100644 --- a/src/cmd/8a/Makefile +++ b/src/cmd/8a/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 8a\ +TARG=8a HFILES=\ a.h\ @@ -20,21 +20,6 @@ OFILES=\ YFILES=\ a.y\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lbio -l9 - -$(OFILES): $(HFILES) +include ../../Make.ccmd lex.$O: ../cc/macbody ../cc/lexbody - -y.tab.h: $(YFILES) - bison -y $(YFLAGS) $(YFILES) - -y.tab.c: y.tab.h - test -f y.tab.c && touch y.tab.c - -clean: - rm -f *.$O $(TARG) *.6 enam.c 6.out a.out y.tab.h y.tab.c - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) diff --git a/src/cmd/8a/a.h b/src/cmd/8a/a.h index 035db2551..fe6b17280 100644 --- a/src/cmd/8a/a.h +++ b/src/cmd/8a/a.h @@ -1,7 +1,7 @@ // Inferno utils/8a/a.h // http://code.google.com/p/inferno-os/source/browse/utils/8a/a.h // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -50,7 +50,7 @@ typedef struct Ref Ref; typedef struct Gen Gen; typedef struct Io Io; typedef struct Hist Hist; -typedef struct Gen2 Gen2; +typedef struct Gen2 Gen2; #define MAXALIGN 7 #define FPCHIP 1 @@ -162,6 +162,7 @@ EXTERN int pass; EXTERN char* pathname; EXTERN int32 pc; EXTERN int peekc; +EXTERN int32 stmtline; EXTERN int sym; EXTERN char* symb; EXTERN int thechar; @@ -169,9 +170,9 @@ EXTERN char* thestring; EXTERN int32 thunk; EXTERN Biobuf obuf; -void* alloc(int32); +void* alloc(int32); void* allocn(void*, int32, int32); -void ensuresymb(int32); +void ensuresymb(int32); void errorexit(void); void pushio(void); void newio(void); diff --git a/src/cmd/8a/a.y b/src/cmd/8a/a.y index 8bc96cce5..04662f83d 100644 --- a/src/cmd/8a/a.y +++ b/src/cmd/8a/a.y @@ -1,7 +1,7 @@ // Inferno utils/8a/a.y // http://code.google.com/p/inferno-os/source/browse/utils/8a/a.y // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -64,7 +64,11 @@ %type <gen2> spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 %% prog: -| prog line +| prog + { + stmtline = lineno; + } + line line: LLAB ':' diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c index c8127bde9..bf298b266 100644 --- a/src/cmd/8a/lex.c +++ b/src/cmd/8a/lex.c @@ -1,7 +1,7 @@ // Inferno utils/8a/lex.c // http://code.google.com/p/inferno-os/source/browse/utils/8a/lex.c // -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) @@ -925,10 +925,10 @@ jackpot: } Bputc(&obuf, a); Bputc(&obuf, a>>8); - Bputc(&obuf, lineno); - Bputc(&obuf, lineno>>8); - Bputc(&obuf, lineno>>16); - Bputc(&obuf, lineno>>24); + Bputc(&obuf, stmtline); + Bputc(&obuf, stmtline>>8); + Bputc(&obuf, stmtline>>16); + Bputc(&obuf, stmtline>>24); zaddr(&g2->from, sf); zaddr(&g2->to, st); diff --git a/src/cmd/8c/Makefile b/src/cmd/8c/Makefile index 85ea3013b..60f46d3c9 100644 --- a/src/cmd/8c/Makefile +++ b/src/cmd/8c/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 8c\ +TARG=8c HFILES=\ gc.h\ @@ -29,18 +29,9 @@ OFILES=\ ../8l/enam.$O\ LIB=\ - ../cc/cc.a$O + ../cc/cc.a\ -$(TARG): $(OFILES) $(LIB) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lm -lbio -l9 - -$(OFILES): $(HFILES) - -clean: - rm -f *.$O $(TARG) *.8 8.out a.out - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +include ../../Make.ccmd %.$O: ../cc/%.c - $(CC) $(CFLAGS) -c -I. -o $@ ../cc/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../cc/$*.c diff --git a/src/cmd/8c/cgen64.c b/src/cmd/8c/cgen64.c index ce1512c51..3424f762c 100644 --- a/src/cmd/8c/cgen64.c +++ b/src/cmd/8c/cgen64.c @@ -57,7 +57,7 @@ vaddr(Node *n, int a) int32 hi64v(Node *n) { - if(align(0, types[TCHAR], Aarg1)) /* isbigendian */ + if(align(0, types[TCHAR], Aarg1, nil)) /* isbigendian */ return (int32)(n->vconst) & ~0L; else return (int32)((uvlong)n->vconst>>32) & ~0L; @@ -66,7 +66,7 @@ hi64v(Node *n) int32 lo64v(Node *n) { - if(align(0, types[TCHAR], Aarg1)) /* isbigendian */ + if(align(0, types[TCHAR], Aarg1, nil)) /* isbigendian */ return (int32)((uvlong)n->vconst>>32) & ~0L; else return (int32)(n->vconst) & ~0L; diff --git a/src/cmd/8c/div.c b/src/cmd/8c/div.c index 538e3522c..14945052e 100644 --- a/src/cmd/8c/div.c +++ b/src/cmd/8c/div.c @@ -120,7 +120,7 @@ sdivgen(Node *l, Node *r, Node *ax, Node *dx) if(c < 0) c = -c; a = sdiv(c, &m, &s); -//print("a=%d i=%ld s=%d m=%lux\n", a, (int32)r->vconst, s, m); +//print("a=%d i=%d s=%d m=%ux\n", a, (int32)r->vconst, s, m); gins(AMOVL, nodconst(m), ax); gins(AIMULL, l, Z); gins(AMOVL, l, ax); @@ -141,7 +141,7 @@ udivgen(Node *l, Node *r, Node *ax, Node *dx) Node nod; a = udiv(r->vconst, &m, &s, &t); -//print("a=%ud i=%ld p=%d s=%d m=%lux\n", a, (int32)r->vconst, t, s, m); +//print("a=%ud i=%d p=%d s=%d m=%ux\n", a, (int32)r->vconst, t, s, m); if(t != 0) { gins(AMOVL, l, ax); gins(ASHRL, nodconst(t), ax); diff --git a/src/cmd/8c/list.c b/src/cmd/8c/list.c index 6caafd258..c422905cd 100644 --- a/src/cmd/8c/list.c +++ b/src/cmd/8c/list.c @@ -76,15 +76,27 @@ Pconv(Fmt *fp) Prog *p; p = va_arg(fp->args, Prog*); - if(p->as == ADATA) - sprint(str, " %A %D/%d,%D", - p->as, &p->from, p->from.scale, &p->to); - else if(p->as == ATEXT) - sprint(str, " %A %D,%d,%D", - p->as, &p->from, p->from.scale, &p->to); - else - sprint(str, " %A %D,%D", - p->as, &p->from, &p->to); + switch(p->as) { + case ADATA: + sprint(str, "(%L) %A %D/%d,%D", + p->lineno, p->as, &p->from, p->from.scale, &p->to); + break; + + case ATEXT: + if(p->from.scale) { + sprint(str, "(%L) %A %D,%d,%lD", + p->lineno, p->as, &p->from, p->from.scale, &p->to); + break; + } + sprint(str, "(%L) %A %D,%lD", + p->lineno, p->as, &p->from, &p->to); + break; + + default: + sprint(str, "(%L) %A %D,%lD", + p->lineno, p->as, &p->from, &p->to); + break; + } return fmtstrcpy(fp, str); } diff --git a/src/cmd/8c/mul.c b/src/cmd/8c/mul.c index 2b7332d54..a0742807e 100644 --- a/src/cmd/8c/mul.c +++ b/src/cmd/8c/mul.c @@ -355,7 +355,7 @@ mulgen1(uint32 v, Node *n) mulparam(v, p); found: -// print("v=%.lx a=%d n=%d s=%d g=%d o=%d \n", p->value, p->alg, p->neg, p->shift, p->arg, p->off); +// print("v=%.x a=%d n=%d s=%d g=%d o=%d \n", p->value, p->alg, p->neg, p->shift, p->arg, p->off); if(p->alg < 0) return 0; diff --git a/src/cmd/8c/reg.c b/src/cmd/8c/reg.c index 837da2d04..6ba07bed2 100644 --- a/src/cmd/8c/reg.c +++ b/src/cmd/8c/reg.c @@ -382,7 +382,7 @@ regopt(Prog *p) if(debug['R'] && debug['v']) { print("\nlooping structure:\n"); for(r = firstr; r != R; r = r->link) { - print("%ld:%P", r->loop, r->prog); + print("%d:%P", r->loop, r->prog); for(z=0; z<BITS; z++) bit.b[z] = r->use1.b[z] | r->use2.b[z] | @@ -1031,7 +1031,7 @@ paint1(Reg *r, int bn) if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) { change -= CLOAD * r->loop; if(debug['R'] && debug['v']) - print("%ld%P\tld %B $%d\n", r->loop, + print("%d%P\td %B $%d\n", r->loop, r->prog, blsh(bn), change); } for(;;) { @@ -1044,7 +1044,7 @@ paint1(Reg *r, int bn) if(BtoR(bb) != D_F0) change = -CINF; if(debug['R'] && debug['v']) - print("%ld%P\tu1 %B $%d\n", r->loop, + print("%d%P\tu1 %B $%d\n", r->loop, p, blsh(bn), change); } @@ -1054,7 +1054,7 @@ paint1(Reg *r, int bn) if(BtoR(bb) != D_F0) change = -CINF; if(debug['R'] && debug['v']) - print("%ld%P\tu2 %B $%d\n", r->loop, + print("%d%P\tu2 %B $%d\n", r->loop, p, blsh(bn), change); } @@ -1064,7 +1064,7 @@ paint1(Reg *r, int bn) if(BtoR(bb) != D_F0) change = -CINF; if(debug['R'] && debug['v']) - print("%ld%P\tst %B $%d\n", r->loop, + print("%d%P\tst %B $%d\n", r->loop, p, blsh(bn), change); } diff --git a/src/cmd/8c/swt.c b/src/cmd/8c/swt.c index 1c502f5ff..be48885f8 100644 --- a/src/cmd/8c/swt.c +++ b/src/cmd/8c/swt.c @@ -40,7 +40,7 @@ swit1(C1 *q, int nc, int32 def, Node *n) if(nc < 5) { for(i=0; i<nc; i++) { if(debug['W']) - print("case = %.8lux\n", q->val); + print("case = %.8ux\n", q->val); gopcode(OEQ, n->type, n, nodconst(q->val)); patch(p, q->label); q++; @@ -52,7 +52,7 @@ swit1(C1 *q, int nc, int32 def, Node *n) i = nc / 2; r = q+i; if(debug['W']) - print("case > %.8lux\n", r->val); + print("case > %.8ux\n", r->val); gopcode(OGT, n->type, n, nodconst(r->val)); sp = p; gbranch(OGOTO); @@ -61,7 +61,7 @@ swit1(C1 *q, int nc, int32 def, Node *n) swit1(q, i, def, n); if(debug['W']) - print("case < %.8lux\n", r->val); + print("case < %.8ux\n", r->val); patch(sp, pc); swit1(r+1, nc-i-1, def, n); } @@ -501,7 +501,7 @@ zaddr(Biobuf *b, Adr *a, int s) } int32 -align(int32 i, Type *t, int op) +align(int32 i, Type *t, int op, int32 *maxalign) { int32 o; Type *v; @@ -515,7 +515,9 @@ align(int32 i, Type *t, int op) break; case Asu2: /* padding at end of a struct */ - w = SZ_LONG; + w = *maxalign; + if(w < 1) + w = 1; if(packflg) w = packflg; break; @@ -523,10 +525,16 @@ align(int32 i, Type *t, int op) case Ael1: /* initial align of struct element */ for(v=t; v->etype==TARRAY; v=v->link) ; - w = ewidth[v->etype]; - if(w <= 0 || w >= SZ_LONG) - w = SZ_LONG; - if(packflg) + if(v->etype == TSTRUCT || v->etype == TUNION) + w = v->align; + else { + w = ewidth[v->etype]; + if(w == 8) + w = 4; + } + if(w < 1 || w > SZ_LONG) + fatal(Z, "align"); + if(packflg) w = packflg; break; @@ -536,8 +544,8 @@ align(int32 i, Type *t, int op) case Aarg0: /* initial passbyptr argument in arg list */ if(typesuv[t->etype]) { - o = align(o, types[TIND], Aarg1); - o = align(o, types[TIND], Aarg2); + o = align(o, types[TIND], Aarg1, nil); + o = align(o, types[TIND], Aarg2, nil); } break; @@ -558,13 +566,15 @@ align(int32 i, Type *t, int op) break; case Aaut3: /* total align of automatic */ - o = align(o, t, Ael1); - o = align(o, t, Ael2); + o = align(o, t, Ael1, nil); + o = align(o, t, Ael2, nil); break; } o = xround(o, w); + if(maxalign && *maxalign < w) + *maxalign = w; if(debug['A']) - print("align %s %ld %T = %ld\n", bnames[op], i, t, o); + print("align %s %d %T = %d\n", bnames[op], i, t, o); return o; } diff --git a/src/cmd/8c/txt.c b/src/cmd/8c/txt.c index 194599c3a..0dd387d11 100644 --- a/src/cmd/8c/txt.c +++ b/src/cmd/8c/txt.c @@ -385,7 +385,7 @@ err: void regsalloc(Node *n, Node *nn) { - cursafe = align(cursafe, nn->type, Aaut3); + cursafe = align(cursafe, nn->type, Aaut3, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); *n = *nodsafe; n->xoffset = -(stkoff + cursafe); @@ -399,22 +399,22 @@ regaalloc1(Node *n, Node *nn) { nodreg(n, nn, REGARG); reg[REGARG]++; - curarg = align(curarg, nn->type, Aarg1); - curarg = align(curarg, nn->type, Aarg2); + curarg = align(curarg, nn->type, Aarg1, nil); + curarg = align(curarg, nn->type, Aarg2, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); } void regaalloc(Node *n, Node *nn) { - curarg = align(curarg, nn->type, Aarg1); + curarg = align(curarg, nn->type, Aarg1, nil); *n = *nn; n->op = OINDREG; n->reg = REGSP; n->xoffset = curarg; n->complex = 0; n->addable = 20; - curarg = align(curarg, nn->type, Aarg2); + curarg = align(curarg, nn->type, Aarg2, nil); maxargsafe = maxround(maxargsafe, cursafe+curarg); } @@ -1403,7 +1403,6 @@ exreg(Type *t) return o+1; // +1 to avoid 0 == failure; naddr case OEXREG will -1. } - USED(t); return 0; } diff --git a/src/cmd/8g/Makefile b/src/cmd/8g/Makefile index d2431182f..09cf8d4e3 100644 --- a/src/cmd/8g/Makefile +++ b/src/cmd/8g/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 8g +TARG=8g HFILES=\ ../gc/go.h\ @@ -27,18 +27,9 @@ OFILES=\ reg.$O\ LIB=\ - ../gc/gc.a$O + ../gc/gc.a\ -$(TARG): $(OFILES) $(LIB) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lbio -l9 -lm - -$(OFILES): $(HFILES) - -clean: - rm -f *.$O $(TARG) *.8 enam.c 8.out a.out - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +include ../../Make.ccmd %.$O: ../gc/%.c - $(CC) $(CFLAGS) -c -I. -o $@ ../gc/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../gc/$*.c diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c index 8fbdc6ee7..875d434fa 100644 --- a/src/cmd/8g/cgen.c +++ b/src/cmd/8g/cgen.c @@ -175,7 +175,7 @@ cgen(Node *n, Node *res) case OREAL: case OIMAG: case OCMPLX: - // TODO compile complex + fatal("unexpected complex"); return; // these call bgen to get a bool value @@ -230,8 +230,8 @@ cgen(Node *n, Node *res) cgen(nl, res); break; } - mgen(nl, &n1, res); tempname(&n2, n->type); + mgen(nl, &n1, res); gmove(&n1, &n2); gmove(&n2, res); mfree(&n1); @@ -392,23 +392,16 @@ uop: // unary gmove(&n1, res); return; -flt: // floating-point. 387 (not SSE2) to interoperate with 6c +flt: // floating-point. 387 (not SSE2) to interoperate with 8c nodreg(&f0, nl->type, D_F0); nodreg(&f1, n->type, D_F0+1); if(nr != N) goto flt2; - if(n->op == OMINUS) { - nr = nodintconst(-1); - convlit(&nr, n->type); - n->op = OMUL; - goto flt2; - } - // unary cgen(nl, &f0); if(n->op != OCONV && n->op != OPLUS) - gins(foptoas(n->op, n->type, 0), &f0, &f0); + gins(foptoas(n->op, n->type, 0), N, N); gmove(&f0, res); return; @@ -525,9 +518,11 @@ agen(Node *n, Node *res) p2 = nil; // to be patched to panicindex. w = n->type->width; if(nr->addable) { - agenr(nl, &n3, res); - if(!isconst(nr, CTINT)) { + if(!isconst(nr, CTINT)) tempname(&tmp, types[TINT32]); + if(!isconst(nl, CTSTR)) + agenr(nl, &n3, res); + if(!isconst(nr, CTINT)) { p2 = cgenindex(nr, &tmp); regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); @@ -539,13 +534,16 @@ agen(Node *n, Node *res) regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); } - regalloc(&n3, types[tptr], res); - agen(nl, &n3); + if(!isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + agen(nl, &n3); + } } else { tempname(&tmp, types[TINT32]); p2 = cgenindex(nr, &tmp); nr = &tmp; - agenr(nl, &n3, res); + if(!isconst(nl, CTSTR)) + agenr(nl, &n3, res); regalloc(&n1, tmp.type, N); gins(optoas(OAS, tmp.type), &tmp, &n1); } @@ -556,7 +554,7 @@ agen(Node *n, Node *res) // explicit check for nil if array is large enough // that we might derive too big a pointer. - if(!isslice(nl->type) && nl->type->width >= unmappedzero) { + if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) { regalloc(&n4, types[tptr], &n3); gmove(&n3, &n4); n4.op = OINDREG; @@ -571,9 +569,10 @@ agen(Node *n, Node *res) // constant index if(isconst(nr, CTINT)) { + if(isconst(nl, CTSTR)) + fatal("constant string constant index"); v = mpgetfix(nr->val.u.xval); - if(isslice(nl->type)) { - + if(isslice(nl->type) || nl->type->etype == TSTRING) { if(!debug['B'] && !n->etype) { n1 = n3; n1.op = OINDREG; @@ -591,13 +590,6 @@ agen(Node *n, Node *res) n1.type = types[tptr]; n1.xoffset = Array_array; gmove(&n1, &n3); - } else - if(!debug['B'] && !n->etype) { - if(v < 0) - yyerror("out of bounds on array"); - else - if(v >= nl->type->bound) - yyerror("out of bounds on array"); } nodconst(&n2, types[tptr], v*w); @@ -614,7 +606,9 @@ agen(Node *n, Node *res) if(!debug['B'] && !n->etype) { // check bounds - if(isslice(nl->type)) { + if(isconst(nl, CTSTR)) + nodconst(&n1, types[TUINT32], nl->val.u.sval->len); + else if(isslice(nl->type) || nl->type->etype == TSTRING) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; @@ -628,8 +622,17 @@ agen(Node *n, Node *res) ginscall(panicindex, 0); patch(p1, pc); } + + if(isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + p1 = gins(ALEAL, N, &n3); + datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from); + p1->from.scale = 1; + p1->from.index = n2.val.u.reg; + goto indexdone; + } - if(isslice(nl->type)) { + if(isslice(nl->type) || nl->type->etype == TSTRING) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; @@ -649,6 +652,7 @@ agen(Node *n, Node *res) gmove(&n3, res); } + indexdone: gmove(&n3, res); regfree(&n2); regfree(&n3); @@ -724,8 +728,14 @@ igen(Node *n, Node *a, Node *res) { Node n1; + // release register for now, to avoid + // confusing tempname. + if(res != N && res->op == OREGISTER) + reg[res->val.u.reg]--; tempname(&n1, types[tptr]); agen(n, &n1); + if(res != N && res->op == OREGISTER) + reg[res->val.u.reg]++; regalloc(a, types[tptr], res); gmove(&n1, a); a->op = OINDREG; @@ -768,6 +778,9 @@ bgen(Node *n, int true, Prog *to) if(n == N) n = nodbool(1); + if(n->ninit != nil) + genlist(n->ninit); + nl = n->left; nr = n->right; diff --git a/src/cmd/8g/galign.c b/src/cmd/8g/galign.c index 346647205..1c14dfe47 100644 --- a/src/cmd/8g/galign.c +++ b/src/cmd/8g/galign.c @@ -25,7 +25,6 @@ Typedef typedefs[] = void betypeinit(void) { - maxround = 4; widthptr = 4; zprog.link = P; diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c index 8a55ffd59..4dcbd4489 100644 --- a/src/cmd/8g/ggen.c +++ b/src/cmd/8g/ggen.c @@ -8,7 +8,6 @@ #include "opt.h" static Prog *pret; -static Node *naclnop; void compile(Node *fn) @@ -24,7 +23,6 @@ compile(Node *fn) newproc = sysfunc("newproc"); deferproc = sysfunc("deferproc"); deferreturn = sysfunc("deferreturn"); - naclnop = sysfunc("naclnop"); panicindex = sysfunc("panicindex"); panicslice = sysfunc("panicslice"); throwreturn = sysfunc("throwreturn"); @@ -64,6 +62,8 @@ compile(Node *fn) pl = newplist(); pl->name = curfn->nname; + setlineno(curfn); + nodconst(&nod1, types[TINT32], 0); ptxt = gins(ATEXT, curfn->nname, &nod1); afunclit(&ptxt->from); @@ -85,6 +85,8 @@ compile(Node *fn) checklabels(); if(nerrors != 0) goto ret; + if(curfn->endlineno) + lineno = curfn->endlineno; if(curfn->type->outtuple != 0) ginscall(throwreturn, 0); @@ -92,21 +94,13 @@ compile(Node *fn) if(pret) patch(pret, pc); ginit(); + if(hasdefer) + ginscall(deferreturn, 0); if(curfn->exit) genlist(curfn->exit); gclean(); if(nerrors != 0) goto ret; - if(hasdefer) { - // On Native client, insert call to no-op function - // to force alignment immediately before call to deferreturn, - // so that when jmpdefer subtracts 5 from the second CALL's - // return address and then the return masks off the low bits, - // we'll back up to the NOPs immediately after the dummy CALL. - if(strcmp(getgoos(), "nacl") == 0) - ginscall(naclnop, 0); - ginscall(deferreturn, 0); - } pc->as = ARET; // overwrite AEND pc->lineno = lineno; @@ -114,12 +108,12 @@ compile(Node *fn) regopt(ptxt); } // fill in argument size - ptxt->to.offset2 = rnd(curfn->type->argwid, maxround); + ptxt->to.offset2 = rnd(curfn->type->argwid, widthptr); // fill in final stack size if(stksize > maxstksize) maxstksize = stksize; - ptxt->to.offset = rnd(maxstksize+maxarg, maxround); + ptxt->to.offset = rnd(maxstksize+maxarg, widthptr); maxstksize = 0; if(debug['f']) @@ -789,25 +783,58 @@ regcmp(const void *va, const void *vb) static Prog* throwpc; +// We're only going to bother inlining if we can +// convert all the arguments to 32 bits safely. Can we? +static int +fix64(NodeList *nn, int n) +{ + NodeList *l; + Node *r; + int i; + + l = nn; + for(i=0; i<n; i++) { + r = l->n->right; + if(is64(r->type) && !smallintconst(r)) { + if(r->op == OCONV) + r = r->left; + if(is64(r->type)) + return 0; + } + l = l->next; + } + return 1; +} + void getargs(NodeList *nn, Node *reg, int n) { NodeList *l; + Node *r; int i; throwpc = nil; l = nn; for(i=0; i<n; i++) { - if(!smallintconst(l->n->right) && !isslice(l->n->right->type)) { + r = l->n->right; + if(is64(r->type)) { + if(r->op == OCONV) + r = r->left; + else if(smallintconst(r)) + r->type = types[TUINT32]; + if(is64(r->type)) + fatal("getargs"); + } + if(!smallintconst(r) && !isslice(r->type)) { if(i < 3) // AX CX DX - nodreg(reg+i, l->n->right->type, D_AX+i); + nodreg(reg+i, r->type, D_AX+i); else reg[i].op = OXXX; - regalloc(reg+i, l->n->right->type, reg+i); - cgen(l->n->right, reg+i); + regalloc(reg+i, r->type, reg+i); + cgen(r, reg+i); } else - reg[i] = *l->n->right; + reg[i] = *r; if(reg[i].local != 0) yyerror("local used"); reg[i].local = l->n->left->xoffset; @@ -821,44 +848,53 @@ getargs(NodeList *nn, Node *reg, int n) void cmpandthrow(Node *nl, Node *nr) { - vlong cl, cr; + vlong cl; Prog *p1; int op; - Node *c; + Node *c, n1; + Type *t; op = OLE; if(smallintconst(nl)) { cl = mpgetfix(nl->val.u.xval); if(cl == 0) return; - if(smallintconst(nr)) { - cr = mpgetfix(nr->val.u.xval); - if(cl > cr) { - if(throwpc == nil) { - throwpc = pc; - ginscall(panicslice, 0); - } else - patch(gbranch(AJMP, T), throwpc); - } + if(smallintconst(nr)) return; - } - // put the constant on the right op = brrev(op); c = nl; nl = nr; nr = c; } - - gins(optoas(OCMP, types[TUINT32]), nl, nr); + + // Arguments are known not to be 64-bit, + // but they might be smaller than 32 bits. + // Check if we need to use a temporary. + // At least one of the arguments is 32 bits + // (the len or cap) so one temporary suffices. + n1.op = OXXX; + t = types[TUINT32]; + if(nl->type->width != t->width) { + regalloc(&n1, t, nl); + gmove(nl, &n1); + nl = &n1; + } else if(nr->type->width != t->width) { + regalloc(&n1, t, nr); + gmove(nr, &n1); + nr = &n1; + } + gins(optoas(OCMP, t), nl, nr); + if(n1.op != OXXX) + regfree(&n1); if(throwpc == nil) { - p1 = gbranch(optoas(op, types[TUINT32]), T); + p1 = gbranch(optoas(op, t), T); throwpc = pc; ginscall(panicslice, 0); patch(p1, pc); } else { op = brcom(op); - p1 = gbranch(optoas(op, types[TUINT32]), T); + p1 = gbranch(optoas(op, t), T); patch(p1, throwpc); } } @@ -889,6 +925,8 @@ cgen_inline(Node *n, Node *res) goto no; if(!n->left->addable) goto no; + if(n->left->sym == S) + goto no; if(n->left->sym->pkg != runtimepkg) goto no; if(strcmp(n->left->sym->name, "slicearray") == 0) @@ -906,6 +944,8 @@ cgen_inline(Node *n, Node *res) slicearray: if(!sleasy(res)) goto no; + if(!fix64(n->list, 5)) + goto no; getargs(n->list, nodes, 5); // if(hb[3] > nel[1]) goto throw @@ -988,6 +1028,8 @@ slicearray: return 1; sliceslice: + if(!fix64(n->list, narg)) + goto no; ntemp.op = OXXX; if(!sleasy(n->list->n->right)) { Node *n0; @@ -1016,6 +1058,7 @@ sliceslice: // if(lb[1] > old.nel[0]) goto throw; n2 = nodes[0]; n2.xoffset += Array_nel; + n2.type = types[TUINT32]; cmpandthrow(&nodes[1], &n2); // ret.nel = old.nel[0]-lb[1]; @@ -1035,6 +1078,7 @@ sliceslice: // if(hb[2] > old.cap[0]) goto throw; n2 = nodes[0]; n2.xoffset += Array_cap; + n2.type = types[TUINT32]; cmpandthrow(&nodes[2], &n2); // if(lb[1] > hb[2]) goto throw; diff --git a/src/cmd/8g/gobj.c b/src/cmd/8g/gobj.c index 1f4b106f7..e48ad529b 100644 --- a/src/cmd/8g/gobj.c +++ b/src/cmd/8g/gobj.c @@ -642,7 +642,7 @@ dsymptr(Sym *s, int off, Sym *x, int xoff) } void -genembedtramp(Type *rcvr, Type *method, Sym *newnam) +genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface) { Sym *e; int c, d, o, mov, add, loaded; @@ -739,7 +739,7 @@ out: p = pc; gins(AJMP, N, N); p->to.type = D_EXTERN; - p->to.sym = methodsym(method->sym, ptrto(f->type)); + p->to.sym = methodsym(method->sym, ptrto(f->type), 0); //print("6. %P\n", p); pc->as = ARET; // overwrite AEND diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c index 6890c683e..8ed7e5564 100644 --- a/src/cmd/8g/gsubr.c +++ b/src/cmd/8g/gsubr.c @@ -149,6 +149,8 @@ ggloblnod(Node *nam, int32 width) p->to.sym = S; p->to.type = D_CONST; p->to.offset = width; + if(nam->readonly) + p->from.scale = RODATA; } void @@ -165,6 +167,7 @@ ggloblsym(Sym *s, int32 width, int dupok) p->to.offset = width; if(dupok) p->from.scale = DUPOK; + p->from.scale |= RODATA; } int @@ -661,6 +664,11 @@ foptoas(int op, Type *t, int flg) return AFCOMDP; case FCASE(OCMP, TFLOAT64, Fpop2): return AFCOMDPP; + + case FCASE(OMINUS, TFLOAT32, 0): + return AFCHS; + case FCASE(OMINUS, TFLOAT64, 0): + return AFCHS; } fatal("foptoas %O %T %#x", op, t, flg); @@ -707,7 +715,7 @@ gclean(void) for(i=D_AL; i<=D_DI; i++) if(reg[i]) - yyerror("reg %R left allocated at %lux", i, regpc[i]); + yyerror("reg %R left allocated at %ux", i, regpc[i]); } int32 @@ -764,7 +772,7 @@ regalloc(Node *n, Type *t, Node *o) fprint(2, "registers allocated at\n"); for(i=D_AX; i<=D_DI; i++) - fprint(2, "\t%R\t%#lux\n", i, regpc[i]); + fprint(2, "\t%R\t%#ux\n", i, regpc[i]); yyerror("out of fixed registers"); goto err; @@ -1651,7 +1659,7 @@ checkoffset(Addr *a, int canemitcode) if(a->offset < unmappedzero) return; if(!canemitcode) - fatal("checkoffset %#llx, cannot emit code", a->offset); + fatal("checkoffset %#x, cannot emit code", a->offset); // cannot rely on unmapped nil page at 0 to catch // reference with large offset. instead, emit explicit diff --git a/src/cmd/8g/list.c b/src/cmd/8g/list.c index 9b3622a6d..edb1ece84 100644 --- a/src/cmd/8g/list.c +++ b/src/cmd/8g/list.c @@ -47,24 +47,28 @@ Pconv(Fmt *fp) { char str[STRINGSZ]; Prog *p; + char scale[40]; p = va_arg(fp->args, Prog*); sconsize = 8; + scale[0] = '\0'; + if(p->from.scale != 0 && (p->as == AGLOBL || p->as == ATEXT)) + snprint(scale, sizeof scale, "%d,", p->from.scale); switch(p->as) { default: - snprint(str, sizeof(str), "%.4ld (%L) %-7A %D,%D", - p->loc, p->lineno, p->as, &p->from, &p->to); + snprint(str, sizeof(str), "%.4d (%L) %-7A %D,%s%D", + p->loc, p->lineno, p->as, &p->from, scale, &p->to); break; case ADATA: sconsize = p->from.scale; - snprint(str, sizeof(str), "%.4ld (%L) %-7A %D/%d,%D", + snprint(str, sizeof(str), "%.4d (%L) %-7A %D/%d,%D", p->loc, p->lineno, p->as, &p->from, sconsize, &p->to); break; case ATEXT: - snprint(str, sizeof(str), "%.4ld (%L) %-7A %D,%lD", - p->loc, p->lineno, p->as, &p->from, &p->to); + snprint(str, sizeof(str), "%.4d (%L) %-7A %D,%s%lD", + p->loc, p->lineno, p->as, &p->from, scale, &p->to); break; } return fmtstrcpy(fp, str); diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c index 3e57916c7..e1dacf55a 100644 --- a/src/cmd/8g/reg.c +++ b/src/cmd/8g/reg.c @@ -612,17 +612,17 @@ brk: print("\nstats\n"); if(ostats.ncvtreg) - print(" %4ld cvtreg\n", ostats.ncvtreg); + print(" %4d cvtreg\n", ostats.ncvtreg); if(ostats.nspill) - print(" %4ld spill\n", ostats.nspill); + print(" %4d spill\n", ostats.nspill); if(ostats.nreload) - print(" %4ld reload\n", ostats.nreload); + print(" %4d reload\n", ostats.nreload); if(ostats.ndelmov) - print(" %4ld delmov\n", ostats.ndelmov); + print(" %4d delmov\n", ostats.ndelmov); if(ostats.nvar) - print(" %4ld delmov\n", ostats.nvar); + print(" %4d delmov\n", ostats.nvar); if(ostats.naddr) - print(" %4ld delmov\n", ostats.naddr); + print(" %4d delmov\n", ostats.naddr); memset(&ostats, 0, sizeof(ostats)); } @@ -789,6 +789,8 @@ mkvar(Reg *r, Adr *a) // if they overlaps, disable both if(overlap(v->offset, v->width, o, w)) { + if(debug['R']) + print("disable %s\n", v->sym->name); v->addr = 1; flag = 1; } @@ -821,7 +823,7 @@ mkvar(Reg *r, Adr *a) v->addr = flag; // funny punning if(debug['R']) - print("bit=%2d et=%2d w=%d %S %D\n", i, et, w, s, a); + print("bit=%2d et=%2d w=%d %S %D flag=%d\n", i, et, w, s, a, v->addr); ostats.nvar++; bit = blsh(i); @@ -1378,7 +1380,7 @@ dumpone(Reg *r) int z; Bits bit; - print("%ld:%P", r->loop, r->prog); + print("%d:%P", r->loop, r->prog); for(z=0; z<BITS; z++) bit.b[z] = r->set.b[z] | @@ -1427,14 +1429,14 @@ dumpit(char *str, Reg *r0) if(r1 != R) { print(" pred:"); for(; r1 != R; r1 = r1->p2link) - print(" %.4lud", r1->prog->loc); + print(" %.4ud", r1->prog->loc); print("\n"); } // r1 = r->s1; // if(r1 != R) { // print(" succ:"); // for(; r1 != R; r1 = r1->s1) -// print(" %.4lud", r1->prog->loc); +// print(" %.4ud", r1->prog->loc); // print("\n"); // } } diff --git a/src/cmd/8l/8.out.h b/src/cmd/8l/8.out.h index c17f606e2..0866f05f0 100644 --- a/src/cmd/8l/8.out.h +++ b/src/cmd/8l/8.out.h @@ -33,6 +33,7 @@ #define NOPROF (1<<0) #define DUPOK (1<<1) #define NOSPLIT (1<<2) +#define RODATA (1<<3) enum as { @@ -383,8 +384,8 @@ enum as AEND, - ADYNT, - AINIT, + ADYNT_, + AINIT_, ASIGNAME, @@ -498,6 +499,9 @@ enum D_CONST2 = D_INDIR+D_INDIR, D_SIZE, /* 8l internal */ + D_PCREL, + D_GOTOFF, + D_GOTREL, T_TYPE = 1<<0, T_INDEX = 1<<1, diff --git a/src/cmd/8l/Makefile b/src/cmd/8l/Makefile index 88c7c512b..84976ba18 100644 --- a/src/cmd/8l/Makefile +++ b/src/cmd/8l/Makefile @@ -2,15 +2,20 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -TARG=\ - 8l\ +TARG=8l OFILES=\ asm.$O\ + data.$O\ + dwarf.$O\ elf.$O\ enam.$O\ + go.$O\ + ldelf.$O\ + ldmacho.$O\ lib.$O\ list.$O\ macho.$O\ @@ -18,30 +23,26 @@ OFILES=\ optab.$O\ pass.$O\ pe.$O\ + prof.$O\ span.$O\ - go.$O\ + symtab.$O\ + HFILES=\ l.h\ ../8l/8.out.h\ + ../ld/dwarf.h\ ../ld/elf.h\ ../ld/macho.h\ ../ld/pe.h\ - -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lbio -l9 - -$(OFILES): $(HFILES) +include ../../Make.ccmd enam.c: 8.out.h sh mkenam -clean: - rm -f *.$O $(TARG) *.8 enam.c 8.out a.out +CLEANFILES+=enam.c -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) %.$O: ../ld/%.c - $(CC) $(CFLAGS) -c -I. ../ld/$*.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. ../ld/$*.c diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index a7f894aa2..cdb5a33e6 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -28,9 +28,12 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Writing object files. + #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" +#include "../ld/dwarf.h" #include "../ld/macho.h" #include "../ld/pe.h" @@ -38,7 +41,6 @@ char linuxdynld[] = "/lib/ld-linux.so.2"; char freebsddynld[] = "/usr/libexec/ld-elf.so.1"; -uint32 symdatva = SYMDATVA; int32 entryvalue(void) @@ -52,15 +54,8 @@ entryvalue(void) s = lookup(a, 0); if(s->type == 0) return INITTEXT; - switch(s->type) { - case STEXT: - break; - case SDATA: - if(dlm) - return s->value+INITDAT; - default: + if(s->type != STEXT) diag("entry not text: %s", s->name); - } return s->value; } @@ -103,134 +98,13 @@ vputl(uvlong l) lputl(l); } -void -strnput(char *s, int n) -{ - for(; *s && n > 0; s++) { - cput(*s); - n--; - } - while(n > 0) { - cput(0); - n--; - } -} - -vlong -addstring(Sym *s, char *str) -{ - int n, m; - vlong r; - Prog *p; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - n = strlen(str)+1; - while(n > 0) { - m = n; - if(m > sizeof(p->to.scon)) - m = sizeof(p->to.scon); - p = newdata(s, s->value, m, D_EXTERN); - p->to.type = D_SCONST; - memmove(p->to.scon, str, m); - s->value += m; - str += m; - n -= m; - } - return r; -} - -vlong -adduintxx(Sym *s, uint64 v, int wid) -{ - vlong r; - Prog *p; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, wid, D_EXTERN); - s->value += wid; - p->to.type = D_CONST; - p->to.offset = v; - return r; -} - -vlong -adduint8(Sym *s, uint8 v) -{ - return adduintxx(s, v, 1); -} - -vlong -adduint16(Sym *s, uint16 v) -{ - return adduintxx(s, v, 2); -} - -vlong -adduint32(Sym *s, uint32 v) -{ - return adduintxx(s, v, 4); -} - -vlong -adduint64(Sym *s, uint64 v) -{ - return adduintxx(s, v, 8); -} - -vlong -addaddr(Sym *s, Sym *t) -{ - vlong r; - Prog *p; - enum { Ptrsize = 4 }; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, Ptrsize, D_EXTERN); - s->value += Ptrsize; - p->to.type = D_ADDR; - p->to.index = D_EXTERN; - p->to.offset = 0; - p->to.sym = t; - return r; -} - -vlong -addsize(Sym *s, Sym *t) -{ - vlong r; - Prog *p; - enum { Ptrsize = 4 }; - - if(s->type == 0) - s->type = SDATA; - s->reachable = 1; - r = s->value; - p = newdata(s, s->value, Ptrsize, D_EXTERN); - s->value += Ptrsize; - p->to.type = D_SIZE; - p->to.index = D_EXTERN; - p->to.offset = 0; - p->to.sym = t; - return r; -} - vlong datoff(vlong addr) { - if(addr >= INITDAT) { - if(HEADTYPE == 8) - return addr - INITDAT + rnd(HEADR+textsize, 4096); - return addr - INITDAT + rnd(HEADR+textsize, INITRND); - } + if(addr >= segdata.vaddr) + return addr - segdata.vaddr + segdata.fileoff; + if(addr >= segtext.vaddr) + return addr - segtext.vaddr + segtext.fileoff; diag("datoff %#llx", addr); return 0; } @@ -252,6 +126,10 @@ enum { ElfStrGosymtab, ElfStrGopclntab, ElfStrShstrtab, + ElfStrSymtab, + ElfStrStrtab, + ElfStrRelPlt, + ElfStrPlt, NElfStr }; @@ -273,26 +151,424 @@ needlib(char *name) return 0; } +int nelfsym = 1; + +static void addpltsym(Sym*); +static void addgotsym(Sym*); + +void +adddynrel(Sym *s, Reloc *r) +{ + Sym *targ, *rel, *got; + + targ = r->sym; + cursym = s; + + switch(r->type) { + default: + if(r->type >= 256) { + diag("unexpected relocation type %d", r->type); + return; + } + break; + + // Handle relocations found in ELF object files. + case 256 + R_386_PC32: + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected R_386_PC32 relocation for dynamic symbol %s", targ->name); + if(targ->type == 0 || targ->type == SXREF) + diag("unknown symbol %s in pcrel", targ->name); + r->type = D_PCREL; + r->add += 4; + return; + + case 256 + R_386_PLT32: + r->type = D_PCREL; + r->add += 4; + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add += targ->plt; + } + return; + + case 256 + R_386_GOT32: + if(targ->dynimpname == nil || targ->dynexport) { + // have symbol + // turn MOVL of GOT entry into LEAL of symbol itself + if(r->off < 2 || s->p[r->off-2] != 0x8b) { + diag("unexpected GOT reloc for non-dynamic symbol %s", targ->name); + return; + } + s->p[r->off-2] = 0x8d; + r->type = D_GOTOFF; + return; + } + addgotsym(targ); + r->type = D_CONST; // write r->add during relocsym + r->sym = S; + r->add += targ->got; + return; + + case 256 + R_386_GOTOFF: + r->type = D_GOTOFF; + return; + + case 256 + R_386_GOTPC: + r->type = D_PCREL; + r->sym = lookup(".got", 0); + r->add += 4; + return; + + case 256 + R_386_32: + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected R_386_32 relocation for dynamic symbol %s", targ->name); + r->type = D_ADDR; + return; + + case 512 + MACHO_GENERIC_RELOC_VANILLA*2 + 0: + r->type = D_ADDR; + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected reloc for dynamic symbol %s", targ->name); + return; + + case 512 + MACHO_GENERIC_RELOC_VANILLA*2 + 1: + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = targ->plt; + r->type = D_PCREL; + return; + } + r->type = D_PCREL; + return; + + case 512 + MACHO_FAKE_GOTPCREL: + if(targ->dynimpname == nil || targ->dynexport) { + // have symbol + // turn MOVL of GOT entry into LEAL of symbol itself + if(r->off < 2 || s->p[r->off-2] != 0x8b) { + diag("unexpected GOT reloc for non-dynamic symbol %s", targ->name); + return; + } + s->p[r->off-2] = 0x8d; + r->type = D_PCREL; + return; + } + addgotsym(targ); + r->sym = lookup(".got", 0); + r->add += targ->got; + r->type = D_PCREL; + return; + } + + // Handle references to ELF symbols from our own object files. + if(targ->dynimpname == nil || targ->dynexport) + return; + + switch(r->type) { + case D_PCREL: + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = targ->plt; + return; + + case D_ADDR: + if(s->type != SDATA) + break; + if(iself) { + adddynsym(targ); + rel = lookup(".rel", 0); + addaddrplus(rel, s, r->off); + adduint32(rel, ELF32_R_INFO(targ->dynid, R_386_32)); + r->type = D_CONST; // write r->add during relocsym + r->sym = S; + return; + } + if(HEADTYPE == 6 && s->size == PtrSize && r->off == 0) { + // Mach-O relocations are a royal pain to lay out. + // They use a compact stateful bytecode representation + // that is too much bother to deal with. + // Instead, interpret the C declaration + // void *_Cvar_stderr = &stderr; + // as making _Cvar_stderr the name of a GOT entry + // for stderr. This is separate from the usual GOT entry, + // just in case the C code assigns to the variable, + // and of course it only works for single pointers, + // but we only need to support cgo and that's all it needs. + adddynsym(targ); + got = lookup(".got", 0); + s->type = got->type | SSUB; + s->outer = got; + s->sub = got->sub; + got->sub = s; + s->value = got->size; + adduint32(got, 0); + adduint32(lookup(".linkedit.got", 0), targ->dynid); + r->type = 256; // ignore during relocsym + return; + } + break; + } + + cursym = s; + diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); +} + +static void +elfsetupplt(void) +{ + Sym *plt, *got; + + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + if(plt->size == 0) { + // pushl got+4 + adduint8(plt, 0xff); + adduint8(plt, 0x35); + addaddrplus(plt, got, 4); + + // jmp *got+8 + adduint8(plt, 0xff); + adduint8(plt, 0x25); + addaddrplus(plt, got, 8); + + // zero pad + adduint32(plt, 0); + + // assume got->size == 0 too + addaddrplus(got, lookup(".dynamic", 0), 0); + adduint32(got, 0); + adduint32(got, 0); + } +} + +int +archreloc(Reloc *r, Sym *s, vlong *val) +{ + switch(r->type) { + case D_CONST: + *val = r->add; + return 0; + case D_GOTOFF: + *val = symaddr(r->sym) + r->add - symaddr(lookup(".got", 0)); + return 0; + } + return -1; +} + +static void +addpltsym(Sym *s) +{ + Sym *plt, *got, *rel; + + if(s->plt >= 0) + return; + + adddynsym(s); + + if(iself) { + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + rel = lookup(".rel.plt", 0); + if(plt->size == 0) + elfsetupplt(); + + // jmpq *got+size + adduint8(plt, 0xff); + adduint8(plt, 0x25); + addaddrplus(plt, got, got->size); + + // add to got: pointer to current pos in plt + addaddrplus(got, plt, plt->size); + + // pushl $x + adduint8(plt, 0x68); + adduint32(plt, rel->size); + + // jmp .plt + adduint8(plt, 0xe9); + adduint32(plt, -(plt->size+4)); + + // rel + addaddrplus(rel, got, got->size-4); + adduint32(rel, ELF32_R_INFO(s->dynid, R_386_JMP_SLOT)); + + s->plt = plt->size - 16; + } else if(HEADTYPE == 6) { // Mach-O + // Same laziness as in 6l. + + Sym *plt; + + plt = lookup(".plt", 0); + + addgotsym(s); + + adduint32(lookup(".linkedit.plt", 0), s->dynid); + + // jmpq *got+size(IP) + s->plt = plt->size; + + adduint8(plt, 0xff); + adduint8(plt, 0x25); + addaddrplus(plt, lookup(".got", 0), s->got); + } else { + diag("addpltsym: unsupported binary format"); + } +} + +static void +addgotsym(Sym *s) +{ + Sym *got, *rel; + + if(s->got >= 0) + return; + + adddynsym(s); + got = lookup(".got", 0); + s->got = got->size; + adduint32(got, 0); + + if(iself) { + rel = lookup(".rel", 0); + addaddrplus(rel, got, s->got); + adduint32(rel, ELF32_R_INFO(s->dynid, R_386_GLOB_DAT)); + } else if(HEADTYPE == 6) { // Mach-O + adduint32(lookup(".linkedit.got", 0), s->dynid); + } else { + diag("addgotsym: unsupported binary format"); + } +} + +void +adddynsym(Sym *s) +{ + Sym *d, *str; + int t; + char *name; + + if(s->dynid >= 0) + return; + + if(s->dynimpname == nil) + diag("adddynsym: no dynamic name for %s", s->name, *(int32*)0); + + if(iself) { + s->dynid = nelfsym++; + + d = lookup(".dynsym", 0); + + /* name */ + name = s->dynimpname; + if(name == nil) + name = s->name; + adduint32(d, addstring(lookup(".dynstr", 0), name)); + + /* value */ + if(s->type == SDYNIMPORT) + adduint32(d, 0); + else + addaddr(d, s); + + /* size */ + adduint32(d, 0); + + /* type */ + t = STB_GLOBAL << 4; + if(s->dynexport && s->type == STEXT) + t |= STT_FUNC; + else + t |= STT_OBJECT; + adduint8(d, t); + adduint8(d, 0); + + /* shndx */ + if(!s->dynexport && s->dynimpname != nil) + adduint16(d, SHN_UNDEF); + else { + switch(s->type) { + default: + case STEXT: + t = 11; + break; + case SRODATA: + t = 12; + break; + case SDATA: + t = 13; + break; + case SBSS: + t = 14; + break; + } + adduint16(d, t); + } + } else if(HEADTYPE == 6) { + // Mach-O symbol nlist32 + d = lookup(".dynsym", 0); + name = s->dynimpname; + if(name == nil) + name = s->name; + s->dynid = d->size/12; + // darwin still puts _ prefixes on all C symbols + str = lookup(".dynstr", 0); + adduint32(d, str->size); + adduint8(str, '_'); + addstring(str, name); + adduint8(d, 0x01); // type - N_EXT - external symbol + adduint8(d, 0); // section + adduint16(d, 0); // desc + adduint32(d, 0); // value + } else { + diag("adddynsym: unsupported binary format"); + } +} + +void +adddynlib(char *lib) +{ + Sym *s; + + if(!needlib(lib)) + return; + + if(iself) { + s = lookup(".dynstr", 0); + if(s->size == 0) + addstring(s, ""); + elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib)); + } else if(HEADTYPE == 6) { // Mach-O + machoadddynlib(lib); + } else { + diag("adddynlib: unsupported binary format"); + } +} + void doelf(void) { - Sym *s, *shstrtab, *dynamic, *dynstr, *d; - int h, nsym, t; + Sym *s, *shstrtab, *dynstr; if(!iself) return; /* predefine strings we need for section headers */ shstrtab = lookup(".shstrtab", 0); + shstrtab->type = SELFDATA; shstrtab->reachable = 1; + elfstr[ElfStrEmpty] = addstring(shstrtab, ""); elfstr[ElfStrText] = addstring(shstrtab, ".text"); elfstr[ElfStrData] = addstring(shstrtab, ".data"); elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); + addstring(shstrtab, ".elfdata"); + addstring(shstrtab, ".rodata"); if(!debug['s']) { elfstr[ElfStrGosymcounts] = addstring(shstrtab, ".gosymcounts"); elfstr[ElfStrGosymtab] = addstring(shstrtab, ".gosymtab"); elfstr[ElfStrGopclntab] = addstring(shstrtab, ".gopclntab"); + dwarfaddshstrings(shstrtab); } elfstr[ElfStrShstrtab] = addstring(shstrtab, ".shstrtab"); @@ -305,6 +581,8 @@ doelf(void) elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym"); elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr"); elfstr[ElfStrRel] = addstring(shstrtab, ".rel"); + elfstr[ElfStrRelPlt] = addstring(shstrtab, ".rel.plt"); + elfstr[ElfStrPlt] = addstring(shstrtab, ".plt"); /* interpreter string */ s = lookup(".interp", 0); @@ -315,13 +593,14 @@ doelf(void) s = lookup(".dynsym", 0); s->type = SELFDATA; s->reachable = 1; - s->value += ELF32SYMSIZE; + s->size += ELF32SYMSIZE; /* dynamic string table */ s = lookup(".dynstr", 0); s->reachable = 1; s->type = SELFDATA; - addstring(s, ""); + if(s->size == 0) + addstring(s, ""); dynstr = s; /* relocation table */ @@ -332,88 +611,36 @@ doelf(void) /* global offset table */ s = lookup(".got", 0); s->reachable = 1; + s->type = SDATA; // writable, so not SELFDATA + + /* hash */ + s = lookup(".hash", 0); + s->reachable = 1; s->type = SELFDATA; - /* got.plt - ??? */ + /* got.plt */ s = lookup(".got.plt", 0); s->reachable = 1; + s->type = SDATA; // writable, so not SELFDATA + + s = lookup(".plt", 0); + s->reachable = 1; s->type = SELFDATA; - /* define dynamic elf table */ - s = lookup(".dynamic", 0); + s = lookup(".rel.plt", 0); s->reachable = 1; s->type = SELFDATA; - dynamic = s; - /* - * relocation entries for dynimport symbols - */ - nsym = 1; // sym 0 is reserved - for(h=0; h<NHASH; h++) { - for(s=hash[h]; s!=S; s=s->link) { - if(!s->reachable || (s->type != STEXT && s->type != SDATA && s->type != SBSS) || s->dynimpname == nil) - continue; - - if(!s->dynexport) { - d = lookup(".rel", 0); - addaddr(d, s); - adduint32(d, ELF32_R_INFO(nsym, R_386_32)); - } - - nsym++; - - d = lookup(".dynsym", 0); - adduint32(d, addstring(lookup(".dynstr", 0), s->dynimpname)); - /* value */ - if(!s->dynexport) - adduint32(d, 0); - else - addaddr(d, s); - - /* size of object */ - adduint32(d, 0); - - /* type */ - t = STB_GLOBAL << 4; - if(s->dynexport && s->type == STEXT) - t |= STT_FUNC; - else - t |= STT_OBJECT; - adduint8(d, t); - - /* reserved */ - adduint8(d, 0); - - /* section where symbol is defined */ - if(!s->dynexport) - adduint16(d, SHN_UNDEF); - else { - switch(s->type) { - default: - case STEXT: - t = 9; - break; - case SDATA: - t = 10; - break; - case SBSS: - t = 11; - break; - } - adduint16(d, t); - } - - if(!s->dynexport && needlib(s->dynimplib)) - elfwritedynent(dynamic, DT_NEEDED, addstring(dynstr, s->dynimplib)); - } - } + elfsetupplt(); - elfdynhash(nsym); + /* define dynamic elf table */ + s = lookup(".dynamic", 0); + s->reachable = 1; + s->type = SELFDATA; /* * .dynamic table */ - s = dynamic; elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); @@ -424,6 +651,10 @@ doelf(void) elfwritedynent(s, DT_RELENT, ELF32RELSIZE); if(rpath) elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); + elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); + elfwritedynent(s, DT_PLTREL, DT_REL); + elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rel.plt", 0)); + elfwritedynentsym(s, DT_JMPREL, lookup(".rel.plt", 0)); elfwritedynent(s, DT_NULL, 0); } } @@ -450,155 +681,52 @@ phsh(Elf64_Phdr *ph, Elf64_Shdr *sh) void asmb(void) { - Prog *p; int32 v, magic; int a, dynsym; uint32 va, fo, w, symo, startva, machlink; - uchar *op1; - ulong expectpc; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; + Section *sect; if(debug['v']) Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); - seek(cout, HEADR, 0); - pc = INITTEXT; - curp = firstp; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - curp = p; - if(HEADTYPE == 8) { - // native client - expectpc = p->pc; - p->pc = pc; - asmins(p); - if(p->pc != expectpc) { - Bflush(&bso); - diag("phase error %lux sb %lux in %s", p->pc, expectpc, TNAME); - } - while(pc < p->pc) { - cput(0x90); // nop - pc++; - } - } - if(p->pc != pc) { - Bflush(&bso); - if(!debug['a']) - print("%P\n", curp); - diag("phase error %lux sb %lux in %s", p->pc, pc, TNAME); - pc = p->pc; - } - if(HEADTYPE != 8) { - asmins(p); - if(pc != p->pc) { - Bflush(&bso); - diag("asmins changed pc %lux sb %lux in %s", p->pc, pc, TNAME); - } - } - if(cbc < sizeof(and)) - cflush(); - a = (andptr - and); - - if(debug['a']) { - Bprint(&bso, pcstr, pc); - for(op1 = and; op1 < andptr; op1++) - Bprint(&bso, "%.2ux", *op1 & 0xff); - Bprint(&bso, "\t%P\n", curp); - } - if(dlm) { - if(p->as == ATEXT) - reloca = nil; - else if(reloca != nil) - diag("reloc failure: %P", curp); - } - memmove(cbp, and, a); - cbp += a; - pc += a; - cbc -= a; - } - if(HEADTYPE == 8) { - while(pc < INITDAT) { - cput(0xf4); // hlt - pc++; - } - } - cflush(); + sect = segtext.sect; + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + codeblk(sect->vaddr, sect->len); - switch(HEADTYPE) { - default: - if(iself) - goto Elfseek; - diag("unknown header type %d", HEADTYPE); - case 0: - seek(cout, rnd(HEADR+textsize, 8192), 0); - break; - case 1: - textsize = rnd(HEADR+textsize, 4096)-HEADR; - seek(cout, textsize+HEADR, 0); - break; - case 2: - seek(cout, HEADR+textsize, 0); - break; - case 3: - case 4: - seek(cout, HEADR+rnd(textsize, INITRND), 0); - break; - case 6: - v = HEADR+textsize; - seek(cout, v, 0); - v = rnd(v, 4096) - v; - while(v > 0) { - cput(0); - v--; - } - cflush(); - break; - case 8: - // Native Client only needs to round - // text segment file address to 4096 bytes, - // but text segment memory address rounds - // to INITRND (65536). - v = rnd(HEADR+textsize, 4096); - seek(cout, v, 0); - break; - Elfseek: - case 10: - v = rnd(HEADR+textsize, INITRND); - seek(cout, v, 0); - break; - } + /* output read-only data in text segment */ + sect = segtext.sect->next; + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + datblk(sect->vaddr, sect->len); if(debug['v']) Bprint(&bso, "%5.2f datblk\n", cputime()); Bflush(&bso); - if(dlm){ - char buf[8]; - - write(cout, buf, INITDAT-textsize); - textsize = INITDAT; - } - - for(v = 0; v < datsize; v += sizeof(buf)-Dbufslop) { - if(datsize-v > sizeof(buf)-Dbufslop) - datblk(v, sizeof(buf)-Dbufslop); - else - datblk(v, datsize-v); - } + seek(cout, segdata.fileoff, 0); + datblk(segdata.vaddr, segdata.filelen); machlink = 0; if(HEADTYPE == 6) machlink = domacholink(); + if(iself) { + /* index of elf text section; needed by asmelfsym, double-checked below */ + /* !debug['d'] causes extra sections before the .text section */ + elftextsh = 1; + if(!debug['d']) + elftextsh += 10; + } + symsize = 0; spsize = 0; lcsize = 0; symo = 0; if(!debug['s']) { + // TODO: rationalize if(debug['v']) Bprint(&bso, "%5.2f sym\n", cputime()); Bflush(&bso); @@ -607,53 +735,38 @@ asmb(void) if(iself) goto Elfsym; case 0: - seek(cout, rnd(HEADR+textsize, 8192)+datsize, 0); + seek(cout, rnd(HEADR+segtext.filelen, 8192)+segdata.filelen, 0); break; case 1: - seek(cout, rnd(HEADR+textsize, INITRND)+datsize, 0); + seek(cout, rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen, 0); break; case 2: - seek(cout, HEADR+textsize+datsize, 0); + seek(cout, HEADR+segtext.filelen+segdata.filelen, 0); break; case 3: case 4: debug['s'] = 1; - symo = HEADR+textsize+datsize; + symo = HEADR+segtext.filelen+segdata.filelen; break; case 6: - symo = rnd(HEADR+textsize, INITRND)+rnd(datsize, INITRND)+machlink; + symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(segdata.filelen, INITRND)+machlink; break; Elfsym: - case 10: - symo = rnd(HEADR+textsize, INITRND)+datsize; + symo = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); break; + case 10: + // TODO(brainman): not sure what symo meant to be, but it is not used for Windows PE for now anyway + symo = rnd(HEADR+segtext.filelen, PEFILEALIGN)+segdata.filelen; + symo = rnd(symo, PEFILEALIGN); + break; + } + if(HEADTYPE != 10 && !debug['s']) { + seek(cout, symo, 0); + if(debug['v']) + Bprint(&bso, "%5.2f dwarf\n", cputime()); + dwarfemitdebugsections(); } - seek(cout, symo+8, 0); - if(!debug['s']) - asmsym(); - if(debug['v']) - Bprint(&bso, "%5.2f sp\n", cputime()); - Bflush(&bso); - if(debug['v']) - Bprint(&bso, "%5.2f pc\n", cputime()); - Bflush(&bso); - if(!debug['s']) - asmlc(); - if(dlm) - asmdyn(); - if(HEADTYPE == 10 || (iself && !debug['s'])) - strnput("", INITRND-(8+symsize+lcsize)%INITRND); - cflush(); - seek(cout, symo, 0); - lputl(symsize); - lputl(lcsize); - cflush(); - } - else if(dlm){ - seek(cout, HEADR+textsize+datsize, 0); - asmdyn(); - cflush(); } if(debug['v']) Bprint(&bso, "%5.2f headr\n", cputime()); @@ -666,17 +779,17 @@ asmb(void) case 0: /* garbage */ lput(0x160L<<16); /* magic and sections */ lput(0L); /* time and date */ - lput(rnd(HEADR+textsize, 4096)+datsize); + lput(rnd(HEADR+segtext.filelen, 4096)+segdata.filelen); lput(symsize); /* nsyms */ lput((0x38L<<16)|7L); /* size of optional hdr and flags */ lput((0413<<16)|0437L); /* magic and version */ - lput(rnd(HEADR+textsize, 4096)); /* sizes */ - lput(datsize); - lput(bsssize); + lput(rnd(HEADR+segtext.filelen, 4096)); /* sizes */ + lput(segdata.filelen); + lput(segdata.len - segdata.filelen); lput(entryvalue()); /* va of entry */ lput(INITTEXT-HEADR); /* va of base of text */ - lput(INITDAT); /* va of base of data */ - lput(INITDAT+datsize); /* va of base of bss */ + lput(segdata.vaddr); /* va of base of data */ + lput(segdata.vaddr+segdata.filelen); /* va of base of bss */ lput(~0L); /* gp reg mask */ lput(0L); lput(0L); @@ -698,19 +811,19 @@ asmb(void) * a.out header */ lputl(0x10b); /* magic, version stamp */ - lputl(rnd(textsize, INITRND)); /* text sizes */ - lputl(datsize); /* data sizes */ - lputl(bsssize); /* bss sizes */ + lputl(rnd(segtext.filelen, INITRND)); /* text sizes */ + lputl(segdata.filelen); /* data sizes */ + lputl(segdata.len - segdata.filelen); /* bss sizes */ lput(entryvalue()); /* va of entry */ lputl(INITTEXT); /* text start */ - lputl(INITDAT); /* data start */ + lputl(segdata.vaddr); /* data start */ /* * text section header */ s8put(".text"); lputl(HEADR); /* pa */ lputl(HEADR); /* va */ - lputl(textsize); /* text size */ + lputl(segtext.filelen); /* text size */ lputl(HEADR); /* file offset */ lputl(0); /* relocation */ lputl(0); /* line numbers */ @@ -720,10 +833,10 @@ asmb(void) * data section header */ s8put(".data"); - lputl(INITDAT); /* pa */ - lputl(INITDAT); /* va */ - lputl(datsize); /* data size */ - lputl(HEADR+textsize); /* file offset */ + lputl(segdata.vaddr); /* pa */ + lputl(segdata.vaddr); /* va */ + lputl(segdata.filelen); /* data size */ + lputl(HEADR+segtext.filelen); /* file offset */ lputl(0); /* relocation */ lputl(0); /* line numbers */ lputl(0); /* relocation, line numbers */ @@ -732,9 +845,9 @@ asmb(void) * bss section header */ s8put(".bss"); - lputl(INITDAT+datsize); /* pa */ - lputl(INITDAT+datsize); /* va */ - lputl(bsssize); /* bss size */ + lputl(segdata.vaddr+segdata.filelen); /* pa */ + lputl(segdata.vaddr+segdata.filelen); /* va */ + lputl(segdata.len - segdata.filelen); /* bss size */ lputl(0); /* file offset */ lputl(0); /* relocation */ lputl(0); /* line numbers */ @@ -747,20 +860,18 @@ asmb(void) lputl(0); /* pa */ lputl(0); /* va */ lputl(symsize+lcsize); /* comment size */ - lputl(HEADR+textsize+datsize); /* file offset */ - lputl(HEADR+textsize+datsize); /* offset of syms */ - lputl(HEADR+textsize+datsize+symsize);/* offset of line numbers */ + lputl(HEADR+segtext.filelen+segdata.filelen); /* file offset */ + lputl(HEADR+segtext.filelen+segdata.filelen); /* offset of syms */ + lputl(HEADR+segtext.filelen+segdata.filelen+symsize);/* offset of line numbers */ lputl(0); /* relocation, line numbers */ lputl(0x200); /* flags comment only */ break; case 2: /* plan9 */ magic = 4*11*11+7; - if(dlm) - magic |= 0x80000000; lput(magic); /* magic */ - lput(textsize); /* sizes */ - lput(datsize); - lput(bsssize); + lput(segtext.filelen); /* sizes */ + lput(segdata.filelen); + lput(segdata.len - segdata.filelen); lput(symsize); /* nsyms */ lput(entryvalue()); /* va of entry */ lput(spsize); /* sp offsets */ @@ -771,7 +882,7 @@ asmb(void) break; case 4: /* fake MS-DOS .EXE */ - v = rnd(HEADR+textsize, INITRND)+datsize; + v = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen; wputl(0x5A4D); /* 'MZ' */ wputl(v % 512); /* bytes in last page */ wputl(rnd(v, 512)/512); /* total number of pages */ @@ -793,34 +904,31 @@ asmb(void) break; case 6: - asmbmacho(symdatva, symo); + asmbmacho(); break; Elfput: /* elf 386 */ - if(HEADTYPE == 8 || HEADTYPE == 11) + if(HEADTYPE == 11) debug['d'] = 1; eh = getElfEhdr(); fo = HEADR; startva = INITTEXT - HEADR; va = startva + fo; - w = textsize; + w = segtext.filelen; /* This null SHdr must appear before all others */ sh = newElfShdr(elfstr[ElfStrEmpty]); - /* program header info - but not on native client */ - pph = nil; - if(HEADTYPE != 8) { - pph = newElfPhdr(); - pph->type = PT_PHDR; - pph->flags = PF_R + PF_X; - pph->off = eh->ehsize; - pph->vaddr = INITTEXT - HEADR + pph->off; - pph->paddr = INITTEXT - HEADR + pph->off; - pph->align = INITRND; - } + /* program header info */ + pph = newElfPhdr(); + pph->type = PT_PHDR; + pph->flags = PF_R + PF_X; + pph->off = eh->ehsize; + pph->vaddr = INITTEXT - HEADR + pph->off; + pph->paddr = INITTEXT - HEADR + pph->off; + pph->align = INITRND; if(!debug['d']) { /* interpreter */ @@ -843,51 +951,8 @@ asmb(void) phsh(ph, sh); } - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_X+PF_R; - if(HEADTYPE != 8) { // Include header, but not on Native Client. - va -= fo; - w += fo; - fo = 0; - } - ph->vaddr = va; - ph->paddr = va; - ph->off = fo; - ph->filesz = w; - ph->memsz = INITDAT - va; - ph->align = INITRND; - - // NaCl text segment file address rounds to 4096; - // only memory address rounds to INITRND. - if(HEADTYPE == 8) - fo = rnd(fo+w, 4096); - else - fo = rnd(fo+w, INITRND); - va = INITDAT; - w = datsize; - - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_W+PF_R; - ph->off = fo; - ph->vaddr = va; - ph->paddr = va; - ph->filesz = w; - ph->memsz = w+bsssize; - ph->align = INITRND; - - if(!debug['s'] && HEADTYPE != 8 && HEADTYPE != 11) { - ph = newElfPhdr(); - ph->type = PT_LOAD; - ph->flags = PF_R; - ph->off = symo; - ph->vaddr = symdatva; - ph->paddr = symdatva; - ph->filesz = rnd(8+symsize+lcsize, INITRND); - ph->memsz = rnd(8+symsize+lcsize, INITRND); - ph->align = INITRND; - } + elfphload(&segtext); + elfphload(&segdata); /* Dynamic linking sections */ if (!debug['d']) { /* -d suppresses dynamic loader format */ @@ -921,6 +986,22 @@ asmb(void) sh->flags = SHF_ALLOC; sh->addralign = 1; shsym(sh, lookup(".dynstr", 0)); + + sh = newElfShdr(elfstr[ElfStrRelPlt]); + sh->type = SHT_REL; + sh->flags = SHF_ALLOC; + sh->entsize = ELF32RELSIZE; + sh->addralign = 4; + sh->link = dynsym; + sh->info = eh->shnum; // .plt + shsym(sh, lookup(".rel.plt", 0)); + + sh = newElfShdr(elfstr[ElfStrPlt]); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_EXECINSTR; + sh->entsize = 4; + sh->addralign = 4; + shsym(sh, lookup(".plt", 0)); sh = newElfShdr(elfstr[ElfStrHash]); sh->type = SHT_HASH; @@ -968,80 +1049,27 @@ asmb(void) ph->flags = PF_W+PF_R; ph->align = 4; - fo = HEADR; - va = startva + fo; - w = textsize; - - sh = newElfShdr(elfstr[ElfStrText]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_EXECINSTR; - sh->addr = va; - sh->off = fo; - sh->size = w; - sh->addralign = 4; - - // NaCl text segment file address rounds to 4096; - // only memory address rounds to INITRND. - if(HEADTYPE == 8) - fo = rnd(fo+w, 4096); - else - fo = rnd(fo+w, INITRND); - va = rnd(va+w, INITRND); - w = datsize; - - sh = newElfShdr(elfstr[ElfStrData]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_WRITE+SHF_ALLOC; - sh->addr = va + elfdatsize; - sh->off = fo + elfdatsize; - sh->size = w - elfdatsize; - sh->addralign = 4; - - fo += w; - va += w; - w = bsssize; - - sh = newElfShdr(elfstr[ElfStrBss]); - sh->type = SHT_NOBITS; - sh->flags = SHF_WRITE+SHF_ALLOC; - sh->addr = va; - sh->off = fo; - sh->size = w; - sh->addralign = 4; + if(elftextsh != eh->shnum) + diag("elftextsh = %d, want %d", elftextsh, eh->shnum); + for(sect=segtext.sect; sect!=nil; sect=sect->next) + elfshbits(sect); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfshbits(sect); if (!debug['s']) { - fo = symo; - w = 8; - - sh = newElfShdr(elfstr[ElfStrGosymcounts]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; - sh->addralign = 1; - sh->addr = symdatva; - - fo += w; - w = symsize; - sh = newElfShdr(elfstr[ElfStrGosymtab]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; sh->addralign = 1; - sh->addr = symdatva + 8; - - fo += w; - w = lcsize; + shsym(sh, lookup("symtab", 0)); sh = newElfShdr(elfstr[ElfStrGopclntab]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; - sh->off = fo; - sh->size = w; sh->addralign = 1; - sh->addr = symdatva + 8 + symsize; + shsym(sh, lookup("pclntab", 0)); + + dwarfaddelfheaders(); } sh = newElfShstrtab(elfstr[ElfStrShstrtab]); @@ -1058,11 +1086,6 @@ asmb(void) eh->ident[EI_DATA] = ELFDATA2LSB; eh->ident[EI_VERSION] = EV_CURRENT; switch(HEADTYPE) { - case 8: - eh->ident[EI_OSABI] = ELFOSABI_NACL; - eh->ident[EI_ABIVERSION] = 7; - eh->flags = 0x200000; // aligned mod 32 - break; case 9: eh->ident[EI_OSABI] = 9; break; @@ -1113,215 +1136,16 @@ cflush(void) n = sizeof(buf.cbuf) - cbc; if(n) - write(cout, buf.cbuf, n); + ewrite(cout, buf.cbuf, n); cbp = buf.cbuf; cbc = sizeof(buf.cbuf); } -void -datblk(int32 s, int32 n) +/* Current position in file */ +vlong +cpos(void) { - Prog *p; - char *cast; - int32 l, fl, j; - int i, c; - Adr *a; - - memset(buf.dbuf, 0, n+Dbufslop); - for(p = datap; p != P; p = p->link) { - a = &p->from; - - l = a->sym->value + a->offset - s; - if(l >= n) - continue; - - c = a->scale; - i = 0; - if(l < 0) { - if(l+c <= 0) - continue; - i = -l; - l = 0; - } - - curp = p; - if(!a->sym->reachable) - diag("unreachable symbol in datblk - %s", a->sym->name); - if(a->sym->type == SMACHO) - continue; - - if(p->as != AINIT && p->as != ADYNT) { - for(j=l+(c-i)-1; j>=l; j--) - if(buf.dbuf[j]) { - print("%P\n", p); - diag("multiple initialization"); - break; - } - } - switch(p->to.type) { - case D_FCONST: - switch(c) { - default: - case 4: - fl = ieeedtof(&p->to.ieee); - cast = (char*)&fl; - for(; i<c; i++) { - buf.dbuf[l] = cast[fnuxi4[i]]; - l++; - } - break; - case 8: - cast = (char*)&p->to.ieee; - for(; i<c; i++) { - buf.dbuf[l] = cast[fnuxi8[i]]; - l++; - } - break; - } - break; - - case D_SCONST: - for(; i<c; i++) { - buf.dbuf[l] = p->to.scon[i]; - l++; - } - break; - - default: - fl = p->to.offset; - if(p->to.type == D_SIZE) - fl += p->to.sym->size; - if(p->to.type == D_ADDR) { - if(p->to.index != D_STATIC && p->to.index != D_EXTERN) - diag("DADDR type%P", p); - if(p->to.sym) { - if(p->to.sym->type == SUNDEF) - ckoff(p->to.sym, fl); - fl += p->to.sym->value; - if(p->to.sym->type != STEXT && p->to.sym->type != SUNDEF) - fl += INITDAT; - if(dlm) - dynreloc(p->to.sym, l+s+INITDAT, 1); - } - } - cast = (char*)&fl; - switch(c) { - default: - diag("bad nuxi %d %d\n%P", c, i, curp); - break; - case 1: - for(; i<c; i++) { - buf.dbuf[l] = cast[inuxi1[i]]; - l++; - } - break; - case 2: - for(; i<c; i++) { - buf.dbuf[l] = cast[inuxi2[i]]; - l++; - } - break; - case 4: - for(; i<c; i++) { - buf.dbuf[l] = cast[inuxi4[i]]; - l++; - } - break; - } - break; - } - } - - write(cout, buf.dbuf, n); - if(!debug['a']) - return; - - /* - * a second pass just to print the asm - */ - for(p = datap; p != P; p = p->link) { - a = &p->from; - - l = a->sym->value + a->offset - s; - if(l < 0 || l >= n) - continue; - - c = a->scale; - i = 0; - - switch(p->to.type) { - case D_FCONST: - switch(c) { - default: - case 4: - fl = ieeedtof(&p->to.ieee); - cast = (char*)&fl; - Bprint(&bso, pcstr, l+s+INITDAT); - for(j=0; j<c; j++) - Bprint(&bso, "%.2ux", cast[fnuxi4[j]] & 0xff); - Bprint(&bso, "\t%P\n", curp); - break; - case 8: - cast = (char*)&p->to.ieee; - Bprint(&bso, pcstr, l+s+INITDAT); - for(j=0; j<c; j++) - Bprint(&bso, "%.2ux", cast[fnuxi8[j]] & 0xff); - Bprint(&bso, "\t%P\n", curp); - break; - } - break; - - case D_SCONST: - Bprint(&bso, pcstr, l+s+INITDAT); - for(j=0; j<c; j++) - Bprint(&bso, "%.2ux", p->to.scon[j] & 0xff); - Bprint(&bso, "\t%P\n", curp); - break; - - default: - fl = p->to.offset; - if(p->to.type == D_SIZE) - fl += p->to.sym->size; - if(p->to.type == D_ADDR) { - if(p->to.index != D_STATIC && p->to.index != D_EXTERN) - diag("DADDR type%P", p); - if(p->to.sym) { - if(p->to.sym->type == SUNDEF) - ckoff(p->to.sym, fl); - fl += p->to.sym->value; - if(p->to.sym->type != STEXT && p->to.sym->type != SUNDEF) - fl += INITDAT; - if(dlm) - dynreloc(p->to.sym, l+s+INITDAT, 1); - } - } - cast = (char*)&fl; - switch(c) { - default: - diag("bad nuxi %d %d\n%P", c, i, curp); - break; - case 1: - Bprint(&bso, pcstr, l+s+INITDAT); - for(j=0; j<c; j++) - Bprint(&bso, "%.2ux", cast[inuxi1[j]] & 0xff); - Bprint(&bso, "\t%P\n", curp); - break; - case 2: - Bprint(&bso, pcstr, l+s+INITDAT); - for(j=0; j<c; j++) - Bprint(&bso, "%.2ux", cast[inuxi2[j]] & 0xff); - Bprint(&bso, "\t%P\n", curp); - break; - case 4: - Bprint(&bso, pcstr, l+s+INITDAT); - for(j=0; j<c; j++) - Bprint(&bso, "%.2ux", cast[inuxi4[j]] & 0xff); - Bprint(&bso, "\t%P\n", curp); - break; - } - break; - } - } + return seek(cout, 0, 1) + sizeof(buf.cbuf) - cbc; } int32 @@ -1338,3 +1162,71 @@ rnd(int32 v, int32 r) v -= c; return v; } + +void +genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) +{ + Auto *a; + Sym *s; + int h; + + s = lookup("etext", 0); + if(s->type == STEXT) + put(s, s->name, 'T', s->value, s->size, s->version, 0); + + for(h=0; h<NHASH; h++) { + for(s=hash[h]; s!=S; s=s->hash) { + switch(s->type&~SSUB) { + case SCONST: + case SRODATA: + case SDATA: + case SELFDATA: + case SMACHO: + case SMACHOGOT: + case SWINDOWS: + if(!s->reachable) + continue; + put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); + continue; + + case SBSS: + if(!s->reachable) + continue; + put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype); + continue; + + case SFILE: + put(nil, s->name, 'f', s->value, 0, s->version, 0); + continue; + } + } + } + + for(s = textp; s != nil; s = s->next) { + if(s->text == nil) + continue; + + /* filenames first */ + for(a=s->autom; a; a=a->link) + if(a->type == D_FILE) + put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); + else + if(a->type == D_FILE1) + put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); + + put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); + + /* frame, auto and param after */ + put(nil, ".frame", 'm', s->text->to.offset+4, 0, 0, 0); + + for(a=s->autom; a; a=a->link) + if(a->type == D_AUTO) + put(nil, a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype); + else + if(a->type == D_PARAM) + put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); + } + if(debug['v'] || debug['n']) + Bprint(&bso, "symsize = %ud\n", symsize); + Bflush(&bso); +} diff --git a/src/cmd/8l/doc.go b/src/cmd/8l/doc.go index c8c058684..0bf6f151f 100644 --- a/src/cmd/8l/doc.go +++ b/src/cmd/8l/doc.go @@ -29,8 +29,8 @@ Options new in this version: Write Apple Mach-O binaries (default when $GOOS is darwin) -H7 Write Linux ELF binaries (default when $GOOS is linux) --L dir1,dir2,.. - Search for libraries (package files) in the comma-separated list of directories. +-L dir1 -L dir2 + Search for libraries (package files) in dir1, dir2, etc. The default is the single location $GOROOT/pkg/$GOOS_386. -r dir1:dir2:... Set the dynamic linker search path when using ELF. diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index 495c40d64..daede8879 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -44,7 +44,7 @@ enum #define P ((Prog*)0) #define S ((Sym*)0) -#define TNAME (curtext?curtext->from.sym->name:noname) +#define TNAME (cursym?cursym->name:noname) #define cput(c)\ { *cbp++ = c;\ if(--cbc <= 0)\ @@ -55,6 +55,7 @@ typedef struct Prog Prog; typedef struct Sym Sym; typedef struct Auto Auto; typedef struct Optab Optab; +typedef struct Reloc Reloc; struct Adr { @@ -66,11 +67,7 @@ struct Adr Ieee u0ieee; char *u0sbig; } u0; - union - { - Auto* u1autom; - Sym* u1sym; - } u1; + Sym* sym; short type; uchar index; char scale; @@ -83,18 +80,25 @@ struct Adr #define ieee u0.u0ieee #define sbig u0.u0sbig -#define autom u1.u1autom -#define sym u1.u1sym +struct Reloc +{ + int32 off; + uchar siz; + int32 type; + int32 add; + Sym* sym; +}; struct Prog { Adr from; Adr to; - Prog *forwd; + Prog* forwd; + Prog* comefrom; Prog* link; - Prog* dlink; Prog* pcond; /* work on this */ int32 pc; + int32 spadj; int32 line; short as; char width; /* fake for DATA */ @@ -103,8 +107,10 @@ struct Prog uchar mark; /* work on these */ uchar back; uchar bigjmp; - }; +#define datasize from.scale +#define textflag from.scale + struct Auto { Sym* asym; @@ -115,25 +121,39 @@ struct Auto }; struct Sym { - char *name; + char* name; short type; short version; - short become; - short frame; - uchar subtype; uchar dupok; uchar reachable; uchar dynexport; + uchar special; int32 value; int32 size; int32 sig; - Sym* link; - Prog* text; - Prog* data; + int32 dynid; + int32 plt; + int32 got; + Sym* hash; // in hash table + Sym* next; // in text or data list + Sym* sub; // in sub list + Sym* outer; // container of sub Sym* gotype; char* file; char* dynimpname; char* dynimplib; + + // STEXT + Auto* autom; + Prog* text; + + // SDATA, SBSS + uchar* p; + int32 np; + int32 maxp; + Reloc* r; + int32 nr; + int32 maxr; }; struct Optab { @@ -146,23 +166,28 @@ struct Optab enum { Sxxx, - + + /* order here is order in output file */ STEXT, + SELFDATA, + SMACHOPLT, + SRODATA, SDATA, + SMACHO, /* Mach-O __nl_symbol_ptr */ + SMACHOGOT, + SWINDOWS, SBSS, - SDATA1, + SXREF, + SMACHODYNSTR, + SMACHODYNSYM, + SMACHOINDIRECTPLT, + SMACHOINDIRECTGOT, SFILE, SCONST, - SUNDEF, - - SIMPORT, - SEXPORT, + SDYNIMPORT, - SMACHO, /* pointer to mach-o imported symbol */ - - SFIXED, - SELFDATA, + SSUB = 1<<8, /* sub-symbol, linked from parent via ->sub list */ NHASH = 10007, NHUNK = 100000, @@ -240,9 +265,6 @@ enum Pm = 0x0f, /* 2byte opcode escape */ Pq = 0xff, /* both escape */ Pb = 0xfe, /* byte operands */ - - Roffset = 22, /* no. bits for offset in relocation address */ - Rindex = 10, /* no. bits for index in relocation address */ }; EXTERN union @@ -266,12 +288,11 @@ EXTERN union EXTERN int32 HEADR; EXTERN int32 HEADTYPE; -EXTERN int32 INITDAT; EXTERN int32 INITRND; EXTERN int32 INITTEXT; +EXTERN int32 INITDAT; EXTERN char* INITENTRY; /* entry point */ EXTERN Biobuf bso; -EXTERN int32 bsssize; EXTERN int32 casepc; EXTERN int cbc; EXTERN char* cbp; @@ -279,22 +300,17 @@ EXTERN char* pcstr; EXTERN Auto* curauto; EXTERN Auto* curhist; EXTERN Prog* curp; -EXTERN Prog* curtext; -EXTERN Prog* datap; -EXTERN Prog* edatap; -EXTERN int32 datsize; +EXTERN Sym* cursym; +EXTERN Sym* datap; EXTERN int32 elfdatsize; -EXTERN int32 dynptrsize; EXTERN char debug[128]; EXTERN char literal[32]; -EXTERN Prog* etextp; +EXTERN Sym* etextp; EXTERN Prog* firstp; -EXTERN int xrefresolv; EXTERN uchar ycover[Ymax*Ymax]; EXTERN uchar* andptr; EXTERN uchar and[100]; EXTERN char reg[D_NONE]; -EXTERN Prog* lastp; EXTERN int32 lcsize; EXTERN int maxop; EXTERN int nerrors; @@ -304,30 +320,22 @@ EXTERN char* rpath; EXTERN int32 spsize; EXTERN Sym* symlist; EXTERN int32 symsize; -EXTERN Prog* textp; +EXTERN Sym* textp; EXTERN int32 textsize; -EXTERN int32 textpad; EXTERN int version; EXTERN Prog zprg; EXTERN int dtype; EXTERN int tlsoffset; EXTERN Sym* adrgotype; // type symbol on last Adr read EXTERN Sym* fromgotype; // type symbol on last p->from read - -EXTERN Adr* reloca; -EXTERN int doexp, dlm; -EXTERN int imports, nimports; -EXTERN int exports, nexports; -EXTERN char* EXPTAB; -EXTERN Prog undefp; - -#define UP (&undefp) +EXTERN int elftextsh; extern Optab optab[]; extern char* anames[]; int Aconv(Fmt*); int Dconv(Fmt*); +int Iconv(Fmt*); int Pconv(Fmt*); int Rconv(Fmt*); int Sconv(Fmt*); @@ -336,29 +344,23 @@ Prog* appendp(Prog*); void asmb(void); void asmdyn(void); void asmins(Prog*); -void asmlc(void); -void asmsp(void); void asmsym(void); int32 atolwhex(char*); Prog* brchain(Prog*); Prog* brloop(Prog*); void cflush(void); -void ckoff(Sym*, int32); Prog* copyp(Prog*); +vlong cpos(void); double cputime(void); -void datblk(int32, int32); void diag(char*, ...); void dodata(void); void doelf(void); -void doinit(void); void doprof1(void); void doprof2(void); void dostkoff(void); -void dynreloc(Sym*, uint32, int); int32 entryvalue(void); -void export(void); void follow(void); -void import(void); +void instinit(void); void listinit(void); Sym* lookup(char*, int); void lput(int32); @@ -366,26 +368,20 @@ void lputl(int32); void vputl(uvlong); void strnput(char*, int); void main(int, char*[]); -void mkfwd(void); void* mal(uint32); -Prog* newdata(Sym*, int, int, int); -Prog* newtext(Prog*, Sym*); int opsize(Prog*); void patch(void); Prog* prg(void); int relinv(int); -int32 reuse(Prog*, Sym*); int32 rnd(int32, int32); void s8put(char*); void span(void); void undef(void); -int32 vaddr(Adr*); int32 symaddr(Sym*); void wput(ushort); void wputl(ushort); void xdefine(char*, int, int32); -void xfol(Prog*); -void zaddr(Biobuf*, Adr*, Sym*[]); + uint32 machheadr(void); vlong addaddr(Sym *s, Sym *t); vlong addsize(Sym *s, Sym *t); @@ -410,3 +406,9 @@ void deadcode(void); #pragma varargck type "P" Prog* #pragma varargck type "R" int #pragma varargck type "A" int + +/* Used by ../ld/dwarf.c */ +enum +{ + DWARFREGSP = 4 +}; diff --git a/src/cmd/8l/list.c b/src/cmd/8l/list.c index a5dbba7f8..4e199d767 100644 --- a/src/cmd/8l/list.c +++ b/src/cmd/8l/list.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Printing. + #include "l.h" #include "../ld/lib.h" @@ -40,6 +42,7 @@ listinit(void) fmtinstall('D', Dconv); fmtinstall('S', Sconv); fmtinstall('P', Pconv); + fmtinstall('I', Iconv); } static Prog *bigP; @@ -47,7 +50,6 @@ static Prog *bigP; int Pconv(Fmt *fp) { - char str[STRINGSZ]; Prog *p; p = va_arg(fp->args, Prog*); @@ -55,23 +57,23 @@ Pconv(Fmt *fp) switch(p->as) { case ATEXT: if(p->from.scale) { - sprint(str, "(%d) %A %D,%d,%D", + fmtprint(fp, "(%d) %A %D,%d,%D", p->line, p->as, &p->from, p->from.scale, &p->to); break; } default: - sprint(str, "(%d) %A %D,%D", + fmtprint(fp, "(%d) %A %D,%D", p->line, p->as, &p->from, &p->to); break; case ADATA: - case AINIT: - case ADYNT: - sprint(str, "(%d) %A %D/%d,%D", + case AINIT_: + case ADYNT_: + fmtprint(fp, "(%d) %A %D/%d,%D", p->line, p->as, &p->from, p->from.scale, &p->to); break; } bigP = P; - return fmtstrcpy(fp, str); + return 0; } int @@ -102,15 +104,15 @@ Dconv(Fmt *fp) i = a->type; if(i >= D_INDIR && i < 2*D_INDIR) { if(a->offset) - sprint(str, "%ld(%R)", a->offset, i-D_INDIR); + snprint(str, sizeof str, "%d(%R)", a->offset, i-D_INDIR); else - sprint(str, "(%R)", i-D_INDIR); + snprint(str, sizeof str, "(%R)", i-D_INDIR); goto brk; } switch(i) { default: - sprint(str, "%R", i); + snprint(str, sizeof str, "%R", i); break; case D_NONE: @@ -120,54 +122,54 @@ Dconv(Fmt *fp) case D_BRANCH: if(bigP != P && bigP->pcond != P) if(a->sym != S) - sprint(str, "%lux+%s", bigP->pcond->pc, + snprint(str, sizeof str, "%ux+%s", bigP->pcond->pc, a->sym->name); else - sprint(str, "%lux", bigP->pcond->pc); + snprint(str, sizeof str, "%ux", bigP->pcond->pc); else - sprint(str, "%ld(PC)", a->offset); + snprint(str, sizeof str, "%d(PC)", a->offset); break; case D_EXTERN: - sprint(str, "%s+%ld(SB)", xsymname(a->sym), a->offset); + snprint(str, sizeof str, "%s+%d(SB)", xsymname(a->sym), a->offset); break; case D_STATIC: - sprint(str, "%s<%d>+%ld(SB)", xsymname(a->sym), + snprint(str, sizeof str, "%s<%d>+%d(SB)", xsymname(a->sym), a->sym->version, a->offset); break; case D_AUTO: - sprint(str, "%s+%ld(SP)", xsymname(a->sym), a->offset); + snprint(str, sizeof str, "%s+%d(SP)", xsymname(a->sym), a->offset); break; case D_PARAM: if(a->sym) - sprint(str, "%s+%ld(FP)", a->sym->name, a->offset); + snprint(str, sizeof str, "%s+%d(FP)", a->sym->name, a->offset); else - sprint(str, "%ld(FP)", a->offset); + snprint(str, sizeof str, "%d(FP)", a->offset); break; case D_CONST: - sprint(str, "$%ld", a->offset); + snprint(str, sizeof str, "$%d", a->offset); break; case D_CONST2: - sprint(str, "$%ld-%ld", a->offset, a->offset2); + snprint(str, sizeof str, "$%d-%d", a->offset, a->offset2); break; case D_FCONST: - sprint(str, "$(%.8lux,%.8lux)", a->ieee.h, a->ieee.l); + snprint(str, sizeof str, "$(%.8ux,%.8ux)", a->ieee.h, a->ieee.l); break; case D_SCONST: - sprint(str, "$\"%S\"", a->scon); + snprint(str, sizeof str, "$\"%S\"", a->scon); break; case D_ADDR: a->type = a->index; a->index = D_NONE; - sprint(str, "$%D", a); + snprint(str, sizeof str, "$%D", a); a->index = a->type; a->type = D_ADDR; goto conv; @@ -316,19 +318,48 @@ Sconv(Fmt *fp) return fmtstrcpy(fp, str); } +int +Iconv(Fmt *fp) +{ + int i, n; + uchar *p; + char *s; + Fmt fmt; + + n = fp->prec; + fp->prec = 0; + if(!(fp->flags&FmtPrec) || n < 0) + return fmtstrcpy(fp, "%I"); + fp->flags &= ~FmtPrec; + p = va_arg(fp->args, uchar*); + + // format into temporary buffer and + // call fmtstrcpy to handle padding. + fmtstrinit(&fmt); + for(i=0; i<n; i++) + fmtprint(&fmt, "%.2ux", *p++); + s = fmtstrflush(&fmt); + fmtstrcpy(fp, s); + free(s); + return 0; +} + void diag(char *fmt, ...) { - char buf[STRINGSZ], *tn; + char buf[STRINGSZ], *tn, *sep; va_list arg; - tn = "??none??"; - if(curtext != P && curtext->from.sym != S) - tn = curtext->from.sym->name; + tn = ""; + sep = ""; + if(cursym != S) { + tn = cursym->name; + sep = ": "; + } va_start(arg, fmt); vseprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); - print("%s: %s\n", tn, buf); + print("%s%s%s\n", tn, sep, buf); nerrors++; if(nerrors > 20) { diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c index 1a3ecec1d..18b2112fe 100644 --- a/src/cmd/8l/obj.c +++ b/src/cmd/8l/obj.c @@ -28,11 +28,14 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Reading object files. + #define EXTERN #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" #include "../ld/macho.h" +#include "../ld/dwarf.h" #include "../ld/pe.h" #include <ar.h> @@ -52,32 +55,12 @@ char *thestring = "386"; * -H4 -Tx -Rx is fake MS-DOS .EXE * -H6 -Tx -Rx is Apple Mach-O * -H7 -Tx -Rx is Linux ELF32 - * -H8 -Tx -Rx is Google Native Client + * -H8 -Tx -Rx was Google Native Client * -H9 -Tx -Rx is FreeBSD ELF32 + * -H10 -Tx -Rx is MS Windows PE + * -H11 -Tx -Rx is tiny (os image) */ -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 usage(void) { @@ -88,7 +71,7 @@ usage(void) void main(int argc, char *argv[]) { - int i, c; + int c; Binit(&bso, 1, OWRITE); cout = -1; @@ -152,9 +135,6 @@ main(int argc, char *argv[]) if(strcmp(goos, "darwin") == 0) HEADTYPE = 6; else - if(strcmp(goos, "nacl") == 0) - HEADTYPE = 8; - else if(strcmp(goos, "freebsd") == 0) HEADTYPE = 9; else @@ -164,6 +144,9 @@ main(int argc, char *argv[]) if(strcmp(goos, "tiny") == 0) HEADTYPE = 11; else + if(strcmp(goos, "plan9") == 0) + HEADTYPE = 2; + else print("goos is not known: %s\n", goos); } @@ -208,7 +191,7 @@ main(int argc, char *argv[]) if(INITDAT == -1) INITDAT = 0; if(INITRND == -1) - INITRND = 4096; + INITRND = 1; break; case 3: /* MS-DOS .COM */ HEADR = 0; @@ -229,7 +212,7 @@ main(int argc, char *argv[]) INITRND = 4; HEADR += (INITTEXT & 0xFFFF); if(debug['v']) - Bprint(&bso, "HEADR = 0x%ld\n", HEADR); + Bprint(&bso, "HEADR = 0x%d\n", HEADR); break; case 6: /* apple MACH */ /* @@ -249,7 +232,7 @@ main(int argc, char *argv[]) case 7: /* elf32 executable */ case 9: /* - * Linux ELF uses TLS offsets negative from %gs. + * ELF uses TLS offsets negative from %gs. * Translate 0(GS) and 4(GS) into -8(GS) and -4(GS). * Also known to ../../pkg/runtime/linux/386/sys.s * and ../../libcgo/linux_386.c. @@ -264,30 +247,15 @@ main(int argc, char *argv[]) if(INITRND == -1) INITRND = 4096; break; - case 8: /* native client elf32 executable */ - elfinit(); - HEADR = 4096; - if(INITTEXT == -1) - INITTEXT = 0x20000; - if(INITDAT == -1) - INITDAT = 0; - if(INITRND == -1) - INITRND = 65536; - - // 512 kB of address space for closures. - // (Doesn't take any space in the binary file.) - // Closures are 64 bytes each, so this is 8,192 closures. - textpad = 512*1024; - break; case 10: /* PE executable */ peinit(); - HEADR = PERESERVE; + HEADR = PEFILEHEADR; if(INITTEXT == -1) - INITTEXT = PEBASE+0x1000; + INITTEXT = PEBASE+PESECTHEADR; if(INITDAT == -1) INITDAT = 0; if(INITRND == -1) - INITRND = PEALIGN; + INITRND = PESECTALIGN; break; case 11: tlsoffset = 0; @@ -302,68 +270,14 @@ main(int argc, char *argv[]) break; } if(INITDAT != 0 && INITRND != 0) - print("warning: -D0x%lux is ignored because of -R0x%lux\n", + print("warning: -D0x%ux is ignored because of -R0x%ux\n", INITDAT, INITRND); if(debug['v']) - Bprint(&bso, "HEADER = -H0x%ld -T0x%lux -D0x%lux -R0x%lux\n", + Bprint(&bso, "HEADER = -H0x%d -T0x%ux -D0x%ux -R0x%ux\n", HEADTYPE, INITTEXT, INITDAT, INITRND); Bflush(&bso); - for(i=1; optab[i].as; i++) - if(i != optab[i].as) { - diag("phase error in optab: %d", i); - errorexit(); - } - maxop = i; - - for(i=0; i<Ymax; i++) - ycover[i*Ymax + i] = 1; - - ycover[Yi0*Ymax + Yi8] = 1; - ycover[Yi1*Ymax + Yi8] = 1; - - ycover[Yi0*Ymax + Yi32] = 1; - ycover[Yi1*Ymax + Yi32] = 1; - ycover[Yi8*Ymax + Yi32] = 1; - - ycover[Yal*Ymax + Yrb] = 1; - ycover[Ycl*Ymax + Yrb] = 1; - ycover[Yax*Ymax + Yrb] = 1; - ycover[Ycx*Ymax + Yrb] = 1; - ycover[Yrx*Ymax + Yrb] = 1; - - ycover[Yax*Ymax + Yrx] = 1; - ycover[Ycx*Ymax + Yrx] = 1; - - ycover[Yax*Ymax + Yrl] = 1; - ycover[Ycx*Ymax + Yrl] = 1; - ycover[Yrx*Ymax + Yrl] = 1; - - ycover[Yf0*Ymax + Yrf] = 1; - - ycover[Yal*Ymax + Ymb] = 1; - ycover[Ycl*Ymax + Ymb] = 1; - ycover[Yax*Ymax + Ymb] = 1; - ycover[Ycx*Ymax + Ymb] = 1; - ycover[Yrx*Ymax + Ymb] = 1; - ycover[Yrb*Ymax + Ymb] = 1; - ycover[Ym*Ymax + Ymb] = 1; - - ycover[Yax*Ymax + Yml] = 1; - ycover[Ycx*Ymax + Yml] = 1; - ycover[Yrx*Ymax + Yml] = 1; - ycover[Yrl*Ymax + Yml] = 1; - ycover[Ym*Ymax + Yml] = 1; - - for(i=0; i<D_NONE; i++) { - reg[i] = -1; - if(i >= D_AL && i <= D_BH) - reg[i] = (i-D_AL) & 7; - if(i >= D_AX && i <= D_DI) - reg[i] = (i-D_AX) & 7; - if(i >= D_F0 && i <= D_F0+7) - reg[i] = (i-D_F0) & 7; - } + instinit(); zprg.link = P; zprg.pcond = P; zprg.back = 2; @@ -373,49 +287,25 @@ main(int argc, char *argv[]) zprg.from.scale = 1; zprg.to = zprg.from; - pcstr = "%.6lux "; + pcstr = "%.6ux "; nuxiinit(); histgen = 0; - textp = P; - datap = P; - edatap = P; pc = 0; dtype = 4; version = 0; cbp = buf.cbuf; cbc = sizeof(buf.cbuf); - firstp = prg(); - lastp = firstp; addlibpath("command line", "command line", argv[0], "main"); loadlib(); - deadcode(); - - firstp = firstp->link; - if(firstp == P) - errorexit(); - if(doexp || dlm){ - EXPTAB = "_exporttab"; - zerosig(EXPTAB); - zerosig("etext"); - zerosig("edata"); - zerosig("end"); - if(dlm){ - import(); - HEADTYPE = 2; - INITTEXT = INITDAT = 0; - INITRND = 8; - INITENTRY = EXPTAB; - } - export(); - } patch(); follow(); doelf(); if(HEADTYPE == 6) domacho(); - dodata(); + if(HEADTYPE == 10) + dope(); dostkoff(); if(debug['p']) if(debug['1']) @@ -423,14 +313,18 @@ main(int argc, char *argv[]) else doprof2(); span(); - doinit(); - if(HEADTYPE == 10) - dope(); + addexport(); + textaddress(); + pclntab(); + symtab(); + dodata(); + address(); + reloc(); asmb(); undef(); if(debug['v']) { Bprint(&bso, "%5.2f cpu time\n", cputime()); - Bprint(&bso, "%ld symbols\n", nsymbol); + Bprint(&bso, "%d symbols\n", nsymbol); Bprint(&bso, "%d sizeof adr\n", sizeof(Adr)); Bprint(&bso, "%d sizeof prog\n", sizeof(Prog)); } @@ -439,8 +333,19 @@ main(int argc, char *argv[]) errorexit(); } -void -zaddr(Biobuf *f, Adr *a, Sym *h[]) +static Sym* +zsym(char *pn, Biobuf *f, Sym *h[]) +{ + int o; + + o = Bgetc(f); + if(o < 0 || o >= NSYM || h[o] == nil) + mangle(pn); + return h[o]; +} + +static void +zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) { int t; int32 l; @@ -465,7 +370,7 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) } a->sym = S; if(t & T_SYM) - a->sym = h[Bgetc(f)]; + a->sym = zsym(pn, f, h); if(t & T_FCONST) { a->ieee.l = Bget4(f); a->ieee.h = Bget4(f); @@ -479,7 +384,7 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) a->type = Bgetc(f); adrgotype = S; if(t & T_GOTYPE) - adrgotype = h[Bgetc(f)]; + adrgotype = zsym(pn, f, h); t = a->type; if(t == D_INDIR+D_GS) @@ -526,7 +431,7 @@ void ldobj1(Biobuf *f, char *pkg, int64 len, char *pn) { int32 ipc; - Prog *p, *t; + Prog *p; int v, o, r, skip; Sym *h[NSYM], *s, *di; uint32 sig; @@ -534,7 +439,9 @@ ldobj1(Biobuf *f, char *pkg, int64 len, char *pn) int32 eof; char *name, *x; char src[1024]; + Prog *lastp; + lastp = nil; ntext = 0; eof = Boffset(f) + len; di = S; @@ -591,7 +498,7 @@ loop: if(sig != 0){ if(s->sig != 0 && s->sig != sig) diag("incompatible type signatures" - "%lux(%s) and %lux(%s) for %s", + "%ux(%s) and %ux(%s) for %s", s->sig, s->file, sig, pn, s->name); s->sig = sig; s->file = pn; @@ -599,6 +506,8 @@ loop: if(debug['W']) print(" ANAME %s\n", s->name); + if(o < 0 || o >= nelem(h)) + mangle(pn); h[o] = s; if((v == D_EXTERN || v == D_STATIC) && s->type == 0) s->type = SXREF; @@ -613,6 +522,7 @@ loop: histfrogp++; } else collapsefrog(s); + dwarfaddfrag(s->value, s->name); } goto loop; } @@ -623,9 +533,9 @@ loop: p->back = 2; p->ft = 0; p->tt = 0; - zaddr(f, &p->from, h); + zaddr(pn, f, &p->from, h); fromgotype = adrgotype; - zaddr(f, &p->to, h); + zaddr(pn, f, &p->to, h); if(debug['W']) print("%P\n", p); @@ -647,10 +557,10 @@ loop: case AEND: histtoauto(); - if(curtext != P) - curtext->to.autom = curauto; + if(cursym != nil && cursym->text) + cursym->autom = curauto; curauto = 0; - curtext = P; + cursym = nil; if(Boffset(f) == eof) return; goto newloop; @@ -659,89 +569,41 @@ loop: s = p->from.sym; if(s->type == 0 || s->type == SXREF) { s->type = SBSS; - s->value = 0; + s->size = 0; } - if(s->type != SBSS) { + if(s->type != SBSS && !s->dupok) { diag("%s: redefinition: %s in %s", pn, s->name, TNAME); s->type = SBSS; - s->value = 0; + s->size = 0; } - if(p->to.offset > s->value) - s->value = p->to.offset; + if(p->to.offset > s->size) + s->size = p->to.offset; if(p->from.scale & DUPOK) s->dupok = 1; + if(p->from.scale & RODATA) + s->type = SRODATA; goto loop; - case ADYNT: - if(p->to.sym == S) { - diag("DYNT without a sym\n%P", p); - break; - } - di = p->to.sym; - p->from.scale = 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_ADDR; - p->to.index = D_EXTERN; - goto data; - - 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; - goto data; - case ADATA: - data: // Assume that AGLOBL comes after ADATA. // If we've seen an AGLOBL that said this sym was DUPOK, // ignore any more ADATA we see, which must be // redefinitions. s = p->from.sym; - if(s != S && s->dupok) { + if(s->dupok) { // if(debug['v']) // Bprint(&bso, "skipping %s in %s: dupok\n", s->name, pn); goto loop; } - if(s != S) { - p->dlink = s->data; - s->data = p; - if(s->file == nil) - s->file = pn; - else if(s->file != pn) { - diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); - errorexit(); - } + if(s->file == nil) + s->file = pn; + else if(s->file != pn) { + diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn); + errorexit(); } - if(edatap == P) - datap = p; - else - edatap->link = p; - edatap = p; - p->link = P; + savedata(s, p); + unmal(p, sizeof *p); goto loop; case AGOK: @@ -751,9 +613,9 @@ loop: case ATEXT: s = p->from.sym; - if(s == S) { - diag("%s: no TEXT symbol: %P", pn, p); - errorexit(); + if(s->text != nil) { + diag("%s: %s: redefinition", pn, s->name); + return; } if(ntext++ == 0 && s->type != 0 && s->type != SXREF) { /* redefinition, so file has probably been seen before */ @@ -761,13 +623,19 @@ loop: diag("skipping: %s: redefinition: %s", pn, s->name); return; } - if(curtext != P) { + if(cursym != nil && cursym->text) { histtoauto(); - curtext->to.autom = curauto; + cursym->autom = curauto; curauto = 0; } skip = 0; - curtext = p; + if(etextp) + etextp->next = s; + else + textp = s; + etextp = s; + s->text = p; + cursym = s; if(s->type != 0 && s->type != SXREF) { if(p->from.scale & DUPOK) { skip = 1; @@ -775,7 +643,10 @@ loop: } diag("%s: redefinition: %s\n%P", pn, s->name, p); } - newtext(p, s); + s->type = STEXT; + s->value = pc; + lastp = p; + p->pc = pc++; goto loop; case AFMOVF: @@ -791,24 +662,12 @@ loop: goto casdef; if(p->from.type == D_FCONST) { /* size sb 9 max */ - sprint(literal, "$%lux", ieeedtof(&p->from.ieee)); + sprint(literal, "$%ux", 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_EXTERN; - t->from.sym = s; - t->from.scale = 4; - t->to = p->from; - if(edatap == P) - datap = t; - else - edatap->link = t; - edatap = t; - t->link = P; + s->type = SDATA; + adduint32(s, ieeedtof(&p->from.ieee)); + s->reachable = 0; } p->from.type = D_EXTERN; p->from.sym = s; @@ -829,25 +688,14 @@ loop: goto casdef; if(p->from.type == D_FCONST) { /* size sb 18 max */ - sprint(literal, "$%lux.%lux", + sprint(literal, "$%ux.%ux", 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_EXTERN; - t->from.sym = s; - t->from.scale = 8; - t->to = p->from; - if(edatap == P) - datap = t; - else - edatap->link = t; - edatap = t; - t->link = P; + s->type = SDATA; + adduint32(s, p->from.ieee.l); + adduint32(s, p->from.ieee.h); + s->reachable = 0; } p->from.type = D_EXTERN; p->from.sym = s; @@ -859,13 +707,18 @@ loop: default: if(skip) nopout(p); + p->pc = pc; + pc++; if(p->to.type == D_BRANCH) p->to.offset += ipc; + if(lastp == nil) { + if(p->as != ANOP) + diag("unexpected instruction: %P", p); + goto loop; + } lastp->link = p; lastp = p; - p->pc = pc; - pc++; goto loop; } goto loop; @@ -905,153 +758,3 @@ appendp(Prog *q) p->line = q->line; return p; } - -void -doprof1(void) -{ - Sym *s; - int32 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) { - if(p->as == ATEXT) { - q = prg(); - q->line = p->line; - q->link = datap; - datap = q; - q->as = ADATA; - q->from.type = D_EXTERN; - q->from.offset = n*4; - q->from.sym = s; - q->from.scale = 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 = AADDL; - p->from.type = D_CONST; - p->from.offset = 1; - p->to.type = 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_EXTERN; - q->from.sym = s; - q->from.scale = 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) { - if(p->as == ATEXT) { - if(p->from.sym == s2) { - p->from.scale = 1; - ps2 = p; - } - if(p->from.sym == s4) { - p->from.scale = 1; - ps4 = p; - } - } - } - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - curtext = p; - - if(p->from.scale & NOPROF) { /* dont profile */ - for(;;) { - q = p->link; - if(q == P) - break; - if(q->as == ATEXT) - break; - p = q; - } - continue; - } - - /* - * JMPL profin - */ - q = prg(); - q->line = p->line; - q->pc = p->pc; - q->link = p->link; - p->link = q; - p = q; - p->as = ACALL; - p->to.type = D_BRANCH; - p->pcond = 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; - - /* - * JAL profout - */ - p->as = ACALL; - p->from = zprg.from; - p->to = zprg.to; - p->to.type = D_BRANCH; - p->pcond = ps4; - p->to.sym = s4; - - p = q; - - continue; - } - } -} diff --git a/src/cmd/8l/optab.c b/src/cmd/8l/optab.c index 5b7be692e..fceab785d 100644 --- a/src/cmd/8l/optab.c +++ b/src/cmd/8l/optab.c @@ -696,8 +696,8 @@ Optab optab[] = { AFYL2X, ynone, Px, 0xd9, 0xf1 }, { AFYL2XP1, ynone, Px, 0xd9, 0xf9 }, { AEND }, - { ADYNT }, - { AINIT }, + { ADYNT_ }, + { AINIT_ }, { ASIGNAME }, { ACMPXCHGB, yrb_mb, Pm, 0xb0 }, { ACMPXCHGL, yrl_ml, Pm, 0xb1 }, diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c index ace640d22..6e387b0b5 100644 --- a/src/cmd/8l/pass.c +++ b/src/cmd/8l/pass.c @@ -28,9 +28,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Code and data passes. + #include "l.h" #include "../ld/lib.h" +static void xfol(Prog*, Prog**); + // see ../../pkg/runtime/proc.c:/StackGuard enum { @@ -38,138 +42,6 @@ enum StackBig = 4096, }; -void -dodata(void) -{ - int i; - Sym *s; - Prog *p; - int32 t, u; - - if(debug['v']) - Bprint(&bso, "%5.2f dodata\n", cputime()); - Bflush(&bso); - for(p = datap; p != P; p = p->link) { - s = p->from.sym; - if(p->as == ADYNT || p->as == AINIT) - s->value = dtype; - if(s->type == SBSS) - s->type = SDATA; - if(s->type != SDATA && s->type != SELFDATA) - diag("initialize non-data (%d): %s\n%P", - s->type, s->name, p); - t = p->from.offset + p->width; - if(t > s->value) - diag("initialize bounds (%ld): %s\n%P", - s->value, s->name, p); - } - - /* allocate elf guys - must be segregated from real data */ - datsize = 0; - for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) { - if(!s->reachable) - continue; - if(s->type != SELFDATA) - continue; - t = rnd(s->value, 4); - s->size = t; - s->value = datsize; - datsize += t; - } - elfdatsize = datsize; - - /* allocate small guys */ - for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) { - if(!s->reachable) - continue; - if(s->type != SDATA) - if(s->type != SBSS) - continue; - t = s->value; - if(t == 0 && s->name[0] != '.') { - diag("%s: no size", s->name); - t = 1; - } - t = rnd(t, 4); - s->size = t; - s->value = t; - if(t > MINSIZ) - continue; - s->value = datsize; - datsize += t; - s->type = SDATA1; - } - - /* allocate the rest of the data */ - for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) { - if(s->type != SDATA) { - if(s->type == SDATA1) - s->type = SDATA; - continue; - } - t = s->value; - s->size = t; - s->value = datsize; - datsize += t; - } - - if(debug['j']) { - /* - * pad data with bss that fits up to next - * 8k boundary, then push data to 8k - */ - u = rnd(datsize, 8192); - u -= datsize; - for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) { - if(!s->reachable) - continue; - if(s->type != SBSS) - continue; - t = s->value; - if(t > u) - continue; - u -= t; - s->size = t; - s->value = datsize; - s->type = SDATA; - datsize += t; - } - datsize += u; - } - - if(dynptrsize > 0) { - /* dynamic pointer section between data and bss */ - datsize = rnd(datsize, 4); - } - - /* now the bss */ - bsssize = 0; - for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) { - if(!s->reachable) - continue; - if(s->type != SBSS) - continue; - t = s->value; - s->size = t; - s->value = bsssize + dynptrsize + datsize; - bsssize += t; - } - - xdefine("data", SBSS, 0); - xdefine("edata", SBSS, datsize); - xdefine("end", SBSS, dynptrsize + bsssize + datsize); - - if(debug['s'] || HEADTYPE == 8) - xdefine("symdat", SFIXED, 0); - else - xdefine("symdat", SFIXED, SYMDATVA); -} - Prog* brchain(Prog *p) { @@ -186,19 +58,53 @@ brchain(Prog *p) void follow(void) { + Prog *firstp, *lastp; if(debug['v']) Bprint(&bso, "%5.2f follow\n", cputime()); Bflush(&bso); - firstp = prg(); - lastp = firstp; - xfol(textp); - lastp->link = P; - firstp = firstp->link; + + for(cursym = textp; cursym != nil; cursym = cursym->next) { + firstp = prg(); + lastp = firstp; + xfol(cursym->text, &lastp); + lastp->link = nil; + cursym->text = firstp->link; + } } -void -xfol(Prog *p) +static int +nofollow(int a) +{ + switch(a) { + case AJMP: + case ARET: + case AIRETL: + case AIRETW: + return 1; + } + return 0; +} + +static int +pushpop(int a) +{ + switch(a) { + case APUSHL: + case APUSHFL: + case APUSHW: + case APUSHFW: + case APOPL: + case APOPFL: + case APOPW: + case APOPFW: + return 1; + } + return 0; +} + +static void +xfol(Prog *p, Prog **last) { Prog *q; int i; @@ -207,46 +113,31 @@ xfol(Prog *p) loop: if(p == P) return; - if(p->as == ATEXT) - curtext = p; - if(!curtext->from.sym->reachable) { - p = p->pcond; - goto loop; - } if(p->as == AJMP) if((q = p->pcond) != P && q->as != ATEXT) { + /* mark instruction as done and continue layout at target of jump */ p->mark = 1; p = q; if(p->mark == 0) goto loop; } if(p->mark) { - /* copy up to 4 instructions to avoid branch */ + /* + * p goes here, but already used it elsewhere. + * copy up to 4 instructions or else branch to other copy. + */ for(i=0,q=p; i<4; i++,q=q->link) { if(q == P) break; - if(q == lastp) + if(q == *last) break; a = q->as; if(a == ANOP) { i--; continue; } - switch(a) { - case AJMP: - case ARET: - case AIRETL: - - case APUSHL: - case APUSHFL: - case APUSHW: - case APUSHFW: - case APOPL: - case APOPFL: - case APOPW: - case APOPFW: - goto brk; - } + if(nofollow(a) || pushpop(a)) + break; // NOTE(rsc): arm does goto copy if(q->pcond == P || q->pcond->mark) continue; if(a == ACALL || a == ALOOP) @@ -259,8 +150,8 @@ loop: q = copyp(p); p = p->link; q->mark = 1; - lastp->link = q; - lastp = q; + (*last)->link = q; + *last = q; if(q->as != a || q->pcond == P || q->pcond->mark) continue; @@ -268,14 +159,13 @@ loop: p = q->pcond; q->pcond = q->link; q->link = p; - xfol(q->link); + xfol(q->link, last); p = q->link; if(p->mark) return; goto loop; } } /* */ - brk:; q = prg(); q->as = AJMP; q->line = p->line; @@ -284,14 +174,22 @@ loop: q->pcond = p; p = q; } + + /* emit p */ p->mark = 1; - lastp->link = p; - lastp = p; + (*last)->link = p; + *last = p; a = p->as; - if(a == AJMP || a == ARET || a == AIRETL) + + /* continue loop with what comes after p */ + if(nofollow(a)) return; - if(p->pcond != P) - if(a != ACALL) { + if(p->pcond != P && a != ACALL) { + /* + * some kind of conditional branch. + * recurse to follow one path. + * continue loop on the other. + */ q = brchain(p->link); if(q != P && q->mark) if(a != ALOOP) { @@ -299,7 +197,7 @@ loop: p->link = p->pcond; p->pcond = q; } - xfol(p->link); + xfol(p->link, last); q = brchain(p->pcond); if(q->mark) { p->pcond = q; @@ -339,28 +237,6 @@ relinv(int a) } void -doinit(void) -{ - Sym *s; - Prog *p; - int x; - - for(p = datap; p != P; p = p->link) { - x = p->to.type; - if(x != D_EXTERN && x != D_STATIC) - continue; - s = p->to.sym; - if(s->type == 0 || s->type == SXREF) - diag("undefined %s initializer of %s", - s->name, p->from.sym->name); - p->to.offset += s->value; - p->to.type = D_CONST; - if(s->type == SDATA || s->type == SBSS) - p->to.offset += INITDAT; - } -} - -void patch(void) { int32 c; @@ -377,116 +253,106 @@ patch(void) Bflush(&bso); s = lookup("exit", 0); vexit = s->value; - for(p = firstp; p != P; p = p->link) { - if(HEADTYPE == 10) { - // Convert - // op n(GS), reg - // to - // MOVL 0x2C(FS), reg - // op n(reg), reg - // The purpose of this patch is to fix some accesses - // to extern register variables (TLS) on Windows, as - // a different method is used to access them. - if(p->from.type == D_INDIR+D_GS - && p->to.type >= D_AX && p->to.type <= D_DI) { - q = appendp(p); - q->from = p->from; - q->from.type += p->to.type-D_GS; - q->to = p->to; - q->as = p->as; - p->as = AMOVL; - p->from.type = D_INDIR+D_FS; - p->from.offset = 0x2C; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + for(p = cursym->text; p != P; p = p->link) { + if(HEADTYPE == 10) { // Windows + // Convert + // op n(GS), reg + // to + // MOVL 0x2C(FS), reg + // op n(reg), reg + // The purpose of this patch is to fix some accesses + // to extern register variables (TLS) on Windows, as + // a different method is used to access them. + if(p->from.type == D_INDIR+D_GS + && p->to.type >= D_AX && p->to.type <= D_DI) { + q = appendp(p); + q->from = p->from; + q->from.type = D_INDIR + p->to.type; + q->to = p->to; + q->as = p->as; + p->as = AMOVL; + p->from.type = D_INDIR+D_FS; + p->from.offset = 0x2C; + } } - } - if(p->as == ATEXT) - curtext = p; - if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH)) { - s = p->to.sym; - if(s) { - if(debug['c']) - Bprint(&bso, "%s calls %s\n", TNAME, s->name); - switch(s->type) { - default: - /* diag prints TNAME first */ - diag("undefined: %s", s->name); - s->type = STEXT; - s->value = vexit; - continue; // avoid more error messages - case STEXT: - p->to.offset = s->value; - break; - case SUNDEF: - p->pcond = UP; - p->to.offset = 0; - break; + if(HEADTYPE == 7) { // Linux + // Running binaries under Xen requires using + // MOVL 0(GS), reg + // and then off(reg) instead of saying off(GS) directly + // when the offset is negative. + if(p->from.type == D_INDIR+D_GS && p->from.offset < 0 + && p->to.type >= D_AX && p->to.type <= D_DI) { + q = appendp(p); + q->from = p->from; + q->from.type = D_INDIR + p->to.type; + q->to = p->to; + q->as = p->as; + p->as = AMOVL; + p->from.type = D_INDIR+D_GS; + p->from.offset = 0; } - p->to.type = D_BRANCH; } - } - if(p->to.type != D_BRANCH || p->pcond == UP) - continue; - c = p->to.offset; - for(q = firstp; q != P;) { - if(q->forwd != P) - if(c >= q->forwd->pc) { - q = q->forwd; + if(HEADTYPE == 2) { // Plan 9 + if(p->from.type == D_INDIR+D_GS + && p->to.type >= D_AX && p->to.type <= D_DI) { + p->as = AMOVL; + p->from.type = D_ADDR+D_STATIC; + p->from.offset += 0xdfffefc0; + } + } + if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH)) { + s = p->to.sym; + if(s) { + if(debug['c']) + Bprint(&bso, "%s calls %s\n", TNAME, s->name); + if((s->type&~SSUB) != STEXT) { + /* diag prints TNAME first */ + diag("undefined: %s", s->name); + s->type = STEXT; + s->value = vexit; + continue; // avoid more error messages + } + if(s->text == nil) + continue; + p->to.type = D_BRANCH; + p->to.offset = s->text->pc; + p->pcond = s->text; + continue; + } + } + if(p->to.type != D_BRANCH) continue; + c = p->to.offset; + for(q = cursym->text; q != P;) { + if(c == q->pc) + break; + if(q->forwd != P && c >= q->forwd->pc) + q = q->forwd; + else + q = q->link; } - if(c == q->pc) - break; - q = q->link; - } - if(q == P) { - diag("branch out of range in %s\n%P", TNAME, p); - p->to.type = D_NONE; + if(q == P) { + diag("branch out of range in %s (%#ux)\n%P [%s]", + TNAME, c, p, p->to.sym ? p->to.sym->name : "<nil>"); + p->to.type = D_NONE; + } + p->pcond = q; } - p->pcond = q; } - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - p->mark = 0; /* initialization for follow */ - if(p->pcond != P && p->pcond != UP) { - p->pcond = brloop(p->pcond); - if(p->pcond != P) - if(p->to.type == D_BRANCH) - p->to.offset = p->pcond->pc; - } - } -} + for(cursym = textp; cursym != nil; cursym = cursym->next) { + if(cursym->text == nil || cursym->p != nil) + continue; -#define LOG 5 -void -mkfwd(void) -{ - Prog *p; - int i; - int32 dwn[LOG], cnt[LOG]; - Prog *lst[LOG]; - - for(i=0; i<LOG; i++) { - if(i == 0) - cnt[i] = 1; else - cnt[i] = LOG * cnt[i-1]; - dwn[i] = 1; - lst[i] = P; - } - i = 0; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - i--; - if(i < 0) - i = LOG-1; - p->forwd = P; - dwn[i]--; - if(dwn[i] <= 0) { - dwn[i] = cnt[i]; - if(lst[i] != P) - lst[i]->forwd = p; - lst[i] = p; + for(p = cursym->text; p != P; p = p->link) { + p->mark = 0; /* initialization for follow */ + if(p->pcond != P) { + p->pcond = brloop(p->pcond); + if(p->pcond != P) + if(p->to.type == D_BRANCH) + p->to.offset = p->pcond->pc; + } } } } @@ -513,286 +379,221 @@ dostkoff(void) { Prog *p, *q, *q1; int32 autoffset, deltasp; - int a, f, curframe, curbecome, maxbecome; + int a; Prog *pmorestack; Sym *symmorestack; pmorestack = P; symmorestack = lookup("runtime.morestack", 0); - if(symmorestack->type == STEXT) - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - if(p->from.sym == symmorestack) { - pmorestack = p; - p->from.scale |= NOSPLIT; - break; - } - } - } - if(pmorestack == P) + if(symmorestack->type != STEXT) diag("runtime.morestack not defined"); + else { + pmorestack = symmorestack->text; + symmorestack->text->from.scale |= NOSPLIT; + } - curframe = 0; - curbecome = 0; - maxbecome = 0; - curtext = 0; - for(p = firstp; p != P; p = p->link) { - - /* find out how much arg space is used in this TEXT */ - if(p->to.type == (D_INDIR+D_SP)) - if(p->to.offset > curframe) - curframe = p->to.offset; - - switch(p->as) { - case ATEXT: - if(curtext && curtext->from.sym) { - curtext->from.sym->frame = curframe; - curtext->from.sym->become = curbecome; - if(curbecome > maxbecome) - maxbecome = curbecome; - } - curframe = 0; - curbecome = 0; - - curtext = p; - break; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + if(cursym->text == nil || cursym->text->link == nil) + continue; - case ARET: - /* special form of RET is BECOME */ - if(p->from.type == D_CONST) - if(p->from.offset > curbecome) - curbecome = p->from.offset; - break; - } - } - if(curtext && curtext->from.sym) { - curtext->from.sym->frame = curframe; - curtext->from.sym->become = curbecome; - if(curbecome > maxbecome) - maxbecome = curbecome; - } + p = cursym->text; + autoffset = p->to.offset; + if(autoffset < 0) + autoffset = 0; + + q = P; + q1 = P; + if(pmorestack != P) + if(!(p->from.scale & NOSPLIT)) { + p = appendp(p); // load g into CX + switch(HEADTYPE) { + case 10: // Windows + p->as = AMOVL; + p->from.type = D_INDIR+D_FS; + p->from.offset = 0x2c; + p->to.type = D_CX; - if(debug['b']) - print("max become = %d\n", maxbecome); - xdefine("ALEFbecome", STEXT, maxbecome); + p = appendp(p); + p->as = AMOVL; + p->from.type = D_INDIR+D_CX; + p->from.offset = 0; + p->to.type = D_CX; + break; + + case 7: // Linux + p->as = AMOVL; + p->from.type = D_INDIR+D_GS; + p->from.offset = 0; + p->to.type = D_CX; - curtext = 0; - for(p = firstp; p != P; p = p->link) { - switch(p->as) { - case ATEXT: - curtext = p; - break; - case ACALL: - if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) { - f = maxbecome - curtext->from.sym->frame; - if(f <= 0) - break; - /* calling a become or calling a variable */ - if(p->to.sym == S || p->to.sym->become) { - curtext->to.offset += f; - if(debug['b']) { - curp = p; - print("%D calling %D increase %d\n", - &curtext->from, &p->to, f); - } - } + p = appendp(p); + p->as = AMOVL; + p->from.type = D_INDIR+D_CX; + p->from.offset = tlsoffset + 0; + p->to.type = D_CX; + break; + + case 2: // Plan 9 + p->as = AMOVL; + p->from.type = D_ADDR+D_STATIC; + p->from.offset = 0xdfffefc0; + p->to.type = D_CX; + break; + + default: + p->as = AMOVL; + p->from.type = D_INDIR+D_GS; + p->from.offset = tlsoffset + 0; + p->to.type = D_CX; } - break; - } - } - autoffset = 0; - deltasp = 0; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - curtext = p; - autoffset = p->to.offset; - if(autoffset < 0) - autoffset = 0; - - q = P; - q1 = P; - if(pmorestack != P) - if(!(p->from.scale & NOSPLIT)) { - p = appendp(p); // load g into CX - if(HEADTYPE == 10) { - p->as = AMOVL; - p->from.type = D_INDIR+D_FS; - p->from.offset = 0x2c; - p->to.type = D_CX; + if(debug['K']) { + // 8l -K means check not only for stack + // overflow but stack underflow. + // On underflow, INT 3 (breakpoint). + // Underflow itself is rare but this also + // catches out-of-sync stack guard info. + p = appendp(p); + p->as = ACMPL; + p->from.type = D_INDIR+D_CX; + p->from.offset = 4; + p->to.type = D_SP; - p = appendp(p); - p->as = AMOVL; - p->from.type = D_INDIR+D_CX; - p->from.offset = 0; - p->to.type = D_CX; - } else { - p->as = AMOVL; - p->from.type = D_INDIR+D_GS; - p->from.offset = tlsoffset + 0; - p->to.type = D_CX; - } + p = appendp(p); + p->as = AJCC; + p->to.type = D_BRANCH; + p->to.offset = 4; + q1 = p; - if(debug['K']) { - // 8l -K means check not only for stack - // overflow but stack underflow. - // On underflow, INT 3 (breakpoint). - // Underflow itself is rare but this also - // catches out-of-sync stack guard info. - p = appendp(p); - p->as = ACMPL; - p->from.type = D_INDIR+D_CX; - p->from.offset = 4; - p->to.type = D_SP; + p = appendp(p); + p->as = AINT; + p->from.type = D_CONST; + p->from.offset = 3; + + p = appendp(p); + p->as = ANOP; + q1->pcond = p; + } + if(autoffset < StackBig) { // do we need to call morestack + if(autoffset <= StackSmall) { + // small stack p = appendp(p); - p->as = AJCC; - p->to.type = D_BRANCH; - p->to.offset = 4; - q1 = p; - + p->as = ACMPL; + p->from.type = D_SP; + p->to.type = D_INDIR+D_CX; + } else { + // large stack p = appendp(p); - p->as = AINT; - p->from.type = D_CONST; - p->from.offset = 3; - } - - if(autoffset < StackBig) { // do we need to call morestack - if(autoffset <= StackSmall) { - // small stack - p = appendp(p); - p->as = ACMPL; - p->from.type = D_SP; - p->to.type = D_INDIR+D_CX; - if(q1) { - q1->pcond = p; - q1 = P; - } - } else { - // large stack - p = appendp(p); - p->as = ALEAL; - p->from.type = D_INDIR+D_SP; - p->from.offset = -(autoffset-StackSmall); - p->to.type = D_AX; - if(q1) { - q1->pcond = p; - q1 = P; - } - - p = appendp(p); - p->as = ACMPL; - p->from.type = D_AX; - p->to.type = D_INDIR+D_CX; - } + p->as = ALEAL; + p->from.type = D_INDIR+D_SP; + p->from.offset = -(autoffset-StackSmall); + p->to.type = D_AX; - // common p = appendp(p); - p->as = AJHI; - p->to.type = D_BRANCH; - p->to.offset = 4; - q = p; + p->as = ACMPL; + p->from.type = D_AX; + p->to.type = D_INDIR+D_CX; } - p = appendp(p); // save frame size in DX - p->as = AMOVL; - p->to.type = D_DX; - /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */ - p->from.type = D_CONST; - if(autoffset+160 > 4096) - p->from.offset = (autoffset+160) & ~7LL; - - p = appendp(p); // save arg size in AX - p->as = AMOVL; - p->to.type = D_AX; - p->from.type = D_CONST; - p->from.offset = curtext->to.offset2; - + // common p = appendp(p); - p->as = ACALL; + p->as = AJHI; p->to.type = D_BRANCH; - p->pcond = pmorestack; - p->to.sym = symmorestack; - + p->to.offset = 4; + q = p; } - if(q != P) - q->pcond = p->link; + p = appendp(p); // save frame size in DX + p->as = AMOVL; + p->to.type = D_DX; + /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */ + p->from.type = D_CONST; + if(autoffset+160 > 4096) + p->from.offset = (autoffset+160) & ~7LL; + + p = appendp(p); // save arg size in AX + p->as = AMOVL; + p->to.type = D_AX; + p->from.type = D_CONST; + p->from.offset = cursym->text->to.offset2; + + p = appendp(p); + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = pmorestack; + p->to.sym = symmorestack; - if(autoffset) { - p = appendp(p); - p->as = AADJSP; - p->from.type = D_CONST; - p->from.offset = autoffset; - if(q != P) - q->pcond = p; - } - deltasp = autoffset; - } - a = p->from.type; - if(a == D_AUTO) - p->from.offset += deltasp; - if(a == D_PARAM) - p->from.offset += deltasp + 4; - a = p->to.type; - if(a == D_AUTO) - p->to.offset += deltasp; - if(a == D_PARAM) - p->to.offset += deltasp + 4; - - switch(p->as) { - default: - continue; - case APUSHL: - case APUSHFL: - deltasp += 4; - continue; - case APUSHW: - case APUSHFW: - deltasp += 2; - continue; - case APOPL: - case APOPFL: - deltasp -= 4; - continue; - case APOPW: - case APOPFW: - deltasp -= 2; - continue; - case ARET: - break; } - if(autoffset != deltasp) - diag("unbalanced PUSH/POP"); - if(p->from.type == D_CONST) - goto become; + if(q != P) + q->pcond = p->link; if(autoffset) { - q = p; p = appendp(p); - p->as = ARET; - - q->as = AADJSP; - q->from.type = D_CONST; - q->from.offset = -autoffset; + p->as = AADJSP; + p->from.type = D_CONST; + p->from.offset = autoffset; + p->spadj = autoffset; + if(q != P) + q->pcond = p; + } + deltasp = autoffset; + + for(; p != P; p = p->link) { + a = p->from.type; + if(a == D_AUTO) + p->from.offset += deltasp; + if(a == D_PARAM) + p->from.offset += deltasp + 4; + a = p->to.type; + if(a == D_AUTO) + p->to.offset += deltasp; + if(a == D_PARAM) + p->to.offset += deltasp + 4; + + switch(p->as) { + default: + continue; + case APUSHL: + case APUSHFL: + deltasp += 4; + p->spadj = 4; + continue; + case APUSHW: + case APUSHFW: + deltasp += 2; + p->spadj = 2; + continue; + case APOPL: + case APOPFL: + deltasp -= 4; + p->spadj = -4; + continue; + case APOPW: + case APOPFW: + deltasp -= 2; + p->spadj = -2; + continue; + case ARET: + break; + } + + if(autoffset != deltasp) + diag("unbalanced PUSH/POP"); + + if(autoffset) { + q = p; + p = appendp(p); + p->as = ARET; + + q->as = AADJSP; + q->from.type = D_CONST; + q->from.offset = -autoffset; + p->spadj = -autoffset; + } } - continue; - - become: - q = p; - p = appendp(p); - p->as = AJMP; - p->to = q->to; - p->pcond = q->pcond; - - q->as = AADJSP; - q->from = zprg.from; - q->from.type = D_CONST; - q->from.offset = -autoffset; - q->to = zprg.to; - continue; } } @@ -843,176 +644,7 @@ undef(void) Sym *s; for(i=0; i<NHASH; i++) - for(s = hash[i]; s != S; s = s->link) + for(s = hash[i]; s != S; s = s->hash) if(s->type == SXREF) - diag("%s: not defined", s->name); -} - -void -import(void) -{ - int i; - Sym *s; - - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type == SXREF && (nimports == 0 || s->subtype == SIMPORT)){ - if(s->value != 0) - diag("value != 0 on SXREF"); - undefsym(s); - Bprint(&bso, "IMPORT: %s sig=%lux v=%ld\n", s->name, s->sig, s->value); - if(debug['S']) - s->sig = 0; - } -} - -void -ckoff(Sym *s, int32 v) -{ - if(v < 0 || v >= 1<<Roffset) - diag("relocation offset %ld for %s out of range", v, s->name); -} - -Prog* -newdata(Sym *s, int o, int w, int t) -{ - Prog *p; - - p = prg(); - if(edatap == P) - datap = p; - else - edatap->link = p; - edatap = p; - p->as = ADATA; - p->width = w; - p->from.scale = w; - p->from.type = t; - p->from.sym = s; - p->from.offset = o; - p->to.type = D_CONST; - p->dlink = s->data; - s->data = p; - return p; -} - -Prog* -newtext(Prog *p, Sym *s) -{ - if(p == P) { - p = prg(); - p->as = ATEXT; - p->from.sym = s; - } - s->type = STEXT; - s->text = p; - s->value = pc; - lastp->link = p; - lastp = p; - p->pc = pc++; - if(textp == P) - textp = p; - else - etextp->pcond = p; - etextp = p; - return p; -} - -void -export(void) -{ - int i, j, n, off, nb, sv, ne; - Sym *s, *et, *str, **esyms; - Prog *p; - char buf[NSNAME], *t; - - n = 0; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT)) - n++; - esyms = mal(n*sizeof(Sym*)); - ne = n; - n = 0; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->sig != 0 && s->type != SXREF && s->type != SUNDEF && (nexports == 0 || s->subtype == SEXPORT)) - esyms[n++] = s; - for(i = 0; i < ne-1; i++) - for(j = i+1; j < ne; j++) - if(strcmp(esyms[i]->name, esyms[j]->name) > 0){ - s = esyms[i]; - esyms[i] = esyms[j]; - esyms[j] = s; - } - - nb = 0; - off = 0; - et = lookup(EXPTAB, 0); - if(et->type != 0 && et->type != SXREF) - diag("%s already defined", EXPTAB); - et->type = SDATA; - str = lookup(".string", 0); - if(str->type == 0) - str->type = SDATA; - sv = str->value; - for(i = 0; i < ne; i++){ - s = esyms[i]; - if(debug['S']) - s->sig = 0; - /* Bprint(&bso, "EXPORT: %s sig=%lux t=%d\n", s->name, s->sig, s->type); */ - - /* signature */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.offset = s->sig; - - /* address */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.type = D_ADDR; - p->to.index = D_EXTERN; - p->to.sym = s; - - /* string */ - t = s->name; - n = strlen(t)+1; - for(;;){ - buf[nb++] = *t; - sv++; - if(nb >= NSNAME){ - p = newdata(str, sv-NSNAME, NSNAME, D_STATIC); - p->to.type = D_SCONST; - memmove(p->to.scon, buf, NSNAME); - nb = 0; - } - if(*t++ == 0) - break; - } - - /* name */ - p = newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - p->to.type = D_ADDR; - p->to.index = D_STATIC; - p->to.sym = str; - p->to.offset = sv-n; - } - - if(nb > 0){ - p = newdata(str, sv-nb, nb, D_STATIC); - p->to.type = D_SCONST; - memmove(p->to.scon, buf, nb); - } - - for(i = 0; i < 3; i++){ - newdata(et, off, sizeof(int32), D_EXTERN); - off += sizeof(int32); - } - et->value = off; - if(sv == 0) - sv = 1; - str->value = sv; - exports = ne; - free(esyms); + diag("%s(%d): not defined", s->name, s->version); } diff --git a/src/cmd/8l/prof.c b/src/cmd/8l/prof.c new file mode 100644 index 000000000..4e95fad79 --- /dev/null +++ b/src/cmd/8l/prof.c @@ -0,0 +1,173 @@ +// Inferno utils/8l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Profiling. + +#include "l.h" +#include "../ld/lib.h" + +void +doprof1(void) +{ +#if 0 // TODO(rsc) + Sym *s; + int32 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) { + if(p->as == ATEXT) { + q = prg(); + q->line = p->line; + q->link = datap; + datap = q; + q->as = ADATA; + q->from.type = D_EXTERN; + q->from.offset = n*4; + q->from.sym = s; + q->from.scale = 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 = AADDL; + p->from.type = D_CONST; + p->from.offset = 1; + p->to.type = 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_EXTERN; + q->from.sym = s; + q->from.scale = 4; + q->to.type = D_CONST; + q->to.offset = n; + + s->type = SBSS; + s->size = n*4; +#endif +} + +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(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; + if(p->from.sym == s2) { + p->from.scale = 1; + ps2 = p; + } + if(p->from.sym == s4) { + p->from.scale = 1; + ps4 = p; + } + } + for(cursym = textp; cursym != nil; cursym = cursym->next) { + p = cursym->text; + + if(p->from.scale & NOPROF) /* dont profile */ + continue; + + /* + * JMPL profin + */ + q = prg(); + q->line = p->line; + q->pc = p->pc; + q->link = p->link; + p->link = q; + p = q; + p->as = ACALL; + p->to.type = D_BRANCH; + p->pcond = ps2; + p->to.sym = s2; + + for(; p; p=p->link) { + 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; + + /* + * JAL profout + */ + p->as = ACALL; + p->from = zprg.from; + p->to = zprg.to; + p->to.type = D_BRANCH; + p->pcond = ps4; + p->to.sym = s4; + + p = q; + } + } + } +} diff --git a/src/cmd/8l/span.c b/src/cmd/8l/span.c index 99ba279da..66a843b23 100644 --- a/src/cmd/8l/span.c +++ b/src/cmd/8l/span.c @@ -28,29 +28,28 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Instruction layout. + #include "l.h" #include "../ld/lib.h" +static int32 vaddr(Adr*, Reloc*); + void -span(void) +span1(Sym *s) { Prog *p, *q; - int32 v, c, idat; - int m, n, again; - - xdefine("etext", STEXT, 0L); - idat = INITDAT; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) - curtext = p; - n = 0; - if(p->to.type == D_BRANCH) - if(p->pcond == P) - p->pcond = p; - if((q = p->pcond) != P) - if(q->back != 2) - n = 1; - p->back = n; + int32 c, v, loop; + uchar *bp; + int n, m, i; + + cursym = s; + + for(p = s->text; p != P; p = p->link) { + p->back = 2; // use short branches first time through + if((q = p->pcond) != P && (q->back & 2)) + p->back |= 1; // backward jump + if(p->as == AADJSP) { p->to.type = D_SP; v = -p->from.offset; @@ -65,293 +64,186 @@ span(void) p->as = ANOP; } } - + n = 0; -start: - do{ - again = 0; - if(debug['v']) - Bprint(&bso, "%5.2f span %d\n", cputime(), n); - Bflush(&bso); - if(n > 50) { - print("span must be looping - %d\n", textsize); + do { + loop = 0; + memset(s->r, 0, s->nr*sizeof s->r[0]); + s->nr = 0; + s->np = 0; + c = 0; + for(p = s->text; p != P; p = p->link) { + p->pc = c; + + // process forward jumps to p + for(q = p->comefrom; q != P; q = q->forwd) { + v = p->pc - (q->pc + q->mark); + if(q->back & 2) { // short + if(v > 127) { + loop++; + q->back ^= 2; + } + s->p[q->pc+1] = v; + } else { + bp = s->p + q->pc + q->mark - 4; + *bp++ = v; + *bp++ = v>>8; + *bp++ = v>>16; + *bp++ = v>>24; + } + } + p->comefrom = P; + + asmins(p); + p->pc = c; + m = andptr-and; + symgrow(s, p->pc+m); + memmove(s->p+p->pc, and, m); + p->mark = m; + c += m; + } + if(++n > 20) { + diag("span must be looping"); errorexit(); } - c = INITTEXT; - for(p = firstp; p != P; p = p->link) { - if(p->as == ATEXT) { - curtext = p; - if(HEADTYPE == 8) - c = (c+31)&~31; - } - if(p->to.type == D_BRANCH) - if(p->back) - p->pc = c; - if(n == 0 || HEADTYPE == 8 || p->to.type == D_BRANCH) { - if(HEADTYPE == 8) - p->pc = c; - asmins(p); - m = andptr-and; - if(p->mark != m) - again = 1; - p->mark = m; - } - if(HEADTYPE == 8) { - c = p->pc + p->mark; - } else { - p->pc = c; - c += p->mark; - } + } while(loop); + s->size = c; + + if(debug['a'] > 1) { + print("span1 %s %d (%d tries)\n %.6ux", s->name, s->size, n, 0); + for(i=0; i<s->np; i++) { + print(" %.2ux", s->p[i]); + if(i%16 == 15) + print("\n %.6ux", i+1); } - textsize = c; - n++; - }while(again); - - if(INITRND) { - INITDAT = rnd(c+textpad, INITRND); - if(INITDAT != idat) { - idat = INITDAT; - goto start; + if(i%16) + print("\n"); + + for(i=0; i<s->nr; i++) { + Reloc *r; + + r = &s->r[i]; + print(" rel %#.4ux/%d %s%+d\n", r->off, r->siz, r->sym->name, r->add); } } - xdefine("etext", STEXT, c); - if(debug['v']) - Bprint(&bso, "etext = %lux\n", c); - Bflush(&bso); - for(p = textp; p != P; p = p->pcond) - p->from.sym->value = p->pc; - textsize = c - INITTEXT; } void -xdefine(char *p, int t, int32 v) +span(void) { - Sym *s; - - s = lookup(p, 0); - if(s->type == 0 || s->type == SXREF) { - s->type = t; - s->value = v; - } - if(s->type == STEXT && s->value == 0) - s->value = v; -} + Prog *p, *q; + int32 v; + int n; -void -putsymb(char *s, int t, int32 v, int ver, Sym *go) -{ - int i, f; - vlong gv; - - if(t == 'f') - s++; - lput(v); - if(ver) - t += 'a' - 'A'; - cput(t+0x80); /* 0x80 is variable length */ - - if(t == 'Z' || t == 'z') { - cput(s[0]); - for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) { - cput(s[i]); - cput(s[i+1]); - } - cput(0); - cput(0); - i++; - } - else { - for(i=0; s[i]; i++) - cput(s[i]); - cput(0); - } - gv = 0; - if(go) { - if(!go->reachable) - sysfatal("unreachable type %s", go->name); - gv = go->value+INITDAT; - } - lput(gv); + if(debug['v']) + Bprint(&bso, "%5.2f span\n", cputime()); - symsize += 4 + 1 + i+1 + 4; + // NOTE(rsc): If we get rid of the globals we should + // be able to parallelize these iterations. + for(cursym = textp; cursym != nil; cursym = cursym->next) { + if(cursym->text == nil || cursym->text->link == nil) + continue; - if(debug['n']) { - if(t == 'z' || t == 'Z') { - Bprint(&bso, "%c %.8lux ", t, v); - for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) { - f = ((s[i]&0xff) << 8) | (s[i+1]&0xff); - Bprint(&bso, "/%x", f); + // TODO: move into span1 + for(p = cursym->text; p != P; p = p->link) { + n = 0; + if(p->to.type == D_BRANCH) + if(p->pcond == P) + p->pcond = p; + if((q = p->pcond) != P) + if(q->back != 2) + n = 1; + p->back = n; + if(p->as == AADJSP) { + p->to.type = D_SP; + v = -p->from.offset; + p->from.offset = v; + p->as = AADDL; + if(v < 0) { + p->as = ASUBL; + v = -v; + p->from.offset = v; + } + if(v == 0) + p->as = ANOP; } - Bprint(&bso, "\n"); - return; } - if(ver) - Bprint(&bso, "%c %.8lux %s<%d> %s (%.8llux)\n", t, v, s, ver, go ? go->name : "", gv); - else - Bprint(&bso, "%c %.8lux %s\n", t, v, s, go ? go->name : "", gv); + span1(cursym); } } void -asmsym(void) +xdefine(char *p, int t, int32 v) { - Prog *p; - Auto *a; Sym *s; - int h; - - s = lookup("etext", 0); - if(s->type == STEXT) - putsymb(s->name, 'T', s->value, s->version, 0); - - for(h=0; h<NHASH; h++) - for(s=hash[h]; s!=S; s=s->link) - switch(s->type) { - case SCONST: - if(!s->reachable) - continue; - putsymb(s->name, 'D', s->value, s->version, s->gotype); - continue; - - case SDATA: - case SELFDATA: - if(!s->reachable) - continue; - putsymb(s->name, 'D', s->value+INITDAT, s->version, s->gotype); - continue; - - case SMACHO: - if(!s->reachable) - continue; - putsymb(s->name, 'D', s->value+INITDAT+datsize+bsssize, s->version, s->gotype); - continue; - - case SBSS: - if(!s->reachable) - continue; - putsymb(s->name, 'B', s->value+INITDAT, s->version, s->gotype); - continue; - - case SFIXED: - putsymb(s->name, 'B', s->value, s->version, s->gotype); - continue; - - case SFILE: - putsymb(s->name, 'f', s->value, s->version, 0); - continue; - } - for(p=textp; p!=P; p=p->pcond) { - s = p->from.sym; - if(s->type != STEXT) - continue; - - /* filenames first */ - for(a=p->to.autom; a; a=a->link) - if(a->type == D_FILE) - putsymb(a->asym->name, 'z', a->aoffset, 0, 0); - else - if(a->type == D_FILE1) - putsymb(a->asym->name, 'Z', a->aoffset, 0, 0); - - if(!s->reachable) - continue; - - putsymb(s->name, 'T', s->value, s->version, s->gotype); - - /* frame, auto and param after */ - putsymb(".frame", 'm', p->to.offset+4, 0, 0); - - for(a=p->to.autom; a; a=a->link) - if(a->type == D_AUTO) - putsymb(a->asym->name, 'a', -a->aoffset, 0, a->gotype); - else - if(a->type == D_PARAM) - putsymb(a->asym->name, 'p', a->aoffset, 0, a->gotype); - } - if(debug['v'] || debug['n']) - Bprint(&bso, "symsize = %lud\n", symsize); - Bflush(&bso); + s = lookup(p, 0); + s->type = t; + s->value = v; + s->reachable = 1; + s->special = 1; } void -asmlc(void) +instinit(void) { - int32 oldpc, oldlc; - Prog *p; - int32 v, s; - - oldpc = INITTEXT; - oldlc = 0; - for(p = firstp; p != P; p = p->link) { - if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) { - if(p->as == ATEXT) - curtext = p; - if(debug['L']) - Bprint(&bso, "%6lux %P\n", - p->pc, p); - continue; - } - if(debug['L']) - Bprint(&bso, "\t\t%6ld", lcsize); - v = (p->pc - oldpc) / MINLC; - while(v) { - s = 127; - if(v < 127) - s = v; - cput(s+128); /* 129-255 +pc */ - if(debug['L']) - Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128); - v -= s; - lcsize++; - } - s = p->line - oldlc; - oldlc = p->line; - oldpc = p->pc + MINLC; - if(s > 64 || s < -64) { - cput(0); /* 0 vv +lc */ - cput(s>>24); - cput(s>>16); - cput(s>>8); - cput(s); - if(debug['L']) { - if(s > 0) - Bprint(&bso, " lc+%ld(%d,%ld)\n", - s, 0, s); - else - Bprint(&bso, " lc%ld(%d,%ld)\n", - s, 0, s); - Bprint(&bso, "%6lux %P\n", - p->pc, p); - } - lcsize += 5; - continue; - } - if(s > 0) { - cput(0+s); /* 1-64 +lc */ - if(debug['L']) { - Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s); - Bprint(&bso, "%6lux %P\n", - p->pc, p); - } - } else { - cput(64-s); /* 65-128 -lc */ - if(debug['L']) { - Bprint(&bso, " lc%ld(%ld)\n", s, 64-s); - Bprint(&bso, "%6lux %P\n", - p->pc, p); - } + int i; + + for(i=1; optab[i].as; i++) + if(i != optab[i].as) { + diag("phase error in optab: %d", i); + errorexit(); } - lcsize++; - } - while(lcsize & 1) { - s = 129; - cput(s); - lcsize++; + maxop = i; + + for(i=0; i<Ymax; i++) + ycover[i*Ymax + i] = 1; + + ycover[Yi0*Ymax + Yi8] = 1; + ycover[Yi1*Ymax + Yi8] = 1; + + ycover[Yi0*Ymax + Yi32] = 1; + ycover[Yi1*Ymax + Yi32] = 1; + ycover[Yi8*Ymax + Yi32] = 1; + + ycover[Yal*Ymax + Yrb] = 1; + ycover[Ycl*Ymax + Yrb] = 1; + ycover[Yax*Ymax + Yrb] = 1; + ycover[Ycx*Ymax + Yrb] = 1; + ycover[Yrx*Ymax + Yrb] = 1; + + ycover[Yax*Ymax + Yrx] = 1; + ycover[Ycx*Ymax + Yrx] = 1; + + ycover[Yax*Ymax + Yrl] = 1; + ycover[Ycx*Ymax + Yrl] = 1; + ycover[Yrx*Ymax + Yrl] = 1; + + ycover[Yf0*Ymax + Yrf] = 1; + + ycover[Yal*Ymax + Ymb] = 1; + ycover[Ycl*Ymax + Ymb] = 1; + ycover[Yax*Ymax + Ymb] = 1; + ycover[Ycx*Ymax + Ymb] = 1; + ycover[Yrx*Ymax + Ymb] = 1; + ycover[Yrb*Ymax + Ymb] = 1; + ycover[Ym*Ymax + Ymb] = 1; + + ycover[Yax*Ymax + Yml] = 1; + ycover[Ycx*Ymax + Yml] = 1; + ycover[Yrx*Ymax + Yml] = 1; + ycover[Yrl*Ymax + Yml] = 1; + ycover[Ym*Ymax + Yml] = 1; + + for(i=0; i<D_NONE; i++) { + reg[i] = -1; + if(i >= D_AL && i <= D_BH) + reg[i] = (i-D_AL) & 7; + if(i >= D_AX && i <= D_DI) + reg[i] = (i-D_AX) & 7; + if(i >= D_F0 && i <= D_F0+7) + reg[i] = (i-D_F0) & 7; } - if(debug['v'] || debug['L']) - Bprint(&bso, "lcsize = %ld\n", lcsize); - Bflush(&bso); } int @@ -506,11 +398,11 @@ oclass(Adr *a) } void -asmidx(Adr *a, int base) +asmidx(int scale, int index, int base) { int i; - switch(a->index) { + switch(index) { default: goto bad; @@ -525,10 +417,10 @@ asmidx(Adr *a, int base) case D_BP: case D_SI: case D_DI: - i = reg[a->index] << 3; + i = reg[index] << 3; break; } - switch(a->scale) { + switch(scale) { default: goto bad; case 1: @@ -564,7 +456,7 @@ bas: *andptr++ = i; return; bad: - diag("asmidx: bad address %D", a); + diag("asmidx: bad address %d,%d,%d", scale, index, base); *andptr++ = 0; return; } @@ -572,10 +464,6 @@ bad: static void put4(int32 v) { - if(dlm && curp != P && reloca != nil){ - dynreloc(reloca->sym, curp->pc + andptr - &and[0], 1); - reloca = nil; - } andptr[0] = v; andptr[1] = v>>8; andptr[2] = v>>16; @@ -583,24 +471,40 @@ put4(int32 v) andptr += 4; } +static void +relput4(Prog *p, Adr *a) +{ + vlong v; + Reloc rel, *r; + + v = vaddr(a, &rel); + if(rel.siz != 0) { + if(rel.siz != 4) + diag("bad reloc"); + r = addrel(cursym); + *r = rel; + r->off = p->pc + andptr - and; + } + put4(v); +} + int32 symaddr(Sym *s) { - Adr a; - - a.type = D_ADDR; - a.index = D_EXTERN; - a.offset = 0; - a.sym = s; - return vaddr(&a); + if(!s->reachable) + diag("unreachable symbol in symaddr - %s", s->name); + return s->value; } -int32 -vaddr(Adr *a) +static int32 +vaddr(Adr *a, Reloc *r) { int t; int32 v; Sym *s; + + if(r != nil) + memset(r, 0, sizeof *r); t = a->type; v = a->offset; @@ -611,30 +515,18 @@ vaddr(Adr *a) case D_EXTERN: s = a->sym; if(s != nil) { - if(dlm && curp != P) - reloca = a; - switch(s->type) { - case SUNDEF: - ckoff(s, v); - case STEXT: - case SCONST: - if(!s->reachable) - sysfatal("unreachable symbol in vaddr - %s", s->name); - v += s->value; - break; - case SMACHO: - if(!s->reachable) - sysfatal("unreachable symbol in vaddr - %s", s->name); - v += INITDAT + datsize + s->value; - break; - case SFIXED: - v += s->value; - break; - default: - if(!s->reachable) - sysfatal("unreachable symbol in vaddr - %s", s->name); - v += INITDAT + s->value; + if(!s->reachable) + sysfatal("unreachable symbol in vaddr - %s", s->name); + if(r == nil) { + diag("need reloc for %D", a); + errorexit(); } + r->type = D_ADDR; + r->siz = 4; + r->off = -1; + r->sym = s; + r->add = v; + v = 0; } } return v; @@ -644,118 +536,127 @@ void asmand(Adr *a, int r) { int32 v; - int t; - Adr aa; + int t, scale; + Reloc rel; v = a->offset; t = a->type; + rel.siz = 0; if(a->index != D_NONE) { - if(t >= D_INDIR && t < 2*D_INDIR) { - t -= D_INDIR; - if(t == D_NONE) { - *andptr++ = (0 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - put4(v); - return; - } - if(v == 0 && t != D_BP) { - *andptr++ = (0 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - return; - } - if(v >= -128 && v < 128) { - *andptr++ = (1 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - *andptr++ = v; - return; + if(t < D_INDIR || t >= 2*D_INDIR) { + switch(t) { + default: + goto bad; + case D_STATIC: + case D_EXTERN: + t = D_NONE; + v = vaddr(a, &rel); + break; + case D_AUTO: + case D_PARAM: + t = D_SP; + break; } - *andptr++ = (2 << 6) | (4 << 0) | (r << 3); - asmidx(a, t); - put4(v); + } else + t -= D_INDIR; + + if(t == D_NONE) { + *andptr++ = (0 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + goto putrelv; + } + if(v == 0 && rel.siz == 0 && t != D_BP) { + *andptr++ = (0 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + return; + } + if(v >= -128 && v < 128 && rel.siz == 0) { + *andptr++ = (1 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + *andptr++ = v; return; } - switch(t) { + *andptr++ = (2 << 6) | (4 << 0) | (r << 3); + asmidx(a->scale, a->index, t); + goto putrelv; + } + if(t >= D_AL && t <= D_F0+7) { + if(v) + goto bad; + *andptr++ = (3 << 6) | (reg[t] << 0) | (r << 3); + return; + } + + scale = a->scale; + if(t < D_INDIR || t >= 2*D_INDIR) { + switch(a->type) { default: goto bad; case D_STATIC: case D_EXTERN: - aa.type = D_NONE+D_INDIR; + t = D_NONE; + v = vaddr(a, &rel); break; case D_AUTO: case D_PARAM: - aa.type = D_SP+D_INDIR; + t = D_SP; break; } - aa.offset = vaddr(a); - aa.index = a->index; - aa.scale = a->scale; - asmand(&aa, r); - return; - } - if(t >= D_AL && t <= D_F0+7) { - if(v) - goto bad; - *andptr++ = (3 << 6) | (reg[t] << 0) | (r << 3); - return; - } - if(t >= D_INDIR && t < 2*D_INDIR) { + scale = 1; + } else t -= D_INDIR; - if(t == D_NONE || (D_CS <= t && t <= D_GS)) { - *andptr++ = (0 << 6) | (5 << 0) | (r << 3); - put4(v); + + if(t == D_NONE || (D_CS <= t && t <= D_GS)) { + *andptr++ = (0 << 6) | (5 << 0) | (r << 3); + goto putrelv; + } + if(t == D_SP) { + if(v == 0 && rel.siz == 0) { + *andptr++ = (0 << 6) | (4 << 0) | (r << 3); + asmidx(scale, D_NONE, t); return; } - if(t == D_SP) { - if(v == 0) { - *andptr++ = (0 << 6) | (4 << 0) | (r << 3); - asmidx(a, D_SP); - return; - } - if(v >= -128 && v < 128) { - *andptr++ = (1 << 6) | (4 << 0) | (r << 3); - asmidx(a, D_SP); - *andptr++ = v; - return; - } - *andptr++ = (2 << 6) | (4 << 0) | (r << 3); - asmidx(a, D_SP); - put4(v); + if(v >= -128 && v < 128 && rel.siz == 0) { + *andptr++ = (1 << 6) | (4 << 0) | (r << 3); + asmidx(scale, D_NONE, t); + *andptr++ = v; return; } - if(t >= D_AX && t <= D_DI) { - if(v == 0 && t != D_BP) { - *andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); - return; - } - if(v >= -128 && v < 128) { - andptr[0] = (1 << 6) | (reg[t] << 0) | (r << 3); - andptr[1] = v; - andptr += 2; - return; - } - *andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3); - put4(v); + *andptr++ = (2 << 6) | (4 << 0) | (r << 3); + asmidx(scale, D_NONE, t); + goto putrelv; + } + if(t >= D_AX && t <= D_DI) { + if(v == 0 && rel.siz == 0 && t != D_BP) { + *andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3); return; } - goto bad; + if(v >= -128 && v < 128 && rel.siz == 0) { + andptr[0] = (1 << 6) | (reg[t] << 0) | (r << 3); + andptr[1] = v; + andptr += 2; + return; + } + *andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3); + goto putrelv; } - switch(a->type) { - default: - goto bad; - case D_STATIC: - case D_EXTERN: - aa.type = D_NONE+D_INDIR; - break; - case D_AUTO: - case D_PARAM: - aa.type = D_SP+D_INDIR; - break; + goto bad; + +putrelv: + if(rel.siz != 0) { + Reloc *r; + + if(rel.siz != 4) { + diag("bad rel"); + goto bad; + } + r = addrel(cursym); + *r = rel; + r->off = curp->pc + andptr - and; } - aa.index = D_NONE; - aa.scale = 1; - aa.offset = vaddr(a); - asmand(&aa, r); + put4(v); return; + bad: diag("asmand: bad address %D", a); return; @@ -921,22 +822,6 @@ subreg(Prog *p, int from, int to) print("%P\n", p); } -// nacl RET: -// POPL BX -// ANDL BX, $~31 -// JMP BX -uchar naclret[] = { 0x5b, 0x83, 0xe3, ~31, 0xff, 0xe3 }; - -// nacl JMP BX: -// ANDL BX, $~31 -// JMP BX -uchar nacljmpbx[] = { 0x83, 0xe3, ~31, 0xff, 0xe3 }; - -// nacl CALL BX: -// ANDL BX, $~31 -// CALL BX -uchar naclcallbx[] = { 0x83, 0xe3, ~31, 0xff, 0xd3 }; - void doasm(Prog *p) { @@ -945,6 +830,10 @@ doasm(Prog *p) uchar *t; int z, op, ft, tt; int32 v, pre; + Reloc rel, *r; + Adr *a; + + curp = p; // TODO pre = prefixof(&p->from); if(pre) @@ -990,7 +879,7 @@ found: case Pb: /* botch */ break; } - v = vaddr(&p->from); + op = o->op[z]; switch(t[2]) { default: @@ -1001,12 +890,6 @@ found: break; case Zlit: - if(HEADTYPE == 8 && p->as == ARET) { - // native client return. - for(z=0; z<sizeof(naclret); z++) - *andptr++ = naclret[z]; - break; - } for(; op = o->op[z]; z++) *andptr++ = op; break; @@ -1040,137 +923,100 @@ found: break; case Zo_m: - if(HEADTYPE == 8) { - Adr a; - - switch(p->as) { - case AJMP: - if(p->to.type < D_AX || p->to.type > D_DI) - diag("indirect jmp must use register in native client"); - // ANDL $~31, REG - *andptr++ = 0x83; - asmand(&p->to, 04); - *andptr++ = ~31; - // JMP REG - *andptr++ = 0xFF; - asmand(&p->to, 04); - return; - - case ACALL: - a = p->to; - // native client indirect call - if(a.type < D_AX || a.type > D_DI) { - // MOVL target into BX - *andptr++ = 0x8b; - asmand(&p->to, reg[D_BX]); - memset(&a, 0, sizeof a); - a.type = D_BX; - } - // ANDL $~31, REG - *andptr++ = 0x83; - asmand(&a, 04); - *andptr++ = ~31; - // CALL REG - *andptr++ = 0xFF; - asmand(&a, 02); - return; - } - } *andptr++ = op; asmand(&p->to, o->op[z+1]); break; case Zm_ibo: - v = vaddr(&p->to); *andptr++ = op; asmand(&p->from, o->op[z+1]); - *andptr++ = v; + *andptr++ = vaddr(&p->to, nil); break; case Zibo_m: *andptr++ = op; asmand(&p->to, o->op[z+1]); - *andptr++ = v; + *andptr++ = vaddr(&p->from, nil); break; case Z_ib: - v = vaddr(&p->to); case Zib_: - if(HEADTYPE == 8 && p->as == AINT && v == 3) { - // native client disallows all INT instructions. - // translate INT $3 to HLT. - *andptr++ = 0xf4; - break; - } + if(t[2] == Zib_) + a = &p->from; + else + a = &p->to; + v = vaddr(a, nil); *andptr++ = op; *andptr++ = v; break; case Zib_rp: *andptr++ = op + reg[p->to.type]; - *andptr++ = v; + *andptr++ = vaddr(&p->from, nil); break; case Zil_rp: *andptr++ = op + reg[p->to.type]; if(o->prefix == Pe) { + v = vaddr(&p->from, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, &p->from); break; case Zib_rr: *andptr++ = op; asmand(&p->to, reg[p->to.type]); - *andptr++ = v; + *andptr++ = vaddr(&p->from, nil); break; case Z_il: - v = vaddr(&p->to); case Zil_: - *andptr++ = op; - if(o->prefix == Pe) { - *andptr++ = v; - *andptr++ = v>>8; - } + if(t[2] == Zil_) + a = &p->from; else - put4(v); - break; - - case Zm_ilo: - v = vaddr(&p->to); + a = &p->to; *andptr++ = op; - asmand(&p->from, o->op[z+1]); if(o->prefix == Pe) { + v = vaddr(a, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, a); break; + case Zm_ilo: case Zilo_m: *andptr++ = op; - asmand(&p->to, o->op[z+1]); + if(t[2] == Zilo_m) { + a = &p->from; + asmand(&p->to, o->op[z+1]); + } else { + a = &p->to; + asmand(&p->from, o->op[z+1]); + } if(o->prefix == Pe) { + v = vaddr(a, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, a); break; case Zil_rr: *andptr++ = op; asmand(&p->to, reg[p->to.type]); if(o->prefix == Pe) { + v = vaddr(&p->from, nil); *andptr++ = v; *andptr++ = v>>8; } else - put4(v); + relput4(p, &p->from); break; case Z_rp: @@ -1185,99 +1031,128 @@ found: *andptr++ = op; asmand(&p->to, reg[p->to.type]); break; - - case Zbr: - q = p->pcond; - if(q) { - v = q->pc - p->pc - 2; - if(q->pc == 0) - v = 0; - if(v >= -128 && v <= 127 && !p->bigjmp) { - *andptr++ = op; - *andptr++ = v; - } else { - p->bigjmp = 1; - v -= 6-2; - *andptr++ = 0x0f; - *andptr++ = o->op[z+1]; - *andptr++ = v; - *andptr++ = v>>8; - *andptr++ = v>>16; - *andptr++ = v>>24; - } - } - break; - + case Zcall: q = p->pcond; - if(q) { - v = q->pc - p->pc - 5; - if(dlm && curp != P && p->to.sym->type == SUNDEF){ - /* v = 0 - p->pc - 5; */ - v = 0; - ckoff(p->to.sym, v); - v += p->to.sym->value; - dynreloc(p->to.sym, p->pc+1, 0); - } - *andptr++ = op; - *andptr++ = v; - *andptr++ = v>>8; - *andptr++ = v>>16; - *andptr++ = v>>24; + if(q == nil) { + diag("call without target"); + errorexit(); + } + if(q->as != ATEXT) { + // Could handle this case by making D_PCREL + // record the Prog* instead of the Sym*, but let's + // wait until the need arises. + diag("call of non-TEXT %P", q); + errorexit(); } - break; - - case Zcallcon: - v = p->to.offset - p->pc - 5; *andptr++ = op; - *andptr++ = v; - *andptr++ = v>>8; - *andptr++ = v>>16; - *andptr++ = v>>24; + r = addrel(cursym); + r->off = p->pc + andptr - and; + r->type = D_PCREL; + r->siz = 4; + r->sym = q->from.sym; + put4(0); break; + case Zbr: case Zjmp: q = p->pcond; - if(q) { - v = q->pc - p->pc - 2; - if(q->pc == 0) - v = 0; - if(v >= -128 && v <= 127 && !p->bigjmp) { + if(q == nil) { + diag("jmp/branch without target"); + errorexit(); + } + if(q->as == ATEXT) { + // jump out of function + if(t[2] == Zbr) { + diag("branch to ATEXT"); + errorexit(); + } + *andptr++ = o->op[z+1]; + r = addrel(cursym); + r->off = p->pc + andptr - and; + r->sym = q->from.sym; + r->type = D_PCREL; + r->siz = 4; + put4(0); + break; + } + + // Assumes q is in this function. + // TODO: Check in input, preserve in brchain. + + // Fill in backward jump now. + if(p->back & 1) { + v = q->pc - (p->pc + 2); + if(v >= -128) { *andptr++ = op; *andptr++ = v; } else { - p->bigjmp = 1; v -= 5-2; + if(t[2] == Zbr) { + *andptr++ = 0x0f; + v--; + } *andptr++ = o->op[z+1]; *andptr++ = v; *andptr++ = v>>8; *andptr++ = v>>16; *andptr++ = v>>24; } + break; + } + + // Annotate target; will fill in later. + p->forwd = q->comefrom; + q->comefrom = p; + if(p->back & 2) { // short + *andptr++ = op; + *andptr++ = 0; + } else { + if(t[2] == Zbr) + *andptr++ = 0x0f; + *andptr++ = o->op[z+1]; + *andptr++ = 0; + *andptr++ = 0; + *andptr++ = 0; + *andptr++ = 0; } break; + case Zcallcon: case Zjmpcon: - v = p->to.offset - p->pc - 5; - *andptr++ = o->op[z+1]; - *andptr++ = v; - *andptr++ = v>>8; - *andptr++ = v>>16; - *andptr++ = v>>24; + if(t[2] == Zcallcon) + *andptr++ = op; + else + *andptr++ = o->op[z+1]; + r = addrel(cursym); + r->off = p->pc + andptr - and; + r->type = D_PCREL; + r->siz = 4; + r->add = p->to.offset; + put4(0); break; case Zloop: q = p->pcond; - if(q) { - v = q->pc - p->pc - 2; - if(v < -128 && v > 127) - diag("loop too far: %P", p); - *andptr++ = op; - *andptr++ = v; + if(q == nil) { + diag("loop without target"); + errorexit(); } + v = q->pc - p->pc - 2; + if(v < -128 && v > 127) + diag("loop too far: %P", p); + *andptr++ = op; + *andptr++ = v; break; case Zbyte: + v = vaddr(&p->from, &rel); + if(rel.siz != 0) { + rel.siz = op; + r = addrel(cursym); + *r = rel; + r->off = p->pc + andptr - and; + } *andptr++ = v; if(op > 1) { *andptr++ = v>>8; @@ -1342,7 +1217,7 @@ bad: } return; } - diag("doasm: notfound t2=%lux from=%lux to=%lux %P", t[2], p->from.type, p->to.type, p); + diag("doasm: notfound t2=%ux from=%ux to=%ux %P", t[2], p->from.type, p->to.type, p); return; mfound: @@ -1441,204 +1316,10 @@ mfound: void asmins(Prog *p) { - if(HEADTYPE == 8) { - ulong npc; - static Prog *prefix; - - // native client - // - pad indirect jump targets (aka ATEXT) to 32-byte boundary - // - instructions cannot cross 32-byte boundary - // - end of call (return address) must be on 32-byte boundary - if(p->as == ATEXT) - p->pc += 31 & -p->pc; - if(p->as == ACALL) { - // must end on 32-byte boundary. - // doasm to find out how long the CALL encoding is. - andptr = and; - doasm(p); - npc = p->pc + (andptr - and); - p->pc += 31 & -npc; - } - if(p->as == AREP || p->as == AREPN) { - // save prefix for next instruction, - // so that inserted NOPs do not split (e.g.) REP / MOVSL sequence. - prefix = p; - andptr = and; - return; - } - andptr = and; - if(prefix) - doasm(prefix); - doasm(p); - npc = p->pc + (andptr - and); - if(andptr > and && (p->pc&~31) != ((npc-1)&~31)) { - // crossed 32-byte boundary; pad to boundary and try again - p->pc += 31 & -p->pc; - andptr = and; - if(prefix) - doasm(prefix); - doasm(p); - } - prefix = nil; - } else { - andptr = and; - doasm(p); - } + andptr = and; + doasm(p); if(andptr > and+sizeof and) { print("and[] is too short - %d byte instruction\n", andptr - and); errorexit(); } } - -enum{ - ABSD = 0, - ABSU = 1, - RELD = 2, - RELU = 3, -}; - -int modemap[4] = { 0, 1, -1, 2, }; - -typedef struct Reloc Reloc; - -struct Reloc -{ - int n; - int t; - uchar *m; - uint32 *a; -}; - -Reloc rels; - -static void -grow(Reloc *r) -{ - int t; - uchar *m, *nm; - uint32 *a, *na; - - t = r->t; - r->t += 64; - m = r->m; - a = r->a; - r->m = nm = mal(r->t*sizeof(uchar)); - r->a = na = mal(r->t*sizeof(uint32)); - memmove(nm, m, t*sizeof(uchar)); - memmove(na, a, t*sizeof(uint32)); - free(m); - free(a); -} - -void -dynreloc(Sym *s, uint32 v, int abs) -{ - int i, k, n; - uchar *m; - uint32 *a; - Reloc *r; - - if(s->type == SUNDEF) - k = abs ? ABSU : RELU; - else - k = abs ? ABSD : RELD; - /* Bprint(&bso, "R %s a=%ld(%lx) %d\n", s->name, v, v, k); */ - k = modemap[k]; - r = &rels; - n = r->n; - if(n >= r->t) - grow(r); - m = r->m; - a = r->a; - for(i = n; i > 0; i--){ - if(v < a[i-1]){ /* happens occasionally for data */ - m[i] = m[i-1]; - a[i] = a[i-1]; - } - else - break; - } - m[i] = k; - a[i] = v; - r->n++; -} - -static int -sput(char *s) -{ - char *p; - - p = s; - while(*s) - cput(*s++); - cput(0); - return s-p+1; -} - -void -asmdyn() -{ - int i, n, t, c; - Sym *s; - uint32 la, ra, *a; - vlong off; - uchar *m; - Reloc *r; - - cflush(); - off = seek(cout, 0, 1); - lput(0); - t = 0; - lput(imports); - t += 4; - for(i = 0; i < NHASH; i++) - for(s = hash[i]; s != S; s = s->link) - if(s->type == SUNDEF){ - lput(s->sig); - t += 4; - t += sput(s->name); - } - - la = 0; - r = &rels; - n = r->n; - m = r->m; - a = r->a; - lput(n); - t += 4; - for(i = 0; i < n; i++){ - ra = *a-la; - if(*a < la) - diag("bad relocation order"); - if(ra < 256) - c = 0; - else if(ra < 65536) - c = 1; - else - c = 2; - cput((c<<6)|*m++); - t++; - if(c == 0){ - cput(ra); - t++; - } - else if(c == 1){ - wput(ra); - t += 2; - } - else{ - lput(ra); - t += 4; - } - la = *a++; - } - - cflush(); - seek(cout, off, 0); - lput(t); - - if(debug['v']){ - Bprint(&bso, "import table entries = %d\n", imports); - Bprint(&bso, "export table entries = %d\n", exports); - } -} diff --git a/src/cmd/cc/Makefile b/src/cmd/cc/Makefile index 98b89f0a2..71f23383d 100644 --- a/src/cmd/cc/Makefile +++ b/src/cmd/cc/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -LIB=\ - cc.a$O\ +LIB=cc.a HFILES=\ cc.h\ @@ -30,18 +30,7 @@ OFILES=\ dpchk.$O\ omachcap.$O\ -$(LIB): $(OFILES) - ar rsc $(LIB) $(OFILES) - -$(OFILES): $(HFILES) - -y.tab.h: $(YFILES) - bison -y $(YFLAGS) $(YFILES) - -y.tab.c: y.tab.h - test -f y.tab.c && touch y.tab.c - -clean: - rm -f *.$O *.6 enam.c 6.out a.out y.tab.h y.tab.c $(LIB) +NOINSTALL=1 +include ../../Make.clib install: $(LIB) diff --git a/src/cmd/cc/acid.c b/src/cmd/cc/acid.c index eb7968c4f..c6a6722bd 100644 --- a/src/cmd/cc/acid.c +++ b/src/cmd/cc/acid.c @@ -150,7 +150,7 @@ acidmember(Type *t, int32 off, int flag) if(typesu[l->etype]) { s1 = acidsue(l->link); if(s1 != S) { - Bprint(&outbuf, " 'A' %s %ld %s;\n", + Bprint(&outbuf, " 'A' %s %d %s;\n", amap(s1->name), t->offset+off, amap(s->name)); break; @@ -189,7 +189,7 @@ acidmember(Type *t, int32 off, int flag) if(s == S) break; if(flag) { - Bprint(&outbuf, " '%c' %ld %s;\n", + Bprint(&outbuf, " '%c' %d %s;\n", acidchar[t->etype], t->offset+off, amap(s->name)); } else { Bprint(&outbuf, "\tprint(indent, \"%s\t\", addr.%s, \"\\n\");\n", @@ -209,7 +209,7 @@ acidmember(Type *t, int32 off, int flag) acidmember(l, t->offset+off, flag); Bprint(&outbuf, " };\n"); } else { - Bprint(&outbuf, " %s %ld %s;\n", + Bprint(&outbuf, " %s %d %s;\n", amap(s1->name), t->offset+off, amap(s->name)); } @@ -223,7 +223,7 @@ acidmember(Type *t, int32 off, int flag) } else { Bprint(&outbuf, "\tprint(indent, \"%s {\\n\");\n", amap(s1->name)); - Bprint(&outbuf, "\tindent_%s(addr+%ld, indent+\"\\t\");\n", + Bprint(&outbuf, "\tindent_%s(addr+%d, indent+\"\\t\");\n", amap(s1->name), t->offset+off); Bprint(&outbuf, "\tprint(indent, \"}\\n\");\n"); } @@ -263,7 +263,7 @@ acidtype(Type *t) if(debug['s']) goto asmstr; an = amap(s->name); - Bprint(&outbuf, "sizeof%s = %ld;\n", an, t->width); + Bprint(&outbuf, "sizeof%s = %d;\n", an, t->width); Bprint(&outbuf, "aggr %s\n{\n", an); for(l = t->link; l != T; l = l->down) acidmember(l, 0, 1); @@ -280,7 +280,7 @@ acidtype(Type *t) break; for(l = t->link; l != T; l = l->down) if(l->sym != S) - Bprint(&outbuf, "#define\t%s.%s\t%ld\n", + Bprint(&outbuf, "#define\t%s.%s\t%d\n", s->name, l->sym->name, l->offset); diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h index 69adcccb0..3649bf5f6 100644 --- a/src/cmd/cc/cc.h +++ b/src/cmd/cc/cc.h @@ -166,6 +166,7 @@ struct Type uchar nbits; uchar etype; uchar garb; + uchar align; }; #define T ((Type*)0) @@ -785,7 +786,7 @@ int32 outlstring(ushort*, int32); void sextern(Sym*, Node*, int32, int32); void xcom(Node*); int32 exreg(Type*); -int32 align(int32, Type*, int); +int32 align(int32, Type*, int, int32*); int32 maxround(int32, int32); extern schar ewidth[]; diff --git a/src/cmd/cc/com.c b/src/cmd/cc/com.c index 5cbe8b77c..b1a8a4704 100644 --- a/src/cmd/cc/com.c +++ b/src/cmd/cc/com.c @@ -638,10 +638,10 @@ tcomo(Node *n, int f) n->addable = 1; if(n->class == CEXREG) { n->op = OREGISTER; - // on 386, "extern register" generates + // on 386 or amd64, "extern register" generates // memory references relative to the - // fs segment. - if(thechar == '8') // [sic] + // gs or fs segment. + if(thechar == '8' || thechar == '6') // [sic] n->op = OEXREG; n->reg = n->sym->offset; n->xoffset = 0; diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c index b4d8c4d14..f629925d1 100644 --- a/src/cmd/cc/dcl.c +++ b/src/cmd/cc/dcl.c @@ -195,7 +195,7 @@ doinit(Sym *s, Type *t, int32 o, Node *a) dbgdecl(s); } if(debug['i']) { - print("t = %T; o = %ld; n = %s\n", t, o, s->name); + print("t = %T; o = %d; n = %s\n", t, o, s->name); prtree(a, "doinit value"); } @@ -323,7 +323,7 @@ init1(Sym *s, Type *t, int32 o, int exflag) return Z; if(debug['i']) { - print("t = %T; o = %ld; n = %s\n", t, o, s->name); + print("t = %T; o = %d; n = %s\n", t, o, s->name); prtree(a, "init1 value"); } @@ -479,7 +479,7 @@ init1(Sym *s, Type *t, int32 o, int exflag) e = r->vconst; if(t->width != 0) if(e < 0 || e*w >= t->width) { - diag(a, "initialization index out of range: %ld", e); + diag(a, "initialization index out of range: %d", e); continue; } } @@ -552,9 +552,10 @@ void sualign(Type *t) { Type *l; - int32 o, w; + int32 o, w, maxal; o = 0; + maxal = 0; switch(t->etype) { case TSTRUCT: @@ -577,13 +578,14 @@ sualign(Type *t) l->sym->name); else diag(Z, "incomplete structure element"); - w = align(w, l, Ael1); + w = align(w, l, Ael1, &maxal); l->offset = w; - w = align(w, l, Ael2); + w = align(w, l, Ael2, &maxal); } } - w = align(w, t, Asu2); + w = align(w, t, Asu2, &maxal); t->width = w; + t->align = maxal; acidtype(t); pickletype(t); return; @@ -600,12 +602,13 @@ sualign(Type *t) diag(Z, "incomplete union element"); l->offset = 0; l->shift = 0; - o = align(align(0, l, Ael1), l, Ael2); + o = align(align(0, l, Ael1, &maxal), l, Ael2, &maxal); if(o > w) w = o; } - w = align(w, t, Asu2); + w = align(w, t, Asu2, &maxal); t->width = w; + t->align = maxal; acidtype(t); pickletype(t); return; @@ -663,7 +666,7 @@ argmark(Node *n, int pass) { Type *t; - autoffset = align(0, thisfn->link, Aarg0); + autoffset = align(0, thisfn->link, Aarg0, nil); stkoff = 0; for(; n->left != Z; n = n->left) { if(n->op != OFUNC || n->left->op != ONAME) @@ -745,9 +748,9 @@ loop: firstarg = s; firstargtype = s->type; } - autoffset = align(autoffset, s->type, Aarg1); + autoffset = align(autoffset, s->type, Aarg1, nil); s->offset = autoffset; - autoffset = align(autoffset, s->type, Aarg2); + autoffset = align(autoffset, s->type, Aarg2, nil); } else dodecl(pdecl, CXXX, types[TINT], n); break; @@ -916,7 +919,7 @@ fnproto1(Node *n) void dbgdecl(Sym *s) { - print("decl \"%s\": C=%s [B=%d:O=%ld] T=%T\n", + print("decl \"%s\": C=%s [B=%d:O=%d] T=%T\n", s->name, cnames[s->class], s->block, s->offset, s->type); } @@ -1275,7 +1278,7 @@ adecl(int c, Type *t, Sym *s) } switch(c) { case CAUTO: - autoffset = align(autoffset, t, Aaut3); + autoffset = align(autoffset, t, Aaut3, nil); stkoff = maxround(stkoff, autoffset); s->offset = -autoffset; break; @@ -1285,10 +1288,10 @@ adecl(int c, Type *t, Sym *s) firstarg = s; firstargtype = t; } - autoffset = align(autoffset, t, Aarg1); + autoffset = align(autoffset, t, Aarg1, nil); if(s) s->offset = autoffset; - autoffset = align(autoffset, t, Aarg2); + autoffset = align(autoffset, t, Aarg2, nil); break; } } @@ -1571,7 +1574,7 @@ contig(Sym *s, Node *n, int32 v) Type *zt; if(debug['i']) { - print("contig v = %ld; s = %s\n", v, s->name); + print("contig v = %d; s = %s\n", v, s->name); prtree(n, "doinit value"); } @@ -1587,7 +1590,7 @@ contig(Sym *s, Node *n, int32 v) if(v != 0) diag(n, "automatic adjustable array: %s", s->name); v = s->offset; - autoffset = align(autoffset, s->type, Aaut3); + autoffset = align(autoffset, s->type, Aaut3, nil); s->offset = -autoffset; stkoff = maxround(stkoff, autoffset); symadjust(s, n, v - s->offset); diff --git a/src/cmd/cc/dpchk.c b/src/cmd/cc/dpchk.c index 046c0e4da..6eb5fb409 100644 --- a/src/cmd/cc/dpchk.c +++ b/src/cmd/cc/dpchk.c @@ -369,7 +369,7 @@ checkargs(Node *nn, char *s, int pos) continue; for(l=tprot; l; l=l->link) if(sametype(a->type, l->type)) { -/*print("checking %T/%ulx %T/%ulx\n", a->type, flag.b[0], l->type, l->flag.b[0]);*/ +/*print("checking %T/%ux %T/%ux\n", a->type, flag.b[0], l->type, l->flag.b[0]);*/ if(beq(flag, l->flag)) goto loop; } @@ -400,6 +400,7 @@ dpcheck(Node *n) i = l->param; b = n->right; + a = Z; while(i > 0) { b = nextarg(b, &a); i--; @@ -437,9 +438,9 @@ pragpack(void) ; if(debug['f']) if(packflg) - print("%4ld: pack %d\n", lineno, packflg); + print("%4d: pack %d\n", lineno, packflg); else - print("%4ld: pack off\n", lineno); + print("%4d: pack off\n", lineno); } void @@ -459,9 +460,9 @@ pragfpround(void) ; if(debug['f']) if(fproundflg) - print("%4ld: fproundflg %d\n", lineno, fproundflg); + print("%4d: fproundflg %d\n", lineno, fproundflg); else - print("%4ld: fproundflg off\n", lineno); + print("%4d: fproundflg off\n", lineno); } void @@ -477,7 +478,7 @@ pragtextflag(void) while(getnsc() != '\n') ; if(debug['f']) - print("%4ld: textflag %d\n", lineno, textflag); + print("%4d: textflag %d\n", lineno, textflag); } void diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c index c9facc667..3b413c246 100644 --- a/src/cmd/cc/lex.c +++ b/src/cmd/cc/lex.c @@ -38,7 +38,7 @@ int systemtype(int sys) { -#ifdef __MINGW32__ +#ifdef _WIN32 return sys&Windows; #else return sys&Plan9; @@ -630,7 +630,7 @@ l1: vv = c; yylval.vval = convvtox(vv, TUCHAR); if(yylval.vval != vv) - yyerror("overflow in character constant: 0x%lx", c); + yyerror("overflow in character constant: 0x%x", c); else if(c & 0x80){ nearln = lineno; @@ -1410,11 +1410,11 @@ Lconv(Fmt *fp) strcat(str, " "); } if(a[i].line) - snprint(s, STRINGSZ, "%s:%ld[%s:%ld]", + snprint(s, STRINGSZ, "%s:%d[%s:%d]", a[i].line->name, l-a[i].ldel+1, a[i].incl->name, l-a[i].idel+1); else - snprint(s, STRINGSZ, "%s:%ld", + snprint(s, STRINGSZ, "%s:%d", a[i].incl->name, l-a[i].idel+1); if(strlen(s)+strlen(str) >= STRINGSZ-10) break; @@ -1463,7 +1463,7 @@ Tconv(Fmt *fp) n = t->width; if(t->link && t->link->width) n /= t->link->width; - sprint(s, "[%ld]", n); + sprint(s, "[%d]", n); if(strlen(str) + strlen(s) < STRINGSZ) strcat(str, s); } diff --git a/src/cmd/cc/macbody b/src/cmd/cc/macbody index ca8a54c0b..35740e985 100644 --- a/src/cmd/cc/macbody +++ b/src/cmd/cc/macbody @@ -63,7 +63,7 @@ getsym(void) if(cp <= symb+NSYMB-4) *cp++ = c; c = getc(); - if(isalnum(c) || c == '_' || c >= 0x80) + if(isalnum(c) || c == '_' || c >= 0x80 || c == '$') continue; unget(c); break; diff --git a/src/cmd/cc/pgen.c b/src/cmd/cc/pgen.c index cd6fffc57..a9d7f1ef4 100644 --- a/src/cmd/cc/pgen.c +++ b/src/cmd/cc/pgen.c @@ -37,7 +37,7 @@ argsize(void) int32 s; //print("t=%T\n", thisfn); - s = align(0, thisfn->link, Aarg0); + s = align(0, thisfn->link, Aarg0, nil); for(t=thisfn->down; t!=T; t=t->down) { switch(t->etype) { case TVOID: @@ -47,8 +47,8 @@ argsize(void) s += 64; break; default: - s = align(s, t, Aarg1); - s = align(s, t, Aarg2); + s = align(s, t, Aarg1, nil); + s = align(s, t, Aarg2, nil); break; } //print(" %d %T\n", s, t); @@ -99,7 +99,7 @@ codgen(Node *n, Node *nn) nod1 = *nodret->left; nod1.sym = firstarg; nod1.type = firstargtype; - nod1.xoffset = align(0, firstargtype, Aarg1); + nod1.xoffset = align(0, firstargtype, Aarg1, nil); nod1.etype = firstargtype->etype; nodreg(&nod, &nod1, REGARG); gmove(&nod, &nod1); diff --git a/src/cmd/cc/pickle.c b/src/cmd/cc/pickle.c index fb7ec585b..82cf5eb05 100644 --- a/src/cmd/cc/pickle.c +++ b/src/cmd/cc/pickle.c @@ -143,7 +143,7 @@ picklemember(Type *t, int32 off) case TIND: if(s == S) Bprint(&outbuf, - "%s\"p\", (char*)addr+%ld+_i*%ld);\n", + "%s\"p\", (char*)addr+%d+_i*%d);\n", picklestr, t->offset+off, t->width); else Bprint(&outbuf, @@ -164,14 +164,14 @@ picklemember(Type *t, int32 off) case TFLOAT: case TDOUBLE: if(s == S) - Bprint(&outbuf, "%s\"%c\", (char*)addr+%ld+_i*%ld);\n", + Bprint(&outbuf, "%s\"%c\", (char*)addr+%d+_i*%d);\n", picklestr, picklechar[t->etype], t->offset+off, t->width); else Bprint(&outbuf, "%s\"%c\", &addr->%s);\n", picklestr, picklechar[t->etype], pmap(s->name)); break; case TARRAY: - Bprint(&outbuf, "\tfor(_i = 0; _i < %ld; _i++) {\n\t", + Bprint(&outbuf, "\tfor(_i = 0; _i < %d; _i++) {\n\t", t->width/t->link->width); picklemember(t->link, t->offset+off); Bprint(&outbuf, "\t}\n\t_i = 0;\n\tUSED(_i);\n"); @@ -183,7 +183,7 @@ picklemember(Type *t, int32 off) if(s1 == S) break; if(s == S) { - Bprint(&outbuf, "\tbp = pickle_%s(bp, ep, un, (%s*)((char*)addr+%ld+_i*%ld));\n", + Bprint(&outbuf, "\tbp = pickle_%s(bp, ep, un, (%s*)((char*)addr+%d+_i*%d));\n", pmap(s1->name), pmap(s1->name), t->offset+off, t->width); } else { Bprint(&outbuf, "\tbp = pickle_%s(bp, ep, un, &addr->%s);\n", @@ -235,7 +235,7 @@ pickletype(Type *t) break; for(l = t->link; l != T; l = l->down) if(l->sym != S) - Bprint(&outbuf, "#define\t%s.%s\t%ld\n", + Bprint(&outbuf, "#define\t%s.%s\t%d\n", s->name, l->sym->name, l->offset); diff --git a/src/cmd/cc/pswt.c b/src/cmd/cc/pswt.c index 02d4e64a0..0e402dea7 100644 --- a/src/cmd/cc/pswt.c +++ b/src/cmd/cc/pswt.c @@ -80,7 +80,7 @@ doswit(Node *n) qsort(iq, nc, sizeof(C1), swcmp); if(debug['W']) for(i=0; i<nc; i++) - print("case %2ld: = %.8llux\n", i, (vlong)iq[i].val); + print("case %2d: = %.8llux\n", i, (vlong)iq[i].val); for(i=0; i<nc-1; i++) if(iq[i].val == iq[i+1].val) diag(n, "duplicate cases in switch %lld", (vlong)iq[i].val); @@ -115,7 +115,7 @@ outlstring(ushort *s, int32 n) r = nstring; while(n > 0) { c = *s++; - if(align(0, types[TCHAR], Aarg1)) { + if(align(0, types[TCHAR], Aarg1, nil)) { buf[0] = c>>8; buf[1] = c; } else { diff --git a/src/cmd/cc/sub.c b/src/cmd/cc/sub.c index 335d30bfb..e0d5df719 100644 --- a/src/cmd/cc/sub.c +++ b/src/cmd/cc/sub.c @@ -92,18 +92,18 @@ prtree1(Node *n, int d, int f) { case ONAME: print(" \"%F\"", n); - print(" %ld", n->xoffset); + print(" %d", n->xoffset); i = 0; break; case OINDREG: - print(" %ld(R%d)", n->xoffset, n->reg); + print(" %d(R%d)", n->xoffset, n->reg); i = 0; break; case OREGISTER: if(n->xoffset) - print(" %ld+R%d", n->xoffset, n->reg); + print(" %d+R%d", n->xoffset, n->reg); else print(" R%d", n->reg); i = 0; @@ -845,7 +845,7 @@ simplifyshift(Node *n) /* if(debug['h']) - print("%.3o %ld %ld %d #%.lux\n", + print("%.3o %d %d %d #%.ux\n", (s1<<3)|s2, c1, c2, topbit(c3), c3); */ diff --git a/src/cmd/cgo/Makefile b/src/cmd/cgo/Makefile index 34ca3dd46..5458c3e4f 100644 --- a/src/cmd/cgo/Makefile +++ b/src/cmd/cgo/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=cgo GOFILES=\ diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go index 580a72a95..8689ac3da 100644 --- a/src/cmd/cgo/ast.go +++ b/src/cmd/cgo/ast.go @@ -12,63 +12,13 @@ import ( "go/doc" "go/parser" "go/scanner" + "go/token" "os" "strings" ) -// A Cref refers to an expression of the form C.xxx in the AST. -type Cref struct { - Name string - Expr *ast.Expr - Context string // "type", "expr", "const", or "call" - TypeName bool // whether xxx is a C type name - Type *Type // the type of xxx - FuncType *FuncType -} - -// A ExpFunc is an exported function, callable from C. -type ExpFunc struct { - Func *ast.FuncDecl - ExpName string // name to use from C -} - -// A Prog collects information about a cgo program. -type Prog struct { - AST *ast.File // parsed AST - Preamble string // C preamble (doc comment on import "C") - PackagePath string - Package string - Crefs []*Cref - Typedef map[string]ast.Expr - Vardef map[string]*Type - Funcdef map[string]*FuncType - Enumdef map[string]int64 - Constdef map[string]string - ExpFuncs []*ExpFunc - PtrSize int64 - GccOptions []string - OutDefs map[string]bool -} - -// A Type collects information about a type in both the C and Go worlds. -type Type struct { - Size int64 - Align int64 - C string - Go ast.Expr - EnumValues map[string]int64 -} - -// A FuncType collects information about a function type in both the C and Go worlds. -type FuncType struct { - Params []*Type - Result *Type - Go *ast.FuncType -} - -func openProg(name string, p *Prog) { - var err os.Error - p.AST, err = parser.ParseFile(name, nil, nil, parser.ParseComments) +func parse(name string, flags uint) *ast.File { + ast1, err := parser.ParseFile(fset, name, nil, flags) if err != nil { if list, ok := err.(scanner.ErrorList); ok { // If err is a scanner.ErrorList, its String will print just @@ -82,25 +32,37 @@ func openProg(name string, p *Prog) { } fatal("parsing %s: %s", name, err) } - p.Package = p.AST.Name.Name() + return ast1 +} - // Find the import "C" line and get any extra C preamble. - // Delete the import "C" line along the way. +// ReadGo populates f with information learned from reading the +// Go source file with the given file name. It gathers the C preamble +// attached to the import "C" comment, a list of references to C.xxx, +// a list of exported functions, and the actual AST, to be rewritten and +// printed. +func (f *File) ReadGo(name string) { + // Two different parses: once with comments, once without. + // The printer is not good enough at printing comments in the + // right place when we start editing the AST behind its back, + // so we use ast1 to look for the doc comments on import "C" + // and on exported functions, and we use ast2 for translating + // and reprinting. + ast1 := parse(name, parser.ParseComments) + ast2 := parse(name, 0) + + f.Package = ast1.Name.Name + f.Name = make(map[string]*Name) + + // In ast1, find the import "C" line and get any extra C preamble. sawC := false - w := 0 - for _, decl := range p.AST.Decls { + for _, decl := range ast1.Decls { d, ok := decl.(*ast.GenDecl) if !ok { - p.AST.Decls[w] = decl - w++ continue } - ws := 0 for _, spec := range d.Specs { s, ok := spec.(*ast.ImportSpec) if !ok || string(s.Path.Value) != `"C"` { - d.Specs[ws] = spec - ws++ continue } sawC = true @@ -108,269 +70,330 @@ func openProg(name string, p *Prog) { error(s.Path.Pos(), `cannot rename import "C"`) } if s.Doc != nil { - p.Preamble += doc.CommentText(s.Doc) + "\n" + f.Preamble += doc.CommentText(s.Doc) + "\n" } else if len(d.Specs) == 1 && d.Doc != nil { - p.Preamble += doc.CommentText(d.Doc) + "\n" + f.Preamble += doc.CommentText(d.Doc) + "\n" + } + } + } + if !sawC { + error(token.NoPos, `cannot find import "C"`) + } + + // In ast2, strip the import "C" line. + w := 0 + for _, decl := range ast2.Decls { + d, ok := decl.(*ast.GenDecl) + if !ok { + ast2.Decls[w] = decl + w++ + continue + } + ws := 0 + for _, spec := range d.Specs { + s, ok := spec.(*ast.ImportSpec) + if !ok || string(s.Path.Value) != `"C"` { + d.Specs[ws] = spec + ws++ } } if ws == 0 { continue } d.Specs = d.Specs[0:ws] - p.AST.Decls[w] = d + ast2.Decls[w] = d w++ } - p.AST.Decls = p.AST.Decls[0:w] - - if !sawC { - error(noPos, `cannot find import "C"`) - } + ast2.Decls = ast2.Decls[0:w] // Accumulate pointers to uses of C.x. - if p.Crefs == nil { - p.Crefs = make([]*Cref, 0, 8) + if f.Ref == nil { + f.Ref = make([]*Ref, 0, 8) } - walk(p.AST, p, "prog") + f.walk(ast2, "prog", (*File).saveRef) + + // Accumulate exported functions. + // The comments are only on ast1 but we need to + // save the function bodies from ast2. + // The first walk fills in ExpFunc, and the + // second walk changes the entries to + // refer to ast2 instead. + f.walk(ast1, "prog", (*File).saveExport) + f.walk(ast2, "prog", (*File).saveExport2) + + f.AST = ast2 } -func walk(x interface{}, p *Prog, context string) { - switch n := x.(type) { - case *ast.Expr: - if sel, ok := (*n).(*ast.SelectorExpr); ok { - // For now, assume that the only instance of capital C is - // when used as the imported package identifier. - // The parser should take care of scoping in the future, - // so that we will be able to distinguish a "top-level C" - // from a local C. - if l, ok := sel.X.(*ast.Ident); ok && l.Name() == "C" { - i := len(p.Crefs) - if i >= cap(p.Crefs) { - new := make([]*Cref, 2*i) - for j, v := range p.Crefs { - new[j] = v - } - p.Crefs = new - } - p.Crefs = p.Crefs[0 : i+1] - p.Crefs[i] = &Cref{ - Name: sel.Sel.Name(), - Expr: n, - Context: context, +// Save references to C.xxx for later processing. +func (f *File) saveRef(x interface{}, context string) { + n, ok := x.(*ast.Expr) + if !ok { + return + } + if sel, ok := (*n).(*ast.SelectorExpr); ok { + // For now, assume that the only instance of capital C is + // when used as the imported package identifier. + // The parser should take care of scoping in the future, + // so that we will be able to distinguish a "top-level C" + // from a local C. + if l, ok := sel.X.(*ast.Ident); ok && l.Name == "C" { + if context == "as2" { + context = "expr" + } + goname := sel.Sel.Name + if goname == "errno" { + error(sel.Pos(), "cannot refer to errno directly; see documentation") + return + } + name := f.Name[goname] + if name == nil { + name = &Name{ + Go: goname, } - break + f.Name[goname] = name } + f.Ref = append(f.Ref, &Ref{ + Name: name, + Expr: n, + Context: context, + }) + return } - walk(*n, p, context) + } +} + +// If a function should be exported add it to ExpFunc. +func (f *File) saveExport(x interface{}, context string) { + n, ok := x.(*ast.FuncDecl) + if !ok { + return + } + + if n.Doc == nil { + return + } + for _, c := range n.Doc.List { + if string(c.Text[0:9]) != "//export " { + continue + } + + name := strings.TrimSpace(string(c.Text[9:])) + if name == "" { + error(c.Pos(), "export missing name") + } + + f.ExpFunc = append(f.ExpFunc, &ExpFunc{ + Func: n, + ExpName: name, + }) + break + } +} + +// Make f.ExpFunc[i] point at the Func from this AST instead of the other one. +func (f *File) saveExport2(x interface{}, context string) { + n, ok := x.(*ast.FuncDecl) + if !ok { + return + } + + for _, exp := range f.ExpFunc { + if exp.Func.Name.Name == n.Name.Name { + exp.Func = n + break + } + } +} + +// walk walks the AST x, calling visit(f, x, context) for each node. +func (f *File) walk(x interface{}, context string, visit func(*File, interface{}, string)) { + visit(f, x, context) + switch n := x.(type) { + case *ast.Expr: + f.walk(*n, context, visit) // everything else just recurs default: - error(noPos, "unexpected type %T in walk", x) + error(token.NoPos, "unexpected type %T in walk", x, visit) panic("unexpected type") case nil: // These are ordered and grouped to match ../../pkg/go/ast/ast.go case *ast.Field: - walk(&n.Type, p, "type") + f.walk(&n.Type, "type", visit) case *ast.FieldList: - for _, f := range n.List { - walk(f, p, context) + for _, field := range n.List { + f.walk(field, context, visit) } case *ast.BadExpr: case *ast.Ident: case *ast.Ellipsis: case *ast.BasicLit: case *ast.FuncLit: - walk(n.Type, p, "type") - walk(n.Body, p, "stmt") + f.walk(n.Type, "type", visit) + f.walk(n.Body, "stmt", visit) case *ast.CompositeLit: - walk(&n.Type, p, "type") - walk(n.Elts, p, "expr") + f.walk(&n.Type, "type", visit) + f.walk(n.Elts, "expr", visit) case *ast.ParenExpr: - walk(&n.X, p, context) + f.walk(&n.X, context, visit) case *ast.SelectorExpr: - walk(&n.X, p, "selector") + f.walk(&n.X, "selector", visit) case *ast.IndexExpr: - walk(&n.X, p, "expr") - walk(&n.Index, p, "expr") + f.walk(&n.X, "expr", visit) + f.walk(&n.Index, "expr", visit) case *ast.SliceExpr: - walk(&n.X, p, "expr") - walk(&n.Index, p, "expr") - if n.End != nil { - walk(&n.End, p, "expr") + f.walk(&n.X, "expr", visit) + if n.Low != nil { + f.walk(&n.Low, "expr", visit) + } + if n.High != nil { + f.walk(&n.High, "expr", visit) } case *ast.TypeAssertExpr: - walk(&n.X, p, "expr") - walk(&n.Type, p, "type") + f.walk(&n.X, "expr", visit) + f.walk(&n.Type, "type", visit) case *ast.CallExpr: - walk(&n.Fun, p, "call") - walk(n.Args, p, "expr") + if context == "as2" { + f.walk(&n.Fun, "call2", visit) + } else { + f.walk(&n.Fun, "call", visit) + } + f.walk(n.Args, "expr", visit) case *ast.StarExpr: - walk(&n.X, p, context) + f.walk(&n.X, context, visit) case *ast.UnaryExpr: - walk(&n.X, p, "expr") + f.walk(&n.X, "expr", visit) case *ast.BinaryExpr: - walk(&n.X, p, "expr") - walk(&n.Y, p, "expr") + f.walk(&n.X, "expr", visit) + f.walk(&n.Y, "expr", visit) case *ast.KeyValueExpr: - walk(&n.Key, p, "expr") - walk(&n.Value, p, "expr") + f.walk(&n.Key, "expr", visit) + f.walk(&n.Value, "expr", visit) case *ast.ArrayType: - walk(&n.Len, p, "expr") - walk(&n.Elt, p, "type") + f.walk(&n.Len, "expr", visit) + f.walk(&n.Elt, "type", visit) case *ast.StructType: - walk(n.Fields, p, "field") + f.walk(n.Fields, "field", visit) case *ast.FuncType: - walk(n.Params, p, "field") + f.walk(n.Params, "field", visit) if n.Results != nil { - walk(n.Results, p, "field") + f.walk(n.Results, "field", visit) } case *ast.InterfaceType: - walk(n.Methods, p, "field") + f.walk(n.Methods, "field", visit) case *ast.MapType: - walk(&n.Key, p, "type") - walk(&n.Value, p, "type") + f.walk(&n.Key, "type", visit) + f.walk(&n.Value, "type", visit) case *ast.ChanType: - walk(&n.Value, p, "type") + f.walk(&n.Value, "type", visit) case *ast.BadStmt: case *ast.DeclStmt: - walk(n.Decl, p, "decl") + f.walk(n.Decl, "decl", visit) case *ast.EmptyStmt: case *ast.LabeledStmt: - walk(n.Stmt, p, "stmt") + f.walk(n.Stmt, "stmt", visit) case *ast.ExprStmt: - walk(&n.X, p, "expr") + f.walk(&n.X, "expr", visit) case *ast.IncDecStmt: - walk(&n.X, p, "expr") + f.walk(&n.X, "expr", visit) case *ast.AssignStmt: - walk(n.Lhs, p, "expr") - walk(n.Rhs, p, "expr") + f.walk(n.Lhs, "expr", visit) + if len(n.Lhs) == 2 && len(n.Rhs) == 1 { + f.walk(n.Rhs, "as2", visit) + } else { + f.walk(n.Rhs, "expr", visit) + } case *ast.GoStmt: - walk(n.Call, p, "expr") + f.walk(n.Call, "expr", visit) case *ast.DeferStmt: - walk(n.Call, p, "expr") + f.walk(n.Call, "expr", visit) case *ast.ReturnStmt: - walk(n.Results, p, "expr") + f.walk(n.Results, "expr", visit) case *ast.BranchStmt: case *ast.BlockStmt: - walk(n.List, p, "stmt") + f.walk(n.List, "stmt", visit) case *ast.IfStmt: - walk(n.Init, p, "stmt") - walk(&n.Cond, p, "expr") - walk(n.Body, p, "stmt") - walk(n.Else, p, "stmt") + f.walk(n.Init, "stmt", visit) + f.walk(&n.Cond, "expr", visit) + f.walk(n.Body, "stmt", visit) + f.walk(n.Else, "stmt", visit) case *ast.CaseClause: - walk(n.Values, p, "expr") - walk(n.Body, p, "stmt") + f.walk(n.Values, "expr", visit) + f.walk(n.Body, "stmt", visit) case *ast.SwitchStmt: - walk(n.Init, p, "stmt") - walk(&n.Tag, p, "expr") - walk(n.Body, p, "stmt") + f.walk(n.Init, "stmt", visit) + f.walk(&n.Tag, "expr", visit) + f.walk(n.Body, "stmt", visit) case *ast.TypeCaseClause: - walk(n.Types, p, "type") - walk(n.Body, p, "stmt") + f.walk(n.Types, "type", visit) + f.walk(n.Body, "stmt", visit) case *ast.TypeSwitchStmt: - walk(n.Init, p, "stmt") - walk(n.Assign, p, "stmt") - walk(n.Body, p, "stmt") + f.walk(n.Init, "stmt", visit) + f.walk(n.Assign, "stmt", visit) + f.walk(n.Body, "stmt", visit) case *ast.CommClause: - walk(n.Lhs, p, "expr") - walk(n.Rhs, p, "expr") - walk(n.Body, p, "stmt") + f.walk(n.Lhs, "expr", visit) + f.walk(n.Rhs, "expr", visit) + f.walk(n.Body, "stmt", visit) case *ast.SelectStmt: - walk(n.Body, p, "stmt") + f.walk(n.Body, "stmt", visit) case *ast.ForStmt: - walk(n.Init, p, "stmt") - walk(&n.Cond, p, "expr") - walk(n.Post, p, "stmt") - walk(n.Body, p, "stmt") + f.walk(n.Init, "stmt", visit) + f.walk(&n.Cond, "expr", visit) + f.walk(n.Post, "stmt", visit) + f.walk(n.Body, "stmt", visit) case *ast.RangeStmt: - walk(&n.Key, p, "expr") - walk(&n.Value, p, "expr") - walk(&n.X, p, "expr") - walk(n.Body, p, "stmt") + f.walk(&n.Key, "expr", visit) + f.walk(&n.Value, "expr", visit) + f.walk(&n.X, "expr", visit) + f.walk(n.Body, "stmt", visit) case *ast.ImportSpec: case *ast.ValueSpec: - walk(&n.Type, p, "type") - walk(n.Values, p, "expr") + f.walk(&n.Type, "type", visit) + f.walk(n.Values, "expr", visit) case *ast.TypeSpec: - walk(&n.Type, p, "type") + f.walk(&n.Type, "type", visit) case *ast.BadDecl: case *ast.GenDecl: - walk(n.Specs, p, "spec") + f.walk(n.Specs, "spec", visit) case *ast.FuncDecl: if n.Recv != nil { - walk(n.Recv, p, "field") + f.walk(n.Recv, "field", visit) } - walk(n.Type, p, "type") + f.walk(n.Type, "type", visit) if n.Body != nil { - walk(n.Body, p, "stmt") + f.walk(n.Body, "stmt", visit) } - checkExpFunc(n, p) - case *ast.File: - walk(n.Decls, p, "decl") + f.walk(n.Decls, "decl", visit) case *ast.Package: - for _, f := range n.Files { - walk(f, p, "file") + for _, file := range n.Files { + f.walk(file, "file", visit) } case []ast.Decl: for _, d := range n { - walk(d, p, context) + f.walk(d, context, visit) } case []ast.Expr: for i := range n { - walk(&n[i], p, context) + f.walk(&n[i], context, visit) } case []ast.Stmt: for _, s := range n { - walk(s, p, context) + f.walk(s, context, visit) } case []ast.Spec: for _, s := range n { - walk(s, p, context) - } - } -} - -// If a function should be exported add it to ExpFuncs. -func checkExpFunc(n *ast.FuncDecl, p *Prog) { - if n.Doc == nil { - return - } - for _, c := range n.Doc.List { - if string(c.Text[0:9]) != "//export " { - continue - } - - name := strings.TrimSpace(string(c.Text[9:])) - if name == "" { - error(c.Position, "export missing name") - } - - if p.ExpFuncs == nil { - p.ExpFuncs = make([]*ExpFunc, 0, 8) + f.walk(s, context, visit) } - i := len(p.ExpFuncs) - if i >= cap(p.ExpFuncs) { - new := make([]*ExpFunc, 2*i) - for j, v := range p.ExpFuncs { - new[j] = v - } - p.ExpFuncs = new - } - p.ExpFuncs = p.ExpFuncs[0 : i+1] - p.ExpFuncs[i] = &ExpFunc{ - Func: n, - ExpName: name, - } - break } } diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index 022a87c15..0f9204d7f 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -23,6 +23,31 @@ the package. For example: // #include <errno.h> import "C" +C identifiers or field names that are keywords in Go can be +accessed by prefixing them with an underscore: if x points at +a C struct with a field named "type", x._type accesses the field. + +The standard C numeric types are available under the names +C.char, C.schar (signed char), C.uchar (unsigned char), +C.short, C.ushort (unsigned short), C.int, C.uint (unsigned int), +C.long, C.ulong (unsigned long), C.longlong (long long), +C.ulonglong (unsigned long long), C.float, C.double. + +To access a struct, union, or enum type directly, prefix it with +struct_, union_, or enum_, as in C.struct_stat. + +Any C function that returns a value may be called in a multiple +assignment context to retrieve both the return value and the +C errno variable as an os.Error. For example: + + n, err := C.atoi("abc") + +In C, a function argument written as a fixed size array +actually requires a pointer to the first element of the array. +C compilers are aware of this calling convention and adjust +the call accordingly, but Go cannot. In Go, you must pass +the pointer to the first element explicitly: C.f(&x[0]). + Cgo transforms the input file into four output files: two Go source files, a C file for 6c (or 8c or 5c), and a C file for gcc. diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 5e12a6687..be3b8fe64 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Annotate Crefs in Prog with C types by parsing gcc debug output. +// Annotate Ref in Prog with C types by parsing gcc debug output. // Conversion of debug output to Go types. package main @@ -12,6 +12,8 @@ import ( "debug/dwarf" "debug/elf" "debug/macho" + "debug/pe" + "flag" "fmt" "go/ast" "go/parser" @@ -21,12 +23,64 @@ import ( "strings" ) -func (p *Prog) loadDebugInfo() { +var debugDefine = flag.Bool("debug-define", false, "print relevant #defines") +var debugGcc = flag.Bool("debug-gcc", false, "print gcc invocations") + +var nameToC = map[string]string{ + "schar": "signed char", + "uchar": "unsigned char", + "ushort": "unsigned short", + "uint": "unsigned int", + "ulong": "unsigned long", + "longlong": "long long", + "ulonglong": "unsigned long long", +} + +// cname returns the C name to use for C.s. +// The expansions are listed in nameToC and also +// struct_foo becomes "struct foo", and similarly for +// union and enum. +func cname(s string) string { + if t, ok := nameToC[s]; ok { + return t + } + + if strings.HasPrefix(s, "struct_") { + return "struct " + s[len("struct_"):] + } + if strings.HasPrefix(s, "union_") { + return "union " + s[len("union_"):] + } + if strings.HasPrefix(s, "enum_") { + return "enum " + s[len("enum_"):] + } + return s +} + +// Translate rewrites f.AST, the original Go input, to remove +// references to the imported package C, replacing them with +// references to the equivalent Go types, functions, and variables. +func (p *Package) Translate(f *File) { + for _, cref := range f.Ref { + // Convert C.ulong to C.unsigned long, etc. + cref.Name.C = cname(cref.Name.Go) + } + p.loadDefines(f) + needType := p.guessKinds(f) + if len(needType) > 0 { + p.loadDWARF(f, needType) + } + p.rewriteRef(f) +} + +// loadDefines coerces gcc into spitting out the #defines in use +// in the file f and saves relevant renamings in f.Name[name].Define. +func (p *Package) loadDefines(f *File) { var b bytes.Buffer + b.WriteString(builtinProlog) + b.WriteString(f.Preamble) + stdout := p.gccDefines(b.Bytes()) - b.WriteString(p.Preamble) - stdout := p.gccPostProc(b.Bytes()) - defines := make(map[string]string) for _, line := range strings.Split(stdout, "\n", -1) { if len(line) < 9 || line[0:7] != "#define" { continue @@ -48,70 +102,112 @@ func (p *Prog) loadDebugInfo() { val = strings.TrimSpace(line[tabIndex:]) } - // Only allow string, character, and numeric constants. Ignoring #defines for - // symbols allows those symbols to be referenced in Go, as they will be - // translated by gcc later. - _, err := strconv.Atoi(string(val[0])) - if err == nil || val[0] == '\'' || val[0] == '"' { - defines[key] = val - } - } - - // Construct a slice of unique names from p.Crefs. - m := make(map[string]int) - for _, c := range p.Crefs { - // If we've already found this name as a define, it is not a Cref. - if val, ok := defines[c.Name]; ok { - _, err := parser.ParseExpr("", val, nil) - if err != nil { - fmt.Fprintf(os.Stderr, "The value in C.%s does not parse as a Go expression; cannot use.\n", c.Name) - os.Exit(2) + if n := f.Name[key]; n != nil { + if *debugDefine { + fmt.Fprintf(os.Stderr, "#define %s %s\n", key, val) } - - c.Context = "const" - c.TypeName = false - p.Constdef[c.Name] = val - continue + n.Define = val } - m[c.Name] = -1 - } - names := make([]string, 0, len(m)) - for name, _ := range m { - i := len(names) - names = names[0 : i+1] - names[i] = name - m[name] = i } +} +// guessKinds tricks gcc into revealing the kind of each +// name xxx for the references C.xxx in the Go input. +// The kind is either a constant, type, or variable. +func (p *Package) guessKinds(f *File) []*Name { // Coerce gcc into telling us whether each name is // a type, a value, or undeclared. We compile a function // containing the line: // name; // If name is a type, gcc will print: - // x.c:2: warning: useless type name in empty declaration + // cgo-test:2: warning: useless type name in empty declaration // If name is a value, gcc will print - // x.c:2: warning: statement with no effect + // cgo-test:2: warning: statement with no effect // If name is undeclared, gcc will print - // x.c:2: error: 'name' undeclared (first use in this function) + // cgo-test:2: error: 'name' undeclared (first use in this function) // A line number directive causes the line number to // correspond to the index in the names array. - b.Reset() - b.WriteString(p.Preamble) + // + // The line also has an enum declaration: + // name; enum { _cgo_enum_1 = name }; + // If name is not a constant, gcc will print: + // cgo-test:4: error: enumerator value for '_cgo_enum_4' is not an integer constant + // we assume lines without that error are constants. + + // Make list of names that need sniffing, type lookup. + toSniff := make([]*Name, 0, len(f.Name)) + needType := make([]*Name, 0, len(f.Name)) + + for _, n := range f.Name { + // If we've already found this name as a #define + // and we can translate it as a constant value, do so. + if n.Define != "" { + ok := false + if _, err := strconv.Atoi(n.Define); err == nil { + ok = true + } else if n.Define[0] == '"' || n.Define[0] == '\'' { + _, err := parser.ParseExpr(fset, "", n.Define) + if err == nil { + ok = true + } + } + if ok { + n.Kind = "const" + n.Const = n.Define + continue + } + + if isName(n.Define) { + n.C = n.Define + } + } + + // If this is a struct, union, or enum type name, + // record the kind but also that we need type information. + if strings.HasPrefix(n.C, "struct ") || strings.HasPrefix(n.C, "union ") || strings.HasPrefix(n.C, "enum ") { + n.Kind = "type" + i := len(needType) + needType = needType[0 : i+1] + needType[i] = n + continue + } + + i := len(toSniff) + toSniff = toSniff[0 : i+1] + toSniff[i] = n + } + + if len(toSniff) == 0 { + return needType + } + + var b bytes.Buffer + b.WriteString(builtinProlog) + b.WriteString(f.Preamble) b.WriteString("void f(void) {\n") b.WriteString("#line 0 \"cgo-test\"\n") - for _, n := range names { - b.WriteString(n) - b.WriteString(";\n") + for i, n := range toSniff { + fmt.Fprintf(&b, "%s; enum { _cgo_enum_%d = %s }; /* cgo-test:%d */\n", n.C, i, n.C, i) } b.WriteString("}\n") - - kind := make(map[string]string) - _, stderr := p.gccDebug(b.Bytes()) + stderr := p.gccErrors(b.Bytes()) if stderr == "" { - fatal("gcc produced no output") + fatal("gcc produced no output\non input:\n%s", b.Bytes()) } + + names := make([]*Name, len(toSniff)) + copy(names, toSniff) + + isConst := make([]bool, len(toSniff)) + for i := range isConst { + isConst[i] = true // until proven otherwise + } + for _, line := range strings.Split(stderr, "\n", -1) { if len(line) < 9 || line[0:9] != "cgo-test:" { + if len(line) > 8 && line[0:8] == "<stdin>:" { + fatal("gcc produced unexpected output:\n%s\non input:\n%s", line, b.Bytes()) + } continue } line = line[9:] @@ -127,28 +223,52 @@ func (p *Prog) loadDebugInfo() { switch { default: continue - case strings.Index(line, ": useless type name in empty declaration") >= 0: + case strings.Contains(line, ": useless type name in empty declaration"): what = "type" - case strings.Index(line, ": statement with no effect") >= 0: - what = "value" - case strings.Index(line, "undeclared") >= 0: - what = "error" + isConst[i] = false + case strings.Contains(line, ": statement with no effect"): + what = "not-type" // const or func or var + case strings.Contains(line, "undeclared"): + error(token.NoPos, "%s", strings.TrimSpace(line[colon+1:])) + case strings.Contains(line, "is not an integer constant"): + isConst[i] = false + continue } - if old, ok := kind[names[i]]; ok && old != what { - error(noPos, "inconsistent gcc output about C.%s", names[i]) + n := toSniff[i] + if n == nil { + continue } - kind[names[i]] = what + toSniff[i] = nil + n.Kind = what + + j := len(needType) + needType = needType[0 : j+1] + needType[j] = n } - for _, n := range names { - if _, ok := kind[n]; !ok { - error(noPos, "could not determine kind of name for C.%s", n) + for i, b := range isConst { + if b { + names[i].Kind = "const" } } - + for _, n := range toSniff { + if n == nil { + continue + } + if n.Kind != "" { + continue + } + error(token.NoPos, "could not determine kind of name for C.%s", n.Go) + } if nerrors > 0 { - fatal("failed to interpret gcc output:\n%s", stderr) + fatal("unresolved names") } + return needType +} +// loadDWARF parses the DWARF debug information generated +// by gcc to learn the details of the constants, variables, and types +// being referred to as C.xxx. +func (p *Package) loadDWARF(f *File, names []*Name) { // Extract the types from the DWARF section of an object // from a well-formed C program. Gcc only generates DWARF info // for symbols in the object file, so it is not enough to print the @@ -157,19 +277,24 @@ func (p *Prog) loadDebugInfo() { // typeof(names[i]) *__cgo__i; // for each entry in names and then dereference the type we // learn for __cgo__i. - b.Reset() - b.WriteString(p.Preamble) + var b bytes.Buffer + b.WriteString(builtinProlog) + b.WriteString(f.Preamble) for i, n := range names { - fmt.Fprintf(&b, "typeof(%s) *__cgo__%d;\n", n, i) - } - d, stderr := p.gccDebug(b.Bytes()) - if d == nil { - fatal("gcc failed:\n%s\non input:\n%s", stderr, b.Bytes()) + fmt.Fprintf(&b, "typeof(%s) *__cgo__%d;\n", n.C, i) + if n.Kind == "const" { + fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C) + } } + d := p.gccDebug(b.Bytes()) // Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i. types := make([]dwarf.Type, len(names)) enums := make([]dwarf.Offset, len(names)) + nameToIndex := make(map[*Name]int) + for i, n := range names { + nameToIndex[n] = i + } r := d.Reader() for { e, err := r.Next() @@ -192,9 +317,11 @@ func (p *Prog) loadDebugInfo() { } if e.Tag == dwarf.TagEnumerator { entryName := e.Val(dwarf.AttrName).(string) - i, ok := m[entryName] - if ok { - enums[i] = offset + if strings.HasPrefix(entryName, "__cgo_enum__") { + n, _ := strconv.Atoi(entryName[len("__cgo_enum__"):]) + if 0 <= n && n < len(names) { + enums[n] = offset + } } } } @@ -234,97 +361,221 @@ func (p *Prog) loadDebugInfo() { } } - // Record types and typedef information in Crefs. + // Record types and typedef information. var conv typeConv conv.Init(p.PtrSize) - for _, c := range p.Crefs { - i, ok := m[c.Name] - if !ok { - if _, ok := p.Constdef[c.Name]; !ok { - fatal("Cref %s is no longer around", c.Name) - } - continue - } - c.TypeName = kind[c.Name] == "type" + for i, n := range names { f, fok := types[i].(*dwarf.FuncType) - if c.Context == "call" && !c.TypeName && fok { - c.FuncType = conv.FuncType(f) + if n.Kind != "type" && fok { + n.Kind = "func" + n.FuncType = conv.FuncType(f) } else { - c.Type = conv.Type(types[i]) + n.Type = conv.Type(types[i]) + if enums[i] != 0 && n.Type.EnumValues != nil { + k := fmt.Sprintf("__cgo_enum__%d", i) + n.Kind = "const" + n.Const = strconv.Itoa64(n.Type.EnumValues[k]) + // Remove injected enum to ensure the value will deep-compare + // equally in future loads of the same constant. + n.Type.EnumValues[k] = 0, false + } } } - p.Typedef = conv.typedef } -func concat(a, b []string) []string { - c := make([]string, len(a)+len(b)) - for i, s := range a { - c[i] = s +// rewriteRef rewrites all the C.xxx references in f.AST to refer to the +// Go equivalents, now that we have figured out the meaning of all +// the xxx. +func (p *Package) rewriteRef(f *File) { + // Assign mangled names. + for _, n := range f.Name { + if n.Kind == "not-type" { + n.Kind = "var" + } + if n.Mangle == "" { + n.Mangle = "_C" + n.Kind + "_" + n.Go + } } - for i, s := range b { - c[i+len(a)] = s + + // Now that we have all the name types filled in, + // scan through the Refs to identify the ones that + // are trying to do a ,err call. Also check that + // functions are only used in calls. + for _, r := range f.Ref { + var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default + switch r.Context { + case "call", "call2": + if r.Name.Kind != "func" { + if r.Name.Kind == "type" { + r.Context = "type" + expr = r.Name.Type.Go + break + } + error(r.Pos(), "call of non-function C.%s", r.Name.Go) + break + } + if r.Context == "call2" { + if r.Name.FuncType.Result == nil { + error(r.Pos(), "assignment count mismatch: 2 = 0") + } + // Invent new Name for the two-result function. + n := f.Name["2"+r.Name.Go] + if n == nil { + n = new(Name) + *n = *r.Name + n.AddError = true + n.Mangle = "_C2func_" + n.Go + f.Name["2"+r.Name.Go] = n + } + expr = ast.NewIdent(n.Mangle) + r.Name = n + break + } + case "expr": + if r.Name.Kind == "func" { + error(r.Pos(), "must call C.%s", r.Name.Go) + } + if r.Name.Kind == "type" { + // Okay - might be new(T) + expr = r.Name.Type.Go + } + if r.Name.Kind == "var" { + expr = &ast.StarExpr{X: expr} + } + + case "type": + if r.Name.Kind != "type" { + error(r.Pos(), "expression C.%s used as type", r.Name.Go) + } else { + expr = r.Name.Type.Go + } + default: + if r.Name.Kind == "func" { + error(r.Pos(), "must call C.%s", r.Name.Go) + } + } + *r.Expr = expr } - return c } -// gccDebug runs gcc -gdwarf-2 over the C program stdin and -// returns the corresponding DWARF data and any messages -// printed to standard error. -func (p *Prog) gccDebug(stdin []byte) (*dwarf.Data, string) { - machine := "-m32" +// gccName returns the name of the compiler to run. Use $GCC if set in +// the environment, otherwise just "gcc". + +func (p *Package) gccName() (ret string) { + if ret = os.Getenv("GCC"); ret == "" { + ret = "gcc" + } + return +} + +// gccMachine returns the gcc -m flag to use, either "-m32" or "-m64". +func (p *Package) gccMachine() string { if p.PtrSize == 8 { - machine = "-m64" + return "-m64" } + return "-m32" +} - tmp := "_cgo_.o" - base := []string{ - "gcc", - machine, +const gccTmp = "_cgo_.o" + +// gccCmd returns the gcc command line to use for compiling +// the input. +func (p *Package) gccCmd() []string { + return []string{ + p.gccName(), + p.gccMachine(), "-Wall", // many warnings "-Werror", // warnings are errors - "-o" + tmp, // write object to tmp + "-o" + gccTmp, // write object to tmp "-gdwarf-2", // generate DWARF v2 debugging symbols "-fno-eliminate-unused-debug-types", // gets rid of e.g. untyped enum otherwise "-c", // do not link "-xc", // input language is C "-", // read input from standard input } - _, stderr, ok := run(stdin, concat(base, p.GccOptions)) - if !ok { - return nil, string(stderr) - } +} + +// gccDebug runs gcc -gdwarf-2 over the C program stdin and +// returns the corresponding DWARF data and any messages +// printed to standard error. +func (p *Package) gccDebug(stdin []byte) *dwarf.Data { + runGcc(stdin, append(p.gccCmd(), p.GccOptions...)) // Try to parse f as ELF and Mach-O and hope one works. var f interface { DWARF() (*dwarf.Data, os.Error) } var err os.Error - if f, err = elf.Open(tmp); err != nil { - if f, err = macho.Open(tmp); err != nil { - fatal("cannot parse gcc output %s as ELF or Mach-O object", tmp) + if f, err = elf.Open(gccTmp); err != nil { + if f, err = macho.Open(gccTmp); err != nil { + if f, err = pe.Open(gccTmp); err != nil { + fatal("cannot parse gcc output %s as ELF or Mach-O or PE object", gccTmp) + } } } d, err := f.DWARF() if err != nil { - fatal("cannot load DWARF debug information from %s: %s", tmp, err) + fatal("cannot load DWARF debug information from %s: %s", gccTmp, err) } - return d, "" + return d } -func (p *Prog) gccPostProc(stdin []byte) string { - machine := "-m32" - if p.PtrSize == 8 { - machine = "-m64" +// gccDefines runs gcc -E -dM -xc - over the C program stdin +// and returns the corresponding standard output, which is the +// #defines that gcc encountered while processing the input +// and its included files. +func (p *Package) gccDefines(stdin []byte) string { + base := []string{p.gccName(), p.gccMachine(), "-E", "-dM", "-xc", "-"} + stdout, _ := runGcc(stdin, append(base, p.GccOptions...)) + return stdout +} + +// gccErrors runs gcc over the C program stdin and returns +// the errors that gcc prints. That is, this function expects +// gcc to fail. +func (p *Package) gccErrors(stdin []byte) string { + // TODO(rsc): require failure + args := append(p.gccCmd(), p.GccOptions...) + if *debugGcc { + fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(args, " ")) + os.Stderr.Write(stdin) + fmt.Fprint(os.Stderr, "EOF\n") + } + stdout, stderr, _ := run(stdin, args) + if *debugGcc { + os.Stderr.Write(stdout) + os.Stderr.Write(stderr) } + return string(stderr) +} - base := []string{"gcc", machine, "-E", "-dM", "-xc", "-"} - stdout, stderr, ok := run(stdin, concat(base, p.GccOptions)) +// runGcc runs the gcc command line args with stdin on standard input. +// If the command exits with a non-zero exit status, runGcc prints +// details about what was run and exits. +// Otherwise runGcc returns the data written to standard output and standard error. +// Note that for some of the uses we expect useful data back +// on standard error, but for those uses gcc must still exit 0. +func runGcc(stdin []byte, args []string) (string, string) { + if *debugGcc { + fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(args, " ")) + os.Stderr.Write(stdin) + fmt.Fprint(os.Stderr, "EOF\n") + } + stdout, stderr, ok := run(stdin, args) + if *debugGcc { + os.Stderr.Write(stdout) + os.Stderr.Write(stderr) + } if !ok { - return string(stderr) + fmt.Fprint(os.Stderr, "Error running gcc:\n") + fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(args, " ")) + os.Stderr.Write(stdin) + fmt.Fprint(os.Stderr, "EOF\n") + os.Stderr.Write(stderr) + os.Exit(2) } - - return string(stdout) + return string(stdout), string(stderr) } // A typeConv is a translator from dwarf types to Go types @@ -345,14 +596,14 @@ type typeConv struct { string ast.Expr ptrSize int64 - - tagGen int } +var tagGen int +var typedef = make(map[string]ast.Expr) + func (c *typeConv) Init(ptrSize int64) { c.ptrSize = ptrSize c.m = make(map[dwarf.Type]*Type) - c.typedef = make(map[string]ast.Expr) c.bool = c.Ident("bool") c.byte = c.Ident("byte") c.int8 = c.Ident("int8") @@ -388,7 +639,7 @@ func base(dt dwarf.Type) dwarf.Type { } // Map from dwarf text names to aliases we use in package "C". -var cnameMap = map[string]string{ +var dwarfToName = map[string]string{ "long int": "long", "long unsigned int": "ulong", "unsigned int": "uint", @@ -415,6 +666,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { t.C = dtype.Common().Name t.EnumValues = nil c.m[dtype] = t + if t.Size < 0 { // Unsized types are [0]byte t.Size = 0 @@ -550,16 +802,16 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { // Have to give it a name to simulate C "struct foo" references. tag := dt.StructName if tag == "" { - tag = "__" + strconv.Itoa(c.tagGen) - c.tagGen++ + tag = "__" + strconv.Itoa(tagGen) + tagGen++ } else if t.C == "" { t.C = dt.Kind + " " + tag } - name := c.Ident("_C" + dt.Kind + "_" + tag) + name := c.Ident("_Ctype_" + dt.Kind + "_" + tag) t.Go = name // publish before recursive calls switch dt.Kind { case "union", "class": - c.typedef[name.Name()] = c.Opaque(t.Size) + typedef[name.Name] = c.Opaque(t.Size) if t.C == "" { t.C = fmt.Sprintf("typeof(unsigned char[%d])", t.Size) } @@ -569,7 +821,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { t.C = csyntax } t.Align = align - c.typedef[name.Name()] = g + typedef[name.Name] = g } case *dwarf.TypedefType: @@ -583,13 +835,13 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { t.Align = c.ptrSize break } - name := c.Ident("_C_" + dt.Name) + name := c.Ident("_Ctypedef_" + dt.Name) t.Go = name // publish before recursive call sub := c.Type(dt.Type) t.Size = sub.Size t.Align = sub.Align - if _, ok := c.typedef[name.Name()]; !ok { - c.typedef[name.Name()] = sub.Go + if _, ok := typedef[name.Name]; !ok { + typedef[name.Name] = sub.Go } case *dwarf.UcharType: @@ -628,12 +880,12 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { case *dwarf.AddrType, *dwarf.BoolType, *dwarf.CharType, *dwarf.IntType, *dwarf.FloatType, *dwarf.UcharType, *dwarf.UintType: s := dtype.Common().Name if s != "" { - if ss, ok := cnameMap[s]; ok { + if ss, ok := dwarfToName[s]; ok { s = ss } s = strings.Join(strings.Split(s, " ", -1), "") // strip spaces - name := c.Ident("_C_" + s) - c.typedef[name.Name()] = t.Go + name := c.Ident("_Ctype_" + s) + typedef[name.Name] = t.Go t.Go = name } } @@ -710,7 +962,9 @@ func (c *typeConv) FuncType(dtype *dwarf.FuncType) *FuncType { } // Identifier -func (c *typeConv) Ident(s string) *ast.Ident { return ast.NewIdent(s) } +func (c *typeConv) Ident(s string) *ast.Ident { + return ast.NewIdent(s) +} // Opaque type of n bytes. func (c *typeConv) Opaque(n int64) ast.Expr { @@ -736,13 +990,14 @@ func (c *typeConv) pad(fld []*ast.Field, size int64) []*ast.Field { return fld } -// Struct conversion +// Struct conversion: return Go and (6g) C syntax for type. func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax string, align int64) { - csyntax = "struct { " + var buf bytes.Buffer + buf.WriteString("struct {") fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field off := int64(0) - // Mangle struct fields that happen to be named Go keywords into + // Rename struct fields that happen to be named Go keywords into // _{keyword}. Create a map from C ident -> Go ident. The Go ident will // be mangled. Any existing identifier that already has the same name on // the C-side will cause the Go-mangled version to be prefixed with _. @@ -783,7 +1038,10 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[f.Name])}, Type: t.Go} off += t.Size - csyntax += t.C + " " + f.Name + "; " + buf.WriteString(t.C) + buf.WriteString(" ") + buf.WriteString(f.Name) + buf.WriteString("; ") if t.Align > align { align = t.Align } @@ -795,7 +1053,8 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s if off != dt.ByteSize { fatal("struct size calculation error") } - csyntax += "}" + buf.WriteString("}") + csyntax = buf.String() expr = &ast.StructType{Fields: &ast.FieldList{List: fld}} return } diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index 070146c9a..942bda5f4 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -11,13 +11,95 @@ package main import ( + "crypto/md5" + "flag" "fmt" "go/ast" + "go/token" + "io" "os" + "reflect" "strings" ) -func usage() { fmt.Fprint(os.Stderr, "usage: cgo [compiler options] file.go ...\n") } +// A Package collects information about the package we're going to write. +type Package struct { + PackageName string // name of package + PackagePath string + PtrSize int64 + GccOptions []string + Written map[string]bool + Name map[string]*Name // accumulated Name from Files + Typedef map[string]ast.Expr // accumulated Typedef from Files + ExpFunc []*ExpFunc // accumulated ExpFunc from Files + Decl []ast.Decl + GoFiles []string // list of Go files + GccFiles []string // list of gcc output files +} + +// A File collects information about a single Go input file. +type File struct { + AST *ast.File // parsed AST + Package string // Package name + Preamble string // C preamble (doc comment on import "C") + Ref []*Ref // all references to C.xxx in AST + ExpFunc []*ExpFunc // exported functions for this file + Name map[string]*Name // map from Go name to Name + Typedef map[string]ast.Expr // translations of all necessary types from C +} + +// A Ref refers to an expression of the form C.xxx in the AST. +type Ref struct { + Name *Name + Expr *ast.Expr + Context string // "type", "expr", "call", or "call2" +} + +func (r *Ref) Pos() token.Pos { + return (*r.Expr).Pos() +} + +// A Name collects information about C.xxx. +type Name struct { + Go string // name used in Go referring to package C + Mangle string // name used in generated Go + C string // name used in C + Define string // #define expansion + Kind string // "const", "type", "var", "func", "not-type" + Type *Type // the type of xxx + FuncType *FuncType + AddError bool + Const string // constant definition +} + +// A ExpFunc is an exported function, callable from C. +// Such functions are identified in the Go input file +// by doc comments containing the line //export ExpName +type ExpFunc struct { + Func *ast.FuncDecl + ExpName string // name to use from C +} + +// A Type collects information about a type in both the C and Go worlds. +type Type struct { + Size int64 + Align int64 + C string + Go ast.Expr + EnumValues map[string]int64 +} + +// A FuncType collects information about a function type in both the C and Go worlds. +type FuncType struct { + Params []*Type + Result *Type + Go *ast.FuncType +} + +func usage() { + fmt.Fprint(os.Stderr, "usage: cgo [compiler options] file.go ...\n") + os.Exit(2) +} var ptrSizeMap = map[string]int64{ "386": 4, @@ -25,43 +107,60 @@ var ptrSizeMap = map[string]int64{ "arm": 4, } -var expandName = map[string]string{ - "schar": "signed char", - "uchar": "unsigned char", - "ushort": "unsigned short", - "uint": "unsigned int", - "ulong": "unsigned long", - "longlong": "long long", - "ulonglong": "unsigned long long", -} +var cPrefix string + +var fset = token.NewFileSet() + +var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file") func main() { - args := os.Args - if len(args) < 2 { + flag.Usage = usage + flag.Parse() + + if *dynobj != "" { + // cgo -dynimport is essentially a separate helper command + // built into the cgo binary. It scans a gcc-produced executable + // and dumps information about the imported symbols and the + // imported libraries. The Make.pkg rules for cgo prepare an + // appropriate executable and then use its import information + // instead of needing to make the linkers duplicate all the + // specialized knowledge gcc has about where to look for imported + // symbols and which ones to use. + syms, imports := dynimport(*dynobj) + for _, sym := range syms { + fmt.Printf("#pragma dynimport %s %s %q\n", sym, sym, "") + } + for _, p := range imports { + fmt.Printf("#pragma dynimport %s %s %q\n", "_", "_", p) + } + return + } + + args := flag.Args() + if len(args) < 1 { usage() - os.Exit(2) } // Find first arg that looks like a go file and assume everything before // that are options to pass to gcc. var i int - for i = len(args) - 1; i > 0; i-- { - if !strings.HasSuffix(args[i], ".go") { + for i = len(args); i > 0; i-- { + if !strings.HasSuffix(args[i-1], ".go") { break } } - - i += 1 - - gccOptions, goFiles := args[1:i], args[i:] + if i == len(args) { + usage() + } + gccOptions, goFiles := args[0:i], args[i:] arch := os.Getenv("GOARCH") if arch == "" { fatal("$GOARCH is not set") } - ptrSize, ok := ptrSizeMap[arch] - if !ok { - fatal("unknown architecture %s", arch) + ptrSize := ptrSizeMap[arch] + if ptrSize == 0 { + fatal("unknown $GOARCH %q", arch) } // Clear locale variables so gcc emits English errors [sic]. @@ -69,75 +168,82 @@ func main() { os.Setenv("LC_ALL", "C") os.Setenv("LC_CTYPE", "C") - p := new(Prog) - - p.PtrSize = ptrSize - p.GccOptions = gccOptions - p.Vardef = make(map[string]*Type) - p.Funcdef = make(map[string]*FuncType) - p.Enumdef = make(map[string]int64) - p.Constdef = make(map[string]string) - p.OutDefs = make(map[string]bool) + p := &Package{ + PtrSize: ptrSize, + GccOptions: gccOptions, + Written: make(map[string]bool), + } + // Need a unique prefix for the global C symbols that + // we use to coordinate between gcc and ourselves. + // We already put _cgo_ at the beginning, so the main + // concern is other cgo wrappers for the same functions. + // Use the beginning of the md5 of the input to disambiguate. + h := md5.New() for _, input := range goFiles { - // Reset p.Preamble so that we don't end up with conflicting headers / defines - p.Preamble = builtinProlog - openProg(input, p) - for _, cref := range p.Crefs { - // Convert C.ulong to C.unsigned long, etc. - if expand, ok := expandName[cref.Name]; ok { - cref.Name = expand - } + f, err := os.Open(input, os.O_RDONLY, 0) + if err != nil { + fatal("%s", err) } - p.loadDebugInfo() - for _, cref := range p.Crefs { + io.Copy(h, f) + f.Close() + } + cPrefix = fmt.Sprintf("_%x", h.Sum()[0:6]) + + for _, input := range goFiles { + f := new(File) + // Reset f.Preamble so that we don't end up with conflicting headers / defines + f.Preamble = "" + f.ReadGo(input) + p.Translate(f) + for _, cref := range f.Ref { switch cref.Context { - case "const": - // This came from a #define and we'll output it later. - *cref.Expr = ast.NewIdent(cref.Name) - break - case "call": - if !cref.TypeName { - // Is an actual function call. - pos := (*cref.Expr).Pos() - *cref.Expr = &ast.Ident{Position: pos, Obj: ast.NewObj(ast.Err, pos, "_C_"+cref.Name)} - p.Funcdef[cref.Name] = cref.FuncType - break - } - *cref.Expr = cref.Type.Go - case "expr": - if cref.TypeName { - error((*cref.Expr).Pos(), "type C.%s used as expression", cref.Name) - } - // If the expression refers to an enumerated value, then - // place the identifier for the value and add it to Enumdef so - // it will be declared as a constant in the later stage. - if cref.Type.EnumValues != nil { - *cref.Expr = ast.NewIdent(cref.Name) - p.Enumdef[cref.Name] = cref.Type.EnumValues[cref.Name] + case "call", "call2": + if cref.Name.Kind != "type" { break } - // Reference to C variable. - // We declare a pointer and arrange to have it filled in. - *cref.Expr = &ast.StarExpr{X: ast.NewIdent("_C_" + cref.Name)} - p.Vardef[cref.Name] = cref.Type - case "type": - if !cref.TypeName { - error((*cref.Expr).Pos(), "expression C.%s used as type", cref.Name) - } - *cref.Expr = cref.Type.Go + *cref.Expr = cref.Name.Type.Go } } if nerrors > 0 { os.Exit(2) } - pkg := p.Package + pkg := f.Package if dir := os.Getenv("CGOPKGPATH"); dir != "" { pkg = dir + "/" + pkg } p.PackagePath = pkg - p.writeOutput(input) + p.writeOutput(f, input) + + p.Record(f) } p.writeDefs() + if nerrors > 0 { + os.Exit(2) + } +} + +// Record what needs to be recorded about f. +func (p *Package) Record(f *File) { + if p.PackageName == "" { + p.PackageName = f.Package + } else if p.PackageName != f.Package { + error(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package) + } + + if p.Name == nil { + p.Name = f.Name + } else { + for k, v := range f.Name { + if p.Name[k] == nil { + p.Name[k] = v + } else if !reflect.DeepEqual(p.Name[k], v) { + error(token.NoPos, "inconsistent definitions for C.%s", k) + } + } + } + + p.ExpFunc = append(p.ExpFunc, f.ExpFunc...) + p.Decl = append(p.Decl, f.AST.Decls...) } diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 7cdf483f0..c3f9ae60b 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -5,6 +5,9 @@ package main import ( + "bytes" + "debug/elf" + "debug/macho" "fmt" "go/ast" "go/printer" @@ -13,30 +16,9 @@ import ( "strings" ) -func creat(name string) *os.File { - f, err := os.Open(name, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0666) - if err != nil { - fatal("%s", err) - } - return f -} - -func slashToUnderscore(c int) int { - if c == '/' { - c = '_' - } - return c -} - // writeDefs creates output files to be compiled by 6g, 6c, and gcc. // (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.) -func (p *Prog) writeDefs() { - pkgroot := os.Getenv("GOROOT") + "/pkg/" + os.Getenv("GOOS") + "_" + os.Getenv("GOARCH") - path := p.PackagePath - if !strings.HasPrefix(path, "/") { - path = pkgroot + "/" + path - } - +func (p *Package) writeDefs() { // The path for the shared object is slash-free so that ELF loaders // will treat it as a relative path. We rewrite slashes to underscores. sopath := "cgo_" + strings.Map(slashToUnderscore, p.PackagePath) @@ -48,229 +30,305 @@ func (p *Prog) writeDefs() { fgo2 := creat("_cgo_gotypes.go") fc := creat("_cgo_defun.c") + fm := creat("_cgo_main.c") + + // Write C main file for using gcc to resolve imports. + fmt.Fprintf(fm, "int main() { return 0; }\n") + fmt.Fprintf(fm, "int crosscall2;\n\n") // Write second Go output: definitions of _C_xxx. // In a separate file so that the import of "unsafe" does not // pollute the original file. - fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n") - fmt.Fprintf(fgo2, "package %s\n\n", p.Package) + fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n") + fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName) fmt.Fprintf(fgo2, "import \"unsafe\"\n\n") + fmt.Fprintf(fgo2, "import \"os\"\n\n") + fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n") fmt.Fprintf(fgo2, "type _ unsafe.Pointer\n\n") + fmt.Fprintf(fgo2, "func _Cerrno(dst *os.Error, x int) { *dst = os.Errno(x) }\n") - for name, def := range p.Typedef { + for name, def := range typedef { fmt.Fprintf(fgo2, "type %s ", name) - printer.Fprint(fgo2, def) + printer.Fprint(fgo2, fset, def) fmt.Fprintf(fgo2, "\n") } - fmt.Fprintf(fgo2, "type _C_void [0]byte\n") + fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n") + + fmt.Fprintf(fc, cProlog) + + var cVars []string + for _, n := range p.Name { + if n.Kind != "var" { + continue + } + cVars = append(cVars, n.C) - fmt.Fprintf(fc, cProlog, soprefix, soprefix, soprefix, soprefix, soprefix) + fmt.Fprintf(fm, "extern char %s[];\n", n.C) + fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C) + + fmt.Fprintf(fc, "extern byte *%s;\n", n.C) + fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C) + fmt.Fprintf(fc, "\n") - for name, def := range p.Vardef { - fmt.Fprintf(fc, "#pragma dynimport ·_C_%s %s \"%s%s.so\"\n", name, name, soprefix, sopath) - fmt.Fprintf(fgo2, "var _C_%s ", name) - printer.Fprint(fgo2, &ast.StarExpr{X: def.Go}) + fmt.Fprintf(fgo2, "var %s ", n.Mangle) + printer.Fprint(fgo2, fset, &ast.StarExpr{X: n.Type.Go}) fmt.Fprintf(fgo2, "\n") } fmt.Fprintf(fc, "\n") - for name, value := range p.Constdef { - fmt.Fprintf(fgo2, "const %s = %s\n", name, value) - } - - for name, value := range p.Enumdef { - fmt.Fprintf(fgo2, "const %s = %d\n", name, value) + for _, n := range p.Name { + if n.Const != "" { + fmt.Fprintf(fgo2, "const _Cconst_%s = %s\n", n.Go, n.Const) + } } fmt.Fprintf(fgo2, "\n") - for name, def := range p.Funcdef { - // Go func declaration. - d := &ast.FuncDecl{ - Name: ast.NewIdent("_C_" + name), - Type: def.Go, + for _, n := range p.Name { + if n.FuncType != nil { + p.writeDefsFunc(fc, fgo2, n, soprefix, sopath) } - printer.Fprint(fgo2, d) - fmt.Fprintf(fgo2, "\n") + } - if name == "CString" || name == "GoString" { - // The builtins are already defined in the C prolog. - continue + p.writeExports(fgo2, fc, fm) + + fgo2.Close() + fc.Close() +} + +func dynimport(obj string) (syms, imports []string) { + var f interface { + ImportedLibraries() ([]string, os.Error) + ImportedSymbols() ([]string, os.Error) + } + var isMacho bool + var err1, err2 os.Error + if f, err1 = elf.Open(obj); err1 != nil { + if f, err2 = macho.Open(obj); err2 != nil { + fatal("cannot parse %s as ELF (%v) or Mach-O (%v)", obj, err1, err2) } + isMacho = true + } - // Construct a gcc struct matching the 6c argument frame. - // Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes. - // These assumptions are checked by the gccProlog. - // Also assumes that 6c convention is to word-align the - // input and output parameters. - structType := "struct {\n" - off := int64(0) - npad := 0 - for i, t := range def.Params { - if off%t.Align != 0 { - pad := t.Align - off%t.Align - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ + var err os.Error + syms, err = f.ImportedSymbols() + if err != nil { + fatal("cannot load dynamic symbols: %v", err) + } + if isMacho { + // remove leading _ that OS X insists on + for i, s := range syms { + if len(s) >= 2 && s[0] == '_' { + syms[i] = s[1:] } - structType += fmt.Sprintf("\t\t%s p%d;\n", t.C, i) - off += t.Size } - if off%p.PtrSize != 0 { - pad := p.PtrSize - off%p.PtrSize - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + } + + imports, err = f.ImportedLibraries() + if err != nil { + fatal("cannot load dynamic imports: %v", err) + } + + return +} + +// Construct a gcc struct matching the 6c argument frame. +// Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes. +// These assumptions are checked by the gccProlog. +// Also assumes that 6c convention is to word-align the +// input and output parameters. +func (p *Package) structType(n *Name) (string, int64) { + var buf bytes.Buffer + fmt.Fprint(&buf, "struct {\n") + off := int64(0) + for i, t := range n.FuncType.Params { + if off%t.Align != 0 { + pad := t.Align - off%t.Align + fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) off += pad - npad++ - } - if t := def.Result; t != nil { - if off%t.Align != 0 { - pad := t.Align - off%t.Align - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } - structType += fmt.Sprintf("\t\t%s r;\n", t.C) - off += t.Size } - if off%p.PtrSize != 0 { - pad := p.PtrSize - off%p.PtrSize - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + fmt.Fprintf(&buf, "\t\t%s p%d;\n", t.C, i) + off += t.Size + } + if off%p.PtrSize != 0 { + pad := p.PtrSize - off%p.PtrSize + fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) + off += pad + } + if t := n.FuncType.Result; t != nil { + if off%t.Align != 0 { + pad := t.Align - off%t.Align + fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) off += pad - npad++ } - if len(def.Params) == 0 && def.Result == nil { - structType += "\t\tchar unused;\n" // avoid empty struct - off++ + qual := "" + if t.C[len(t.C)-1] == '*' { + qual = "const " } - structType += "\t}" - argSize := off + fmt.Fprintf(&buf, "\t\t%s%s r;\n", qual, t.C) + off += t.Size + } + if off%p.PtrSize != 0 { + pad := p.PtrSize - off%p.PtrSize + fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) + off += pad + } + if n.AddError { + fmt.Fprint(&buf, "\t\tvoid *e[2]; /* os.Error */\n") + off += 2 * p.PtrSize + } + if off == 0 { + fmt.Fprintf(&buf, "\t\tchar unused;\n") // avoid empty struct + off++ + } + fmt.Fprintf(&buf, "\t}") + return buf.String(), off +} - // C wrapper calls into gcc, passing a pointer to the argument frame. - // Also emit #pragma to get a pointer to the gcc wrapper. - fmt.Fprintf(fc, "#pragma dynimport _cgo_%s _cgo_%s \"%s%s.so\"\n", name, name, soprefix, sopath) - fmt.Fprintf(fc, "void (*_cgo_%s)(void*);\n", name) - fmt.Fprintf(fc, "\n") - fmt.Fprintf(fc, "void\n") - fmt.Fprintf(fc, "·_C_%s(struct{uint8 x[%d];}p)\n", name, argSize) - fmt.Fprintf(fc, "{\n") - fmt.Fprintf(fc, "\tcgocall(_cgo_%s, &p);\n", name) - fmt.Fprintf(fc, "}\n") - fmt.Fprintf(fc, "\n") +func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name, soprefix, sopath string) { + name := n.Go + gtype := n.FuncType.Go + if n.AddError { + // Add "os.Error" to return type list. + // Type list is known to be 0 or 1 element - it's a C function. + err := &ast.Field{Type: ast.NewIdent("os.Error")} + l := gtype.Results.List + if len(l) == 0 { + l = []*ast.Field{err} + } else { + l = []*ast.Field{l[0], err} + } + t := new(ast.FuncType) + *t = *gtype + t.Results = &ast.FieldList{List: l} + gtype = t } - p.writeExports(fgo2, fc) + // Go func declaration. + d := &ast.FuncDecl{ + Name: ast.NewIdent(n.Mangle), + Type: gtype, + } + printer.Fprint(fgo2, fset, d) + fmt.Fprintf(fgo2, "\n") - fgo2.Close() - fc.Close() + if name == "CString" || name == "GoString" || name == "GoStringN" { + // The builtins are already defined in the C prolog. + return + } + + var argSize int64 + _, argSize = p.structType(n) + + // C wrapper calls into gcc, passing a pointer to the argument frame. + fmt.Fprintf(fc, "void _cgo%s%s(void*);\n", cPrefix, n.Mangle) + fmt.Fprintf(fc, "\n") + fmt.Fprintf(fc, "void\n") + fmt.Fprintf(fc, "·%s(struct{uint8 x[%d];}p)\n", n.Mangle, argSize) + fmt.Fprintf(fc, "{\n") + fmt.Fprintf(fc, "\truntime·cgocall(_cgo%s%s, &p);\n", cPrefix, n.Mangle) + if n.AddError { + // gcc leaves errno in first word of interface at end of p. + // check whether it is zero; if so, turn interface into nil. + // if not, turn interface into errno. + // Go init function initializes ·_Cerrno with an os.Errno + // for us to copy. + fmt.Fprintln(fc, ` { + int32 e; + void **v; + v = (void**)(&p+1) - 2; /* v = final two void* of p */ + e = *(int32*)v; + v[0] = (void*)0xdeadbeef; + v[1] = (void*)0xdeadbeef; + if(e == 0) { + /* nil interface */ + v[0] = 0; + v[1] = 0; + } else { + ·_Cerrno(v, e); /* fill in v as os.Error for errno e */ + } + }`) + } + fmt.Fprintf(fc, "}\n") + fmt.Fprintf(fc, "\n") } // writeOutput creates stubs for a specific source file to be compiled by 6g // (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.) -func (p *Prog) writeOutput(srcfile string) { +func (p *Package) writeOutput(f *File, srcfile string) { base := srcfile if strings.HasSuffix(base, ".go") { base = base[0 : len(base)-3] } + base = strings.Map(slashToUnderscore, base) fgo1 := creat(base + ".cgo1.go") fgcc := creat(base + ".cgo2.c") + p.GoFiles = append(p.GoFiles, base+".cgo1.go") + p.GccFiles = append(p.GccFiles, base+".cgo2.c") + // Write Go output: Go input with rewrites of C.xxx to _C_xxx. - fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n") + fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n\n") fmt.Fprintf(fgo1, "//line %s:1\n", srcfile) - printer.Fprint(fgo1, p.AST) + printer.Fprint(fgo1, fset, f.AST) // While we process the vars and funcs, also write 6c and gcc output. // Gcc output starts with the preamble. - fmt.Fprintf(fgcc, "%s\n", p.Preamble) + fmt.Fprintf(fgcc, "%s\n", f.Preamble) fmt.Fprintf(fgcc, "%s\n", gccProlog) - for name, def := range p.Funcdef { - _, ok := p.OutDefs[name] - if name == "CString" || name == "GoString" || ok { - // The builtins are already defined in the C prolog, and we don't - // want to duplicate function definitions we've already done. - continue - } - p.OutDefs[name] = true - - // Construct a gcc struct matching the 6c argument frame. - // Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes. - // These assumptions are checked by the gccProlog. - // Also assumes that 6c convention is to word-align the - // input and output parameters. - structType := "struct {\n" - off := int64(0) - npad := 0 - for i, t := range def.Params { - if off%t.Align != 0 { - pad := t.Align - off%t.Align - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } - structType += fmt.Sprintf("\t\t%s p%d;\n", t.C, i) - off += t.Size + for _, n := range f.Name { + if n.FuncType != nil { + p.writeOutputFunc(fgcc, n) } - if off%p.PtrSize != 0 { - pad := p.PtrSize - off%p.PtrSize - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } - if t := def.Result; t != nil { - if off%t.Align != 0 { - pad := t.Align - off%t.Align - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } - structType += fmt.Sprintf("\t\t%s r;\n", t.C) - off += t.Size - } - if off%p.PtrSize != 0 { - pad := p.PtrSize - off%p.PtrSize - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } - if len(def.Params) == 0 && def.Result == nil { - structType += "\t\tchar unused;\n" // avoid empty struct - off++ - } - structType += "\t}" - - // Gcc wrapper unpacks the C argument struct - // and calls the actual C function. - fmt.Fprintf(fgcc, "void\n") - fmt.Fprintf(fgcc, "_cgo_%s(void *v)\n", name) - fmt.Fprintf(fgcc, "{\n") - fmt.Fprintf(fgcc, "\t%s *a = v;\n", structType) - fmt.Fprintf(fgcc, "\t") - if def.Result != nil { - fmt.Fprintf(fgcc, "a->r = ") - } - fmt.Fprintf(fgcc, "%s(", name) - for i := range def.Params { - if i > 0 { - fmt.Fprintf(fgcc, ", ") - } - fmt.Fprintf(fgcc, "a->p%d", i) - } - fmt.Fprintf(fgcc, ");\n") - fmt.Fprintf(fgcc, "}\n") - fmt.Fprintf(fgcc, "\n") } fgo1.Close() fgcc.Close() } -// Write out the various stubs we need to support functions exported -// from Go so that they are callable from C. -func (p *Prog) writeExports(fgo2, fc *os.File) { - if len(p.ExpFuncs) == 0 { +func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { + name := n.Mangle + if name == "_Cfunc_CString" || name == "_Cfunc_GoString" || name == "_Cfunc_GoStringN" || p.Written[name] { + // The builtins are already defined in the C prolog, and we don't + // want to duplicate function definitions we've already done. return } + p.Written[name] = true + + ctype, _ := p.structType(n) + + // Gcc wrapper unpacks the C argument struct + // and calls the actual C function. + fmt.Fprintf(fgcc, "void\n") + fmt.Fprintf(fgcc, "_cgo%s%s(void *v)\n", cPrefix, n.Mangle) + fmt.Fprintf(fgcc, "{\n") + if n.AddError { + fmt.Fprintf(fgcc, "\tint e;\n") // assuming 32 bit (see comment above structType) + fmt.Fprintf(fgcc, "\terrno = 0;\n") + } + fmt.Fprintf(fgcc, "\t%s *a = v;\n", ctype) + fmt.Fprintf(fgcc, "\t") + if n.FuncType.Result != nil { + fmt.Fprintf(fgcc, "a->r = ") + } + fmt.Fprintf(fgcc, "%s(", n.C) + for i := range n.FuncType.Params { + if i > 0 { + fmt.Fprintf(fgcc, ", ") + } + fmt.Fprintf(fgcc, "a->p%d", i) + } + fmt.Fprintf(fgcc, ");\n") + if n.AddError { + fmt.Fprintf(fgcc, "\t*(int*)(a->e) = errno;\n") + } + fmt.Fprintf(fgcc, "}\n") + fmt.Fprintf(fgcc, "\n") +} +// Write out the various stubs we need to support functions exported +// from Go so that they are callable from C. +func (p *Package) writeExports(fgo2, fc, fm *os.File) { fgcc := creat("_cgo_export.c") fgcch := creat("_cgo_export.h") @@ -280,17 +338,17 @@ func (p *Prog) writeExports(fgo2, fc *os.File) { fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n") - for _, exp := range p.ExpFuncs { + for _, exp := range p.ExpFunc { fn := exp.Func // Construct a gcc struct matching the 6c argument and // result frame. - structType := "struct {\n" + ctype := "struct {\n" off := int64(0) npad := 0 if fn.Recv != nil { t := p.cgoType(fn.Recv.List[0].Type) - structType += fmt.Sprintf("\t\t%s recv;\n", t.C) + ctype += fmt.Sprintf("\t\t%s recv;\n", t.C) off += t.Size } fntype := fn.Type @@ -299,16 +357,16 @@ func (p *Prog) writeExports(fgo2, fc *os.File) { t := p.cgoType(atype) if off%t.Align != 0 { pad := t.Align - off%t.Align - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) off += pad npad++ } - structType += fmt.Sprintf("\t\t%s p%d;\n", t.C, i) + ctype += fmt.Sprintf("\t\t%s p%d;\n", t.C, i) off += t.Size }) if off%p.PtrSize != 0 { pad := p.PtrSize - off%p.PtrSize - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) off += pad npad++ } @@ -317,24 +375,24 @@ func (p *Prog) writeExports(fgo2, fc *os.File) { t := p.cgoType(atype) if off%t.Align != 0 { pad := t.Align - off%t.Align - structType += fmt.Sprintf("\t\tchar __pad%d[%d]\n", npad, pad) + ctype += fmt.Sprintf("\t\tchar __pad%d[%d]\n", npad, pad) off += pad npad++ } - structType += fmt.Sprintf("\t\t%s r%d;\n", t.C, i) + ctype += fmt.Sprintf("\t\t%s r%d;\n", t.C, i) off += t.Size }) if off%p.PtrSize != 0 { pad := p.PtrSize - off%p.PtrSize - structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) off += pad npad++ } - if structType == "struct {\n" { - structType += "\t\tchar unused;\n" // avoid empty struct + if ctype == "struct {\n" { + ctype += "\t\tchar unused;\n" // avoid empty struct off++ } - structType += "\t}" + ctype += "\t}" // Get the return type of the wrapper function // compiled by gcc. @@ -370,10 +428,10 @@ func (p *Prog) writeExports(fgo2, fc *os.File) { s += ")" fmt.Fprintf(fgcch, "\nextern %s;\n", s) - fmt.Fprintf(fgcc, "extern _cgoexp_%s(void *, int);\n", exp.ExpName) + fmt.Fprintf(fgcc, "extern _cgoexp%s_%s(void *, int);\n", cPrefix, exp.ExpName) fmt.Fprintf(fgcc, "\n%s\n", s) fmt.Fprintf(fgcc, "{\n") - fmt.Fprintf(fgcc, "\t%s a;\n", structType) + fmt.Fprintf(fgcc, "\t%s a;\n", ctype) if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) { fmt.Fprintf(fgcc, "\t%s r;\n", gccResult) } @@ -384,7 +442,7 @@ func (p *Prog) writeExports(fgo2, fc *os.File) { func(i int, atype ast.Expr) { fmt.Fprintf(fgcc, "\ta.p%d = p%d;\n", i, i) }) - fmt.Fprintf(fgcc, "\tcrosscall2(_cgoexp_%s, &a, (int) sizeof a);\n", exp.ExpName) + fmt.Fprintf(fgcc, "\tcrosscall2(_cgoexp%s_%s, &a, (int) sizeof a);\n", cPrefix, exp.ExpName) if gccResult != "void" { if len(fntype.Results.List) == 1 && len(fntype.Results.List[0].Names) <= 1 { fmt.Fprintf(fgcc, "\treturn a.r0;\n") @@ -399,27 +457,28 @@ func (p *Prog) writeExports(fgo2, fc *os.File) { fmt.Fprintf(fgcc, "}\n") // Build the wrapper function compiled by 6c/8c - goname := exp.Func.Name.Name() + goname := exp.Func.Name.Name if fn.Recv != nil { - goname = "_cgoexpwrap_" + fn.Recv.List[0].Names[0].Name() + "_" + goname + goname = "_cgoexpwrap" + cPrefix + "_" + fn.Recv.List[0].Names[0].Name + "_" + goname } - fmt.Fprintf(fc, "#pragma dynexport _cgoexp_%s _cgoexp_%s\n", exp.ExpName, exp.ExpName) fmt.Fprintf(fc, "extern void ·%s();\n", goname) fmt.Fprintf(fc, "\nvoid\n") - fmt.Fprintf(fc, "_cgoexp_%s(void *a, int32 n)\n", exp.ExpName) + fmt.Fprintf(fc, "_cgoexp%s_%s(void *a, int32 n)\n", cPrefix, exp.ExpName) fmt.Fprintf(fc, "{\n") - fmt.Fprintf(fc, "\tcgocallback(·%s, a, n);\n", goname) + fmt.Fprintf(fc, "\truntime·cgocallback(·%s, a, n);\n", goname) fmt.Fprintf(fc, "}\n") + fmt.Fprintf(fm, "int _cgoexp%s_%s;\n", cPrefix, exp.ExpName) + // Calling a function with a receiver from C requires // a Go wrapper function. if fn.Recv != nil { fmt.Fprintf(fgo2, "func %s(recv ", goname) - printer.Fprint(fgo2, fn.Recv.List[0].Type) + printer.Fprint(fgo2, fset, fn.Recv.List[0].Type) forFieldList(fntype.Params, func(i int, atype ast.Expr) { - fmt.Fprintf(fgo2, ", p%d", i) - printer.Fprint(fgo2, atype) + fmt.Fprintf(fgo2, ", p%d ", i) + printer.Fprint(fgo2, fset, atype) }) fmt.Fprintf(fgo2, ")") if gccResult != "void" { @@ -429,7 +488,7 @@ func (p *Prog) writeExports(fgo2, fc *os.File) { if i > 0 { fmt.Fprint(fgo2, ", ") } - printer.Fprint(fgo2, atype) + printer.Fprint(fgo2, fset, atype) }) fmt.Fprint(fgo2, ")") } @@ -493,7 +552,7 @@ var goTypes = map[string]*Type{ } // Map an ast type to a Type. -func (p *Prog) cgoType(e ast.Expr) *Type { +func (p *Package) cgoType(e ast.Expr) *Type { switch t := e.(type) { case *ast.StarExpr: x := p.cgoType(t.X) @@ -515,7 +574,7 @@ func (p *Prog) cgoType(e ast.Expr) *Type { case *ast.Ident: // Look up the type in the top level declarations. // TODO: Handle types defined within a function. - for _, d := range p.AST.Decls { + for _, d := range p.Decl { gd, ok := d.(*ast.GenDecl) if !ok || gd.Tok != token.TYPE { continue @@ -525,30 +584,35 @@ func (p *Prog) cgoType(e ast.Expr) *Type { if !ok { continue } - if ts.Name.Name() == t.Name() { + if ts.Name.Name == t.Name { return p.cgoType(ts.Type) } } } - for name, def := range p.Typedef { - if name == t.Name() { + for name, def := range typedef { + if name == t.Name { return p.cgoType(def) } } - if t.Name() == "uintptr" { + if t.Name == "uintptr" { return &Type{Size: p.PtrSize, Align: p.PtrSize, C: "uintptr"} } - if t.Name() == "string" { + if t.Name == "string" { return &Type{Size: p.PtrSize + 4, Align: p.PtrSize, C: "GoString"} } - if r, ok := goTypes[t.Name()]; ok { + if r, ok := goTypes[t.Name]; ok { if r.Align > p.PtrSize { r.Align = p.PtrSize } return r } + case *ast.SelectorExpr: + id, ok := t.X.(*ast.Ident) + if ok && id.Name == "unsafe" && t.Sel.Name == "Pointer" { + return &Type{Size: p.PtrSize, Align: p.PtrSize, C: "void*"} + } } - error(e.Pos(), "unrecognized Go type %v", e) + error(e.Pos(), "unrecognized Go type %T", e) return &Type{Size: 4, Align: 4, C: "int"} } @@ -568,11 +632,15 @@ typedef long long __cgo_long_long; __cgo_size_assert(__cgo_long_long, 8) __cgo_size_assert(float, 4) __cgo_size_assert(double, 8) + +#include <errno.h> +#include <string.h> ` const builtinProlog = ` typedef struct { char *p; int n; } _GoString_; _GoString_ GoString(char *p); +_GoString_ GoStringN(char *p, int l); char *CString(_GoString_); ` @@ -580,24 +648,27 @@ const cProlog = ` #include "runtime.h" #include "cgocall.h" -#pragma dynimport initcgo initcgo "%slibcgo.so" -#pragma dynimport libcgo_thread_start libcgo_thread_start "%slibcgo.so" -#pragma dynimport libcgo_set_scheduler libcgo_set_scheduler "%slibcgo.so" -#pragma dynimport _cgo_malloc _cgo_malloc "%slibcgo.so" -#pragma dynimport _cgo_free _cgo_free "%slibcgo.so" +void ·_Cerrno(void*, int32); + +void +·_Cfunc_GoString(int8 *p, String s) +{ + s = runtime·gostring((byte*)p); + FLUSH(&s); +} void -·_C_GoString(int8 *p, String s) +·_Cfunc_GoStringN(int8 *p, int32 l, String s) { - s = gostring((byte*)p); + s = runtime·gostringn((byte*)p, l); FLUSH(&s); } void -·_C_CString(String s, int8 *p) +·_Cfunc_CString(String s, int8 *p) { - p = cmalloc(s.len+1); - mcpy((byte*)p, s.str, s.len); + p = runtime·cmalloc(s.len+1); + runtime·mcpy((byte*)p, s.str, s.len); p[s.len] = 0; FLUSH(&p); } @@ -610,6 +681,7 @@ typedef unsigned char uchar; typedef unsigned short ushort; typedef long long int64; typedef unsigned long long uint64; +typedef __SIZE_TYPE__ uintptr; typedef struct { char *p; int n; } GoString; typedef void *GoMap; diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go index 95067039c..a6f509dc4 100644 --- a/src/cmd/cgo/util.go +++ b/src/cmd/cgo/util.go @@ -12,16 +12,6 @@ import ( "os" ) -// A ByteReaderAt implements io.ReadAt using a slice of bytes. -type ByteReaderAt []byte - -func (r ByteReaderAt) ReadAt(p []byte, off int64) (n int, err os.Error) { - if off >= int64(len(r)) || off < 0 { - return 0, os.EOF - } - return copy(p, r[off:]), nil -} - // run runs the command argv, feeding in stdin on standard input. // It returns the output to standard output and standard error. // ok indicates whether the command exited successfully. @@ -55,9 +45,8 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { w0.Close() c <- true }() - var xstdout []byte // TODO(rsc): delete after 6g can take address of out parameter go func() { - xstdout, _ = ioutil.ReadAll(r1) + stdout, _ = ioutil.ReadAll(r1) r1.Close() c <- true }() @@ -65,7 +54,6 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { r2.Close() <-c <-c - stdout = xstdout w, err := os.Wait(pid, 0) if err != nil { @@ -77,18 +65,45 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { // Die with an error message. func fatal(msg string, args ...interface{}) { - fmt.Fprintf(os.Stderr, msg+"\n", args) + fmt.Fprintf(os.Stderr, msg+"\n", args...) os.Exit(2) } var nerrors int -var noPos token.Position -func error(pos token.Position, msg string, args ...interface{}) { +func error(pos token.Pos, msg string, args ...interface{}) { nerrors++ if pos.IsValid() { - fmt.Fprintf(os.Stderr, "%s: ", pos) + fmt.Fprintf(os.Stderr, "%s: ", fset.Position(pos).String()) } - fmt.Fprintf(os.Stderr, msg, args) + fmt.Fprintf(os.Stderr, msg, args...) fmt.Fprintf(os.Stderr, "\n") } + +// isName returns true if s is a valid C identifier +func isName(s string) bool { + for i, v := range s { + if v != '_' && (v < 'A' || v > 'Z') && (v < 'a' || v > 'z') && (v < '0' || v > '9') { + return false + } + if i == 0 && '0' <= v && v <= '9' { + return false + } + } + return s != "" +} + +func creat(name string) *os.File { + f, err := os.Open(name, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0666) + if err != nil { + fatal("%s", err) + } + return f +} + +func slashToUnderscore(c int) int { + if c == '/' { + c = '_' + } + return c +} diff --git a/src/cmd/clean.bash b/src/cmd/clean.bash index 9317b8ae5..6349919a8 100644 --- a/src/cmd/clean.bash +++ b/src/cmd/clean.bash @@ -3,11 +3,9 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -GOBIN="${GOBIN:-$HOME/bin}" - for i in cc 6l 6a 6c 8l 8a 8c 8g 5l 5a 5c 5g gc 6g gopack nm cgo cov ebnflint godefs godoc gofmt goinstall gotest goyacc hgpatch prof do cd $i - "$GOBIN"/gomake clean + gomake clean cd .. done diff --git a/src/cmd/cov/Makefile b/src/cmd/cov/Makefile index 58cb2302c..fdeb14636 100644 --- a/src/cmd/cov/Makefile +++ b/src/cmd/cov/Makefile @@ -2,7 +2,8 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) # The directory is cov because the source is portable and general. # We call the binary 6cov to avoid confusion and because this binary @@ -16,15 +17,22 @@ OFILES=\ HFILES=\ tree.h\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lmach -lbio -l9 +LIB=\ + ../../../lib/libmach.a\ -clean: - rm -f *.$O $(TARG) +NOINSTALL=1 +include ../../Make.ccmd -install: install-$(shell uname | tr A-Z a-z) +ifeq ($(GOOS),windows) +NAME=windows +else +NAME=$(shell uname | tr A-Z a-z) +endif + +install: install-$(NAME) install-linux: install-default install-freebsd: install-default +install-windows: install-default # on Darwin, have to install and setgid; see $GOROOT/src/sudo.bash install-darwin: $(TARG) @@ -32,5 +40,3 @@ install-darwin: $(TARG) install-default: $(TARG) cp $(TARG) "$(GOBIN)"/$(TARG) - -$(OFILES): $(HFILES) diff --git a/src/cmd/cov/main.c b/src/cmd/cov/main.c index 1b3138a7f..5ff22c00a 100644 --- a/src/cmd/cov/main.c +++ b/src/cmd/cov/main.c @@ -81,7 +81,7 @@ ran(uvlong pc, uvlong epc) key.epc = pc+1; r = treeget(&breakpoints, &key); if(r == nil) - sysfatal("unchecked breakpoint at %#lux+%d", pc, (int)(epc-pc)); + sysfatal("unchecked breakpoint at %#llux+%d", pc, (int)(epc-pc)); // Might be that the tail of the sequence // was run already, so r->epc is before the end. diff --git a/src/cmd/ebnflint/Makefile b/src/cmd/ebnflint/Makefile index 8cb9fd821..8f030aaef 100644 --- a/src/cmd/ebnflint/Makefile +++ b/src/cmd/ebnflint/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=ebnflint GOFILES=\ @@ -11,5 +11,5 @@ GOFILES=\ include ../../Make.cmd test: $(TARG) - $(QUOTED_GOBIN)/$(TARG) -start="SourceFile" "$(GOROOT)"/doc/go_spec.html + $(TARG) -start="SourceFile" "$(GOROOT)"/doc/go_spec.html diff --git a/src/cmd/ebnflint/ebnflint.go b/src/cmd/ebnflint/ebnflint.go index 3dfa71f07..10cb5b387 100644 --- a/src/cmd/ebnflint/ebnflint.go +++ b/src/cmd/ebnflint/ebnflint.go @@ -10,12 +10,14 @@ import ( "flag" "fmt" "go/scanner" + "go/token" "io/ioutil" "os" "path" ) +var fset = token.NewFileSet() var start = flag.String("start", "Start", "name of start production") @@ -92,12 +94,12 @@ func main() { src = extractEBNF(src) } - grammar, err := ebnf.Parse(filename, src) + grammar, err := ebnf.Parse(fset, filename, src) if err != nil { scanner.PrintError(os.Stderr, err) } - if err = ebnf.Verify(grammar, *start); err != nil { + if err = ebnf.Verify(fset, grammar, *start); err != nil { scanner.PrintError(os.Stderr, err) } } diff --git a/src/cmd/gc/Makefile b/src/cmd/gc/Makefile index 46dc6dfbc..dbfd86474 100644 --- a/src/cmd/gc/Makefile +++ b/src/cmd/gc/Makefile @@ -2,10 +2,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) -LIB=\ - gc.a$O\ +LIB=gc.a HFILES=\ go.h\ @@ -43,16 +43,10 @@ OFILES=\ walk.$O\ y1.tab.$O\ -$(LIB): $(OFILES) - ar rsc $(LIB) $(OFILES) +NOINSTALL=1 +include ../../Make.clib -$(OFILES): $(HFILES) - -y.tab.h: $(YFILES) - LANG=C LANGUAGE="en_US.UTF8" bison -v -y $(YFLAGS) $(YFILES) - -y.tab.c: y.tab.h - test -f y.tab.c && touch y.tab.c +install: $(LIB) y1.tab.c: y.tab.c # make yystate global, yytname mutable cat y.tab.c | sed '/ int yystate;/d; s/int yychar;/int yychar, yystate;/; s/static const char \*const yytname/const char *yytname/' >y1.tab.c @@ -70,7 +64,4 @@ subr.$O: opnames.h opnames.h: mkopnames go.h ./mkopnames go.h >opnames.h -clean: - rm -f *.[568o] enam.c [568].out a.out y.tab.h y.tab.c y1.tab.c y.output yerr.h $(LIB) mkbuiltin1 builtin.c _builtin.c opnames.h - -install: $(LIB) +CLEANFILES+=*.[568] [568].out y1.tab.c yerr.h mkbuiltin1 builtin.c _builtin.c opnames.h diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c index 1b9112d69..a3785e871 100644 --- a/src/cmd/gc/align.c +++ b/src/cmd/gc/align.c @@ -7,8 +7,8 @@ /* * machine size and rounding * alignment is dictated around - * the size of a pointer, set in belexinit - * (see ../6g/align.c). + * the size of a pointer, set in betypeinit + * (see ../6g/galign.c). */ static int defercalc; @@ -16,15 +16,9 @@ static int defercalc; uint32 rnd(uint32 o, uint32 r) { - if(maxround == 0) + if(r < 1 || r > 8 || (r&(r-1)) != 0) fatal("rnd"); - - if(r > maxround) - r = maxround; - if(r != 0) - while(o%r != 0) - o++; - return o; + return (o+r-1)&~(r-1); } static void @@ -43,29 +37,24 @@ offmod(Type *t) } static uint32 -arrayelemwidth(Type *t) -{ - - while(t->etype == TARRAY && t->bound >= 0) - t = t->type; - return t->width; -} - -static uint32 widstruct(Type *t, uint32 o, int flag) { Type *f; - int32 w, m; - + int32 w, maxalign; + + maxalign = flag; + if(maxalign < 1) + maxalign = 1; for(f=t->type; f!=T; f=f->down) { if(f->etype != TFIELD) fatal("widstruct: not TFIELD: %lT", f); dowidth(f->type); + if(f->type->align > maxalign) + maxalign = f->type->align; if(f->type->width < 0) fatal("invalid width %lld", f->type->width); w = f->type->width; - m = arrayelemwidth(f->type); - o = rnd(o, m); + o = rnd(o, f->type->align); f->width = o; // really offset for TFIELD if(f->nname != N) { // this same stackparam logic is in addrescapes @@ -82,7 +71,8 @@ widstruct(Type *t, uint32 o, int flag) } // final width is rounded if(flag) - o = rnd(o, maxround); + o = rnd(o, maxalign); + t->align = maxalign; // type width only includes back to first field's offset if(t->type == T) @@ -100,7 +90,7 @@ dowidth(Type *t) int lno; Type *t1; - if(maxround == 0 || widthptr == 0) + if(widthptr == 0) fatal("dowidth without betypeinit"); if(t == T) @@ -124,6 +114,7 @@ dowidth(Type *t) lno = lineno; lineno = t->lineno; t->width = -2; + t->align = 0; et = t->etype; switch(et) { @@ -166,9 +157,11 @@ dowidth(Type *t) case TFLOAT64: case TCOMPLEX64: w = 8; + t->align = widthptr; break; case TCOMPLEX128: w = 16; + t->align = widthptr; break; case TPTR32: w = 4; @@ -180,6 +173,7 @@ dowidth(Type *t) break; case TINTER: // implemented as 2 pointers w = 2*widthptr; + t->align = widthptr; offmod(t); break; case TCHAN: // implemented as pointer @@ -197,6 +191,7 @@ dowidth(Type *t) dowidth(t->type); // just in case if(t1->type->width >= (1<<16)) yyerror("channel element type too large (>64kB)"); + t->width = 1; break; case TMAP: // implemented as pointer w = widthptr; @@ -217,6 +212,7 @@ dowidth(Type *t) if(sizeof_String == 0) fatal("early dowidth string"); w = sizeof_String; + t->align = widthptr; break; case TARRAY: if(t->type == T) @@ -235,11 +231,13 @@ dowidth(Type *t) yyerror("type %lT larger than address space", t); w = t->bound * t->type->width; if(w == 0) - w = maxround; + w = 1; + t->align = t->type->align; } else if(t->bound == -1) { w = sizeof_Array; checkwidth(t->type); + t->align = widthptr; } else if(t->bound == -100) yyerror("use of [...] array outside of array literal"); @@ -252,7 +250,9 @@ dowidth(Type *t) fatal("dowidth fn struct %T", t); w = widstruct(t, 0, 1); if(w == 0) - w = maxround; + w = 1; + //if(t->align < widthptr) + // warn("align %d: %T\n", t->align, t); break; case TFUNC: @@ -271,16 +271,24 @@ dowidth(Type *t) // compute their widths as side-effect. t1 = t->type; w = widstruct(*getthis(t1), 0, 0); - w = widstruct(*getinarg(t1), w, 1); - w = widstruct(*getoutarg(t1), w, 1); + w = widstruct(*getinarg(t1), w, widthptr); + w = widstruct(*getoutarg(t1), w, widthptr); t1->argwid = w; + if(w%widthptr) + warn("bad type %T %d\n", t1, w); + t->align = 1; break; } // catch all for error cases; avoid divide by zero later if(w == 0) - w = maxround; + w = 1; t->width = w; + if(t->align == 0) { + if(w > 8 || (w&(w-1)) != 0) + fatal("invalid alignment for %T", t); + t->align = w; + } lineno = lno; if(defercalc == 1) @@ -351,8 +359,8 @@ void defercheckwidth(void) { // we get out of sync on syntax errors, so don't be pedantic. - // if(defercalc) - // fatal("defercheckwidth"); + if(defercalc && nerrors == 0) + fatal("defercheckwidth"); defercalc = 1; } @@ -596,10 +604,10 @@ typeinit(void) Array_array = rnd(0, widthptr); Array_nel = rnd(Array_array+widthptr, types[TUINT32]->width); Array_cap = rnd(Array_nel+types[TUINT32]->width, types[TUINT32]->width); - sizeof_Array = rnd(Array_cap+types[TUINT32]->width, maxround); + sizeof_Array = rnd(Array_cap+types[TUINT32]->width, widthptr); // string is same as slice wo the cap - sizeof_String = rnd(Array_nel+types[TUINT32]->width, maxround); + sizeof_String = rnd(Array_nel+types[TUINT32]->width, widthptr); dowidth(types[TSTRING]); dowidth(idealstring); diff --git a/src/cmd/gc/builtin.c.boot b/src/cmd/gc/builtin.c.boot index 3e2d98872..380abc642 100644 --- a/src/cmd/gc/builtin.c.boot +++ b/src/cmd/gc/builtin.c.boot @@ -1,6 +1,6 @@ char *runtimeimport = "package runtime\n" - "func \"\".mal (? int32) *any\n" + "func \"\".new (? int32) *any\n" "func \"\".panicindex ()\n" "func \"\".panicslice ()\n" "func \"\".throwreturn ()\n" @@ -19,12 +19,13 @@ char *runtimeimport = "func \"\".printslice (? any)\n" "func \"\".printnl ()\n" "func \"\".printsp ()\n" - "func \"\".printf ()\n" - "func \"\".catstring (? string, ? string) string\n" + "func \"\".goprintf ()\n" + "func \"\".concatstring ()\n" + "func \"\".append ()\n" + "func \"\".appendslice (typ *uint8, x any, y []any) any\n" "func \"\".cmpstring (? string, ? string) int\n" "func \"\".slicestring (? string, ? int, ? int) string\n" "func \"\".slicestring1 (? string, ? int) string\n" - "func \"\".indexstring (? string, ? int) uint8\n" "func \"\".intstring (? int64) string\n" "func \"\".slicebytetostring (? []uint8) string\n" "func \"\".sliceinttostring (? []int) string\n" @@ -33,6 +34,7 @@ char *runtimeimport = "func \"\".stringiter (? string, ? int) int\n" "func \"\".stringiter2 (? string, ? int) (retk int, retv int)\n" "func \"\".slicecopy (to any, fr any, wid uint32) int\n" + "func \"\".slicestringcopy (to any, fr any) int\n" "func \"\".convI2E (elem any) any\n" "func \"\".convI2I (typ *uint8, elem any) any\n" "func \"\".convT2E (typ *uint8, elem any) any\n" @@ -75,16 +77,18 @@ char *runtimeimport = "func \"\".selectdefault (sel *uint8) bool\n" "func \"\".selectgo (sel *uint8)\n" "func \"\".makeslice (typ *uint8, nel int64, cap int64) []any\n" - "func \"\".sliceslice1 (old []any, lb int, width int) []any\n" - "func \"\".sliceslice (old []any, lb int, hb int, width int) []any\n" - "func \"\".slicearray (old *any, nel int, lb int, hb int, width int) []any\n" + "func \"\".sliceslice1 (old []any, lb uint64, width uint64) []any\n" + "func \"\".sliceslice (old []any, lb uint64, hb uint64, width uint64) []any\n" + "func \"\".slicearray (old *any, nel uint64, lb uint64, hb uint64, width uint64) []any\n" "func \"\".closure ()\n" "func \"\".int64div (? int64, ? int64) int64\n" "func \"\".uint64div (? uint64, ? uint64) uint64\n" "func \"\".int64mod (? int64, ? int64) int64\n" "func \"\".uint64mod (? uint64, ? uint64) uint64\n" "func \"\".float64toint64 (? float64) int64\n" + "func \"\".float64touint64 (? float64) uint64\n" "func \"\".int64tofloat64 (? int64) float64\n" + "func \"\".uint64tofloat64 (? uint64) float64\n" "func \"\".complex128div (num complex128, den complex128) complex128\n" "\n" "$$\n"; diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c index a24a03a49..eb7014366 100644 --- a/src/cmd/gc/closure.c +++ b/src/cmd/gc/closure.c @@ -158,7 +158,7 @@ walkclosure(Node *func, NodeList **init) // create the function xfunc = nod(ODCLFUNC, N, N); - snprint(namebuf, sizeof namebuf, "_func_%.3ld", ++closgen); + snprint(namebuf, sizeof namebuf, "_func_%.3d", ++closgen); xfunc->nname = newname(lookup(namebuf)); xfunc->nname->ntype = xtype; xfunc->nname->defn = xfunc; diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c index cec95359a..72e67a634 100644 --- a/src/cmd/gc/const.c +++ b/src/cmd/gc/const.c @@ -101,7 +101,7 @@ convlit1(Node **np, Type *t, int explicit) break; case OLSH: case ORSH: - convlit1(&n->left, t, explicit); + convlit1(&n->left, t, explicit && isideal(n->left->type)); t = n->left->type; if(t != T && !isint[t->etype]) { yyerror("invalid operation: %#N (shift of type %T)", n, t); @@ -202,8 +202,6 @@ convlit1(Node **np, Type *t, int explicit) goto bad; case CTFLT: case CTINT: - if(explicit) - goto bad; n->val = tocplx(n->val); break; case CTCPLX: @@ -300,7 +298,7 @@ toflt(Val v) f = mal(sizeof(*f)); mpmovefltflt(f, &v.u.cval->real); if(mpcmpfltc(&v.u.cval->imag, 0) != 0) - yyerror("constant %#F truncated to real", v.u.fval); + yyerror("constant %#F%+#Fi truncated to real", &v.u.cval->real, &v.u.cval->imag); v.ctype = CTFLT; v.u.fval = f; break; @@ -324,9 +322,9 @@ toint(Val v) case CTCPLX: i = mal(sizeof(*i)); if(mpmovefltfix(i, &v.u.cval->real) < 0) - yyerror("constant %#F truncated to integer", v.u.fval); + yyerror("constant %#F%+#Fi truncated to integer", &v.u.cval->real, &v.u.cval->imag); if(mpcmpfltc(&v.u.cval->imag, 0) != 0) - yyerror("constant %#F truncated to real", v.u.fval); + yyerror("constant %#F%+#Fi truncated to real", &v.u.cval->real, &v.u.cval->imag); v.ctype = CTINT; v.u.xval = i; break; @@ -536,6 +534,12 @@ evconst(Node *n) v = toflt(v); rv = toflt(rv); } + if(v.ctype != rv.ctype) { + // Use of undefined name as constant? + if((v.ctype == 0 || rv.ctype == 0) && nerrors > 0) + return; + fatal("constant type mismatch %T(%d) %T(%d)", nl->type, v.ctype, nr->type, rv.ctype); + } // run op switch(TUP(n->op, v.ctype)) { @@ -1086,6 +1090,12 @@ smallintconst(Node *n) case TBOOL: case TPTR32: return 1; + case TINT64: + case TUINT64: + if(mpcmpfixfix(n->val.u.xval, minintval[TINT32]) < 0 + || mpcmpfixfix(n->val.u.xval, maxintval[TINT32]) > 0) + break; + return 1; } return 0; } diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c index adb1531c3..a71272aa2 100644 --- a/src/cmd/gc/dcl.c +++ b/src/cmd/gc/dcl.c @@ -729,8 +729,11 @@ stotype(NodeList *l, int et, Type **t) n->right = N; if(n->embedded && n->type != T) { t1 = n->type; - if(t1->sym == S && isptr[t1->etype]) + if(t1->sym == S && isptr[t1->etype]) { t1 = t1->type; + if(t1->etype == TINTER) + yyerror("embedded type cannot be a pointer to interface"); + } if(isptr[t1->etype]) yyerror("embedded type cannot be a pointer"); else if(t1->etype == TFORW && t1->embedlineno == 0) @@ -841,6 +844,8 @@ dostruct(NodeList *l, int et) t->broke = 1; return t; } + if(et == TINTER) + t = sortinter(t); if(!funarg) checkwidth(t); return t; @@ -1017,11 +1022,12 @@ functype(Node *this, NodeList *in, NodeList *out) } Sym* -methodsym(Sym *nsym, Type *t0) +methodsym(Sym *nsym, Type *t0, int iface) { Sym *s; char *p; Type *t; + char *suffix; t = t0; if(t == T) @@ -1043,7 +1049,13 @@ methodsym(Sym *nsym, Type *t0) if(t != t0 && t0->sym) t0 = ptrto(t); - p = smprint("%#hT·%s", t0, nsym->name); + suffix = ""; + if(iface) { + dowidth(t0); + if(t0->width < types[tptr]->width) + suffix = "·i"; + } + p = smprint("%#hT·%s%s", t0, nsym->name, suffix); s = pkglookup(p, s->pkg); free(p); return s; @@ -1058,7 +1070,7 @@ methodname(Node *n, Type *t) { Sym *s; - s = methodsym(n->sym, t); + s = methodsym(n->sym, t, 0); if(s == S) return n; return newname(s); diff --git a/src/cmd/gc/doc.go b/src/cmd/gc/doc.go index 108a091b2..21e1b103b 100644 --- a/src/cmd/gc/doc.go +++ b/src/cmd/gc/doc.go @@ -25,13 +25,18 @@ other packages. It is therefore not necessary when compiling client C of package P to read the files of P's dependencies, only the compiled output of P. -Usage: 6g [flags] *.go (or 8g or 5g) +Usage: + 6g [flags] file... +The specified files must be Go source files and all part of the same package. +Substitute 6g with 8g or 5g where appropriate. Flags: -o file - output file, default 6.out for 6g, etc. + output file, default file.6 for 6g, etc. -e normally the compiler quits after 10 errors; -e prints all errors + -L + show entire file path when printing line numbers in errors -I dir1 -I dir2 add dir1 and dir2 to the list of paths to check for imported packages -N diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c index 52853c454..594509915 100644 --- a/src/cmd/gc/export.c +++ b/src/cmd/gc/export.c @@ -176,7 +176,8 @@ dumpexporttype(Sym *s) yyerror("export of incomplete type %T", t); return; } - Bprint(bout, "type %#T %l#T\n", t, t); + if(Bprint(bout, "type %#T %l#T\n", t, t) < 0) + fatal("Bprint failed for %T", t); } static int diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index fd8a7f39b..04af5a7bb 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -58,7 +58,7 @@ allocparams(void) if(w >= MAXWIDTH) fatal("bad width"); stksize += w; - stksize = rnd(stksize, w); + stksize = rnd(stksize, n->type->align); n->xoffset = -stksize; } lineno = lno; @@ -139,8 +139,10 @@ gen(Node *n) Prog *scontin, *sbreak; Prog *p1, *p2, *p3; Label *lab; + int32 wasregalloc; lno = setlineno(n); + wasregalloc = anyregalloc(); if(n == N) goto ret; @@ -246,9 +248,6 @@ gen(Node *n) gen(n->nincr); // contin: incr patch(p1, pc); // test: - if(n->ntest != N) - if(n->ntest->ninit != nil) - genlist(n->ntest->ninit); bgen(n->ntest, 0, breakpc); // if(!test) goto break genlist(n->nbody); // body gjmp(continpc); @@ -261,9 +260,6 @@ gen(Node *n) p1 = gjmp(P); // goto test p2 = gjmp(P); // p2: goto else patch(p1, pc); // test: - if(n->ntest != N) - if(n->ntest->ninit != nil) - genlist(n->ntest->ninit); bgen(n->ntest, 0, p2); // if(!test) goto p2 genlist(n->nbody); // then p3 = gjmp(P); // goto done @@ -342,6 +338,11 @@ gen(Node *n) } ret: + if(anyregalloc() != wasregalloc) { + dump("node", n); + fatal("registers left allocated"); + } + lineno = lno; } @@ -432,7 +433,7 @@ cgen_discard(Node *nr) switch(nr->op) { case ONAME: - if(!(nr->class & PHEAP) && nr->class != PEXTERN && nr->class != PFUNC) + if(!(nr->class & PHEAP) && nr->class != PEXTERN && nr->class != PFUNC && nr->class != PPARAMREF) gused(nr); break; @@ -651,7 +652,6 @@ tempname(Node *n, Type *t) snprint(namebuf, sizeof(namebuf), "autotmp_%.4d", statuniqgen); statuniqgen++; s = lookup(namebuf); - memset(n, 0, sizeof(*n)); n->op = ONAME; n->sym = s; @@ -664,7 +664,7 @@ tempname(Node *n, Type *t) dowidth(t); w = t->width; stksize += w; - stksize = rnd(stksize, w); + stksize = rnd(stksize, t->align); n->xoffset = -stksize; n->pun = anyregalloc(); } diff --git a/src/cmd/gc/go.errors b/src/cmd/gc/go.errors index cdd7578d4..b5af4678c 100644 --- a/src/cmd/gc/go.errors +++ b/src/cmd/gc/go.errors @@ -35,6 +35,15 @@ static struct { % loadsys package imports LTYPE LNAME ';' "unexpected semicolon or newline in type declaration", + % loadsys package imports LCHAN '}' + "unexpected } in channel type", + + % loadsys package imports LCHAN ')' + "unexpected ) in channel type", + + % loadsys package imports LCHAN ',' + "unexpected comma in channel type", + % loadsys package imports LFUNC LNAME '(' ')' '{' if_stmt ';' LELSE "unexpected semicolon or newline before else", diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 99e369eca..73ea5b976 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -6,6 +6,8 @@ #include <libc.h> #include <bio.h> +#undef OAPPEND + // avoid <ctype.h> #undef isblank #define isblank goisblank @@ -38,9 +40,10 @@ enum ASTRING, AINTER, ANILINTER, + AMEMWORD, BADWIDTH = -1000000000, - MAXWIDTH = 1<<30 + MAXWIDTH = 1<<30 }; /* @@ -151,6 +154,7 @@ struct Type uchar deferwidth; uchar broke; uchar isddd; // TFIELD is ... argument + uchar align; Node* nod; // canonical OTYPE node Type* orig; // original type (type literal or predefined type) @@ -209,12 +213,15 @@ struct Node uchar dodata; // compile literal assignment as data statement uchar used; uchar isddd; - uchar pun; // dont registerize variable ONAME + uchar pun; // don't registerize variable ONAME + uchar readonly; + uchar implicit; // don't show in printout // most nodes Node* left; Node* right; Type* type; + Type* realtype; // as determined by typecheck NodeList* list; NodeList* rlist; @@ -261,6 +268,7 @@ struct Node Sym* sym; // various int32 vargen; // unique name for OTYPE/ONAME int32 lineno; + int32 endlineno; vlong xoffset; int32 ostk; int32 iota; @@ -344,6 +352,7 @@ enum OADD, OSUB, OOR, OXOR, OADDSTR, OADDR, OANDAND, + OAPPEND, OARRAY, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, @@ -356,7 +365,7 @@ enum OCLOSURE, OCMPIFACE, OCMPSTR, OCOMPLIT, OMAPLIT, OSTRUCTLIT, OARRAYLIT, - OCONV, OCONVIFACE, OCONVNOP, OCONVSLICE, + OCONV, OCONVIFACE, OCONVNOP, OCOPY, ODCL, ODCLFUNC, ODCLFIELD, ODCLCONST, ODCLTYPE, ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OXDOT, @@ -364,7 +373,7 @@ enum ODOTTYPE2, OEQ, ONE, OLT, OLE, OGE, OGT, OIND, - OINDEX, OINDEXSTR, OINDEXMAP, + OINDEX, OINDEXMAP, OKEY, OPARAM, OLEN, OMAKE, OMAKECHAN, OMAKEMAP, OMAKESLICE, @@ -400,7 +409,6 @@ enum ORETURN, OSELECT, OSWITCH, - OTYPECASE, OTYPESW, // l = r.(type) // types @@ -410,6 +418,7 @@ enum OTINTER, OTFUNC, OTARRAY, + OTPAREN, // misc ODDD, @@ -632,9 +641,9 @@ EXTERN Label* labellist; * * typedef struct * { // must not move anything - * uchar array[8]; // pointer to data - * uchar nel[4]; // number of elements - * uchar cap[4]; // allocated number of elements + * uchar array[8]; // pointer to data + * uchar nel[4]; // number of elements + * uchar cap[4]; // allocated number of elements * } Array; */ EXTERN int Array_array; // runtime offsetof(Array,array) - same for String @@ -649,8 +658,8 @@ EXTERN int sizeof_Array; // runtime sizeof(Array) * * typedef struct * { // must not move anything - * uchar array[8]; // pointer to data - * uchar nel[4]; // number of elements + * uchar array[8]; // pointer to data + * uchar nel[4]; // number of elements * } String; */ EXTERN int sizeof_String; // runtime sizeof(String) @@ -743,7 +752,6 @@ EXTERN int hasdefer; // flag that curfn has defer statetment EXTERN Node* curfn; -EXTERN int maxround; EXTERN int widthptr; EXTERN Node* typesw; @@ -858,7 +866,7 @@ int isifacemethod(Type *f); void markdcl(void); Node* methodname(Node *n, Type *t); Node* methodname1(Node *n, Node *t); -Sym* methodsym(Sym *nsym, Type *t0); +Sym* methodsym(Sym *nsym, Type *t0, int iface); Node* newname(Sym *s); Type* newtype(Sym *s); Node* oldname(Sym *s); @@ -914,6 +922,9 @@ char* lexname(int lex); void mkpackage(char* pkgname); void unimportfile(void); int32 yylex(void); +extern int windows; +extern int yylast; +extern int yyprev; /* * mparith1.c @@ -1003,7 +1014,7 @@ void walkrange(Node *n); * reflect.c */ void dumptypestructs(void); -Type* methodfunc(Type *f, int use_receiver); +Type* methodfunc(Type *f, Type*); Node* typename(Type *t); Sym* typesym(Type *t); @@ -1016,7 +1027,7 @@ void walkselect(Node *sel); /* * sinit.c */ -void anylit(int ctxt, Node *n, Node *var, NodeList **init); +void anylit(int, Node *n, Node *var, NodeList **init); int gen_as_init(Node *n); NodeList* initfix(NodeList *l); int oaslit(Node *n, NodeList **init); @@ -1059,7 +1070,7 @@ void flusherrors(void); void frame(int context); Type* funcfirst(Iter *s, Type *t); Type* funcnext(Iter *s); -void genwrapper(Type *rcvr, Type *method, Sym *newnam); +void genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface); Type** getinarg(Type *t); Type* getinargx(Type *t); Type** getoutarg(Type *t); @@ -1095,6 +1106,7 @@ Node* nod(int op, Node *nleft, Node *nright); Node* nodbool(int b); void nodconst(Node *n, Type *t, int64 v); Node* nodintconst(int64 v); +Node* nodfltconst(Mpflt *v); Node* nodnil(void); int parserline(void); Sym* pkglookup(char *name, Pkg *pkg); @@ -1103,13 +1115,13 @@ Type* ptrto(Type *t); void* remal(void *p, int32 on, int32 n); Sym* restrictlookup(char *name, Pkg *pkg); Node* safeexpr(Node *n, NodeList **init); +Node* cheapexpr(Node *n, NodeList **init); int32 setlineno(Node *n); void setmaxarg(Type *t); Type* shallow(Type *t); int simsimtype(Type *t); void smagic(Magic *m); Type* sortinter(Type *t); -Node* staticname(Type *t); uint32 stringhash(char *p); Strlit* strlit(char *s); int structcount(Type *t); @@ -1143,7 +1155,7 @@ void typechecklist(NodeList *l, int top); /* * unsafe.c */ -Node* unsafenmagic(Node *fn, NodeList *args); +Node* unsafenmagic(Node *n); /* * walk.c @@ -1206,7 +1218,7 @@ void dumpfuncs(void); void gdata(Node*, Node*, int); void gdatacomplex(Node*, Mpcplx*); void gdatastring(Node*, Strlit*); -void genembedtramp(Type*, Type*, Sym*); +void genembedtramp(Type*, Type*, Sym*, int iface); void ggloblnod(Node *nam, int32 width); void ggloblsym(Sym *s, int32 width, int dupok); Prog* gjmp(Prog*); diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index c46abaa56..917265758 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -20,6 +20,8 @@ %{ #include <stdio.h> /* if we don't, bison will, and go.h re-#defines getc */ #include "go.h" + +static void fixlbrace(int); %} %union { Node* node; @@ -50,7 +52,7 @@ %type <node> stmt ntype %type <node> arg_type %type <node> case caseblock -%type <node> compound_stmt dotname embed expr +%type <node> compound_stmt dotname embed expr complitexpr %type <node> expr_or_type %type <node> fndcl fnliteral %type <node> for_body for_header for_stmt if_header if_stmt non_dcl_stmt @@ -58,7 +60,7 @@ %type <node> name_or_type non_expr_type %type <node> new_name dcl_name oexpr typedclname %type <node> onew_name -%type <node> osimple_stmt pexpr +%type <node> osimple_stmt pexpr pexpr_no_paren %type <node> pseudocall range_stmt select_stmt %type <node> simple_stmt %type <node> switch_stmt uexpr @@ -66,7 +68,7 @@ %type <list> xdcl fnbody fnres switch_body loop_body dcl_name_list %type <list> new_name_list expr_list keyval_list braced_keyval_list expr_or_type_list xdcl_list -%type <list> oexpr_list oexpr_or_type_list_ocomma caseblock_list stmt_list oarg_type_list_ocomma arg_type_list +%type <list> oexpr_list caseblock_list stmt_list oarg_type_list_ocomma arg_type_list %type <list> interfacedcl_list vardcl vardcl_list structdcl structdcl_list %type <list> common_dcl constdcl constdcl1 constdcl_list typedcl_list @@ -459,7 +461,7 @@ case: } break; } -| LCASE name '=' expr ':' +| LCASE expr '=' expr ':' { // will be converted to OCASE // right will point to next case @@ -515,10 +517,33 @@ switch_body: } caseblock: - case stmt_list + case + { + // If the last token read by the lexer was consumed + // as part of the case, clear it (parser has cleared yychar). + // If the last token read by the lexer was the lookahead + // leave it alone (parser has it cached in yychar). + // This is so that the stmt_list action doesn't look at + // the case tokens if the stmt_list is empty. + yylast = yychar; + } + stmt_list { + int last; + + // This is the only place in the language where a statement + // list is not allowed to drop the final semicolon, because + // it's the only place where a statement list is not followed + // by a closing brace. Handle the error for pedantry. + + // Find the final token of the statement list. + // yylast is lookahead; yyprev is last of stmt_list + last = yyprev; + + if(last > 0 && last != ';' && yychar != '}') + yyerror("missing statement after label"); $$ = $1; - $$->nbody = $2; + $$->nbody = $3; } caseblock_list: @@ -648,11 +673,13 @@ select_stmt: LSELECT { markdcl(); + typesw = nod(OXXX, typesw, N); } switch_body { $$ = nod(OSELECT, N, N); $$->list = $3; + typesw = typesw->left; popdcl(); } @@ -783,13 +810,23 @@ uexpr: * can be preceded by 'defer' and 'go' */ pseudocall: - pexpr '(' oexpr_or_type_list_ocomma ')' + pexpr '(' ')' + { + $$ = nod(OCALL, $1, N); + } +| pexpr '(' expr_or_type_list ocomma ')' { $$ = nod(OCALL, $1, N); $$->list = $3; } +| pexpr '(' expr_or_type_list LDDD ocomma ')' + { + $$ = nod(OCALL, $1, N); + $$->list = $3; + $$->isddd = 1; + } -pexpr: +pexpr_no_paren: LLITERAL { $$ = nodlit($1); @@ -806,10 +843,6 @@ pexpr: } $$ = nod(OXDOT, $1, newname($3)); } -| '(' expr_or_type ')' - { - $$ = $2; - } | pexpr '.' '(' expr_or_type ')' { $$ = nod(ODOTTYPE, $1, $4); @@ -824,10 +857,6 @@ pexpr: } | pexpr '[' oexpr ':' oexpr ']' { - if($3 == N) { - yyerror("missing lower bound in slice expression"); - $3 = nodintconst(0); - } $$ = nod(OSLICE, $1, nod(OKEY, $3, $5)); } | pseudocall @@ -842,21 +871,45 @@ pexpr: // composite expression $$ = nod(OCOMPLIT, N, $1); $$->list = $3; - - // If the opening brace was an LBODY, - // set up for another one now that we're done. - // See comment in lex.c about loophack. - if($2 == LBODY) - loophack = 1; + + fixlbrace($2); } -| pexpr '{' braced_keyval_list '}' +| pexpr_no_paren '{' braced_keyval_list '}' { // composite expression $$ = nod(OCOMPLIT, N, $1); $$->list = $3; } +| '(' expr_or_type ')' '{' braced_keyval_list '}' + { + yyerror("cannot parenthesize type in composite literal"); + // composite expression + $$ = nod(OCOMPLIT, N, $2); + $$->list = $5; + } | fnliteral +keyval: + expr ':' complitexpr + { + $$ = nod(OKEY, $1, $3); + } + +complitexpr: + expr +| '{' braced_keyval_list '}' + { + $$ = nod(OCOMPLIT, N, N); + $$->list = $2; + } + +pexpr: + pexpr_no_paren +| '(' expr_or_type ')' + { + $$ = $2; + } + expr_or_type: expr | non_expr_type %prec PreferToRightParen @@ -939,7 +992,7 @@ ntype: | dotname | '(' ntype ')' { - $$ = $2; + $$ = nod(OTPAREN, $2, N); } non_expr_type: @@ -958,7 +1011,7 @@ non_recvchantype: | dotname | '(' ntype ')' { - $$ = $2; + $$ = nod(OTPAREN, $2, N); } convtype: @@ -1030,34 +1083,31 @@ recvchantype: } structtype: - LSTRUCT '{' structdcl_list osemi '}' + LSTRUCT lbrace structdcl_list osemi '}' { $$ = nod(OTSTRUCT, N, N); $$->list = $3; + fixlbrace($2); } -| LSTRUCT '{' '}' +| LSTRUCT lbrace '}' { $$ = nod(OTSTRUCT, N, N); + fixlbrace($2); } interfacetype: - LINTERFACE '{' interfacedcl_list osemi '}' + LINTERFACE lbrace interfacedcl_list osemi '}' { $$ = nod(OTINTER, N, N); $$->list = $3; + fixlbrace($2); } -| LINTERFACE '{' '}' +| LINTERFACE lbrace '}' { $$ = nod(OTINTER, N, N); + fixlbrace($2); } -keyval: - expr ':' expr - { - $$ = nod(OKEY, $1, $3); - } - - /* * function stuff * all in one place to show how crappy it all is @@ -1069,6 +1119,7 @@ xfndcl: if($$ == N) break; $$->nbody = $3; + $$->endlineno = lineno; funcbody($$); } @@ -1118,6 +1169,8 @@ fndcl: yyerror("bad receiver in method"); break; } + if(rcvr->right->op == OTPAREN || (rcvr->right->op == OIND && rcvr->right->left->op == OTPAREN)) + yyerror("cannot parenthesize receiver type"); $$ = nod(ODCLFUNC, N, N); $$->nname = methodname1(name, rcvr->right); @@ -1250,12 +1303,32 @@ structdcl: $1->val = $2; $$ = list1($1); } +| '(' embed ')' oliteral + { + $2->val = $4; + $$ = list1($2); + yyerror("cannot parenthesize embedded type"); + } | '*' embed oliteral { $2->right = nod(OIND, $2->right, N); $2->val = $3; $$ = list1($2); } +| '(' '*' embed ')' oliteral + { + $3->right = nod(OIND, $3->right, N); + $3->val = $5; + $$ = list1($3); + yyerror("cannot parenthesize embedded type"); + } +| '*' '(' embed ')' oliteral + { + $3->right = nod(OIND, $3->right, N); + $3->val = $5; + $$ = list1($3); + yyerror("cannot parenthesize embedded type"); + } packname: LNAME @@ -1296,6 +1369,11 @@ interfacedcl: { $$ = nod(ODCLFIELD, N, oldname($1)); } +| '(' packname ')' + { + $$ = nod(ODCLFIELD, N, oldname($2)); + yyerror("cannot parenthesize embedded type"); + } indcl: '(' oarg_type_list_ocomma ')' fnres @@ -1481,7 +1559,7 @@ keyval_list: { $$ = list1($1); } -| expr +| complitexpr { $$ = list1($1); } @@ -1489,7 +1567,7 @@ keyval_list: { $$ = list($1, $3); } -| keyval_list ',' expr +| keyval_list ',' complitexpr { $$ = list($1, $3); } @@ -1524,12 +1602,6 @@ oexpr_list: } | expr_list -oexpr_or_type_list_ocomma: - { - $$ = nil; - } -| expr_or_type_list ocomma - osimple_stmt: { $$ = N; @@ -1654,7 +1726,6 @@ hidden_type_misc: | LINTERFACE '{' ohidden_interfacedcl_list '}' { $$ = dostruct($3, TINTER); - $$ = sortinter($$); } | '*' hidden_type { @@ -1867,3 +1938,16 @@ hidden_interfacedcl_list: { $$ = list($1, $3); } + +%% + +static void +fixlbrace(int lbr) +{ + // If the opening brace was an LBODY, + // set up for another one now that we're done. + // See comment in lex.c about loophack. + if(lbr == LBODY) + loophack = 1; +} + diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 452acfc76..0f1acd2fc 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -14,6 +14,8 @@ extern int yychar; int windows; +int yyprev; +int yylast; static void lexinit(void); static void lexfini(void); @@ -23,9 +25,45 @@ static void ungetc(int); static int32 getr(void); static int escchar(int, int*, vlong*); static void addidir(char*); - +static int getlinepragma(void); static char *goos, *goarch, *goroot; +// Our own isdigit, isspace, isalpha, isalnum that take care +// of EOF and other out of range arguments. +static int +yy_isdigit(int c) +{ + return c >= 0 && c <= 0xFF && isdigit(c); +} + +static int +yy_isspace(int c) +{ + return c >= 0 && c <= 0xFF && isspace(c); +} + +static int +yy_isalpha(int c) +{ + return c >= 0 && c <= 0xFF && isalpha(c); +} + +static int +yy_isalnum(int c) +{ + return c >= 0 && c <= 0xFF && isalnum(c); +} + +// Disallow use of isdigit etc. +#undef isdigit +#undef isspace +#undef isalpha +#undef isalnum +#define isdigit use_yy_isdigit_instead_of_isdigit +#define isspace use_yy_isspace_instead_of_isspace +#define isalpha use_yy_isalpha_instead_of_isalpha +#define isalnum use_yy_isalnum_instead_of_isalnum + #define DBG if(!debug['x']);else print enum { @@ -35,7 +73,7 @@ enum void usage(void) { - print("usage: %cg [flags] file.go...\n"); + print("gc: usage: %cg [flags] file.go...\n", thechar); print("flags:\n"); // -A is allow use of "any" type, for bootstrapping print(" -I DIR search for packages in DIR\n"); @@ -52,12 +90,27 @@ usage(void) exit(0); } +void +fault(int s) +{ + // If we've already complained about things + // in the program, don't bother complaining + // about the seg fault too; let the user clean up + // the code and try again. + if(nerrors > 0) + errorexit(); + fatal("fault"); +} + int main(int argc, char *argv[]) { int i, c; NodeList *l; char *p; + + signal(SIGBUS, fault); + signal(SIGSEGV, fault); localpkg = mkpkg(strlit("")); localpkg->prefix = "\"\""; @@ -119,7 +172,7 @@ main(int argc, char *argv[]) if(getwd(pathname, 999) == 0) strcpy(pathname, "/???"); - if(isalpha(pathname[0]) && pathname[1] == ':') { + if(yy_isalpha(pathname[0]) && pathname[1] == ':') { // On Windows. windows = 1; @@ -141,7 +194,7 @@ main(int argc, char *argv[]) fmtinstall('F', Fconv); // big float numbers betypeinit(); - if(maxround == 0 || widthptr == 0) + if(widthptr == 0) fatal("betypeinit failed"); lexinit(); @@ -287,7 +340,7 @@ islocalname(Strlit *name) if(!windows && name->len >= 1 && name->s[0] == '/') return 1; if(windows && name->len >= 3 && - isalpha(name->s[0]) && name->s[1] == ':' && name->s[2] == '/') + yy_isalpha(name->s[0]) && name->s[1] == ':' && name->s[2] == '/') return 1; if(name->len >= 2 && strncmp(name->s, "./", 2) == 0) return 1; @@ -300,8 +353,11 @@ static int findpkg(Strlit *name) { Idir *p; + char *q; if(islocalname(name)) { + if(safemode) + return 0; // try .a before .6. important for building libraries: // if there is an array.6 in the array.a library, // want to find all of array.a, not just array.6. @@ -314,6 +370,18 @@ findpkg(Strlit *name) return 0; } + // local imports should be canonicalized already. + // don't want to see "container/../container/vector" + // as different from "container/vector". + q = mal(name->len+1); + memmove(q, name->s, name->len); + q[name->len] = '\0'; + cleanname(q); + if(strlen(q) != name->len || memcmp(q, name->s, name->len) != 0) { + yyerror("non-canonical import path %Z (should be %s)", name, q); + return 0; + } + for(p = idirs; p != nil; p = p->link) { snprint(namebuf, sizeof(namebuf), "%s/%Z.a", p->dir, name); if(access(namebuf, 0) >= 0) @@ -368,7 +436,9 @@ importfile(Val *f, int line) path = f->u.sval; if(islocalname(path)) { cleanbuf = mal(strlen(pathname) + strlen(path->s) + 2); - sprint(cleanbuf, "%s/%s", pathname, path->s); + strcpy(cleanbuf, pathname); + strcat(cleanbuf, "/"); + strcat(cleanbuf, path->s); cleanname(cleanbuf); path = strlit(cleanbuf); } @@ -381,7 +451,7 @@ importfile(Val *f, int line) imp = Bopen(namebuf, OREAD); if(imp == nil) { - yyerror("can't open import: %Z", f->u.sval); + yyerror("can't open import: %Z: %r", f->u.sval); errorexit(); } file = strdup(namebuf); @@ -470,7 +540,7 @@ isfrog(int c) return 0; return 1; } - if(0x80 <= c && c <= 0xa0) // unicode block including unbreakable space. + if(0x7f <= c && c <= 0xa0) // DEL, unicode block including unbreakable space. return 1; return 0; } @@ -496,7 +566,7 @@ _yylex(void) l0: c = getc(); - if(isspace(c)) { + if(yy_isspace(c)) { if(c == '\n' && curio.nlsemi) { ungetc(c); DBG("lex: implicit semi\n"); @@ -514,13 +584,13 @@ l0: goto talph; } - if(isalpha(c)) { + if(yy_isalpha(c)) { cp = lexbuf; ep = lexbuf+sizeof lexbuf; goto talph; } - if(isdigit(c)) + if(yy_isdigit(c)) goto tnum; switch(c) { @@ -536,7 +606,7 @@ l0: case '.': c1 = getc(); - if(isdigit(c1)) { + if(yy_isdigit(c1)) { cp = lexbuf; ep = lexbuf+sizeof lexbuf; *cp++ = c; @@ -656,16 +726,13 @@ l0: } } if(c1 == '/') { + c = getlinepragma(); for(;;) { - c = getr(); - if(c == '\n') { + if(c == '\n' || c == EOF) { ungetc(c); goto l0; } - if(c == EOF) { - yyerror("eof in comment"); - errorexit(); - } + c = getr(); } } if(c1 == '=') { @@ -878,6 +945,10 @@ lx: yyerror("illegal character 0x%ux", c); goto l0; } + if(importpkg == nil && (c == '#' || c == '$' || c == '?' || c == '@' || c == '\\')) { + yyerror("%s: unexpected %c", "syntax error", c); + goto l0; + } return c; asop: @@ -902,7 +973,7 @@ talph: if(!isalpharune(rune) && !isdigitrune(rune) && (importpkg == nil || rune != 0xb7)) yyerror("invalid identifier character 0x%ux", rune); cp += runetochar(cp, &rune); - } else if(!isalnum(c) && c != '_') + } else if(!yy_isalnum(c) && c != '_') break; else *cp++ = c; @@ -940,7 +1011,7 @@ tnum: } *cp++ = c; c = getc(); - if(isdigit(c)) + if(yy_isdigit(c)) continue; goto dc; } @@ -955,7 +1026,7 @@ tnum: } *cp++ = c; c = getc(); - if(isdigit(c)) + if(yy_isdigit(c)) continue; if(c >= 'a' && c <= 'f') continue; @@ -976,7 +1047,7 @@ tnum: yyerror("identifier too long"); errorexit(); } - if(!isdigit(c)) + if(!yy_isdigit(c)) break; if(c < '0' || c > '7') c1 = 1; // not octal @@ -1025,7 +1096,7 @@ casedot: } *cp++ = c; c = getc(); - if(!isdigit(c)) + if(!yy_isdigit(c)) break; } if(c == 'i') @@ -1040,9 +1111,9 @@ casee: *cp++ = c; c = getc(); } - if(!isdigit(c)) + if(!yy_isdigit(c)) yyerror("malformed fp constant exponent"); - while(isdigit(c)) { + while(yy_isdigit(c)) { if(cp+10 >= ep) { yyerror("identifier too long"); errorexit(); @@ -1061,9 +1132,9 @@ casep: *cp++ = c; c = getc(); } - if(!isdigit(c)) + if(!yy_isdigit(c)) yyerror("malformed fp constant exponent"); - while(isdigit(c)) { + while(yy_isdigit(c)) { if(cp+10 >= ep) { yyerror("identifier too long"); errorexit(); @@ -1104,6 +1175,68 @@ caseout: return LLITERAL; } +/* + * read and interpret syntax that looks like + * //line parse.y:15 + * as a discontinuity in sequential line numbers. + * the next line of input comes from parse.y:15 + */ +static int +getlinepragma(void) +{ + int i, c, n; + char *cp, *ep; + Hist *h; + + for(i=0; i<5; i++) { + c = getr(); + if(c != "line "[i]) + goto out; + } + + cp = lexbuf; + ep = lexbuf+sizeof(lexbuf)-5; + for(;;) { + c = getr(); + if(c == '\n' || c == EOF) + goto out; + if(c == ' ') + continue; + if(c == ':') + break; + if(cp < ep) + *cp++ = c; + } + *cp = 0; + + n = 0; + for(;;) { + c = getr(); + if(!yy_isdigit(c)) + break; + n = n*10 + (c-'0'); + if(n > 1e8) { + yyerror("line number out of range"); + errorexit(); + } + } + + if(c != '\n' || n <= 0) + goto out; + + // try to avoid allocating file name over and over + for(h=hist; h!=H; h=h->link) { + if(h->name != nil && strcmp(h->name, lexbuf) == 0) { + linehist(h->name, n, 0); + goto out; + } + } + linehist(strdup(lexbuf), n, 0); + +out: + return c; +} + int32 yylex(void) { @@ -1112,13 +1245,8 @@ yylex(void) lx = _yylex(); if(curio.nlsemi && lx == EOF) { - // if the nlsemi bit is set, we'd be willing to - // insert a ; if we saw a \n, but we didn't. - // that means the final \n is missing. - // complain here, because we can give a - // good message. the syntax error we'd get - // otherwise is inscrutable. - yyerror("missing newline at end of file"); + // Treat EOF as "end of line" for the purposes + // of inserting a semicolon. lx = ';'; } @@ -1140,7 +1268,11 @@ yylex(void) curio.nlsemi = 0; break; } - return lx; + + // Track last two tokens returned by yylex. + yyprev = yylast; + yylast = lx; + return lx; } static int @@ -1395,6 +1527,7 @@ static struct "type", LTYPE, Txxx, OXXX, "var", LVAR, Txxx, OXXX, + "append", LNAME, Txxx, OAPPEND, "cap", LNAME, Txxx, OCAP, "close", LNAME, Txxx, OCLOSE, "closed", LNAME, Txxx, OCLOSED, diff --git a/src/cmd/gc/mkbuiltin b/src/cmd/gc/mkbuiltin index 13309ec32..4dfff1caa 100755 --- a/src/cmd/gc/mkbuiltin +++ b/src/cmd/gc/mkbuiltin @@ -10,11 +10,9 @@ set -e -GOBIN="${GOBIN:-$HOME/bin}" - -. "$GOROOT"/src/Make.$GOARCH +eval $(gomake --no-print-directory -f ../../Make.inc go-env) if [ -z "$GC" ]; then - echo 'missing $GC - maybe no Make.$GOARCH?' 1>&2 + echo 'missing $GC - gomake failed?' 1>&2 exit 1 fi @@ -22,7 +20,7 @@ gcc -o mkbuiltin1 mkbuiltin1.c rm -f _builtin.c for i in runtime unsafe do - "$GOBIN"/$GC -A $i.go + $GC -A $i.go O=$O ./mkbuiltin1 $i >>_builtin.c done diff --git a/src/cmd/gc/mparith1.c b/src/cmd/gc/mparith1.c index 8110e77b9..6cd4e2500 100644 --- a/src/cmd/gc/mparith1.c +++ b/src/cmd/gc/mparith1.c @@ -156,10 +156,11 @@ mpmovefixflt(Mpflt *a, Mpint *b) // convert (truncate) b to a. // return -1 (but still convert) if b was non-integer. -int -mpmovefltfix(Mpint *a, Mpflt *b) +static int +mpexactfltfix(Mpint *a, Mpflt *b) { Mpflt f; + *a = b->val; mpshiftfix(a, b->exp); if(b->exp < 0) { @@ -172,6 +173,35 @@ mpmovefltfix(Mpint *a, Mpflt *b) return 0; } +int +mpmovefltfix(Mpint *a, Mpflt *b) +{ + Mpflt f; + int i; + + if(mpexactfltfix(a, b) == 0) + return 0; + + // try rounding down a little + f = *b; + f.val.a[0] = 0; + if(mpexactfltfix(a, &f) == 0) + return 0; + + // try rounding up a little + for(i=1; i<Mpprec; i++) { + f.val.a[i]++; + if(f.val.a[i] != Mpbase) + break; + f.val.a[i] = 0; + } + mpnorm(&f); + if(mpexactfltfix(a, &f) == 0) + return 0; + + return -1; +} + void mpmovefixfix(Mpint *a, Mpint *b) { @@ -188,6 +218,8 @@ static double tab[] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7 }; static void mppow10flt(Mpflt *a, int p) { + if(p < 0) + abort(); if(p < nelem(tab)) { mpmovecflt(a, tab[p]); return; @@ -267,6 +299,10 @@ mpatoflt(Mpflt *a, char *as) } if(c >= '0' && c <= '9') { ex = ex*10 + (c-'0'); + if(ex > 1e8) { + yyerror("exponent out of range"); + errorexit(); + } continue; } break; @@ -439,6 +475,8 @@ Fconv(Fmt *fp) // for well in range, convert to double and use print's %g if(-900 < fvp->exp && fvp->exp < 900) { d = mpgetflt(fvp); + if(d >= 0 && (fp->flags & FmtSign)) + fmtprint(fp, "+"); return fmtprint(fp, "%g", d); } // TODO(rsc): for well out of range, print diff --git a/src/cmd/gc/mparith2.c b/src/cmd/gc/mparith2.c index b025917fa..403255005 100644 --- a/src/cmd/gc/mparith2.c +++ b/src/cmd/gc/mparith2.c @@ -319,7 +319,11 @@ mpmulfract(Mpint *a, Mpint *b) s.neg = 0; mpmovecfix(&q, 0); - for(i=0; i<Mpprec; i++) { + x = *--a1; + if(x != 0) + yyerror("mpmulfract not normal"); + + for(i=0; i<Mpprec-1; i++) { x = *--a1; if(x == 0) { mprshw(&s); @@ -532,7 +536,7 @@ mpgetfix(Mpint *a) vlong v; if(a->ovf) { - yyerror("ovf in mpgetfix"); + yyerror("constant overflow"); return 0; } diff --git a/src/cmd/gc/mparith3.c b/src/cmd/gc/mparith3.c index b9cd4ea84..7b7e66668 100644 --- a/src/cmd/gc/mparith3.c +++ b/src/cmd/gc/mparith3.c @@ -27,16 +27,36 @@ sigfig(Mpflt *a) void mpnorm(Mpflt *a) { - int s; + int s, os; + long x; - s = sigfig(a); - if(s == 0) { + os = sigfig(a); + if(os == 0) { // zero a->exp = 0; a->val.neg = 0; return; } - s = (Mpnorm-s) * Mpscale; + + // this will normalize to the nearest word + x = a->val.a[os-1]; + s = (Mpnorm-os) * Mpscale; + + // further normalize to the nearest bit + for(;;) { + x <<= 1; + if(x & Mpbase) + break; + s++; + if(x == 0) { + // this error comes from trying to + // convert an Inf or something + // where the initial x=0x80000000 + s = (Mpnorm-os) * Mpscale; + break; + } + } + mpshiftfix(&a->val, s); a->exp -= s; } @@ -109,7 +129,7 @@ mpmulfltflt(Mpflt *a, Mpflt *b) } mpmulfract(&a->val, &b->val); - a->exp = (a->exp + b->exp) + Mpscale*Mpprec - 1; + a->exp = (a->exp + b->exp) + Mpscale*Mpprec - Mpscale - 1; mpnorm(a); if(Mpdebug) diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c index ae16f2725..0d0d70ac9 100644 --- a/src/cmd/gc/obj.c +++ b/src/cmd/gc/obj.c @@ -74,41 +74,96 @@ Bputname(Biobuf *b, Sym *s) } static void +outzfile(Biobuf *b, char *p) +{ + char *q, *q2; + + while(p) { + q = utfrune(p, '/'); + if(windows) { + q2 = utfrune(p, '\\'); + if(q2 && (!q || q2 < q)) + q = q2; + } + if(!q) { + zfile(b, p, strlen(p)); + return; + } + if(q > p) + zfile(b, p, q-p); + p = q + 1; + } +} + +#define isdelim(c) (c == '/' || c == '\\') + +static void +outwinname(Biobuf *b, Hist *h, char *ds, char *p) +{ + if(isdelim(p[0])) { + // full rooted name + zfile(b, ds, 3); // leading "c:/" + outzfile(b, p+1); + } else { + // relative name + if(h->offset == 0 && pathname && pathname[1] == ':') { + if(tolowerrune(ds[0]) == tolowerrune(pathname[0])) { + // using current drive + zfile(b, pathname, 3); // leading "c:/" + outzfile(b, pathname+3); + } else { + // using drive other then current, + // we don't have any simple way to + // determine current working directory + // there, therefore will output name as is + zfile(b, ds, 2); // leading "c:" + } + } + outzfile(b, p); + } +} + +static void outhist(Biobuf *b) { Hist *h; - char *p, *q, *op; - int n; + char *p, ds[] = {'c', ':', '/', 0}; for(h = hist; h != H; h = h->link) { p = h->name; - op = 0; - - if(p && p[0] != '/' && h->offset == 0 && pathname && pathname[0] == '/') { - op = p; - p = pathname; - } - - while(p) { - q = utfrune(p, '/'); - if(q) { - n = q-p; - if(n == 0) - n = 1; // leading "/" - q++; + if(p) { + if(windows) { + // if windows variable is set, then, we know already, + // pathname is started with windows drive specifier + // and all '\' were replaced with '/' (see lex.c) + if(isdelim(p[0]) && isdelim(p[1])) { + // file name has network name in it, + // like \\server\share\dir\file.go + zfile(b, "//", 2); // leading "//" + outzfile(b, p+2); + } else if(p[1] == ':') { + // file name has drive letter in it + ds[0] = p[0]; + outwinname(b, h, ds, p+2); + } else { + // no drive letter in file name + outwinname(b, h, pathname, p); + } } else { - n = strlen(p); - q = 0; - } - if(n) - zfile(b, p, n); - p = q; - if(p == 0 && op) { - p = op; - op = 0; + if(p[0] == '/') { + // full rooted name, like /home/rsc/dir/file.go + zfile(b, "/", 1); // leading "/" + outzfile(b, p+1); + } else { + // relative name, like dir/file.go + if(h->offset == 0 && pathname && pathname[0] == '/') { + zfile(b, "/", 1); // leading "/" + outzfile(b, pathname+1); + } + outzfile(b, p); + } } } - zhist(b, h->line, h->offset); } } diff --git a/src/cmd/gc/print.c b/src/cmd/gc/print.c index 8738eb41b..6bb1f026b 100644 --- a/src/cmd/gc/print.c +++ b/src/cmd/gc/print.c @@ -23,14 +23,21 @@ void exprfmt(Fmt *f, Node *n, int prec) { int nprec; + char *p; nprec = 0; if(n == nil) { fmtprint(f, "<nil>"); return; } + + if(n->implicit) { + exprfmt(f, n->left, prec); + return; + } switch(n->op) { + case OAPPEND: case ONAME: case ONONAME: case OPACK: @@ -53,9 +60,11 @@ exprfmt(Fmt *f, Node *n, int prec) case OPRINT: case OPRINTN: case OCALL: + case OCALLMETH: + case OCALLINTER: + case OCALLFUNC: case OCONV: case OCONVNOP: - case OCONVSLICE: case OMAKESLICE: case ORUNESTR: case OADDR: @@ -66,6 +75,9 @@ exprfmt(Fmt *f, Node *n, int prec) case OPLUS: case ORECV: case OCONVIFACE: + case OTPAREN: + case OINDEX: + case OINDEXMAP: nprec = 7; break; @@ -106,6 +118,11 @@ exprfmt(Fmt *f, Node *n, int prec) case OOROR: nprec = 1; break; + + case OTYPE: + if(n->sym != S) + nprec = 7; + break; } if(prec > nprec) @@ -118,6 +135,10 @@ exprfmt(Fmt *f, Node *n, int prec) break; case OLITERAL: + if(n->sym != S) { + fmtprint(f, "%S", n->sym); + break; + } switch(n->val.ctype) { default: goto bad; @@ -154,6 +175,10 @@ exprfmt(Fmt *f, Node *n, int prec) break; case OTYPE: + if(n->type == T && n->sym != S) { + fmtprint(f, "%S", n->sym); + break; + } fmtprint(f, "%T", n->type); break; @@ -161,6 +186,12 @@ exprfmt(Fmt *f, Node *n, int prec) fmtprint(f, "[]"); exprfmt(f, n->left, PFIXME); break; + + case OTPAREN: + fmtprint(f, "("); + exprfmt(f, n->left, 0); + fmtprint(f, ")"); + break; case OTMAP: fmtprint(f, "map["); @@ -178,7 +209,7 @@ exprfmt(Fmt *f, Node *n, int prec) exprfmt(f, n->left, 0); } else { fmtprint(f, " "); - if(n->left->op == OTCHAN && n->left->etype == Crecv) { + if(n->left->op == OTCHAN && n->left->sym == S && n->left->etype == Crecv) { fmtprint(f, "("); exprfmt(f, n->left, 0); fmtprint(f, ")"); @@ -248,6 +279,10 @@ exprfmt(Fmt *f, Node *n, int prec) exprfmt(f, n->left, 0); break; + case OCLOSURE: + fmtprint(f, "func literal"); + break; + case OCOMPLIT: fmtprint(f, "composite literal"); break; @@ -267,6 +302,7 @@ exprfmt(Fmt *f, Node *n, int prec) fmtprint(f, "struct literal"); break; + case OXDOT: case ODOT: case ODOTPTR: case ODOTINTER: @@ -274,8 +310,15 @@ exprfmt(Fmt *f, Node *n, int prec) exprfmt(f, n->left, 7); if(n->right == N || n->right->sym == S) fmtprint(f, ".<nil>"); - else - fmtprint(f, ".%s", n->right->sym->name); + else { + // skip leading type· in method name + p = utfrrune(n->right->sym->name, 0xb7); + if(p) + p+=2; + else + p = n->right->sym->name; + fmtprint(f, ".%s", p); + } break; case ODOTTYPE: @@ -291,7 +334,6 @@ exprfmt(Fmt *f, Node *n, int prec) case OINDEX: case OINDEXMAP: - case OINDEXSTR: exprfmt(f, n->left, 7); fmtprint(f, "["); exprfmt(f, n->right, 0); @@ -299,9 +341,12 @@ exprfmt(Fmt *f, Node *n, int prec) break; case OSLICE: + case OSLICESTR: + case OSLICEARR: exprfmt(f, n->left, 7); fmtprint(f, "["); - exprfmt(f, n->right->left, 0); + if(n->right->left != N) + exprfmt(f, n->right->left, 0); fmtprint(f, ":"); if(n->right->right != N) exprfmt(f, n->right->right, 0); @@ -315,6 +360,8 @@ exprfmt(Fmt *f, Node *n, int prec) exprfmt(f, n->left, 7); fmtprint(f, "("); exprlistfmt(f, n->list); + if(n->isddd) + fmtprint(f, "..."); fmtprint(f, ")"); break; @@ -341,7 +388,6 @@ exprfmt(Fmt *f, Node *n, int prec) case OCONV: case OCONVIFACE: case OCONVNOP: - case OCONVSLICE: case OARRAYBYTESTR: case ORUNESTR: if(n->type == T || n->type->sym == S) @@ -355,6 +401,7 @@ exprfmt(Fmt *f, Node *n, int prec) fmtprint(f, ")"); break; + case OAPPEND: case OCAP: case OCLOSE: case OCLOSED: diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c index 09d54b3ee..dca3a5454 100644 --- a/src/cmd/gc/range.c +++ b/src/cmd/gc/range.c @@ -24,6 +24,8 @@ typecheckrange(Node *n) typecheck(&n->right, Erv); if((t = n->right->type) == T) goto out; + if(isptr[t->etype] && isfixedarray(t->type)) + t = t->type; n->type = t; switch(t->etype) { @@ -104,9 +106,6 @@ walkrange(Node *n) a = nod(OCONV, n->right, N); a->type = types[TSTRING]; } - ha = nod(OXXX, N, N); - tempname(ha, a->type); - init = list(init, nod(OAS, ha, a)); v1 = n->list->n; hv1 = N; @@ -116,6 +115,16 @@ walkrange(Node *n) v2 = n->list->next->n; hv2 = N; + if(v2 == N && t->etype == TARRAY) { + // will have just one reference to argument. + // no need to make a potentially expensive copy. + ha = a; + } else { + ha = nod(OXXX, N, N); + tempname(ha, a->type); + init = list(init, nod(OAS, ha, a)); + } + switch(t->etype) { default: fatal("walkrange"); @@ -131,7 +140,7 @@ walkrange(Node *n) init = list(init, nod(OAS, hn, nod(OLEN, ha, N))); if(v2) { hp = nod(OXXX, N, N); - tempname(hp, ptrto(a->type->type)); + tempname(hp, ptrto(n->type->type)); tmp = nod(OINDEX, ha, nodintconst(0)); tmp->etype = 1; // no bounds check init = list(init, nod(OAS, hp, nod(OADDR, tmp, N))); diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c index 467f3615b..b31eb5154 100644 --- a/src/cmd/gc/reflect.c +++ b/src/cmd/gc/reflect.c @@ -100,16 +100,16 @@ lsort(Sig *l, int(*f)(Sig*, Sig*)) * return function type, receiver as first argument (or not). */ Type* -methodfunc(Type *f, int use_receiver) +methodfunc(Type *f, Type *receiver) { NodeList *in, *out; Node *d; Type *t; in = nil; - if(use_receiver) { + if(receiver) { d = nod(ODCLFIELD, N, N); - d->type = getthisx(f)->type->type; + d->type = receiver; in = list(in, d); } for(t=getinargx(f)->type; t; t=t->down) { @@ -183,14 +183,14 @@ methods(Type *t) a = b; a->name = method->name; - a->isym = methodsym(method, it); - a->tsym = methodsym(method, t); - a->type = methodfunc(f->type, 1); - a->mtype = methodfunc(f->type, 0); + a->isym = methodsym(method, it, 1); + a->tsym = methodsym(method, t, 0); + a->type = methodfunc(f->type, t); + a->mtype = methodfunc(f->type, nil); if(!(a->isym->flags & SymSiggen)) { a->isym->flags |= SymSiggen; - if(!eqtype(this, it)) { + if(!eqtype(this, it) || this->width < types[tptr]->width) { if(oldlist == nil) oldlist = pc; // Is okay to call genwrapper here always, @@ -199,9 +199,9 @@ methods(Type *t) // is a pointer adjustment and a JMP. if(isptr[it->etype] && isptr[this->etype] && f->embedded && !isifacemethod(f->type)) - genembedtramp(it, f, a->isym); + genembedtramp(it, f, a->isym, 1); else - genwrapper(it, f, a->isym); + genwrapper(it, f, a->isym, 1); } } @@ -212,9 +212,9 @@ methods(Type *t) oldlist = pc; if(isptr[t->etype] && isptr[this->etype] && f->embedded && !isifacemethod(f->type)) - genembedtramp(t, f, a->tsym); + genembedtramp(t, f, a->tsym, 0); else - genwrapper(t, f, a->tsym); + genwrapper(t, f, a->tsym, 0); } } } @@ -241,22 +241,27 @@ imethods(Type *t) Sig *a, *all, *last; int o; Type *f; + Sym *method, *isym; + Prog *oldlist; all = nil; last = nil; o = 0; + oldlist = nil; for(f=t->type; f; f=f->down) { if(f->etype != TFIELD) fatal("imethods: not field"); if(f->type->etype != TFUNC || f->sym == nil) continue; + method = f->sym; a = mal(sizeof(*a)); - a->name = f->sym->name; - if(!exportname(f->sym->name)) - a->pkg = f->sym->pkg; + a->name = method->name; + if(!exportname(method->name)) + a->pkg = method->pkg; a->mtype = f->type; a->offset = 0; - a->type = methodfunc(f->type, 0); + a->type = methodfunc(f->type, nil); + if(last && sigcmp(last, a) >= 0) fatal("sigcmp vs sortinter %s %s", last->name, a->name); if(last == nil) @@ -264,7 +269,34 @@ imethods(Type *t) else last->link = a; last = a; + + // Compiler can only refer to wrappers for + // named interface types. + if(t->sym == S) + continue; + + // NOTE(rsc): Perhaps an oversight that + // IfaceType.Method is not in the reflect data. + // Generate the method body, so that compiled + // code can refer to it. + isym = methodsym(method, t, 0); + if(!(isym->flags & SymSiggen)) { + isym->flags |= SymSiggen; + if(oldlist == nil) + oldlist = pc; + genwrapper(t, f, isym, 0); + } } + + if(oldlist) { + // old list ended with AEND; change to ANOP + // so that the trampolines that follow can be found. + nopout(oldlist); + + // start new data list + newplist(); + } + return all; } @@ -545,7 +577,6 @@ dcommontype(Sym *s, int ot, Type *t) { int i; Sym *s1; - Type *elem; char *p; dowidth(t); @@ -566,26 +597,23 @@ dcommontype(Sym *s, int ot, Type *t) // alg uint8; // align uint8; // fieldAlign uint8; + // kind uint8; // string *string; // *nameInfo; // } ot = duintptr(s, ot, t->width); ot = duint32(s, ot, typehash(t)); ot = duint8(s, ot, algtype(t)); - elem = t; - while(elem->etype == TARRAY && elem->bound >= 0) - elem = elem->type; - i = elem->width; - if(i > maxround) - i = maxround; - ot = duint8(s, ot, i); // align - ot = duint8(s, ot, i); // fieldAlign + ot = duint8(s, ot, t->align); // align + ot = duint8(s, ot, t->align); // fieldAlign i = kinds[t->etype]; if(t->etype == TARRAY && t->bound < 0) i = KindSlice; + if(isptr[t->etype] && t->type->etype == TANY) + i = KindUnsafePointer; if(!haspointers(t)) i |= KindNoPointers; - ot = duint8(s, ot, i); + ot = duint8(s, ot, i); // kind longsymnames = 1; p = smprint("%-T", t); longsymnames = 0; @@ -643,11 +671,10 @@ typename(Type *t) static Sym* dtypesym(Type *t) { - int ot, n, isddd; + int ot, n, isddd, dupok; Sym *s, *s1, *s2; Sig *a, *m; - Type *t1; - Sym *tsym; + Type *t1, *tbase; if(isideal(t)) fatal("dtypesym %T", t); @@ -660,30 +687,22 @@ dtypesym(Type *t) // special case (look for runtime below): // when compiling package runtime, // emit the type structures for int, float, etc. - t1 = T; - if(isptr[t->etype]) - t1 = t->type; - tsym = S; - if(t1) - tsym = t1->sym; - else - tsym = t->sym; + tbase = t; + if(isptr[t->etype] && t->sym == S && t->type->sym != S) + tbase = t->type; + dupok = tbase->sym == S; if(compiling_runtime) { - if(t == types[t->etype]) + if(tbase == types[tbase->etype]) // int, float, etc goto ok; - if(t1 && t1 == types[t1->etype]) - goto ok; - if(t1 && t1->etype == tptr && t1->type->etype == TANY) + if(tbase->etype == tptr && tbase->type->etype == TANY) // unsafe.Pointer goto ok; } - // named types from other files are defined in those files - if(t->sym && !t->local) - return s; - if(!t->sym && t1 && t1->sym && !t1->local) + // named types from other files are defined only by those files + if(tbase->sym && !tbase->local) return s; - if(isforw[t->etype] || (t1 && isforw[t1->etype])) + if(isforw[tbase->etype]) return s; ok: @@ -778,6 +797,7 @@ ok: case TPTR32: case TPTR64: if(t->type->etype == TANY) { + // ../../pkg/runtime/type.go:/UnsafePointerType ot = dcommontype(s, ot, t); break; } @@ -818,7 +838,7 @@ ok: break; } - ggloblsym(s, ot, tsym == nil); + ggloblsym(s, ot, dupok); return s; } @@ -846,7 +866,7 @@ dumptypestructs(void) continue; t = n->type; dtypesym(t); - if(t->sym && !isptr[t->etype]) + if(t->sym) dtypesym(ptrto(t)); } diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index 5783faafd..174bc050e 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -10,7 +10,7 @@ package PACKAGE // emitted by compiler, not referred to by go programs -func mal(int32) *any +func new(int32) *any func panicindex() func panicslice() func throwreturn() @@ -31,13 +31,18 @@ func printeface(any) func printslice(any) func printnl() func printsp() -func printf() +func goprintf() + +// filled in by compiler: int n, string, string, ... +func concatstring() + +// filled in by compiler: Type*, int n, Slice, ... +func append() +func appendslice(typ *byte, x any, y []any) any -func catstring(string, string) string func cmpstring(string, string) int func slicestring(string, int, int) string func slicestring1(string, int) string -func indexstring(string, int) byte func intstring(int64) string func slicebytetostring([]byte) string func sliceinttostring([]int) string @@ -46,6 +51,7 @@ func stringtosliceint(string) []int func stringiter(string, int) int func stringiter2(string, int) (retk int, retv int) func slicecopy(to any, fr any, wid uint32) int +func slicestringcopy(to any, fr any) int // interface conversions func convI2E(elem any) (ret any) @@ -99,9 +105,9 @@ func selectdefault(sel *byte) (selected bool) func selectgo(sel *byte) func makeslice(typ *byte, nel int64, cap int64) (ary []any) -func sliceslice1(old []any, lb int, width int) (ary []any) -func sliceslice(old []any, lb int, hb int, width int) (ary []any) -func slicearray(old *any, nel int, lb int, hb int, width int) (ary []any) +func sliceslice1(old []any, lb uint64, width uint64) (ary []any) +func sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any) +func slicearray(old *any, nel uint64, lb uint64, hb uint64, width uint64) (ary []any) func closure() // has args, but compiler fills in @@ -111,6 +117,8 @@ func uint64div(uint64, uint64) uint64 func int64mod(int64, int64) int64 func uint64mod(uint64, uint64) uint64 func float64toint64(float64) int64 +func float64touint64(float64) uint64 func int64tofloat64(int64) float64 +func uint64tofloat64(uint64) float64 func complex128div(num complex128, den complex128) (quo complex128) diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c index 9cba01fa5..1a3771311 100644 --- a/src/cmd/gc/select.c +++ b/src/cmd/gc/select.c @@ -41,11 +41,18 @@ typecheckselect(Node *sel) setlineno(n); switch(n->op) { default: - yyerror("select case must be receive, send or assign recv");; + yyerror("select case must be receive, send or assign recv"); break; case OAS: // convert x = <-c into OSELRECV(x, c) + // assignment might have introduced a + // conversion. throw it away. + // it will come back when the select code + // gets generated, because it always assigns + // through a temporary. + if((n->right->op == OCONVNOP || n->right->op == OCONVIFACE) && n->right->implicit) + n->right = n->right->left; if(n->right->op != ORECV) { yyerror("select assignment must have receive on right hand side"); break; @@ -68,8 +75,6 @@ typecheckselect(Node *sel) typechecklist(ncase->nbody, Etop); } sel->xoffset = count; - if(count == 0) - yyerror("empty select"); lineno = lno; } @@ -91,7 +96,7 @@ walkselect(Node *sel) typecheck(&r, Etop); init = list(init, r); - if(sel->list == nil) + if(sel->list == nil && sel->xoffset != 0) fatal("double walkselect"); // already rewrote // register cases diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c index 5ac14a537..19ee3327b 100644 --- a/src/cmd/gc/sinit.c +++ b/src/cmd/gc/sinit.c @@ -92,15 +92,9 @@ init1(Node *n, NodeList **out) break; case OAS2FUNC: - if(n->defn->initorder) - break; - n->defn->initorder = 1; - for(l=n->defn->rlist; l; l=l->next) - init1(l->n, out); - *out = list(*out, n->defn); - break; - case OAS2MAPR: + case OAS2DOTTYPE: + case OAS2RECV: if(n->defn->initorder) break; n->defn->initorder = 1; @@ -190,6 +184,20 @@ static void arraylit(int ctxt, int pass, Node *n, Node *var, NodeList **init); static void slicelit(int ctxt, Node *n, Node *var, NodeList **init); static void maplit(int ctxt, Node *n, Node *var, NodeList **init); +static Node* +staticname(Type *t, int ctxt) +{ + Node *n; + + snprint(namebuf, sizeof(namebuf), "statictmp_%.4d", statuniqgen); + statuniqgen++; + n = newname(lookup(namebuf)); + if(!ctxt) + n->readonly = 1; + addvar(n, t, PEXTERN); + return n; +} + static int isliteral(Node *n) { @@ -402,12 +410,12 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) if(ctxt != 0) { // put everything into static array - vstat = staticname(t); + vstat = staticname(t, ctxt); arraylit(ctxt, 1, n, vstat, init); arraylit(ctxt, 2, n, vstat, init); // copy static to slice - a = nod(OADDR, vstat, N); + a = nod(OSLICE, vstat, nod(OKEY, N, N)); a = nod(OAS, var, a); typecheck(&a, Etop); a->dodata = 2; @@ -439,7 +447,7 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) vstat = N; mode = getdyn(n, 1); if(mode & MODECONST) { - vstat = staticname(t); + vstat = staticname(t, ctxt); arraylit(ctxt, 1, n, vstat, init); } @@ -465,7 +473,7 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) } // make slice out of heap (5) - a = nod(OAS, var, vauto); + a = nod(OAS, var, nod(OSLICE, vauto, nod(OKEY, N, N))); typecheck(&a, Etop); walkexpr(&a, init); *init = list(*init, a); @@ -514,6 +522,8 @@ maplit(int ctxt, Node *n, Node *var, NodeList **init) Node *vstat, *index, *value; Sym *syma, *symb; +ctxt = 0; + // make the map var nerr = nerrors; @@ -566,7 +576,7 @@ maplit(int ctxt, Node *n, Node *var, NodeList **init) dowidth(t); // make and initialize static array - vstat = staticname(t); + vstat = staticname(t, ctxt); b = 0; for(l=n->list; l; l=l->next) { r = l->n; @@ -675,7 +685,7 @@ anylit(int ctxt, Node *n, Node *var, NodeList **init) if(ctxt == 0) { // lay out static data - vstat = staticname(t); + vstat = staticname(t, ctxt); structlit(1, 1, n, vstat, init); // copy static to var @@ -715,7 +725,7 @@ anylit(int ctxt, Node *n, Node *var, NodeList **init) if(ctxt == 0) { // lay out static data - vstat = staticname(t); + vstat = staticname(t, ctxt); arraylit(1, 1, n, vstat, init); // copy static to automatic @@ -769,8 +779,8 @@ oaslit(Node *n, NodeList **init) // implies generated data executed // exactly once and not subject to races. ctxt = 0; - if(n->dodata == 1) - ctxt = 1; +// if(n->dodata == 1) +// ctxt = 1; switch(n->right->op) { default: @@ -870,8 +880,18 @@ gen_as_init(Node *n) default: goto no; - case OCONVSLICE: - goto slice; + case OCONVNOP: + nr = nr->left; + if(nr == N || nr->op != OSLICEARR) + goto no; + // fall through + + case OSLICEARR: + if(nr->right->op == OKEY && nr->right->left == N && nr->right->right == N) { + nr = nr->left; + goto slice; + } + goto no; case OLITERAL: break; @@ -920,7 +940,7 @@ yes: slice: gused(N); // in case the data is the dest of a goto - nr = n->right->left; + nl = nr; if(nr == N || nr->op != OADDR) goto no; nr = nr->left; @@ -932,7 +952,7 @@ slice: goto no; nam.xoffset += Array_array; - gdata(&nam, n->right->left, types[tptr]->width); + gdata(&nam, nl, types[tptr]->width); nam.xoffset += Array_nel-Array_array; nodconst(&nod1, types[TINT32], nr->type->bound); diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 65b56dee6..3c4501096 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -228,11 +228,15 @@ linehist(char *file, int32 off, int relative) if(debug['i']) { if(file != nil) { if(off < 0) - print("pragma %s at line %L\n", file, lineno); + print("pragma %s", file); else - print("import %s at line %L\n", file, lineno); + if(off > 0) + print("line %s", file); + else + print("import %s", file); } else - print("end of import at line %L\n", lineno); + print("end of import"); + print(" at line %L\n", lexlineno); } if(off < 0 && file[0] != '/' && !relative) { @@ -267,7 +271,6 @@ setlineno(Node *n) case OTYPE: case OPACK: case OLITERAL: - case ONONAME: break; default: lineno = n->lineno; @@ -360,6 +363,8 @@ importdot(Pkg *opkg, Node *pack) for(s = hash[h]; s != S; s = s->link) { if(s->pkg != opkg) continue; + if(s->def == N) + continue; if(!exportname(s->name) || utfrune(s->name, 0xb7)) // 0xb7 = center dot continue; s1 = lookup(s->name); @@ -473,9 +478,12 @@ algtype(Type *t) int a; if(issimple[t->etype] || isptr[t->etype] || iscomplex[t->etype] || - t->etype == TCHAN || t->etype == TFUNC || t->etype == TMAP) - a = AMEM; // just bytes (int, ptr, etc) - else if(t->etype == TSTRING) + t->etype == TCHAN || t->etype == TFUNC || t->etype == TMAP) { + if(t->width == widthptr) + a = AMEMWORD; + else + a = AMEM; // just bytes (int, ptr, etc) + } else if(t->etype == TSTRING) a = ASTRING; // string else if(isnilinter(t)) a = ANILINTER; // nil interface @@ -584,6 +592,21 @@ nodintconst(int64 v) return c; } +Node* +nodfltconst(Mpflt* v) +{ + Node *c; + + c = nod(OLITERAL, N, N); + c->addable = 1; + c->val.u.fval = mal(sizeof(*c->val.u.fval)); + mpmovefltflt(c->val.u.fval, v); + c->val.ctype = CTFLT; + c->type = types[TIDEAL]; + ullmancalc(c); + return c; +} + void nodconst(Node *n, Type *t, int64 v) { @@ -804,6 +827,7 @@ goopnames[] = [OANDAND] = "&&", [OANDNOT] = "&^", [OAND] = "&", + [OAPPEND] = "append", [OAS] = "=", [OAS2] = "=", [OBREAK] = "break", @@ -894,12 +918,21 @@ Lconv(Fmt *fp) if(lno < h->line) break; if(h->name) { - if(n < HISTSZ) { /* beginning of file */ - a[n].incl = h; - a[n].idel = h->line; - a[n].line = 0; + if(h->offset > 0) { + // #line directive + if(n > 0 && n < HISTSZ) { + a[n-1].line = h; + a[n-1].ldel = h->line - h->offset + 1; + } + } else { + // beginning of file + if(n < HISTSZ) { + a[n].incl = h; + a[n].idel = h->line; + a[n].line = 0; + } + n++; } - n++; continue; } n--; @@ -919,14 +952,16 @@ Lconv(Fmt *fp) break; fmtprint(fp, " "); } + if(debug['L']) + fmtprint(fp, "%s/", pathname); if(a[i].line) - fmtprint(fp, "%s:%ld[%s:%ld]", + fmtprint(fp, "%s:%d[%s:%d]", a[i].line->name, lno-a[i].ldel+1, a[i].incl->name, lno-a[i].idel+1); else - fmtprint(fp, "%s:%ld", + fmtprint(fp, "%s:%d", a[i].incl->name, lno-a[i].idel+1); - lno = a[i].incl->line - 1; /* now print out start of this file */ + lno = a[i].incl->line - 1; // now print out start of this file } if(n == 0) fmtprint(fp, "<epoch>"); @@ -1003,10 +1038,10 @@ Jconv(Fmt *fp) fmtprint(fp, " a(%d)", n->addable); if(n->vargen != 0) - fmtprint(fp, " g(%ld)", n->vargen); + fmtprint(fp, " g(%d)", n->vargen); if(n->lineno != 0) - fmtprint(fp, " l(%ld)", n->lineno); + fmtprint(fp, " l(%d)", n->lineno); if(n->xoffset != 0) fmtprint(fp, " x(%lld)", n->xoffset); @@ -1029,6 +1064,9 @@ Jconv(Fmt *fp) if(n->isddd != 0) fmtprint(fp, " isddd(%d)", n->isddd); + if(n->implicit != 0) + fmtprint(fp, " implicit(%d)", n->implicit); + return 0; } @@ -1146,7 +1184,7 @@ Tpretty(Fmt *fp, Type *t) case Csend: return fmtprint(fp, "chan<- %T", t->type); } - if(t->type != T && t->type->etype == TCHAN && t->type->chan == Crecv) + if(t->type != T && t->type->etype == TCHAN && t->type->sym == S && t->type->chan == Crecv) return fmtprint(fp, "chan (%T)", t->type); return fmtprint(fp, "chan %T", t->type); @@ -1256,7 +1294,7 @@ Tpretty(Fmt *fp, Type *t) fmtprint(fp, "...%T", t->type->type); else fmtprint(fp, "%T", t->type); - if(t->note) { + if(t->note) { fmtprint(fp, " "); if(exporting) fmtprint(fp, ":"); @@ -1360,7 +1398,7 @@ Tconv(Fmt *fp) case TARRAY: if(t->bound >= 0) - fmtprint(fp, "[%ld]%T", t->bound, t->type); + fmtprint(fp, "[%d]%T", t->bound, t->type); else fmtprint(fp, "[]%T", t->type); break; @@ -1414,7 +1452,7 @@ Nconv(Fmt *fp) fmtprint(fp, "%O%J", n->op, n); break; } - fmtprint(fp, "%O-%S G%ld%J", n->op, + fmtprint(fp, "%O-%S G%d%J", n->op, n->sym, n->vargen, n); goto ptyp; @@ -1460,7 +1498,7 @@ Nconv(Fmt *fp) break; } if(n->sym != S) - fmtprint(fp, " %S G%ld", n->sym, n->vargen); + fmtprint(fp, " %S G%d", n->sym, n->vargen); ptyp: if(n->type != T) @@ -1909,13 +1947,6 @@ assignop(Type *src, Type *dst, char **why) // 7. Any typed value can be assigned to the blank identifier. if(dst->etype == TBLANK) return OCONVNOP; - - // 8. Array to slice. - // TODO(rsc): Not for long. - if(!src->sym || !dst->sym) - if(isptr[src->etype] && isfixedarray(src->type) && isslice(dst)) - if(eqtype(src->type->type, dst->type)) - return OCONVSLICE; return 0; } @@ -2037,6 +2068,7 @@ assignconv(Node *n, Type *t, char *context) r = nod(op, n, N); r->type = t; r->typecheck = 1; + r->implicit = 1; return r; } @@ -2292,7 +2324,8 @@ ptrto(Type *t) fatal("ptrto: nil"); t1 = typ(tptr); t1->type = t; - t1->width = types[tptr]->width; + t1->width = widthptr; + t1->align = widthptr; return t1; } @@ -2320,7 +2353,7 @@ frame(int context) case ONAME: if(flag) print("--- %s frame ---\n", p); - print("%O %S G%ld %T\n", n->op, n->sym, n->vargen, n->type); + print("%O %S G%d %T\n", n->op, n->sym, n->vargen, n->type); flag = 0; break; @@ -2584,20 +2617,8 @@ brrev(int a) return a; } -Node* -staticname(Type *t) -{ - Node *n; - - snprint(namebuf, sizeof(namebuf), "statictmp_%.4d", statuniqgen); - statuniqgen++; - n = newname(lookup(namebuf)); - addvar(n, t, PEXTERN); - return n; -} - /* - * return side effect-free appending side effects to init. + * return side effect-free n, appending side effects to init. * result is assignable if n is. */ Node* @@ -2654,6 +2675,24 @@ safeexpr(Node *n, NodeList **init) // make a copy; must not be used as an lvalue if(islvalue(n)) fatal("missing lvalue case in safeexpr: %N", n); + return cheapexpr(n, init); +} + +/* + * return side-effect free and cheap n, appending side effects to init. + * result may not be assignable. + */ +Node* +cheapexpr(Node *n, NodeList **init) +{ + Node *a, *l; + + switch(n->op) { + case ONAME: + case OLITERAL: + return n; + } + l = nod(OXXX, N, N); tempname(l, n->type); a = nod(OAS, l, n); @@ -2939,6 +2978,11 @@ expandmeth(Sym *s, Type *t) if(t == T || t->xmethod != nil) return; + // mark top-level method symbols + // so that expand1 doesn't consider them. + for(f=t->method; f != nil; f=f->down) + f->sym->flags |= SymUniq; + // generate all reachable methods slist = nil; expand1(t, nelem(dotlist)-1, 0); @@ -2958,6 +3002,9 @@ expandmeth(Sym *s, Type *t) } } + for(f=t->method; f != nil; f=f->down) + f->sym->flags &= ~SymUniq; + t->xmethod = t->method; for(sl=slist; sl!=nil; sl=sl->link) { if(sl->good) { @@ -2969,7 +3016,6 @@ expandmeth(Sym *s, Type *t) f->embedded = 2; f->down = t->xmethod; t->xmethod = f; - } } } @@ -3031,15 +3077,19 @@ structargs(Type **tl, int mustname) * newnam - the eventual mangled name of this function */ void -genwrapper(Type *rcvr, Type *method, Sym *newnam) +genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) { - Node *this, *fn, *call, *n, *t; + Node *this, *fn, *call, *n, *t, *pad; NodeList *l, *args, *in, *out; + Type *tpad; + int isddd; if(debug['r']) print("genwrapper rcvrtype=%T method=%T newnam=%S\n", rcvr, method, newnam); + lineno = 1; // less confusing than end of input + dclcontext = PEXTERN; markdcl(); @@ -3050,20 +3100,37 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam) fn = nod(ODCLFUNC, N, N); fn->nname = newname(newnam); - t = nod(OTFUNC, this, N); - t->list = in; + t = nod(OTFUNC, N, N); + l = list1(this); + if(iface && rcvr->width < types[tptr]->width) { + // Building method for interface table and receiver + // is smaller than the single pointer-sized word + // that the interface call will pass in. + // Add a dummy padding argument after the + // receiver to make up the difference. + tpad = typ(TARRAY); + tpad->type = types[TUINT8]; + tpad->bound = types[tptr]->width - rcvr->width; + pad = nod(ODCLFIELD, newname(lookup(".pad")), typenod(tpad)); + l = list(l, pad); + } + t->list = concat(l, in); t->rlist = out; fn->nname->ntype = t; funchdr(fn); // arg list args = nil; - for(l=in; l; l=l->next) + isddd = 0; + for(l=in; l; l=l->next) { args = list(args, l->n->left); + isddd = l->n->left->isddd; + } // generate call call = nod(OCALL, adddot(nod(OXDOT, this->left, newname(method->sym))), N); call->list = args; + call->isddd = isddd; fn->nbody = list1(call); if(method->type->outtuple > 0) { n = nod(ORETURN, N, N); @@ -3563,10 +3630,11 @@ umagic(Magic *m) Sym* ngotype(Node *n) { - if(n->sym != S && strncmp(n->sym->name, "autotmp_", 8) != 0) - if(n->type->etype != TFUNC || n->type->thistuple == 0) - if(n->type->etype != TSTRUCT || n->type->funarg == 0) - return typename(n->type)->left->sym; + if(n->sym != S && n->realtype != T) + if(strncmp(n->sym->name, "autotmp_", 8) != 0) + if(strncmp(n->sym->name, "statictmp_", 8) != 0) + return typename(n->realtype)->left->sym; + return S; } @@ -3640,4 +3708,3 @@ strlit(char *s) t->len = strlen(s); return t; } - diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 457b82b4c..ca114d47c 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -17,7 +17,8 @@ static void implicitstar(Node**); static int onearg(Node*, char*, ...); static int twoarg(Node*); static int lookdot(Node*, Type*, int); -static void typecheckaste(int, Type*, NodeList*, char*); +static int looktypedot(Node*, Type*, int); +static void typecheckaste(int, int, Type*, NodeList*, char*); static Type* lookdot1(Sym *s, Type *t, Type *f, int); static int nokeys(NodeList*); static void typecheckcomplit(Node**); @@ -63,7 +64,7 @@ typechecklist(NodeList *l, int top) Node* typecheck(Node **np, int top) { - int et, op, ptr; + int et, aop, op, ptr; Node *n, *l, *r; NodeList *args; int lno, ok, ntop; @@ -79,7 +80,7 @@ typecheck(Node **np, int top) n = *np; if(n == N) return N; - + // Resolve definition of name and value of iota lazily. n = resolve(n); *np = n; @@ -111,6 +112,7 @@ typecheck(Node **np, int top) goto error; } walkdef(n); + n->realtype = n->type; if(n->op == ONONAME) goto error; } @@ -169,6 +171,16 @@ reswitch: goto error; break; + case OTPAREN: + ok |= Etype; + l = typecheck(&n->left, Etype); + if(l->type == T) + goto error; + n->op = OTYPE; + n->type = l->type; + n->left = N; + break; + case OTARRAY: ok |= Etype; t = typ(TARRAY); @@ -251,7 +263,6 @@ reswitch: n->type = dostruct(n->list, TINTER); if(n->type == T) goto error; - n->type = sortinter(n->type); break; case OTFUNC: @@ -340,6 +351,26 @@ reswitch: et = t->etype; if(et == TIDEAL) et = TINT; + if(iscmp[n->op] && t->etype != TIDEAL && !eqtype(l->type, r->type)) { + // comparison is okay as long as one side is + // assignable to the other. convert so they have + // the same type. (the only conversion that isn't + // a no-op is concrete == interface.) + if(r->type->etype != TBLANK && (aop = assignop(l->type, r->type, nil)) != 0) { + l = nod(aop, l, N); + l->type = r->type; + l->typecheck = 1; + n->left = l; + t = l->type; + } else if(l->type->etype != TBLANK && (aop = assignop(r->type, l->type, nil)) != 0) { + r = nod(aop, r, N); + r->type = l->type; + r->typecheck = 1; + n->right = r; + t = r->type; + } + et = t->etype; + } if(t->etype != TIDEAL && !eqtype(l->type, r->type)) { badbinary: defaultlit2(&l, &r, 1); @@ -393,7 +424,7 @@ reswitch: n->right = r; t = r->type; if(!isint[t->etype] || issigned[t->etype]) { - yyerror("invalid operation: %#N (shift count type %T)", n, r->type); + yyerror("invalid operation: %#N (shift count type %T, must be unsigned integer)", n, r->type); goto error; } t = l->type; @@ -467,41 +498,42 @@ reswitch: yyerror("rhs of . must be a name"); // impossible goto error; } - if(isptr[t->etype]) { - t = t->type; - if(t == T) - goto error; - n->op = ODOTPTR; - checkwidth(t); - } sym = n->right->sym; - if(!lookdot(n, t, 0)) { - if(lookdot(n, t, 1)) - yyerror("%#N undefined (cannot refer to unexported field %S)", n, n->right->sym); - else - yyerror("%#N undefined (type %T has no field %S)", n, t, n->right->sym); - goto error; - } if(l->op == OTYPE) { - if(n->type->etype != TFUNC || n->type->thistuple != 1) { - yyerror("type %T has no method %hS", n->left->type, sym); - n->type = T; + if(!looktypedot(n, t, 0)) { + if(looktypedot(n, t, 1)) + yyerror("%#N undefined (cannot refer to unexported method %S)", n, n->right->sym); + else + yyerror("%#N undefined (type %T has no method %S)", n, t, n->right->sym); goto error; } - if(t->etype == TINTER) { - yyerror("method expression on interface not implemented"); + if(n->type->etype != TFUNC || n->type->thistuple != 1) { + yyerror("type %T has no method %hS", n->left->type, sym); n->type = T; goto error; } n->op = ONAME; - n->sym = methodsym(sym, l->type); - n->type = methodfunc(n->type, 1); + n->sym = methodsym(sym, l->type, 0); + n->type = methodfunc(n->type, l->type); n->xoffset = 0; - getinargx(n->type)->type->type = l->type; // fix up receiver n->class = PFUNC; ok = Erv; goto ret; } + if(isptr[t->etype] && t->type->etype != TINTER) { + t = t->type; + if(t == T) + goto error; + n->op = ODOTPTR; + checkwidth(t); + } + if(!lookdot(n, t, 0)) { + if(lookdot(n, t, 1)) + yyerror("%#N undefined (cannot refer to unexported field or method %S)", n, n->right->sym); + else + yyerror("%#N undefined (type %T has no field or method %S)", n, t, n->right->sym); + goto error; + } switch(n->op) { case ODOTINTER: case ODOTMETH: @@ -561,7 +593,7 @@ reswitch: goto error; case TARRAY: - defaultlit(&n->right, types[TUINT]); + defaultlit(&n->right, T); if(n->right->type != T && !isint[n->right->type->etype]) yyerror("non-integer array index %#N", n->right); n->type = t->type; @@ -581,7 +613,6 @@ reswitch: if(n->right->type != T && !isint[n->right->type->etype]) yyerror("non-integer string index %#N", n->right); n->type = types[TUINT8]; - n->op = OINDEXSTR; break; } goto ret; @@ -612,6 +643,10 @@ reswitch: l = n->left; if((t = l->type) == T) goto error; + if(t->etype != TCHAN) { + yyerror("invalid operation: %#N (send to non-chan type %T)", n, t); + goto error; + } if(!(t->chan & Csend)) { yyerror("invalid operation: %#N (send to receive-only type %T)", n, t); goto error; @@ -620,6 +655,7 @@ reswitch: r = n->right; if((t = r->type) == T) goto error; + r = assignconv(r, l->type->type, "send"); // TODO: more aggressive n->etype = 0; n->type = T; @@ -635,24 +671,19 @@ reswitch: typecheck(&n->right->left, Erv); typecheck(&n->right->right, Erv); defaultlit(&n->left, T); - defaultlit(&n->right->left, types[TUINT]); - defaultlit(&n->right->right, types[TUINT]); + defaultlit(&n->right->left, T); + defaultlit(&n->right->right, T); if(isfixedarray(n->left->type)) { - // Insert explicit & before fixed array - // so that back end knows to move to heap. n->left = nod(OADDR, n->left, N); typecheck(&n->left, top); } - implicitstar(&n->left); - if(n->right->left == N) { - yyerror("missing slice bounds?"); - goto error; - } - if((t = n->right->left->type) == T) - goto error; - if(!isint[t->etype]) { - yyerror("invalid slice index %#N (type %T)", n->right->left, t); - goto error; + if(n->right->left != N) { + if((t = n->right->left->type) == T) + goto error; + if(!isint[t->etype]) { + yyerror("invalid slice index %#N (type %T)", n->right->left, t); + goto error; + } } if(n->right->right != N) { if((t = n->right->right->type) == T) @@ -670,9 +701,9 @@ reswitch: n->op = OSLICESTR; goto ret; } - if(isfixedarray(t)) { + if(isptr[t->etype] && isfixedarray(t->type)) { n->type = typ(TARRAY); - n->type->type = t->type; + n->type->type = t->type->type; n->type->bound = -1; dowidth(n->type); n->op = OSLICEARR; @@ -690,13 +721,17 @@ reswitch: */ case OCALL: l = n->left; - if(l->op == ONAME && (r = unsafenmagic(l, n->list)) != N) { + if(l->op == ONAME && (r = unsafenmagic(n)) != N) { + if(n->isddd) + yyerror("invalid use of ... with builtin %#N", l); n = r; goto reswitch; } typecheck(&n->left, Erv | Etype | Ecall); l = n->left; if(l->op == ONAME && l->etype != 0) { + if(n->isddd && l->etype != OAPPEND) + yyerror("invalid use of ... with builtin %#N", l); // builtin: OLEN, OCAP, etc. n->op = l->etype; n->left = n->right; @@ -706,6 +741,8 @@ reswitch: defaultlit(&n->left, T); l = n->left; if(l->op == OTYPE) { + if(n->isddd) + yyerror("invalid use of ... in type conversion", l); // pick off before type-checking arguments ok |= Erv; // turn CALL(type, arg) into CONV(arg) w/ type @@ -732,7 +769,7 @@ reswitch: case ODOTMETH: n->op = OCALLMETH; - typecheckaste(OCALL, getthisx(t), list1(l->left), "method receiver"); + typecheckaste(OCALL, 0, getthisx(t), list1(l->left), "method receiver"); break; default: @@ -743,7 +780,7 @@ reswitch: } break; } - typecheckaste(OCALL, getinargx(t), n->list, "function argument"); + typecheckaste(OCALL, n->isddd, getinargx(t), n->list, "function argument"); ok |= Etop; if(t->outtuple == 0) goto ret; @@ -792,6 +829,12 @@ reswitch: case OIMAG: if(!iscomplex[t->etype]) goto badcall1; + if(isconst(l, CTCPLX)){ + if(n->op == OREAL) + n = nodfltconst(&l->val.u.cval->real); + else + n = nodfltconst(&l->val.u.cval->imag); + } n->type = types[cplxsubtype(t->etype)]; goto ret; } @@ -802,7 +845,7 @@ reswitch: nodconst(n, types[TINT], l->val.u.sval->len); break; case TARRAY: - if(t->bound >= 0) + if(t->bound >= 0 && l->op == ONAME) nodconst(n, types[TINT], t->bound); break; } @@ -868,6 +911,40 @@ reswitch: ok |= Etop; goto ret; + case OAPPEND: + ok |= Erv; + args = n->list; + if(args == nil) { + yyerror("missing arguments to append"); + goto error; + } + typechecklist(args, Erv); + if((t = args->n->type) == T) + goto error; + n->type = t; + if(!isslice(t)) { + yyerror("first argument to append must be slice; have %lT", t); + goto error; + } + if(n->isddd) { + if(args->next == nil) { + yyerror("cannot use ... on first argument to append"); + goto error; + } + if(args->next->next != nil) { + yyerror("too many arguments to append"); + goto error; + } + args->next->n = assignconv(args->next->n, t->orig, "append"); + goto ret; + } + for(args=args->next; args != nil; args=args->next) { + if(args->n->type == T) + continue; + args->n = assignconv(args->n, t->type, "append"); + } + goto ret; + case OCOPY: ok |= Etop|Erv; args = n->list; @@ -888,13 +965,18 @@ reswitch: goto error; defaultlit(&n->left, T); defaultlit(&n->right, T); + + // copy([]byte, string) + if(isslice(n->left->type) && n->left->type->type == types[TUINT8] && n->right->type->etype == TSTRING) + goto ret; + if(!isslice(n->left->type) || !isslice(n->right->type)) { if(!isslice(n->left->type) && !isslice(n->right->type)) yyerror("arguments to copy must be slices; have %lT, %lT", n->left->type, n->right->type); else if(!isslice(n->left->type)) yyerror("first argument to copy should be slice; have %lT", n->left->type); else - yyerror("second argument to copy should be slice; have %lT", n->right->type); + yyerror("second argument to copy should be slice or string; have %lT", n->right->type); goto error; } if(!eqtype(n->left->type->type, n->right->type->type)) { @@ -1048,7 +1130,7 @@ reswitch: case OPRINT: case OPRINTN: ok |= Etop; - typechecklist(n->list, Erv); + typechecklist(n->list, Erv | Eindir); // Eindir: address does not escape goto ret; case OPANIC: @@ -1056,7 +1138,7 @@ reswitch: if(onearg(n, "panic") < 0) goto error; typecheck(&n->left, Erv); - defaultlit(&n->left, T); + defaultlit(&n->left, types[TINTER]); if(n->left->type == T) goto error; goto ret; @@ -1129,9 +1211,13 @@ reswitch: case ORETURN: ok |= Etop; typechecklist(n->list, Erv | Efnstruct); + if(curfn == N) { + yyerror("return outside function"); + goto error; + } if(curfn->type->outnamed && n->list == nil) goto ret; - typecheckaste(ORETURN, getoutargx(curfn->type), n->list, "return argument"); + typecheckaste(ORETURN, 0, getoutargx(curfn->type), n->list, "return argument"); goto ret; case OSELECT: @@ -1149,11 +1235,6 @@ reswitch: typecheckrange(n); goto ret; - case OTYPECASE: - ok |= Etop | Erv; - typecheck(&n->left, Erv); - goto ret; - case OTYPESW: yyerror("use of .(type) outside type switch"); goto error; @@ -1218,7 +1299,7 @@ ret: goto error; } if((ok & Ecall) && !(top & Ecall)) { - yyerror("must call %#N", n); + yyerror("method %#N is not an expression, must be called", n); goto error; } // TODO(rsc): simplify @@ -1257,7 +1338,7 @@ implicitstar(Node **nn) Type *t; Node *n; - // insert implicit * if needed + // insert implicit * if needed for fixed array n = *nn; t = n->type; if(t == T || !isptr[t->etype]) @@ -1347,6 +1428,57 @@ lookdot1(Sym *s, Type *t, Type *f, int dostrcmp) } static int +looktypedot(Node *n, Type *t, int dostrcmp) +{ + Type *f1, *f2, *tt; + Sym *s; + + s = n->right->sym; + + if(t->etype == TINTER) { + f1 = lookdot1(s, t, t->type, dostrcmp); + if(f1 == T) + return 0; + + if(f1->width == BADWIDTH) + fatal("lookdot badwidth %T %p", f1, f1); + n->right = methodname(n->right, t); + n->xoffset = f1->width; + n->type = f1->type; + n->op = ODOTINTER; + return 1; + } + + tt = t; + if(t->sym == S && isptr[t->etype]) + tt = t->type; + + f2 = methtype(tt); + if(f2 == T) + return 0; + + expandmeth(f2->sym, f2); + f2 = lookdot1(s, f2, f2->xmethod, dostrcmp); + if(f2 == T) + return 0; + + // disallow T.m if m requires *T receiver + if(isptr[getthisx(f2->type)->type->type->etype] + && !isptr[t->etype] + && f2->embedded != 2 + && !isifacemethod(f2->type)) { + yyerror("invalid method expression %#N (needs pointer receiver: (*%T).%s)", n, t, f2->sym->name); + return 0; + } + + n->right = methodname(n->right, t); + n->xoffset = f2->width; + n->type = f2->type; + n->op = ODOTMETH; + return 1; +} + +static int lookdot(Node *n, Type *t, int dostrcmp) { Type *f1, *f2, *tt, *rcvr; @@ -1359,9 +1491,15 @@ lookdot(Node *n, Type *t, int dostrcmp) if(t->etype == TSTRUCT || t->etype == TINTER) f1 = lookdot1(s, t, t->type, dostrcmp); - f2 = methtype(n->left->type); - if(f2 != T) - f2 = lookdot1(s, f2, f2->method, dostrcmp); + f2 = T; + if(n->left->type == t || n->left->type->sym == S) { + f2 = methtype(t); + if(f2 != T) { + // Use f2->method, not f2->xmethod: adddot has + // already inserted all the necessary embedded dots. + f2 = lookdot1(s, f2, f2->method, dostrcmp); + } + } if(f1 != T) { if(f2 != T) @@ -1385,14 +1523,16 @@ lookdot(Node *n, Type *t, int dostrcmp) tt = n->left->type; dowidth(tt); rcvr = getthisx(f2->type)->type->type; - if(n->left->op != OTYPE && !eqtype(rcvr, tt)) { + if(!eqtype(rcvr, tt)) { if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) { checklvalue(n->left, "call pointer method on"); addrescapes(n->left); n->left = nod(OADDR, n->left, N); + n->left->implicit = 1; typecheck(&n->left, Etype|Erv); } else if(tt->etype == tptr && eqtype(tt->type, rcvr)) { n->left = nod(OIND, n->left, N); + n->left->implicit = 1; typecheck(&n->left, Etype|Erv); } else { // method is attached to wrong type? @@ -1422,7 +1562,7 @@ nokeys(NodeList *l) * typecheck assignment: type list = expression list */ static void -typecheckaste(int op, Type *tstruct, NodeList *nl, char *desc) +typecheckaste(int op, int isddd, Type *tstruct, NodeList *nl, char *desc) { Type *t, *tl, *tn; Node *n; @@ -1436,63 +1576,79 @@ typecheckaste(int op, Type *tstruct, NodeList *nl, char *desc) if(nl != nil && nl->next == nil && (n = nl->n)->type != T) if(n->type->etype == TSTRUCT && n->type->funarg) { - setlineno(n); tn = n->type->type; for(tl=tstruct->type; tl; tl=tl->down) { if(tl->isddd) { - for(; tn; tn=tn->down) + for(; tn; tn=tn->down) { + exportassignok(tn->type, desc); if(assignop(tn->type, tl->type->type, &why) == 0) yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type->type, desc, why); + } goto out; } - if(tn == T) { - yyerror("not enough arguments to %#O", op); - goto out; - } + if(tn == T) + goto notenough; + exportassignok(tn->type, desc); if(assignop(tn->type, tl->type, &why) == 0) yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type, desc, why); tn = tn->down; } if(tn != T) - yyerror("too many arguments to %#O", op); + goto toomany; goto out; } for(tl=tstruct->type; tl; tl=tl->down) { t = tl->type; if(tl->isddd) { - if(nl != nil && nl->n->isddd && !eqtype(nl->n->type, t)) { - // TODO(rsc): This is not actually illegal but will - // help catch bugs. - yyerror("cannot pass %+N as %T (... mismatch)", nl->n, tl); + if(nl != nil && nl->n->op == ONAME && nl->n->isddd && !isddd) { + // TODO(rsc): This is not actually illegal, but it will help catch bugs. + yyerror("to pass '%#N' as ...%T, use '%#N...'", nl->n, t->type, nl->n); + isddd = 1; } - if(nl != nil && nl->next == nil && nl->n->isddd && eqtype(nl->n->type, t)) + if(isddd) { + if(nl == nil) + goto notenough; + if(nl->next != nil) + goto toomany; + n = nl->n; + setlineno(n); + if(n->type != T) + nl->n = assignconv(n, t, desc); goto out; + } for(; nl; nl=nl->next) { + n = nl->n; setlineno(nl->n); - defaultlit(&nl->n, t->type); - if(assignop(nl->n->type, t->type, &why) == 0) - yyerror("cannot use %+N as type %T in %s%s", nl->n, t->type, desc, why); + if(n->type != T) + nl->n = assignconv(n, t->type, desc); } goto out; } - if(nl == nil) { - yyerror("not enough arguments to %#O", op); - goto out; - } + if(nl == nil) + goto notenough; n = nl->n; - setlineno(nl->n); + setlineno(n); if(n->type != T) nl->n = assignconv(n, t, desc); nl = nl->next; } - if(nl != nil) { - yyerror("too many arguments to %#O", op); - goto out; - } + if(nl != nil) + goto toomany; + if(isddd) + yyerror("invalid use of ... in %#O", op); out: lineno = lno; + return; + +notenough: + yyerror("not enough arguments to %#O", op); + goto out; + +toomany: + yyerror("too many arguments to %#O", op); + goto out; } /* @@ -1658,23 +1814,103 @@ indexdup(Node *n, Node *hash[], ulong nhash) hash[h] = n; } +static int +prime(ulong h, ulong sr) +{ + ulong n; + + for(n=3; n<=sr; n+=2) + if(h%n == 0) + return 0; + return 1; +} + +static ulong +inithash(Node *n, Node ***hash, Node **autohash, ulong nautohash) +{ + ulong h, sr; + NodeList *ll; + int i; + + // count the number of entries + h = 0; + for(ll=n->list; ll; ll=ll->next) + h++; + + // if the auto hash table is + // large enough use it. + if(h <= nautohash) { + *hash = autohash; + memset(*hash, 0, nautohash * sizeof(**hash)); + return nautohash; + } + + // make hash size odd and 12% larger than entries + h += h/8; + h |= 1; + + // calculate sqrt of h + sr = h/2; + for(i=0; i<5; i++) + sr = (sr + h/sr)/2; + + // check for primeality + while(!prime(h, sr)) + h += 2; + + // build and return a throw-away hash table + *hash = mal(h * sizeof(**hash)); + memset(*hash, 0, h * sizeof(**hash)); + return h; +} + static void typecheckcomplit(Node **np) { int bad, i, len, nerr; - Node *l, *n, *hash[101]; + Node *l, *n, **hash; NodeList *ll; - Type *t, *f; + Type *t, *f, *pushtype; Sym *s; + int32 lno; + ulong nhash; + Node *autohash[101]; n = *np; + lno = lineno; - memset(hash, 0, sizeof hash); + if(n->right == N) { + if(n->list != nil) + setlineno(n->list->n); + yyerror("missing type in composite literal"); + goto error; + } + setlineno(n->right); l = typecheck(&n->right /* sic */, Etype); if((t = l->type) == T) goto error; nerr = nerrors; + + // can omit type on composite literal values if the outer + // composite literal is array, slice, or map, and the + // element type is itself a struct, array, slice, or map. + pushtype = T; + if(t->etype == TARRAY || t->etype == TMAP) { + pushtype = t->type; + if(pushtype != T) { + switch(pushtype->etype) { + case TSTRUCT: + case TARRAY: + case TMAP: + break; + default: + pushtype = T; + break; + } + } + } + switch(t->etype) { default: yyerror("invalid type for composite literal: %T", t); @@ -1682,31 +1918,29 @@ typecheckcomplit(Node **np) break; case TARRAY: + nhash = inithash(n, &hash, autohash, nelem(autohash)); + len = 0; i = 0; for(ll=n->list; ll; ll=ll->next) { l = ll->n; - if(l->op == OKEY) { - typecheck(&l->left, Erv); - evconst(l->left); - i = nonnegconst(l->left); - if(i < 0) { - yyerror("array index must be non-negative integer constant"); - i = -(1<<30); // stay negative for a while - } - typecheck(&l->right, Erv); - defaultlit(&l->right, t->type); - l->right = assignconv(l->right, t->type, "array index"); - } else { - typecheck(&ll->n, Erv); - defaultlit(&ll->n, t->type); - ll->n = assignconv(ll->n, t->type, "array index"); - ll->n = nod(OKEY, nodintconst(i), ll->n); - ll->n->left->type = types[TINT]; - ll->n->left->typecheck = 1; + setlineno(l); + if(l->op != OKEY) { + l = nod(OKEY, nodintconst(i), l); + l->left->type = types[TINT]; + l->left->typecheck = 1; + ll->n = l; + } + + typecheck(&l->left, Erv); + evconst(l->left); + i = nonnegconst(l->left); + if(i < 0) { + yyerror("array index must be non-negative integer constant"); + i = -(1<<30); // stay negative for a while } if(i >= 0) - indexdup(ll->n->left, hash, nelem(hash)); + indexdup(l->left, hash, nhash); i++; if(i > len) { len = i; @@ -1716,6 +1950,12 @@ typecheckcomplit(Node **np) t->bound = -1; // no more errors } } + + if(l->right->op == OCOMPLIT && l->right->right == N && pushtype != T) + l->right->right = typenod(pushtype); + typecheck(&l->right, Erv); + defaultlit(&l->right, t->type); + l->right = assignconv(l->right, t->type, "array index"); } if(t->bound == -100) t->bound = len; @@ -1725,20 +1965,27 @@ typecheckcomplit(Node **np) break; case TMAP: + nhash = inithash(n, &hash, autohash, nelem(autohash)); + for(ll=n->list; ll; ll=ll->next) { l = ll->n; + setlineno(l); if(l->op != OKEY) { typecheck(&ll->n, Erv); yyerror("missing key in map literal"); continue; } + typecheck(&l->left, Erv); - typecheck(&l->right, Erv); defaultlit(&l->left, t->down); - defaultlit(&l->right, t->type); l->left = assignconv(l->left, t->down, "map key"); + keydup(l->left, hash, nhash); + + if(l->right->op == OCOMPLIT && l->right->right == N && pushtype != T) + l->right->right = typenod(pushtype); + typecheck(&l->right, Erv); + defaultlit(&l->right, t->type); l->right = assignconv(l->right, t->type, "map value"); - keydup(l->left, hash, nelem(hash)); } n->op = OMAPLIT; break; @@ -1749,6 +1996,7 @@ typecheckcomplit(Node **np) // simple list of variables f = t->type; for(ll=n->list; ll; ll=ll->next) { + setlineno(ll->n); typecheck(&ll->n, Erv); if(f == nil) { if(!bad++) @@ -1766,9 +2014,12 @@ typecheckcomplit(Node **np) if(f != nil) yyerror("too few values in struct initializer"); } else { + nhash = inithash(n, &hash, autohash, nelem(autohash)); + // keyed list for(ll=n->list; ll; ll=ll->next) { l = ll->n; + setlineno(l); if(l->op != OKEY) { if(!bad++) yyerror("mixture of field:value and value initializers"); @@ -1781,6 +2032,11 @@ typecheckcomplit(Node **np) typecheck(&l->right, Erv); continue; } + // Sym might have resolved to name in other top-level + // package, because of import dot. Redirect to correct sym + // before we do the lookup. + if(s->pkg != localpkg) + s = lookup(s->name); l->left = newname(s); l->left->typecheck = 1; f = lookdot1(s, t, t->type, 0); @@ -1790,7 +2046,7 @@ typecheckcomplit(Node **np) continue; } s = f->sym; - fielddup(newname(s), hash, nelem(hash)); + fielddup(newname(s), hash, nhash); l->right = assignconv(l->right, f->type, "field value"); } } @@ -1802,11 +2058,13 @@ typecheckcomplit(Node **np) n->type = t; *np = n; + lineno = lno; return; error: n->type = T; *np = n; + lineno = lno; } /* @@ -1890,6 +2148,8 @@ islvalue(Node *n) case OINDEX: if(isfixedarray(n->left->type)) return islvalue(n->left); + if(n->left->type != T && n->left->type->etype == TSTRING) + return 0; // fall through case OIND: case ODOTPTR: diff --git a/src/cmd/gc/unsafe.c b/src/cmd/gc/unsafe.c index dbf6f708a..33f375631 100644 --- a/src/cmd/gc/unsafe.c +++ b/src/cmd/gc/unsafe.c @@ -11,13 +11,18 @@ * rewrite with a constant */ Node* -unsafenmagic(Node *fn, NodeList *args) +unsafenmagic(Node *nn) { Node *r, *n; Sym *s; Type *t, *tr; long v; Val val; + Node *fn; + NodeList *args; + + fn = nn->left; + args = nn->list; if(safemode || fn == N || fn->op != ONAME || (s = fn->sym) == S) goto no; @@ -35,13 +40,14 @@ unsafenmagic(Node *fn, NodeList *args) defaultlit(&r, T); tr = r->type; if(tr == T) - goto no; + goto bad; v = tr->width; goto yes; } if(strcmp(s->name, "Offsetof") == 0) { + typecheck(&r, Erv); if(r->op != ODOT && r->op != ODOTPTR) - goto no; + goto bad; typecheck(&r, Erv); v = r->xoffset; goto yes; @@ -51,7 +57,7 @@ unsafenmagic(Node *fn, NodeList *args) defaultlit(&r, T); tr = r->type; if(tr == T) - goto no; + goto bad; // make struct { byte; T; } t = typ(TSTRUCT); @@ -70,9 +76,15 @@ unsafenmagic(Node *fn, NodeList *args) no: return N; +bad: + yyerror("invalid expression %#N", nn); + v = 0; + goto ret; + yes: if(args->next != nil) yyerror("extra arguments for %S", s); +ret: // any side effects disappear; ignore init val.ctype = CTINT; val.u.xval = mal(sizeof(*n->val.u.xval)); diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index 4f59d5598..fa3e5d5e4 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -11,12 +11,14 @@ static Node* makenewvar(Type*, NodeList**, Node**); static Node* ascompatee1(int, Node*, Node*, NodeList**); static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**); static NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**); -static NodeList* ascompatte(int, Type**, NodeList*, int, NodeList**); +static NodeList* ascompatte(int, int, Type**, NodeList*, int, NodeList**); static Node* convas(Node*, NodeList**); static void heapmoves(void); static NodeList* paramstoheap(Type **argin, int out); static NodeList* reorder1(NodeList*); static NodeList* reorder3(NodeList*); +static Node* addstr(Node*, NodeList**); +static Node* append(Node*, NodeList**); static NodeList* walkdefstack; @@ -134,6 +136,10 @@ walkdeftype(Node *n) n->diag = 1; goto ret; } + if(n->type == T) { + n->diag = 1; + goto ret; + } // copy new type and clear fields // that don't come along @@ -257,6 +263,10 @@ walkdef(Node *n) yyerror("const initializer must be constant"); goto ret; } + if(isconst(e, CTNIL)) { + yyerror("const initializer cannot be nil"); + goto ret; + } t = n->type; if(t != T) { convlit(&e, t); @@ -281,6 +291,13 @@ walkdef(Node *n) if(n->defn == N) { if(n->etype != 0) // like OPRINTN break; + if(nerrors > 0) { + // Can have undefined variables in x := foo + // that make x have an n->ndefn == nil. + // If there are other errors anyway, don't + // bother adding to the noise. + break; + } fatal("var without type, init: %S", n->sym); } if(n->defn->op == ONAME) { @@ -292,10 +309,14 @@ walkdef(Node *n) break; case OTYPE: + if(curfn) + defercheckwidth(); n->walkdef = 1; n->type = typ(TFORW); n->type->sym = n->sym; walkdeftype(n); + if(curfn) + resumecheckwidth(); break; case OPACK: @@ -354,7 +375,7 @@ walkstmt(Node **np) NodeList *init; NodeList *ll, *rl; int cl, lno; - Node *n; + Node *n, *f; n = *np; if(n == N) @@ -481,11 +502,19 @@ walkstmt(Node **np) n->list = nil; break; } + if(count(n->list) == 1 && count(rl) > 1) { + // OAS2FUNC in disguise + f = n->list->n; + if(f->op != OCALLFUNC && f->op != OCALLMETH && f->op != OCALLINTER) + fatal("expected return of call, have %#N", f); + n->list = concat(list1(f), ascompatet(n->op, rl, &f->type, 0, &n->ninit)); + break; + } ll = ascompatee(n->op, rl, n->list, &n->ninit); n->list = reorder3(ll); break; } - ll = ascompatte(n->op, getoutarg(curfn->type), n->list, 1, &n->ninit); + ll = ascompatte(n->op, 0, getoutarg(curfn->type), n->list, 1, &n->ninit); n->list = ll; break; @@ -542,6 +571,7 @@ walkexpr(Node **np, NodeList **init) NodeList *ll, *lr, *lpost; Type *t; int et; + int64 v, v1, v2, len; int32 lno; Node *n, *fn; char buf[100], *p; @@ -594,8 +624,6 @@ walkexpr(Node **np, NodeList **init) case OMINUS: case OPLUS: case OCOM: - case OLEN: - case OCAP: case OREAL: case OIMAG: case ODOT: @@ -606,13 +634,27 @@ walkexpr(Node **np, NodeList **init) walkexpr(&n->left, init); goto ret; + case OLEN: + case OCAP: + walkexpr(&n->left, init); + + // replace len(*[10]int) with 10. + // delayed until now to preserve side effects. + t = n->left->type; + if(isptr[t->etype]) + t = t->type; + if(isfixedarray(t)) { + safeexpr(n->left, init); + nodconst(n, n->type, t->bound); + n->typecheck = 1; + } + goto ret; + case OLSH: case ORSH: case OAND: case OOR: case OXOR: - case OANDAND: - case OOROR: case OSUB: case OMUL: case OEQ: @@ -626,6 +668,17 @@ walkexpr(Node **np, NodeList **init) walkexpr(&n->left, init); walkexpr(&n->right, init); goto ret; + + case OANDAND: + case OOROR: + walkexpr(&n->left, init); + // cannot put side effects from n->right on init, + // because they cannot run before n->left is checked. + // save elsewhere and store on the eventual n->right. + ll = nil; + walkexpr(&n->right, &ll); + n->right->ninit = concat(n->right->ninit, ll); + goto ret; case OPRINT: case OPRINTN: @@ -656,7 +709,7 @@ walkexpr(Node **np, NodeList **init) goto ret; walkexpr(&n->left, init); walkexprlist(n->list, init); - ll = ascompatte(n->op, getinarg(t), n->list, 0, init); + ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init); n->list = reorder1(ll); goto ret; @@ -666,7 +719,7 @@ walkexpr(Node **np, NodeList **init) goto ret; walkexpr(&n->left, init); walkexprlist(n->list, init); - ll = ascompatte(n->op, getinarg(t), n->list, 0, init); + ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init); n->list = reorder1(ll); if(isselect(n)) { // special prob with selectsend and selectrecv: @@ -676,7 +729,7 @@ walkexpr(Node **np, NodeList **init) Node *b; b = nodbool(0); typecheck(&b, Erv); - lr = ascompatte(n->op, getoutarg(t), list1(b), 0, init); + lr = ascompatte(n->op, 0, getoutarg(t), list1(b), 0, init); n->list = concat(n->list, lr); } goto ret; @@ -687,8 +740,8 @@ walkexpr(Node **np, NodeList **init) goto ret; walkexpr(&n->left, init); walkexprlist(n->list, init); - ll = ascompatte(n->op, getinarg(t), n->list, 0, init); - lr = ascompatte(n->op, getthis(t), list1(n->left->left), 0, init); + ll = ascompatte(n->op, 0, getthis(t), list1(n->left->left), 0, init); + lr = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init); ll = concat(ll, lr); n->left->left = N; ullmancalc(n->left); @@ -882,43 +935,55 @@ walkexpr(Node **np, NodeList **init) case OCONV: case OCONVNOP: if(thechar == '5') { - if(isfloat[n->left->type->etype] && - (n->type->etype == TINT64 || n->type->etype == TUINT64)) { - n = mkcall("float64toint64", n->type, init, conv(n->left, types[TFLOAT64])); - goto ret; + if(isfloat[n->left->type->etype]) { + if(n->type->etype == TINT64) { + n = mkcall("float64toint64", n->type, init, conv(n->left, types[TFLOAT64])); + goto ret; + } + if(n->type->etype == TUINT64) { + n = mkcall("float64touint64", n->type, init, conv(n->left, types[TFLOAT64])); + goto ret; + } } - if((n->left->type->etype == TINT64 || n->left->type->etype == TUINT64) && - isfloat[n->type->etype]) { - n = mkcall("int64tofloat64", n->type, init, conv(n->left, types[TINT64])); - goto ret; + if(isfloat[n->type->etype]) { + if(n->left->type->etype == TINT64) { + n = mkcall("int64tofloat64", n->type, init, conv(n->left, types[TINT64])); + goto ret; + } + if(n->left->type->etype == TUINT64) { + n = mkcall("uint64tofloat64", n->type, init, conv(n->left, types[TUINT64])); + goto ret; + } } } walkexpr(&n->left, init); goto ret; case OASOP: - n->left = safeexpr(n->left, init); - walkexpr(&n->left, init); - l = n->left; - walkexpr(&n->right, init); if(n->etype == OANDNOT) { n->etype = OAND; n->right = nod(OCOM, n->right, N); typecheck(&n->right, Erv); } + n->left = safeexpr(n->left, init); + walkexpr(&n->left, init); + l = n->left; + walkexpr(&n->right, init); /* * on 32-bit arch, rewrite 64-bit ops into l = l op r. * on 386, rewrite float ops into l = l op r. * everywhere, rewrite map ops into l = l op r. * everywhere, rewrite string += into l = l op r. + * everywhere, rewrite complex /= into l = l op r. * TODO(rsc): Maybe this rewrite should be done always? */ et = n->left->type->etype; if((widthptr == 4 && (et == TUINT64 || et == TINT64)) || (thechar == '8' && isfloat[et]) || l->op == OINDEXMAP || - et == TSTRING) { + et == TSTRING || + (iscomplex[et] && n->etype == ODIV)) { l = safeexpr(n->left, init); a = l; if(a->op == OINDEXMAP) { @@ -952,9 +1017,11 @@ walkexpr(Node **np, NodeList **init) */ et = n->left->type->etype; if(iscomplex[et] && n->op == ODIV) { - n = mkcall("complex128div", n->type, init, + t = n->type; + n = mkcall("complex128div", types[TCOMPLEX128], init, conv(n->left, types[TCOMPLEX128]), conv(n->right, types[TCOMPLEX128])); + n = conv(n, t); goto ret; } /* @@ -981,11 +1048,36 @@ walkexpr(Node **np, NodeList **init) // if range of type cannot exceed static array bound, // disable bounds check - if(!isslice(n->left->type)) + if(isfixedarray(n->left->type)) if(n->right->type->width < 4) if((1<<(8*n->right->type->width)) <= n->left->type->bound) n->etype = 1; + if(isconst(n->left, CTSTR)) + if(n->right->type->width < 4) + if((1<<(8*n->right->type->width)) <= n->left->val.u.sval->len) + n->etype = 1; + + // check for static out of bounds + if(isconst(n->right, CTINT) && !n->etype) { + v = mpgetfix(n->right->val.u.xval); + len = 1LL<<60; + t = n->left->type; + if(isconst(n->left, CTSTR)) + len = n->left->val.u.sval->len; + if(t != T && isptr[t->etype]) + t = t->type; + if(isfixedarray(t)) + len = t->bound; + if(v < 0 || v >= (1LL<<31) || v >= len) + yyerror("index out of bounds"); + else if(isconst(n->left, CTSTR)) { + // replace "abc"[2] with 'b'. + // delayed until now because "abc"[2] is not + // an ideal constant. + nodconst(n, n->type, n->left->val.u.sval->s[v]); + } + } goto ret; case OINDEXMAP: @@ -1002,24 +1094,61 @@ walkexpr(Node **np, NodeList **init) goto ret; case OSLICE: + case OSLICEARR: walkexpr(&n->left, init); n->left = safeexpr(n->left, init); walkexpr(&n->right->left, init); n->right->left = safeexpr(n->right->left, init); walkexpr(&n->right->right, init); n->right->right = safeexpr(n->right->right, init); + + len = 1LL<<60; + t = n->left->type; + if(t != T && isptr[t->etype]) + t = t->type; + if(isfixedarray(t)) + len = t->bound; + + // check for static out of bounds + // NOTE: v > len not v >= len. + v1 = -1; + v2 = -1; + if(isconst(n->right->left, CTINT)) { + v1 = mpgetfix(n->right->left->val.u.xval); + if(v1 < 0 || v1 >= (1LL<<31) || v1 > len) { + yyerror("slice index out of bounds"); + v1 = -1; + } + } + if(isconst(n->right->right, CTINT)) { + v2 = mpgetfix(n->right->right->val.u.xval); + if(v2 < 0 || v2 >= (1LL<<31) || v2 > len) { + yyerror("slice index out of bounds"); + v2 = -1; + } + } + if(v1 >= 0 && v2 >= 0 && v1 > v2) + yyerror("inverted slice range"); + + if(n->op == OSLICEARR) + goto slicearray; + // dynamic slice - // sliceslice(old []any, lb int, hb int, width int) (ary []any) - // sliceslice1(old []any, lb int, width int) (ary []any) + // sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any) + // sliceslice1(old []any, lb uint64, width uint64) (ary []any) t = n->type; + if(n->right->left == N) + l = nodintconst(0); + else + l = conv(n->right->left, types[TUINT64]); if(n->right->right != N) { fn = syslook("sliceslice", 1); argtype(fn, t->type); // any-1 argtype(fn, t->type); // any-2 n = mkcall1(fn, t, init, n->left, - conv(n->right->left, types[TINT]), - conv(n->right->right, types[TINT]), + l, + conv(n->right->right, types[TUINT64]), nodintconst(t->type->width)); } else { fn = syslook("sliceslice1", 1); @@ -1027,47 +1156,33 @@ walkexpr(Node **np, NodeList **init) argtype(fn, t->type); // any-2 n = mkcall1(fn, t, init, n->left, - conv(n->right->left, types[TINT]), + l, nodintconst(t->type->width)); } goto ret; - case OSLICEARR: - walkexpr(&n->left, init); - n->left = safeexpr(n->left, init); - walkexpr(&n->right->left, init); - n->right->left = safeexpr(n->right->left, init); - walkexpr(&n->right->right, init); - n->right->right = safeexpr(n->right->right, init); + slicearray: // static slice - // slicearray(old *any, nel int, lb int, hb int, width int) (ary []any) + // slicearray(old *any, uint64 nel, lb uint64, hb uint64, width uint64) (ary []any) t = n->type; fn = syslook("slicearray", 1); - argtype(fn, n->left->type); // any-1 + argtype(fn, n->left->type->type); // any-1 argtype(fn, t->type); // any-2 + if(n->right->left == N) + l = nodintconst(0); + else + l = conv(n->right->left, types[TUINT64]); if(n->right->right == N) - r = nodintconst(n->left->type->bound); + r = nodintconst(n->left->type->type->bound); else - r = conv(n->right->right, types[TINT]); + r = conv(n->right->right, types[TUINT64]); n = mkcall1(fn, t, init, - nod(OADDR, n->left, N), nodintconst(n->left->type->bound), - conv(n->right->left, types[TINT]), + n->left, nodintconst(n->left->type->type->bound), + l, r, nodintconst(t->type->width)); goto ret; - case OCONVSLICE: - // slicearray(old *any, nel int, lb int, hb int, width int) (ary []any) - fn = syslook("slicearray", 1); - argtype(fn, n->left->type->type); // any-1 - argtype(fn, n->type->type); // any-2 - n = mkcall1(fn, n->type, init, n->left, - nodintconst(n->left->type->type->bound), - nodintconst(0), - nodintconst(n->left->type->type->bound), - nodintconst(n->type->type->width)); - goto ret; - case OADDR:; Node *nvar, *nstar; @@ -1118,46 +1233,62 @@ walkexpr(Node **np, NodeList **init) goto ret; } + // prepare for rewrite below + if(n->etype == OEQ || n->etype == ONE) { + n->left = cheapexpr(n->left, init); + n->right = cheapexpr(n->right, init); + } + // sys_cmpstring(s1, s2) :: 0 r = mkcall("cmpstring", types[TINT], init, conv(n->left, types[TSTRING]), conv(n->right, types[TSTRING])); r = nod(n->etype, r, nodintconst(0)); + + // quick check of len before full compare for == or != + if(n->etype == OEQ || n->etype == ONE) { + if(n->etype == OEQ) + r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); + else + r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); + typecheck(&r, Erv); + walkexpr(&r, nil); + } typecheck(&r, Erv); n = r; goto ret; case OADDSTR: - // sys_catstring(s1, s2) - n = mkcall("catstring", n->type, init, - conv(n->left, types[TSTRING]), - conv(n->right, types[TSTRING])); + n = addstr(n, init); goto ret; case OSLICESTR: // sys_slicestring(s, lb, hb) + if(n->right->left == N) + l = nodintconst(0); + else + l = conv(n->right->left, types[TINT]); if(n->right->right) { n = mkcall("slicestring", n->type, init, conv(n->left, types[TSTRING]), - conv(n->right->left, types[TINT]), + l, conv(n->right->right, types[TINT])); } else { n = mkcall("slicestring1", n->type, init, conv(n->left, types[TSTRING]), - conv(n->right->left, types[TINT])); + l); } goto ret; - - case OINDEXSTR: - // TODO(rsc): should be done in back end - // sys_indexstring(s, i) - n = mkcall("indexstring", n->type, init, - conv(n->left, types[TSTRING]), - conv(n->right, types[TINT])); + + case OAPPEND: + n = append(n, init); goto ret; case OCOPY: - fn = syslook("slicecopy", 1); + if(n->right->type->etype == TSTRING) + fn = syslook("slicestringcopy", 1); + else + fn = syslook("slicecopy", 1); argtype(fn, n->left->type); argtype(fn, n->right->type); n = mkcall1(fn, n->type, init, @@ -1441,47 +1572,51 @@ mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init) /* * helpers for shape errors */ -static void +static char* dumptypes(Type **nl, char *what) { int first; Type *l; Iter savel; + Fmt fmt; + fmtstrinit(&fmt); + fmtprint(&fmt, "\t"); l = structfirst(&savel, nl); - print("\t"); first = 1; for(l = structfirst(&savel, nl); l != T; l = structnext(&savel)) { if(first) first = 0; else - print(", "); - print("%T", l); + fmtprint(&fmt, ", "); + fmtprint(&fmt, "%T", l); } if(first) - print("[no arguments %s]", what); - print("\n"); + fmtprint(&fmt, "[no arguments %s]", what); + return fmtstrflush(&fmt); } -static void +static char* dumpnodetypes(NodeList *l, char *what) { int first; Node *r; + Fmt fmt; - print("\t"); + fmtstrinit(&fmt); + fmtprint(&fmt, "\t"); first = 1; for(; l; l=l->next) { r = l->n; if(first) first = 0; else - print(", "); - print("%T", r->type); + fmtprint(&fmt, ", "); + fmtprint(&fmt, "%T", r->type); } if(first) - print("[no arguments %s]", what); - print("\n"); + fmtprint(&fmt, "[no arguments %s]", what); + return fmtstrflush(&fmt); } /* @@ -1491,12 +1626,13 @@ dumpnodetypes(NodeList *l, char *what) * func(expr-list) */ static NodeList* -ascompatte(int op, Type **nl, NodeList *lr, int fp, NodeList **init) +ascompatte(int op, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init) { Type *l, *ll; Node *r, *a; NodeList *nn, *lr0, *alist; Iter savel; + char *l1, *l2; lr0 = lr; l = structfirst(&savel, nl); @@ -1545,7 +1681,7 @@ loop: // only if we are assigning a single ddd // argument to a ddd parameter then it is // passed thru unencapsulated - if(r != N && lr->next == nil && r->isddd && eqtype(l->type, r->type)) { + if(r != N && lr->next == nil && isddd && eqtype(l->type, r->type)) { a = nod(OAS, nodarg(l, fp), r); a = convas(a, init); nn = list(nn, a); @@ -1561,12 +1697,12 @@ loop: if(l == T || r == N) { if(l != T || r != N) { + l1 = dumptypes(nl, "expected"); + l2 = dumpnodetypes(lr0, "given"); if(l != T) - yyerror("not enough arguments to %O", op); + yyerror("not enough arguments to %O\n%s\n%s", op, l1, l2); else - yyerror("too many arguments to %O", op); - dumptypes(nl, "expected"); - dumpnodetypes(lr0, "given"); + yyerror("too many arguments to %O\n%s\n%s", op, l1, l2); } goto ret; } @@ -1740,7 +1876,7 @@ walkprint(Node *nn, NodeList **init, int defer) if(defer) { if(op == OPRINTN) fmtprint(&fmt, "\n"); - on = syslook("printf", 1); + on = syslook("goprintf", 1); on->type = functype(nil, intypes, nil); args->n = nod(OLITERAL, N, N); args->n->val.ctype = CTSTR; @@ -1769,7 +1905,7 @@ callnew(Type *t) Node *fn; dowidth(t); - fn = syslook("mal", 1); + fn = syslook("new", 1); argtype(fn, t); return mkcall1(fn, ptrto(t), nil, nodintconst(t->width)); } @@ -2046,12 +2182,17 @@ static void heapmoves(void) { NodeList *nn; - + int32 lno; + + lno = lineno; + lineno = curfn->lineno; nn = paramstoheap(getthis(curfn->type), 0); nn = concat(nn, paramstoheap(getinarg(curfn->type), 0)); nn = concat(nn, paramstoheap(getoutarg(curfn->type), 1)); curfn->enter = concat(curfn->enter, nn); + lineno = curfn->endlineno; curfn->exit = returnsfromheap(getoutarg(curfn->type)); + lineno = lno; } static Node* @@ -2143,3 +2284,83 @@ mapfn(char *name, Type *t) argtype(fn, t->type); return fn; } + +static Node* +addstr(Node *n, NodeList **init) +{ + Node *r, *cat, *typstr; + NodeList *in, *args; + int i, count; + + count = 0; + for(r=n; r->op == OADDSTR; r=r->left) + count++; // r->right + count++; // r + + // prepare call of runtime.catstring of type int, string, string, string + // with as many strings as we have. + cat = syslook("concatstring", 1); + cat->type = T; + cat->ntype = nod(OTFUNC, N, N); + in = list1(nod(ODCLFIELD, N, typenod(types[TINT]))); // count + typstr = typenod(types[TSTRING]); + for(i=0; i<count; i++) + in = list(in, nod(ODCLFIELD, N, typstr)); + cat->ntype->list = in; + cat->ntype->rlist = list1(nod(ODCLFIELD, N, typstr)); + + args = nil; + for(r=n; r->op == OADDSTR; r=r->left) + args = concat(list1(conv(r->right, types[TSTRING])), args); + args = concat(list1(conv(r, types[TSTRING])), args); + args = concat(list1(nodintconst(count)), args); + + r = nod(OCALL, cat, N); + r->list = args; + typecheck(&r, Erv); + walkexpr(&r, init); + r->type = n->type; + + return r; +} + +static Node* +append(Node *n, NodeList **init) +{ + int i, j; + Node *f, *r; + NodeList *in, *args; + + if(n->isddd) { + f = syslook("appendslice", 1); + argtype(f, n->type); + argtype(f, n->type->type); + argtype(f, n->type); + r = mkcall1(f, n->type, init, typename(n->type), n->list->n, n->list->next->n); + return r; + } + + j = count(n->list) - 1; + f = syslook("append", 1); + f->type = T; + f->ntype = nod(OTFUNC, N, N); + in = list1(nod(ODCLFIELD, N, typenod(ptrto(types[TUINT8])))); // type + in = list(in, nod(ODCLFIELD, N, typenod(types[TINT]))); // count + in = list(in, nod(ODCLFIELD, N, typenod(n->type))); // slice + for(i=0; i<j; i++) + in = list(in, nod(ODCLFIELD, N, typenod(n->type->type))); + f->ntype->list = in; + f->ntype->rlist = list1(nod(ODCLFIELD, N, typenod(n->type))); + + args = list1(typename(n->type)); + args = list(args, nodintconst(j)); + args = concat(args, n->list); + + r = nod(OCALL, f, N); + r->list = args; + typecheck(&r, Erv); + walkexpr(&r, init); + r->type = n->type; + + return r; +} diff --git a/src/cmd/godefs/Makefile b/src/cmd/godefs/Makefile index 49244f152..b5c76fb0f 100644 --- a/src/cmd/godefs/Makefile +++ b/src/cmd/godefs/Makefile @@ -2,7 +2,8 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) TARG=godefs OFILES=\ @@ -12,13 +13,4 @@ OFILES=\ HFILES=a.h -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lbio -l9 - -clean: - rm -f *.$O $(TARG) - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) - -$(OFILES): $(HFILES) +include ../../Make.ccmd diff --git a/src/cmd/godefs/main.c b/src/cmd/godefs/main.c index 6ff542f48..69ee1be5d 100644 --- a/src/cmd/godefs/main.c +++ b/src/cmd/godefs/main.c @@ -82,7 +82,7 @@ #include "a.h" -#ifdef __MINGW32__ +#ifdef _WIN32 int spawn(char *prog, char **argv) { @@ -133,7 +133,7 @@ Lang go = "type %s struct {\n", "type %s struct {\n", - "\tPad%d [%d]byte;\n", + "\tPad_godefs_%d [%d]byte;\n", "}\n", gotypefmt, @@ -150,7 +150,7 @@ Lang c = "typedef struct %s %s;\nstruct %s {\n", "typedef union %s %s;\nunion %s {\n", - "\tbyte pad%d[%d];\n", + "\tbyte pad_godefs_%d[%d];\n", "};\n", ctypefmt, @@ -373,6 +373,8 @@ Continue: prefix = prefixlen(t); for(j=0; j<t->nf; j++) { f = &t->f[j]; + if(f->type->kind == 0) + continue; // padding if(t->kind == Struct || lang == &go) { if(f->offset%8 != 0 || f->size%8 != 0) { @@ -391,7 +393,7 @@ Continue: if(cutprefix(name)) name += prefix; if(strcmp(name, "") == 0) { - snprint(nambuf, sizeof nambuf, "Pad%d", npad++); + snprint(nambuf, sizeof nambuf, "Pad_godefs_%d", npad++); name = nambuf; } Bprint(bout, "\t%#lT;\n", name, f->type); diff --git a/src/cmd/godefs/stabs.c b/src/cmd/godefs/stabs.c index 8d3be1913..1bc96d4c8 100644 --- a/src/cmd/godefs/stabs.c +++ b/src/cmd/godefs/stabs.c @@ -363,13 +363,22 @@ parsedef(char **pp, char *name) return nil; } + while(f->type->kind == Typedef) + f->type = f->type->type; + if(f->type->kind == 0 && f->size <= 64 && (f->size&(f->size-1)) == 0) { + // unknown type but <= 64 bits and bit size is a power of two. + // could be enum - make Uint64 and then let it reduce + tt = emalloc(sizeof *tt); + *tt = *f->type; + f->type = tt; + tt->kind = Uint64; + } + // rewrite // uint32 x : 8; // into // uint8 x; // hooray for bitfields. - while(f->type->kind == Typedef) - f->type = f->type->type; while(Int16 <= f->type->kind && f->type->kind <= Uint64 && kindsize[f->type->kind] > f->size) { tt = emalloc(sizeof *tt); *tt = *f->type; diff --git a/src/cmd/godoc/Makefile b/src/cmd/godoc/Makefile index d799219dd..c4e0fd9f9 100644 --- a/src/cmd/godoc/Makefile +++ b/src/cmd/godoc/Makefile @@ -2,16 +2,19 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=godoc GOFILES=\ codewalk.go\ + dirtrees.go\ + format.go\ godoc.go\ index.go\ main.go\ mapping.go\ snippet.go\ spec.go\ + utils.go\ include ../../Make.cmd diff --git a/src/cmd/godoc/codewalk.go b/src/cmd/godoc/codewalk.go index 806849c00..bda63a9f6 100644 --- a/src/cmd/godoc/codewalk.go +++ b/src/cmd/godoc/codewalk.go @@ -31,26 +31,26 @@ import ( // Handler for /doc/codewalk/ and below. -func codewalk(c *http.Conn, r *http.Request) { +func codewalk(w http.ResponseWriter, r *http.Request) { relpath := r.URL.Path[len("/doc/codewalk/"):] abspath := absolutePath(r.URL.Path[1:], *goroot) r.ParseForm() if f := r.FormValue("fileprint"); f != "" { - codewalkFileprint(c, r, f) + codewalkFileprint(w, r, f) return } // If directory exists, serve list of code walks. dir, err := os.Lstat(abspath) if err == nil && dir.IsDirectory() { - codewalkDir(c, r, relpath, abspath) + codewalkDir(w, r, relpath, abspath) return } // If file exists, serve using standard file server. if err == nil { - serveFile(c, r) + serveFile(w, r) return } @@ -58,18 +58,18 @@ func codewalk(c *http.Conn, r *http.Request) { // a codewalk description. cw, err := loadCodewalk(abspath + ".xml") if err != nil { - log.Stderr(err) - serveError(c, r, relpath, err) + log.Print(err) + serveError(w, r, relpath, err) return } // Canonicalize the path and redirect if changed - if redirect(c, r) { + if redirect(w, r) { return } b := applyTemplate(codewalkHTML, "codewalk", cw) - servePage(c, "Codewalk: "+cw.Title, "", "", b) + servePage(w, "Codewalk: "+cw.Title, "", "", b) } @@ -178,7 +178,7 @@ func loadCodewalk(file string) (*Codewalk, os.Error) { // codewalkDir serves the codewalk directory listing. // It scans the directory for subdirectories or files named *.xml // and prepares a table. -func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) { +func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string) { type elem struct { Name string Title string @@ -186,8 +186,8 @@ func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) { dir, err := ioutil.ReadDir(abspath) if err != nil { - log.Stderr(err) - serveError(c, r, relpath, err) + log.Print(err) + serveError(w, r, relpath, err) return } var v vector.Vector @@ -204,7 +204,7 @@ func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) { } b := applyTemplate(codewalkdirHTML, "codewalkdir", v) - servePage(c, "Codewalks", "", "", b) + servePage(w, "Codewalks", "", "", b) } @@ -214,11 +214,12 @@ func codewalkDir(c *http.Conn, r *http.Request, relpath, abspath string) { // in the response. This format is used for the middle window pane // of the codewalk pages. It is a separate iframe and does not get // the usual godoc HTML wrapper. -func codewalkFileprint(c *http.Conn, r *http.Request, f string) { +func codewalkFileprint(w http.ResponseWriter, r *http.Request, f string) { abspath := absolutePath(f, *goroot) data, err := ioutil.ReadFile(abspath) if err != nil { - serveError(c, r, f, err) + log.Print(err) + serveError(w, r, f, err) return } lo, _ := strconv.Atoi(r.FormValue("lo")) @@ -242,17 +243,17 @@ func codewalkFileprint(c *http.Conn, r *http.Request, f string) { } } - io.WriteString(c, `<style type="text/css">@import "/doc/codewalk/codewalk.css";</style><pre>`) - template.HTMLEscape(c, data[0:mark]) - io.WriteString(c, "<a name='mark'></a>") - template.HTMLEscape(c, data[mark:lo]) + io.WriteString(w, `<style type="text/css">@import "/doc/codewalk/codewalk.css";</style><pre>`) + template.HTMLEscape(w, data[0:mark]) + io.WriteString(w, "<a name='mark'></a>") + template.HTMLEscape(w, data[mark:lo]) if lo < hi { - io.WriteString(c, "<div class='codewalkhighlight'>") - template.HTMLEscape(c, data[lo:hi]) - io.WriteString(c, "</div>") + io.WriteString(w, "<div class='codewalkhighlight'>") + template.HTMLEscape(w, data[lo:hi]) + io.WriteString(w, "</div>") } - template.HTMLEscape(c, data[hi:]) - io.WriteString(c, "</pre>") + template.HTMLEscape(w, data[hi:]) + io.WriteString(w, "</pre>") } @@ -450,13 +451,13 @@ func addrRegexp(data []byte, lo, hi int, dir byte, pattern string) (int, int, os // through file, but that seems like overkill. return 0, 0, os.NewError("reverse search not implemented") } - m := re.Execute(data[hi:]) + m := re.FindIndex(data[hi:]) if len(m) > 0 { m[0] += hi m[1] += hi } else if hi > 0 { // No match. Wrap to beginning of data. - m = re.Execute(data) + m = re.FindIndex(data) } if len(m) == 0 { return 0, 0, os.NewError("no match for " + pattern) diff --git a/src/cmd/godoc/dirtrees.go b/src/cmd/godoc/dirtrees.go new file mode 100644 index 000000000..edb4a169d --- /dev/null +++ b/src/cmd/godoc/dirtrees.go @@ -0,0 +1,341 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains the code dealing with package directory trees. + +package main + +import ( + "bytes" + "go/doc" + "go/parser" + "go/token" + "io/ioutil" + "os" + pathutil "path" + "strings" + "unicode" +) + + +type Directory struct { + Depth int + Path string // includes Name + Name string + Text string // package documentation, if any + Dirs []*Directory // subdirectories +} + + +func isGoFile(f *os.FileInfo) bool { + return f.IsRegular() && + !strings.HasPrefix(f.Name, ".") && // ignore .files + pathutil.Ext(f.Name) == ".go" +} + + +func isPkgFile(f *os.FileInfo) bool { + return isGoFile(f) && + !strings.HasSuffix(f.Name, "_test.go") // ignore test files +} + + +func isPkgDir(f *os.FileInfo) bool { + return f.IsDirectory() && len(f.Name) > 0 && f.Name[0] != '_' +} + + +func firstSentence(s string) string { + i := -1 // index+1 of first terminator (punctuation ending a sentence) + j := -1 // index+1 of first terminator followed by white space + prev := 'A' + for k, ch := range s { + k1 := k + 1 + if ch == '.' || ch == '!' || ch == '?' { + if i < 0 { + i = k1 // first terminator + } + if k1 < len(s) && s[k1] <= ' ' { + if j < 0 { + j = k1 // first terminator followed by white space + } + if !unicode.IsUpper(prev) { + j = k1 + break + } + } + } + prev = ch + } + + if j < 0 { + // use the next best terminator + j = i + if j < 0 { + // no terminator at all, use the entire string + j = len(s) + } + } + + return s[0:j] +} + + +type treeBuilder struct { + pathFilter func(string) bool + maxDepth int +} + + +func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth int) *Directory { + if b.pathFilter != nil && !b.pathFilter(path) { + return nil + } + + if depth >= b.maxDepth { + // return a dummy directory so that the parent directory + // doesn't get discarded just because we reached the max + // directory depth + return &Directory{depth, path, name, "", nil} + } + + list, _ := ioutil.ReadDir(path) // ignore errors + + // determine number of subdirectories and if there are package files + ndirs := 0 + hasPkgFiles := false + var synopses [4]string // prioritized package documentation (0 == highest priority) + for _, d := range list { + switch { + case isPkgDir(d): + ndirs++ + case isPkgFile(d): + // looks like a package file, but may just be a file ending in ".go"; + // don't just count it yet (otherwise we may end up with hasPkgFiles even + // though the directory doesn't contain any real package files - was bug) + if synopses[0] == "" { + // no "optimal" package synopsis yet; continue to collect synopses + file, err := parser.ParseFile(fset, pathutil.Join(path, d.Name), nil, + parser.ParseComments|parser.PackageClauseOnly) + if err == nil { + hasPkgFiles = true + if file.Doc != nil { + // prioritize documentation + i := -1 + switch file.Name.Name { + case name: + i = 0 // normal case: directory name matches package name + case fakePkgName: + i = 1 // synopses for commands + case "main": + i = 2 // directory contains a main package + default: + i = 3 // none of the above + } + if 0 <= i && i < len(synopses) && synopses[i] == "" { + synopses[i] = firstSentence(doc.CommentText(file.Doc)) + } + } + } + } + } + } + + // create subdirectory tree + var dirs []*Directory + if ndirs > 0 { + dirs = make([]*Directory, ndirs) + i := 0 + for _, d := range list { + if isPkgDir(d) { + dd := b.newDirTree(fset, pathutil.Join(path, d.Name), d.Name, depth+1) + if dd != nil { + dirs[i] = dd + i++ + } + } + } + dirs = dirs[0:i] + } + + // if there are no package files and no subdirectories + // containing package files, ignore the directory + if !hasPkgFiles && len(dirs) == 0 { + return nil + } + + // select the highest-priority synopsis for the directory entry, if any + synopsis := "" + for _, synopsis = range synopses { + if synopsis != "" { + break + } + } + + return &Directory{depth, path, name, synopsis, dirs} +} + + +// newDirectory creates a new package directory tree with at most maxDepth +// levels, anchored at root. The result tree is pruned such that it only +// contains directories that contain package files or that contain +// subdirectories containing package files (transitively). If a non-nil +// pathFilter is provided, directory paths additionally must be accepted +// by the filter (i.e., pathFilter(path) must be true). If a value >= 0 is +// provided for maxDepth, nodes at larger depths are pruned as well; they +// are assumed to contain package files even if their contents are not known +// (i.e., in this case the tree may contain directories w/o any package files). +// +func newDirectory(root string, pathFilter func(string) bool, maxDepth int) *Directory { + d, err := os.Lstat(root) + if err != nil || !isPkgDir(d) { + return nil + } + if maxDepth < 0 { + maxDepth = 1e6 // "infinity" + } + b := treeBuilder{pathFilter, maxDepth} + // the file set provided is only for local parsing, no position + // information escapes and thus we don't need to save the set + return b.newDirTree(token.NewFileSet(), root, d.Name, 0) +} + + +func (dir *Directory) writeLeafs(buf *bytes.Buffer) { + if dir != nil { + if len(dir.Dirs) == 0 { + buf.WriteString(dir.Path) + buf.WriteByte('\n') + return + } + + for _, d := range dir.Dirs { + d.writeLeafs(buf) + } + } +} + + +func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) { + if dir != nil { + if !skipRoot { + c <- dir + } + for _, d := range dir.Dirs { + d.walk(c, false) + } + } +} + + +func (dir *Directory) iter(skipRoot bool) <-chan *Directory { + c := make(chan *Directory) + go func() { + dir.walk(c, skipRoot) + close(c) + }() + return c +} + + +func (dir *Directory) lookupLocal(name string) *Directory { + for _, d := range dir.Dirs { + if d.Name == name { + return d + } + } + return nil +} + + +// lookup looks for the *Directory for a given path, relative to dir. +func (dir *Directory) lookup(path string) *Directory { + d := strings.Split(dir.Path, "/", -1) + p := strings.Split(path, "/", -1) + i := 0 + for i < len(d) { + if i >= len(p) || d[i] != p[i] { + return nil + } + i++ + } + for dir != nil && i < len(p) { + dir = dir.lookupLocal(p[i]) + i++ + } + return dir +} + + +// DirEntry describes a directory entry. The Depth and Height values +// are useful for presenting an entry in an indented fashion. +// +type DirEntry struct { + Depth int // >= 0 + Height int // = DirList.MaxHeight - Depth, > 0 + Path string // includes Name, relative to DirList root + Name string + Synopsis string +} + + +type DirList struct { + MaxHeight int // directory tree height, > 0 + List []DirEntry +} + + +// listing creates a (linear) directory listing from a directory tree. +// If skipRoot is set, the root directory itself is excluded from the list. +// +func (root *Directory) listing(skipRoot bool) *DirList { + if root == nil { + return nil + } + + // determine number of entries n and maximum height + n := 0 + minDepth := 1 << 30 // infinity + maxDepth := 0 + for d := range root.iter(skipRoot) { + n++ + if minDepth > d.Depth { + minDepth = d.Depth + } + if maxDepth < d.Depth { + maxDepth = d.Depth + } + } + maxHeight := maxDepth - minDepth + 1 + + if n == 0 { + return nil + } + + // create list + list := make([]DirEntry, n) + i := 0 + for d := range root.iter(skipRoot) { + p := &list[i] + p.Depth = d.Depth - minDepth + p.Height = maxHeight - p.Depth + // the path is relative to root.Path - remove the root.Path + // prefix (the prefix should always be present but avoid + // crashes and check) + path := d.Path + if strings.HasPrefix(d.Path, root.Path) { + path = d.Path[len(root.Path):] + } + // remove trailing '/' if any - path must be relative + if len(path) > 0 && path[0] == '/' { + path = path[1:] + } + p.Path = path + p.Name = d.Name + p.Synopsis = d.Text + i++ + } + + return &DirList{maxHeight, list} +} diff --git a/src/cmd/godoc/doc.go b/src/cmd/godoc/doc.go index 955ed35bf..02779384c 100644 --- a/src/cmd/godoc/doc.go +++ b/src/cmd/godoc/doc.go @@ -24,7 +24,7 @@ godoc first tries localhost:6060 and then http://golang.org. godoc -q Reader Writer godoc -q math.Sin - godoc -server=:6666 -q sin + godoc -server=:6060 -q sin With the -http flag, it runs as a web server and presents the documentation as a web page. @@ -45,6 +45,10 @@ The flags are: print (exported) source in command-line mode -tabwidth=4 width of tabs in units of spaces + -timestamps=true + show timestamps with directory listings + -fulltext=false + build full text index for regular expression queries -path="" additional package directories (colon-separated) -html @@ -61,6 +65,10 @@ The flags are: repository holding the source files. -sync_minutes=0 sync interval in minutes; sync is disabled if <= 0 + -filter="" + filter file containing permitted package directory paths + -filter_minutes=0 + filter file update interval in minutes; update is disabled if <= 0 The -path flag accepts a list of colon-separated paths; unrooted paths are relative to the current working directory. Each path is considered as an additional root for @@ -76,6 +84,13 @@ as follows: /home/bar/x -> bar/x /public/x -> public/x +Paths provided via -path may point to very large file systems that contain +non-Go files. Creating the subtree of directories with Go packages may take +a long amount of time. A file containing newline-separated directory paths +may be provided with the -filter flag; if it exists, only directories +on those paths are considered. If -filter_minutes is set, the filter_file is +updated regularly by walking the entire directory tree. + When godoc runs as a web server, it creates a search index from all .go files under -goroot (excluding files starting with .). The index is created at startup and is automatically updated every time the -sync command terminates with exit diff --git a/src/cmd/godoc/format.go b/src/cmd/godoc/format.go new file mode 100644 index 000000000..f68c67b24 --- /dev/null +++ b/src/cmd/godoc/format.go @@ -0,0 +1,342 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements FormatSelections and FormatText. +// FormatText is used to HTML-format Go and non-Go source +// text with line numbers and highlighted sections. It is +// built on top of FormatSelections, a generic formatter +// for "selected" text. + +package main + +import ( + "bytes" + "fmt" + "go/scanner" + "go/token" + "io" + "regexp" + "strconv" + "template" +) + + +// ---------------------------------------------------------------------------- +// Implementation of FormatSelections + +// A Selection is a function returning offset pairs []int{a, b} +// describing consecutive non-overlapping text segments [a, b). +// If there are no more segments, a Selection must return nil. +// +// TODO It's more efficient to return a pair (a, b int) instead +// of creating lots of slices. Need to determine how to +// indicate the end of a Selection. +// +type Selection func() []int + + +// A LinkWriter writes some start or end "tag" to w for the text offset offs. +// It is called by FormatSelections at the start or end of each link segment. +// +type LinkWriter func(w io.Writer, offs int, start bool) + + +// A SegmentWriter formats a text according to selections and writes it to w. +// The selections parameter is a bit set indicating which selections provided +// to FormatSelections overlap with the text segment: If the n'th bit is set +// in selections, the n'th selection provided to FormatSelections is overlapping +// with the text. +// +type SegmentWriter func(w io.Writer, text []byte, selections int) + + +// FormatSelections takes a text and writes it to w using link and segment +// writers lw and sw as follows: lw is invoked for consecutive segment starts +// and ends as specified through the links selection, and sw is invoked for +// consecutive segments of text overlapped by the same selections as specified +// by selections. The link writer lw may be nil, in which case the links +// Selection is ignored. +// +func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, sw SegmentWriter, selections ...Selection) { + if lw != nil { + selections = append(selections, links) + } + // compute the sequence of consecutive segment changes + changes := newMerger(selections) + // The i'th bit in bitset indicates that the text + // at the current offset is covered by selections[i]. + bitset := 0 + lastOffs := 0 + for { + // get the next segment change + index, offs, start := changes.next() + if index < 0 || offs > len(text) { + // no more segment changes or the next change + // is past the end of the text - we're done + break + } + // determine the kind of segment change + if index == len(selections)-1 { + // we have a link segment change: + // format the previous selection segment, write the + // link tag and start a new selection segment + sw(w, text[lastOffs:offs], bitset) + lastOffs = offs + lw(w, offs, start) + } else { + // we have a selection change: + // format the previous selection segment, determine + // the new selection bitset and start a new segment + sw(w, text[lastOffs:offs], bitset) + lastOffs = offs + mask := 1 << uint(index) + if start { + bitset |= mask + } else { + bitset &^= mask + } + } + } + sw(w, text[lastOffs:], bitset) +} + + +// A merger merges a slice of Selections and produces a sequence of +// consecutive segment change events through repeated next() calls. +// +type merger struct { + selections []Selection + segments [][]int // segments[i] is the next segment of selections[i] +} + + +const infinity int = 2e9 + +func newMerger(selections []Selection) *merger { + segments := make([][]int, len(selections)) + for i, sel := range selections { + segments[i] = []int{infinity, infinity} + if sel != nil { + if seg := sel(); seg != nil { + segments[i] = seg + } + } + } + return &merger{selections, segments} +} + + +// next returns the next segment change: index specifies the Selection +// to which the segment belongs, offs is the segment start or end offset +// as determined by the start value. If there are no more segment changes, +// next returns an index value < 0. +// +func (m *merger) next() (index, offs int, start bool) { + // find the next smallest offset where a segment starts or ends + offs = infinity + index = -1 + for i, seg := range m.segments { + switch { + case seg[0] < offs: + offs = seg[0] + index = i + start = true + case seg[1] < offs: + offs = seg[1] + index = i + start = false + } + } + if index < 0 { + // no offset found => all selections merged + return + } + // offset found - it's either the start or end offset but + // either way it is ok to consume the start offset: set it + // to infinity so it won't be considered in the following + // next call + m.segments[index][0] = infinity + if start { + return + } + // end offset found - consume it + m.segments[index][1] = infinity + // advance to the next segment for that selection + seg := m.selections[index]() + if seg == nil { + return + } + m.segments[index] = seg + return +} + + +// ---------------------------------------------------------------------------- +// Implementation of FormatText + +// lineSelection returns the line segments for text as a Selection. +func lineSelection(text []byte) Selection { + i, j := 0, 0 + return func() (seg []int) { + // find next newline, if any + for j < len(text) { + j++ + if text[j-1] == '\n' { + break + } + } + if i < j { + // text[i:j] constitutes a line + seg = []int{i, j} + i = j + } + return + } +} + + +// commentSelection returns the sequence of consecutive comments +// in the Go src text as a Selection. +// +func commentSelection(src []byte) Selection { + var s scanner.Scanner + file := s.Init(token.NewFileSet(), "", src, nil, scanner.ScanComments+scanner.InsertSemis) + return func() (seg []int) { + for { + pos, tok, lit := s.Scan() + if tok == token.EOF { + break + } + offs := file.Offset(pos) + if tok == token.COMMENT { + seg = []int{offs, offs + len(lit)} + break + } + } + return + } +} + + +// makeSelection is a helper function to make a Selection from a slice of pairs. +func makeSelection(matches [][]int) Selection { + return func() (seg []int) { + if len(matches) > 0 { + seg = matches[0] + matches = matches[1:] + } + return + } +} + + +// regexpSelection computes the Selection for the regular expression expr in text. +func regexpSelection(text []byte, expr string) Selection { + var matches [][]int + if rx, err := regexp.Compile(expr); err == nil { + matches = rx.FindAllIndex(text, -1) + } + return makeSelection(matches) +} + + +var selRx = regexp.MustCompile(`^([0-9]+):([0-9]+)`) + +// rangeSelection computes the Selection for a text range described +// by the argument str; the range description must match the selRx +// regular expression. +// +func rangeSelection(str string) Selection { + m := selRx.FindStringSubmatch(str) + if len(m) >= 2 { + from, _ := strconv.Atoi(m[1]) + to, _ := strconv.Atoi(m[2]) + if from < to { + return makeSelection([][]int{[]int{from, to}}) + } + } + return nil +} + + +// Span tags for all the possible selection combinations that may +// be generated by FormatText. Selections are indicated by a bitset, +// and the value of the bitset specifies the tag to be used. +// +// bit 0: comments +// bit 1: highlights +// bit 2: selections +// +var startTags = [][]byte{ + /* 000 */ []byte(``), + /* 001 */ []byte(`<span class ="comment">`), + /* 010 */ []byte(`<span class="highlight">`), + /* 011 */ []byte(`<span class="highlight-comment">`), + /* 100 */ []byte(`<span class="selection">`), + /* 101 */ []byte(`<span class="selection-comment">`), + /* 110 */ []byte(`<span class="selection-highlight">`), + /* 111 */ []byte(`<span class="selection-highlight-comment">`), +} + +var endTag = []byte(`</span>`) + + +func selectionTag(w io.Writer, text []byte, selections int) { + if len(text) > 0 { + if selections < len(startTags) { + if tag := startTags[selections]; len(tag) > 0 { + w.Write(tag) + template.HTMLEscape(w, text) + w.Write(endTag) + return + } + } + template.HTMLEscape(w, text) + } +} + + +// FormatText HTML-escapes text and returns it wrapped in <pre> tags. +// Conscutive text segments are wrapped in HTML spans (with tags as +// defined by startTags and endTag) as follows: +// +// - if line >= 0, line numbers are printed before each line, starting +// with the value of line +// - if the text is Go source, comments get the "comment" span class +// - each occurrence of the regular expression pattern gets the "highlight" +// span class +// - text segments covered by selection get the "selection" span class +// +// Comments, highlights, and selections may overlap arbitrarily; the respective +// HTML span classes are specified in the startTags variable. +// +func FormatText(text []byte, line int, goSource bool, pattern string, selection Selection) []byte { + var buf bytes.Buffer + buf.WriteString("<pre>\n") + + var comments, highlights Selection + if goSource { + comments = commentSelection(text) + } + if pattern != "" { + highlights = regexpSelection(text, pattern) + } + if comments != nil || highlights != nil || selection != nil { + var lineTag LinkWriter + if line >= 0 { + lineTag = func(w io.Writer, _ int, start bool) { + if start { + fmt.Fprintf(w, "<a id=\"L%d\"></a>%5d\t", line, line) + line++ + } + } + } + FormatSelections(&buf, text, lineTag, lineSelection(text), selectionTag, comments, highlights, selection) + } else { + template.HTMLEscape(&buf, text) + } + + buf.WriteString("</pre>\n") + return buf.Bytes() +} diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go index 61c53e2c3..d6054ab9d 100644 --- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -21,43 +21,15 @@ import ( pathutil "path" "regexp" "runtime" + "sort" "strings" - "sync" "template" "time" - "unicode" "utf8" ) // ---------------------------------------------------------------------------- -// Support types - -// An RWValue wraps a value and permits mutually exclusive -// access to it and records the time the value was last set. -type RWValue struct { - mutex sync.RWMutex - value interface{} - timestamp int64 // time of last set(), in seconds since epoch -} - - -func (v *RWValue) set(value interface{}) { - v.mutex.Lock() - v.value = value - v.timestamp = time.Seconds() - v.mutex.Unlock() -} - - -func (v *RWValue) get() (interface{}, int64) { - v.mutex.RLock() - defer v.mutex.RUnlock() - return v.value, v.timestamp -} - - -// ---------------------------------------------------------------------------- // Globals type delayTime struct { @@ -72,6 +44,7 @@ func (dt *delayTime) backoff(max int) { v = max } dt.value = v + // don't change dt.timestamp - calling backoff indicates an error condition dt.mutex.Unlock() } @@ -80,15 +53,24 @@ var ( verbose = flag.Bool("v", false, "verbose mode") // file system roots - goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory") - path = flag.String("path", "", "additional package directories (colon-separated)") + // TODO(gri) consider the invariant that goroot always end in '/' + goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory") + testDir = flag.String("testdir", "", "Go root subdirectory - for testing only (faster startups)") + path = flag.String("path", "", "additional package directories (colon-separated)") + filter = flag.String("filter", "", "filter file containing permitted package directory paths") + filterMin = flag.Int("filter_minutes", 0, "filter file update interval in minutes; disabled if <= 0") + filterDelay delayTime // actual filter update interval in minutes; usually filterDelay == filterMin, but filterDelay may back off exponentially // layout control - tabwidth = flag.Int("tabwidth", 4, "tab width") + tabwidth = flag.Int("tabwidth", 4, "tab width") + showTimestamps = flag.Bool("timestamps", true, "show timestamps with directory listings") + fulltextIndex = flag.Bool("fulltext", false, "build full text index for regular expression queries") // file system mapping - fsMap Mapping // user-defined mapping - fsTree RWValue // *Directory tree of packages, updated with each sync + fsMap Mapping // user-defined mapping + fsTree RWValue // *Directory tree of packages, updated with each sync + pathFilter RWValue // filter used when building fsMap directory trees + fsModified RWValue // timestamp of last call to invalidateIndex // http handlers fileServer http.Handler // default file server @@ -114,410 +96,179 @@ func registerPublicHandlers(mux *http.ServeMux) { } -// ---------------------------------------------------------------------------- -// Predicates and small utility functions - -func isGoFile(f *os.FileInfo) bool { - return f.IsRegular() && - !strings.HasPrefix(f.Name, ".") && // ignore .files - pathutil.Ext(f.Name) == ".go" -} - - -func isPkgFile(f *os.FileInfo) bool { - return isGoFile(f) && - !strings.HasSuffix(f.Name, "_test.go") // ignore test files +func initFSTree() { + fsTree.set(newDirectory(pathutil.Join(*goroot, *testDir), nil, -1)) + invalidateIndex() } -func isPkgDir(f *os.FileInfo) bool { - return f.IsDirectory() && len(f.Name) > 0 && f.Name[0] != '_' -} - - -func pkgName(filename string) string { - file, err := parser.ParseFile(filename, nil, nil, parser.PackageClauseOnly) - if err != nil || file == nil { - return "" - } - return file.Name.Name() -} - +// ---------------------------------------------------------------------------- +// Directory filters -func htmlEscape(s string) string { - var buf bytes.Buffer - template.HTMLEscape(&buf, []byte(s)) - return buf.String() +// isParentOf returns true if p is a parent of (or the same as) q +// where p and q are directory paths. +func isParentOf(p, q string) bool { + n := len(p) + return strings.HasPrefix(q, p) && (len(q) <= n || q[n] == '/') } -func firstSentence(s string) string { - i := -1 // index+1 of first period - j := -1 // index+1 of first period that is followed by white space - prev := 'A' - for k, ch := range s { - k1 := k + 1 - if ch == '.' { - if i < 0 { - i = k1 // first period - } - if k1 < len(s) && s[k1] <= ' ' { - if j < 0 { - j = k1 // first period followed by white space - } - if !unicode.IsUpper(prev) { - j = k1 - break - } - } - } - prev = ch - } - - if j < 0 { - // use the next best period - j = i - if j < 0 { - // no period at all, use the entire string - j = len(s) - } +func setPathFilter(list []string) { + if len(list) == 0 { + pathFilter.set(nil) + return } - return s[0:j] + // len(list) > 0 + pathFilter.set(func(path string) bool { + // list is sorted in increasing order and for each path all its children are removed + i := sort.Search(len(list), func(i int) bool { return list[i] > path }) + // Now we have list[i-1] <= path < list[i]. + // Path may be a child of list[i-1] or a parent of list[i]. + return i > 0 && isParentOf(list[i-1], path) || i < len(list) && isParentOf(path, list[i]) + }) } -func absolutePath(path, defaultRoot string) string { - abspath := fsMap.ToAbsolute(path) - if abspath == "" { - // no user-defined mapping found; use default mapping - abspath = pathutil.Join(defaultRoot, path) +func getPathFilter() func(string) bool { + f, _ := pathFilter.get() + if f != nil { + return f.(func(string) bool) } - return abspath + return nil } -func relativePath(path string) string { - relpath := fsMap.ToRelative(path) - if relpath == "" && strings.HasPrefix(path, *goroot+"/") { - // no user-defined mapping found; use default mapping - relpath = path[len(*goroot)+1:] +// readDirList reads a file containing a newline-separated list +// of directory paths and returns the list of paths. +func readDirList(filename string) ([]string, os.Error) { + contents, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err } - // Only if path is an invalid absolute path is relpath == "" - // at this point. This should never happen since absolute paths - // are only created via godoc for files that do exist. However, - // it is ok to return ""; it will simply provide a link to the - // top of the pkg or src directories. - return relpath -} - - -// ---------------------------------------------------------------------------- -// Package directories - -type Directory struct { - Depth int - Path string // includes Name - Name string - Text string // package documentation, if any - Dirs []*Directory // subdirectories -} - - -func newDirTree(path, name string, depth, maxDepth int) *Directory { - if depth >= maxDepth { - // return a dummy directory so that the parent directory - // doesn't get discarded just because we reached the max - // directory depth - return &Directory{depth, path, name, "", nil} + // create a sorted list of valid directory names + filter := func(path string) bool { + d, err := os.Lstat(path) + return err == nil && isPkgDir(d) } - - list, _ := ioutil.ReadDir(path) // ignore errors - - // determine number of subdirectories and package files - ndirs := 0 - nfiles := 0 - text := "" - for _, d := range list { - switch { - case isPkgDir(d): - ndirs++ - case isPkgFile(d): - nfiles++ - if text == "" { - // no package documentation yet; take the first found - file, err := parser.ParseFile(pathutil.Join(path, d.Name), nil, nil, - parser.ParseComments|parser.PackageClauseOnly) - if err == nil && - // Also accept fakePkgName, so we get synopses for commmands. - // Note: This may lead to incorrect results if there is a - // (left-over) "documentation" package somewhere in a package - // directory of different name, but this is very unlikely and - // against current conventions. - (file.Name.Name() == name || file.Name.Name() == fakePkgName) && - file.Doc != nil { - // found documentation; extract a synopsys - text = firstSentence(doc.CommentText(file.Doc)) - } - } - } - } - - // create subdirectory tree - var dirs []*Directory - if ndirs > 0 { - dirs = make([]*Directory, ndirs) - i := 0 - for _, d := range list { - if isPkgDir(d) { - dd := newDirTree(pathutil.Join(path, d.Name), d.Name, depth+1, maxDepth) - if dd != nil { - dirs[i] = dd - i++ - } - } + list := canonicalizePaths(strings.Split(string(contents), "\n", -1), filter) + // for each parent path, remove all it's children q + // (requirement for binary search to work when filtering) + i := 0 + for _, q := range list { + if i == 0 || !isParentOf(list[i-1], q) { + list[i] = q + i++ } - dirs = dirs[0:i] } - - // if there are no package files and no subdirectories - // (with package files), ignore the directory - if nfiles == 0 && len(dirs) == 0 { - return nil - } - - return &Directory{depth, path, name, text, dirs} + return list[0:i], nil } -// newDirectory creates a new package directory tree with at most maxDepth -// levels, anchored at root which is relative to goroot. The result tree -// only contains directories that contain package files or that contain -// subdirectories containing package files (transitively). +// updateMappedDirs computes the directory tree for +// each user-defined file system mapping. If a filter +// is provided, it is used to filter directories. // -func newDirectory(root string, maxDepth int) *Directory { - d, err := os.Lstat(root) - if err != nil || !isPkgDir(d) { - return nil - } - return newDirTree(root, d.Name, 0, maxDepth) -} - - -func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) { - if dir != nil { - if !skipRoot { - c <- dir - } - for _, d := range dir.Dirs { - d.walk(c, false) - } +func updateMappedDirs(filter func(string) bool) { + if !fsMap.IsEmpty() { + fsMap.Iterate(func(path string, value *RWValue) bool { + value.set(newDirectory(path, filter, -1)) + return true + }) + invalidateIndex() } } -func (dir *Directory) iter(skipRoot bool) <-chan *Directory { - c := make(chan *Directory) - go func() { - dir.walk(c, skipRoot) - close(c) - }() - return c -} - +func updateFilterFile() { + updateMappedDirs(nil) // no filter for accuracy -func (dir *Directory) lookupLocal(name string) *Directory { - for _, d := range dir.Dirs { - if d.Name == name { - return d + // collect directory tree leaf node paths + var buf bytes.Buffer + fsMap.Iterate(func(_ string, value *RWValue) bool { + v, _ := value.get() + if v != nil && v.(*Directory) != nil { + v.(*Directory).writeLeafs(&buf) } - } - return nil -} + return true + }) - -// lookup looks for the *Directory for a given path, relative to dir. -func (dir *Directory) lookup(path string) *Directory { - d := strings.Split(dir.Path, "/", -1) - p := strings.Split(path, "/", -1) - i := 0 - for i < len(d) { - if i >= len(p) || d[i] != p[i] { - return nil - } - i++ - } - for dir != nil && i < len(p) { - dir = dir.lookupLocal(p[i]) - i++ + // update filter file + if err := writeFileAtomically(*filter, buf.Bytes()); err != nil { + log.Printf("writeFileAtomically(%s): %s", *filter, err) + filterDelay.backoff(24 * 60) // back off exponentially, but try at least once a day + } else { + filterDelay.set(*filterMin) // revert to regular filter update schedule } - return dir -} - - -// DirEntry describes a directory entry. The Depth and Height values -// are useful for presenting an entry in an indented fashion. -// -type DirEntry struct { - Depth int // >= 0 - Height int // = DirList.MaxHeight - Depth, > 0 - Path string // includes Name, relative to DirList root - Name string - Synopsis string } -type DirList struct { - MaxHeight int // directory tree height, > 0 - List []DirEntry -} - - -// listing creates a (linear) directory listing from a directory tree. -// If skipRoot is set, the root directory itself is excluded from the list. -// -func (root *Directory) listing(skipRoot bool) *DirList { - if root == nil { - return nil - } - - // determine number of entries n and maximum height - n := 0 - minDepth := 1 << 30 // infinity - maxDepth := 0 - for d := range root.iter(skipRoot) { - n++ - if minDepth > d.Depth { - minDepth = d.Depth - } - if maxDepth < d.Depth { - maxDepth = d.Depth +func initDirTrees() { + // setup initial path filter + if *filter != "" { + list, err := readDirList(*filter) + if err != nil { + log.Printf("%s", err) + } else if len(list) == 0 { + log.Printf("no directory paths in file %s", *filter) } + setPathFilter(list) } - maxHeight := maxDepth - minDepth + 1 - if n == 0 { - return nil - } + go updateMappedDirs(getPathFilter()) // use filter for speed - // create list - list := make([]DirEntry, n) - i := 0 - for d := range root.iter(skipRoot) { - p := &list[i] - p.Depth = d.Depth - minDepth - p.Height = maxHeight - p.Depth - // the path is relative to root.Path - remove the root.Path - // prefix (the prefix should always be present but avoid - // crashes and check) - path := d.Path - if strings.HasPrefix(d.Path, root.Path) { - path = d.Path[len(root.Path):] - } - // remove trailing '/' if any - path must be relative - if len(path) > 0 && path[0] == '/' { - path = path[1:] - } - p.Path = path - p.Name = d.Name - p.Synopsis = d.Text - i++ + // start filter update goroutine, if enabled. + if *filter != "" && *filterMin > 0 { + filterDelay.set(*filterMin) // initial filter update delay + go func() { + for { + if *verbose { + log.Printf("start update of %s", *filter) + } + updateFilterFile() + delay, _ := filterDelay.get() + if *verbose { + log.Printf("next filter update in %dmin", delay.(int)) + } + time.Sleep(int64(delay.(int)) * 60e9) + } + }() } - - return &DirList{maxHeight, list} } // ---------------------------------------------------------------------------- -// HTML formatting support - -// Styler implements a printer.Styler. -type Styler struct { - linetags bool - highlight string - objmap map[*ast.Object]int - count int -} - - -func newStyler(highlight string) *Styler { - return &Styler{true, highlight, make(map[*ast.Object]int), 0} -} +// Path mapping - -func (s *Styler) id(obj *ast.Object) int { - n, found := s.objmap[obj] - if !found { - n = s.count - s.objmap[obj] = n - s.count++ - } - return n -} - - -func (s *Styler) mapping() []*ast.Object { - if s.objmap == nil { - return nil - } - m := make([]*ast.Object, s.count) - for obj, i := range s.objmap { - m[i] = obj - } - return m -} - - -// Use the defaultStyler when there is no specific styler. -// The defaultStyler does not emit line tags since they may -// interfere with tags emitted by templates. -// TODO(gri): Should emit line tags at the beginning of a line; -// never in the middle of code. -var defaultStyler Styler - - -func (s *Styler) LineTag(line int) (text []byte, tag printer.HTMLTag) { - if s.linetags { - tag = printer.HTMLTag{fmt.Sprintf(`<a id="L%d">`, line), "</a>"} +func absolutePath(path, defaultRoot string) string { + abspath := fsMap.ToAbsolute(path) + if abspath == "" { + // no user-defined mapping found; use default mapping + abspath = pathutil.Join(defaultRoot, path) } - return -} - - -func (s *Styler) Comment(c *ast.Comment, line []byte) (text []byte, tag printer.HTMLTag) { - text = line - // minimal syntax-coloring of comments for now - people will want more - // (don't do anything more until there's a button to turn it on/off) - tag = printer.HTMLTag{`<span class="comment">`, "</span>"} - return -} - - -func (s *Styler) BasicLit(x *ast.BasicLit) (text []byte, tag printer.HTMLTag) { - text = x.Value - return + return abspath } -func (s *Styler) Ident(id *ast.Ident) (text []byte, tag printer.HTMLTag) { - text = []byte(id.Name()) - var str string - if s.objmap != nil { - str = fmt.Sprintf(` id="%d"`, s.id(id.Obj)) - } - if s.highlight == id.Name() { - str += ` class="highlight"` - } - if str != "" { - tag = printer.HTMLTag{"<span" + str + ">", "</span>"} +func relativePath(path string) string { + relpath := fsMap.ToRelative(path) + if relpath == "" { + // prefix must end in '/' + prefix := *goroot + if len(prefix) > 0 && prefix[len(prefix)-1] != '/' { + prefix += "/" + } + if strings.HasPrefix(path, prefix) { + // no user-defined mapping found; use default mapping + relpath = path[len(prefix):] + } } - return -} - - -func (s *Styler) Token(tok token.Token) (text []byte, tag printer.HTMLTag) { - text = []byte(tok.String()) - return + // Only if path is an invalid absolute path is relpath == "" + // at this point. This should never happen since absolute paths + // are only created via godoc for files that do exist. However, + // it is ok to return ""; it will simply provide a link to the + // top of the pkg or src directories. + return relpath } @@ -597,7 +348,7 @@ func (p *tconv) Write(data []byte) (n int, err os.Error) { // Templates // Write an AST-node to w; optionally html-escaped. -func writeNode(w io.Writer, node interface{}, html bool, styler printer.Styler) { +func writeNode(w io.Writer, fset *token.FileSet, node interface{}, html bool) { mode := printer.TabIndent | printer.UseSpaces if html { mode |= printer.GenHTML @@ -606,7 +357,7 @@ func writeNode(w io.Writer, node interface{}, html bool, styler printer.Styler) // to ensure a good outcome in most browsers (there may still // be tabs in comments and strings, but converting those into // the right number of spaces is much harder) - (&printer.Config{mode, *tabwidth, styler}).Fprint(&tconv{output: w}, node) + (&printer.Config{mode, *tabwidth, nil}).Fprint(&tconv{output: w}, fset, node) } @@ -621,14 +372,14 @@ func writeText(w io.Writer, text []byte, html bool) { // Write anything to w; optionally html-escaped. -func writeAny(w io.Writer, x interface{}, html bool) { +func writeAny(w io.Writer, fset *token.FileSet, html bool, x interface{}) { switch v := x.(type) { case []byte: writeText(w, v, html) case string: writeText(w, []byte(v), html) case ast.Decl, ast.Expr, ast.Stmt, *ast.File: - writeNode(w, x, html, &defaultStyler) + writeNode(w, fset, x, html) default: if html { var buf bytes.Buffer @@ -641,24 +392,34 @@ func writeAny(w io.Writer, x interface{}, html bool) { } +func fileset(x []interface{}) *token.FileSet { + if len(x) > 1 { + if fset, ok := x[1].(*token.FileSet); ok { + return fset + } + } + return nil +} + + // Template formatter for "html" format. -func htmlFmt(w io.Writer, x interface{}, format string) { - writeAny(w, x, true) +func htmlFmt(w io.Writer, format string, x ...interface{}) { + writeAny(w, fileset(x), true, x[0]) } // Template formatter for "html-esc" format. -func htmlEscFmt(w io.Writer, x interface{}, format string) { +func htmlEscFmt(w io.Writer, format string, x ...interface{}) { var buf bytes.Buffer - writeAny(&buf, x, false) + writeAny(&buf, fileset(x), false, x[0]) template.HTMLEscape(w, buf.Bytes()) } // Template formatter for "html-comment" format. -func htmlCommentFmt(w io.Writer, x interface{}, format string) { +func htmlCommentFmt(w io.Writer, format string, x ...interface{}) { var buf bytes.Buffer - writeAny(&buf, x, false) + writeAny(&buf, fileset(x), false, x[0]) // TODO(gri) Provide list of words (e.g. function parameters) // to be emphasized by ToHTML. doc.ToHTML(w, buf.Bytes(), nil) // does html-escaping @@ -666,29 +427,49 @@ func htmlCommentFmt(w io.Writer, x interface{}, format string) { // Template formatter for "" (default) format. -func textFmt(w io.Writer, x interface{}, format string) { - writeAny(w, x, false) +func textFmt(w io.Writer, format string, x ...interface{}) { + writeAny(w, fileset(x), false, x[0]) +} + + +// Template formatter for "urlquery-esc" format. +func urlQueryEscFmt(w io.Writer, format string, x ...interface{}) { + var buf bytes.Buffer + writeAny(&buf, fileset(x), false, x[0]) + template.HTMLEscape(w, []byte(http.URLEscape(string(buf.Bytes())))) } -// Template formatter for the various "url-xxx" formats. -func urlFmt(w io.Writer, x interface{}, format string) { +// Template formatter for the various "url-xxx" formats excluding url-esc. +func urlFmt(w io.Writer, format string, x ...interface{}) { var path string var line int + var low, high int // selection // determine path and position info, if any type positioner interface { - Pos() token.Position + Pos() token.Pos + End() token.Pos } - switch t := x.(type) { + switch t := x[0].(type) { case string: path = t case positioner: - pos := t.Pos() - if pos.IsValid() { + fset := fileset(x) + if p := t.Pos(); p.IsValid() { + pos := fset.Position(p) path = pos.Filename line = pos.Line + low = pos.Offset } + if p := t.End(); p.IsValid() { + high = fset.Position(p).Offset + } + default: + // we should never reach here, but be resilient + // and assume the position is invalid (empty path, + // and line 0) + log.Printf("INTERNAL ERROR: urlFmt(%s) without a string or positioner", format) } // map path @@ -700,7 +481,7 @@ func urlFmt(w io.Writer, x interface{}, format string) { default: // we should never reach here, but be resilient // and assume the url-pkg format instead - log.Stderrf("INTERNAL ERROR: urlFmt(%s)", format) + log.Printf("INTERNAL ERROR: urlFmt(%s)", format) fallthrough case "url-pkg": // because of the irregular mapping under goroot @@ -712,10 +493,22 @@ func urlFmt(w io.Writer, x interface{}, format string) { case "url-src": template.HTMLEscape(w, []byte(relpath)) case "url-pos": + template.HTMLEscape(w, []byte(relpath)) + // selection ranges are of form "s=low:high" + if low < high { + fmt.Fprintf(w, "?s=%d:%d", low, high) + // if we have a selection, position the page + // such that the selection is a bit below the top + line -= 10 + if line < 1 { + line = 1 + } + } // line id's in html-printed source are of the // form "L%d" where %d stands for the line number - template.HTMLEscape(w, []byte(relpath)) - fmt.Fprintf(w, "#L%d", line) + if line > 0 { + fmt.Fprintf(w, "#L%d", line) + } } } @@ -734,14 +527,14 @@ var infoKinds = [nKinds]string{ // Template formatter for "infoKind" format. -func infoKindFmt(w io.Writer, x interface{}, format string) { - fmt.Fprintf(w, infoKinds[x.(SpotKind)]) // infoKind entries are html-escaped +func infoKindFmt(w io.Writer, format string, x ...interface{}) { + fmt.Fprintf(w, infoKinds[x[0].(SpotKind)]) // infoKind entries are html-escaped } // Template formatter for "infoLine" format. -func infoLineFmt(w io.Writer, x interface{}, format string) { - info := x.(SpotInfo) +func infoLineFmt(w io.Writer, format string, x ...interface{}) { + info := x[0].(SpotInfo) line := info.Lori() if info.IsIndex() { index, _ := searchIndex.get() @@ -760,58 +553,52 @@ func infoLineFmt(w io.Writer, x interface{}, format string) { // Template formatter for "infoSnippet" format. -func infoSnippetFmt(w io.Writer, x interface{}, format string) { - info := x.(SpotInfo) - text := `<span class="alert">no snippet text available</span>` +func infoSnippetFmt(w io.Writer, format string, x ...interface{}) { + info := x[0].(SpotInfo) + text := []byte(`<span class="alert">no snippet text available</span>`) if info.IsIndex() { index, _ := searchIndex.get() // no escaping of snippet text needed; // snippet text is escaped when generated text = index.(*Index).Snippet(info.Lori()).Text } - fmt.Fprint(w, text) + w.Write(text) } // Template formatter for "padding" format. -func paddingFmt(w io.Writer, x interface{}, format string) { - for i := x.(int); i > 0; i-- { +func paddingFmt(w io.Writer, format string, x ...interface{}) { + for i := x[0].(int); i > 0; i-- { fmt.Fprint(w, `<td width="25"></td>`) } } // Template formatter for "time" format. -func timeFmt(w io.Writer, x interface{}, format string) { - template.HTMLEscape(w, []byte(time.SecondsToLocalTime(x.(int64)/1e9).String())) +func timeFmt(w io.Writer, format string, x ...interface{}) { + template.HTMLEscape(w, []byte(time.SecondsToLocalTime(x[0].(int64)/1e9).String())) } // Template formatter for "dir/" format. -func dirslashFmt(w io.Writer, x interface{}, format string) { - if x.(*os.FileInfo).IsDirectory() { +func dirslashFmt(w io.Writer, format string, x ...interface{}) { + if x[0].(*os.FileInfo).IsDirectory() { w.Write([]byte{'/'}) } } // Template formatter for "localname" format. -func localnameFmt(w io.Writer, x interface{}, format string) { - _, localname := pathutil.Split(x.(string)) +func localnameFmt(w io.Writer, format string, x ...interface{}) { + _, localname := pathutil.Split(x[0].(string)) template.HTMLEscape(w, []byte(localname)) } -// Template formatter for "popupInfo" format. -func popupInfoFmt(w io.Writer, x interface{}, format string) { - obj := x.(*ast.Object) - // for now, show object kind and name; eventually - // do something more interesting (show declaration, - // for instance) - if obj.Kind != ast.Err { - fmt.Fprintf(w, "%s ", obj.Kind) - } - template.HTMLEscape(w, []byte(obj.Name)) +// Template formatter for "numlines" format. +func numlinesFmt(w io.Writer, format string, x ...interface{}) { + list := x[0].([]int) + fmt.Fprintf(w, "%d", len(list)) } @@ -820,6 +607,7 @@ var fmap = template.FormatterMap{ "html": htmlFmt, "html-esc": htmlEscFmt, "html-comment": htmlCommentFmt, + "urlquery-esc": urlQueryEscFmt, "url-pkg": urlFmt, "url-src": urlFmt, "url-pos": urlFmt, @@ -830,7 +618,7 @@ var fmap = template.FormatterMap{ "time": timeFmt, "dir/": dirslashFmt, "localname": localnameFmt, - "popupInfo": popupInfoFmt, + "numlines": numlinesFmt, } @@ -857,8 +645,7 @@ var ( packageHTML, packageText, searchHTML, - searchText, - sourceHTML *template.Template + searchText *template.Template ) func readTemplates() { @@ -872,46 +659,40 @@ func readTemplates() { packageText = readTemplate("package.txt") searchHTML = readTemplate("search.html") searchText = readTemplate("search.txt") - sourceHTML = readTemplate("source.html") } // ---------------------------------------------------------------------------- // Generic HTML wrapper -func servePage(c *http.Conn, title, subtitle, query string, content []byte) { - type Data struct { - Title string - Subtitle string - PkgRoots []string - Timestamp int64 - Query string - Version string - Menu []byte - Content []byte - } - - _, ts := fsTree.get() - d := Data{ - Title: title, - Subtitle: subtitle, - PkgRoots: fsMap.PrefixList(), - Timestamp: ts * 1e9, // timestamp in ns - Query: query, - Version: runtime.Version(), - Menu: nil, - Content: content, - } - - if err := godocHTML.Execute(&d, c); err != nil { - log.Stderrf("godocHTML.Execute: %s", err) +func servePage(w http.ResponseWriter, title, subtitle, query string, content []byte) { + d := struct { + Title string + Subtitle string + PkgRoots []string + Query string + Version string + Menu []byte + Content []byte + }{ + title, + subtitle, + fsMap.PrefixList(), + query, + runtime.Version(), + nil, + content, + } + + if err := godocHTML.Execute(&d, w); err != nil { + log.Printf("godocHTML.Execute: %s", err) } } -func serveText(c *http.Conn, text []byte) { - c.SetHeader("Content-Type", "text/plain; charset=utf-8") - c.Write(text) +func serveText(w http.ResponseWriter, text []byte) { + w.SetHeader("Content-Type", "text/plain; charset=utf-8") + w.Write(text) } @@ -926,27 +707,27 @@ var ( func extractString(src []byte, rx *regexp.Regexp) (s string) { - m := rx.Execute(src) - if len(m) >= 4 { - s = strings.TrimSpace(string(src[m[2]:m[3]])) + m := rx.FindSubmatch(src) + if m != nil { + s = strings.TrimSpace(string(m[1])) } return } -func serveHTMLDoc(c *http.Conn, r *http.Request, abspath, relpath string) { +func serveHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) { // get HTML body contents src, err := ioutil.ReadFile(abspath) if err != nil { - log.Stderrf("ioutil.ReadFile: %s", err) - serveError(c, r, relpath, err) + log.Printf("ioutil.ReadFile: %s", err) + serveError(w, r, relpath, err) return } // if it begins with "<!DOCTYPE " assume it is standalone // html that doesn't need the template wrapping. if bytes.HasPrefix(src, []byte("<!DOCTYPE ")) { - c.Write(src) + w.Write(src) return } @@ -965,45 +746,22 @@ func serveHTMLDoc(c *http.Conn, r *http.Request, abspath, relpath string) { } subtitle := extractString(src, subtitleRx) - servePage(c, title, subtitle, "", src) + servePage(w, title, subtitle, "", src) } func applyTemplate(t *template.Template, name string, data interface{}) []byte { var buf bytes.Buffer if err := t.Execute(data, &buf); err != nil { - log.Stderrf("%s.Execute: %s", name, err) + log.Printf("%s.Execute: %s", name, err) } return buf.Bytes() } -func serveGoSource(c *http.Conn, r *http.Request, abspath, relpath string) { - file, err := parser.ParseFile(abspath, nil, nil, parser.ParseComments) - if err != nil { - log.Stderrf("parser.ParseFile: %s", err) - serveError(c, r, relpath, err) - return - } - - var buf bytes.Buffer - styler := newStyler(r.FormValue("h")) - writeNode(&buf, file, true, styler) - - type SourceInfo struct { - Source []byte - Data []*ast.Object - } - info := &SourceInfo{buf.Bytes(), styler.mapping()} - - contents := applyTemplate(sourceHTML, "sourceHTML", info) - servePage(c, "Source file "+relpath, "", "", contents) -} - - -func redirect(c *http.Conn, r *http.Request) (redirected bool) { +func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) { if canonical := pathutil.Clean(r.URL.Path) + "/"; r.URL.Path != canonical { - http.Redirect(c, canonical, http.StatusMovedPermanently) + http.Redirect(w, r, canonical, http.StatusMovedPermanently) redirected = true } return @@ -1057,32 +815,28 @@ func isTextFile(path string) bool { } -func serveTextFile(c *http.Conn, r *http.Request, abspath, relpath string) { +func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) { src, err := ioutil.ReadFile(abspath) if err != nil { - log.Stderrf("ioutil.ReadFile: %s", err) - serveError(c, r, relpath, err) + log.Printf("ioutil.ReadFile: %s", err) + serveError(w, r, relpath, err) return } - var buf bytes.Buffer - fmt.Fprintln(&buf, "<pre>") - template.HTMLEscape(&buf, src) - fmt.Fprintln(&buf, "</pre>") - - servePage(c, "Text file "+relpath, "", "", buf.Bytes()) + contents := FormatText(src, 1, pathutil.Ext(abspath) == ".go", r.FormValue("h"), rangeSelection(r.FormValue("s"))) + servePage(w, title+" "+relpath, "", "", contents) } -func serveDirectory(c *http.Conn, r *http.Request, abspath, relpath string) { - if redirect(c, r) { +func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath string) { + if redirect(w, r) { return } list, err := ioutil.ReadDir(abspath) if err != nil { - log.Stderrf("ioutil.ReadDir: %s", err) - serveError(c, r, relpath, err) + log.Printf("ioutil.ReadDir: %s", err) + serveError(w, r, relpath, err) return } @@ -1093,23 +847,23 @@ func serveDirectory(c *http.Conn, r *http.Request, abspath, relpath string) { } contents := applyTemplate(dirlistHTML, "dirlistHTML", list) - servePage(c, "Directory "+relpath, "", "", contents) + servePage(w, "Directory "+relpath, "", "", contents) } -func serveFile(c *http.Conn, r *http.Request) { +func serveFile(w http.ResponseWriter, r *http.Request) { relpath := r.URL.Path[1:] // serveFile URL paths start with '/' abspath := absolutePath(relpath, *goroot) // pick off special cases and hand the rest to the standard file server switch r.URL.Path { case "/": - serveHTMLDoc(c, r, pathutil.Join(*goroot, "doc/root.html"), "doc/root.html") + serveHTMLDoc(w, r, pathutil.Join(*goroot, "doc/root.html"), "doc/root.html") return case "/doc/root.html": // hide landing page from its real name - http.Redirect(c, "/", http.StatusMovedPermanently) + http.Redirect(w, r, "/", http.StatusMovedPermanently) return } @@ -1118,42 +872,42 @@ func serveFile(c *http.Conn, r *http.Request) { if strings.HasSuffix(abspath, "/index.html") { // We'll show index.html for the directory. // Use the dir/ version as canonical instead of dir/index.html. - http.Redirect(c, r.URL.Path[0:len(r.URL.Path)-len("index.html")], http.StatusMovedPermanently) + http.Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len("index.html")], http.StatusMovedPermanently) return } - serveHTMLDoc(c, r, abspath, relpath) + serveHTMLDoc(w, r, abspath, relpath) return case ".go": - serveGoSource(c, r, abspath, relpath) + serveTextFile(w, r, abspath, relpath, "Source file") return } dir, err := os.Lstat(abspath) if err != nil { - log.Stderr(err) - serveError(c, r, relpath, err) + log.Print(err) + serveError(w, r, relpath, err) return } if dir != nil && dir.IsDirectory() { - if redirect(c, r) { + if redirect(w, r) { return } if index := abspath + "/index.html"; isTextFile(index) { - serveHTMLDoc(c, r, index, relativePath(index)) + serveHTMLDoc(w, r, index, relativePath(index)) return } - serveDirectory(c, r, abspath, relpath) + serveDirectory(w, r, abspath, relpath) return } if isTextFile(abspath) { - serveTextFile(c, r, abspath, relpath) + serveTextFile(w, r, abspath, relpath, "Text file") return } - fileServer.ServeHTTP(c, r) + fileServer.ServeHTTP(w, r) } @@ -1169,17 +923,19 @@ type PageInfoMode uint const ( exportsOnly PageInfoMode = 1 << iota // only keep exported stuff genDoc // generate documentation - tryMode // don't log errors ) type PageInfo struct { Dirname string // directory containing the package PList []string // list of package names found + FSet *token.FileSet // corresponding file set PAst *ast.File // nil if no single AST with package exports PDoc *doc.PackageDoc // nil if no single package documentation Dirs *DirList // nil if no directory information + DirTime int64 // directory time stamp in seconds since epoch IsPkg bool // false if this is not documenting a real package + Err os.Error // directory read error or nil } @@ -1193,10 +949,10 @@ type httpHandler struct { // getPageInfo returns the PageInfo for a package directory abspath. If the // parameter genAST is set, an AST containing only the package exports is // computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc) -// is extracted from the AST. If the parameter try is set, no errors are -// logged if getPageInfo fails. If there is no corresponding package in the -// directory, PageInfo.PDoc and PageInfo.PExp are nil. If there are no sub- -// directories, PageInfo.Dirs is nil. +// is extracted from the AST. If there is no corresponding package in the +// directory, PageInfo.PAst and PageInfo.PDoc are nil. If there are no sub- +// directories, PageInfo.Dirs is nil. If a directory read error occurred, +// PageInfo.Err is set to the respective error but the error is not logged. // func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInfoMode) PageInfo { // filter function to select the desired .go files @@ -1207,10 +963,12 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf } // get package ASTs - pkgs, err := parser.ParseDir(abspath, filter, parser.ParseComments) - if err != nil && mode&tryMode != 0 { - // TODO: errors should be shown instead of an empty directory - log.Stderrf("parser.parseDir: %s", err) + fset := token.NewFileSet() + pkgs, err := parser.ParseDir(fset, abspath, filter, parser.ParseComments) + if err != nil && pkgs == nil { + // only report directory read errors, ignore parse errors + // (may be able to extract partial package information) + return PageInfo{Dirname: abspath, Err: err} } // select package @@ -1258,7 +1016,7 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf // (excluding the selected package, if any). plist = make([]string, len(pkgs)) i := 0 - for name, _ := range pkgs { + for name := range pkgs { if pkg == nil || name != pkg.Name { plist[i] = name i++ @@ -1283,26 +1041,52 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf // get directory information var dir *Directory - if tree, _ := fsTree.get(); tree != nil && tree.(*Directory) != nil { + var timestamp int64 + if tree, ts := fsTree.get(); tree != nil && tree.(*Directory) != nil { // directory tree is present; lookup respective directory // (may still fail if the file system was updated and the // new directory tree has not yet been computed) - // TODO(gri) Need to build directory tree for fsMap entries dir = tree.(*Directory).lookup(abspath) + timestamp = ts + } + if dir == nil { + // the path may refer to a user-specified file system mapped + // via fsMap; lookup that mapping and corresponding RWValue + // if any + var v *RWValue + fsMap.Iterate(func(path string, value *RWValue) bool { + if isParentOf(path, abspath) { + // mapping found + v = value + return false + } + return true + }) + if v != nil { + // found a RWValue associated with a user-specified file + // system; a non-nil RWValue stores a (possibly out-of-date) + // directory tree for that file system + if tree, ts := v.get(); tree != nil && tree.(*Directory) != nil { + dir = tree.(*Directory).lookup(abspath) + timestamp = ts + } + } } if dir == nil { - // no directory tree present (either early after startup - // or command-line mode, or we don't build a tree for the - // directory; e.g. google3); compute one level for this page - dir = newDirectory(abspath, 1) + // no directory tree present (too early after startup or + // command-line mode); compute one level for this page + // note: cannot use path filter here because in general + // it doesn't contain the fsTree path + dir = newDirectory(abspath, nil, 1) + timestamp = time.Seconds() } - return PageInfo{abspath, plist, past, pdoc, dir.listing(true), h.isPkg} + return PageInfo{abspath, plist, fset, past, pdoc, dir.listing(true), timestamp, h.isPkg, nil} } -func (h *httpHandler) ServeHTTP(c *http.Conn, r *http.Request) { - if redirect(c, r) { +func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if redirect(w, r) { return } @@ -1313,17 +1097,22 @@ func (h *httpHandler) ServeHTTP(c *http.Conn, r *http.Request) { mode |= genDoc } info := h.getPageInfo(abspath, relpath, r.FormValue("p"), mode) + if info.Err != nil { + log.Print(info.Err) + serveError(w, r, relpath, info.Err) + return + } if r.FormValue("f") == "text" { contents := applyTemplate(packageText, "packageText", info) - serveText(c, contents) + serveText(w, contents) return } - var title string + var title, subtitle string switch { case info.PAst != nil: - title = "Package " + info.PAst.Name.Name() + title = "Package " + info.PAst.Name.Name case info.PDoc != nil: switch { case h.isPkg: @@ -1337,10 +1126,13 @@ func (h *httpHandler) ServeHTTP(c *http.Conn, r *http.Request) { } default: title = "Directory " + relativePath(info.Dirname) + if *showTimestamps { + subtitle = "Last update: " + time.SecondsToLocalTime(info.DirTime).String() + } } contents := applyTemplate(packageHTML, "packageHTML", info) - servePage(c, title, "", "", contents) + servePage(w, title, subtitle, "", contents) } @@ -1350,71 +1142,160 @@ func (h *httpHandler) ServeHTTP(c *http.Conn, r *http.Request) { var searchIndex RWValue type SearchResult struct { - Query string - Hit *LookupResult - Alt *AltWords - Illegal bool - Accurate bool + Query string + Alert string // error or warning message + + // identifier matches + Hit *LookupResult // identifier matches of Query + Alt *AltWords // alternative identifiers to look for + + // textual matches + Found int // number of textual occurrences found + Textual []FileLines // textual matches of Query + Complete bool // true if all textual occurrences of Query are reported } func lookup(query string) (result SearchResult) { result.Query = query + + // determine identifier lookup string and full text regexp + lookupStr := "" + lookupRx, err := regexp.Compile(query) + if err != nil { + result.Alert = "Error in query regular expression: " + err.String() + return + } + if prefix, complete := lookupRx.LiteralPrefix(); complete { + // otherwise we lookup "" (with no result) because + // identifier lookup doesn't support regexp search + lookupStr = prefix + } + if index, timestamp := searchIndex.get(); index != nil { - result.Hit, result.Alt, result.Illegal = index.(*Index).Lookup(query) - _, ts := fsTree.get() - result.Accurate = timestamp >= ts + // identifier search + index := index.(*Index) + result.Hit, result.Alt, err = index.Lookup(lookupStr) + if err != nil && !*fulltextIndex { + // ignore the error if there is full text search + // since it accepts that query regular expression + result.Alert = "Error in query string: " + err.String() + return + } + + // textual search + // TODO(gri) should max be a flag? + const max = 10000 // show at most this many fulltext results + result.Found, result.Textual = index.LookupRegexp(lookupRx, max+1) + result.Complete = result.Found <= max + + // is the result accurate? + if _, ts := fsModified.get(); timestamp < ts { + result.Alert = "Indexing in progress: result may be inaccurate" + } } return } -func search(c *http.Conn, r *http.Request) { +func search(w http.ResponseWriter, r *http.Request) { query := strings.TrimSpace(r.FormValue("q")) result := lookup(query) if r.FormValue("f") == "text" { contents := applyTemplate(searchText, "searchText", result) - serveText(c, contents) + serveText(w, contents) return } var title string - if result.Hit != nil { + if result.Hit != nil || len(result.Textual) > 0 { title = fmt.Sprintf(`Results for query %q`, query) } else { title = fmt.Sprintf(`No results found for query %q`, query) } contents := applyTemplate(searchHTML, "searchHTML", result) - servePage(c, title, "", query, contents) + servePage(w, title, "", query, contents) } // ---------------------------------------------------------------------------- // Indexer +// invalidateIndex should be called whenever any of the file systems +// under godoc's observation change so that the indexer is kicked on. +// +func invalidateIndex() { + fsModified.set(nil) +} + + +// indexUpToDate() returns true if the search index is not older +// than any of the file systems under godoc's observation. +// +func indexUpToDate() bool { + _, fsTime := fsModified.get() + _, siTime := searchIndex.get() + return fsTime <= siTime +} + + +// feedDirnames feeds the directory names of all directories +// under the file system given by root to channel c. +// +func feedDirnames(root *RWValue, c chan<- string) { + if dir, _ := root.get(); dir != nil { + for d := range dir.(*Directory).iter(false) { + c <- d.Path + } + } +} + + +// fsDirnames() returns a channel sending all directory names +// of all the file systems under godoc's observation. +// +func fsDirnames() <-chan string { + c := make(chan string, 256) // asynchronous for fewer context switches + go func() { + feedDirnames(&fsTree, c) + fsMap.Iterate(func(_ string, root *RWValue) bool { + feedDirnames(root, c) + return true + }) + close(c) + }() + return c +} + + func indexer() { for { - _, ts := fsTree.get() - if _, timestamp := searchIndex.get(); timestamp < ts { + if !indexUpToDate() { // index possibly out of date - make a new one - // (could use a channel to send an explicit signal - // from the sync goroutine, but this solution is - // more decoupled, trivial, and works well enough) + if *verbose { + log.Printf("updating index...") + } start := time.Nanoseconds() - index := NewIndex(*goroot) + index := NewIndex(fsDirnames(), *fulltextIndex) stop := time.Nanoseconds() searchIndex.set(index) if *verbose { secs := float64((stop-start)/1e6) / 1e3 - nwords, nspots := index.Size() - log.Stderrf("index updated (%gs, %d unique words, %d spots)", secs, nwords, nspots) + stats := index.Stats() + log.Printf("index updated (%gs, %d bytes of source, %d files, %d lines, %d unique words, %d spots)", + secs, stats.Bytes, stats.Files, stats.Lines, stats.Words, stats.Spots) } - log.Stderrf("bytes=%d footprint=%d\n", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys) + log.Printf("before GC: bytes = %d footprint = %d", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys) runtime.GC() - log.Stderrf("bytes=%d footprint=%d\n", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys) + log.Printf("after GC: bytes = %d footprint = %d", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys) + } + var delay int64 = 60 * 1e9 // by default, try every 60s + if *testDir != "" { + // in test mode, try once a second for fast startup + delay = 1 * 1e9 } - time.Sleep(1 * 60e9) // try once a minute + time.Sleep(delay) } } diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go index 8745b8b0a..ba6fe9acd 100644 --- a/src/cmd/godoc/index.go +++ b/src/cmd/godoc/index.go @@ -3,11 +3,11 @@ // license that can be found in the LICENSE file. // This file contains the infrastructure to create an -// (identifier) index for a set of Go files. +// identifier and full-text index for a set of Go files. // -// Basic indexing algorithm: +// Algorithm for identifier index: // - traverse all .go files of the file tree specified by root -// - for each word (identifier) encountered, collect all occurences (spots) +// - for each word (identifier) encountered, collect all occurrences (spots) // into a list; this produces a list of spots for each word // - reduce the lists: from a list of spots to a list of FileRuns, // and from a list of FileRuns into a list of PakRuns @@ -21,17 +21,34 @@ // (the line number for spots with snippets is stored in the snippet) // - at the end, create lists of alternative spellings for a given // word +// +// Algorithm for full text index: +// - concatenate all source code in a byte buffer (in memory) +// - add the files to a file set in lockstep as they are added to the byte +// buffer such that a byte buffer offset corresponds to the Pos value for +// that file location +// - create a suffix array from the concatenated sources +// +// String lookup in full text index: +// - use the suffix array to lookup a string's offsets - the offsets +// correspond to the Pos values relative to the file set +// - translate the Pos values back into file and line information and +// sort the result package main import ( + "bytes" "container/vector" "go/ast" "go/parser" "go/token" "go/scanner" + "index/suffixarray" + "io/ioutil" "os" pathutil "path" + "regexp" "sort" "strings" ) @@ -231,7 +248,7 @@ type File struct { } -// A Spot describes a single occurence of a word. +// A Spot describes a single occurrence of a word. type Spot struct { File *File Info SpotInfo @@ -419,7 +436,17 @@ const excludeTestFiles = false type IndexResult struct { Decls RunList // package-level declarations (with snippets) - Others RunList // all other occurences + Others RunList // all other occurrences +} + + +// Statistics provides statistics information for an index. +type Statistics struct { + Bytes int // total size of indexed source files + Files int // number of indexed source files + Lines int // number of lines (all files) + Words int // number of different identifiers + Spots int // number of identifier occurrences } @@ -428,11 +455,14 @@ type IndexResult struct { // interface for walking file trees, and the ast.Visitor interface for // walking Go ASTs. type Indexer struct { + fset *token.FileSet // file set for all indexed files + sources bytes.Buffer // concatenated sources words map[string]*IndexResult // RunLists of Spots snippets vector.Vector // vector of *Snippets, indexed by snippet indices - file *File // current file - decl ast.Decl // current decl - nspots int // number of spots encountered + current *token.File // last file added to file set + file *File // AST for current file + decl ast.Decl // AST for current decl + stats Statistics } @@ -452,24 +482,24 @@ func (x *Indexer) visitComment(c *ast.CommentGroup) { func (x *Indexer) visitIdent(kind SpotKind, id *ast.Ident) { if id != nil { - lists, found := x.words[id.Name()] + lists, found := x.words[id.Name] if !found { lists = new(IndexResult) - x.words[id.Name()] = lists + x.words[id.Name] = lists } if kind == Use || x.decl == nil { // not a declaration or no snippet required - info := makeSpotInfo(kind, id.Pos().Line, false) + info := makeSpotInfo(kind, x.current.Line(id.Pos()), false) lists.Others.Push(Spot{x.file, info}) } else { // a declaration with snippet - index := x.addSnippet(NewSnippet(x.decl, id)) + index := x.addSnippet(NewSnippet(x.fset, x.decl, id)) info := makeSpotInfo(kind, index, true) lists.Decls.Push(Spot{x.file, info}) } - x.nspots++ + x.stats.Spots++ } } @@ -506,7 +536,7 @@ func (x *Indexer) visitSpec(spec ast.Spec, isVarDecl bool) { } -func (x *Indexer) Visit(node interface{}) ast.Visitor { +func (x *Indexer) Visit(node ast.Node) ast.Visitor { // TODO(gri): methods in interface types are categorized as VarDecl switch n := node.(type) { case nil: @@ -578,16 +608,69 @@ func (x *Indexer) Visit(node interface{}) ast.Visitor { } -func (x *Indexer) VisitDir(path string, f *os.FileInfo) bool { - return true +func pkgName(filename string) string { + // use a new file set each time in order to not pollute the indexer's + // file set (which must stay in sync with the concatenated source code) + file, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.PackageClauseOnly) + if err != nil || file == nil { + return "" + } + return file.Name.Name +} + + +func (x *Indexer) addFile(filename string) *ast.File { + // open file + f, err := os.Open(filename, os.O_RDONLY, 0) + if err != nil { + return nil + } + defer f.Close() + + // The file set's base offset and x.sources size must be in lock-step; + // this permits the direct mapping of suffix array lookup results to + // to corresponding Pos values. + // + // When a file is added to the file set, it's offset base increases by + // the size of the file + 1; and the initial base offset is 1. Add an + // extra byte to the sources here. + x.sources.WriteByte(0) + + // If the sources length doesn't match the file set base at this point + // the file set implementation changed or we have another error. + base := x.fset.Base() + if x.sources.Len() != base { + panic("internal error - file base incorrect") + } + + // append file contents to x.sources + if _, err := x.sources.ReadFrom(f); err != nil { + x.sources.Truncate(base) // discard possibly added data + return nil // ignore files with I/O errors + } + + // parse the file and in the process add it to the file set + src := x.sources.Bytes()[base:] // no need to reread the file + file, err := parser.ParseFile(x.fset, filename, src, parser.ParseComments) + if err != nil { + // do not discard the added source code in this case + // because the file has been added to the file set and + // the source size must match the file set base + // TODO(gri): given a FileSet.RemoveFile() one might be + // able to discard the data here (worthwhile?) + return nil // ignore files with (parse) errors + } + + return file } -func (x *Indexer) VisitFile(path string, f *os.FileInfo) { +func (x *Indexer) visitFile(dirname string, f *os.FileInfo) { if !isGoFile(f) { return } + path := pathutil.Join(dirname, f.Name) if excludeTestFiles && (!isPkgFile(f) || strings.HasPrefix(path, "test/")) { return } @@ -596,15 +679,23 @@ func (x *Indexer) VisitFile(path string, f *os.FileInfo) { return } - file, err := parser.ParseFile(path, nil, nil, parser.ParseComments) - if err != nil { - return // ignore files with (parse) errors + file := x.addFile(path) + if file == nil { + return } + // we've got a file to index + x.current = x.fset.File(file.Pos()) // file.Pos is in the current file dir, _ := pathutil.Split(path) - pak := Pak{dir, file.Name.Name()} + pak := Pak{dir, file.Name.Name} x.file = &File{path, pak} ast.Walk(x, file) + + // update statistics + // (count real file size as opposed to using the padded x.sources.Len()) + x.stats.Bytes += x.current.Size() + x.stats.Files++ + x.stats.Lines += x.current.LineCount() } @@ -613,30 +704,54 @@ func (x *Indexer) VisitFile(path string, f *os.FileInfo) { type LookupResult struct { Decls HitList // package-level declarations (with snippets) - Others HitList // all other occurences + Others HitList // all other occurrences } type Index struct { + fset *token.FileSet // file set used during indexing; nil if no textindex + suffixes *suffixarray.Index // suffixes for concatenated sources; nil if no textindex words map[string]*LookupResult // maps words to hit lists alts map[string]*AltWords // maps canonical(words) to lists of alternative spellings snippets []*Snippet // all snippets, indexed by snippet index - nspots int // number of spots indexed (a measure of the index size) + stats Statistics } func canonical(w string) string { return strings.ToLower(w) } -// NewIndex creates a new index for the file tree rooted at root. -func NewIndex(root string) *Index { +// NewIndex creates a new index for the .go files +// in the directories given by dirnames. +// +func NewIndex(dirnames <-chan string, fulltextIndex bool) *Index { var x Indexer // initialize Indexer + x.fset = token.NewFileSet() x.words = make(map[string]*IndexResult) - // collect all Spots - pathutil.Walk(root, &x, nil) + // index all files in the directories given by dirnames + for dirname := range dirnames { + list, err := ioutil.ReadDir(dirname) + if err != nil { + continue // ignore this directory + } + for _, f := range list { + if !f.IsDirectory() { + x.visitFile(dirname, f) + } + } + } + + if !fulltextIndex { + // the file set, the current file, and the sources are + // not needed after indexing if no text index is built - + // help GC and clear them + x.fset = nil + x.sources.Reset() + x.current = nil // contains reference to fset! + } // for each word, reduce the RunLists into a LookupResult; // also collect the word with its canonical spelling in a @@ -652,6 +767,7 @@ func NewIndex(root string) *Index { } wlist.Push(&wordPair{canonical(w), w}) } + x.stats.Words = len(words) // reduce the word list {canonical(w), w} into // a list of AltWords runs {canonical(w), {w}} @@ -670,14 +786,19 @@ func NewIndex(root string) *Index { snippets[i] = x.snippets.At(i).(*Snippet) } - return &Index{words, alts, snippets, x.nspots} + // create text index + var suffixes *suffixarray.Index + if fulltextIndex { + suffixes = suffixarray.New(x.sources.Bytes()) + } + + return &Index{x.fset, suffixes, words, alts, snippets, x.stats} } -// Size returns the number of different words and -// spots indexed as a measure for the index size. -func (x *Index) Size() (nwords int, nspots int) { - return len(x.words), x.nspots +// Stats() returns index statistics. +func (x *Index) Stats() Statistics { + return x.stats } @@ -696,7 +817,7 @@ func (x *Index) LookupWord(w string) (match *LookupResult, alt *AltWords) { func isIdentifier(s string) bool { var S scanner.Scanner - S.Init("", []byte(s), nil, 0) + S.Init(token.NewFileSet(), "", []byte(s), nil, 0) if _, tok, _ := S.Scan(); tok == token.IDENT { _, tok, _ := S.Scan() return tok == token.EOF @@ -707,14 +828,14 @@ func isIdentifier(s string) bool { // For a given query, which is either a single identifier or a qualified // identifier, Lookup returns a LookupResult, and a list of alternative -// spellings, if any. If the query syntax is wrong, illegal is set. -func (x *Index) Lookup(query string) (match *LookupResult, alt *AltWords, illegal bool) { +// spellings, if any. If the query syntax is wrong, an error is reported. +func (x *Index) Lookup(query string) (match *LookupResult, alt *AltWords, err os.Error) { ss := strings.Split(query, ".", -1) // check query syntax for _, s := range ss { if !isIdentifier(s) { - illegal = true + err = os.NewError("all query parts must be identifiers") return } } @@ -734,7 +855,7 @@ func (x *Index) Lookup(query string) (match *LookupResult, alt *AltWords, illega } default: - illegal = true + err = os.NewError("query is not a (qualified) identifier") } return @@ -748,3 +869,103 @@ func (x *Index) Snippet(i int) *Snippet { } return nil } + + +type positionList []struct { + filename string + line int +} + +func (list positionList) Len() int { return len(list) } +func (list positionList) Less(i, j int) bool { return list[i].filename < list[j].filename } +func (list positionList) Swap(i, j int) { list[i], list[j] = list[j], list[i] } + + +// unique returns the list sorted and with duplicate entries removed +func unique(list []int) []int { + sort.SortInts(list) + var last int + i := 0 + for _, x := range list { + if i == 0 || x != last { + last = x + list[i] = x + i++ + } + } + return list[0:i] +} + + +// A FileLines value specifies a file and line numbers within that file. +type FileLines struct { + Filename string + Lines []int +} + + +// LookupRegexp returns the number of matches and the matches where a regular +// expression r is found in the full text index. At most n matches are +// returned (thus found <= n). +// +func (x *Index) LookupRegexp(r *regexp.Regexp, n int) (found int, result []FileLines) { + if x.suffixes == nil || n <= 0 { + return + } + // n > 0 + + var list positionList + // FindAllIndex may returns matches that span across file boundaries. + // Such matches are unlikely, buf after eliminating them we may end up + // with fewer than n matches. If we don't have enough at the end, redo + // the search with an increased value n1, but only if FindAllIndex + // returned all the requested matches in the first place (if it + // returned fewer than that there cannot be more). + for n1 := n; found < n; n1 += n - found { + found = 0 + matches := x.suffixes.FindAllIndex(r, n1) + // compute files, exclude matches that span file boundaries, + // and map offsets to file-local offsets + list = make(positionList, len(matches)) + for _, m := range matches { + // by construction, an offset corresponds to the Pos value + // for the file set - use it to get the file and line + p := token.Pos(m[0]) + if file := x.fset.File(p); file != nil { + if base := file.Base(); base <= m[1] && m[1] <= base+file.Size() { + // match [m[0], m[1]) is within the file boundaries + list[found].filename = file.Name() + list[found].line = file.Line(p) + found++ + } + } + } + if found == n || len(matches) < n1 { + // found all matches or there's no chance to find more + break + } + } + list = list[0:found] + sort.Sort(list) // sort by filename + + // collect matches belonging to the same file + var last string + var lines []int + addLines := func() { + if len(lines) > 0 { + // remove duplicate lines + result = append(result, FileLines{last, unique(lines)}) + lines = nil + } + } + for _, m := range list { + if m.filename != last { + addLines() + last = m.filename + } + lines = append(lines, m.line) + } + addLines() + + return +} diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go index 7a9279a2f..fe3d22fb9 100644 --- a/src/cmd/godoc/main.go +++ b/src/cmd/godoc/main.go @@ -49,7 +49,7 @@ var ( // periodic sync syncCmd = flag.String("sync", "", "sync command; disabled if empty") syncMin = flag.Int("sync_minutes", 0, "sync interval in minutes; disabled if <= 0") - syncDelay delayTime // actual sync delay in minutes; usually syncDelay == syncMin, but delay may back off exponentially + syncDelay delayTime // actual sync interval in minutes; usually syncDelay == syncMin, but syncDelay may back off exponentially // network httpAddr = flag.String("http", "", "HTTP service address (e.g., '"+defaultAddr+"')") @@ -64,29 +64,30 @@ var ( ) -func serveError(c *http.Conn, r *http.Request, relpath string, err os.Error) { +func serveError(w http.ResponseWriter, r *http.Request, relpath string, err os.Error) { contents := applyTemplate(errorHTML, "errorHTML", err) // err may contain an absolute path! - servePage(c, "File "+relpath, "", "", contents) + w.WriteHeader(http.StatusNotFound) + servePage(w, "File "+relpath, "", "", contents) } -func exec(c *http.Conn, args []string) (status int) { +func exec(rw http.ResponseWriter, args []string) (status int) { r, w, err := os.Pipe() if err != nil { - log.Stderrf("os.Pipe(): %v\n", err) + log.Printf("os.Pipe(): %v", err) return 2 } bin := args[0] fds := []*os.File{nil, w, w} if *verbose { - log.Stderrf("executing %v", args) + log.Printf("executing %v", args) } pid, err := os.ForkExec(bin, args, os.Environ(), *goroot, fds) defer r.Close() w.Close() if err != nil { - log.Stderrf("os.ForkExec(%q): %v\n", bin, err) + log.Printf("os.ForkExec(%q): %v", bin, err) return 2 } @@ -95,41 +96,38 @@ func exec(c *http.Conn, args []string) (status int) { wait, err := os.Wait(pid, 0) if err != nil { os.Stderr.Write(buf.Bytes()) - log.Stderrf("os.Wait(%d, 0): %v\n", pid, err) + log.Printf("os.Wait(%d, 0): %v", pid, err) return 2 } status = wait.ExitStatus() if !wait.Exited() || status > 1 { os.Stderr.Write(buf.Bytes()) - log.Stderrf("executing %v failed (exit status = %d)", args, status) + log.Printf("executing %v failed (exit status = %d)", args, status) return } if *verbose { os.Stderr.Write(buf.Bytes()) } - if c != nil { - c.SetHeader("content-type", "text/plain; charset=utf-8") - c.Write(buf.Bytes()) + if rw != nil { + rw.SetHeader("content-type", "text/plain; charset=utf-8") + rw.Write(buf.Bytes()) } return } -// Maximum directory depth, adjust as needed. -const maxDirDepth = 24 - -func dosync(c *http.Conn, r *http.Request) { +func dosync(w http.ResponseWriter, r *http.Request) { args := []string{"/bin/sh", "-c", *syncCmd} - switch exec(c, args) { + switch exec(w, args) { case 0: // sync succeeded and some files have changed; // update package tree. // TODO(gri): The directory tree may be temporarily out-of-sync. // Consider keeping separate time stamps so the web- // page can indicate this discrepancy. - fsTree.set(newDirectory(*goroot, maxDirDepth)) + initFSTree() fallthrough case 1: // sync failed because no files changed; @@ -152,9 +150,9 @@ func usage() { func loggingHandler(h http.Handler) http.Handler { - return http.HandlerFunc(func(c *http.Conn, req *http.Request) { - log.Stderrf("%s\t%s", c.RemoteAddr, req.URL) - h.ServeHTTP(c, req) + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + log.Printf("%s\t%s", w.RemoteAddr(), req.URL) + h.ServeHTTP(w, req) }) } @@ -239,13 +237,16 @@ func main() { // HTTP server mode. var handler http.Handler = http.DefaultServeMux if *verbose { - log.Stderrf("Go Documentation Server\n") - log.Stderrf("version = %s\n", runtime.Version()) - log.Stderrf("address = %s\n", *httpAddr) - log.Stderrf("goroot = %s\n", *goroot) - log.Stderrf("tabwidth = %d\n", *tabwidth) + log.Printf("Go Documentation Server") + log.Printf("version = %s", runtime.Version()) + log.Printf("address = %s", *httpAddr) + log.Printf("goroot = %s", *goroot) + log.Printf("tabwidth = %d", *tabwidth) + if *fulltextIndex { + log.Print("full text index enabled") + } if !fsMap.IsEmpty() { - log.Stderr("user-defined mapping:") + log.Print("user-defined mapping:") fsMap.Fprint(os.Stderr) } handler = loggingHandler(handler) @@ -256,12 +257,12 @@ func main() { http.Handle("/debug/sync", http.HandlerFunc(dosync)) } - // Initialize directory tree with corresponding timestamp. - // Do it in two steps: - // 1) set timestamp right away so that the indexer is kicked on - fsTree.set(nil) - // 2) compute initial directory tree in a goroutine so that launch is quick - go func() { fsTree.set(newDirectory(*goroot, maxDirDepth)) }() + // Initialize default directory tree with corresponding timestamp. + // (Do it in a goroutine so that launch is quick.) + go initFSTree() + + // Initialize directory trees for user-defined file systems (-path flag). + initDirTrees() // Start sync goroutine, if enabled. if *syncCmd != "" && *syncMin > 0 { @@ -271,7 +272,7 @@ func main() { dosync(nil, nil) delay, _ := syncDelay.get() if *verbose { - log.Stderrf("next sync in %dmin", delay.(int)) + log.Printf("next sync in %dmin", delay.(int)) } time.Sleep(int64(delay.(int)) * 60e9) } @@ -333,15 +334,18 @@ func main() { } // TODO(gri): Provide a mechanism (flag?) to select a package // if there are multiple packages in a directory. - info := pkgHandler.getPageInfo(abspath, relpath, "", mode|tryMode) + info := pkgHandler.getPageInfo(abspath, relpath, "", mode) - if info.PAst == nil && info.PDoc == nil && info.Dirs == nil { + if info.Err != nil || info.PAst == nil && info.PDoc == nil && info.Dirs == nil { // try again, this time assume it's a command if len(path) > 0 && path[0] != '/' { abspath = absolutePath(path, cmdHandler.fsRoot) } info = cmdHandler.getPageInfo(abspath, relpath, "", mode) } + if info.Err != nil { + log.Exitf("%v", info.Err) + } // If we have more than one argument, use the remaining arguments for filtering if flag.NArg() > 1 { @@ -362,7 +366,7 @@ func main() { if i > 0 { fmt.Println() } - writeAny(os.Stdout, d, *html) + writeAny(os.Stdout, info.FSet, *html, d) fmt.Println() } return @@ -373,6 +377,6 @@ func main() { } if err := packageText.Execute(info, os.Stdout); err != nil { - log.Stderrf("packageText.Execute: %s", err) + log.Printf("packageText.Execute: %s", err) } } diff --git a/src/cmd/godoc/mapping.go b/src/cmd/godoc/mapping.go index 400f97e1f..1d87bbc76 100644 --- a/src/cmd/godoc/mapping.go +++ b/src/cmd/godoc/mapping.go @@ -11,6 +11,7 @@ import ( "io" "os" pathutil "path" + "sort" "strings" ) @@ -42,14 +43,19 @@ import ( // // (assuming that file exists). // +// Each individual mapping also has a RWValue associated with it that +// may be used to store mapping-specific information. See the Iterate +// method. +// type Mapping struct { list []mapping - prefixes []string + prefixes []string // lazily computed from list } type mapping struct { prefix, path string + value *RWValue } @@ -75,43 +81,16 @@ type mapping struct { // public -> /home/build/public // func (m *Mapping) Init(paths string) { - cwd, _ := os.Getwd() // ignore errors - - pathlist := strings.Split(paths, ":", -1) - + pathlist := canonicalizePaths(strings.Split(paths, ":", -1), nil) list := make([]mapping, len(pathlist)) - n := 0 // number of mappings - for _, path := range pathlist { - if len(path) == 0 { - // ignore empty paths (don't assume ".") - continue - } - - // len(path) > 0: normalize path - if path[0] != '/' { - path = pathutil.Join(cwd, path) - } else { - path = pathutil.Clean(path) - } - - // check if mapping exists already - var i int - for i = 0; i < n; i++ { - if path == list[i].path { - break - } - } - - // add mapping if it is new - if i >= n { - _, prefix := pathutil.Split(path) - list[n] = mapping{prefix, path} - n++ - } + // create mapping list + for i, path := range pathlist { + _, prefix := pathutil.Split(path) + list[i] = mapping{prefix, path, new(RWValue)} } - m.list = list[0:n] + m.list = list } @@ -134,24 +113,25 @@ func (m *Mapping) PrefixList() []string { // compute the list lazily if m.prefixes == nil { list := make([]string, len(m.list)) - n := 0 // nuber of prefixes - - for _, e := range m.list { - // check if prefix exists already - var i int - for i = 0; i < n; i++ { - if e.prefix == list[i] { - break - } - } - // add prefix if it is new - if i >= n { - list[n] = e.prefix - n++ + // populate list + for i, e := range m.list { + list[i] = e.prefix + } + + // sort the list and remove duplicate entries + sort.SortStrings(list) + i := 0 + prev := "" + for _, path := range list { + if path != prev { + list[i] = path + i++ + prev = path } } - m.prefixes = list[0:n] + + m.prefixes = list[0:i] } return m.prefixes @@ -166,7 +146,7 @@ func (m *Mapping) Fprint(w io.Writer) { } -func split(path string) (head, tail string) { +func splitFirst(path string) (head, tail string) { i := strings.Index(path, "/") if i > 0 { // 0 < i < len(path) @@ -181,7 +161,7 @@ func split(path string) (head, tail string) { // string is returned. // func (m *Mapping) ToAbsolute(path string) string { - prefix, tail := split(path) + prefix, tail := splitFirst(path) for _, e := range m.list { switch { case e.prefix == prefix: @@ -214,3 +194,15 @@ func (m *Mapping) ToRelative(path string) string { } return "" // no match } + + +// Iterate calls f for each path and RWValue in the mapping (in uspecified order) +// until f returns false. +// +func (m *Mapping) Iterate(f func(path string, value *RWValue) bool) { + for _, e := range m.list { + if !f(e.path, e.value) { + return + } + } +} diff --git a/src/cmd/godoc/snippet.go b/src/cmd/godoc/snippet.go index d8fb19533..6a12febe1 100755 --- a/src/cmd/godoc/snippet.go +++ b/src/cmd/godoc/snippet.go @@ -12,41 +12,22 @@ package main import ( "bytes" "go/ast" - "go/printer" + "go/token" "fmt" ) type Snippet struct { Line int - Text string + Text []byte } -type snippetStyler struct { - Styler // defined in godoc.go - highlight *ast.Ident // identifier to highlight -} - - -func (s *snippetStyler) LineTag(line int) (text []uint8, tag printer.HTMLTag) { - return // no LineTag for snippets -} - - -func (s *snippetStyler) Ident(id *ast.Ident) (text []byte, tag printer.HTMLTag) { - text = []byte(id.Name()) - if s.highlight == id { - tag = printer.HTMLTag{"<span class=highlight>", "</span>"} - } - return -} - - -func newSnippet(decl ast.Decl, id *ast.Ident) *Snippet { +func newSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet { + // TODO instead of pretty-printing the node, should use the original source instead var buf bytes.Buffer - writeNode(&buf, decl, true, &snippetStyler{highlight: id}) - return &Snippet{id.Pos().Line, buf.String()} + writeNode(&buf, fset, decl, true) + return &Snippet{fset.Position(id.Pos()).Line, FormatText(buf.Bytes(), -1, true, id.Name, nil)} } @@ -73,20 +54,20 @@ func findSpec(list []ast.Spec, id *ast.Ident) ast.Spec { } -func genSnippet(d *ast.GenDecl, id *ast.Ident) *Snippet { +func genSnippet(fset *token.FileSet, d *ast.GenDecl, id *ast.Ident) *Snippet { s := findSpec(d.Specs, id) if s == nil { return nil // declaration doesn't contain id - exit gracefully } // only use the spec containing the id for the snippet - dd := &ast.GenDecl{d.Doc, d.Position, d.Tok, d.Lparen, []ast.Spec{s}, d.Rparen} + dd := &ast.GenDecl{d.Doc, d.Pos(), d.Tok, d.Lparen, []ast.Spec{s}, d.Rparen} - return newSnippet(dd, id) + return newSnippet(fset, dd, id) } -func funcSnippet(d *ast.FuncDecl, id *ast.Ident) *Snippet { +func funcSnippet(fset *token.FileSet, d *ast.FuncDecl, id *ast.Ident) *Snippet { if d.Name != id { return nil // declaration doesn't contain id - exit gracefully } @@ -94,7 +75,7 @@ func funcSnippet(d *ast.FuncDecl, id *ast.Ident) *Snippet { // only use the function signature for the snippet dd := &ast.FuncDecl{d.Doc, d.Recv, d.Name, d.Type, nil} - return newSnippet(dd, id) + return newSnippet(fset, dd, id) } @@ -102,19 +83,21 @@ func funcSnippet(d *ast.FuncDecl, id *ast.Ident) *Snippet { // identifier id. Parts of the declaration not containing the identifier // may be removed for a more compact snippet. // -func NewSnippet(decl ast.Decl, id *ast.Ident) (s *Snippet) { +func NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) (s *Snippet) { switch d := decl.(type) { case *ast.GenDecl: - s = genSnippet(d, id) + s = genSnippet(fset, d, id) case *ast.FuncDecl: - s = funcSnippet(d, id) + s = funcSnippet(fset, d, id) } // handle failure gracefully if s == nil { + var buf bytes.Buffer + fmt.Fprintf(&buf, `<span class="alert">could not generate a snippet for <span class="highlight">%s</span></span>`, id.Name) s = &Snippet{ - id.Pos().Line, - fmt.Sprintf(`could not generate a snippet for <span class="highlight">%s</span>`, id.Name()), + fset.Position(id.Pos()).Line, + buf.Bytes(), } } return diff --git a/src/cmd/godoc/spec.go b/src/cmd/godoc/spec.go index 2298fae2c..b1c1a883f 100644 --- a/src/cmd/godoc/spec.go +++ b/src/cmd/godoc/spec.go @@ -20,24 +20,28 @@ import ( type ebnfParser struct { - out io.Writer // parser output - src []byte // parser source + out io.Writer // parser output + src []byte // parser source + file *token.File // for position information scanner scanner.Scanner - prev int // offset of previous token - pos token.Position // token position - tok token.Token // one token look-ahead - lit []byte // token literal + prev int // offset of previous token + pos token.Pos // token position + tok token.Token // one token look-ahead + lit []byte // token literal } func (p *ebnfParser) flush() { - p.out.Write(p.src[p.prev:p.pos.Offset]) - p.prev = p.pos.Offset + offs := p.file.Offset(p.pos) + p.out.Write(p.src[p.prev:offs]) + p.prev = offs } func (p *ebnfParser) next() { - p.flush() + if p.pos.IsValid() { + p.flush() + } p.pos, p.tok, p.lit = p.scanner.Scan() if p.tok.IsKeyword() { // TODO Should keyword mapping always happen outside scanner? @@ -52,9 +56,9 @@ func (p *ebnfParser) Error(pos token.Position, msg string) { } -func (p *ebnfParser) errorExpected(pos token.Position, msg string) { +func (p *ebnfParser) errorExpected(pos token.Pos, msg string) { msg = "expected " + msg - if pos.Offset == p.pos.Offset { + if pos == p.pos { // the error happened at the current position; // make the error message more specific msg += ", found '" + p.tok.String() + "'" @@ -62,11 +66,11 @@ func (p *ebnfParser) errorExpected(pos token.Position, msg string) { msg += " " + string(p.lit) } } - p.Error(pos, msg) + p.Error(p.file.Position(pos), msg) } -func (p *ebnfParser) expect(tok token.Token) token.Position { +func (p *ebnfParser) expect(tok token.Token) token.Pos { pos := p.pos if p.tok != tok { p.errorExpected(pos, "'"+tok.String()+"'") @@ -148,11 +152,11 @@ func (p *ebnfParser) parseProduction() { } -func (p *ebnfParser) parse(out io.Writer, src []byte) { +func (p *ebnfParser) parse(fset *token.FileSet, out io.Writer, src []byte) { // initialize ebnfParser p.out = out p.src = src - p.scanner.Init("", src, p, 0) + p.file = p.scanner.Init(fset, "", src, p, 0) p.next() // initializes pos, tok, lit // process source @@ -171,6 +175,7 @@ var ( func linkify(out io.Writer, src []byte) { + fset := token.NewFileSet() for len(src) > 0 { n := len(src) @@ -192,7 +197,7 @@ func linkify(out io.Writer, src []byte) { out.Write(src[0:i]) // parse and write EBNF var p ebnfParser - p.parse(out, src[i:j]) + p.parse(fset, out, src[i:j]) // advance src = src[j:n] diff --git a/src/cmd/godoc/utils.go b/src/cmd/godoc/utils.go new file mode 100644 index 000000000..55cf87841 --- /dev/null +++ b/src/cmd/godoc/utils.go @@ -0,0 +1,109 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains support functionality for godoc. + +package main + +import ( + "io" + "io/ioutil" + "os" + pathutil "path" + "sort" + "strings" + "sync" + "time" +) + + +// An RWValue wraps a value and permits mutually exclusive +// access to it and records the time the value was last set. +type RWValue struct { + mutex sync.RWMutex + value interface{} + timestamp int64 // time of last set(), in seconds since epoch +} + + +func (v *RWValue) set(value interface{}) { + v.mutex.Lock() + v.value = value + v.timestamp = time.Seconds() + v.mutex.Unlock() +} + + +func (v *RWValue) get() (interface{}, int64) { + v.mutex.RLock() + defer v.mutex.RUnlock() + return v.value, v.timestamp +} + + +var cwd, _ = os.Getwd() // ignore errors + +// canonicalizePaths takes a list of (directory/file) paths and returns +// the list of corresponding absolute paths in sorted (increasing) order. +// Relative paths are assumed to be relative to the current directory, +// empty and duplicate paths as well as paths for which filter(path) is +// false are discarded. filter may be nil in which case it is not used. +// +func canonicalizePaths(list []string, filter func(path string) bool) []string { + i := 0 + for _, path := range list { + path = strings.TrimSpace(path) + if len(path) == 0 { + continue // ignore empty paths (don't assume ".") + } + // len(path) > 0: normalize path + if path[0] != '/' { + path = pathutil.Join(cwd, path) + } else { + path = pathutil.Clean(path) + } + // we have a non-empty absolute path + if filter != nil && !filter(path) { + continue + } + // keep the path + list[i] = path + i++ + } + list = list[0:i] + + // sort the list and remove duplicate entries + sort.SortStrings(list) + i = 0 + prev := "" + for _, path := range list { + if path != prev { + list[i] = path + i++ + prev = path + } + } + + return list[0:i] +} + + +// writeFileAtomically writes data to a temporary file and then +// atomically renames that file to the file named by filename. +// +func writeFileAtomically(filename string, data []byte) os.Error { + f, err := ioutil.TempFile(cwd, filename) + if err != nil { + return err + } + n, err := f.Write(data) + f.Close() + if err != nil { + return err + } + if n < len(data) { + return io.ErrShortWrite + } + return os.Rename(f.Name(), filename) +} diff --git a/src/cmd/gofmt/Makefile b/src/cmd/gofmt/Makefile index 2294fd1db..5f2f454e8 100644 --- a/src/cmd/gofmt/Makefile +++ b/src/cmd/gofmt/Makefile @@ -2,12 +2,13 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=gofmt GOFILES=\ gofmt.go\ rewrite.go\ + simplify.go\ include ../../Make.cmd @@ -15,5 +16,4 @@ test: $(TARG) ./test.sh smoketest: $(TARG) - ./test.sh "$(GOROOT)"/src/pkg/go/parser/parser.go - + (cd testdata; ./test.sh) diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go index 2e4c40c21..2d2c9ae61 100644 --- a/src/cmd/gofmt/doc.go +++ b/src/cmd/gofmt/doc.go @@ -20,6 +20,8 @@ The flags are: unless -w is also set. -r rule apply the rewrite rule to the source before reformatting. + -s + try to simplify code (after applying the rewrite rule, if any). -w if set, overwrite each input file with its output. -spaces @@ -33,6 +35,8 @@ Debugging flags: -trace print parse trace. + -ast + print AST (before rewrites). -comments=true print comments; if false, all comments are elided from the output. diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index ffec0325f..d7b70c461 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -12,6 +12,7 @@ import ( "go/parser" "go/printer" "go/scanner" + "go/token" "io/ioutil" "os" pathutil "path" @@ -24,11 +25,12 @@ var ( list = flag.Bool("l", false, "list files whose formatting differs from gofmt's") write = flag.Bool("w", false, "write result to (source) file instead of stdout") rewriteRule = flag.String("r", "", "rewrite rule (e.g., 'α[β:len(α)] -> α[β:]')") + simplifyAST = flag.Bool("s", false, "simplify code") // debugging support comments = flag.Bool("comments", true, "print comments") - debug = flag.Bool("debug", false, "print debugging information") trace = flag.Bool("trace", false, "print parse trace") + printAST = flag.Bool("ast", false, "print AST (before rewrites)") // layout control tabWidth = flag.Int("tabwidth", 8, "tab width") @@ -38,6 +40,7 @@ var ( var ( + fset = token.NewFileSet() exitCode = 0 rewrite func(*ast.File) *ast.File parserMode uint @@ -92,22 +95,26 @@ func processFile(f *os.File) os.Error { return err } - var scope *ast.Scope - if *debug { - scope = ast.NewScope(nil) - } - file, err := parser.ParseFile(f.Name(), src, scope, parserMode) + file, err := parser.ParseFile(fset, f.Name(), src, parserMode) if err != nil { return err } + if *printAST { + ast.Print(file) + } + if rewrite != nil { file = rewrite(file) } + if *simplifyAST { + simplify(file) + } + var res bytes.Buffer - _, err = (&printer.Config{printerMode, *tabWidth, nil}).Fprint(&res, file) + _, err = (&printer.Config{printerMode, *tabWidth, nil}).Fprint(&res, fset, file) if err != nil { return err } @@ -133,10 +140,10 @@ func processFile(f *os.File) os.Error { } -func processFileByName(filename string) (err os.Error) { +func processFileByName(filename string) os.Error { file, err := os.Open(filename, os.O_RDONLY, 0) if err != nil { - return + return err } defer file.Close() return processFile(file) diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go index a89146ca0..8ea5334e9 100644 --- a/src/cmd/gofmt/rewrite.go +++ b/src/cmd/gofmt/rewrite.go @@ -37,7 +37,7 @@ func initRewrite() { // but there are problems with preserving formatting and also // with what a wildcard for a statement looks like. func parseExpr(s string, what string) ast.Expr { - x, err := parser.ParseExpr("input", s, nil) + x, err := parser.ParseExpr(fset, "input", s) if err != nil { fmt.Fprintf(os.Stderr, "parsing %s %s: %s\n", what, s, err) os.Exit(2) @@ -66,13 +66,19 @@ func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File { } -var positionType = reflect.Typeof(token.Position{}) -var identType = reflect.Typeof((*ast.Ident)(nil)) - - -func isWildcard(s string) bool { - rune, size := utf8.DecodeRuneInString(s) - return size == len(s) && unicode.IsLower(rune) +// setValue is a wrapper for x.SetValue(y); it protects +// the caller from panics if x cannot be changed to y. +func setValue(x, y reflect.Value) { + defer func() { + if x := recover(); x != nil { + if s, ok := x.(string); ok && strings.HasPrefix(s, "type mismatch") { + // x cannot be set to y - ignore this rewrite + return + } + panic(x) + } + }() + x.SetValue(y) } @@ -86,21 +92,31 @@ func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value case *reflect.SliceValue: for i := 0; i < v.Len(); i++ { e := v.Elem(i) - e.SetValue(f(e)) + setValue(e, f(e)) } case *reflect.StructValue: for i := 0; i < v.NumField(); i++ { e := v.Field(i) - e.SetValue(f(e)) + setValue(e, f(e)) } case *reflect.InterfaceValue: e := v.Elem() - v.SetValue(f(e)) + setValue(v, f(e)) } return val } +var positionType = reflect.Typeof(token.NoPos) +var identType = reflect.Typeof((*ast.Ident)(nil)) + + +func isWildcard(s string) bool { + rune, size := utf8.DecodeRuneInString(s) + return size == len(s) && unicode.IsLower(rune) +} + + // match returns true if pattern matches val, // recording wildcard submatches in m. // If m == nil, match checks whether pattern == val. @@ -109,17 +125,20 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool { // times in the pattern, it must match the same expression // each time. if m != nil && pattern.Type() == identType { - name := pattern.Interface().(*ast.Ident).Name() + name := pattern.Interface().(*ast.Ident).Name if isWildcard(name) { - if old, ok := m[name]; ok { - return match(nil, old, val) + // wildcards only match expressions + if _, ok := val.Interface().(ast.Expr); ok { + if old, ok := m[name]; ok { + return match(nil, old, val) + } + m[name] = val + return true } - m[name] = val - return true } } - // Otherwise, the expressions must match recursively. + // Otherwise, pattern and val must match recursively. if pattern == nil || val == nil { return pattern == nil && val == nil } @@ -139,7 +158,7 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool { // of recursing down any further via reflection. p := pattern.Interface().(*ast.Ident) v := val.Interface().(*ast.Ident) - return p == nil && v == nil || p != nil && v != nil && p.Name() == v.Name() + return p == nil && v == nil || p != nil && v != nil && p.Name == v.Name } p := reflect.Indirect(pattern) @@ -194,7 +213,7 @@ func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) // Wildcard gets replaced with map value. if m != nil && pattern.Type() == identType { - name := pattern.Interface().(*ast.Ident).Name() + name := pattern.Interface().(*ast.Ident).Name if isWildcard(name) { if old, ok := m[name]; ok { return subst(nil, old, nil) @@ -203,6 +222,10 @@ func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) } if pos != nil && pattern.Type() == positionType { + // use new position only if old position was valid in the first place + if old := pattern.Interface().(token.Pos); !old.IsValid() { + return pattern + } return pos } diff --git a/src/cmd/gofmt/simplify.go b/src/cmd/gofmt/simplify.go new file mode 100644 index 000000000..bcc67c4a6 --- /dev/null +++ b/src/cmd/gofmt/simplify.go @@ -0,0 +1,67 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" + "reflect" +) + + +type simplifier struct{} + +func (s *simplifier) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.CompositeLit: + // array, slice, and map composite literals may be simplified + outer := n + var eltType ast.Expr + switch typ := outer.Type.(type) { + case *ast.ArrayType: + eltType = typ.Elt + case *ast.MapType: + eltType = typ.Value + } + + if eltType != nil { + typ := reflect.NewValue(eltType) + for _, x := range outer.Elts { + // look at value of indexed/named elements + if t, ok := x.(*ast.KeyValueExpr); ok { + x = t.Value + } + simplify(x) + // if the element is a composite literal and its literal type + // matches the outer literal's element type exactly, the inner + // literal type may be omitted + if inner, ok := x.(*ast.CompositeLit); ok { + if match(nil, typ, reflect.NewValue(inner.Type)) { + inner.Type = nil + } + } + } + + // node was simplified - stop walk (there are no subnodes to simplify) + return nil + } + + case *ast.RangeStmt: + // range of the form: for x, _ = range v {...} + // can be simplified to: for x = range v {...} + if n.Value != nil { + if ident, ok := n.Value.(*ast.Ident); ok && ident.Name == "_" { + n.Value = nil + } + } + } + + return s +} + + +func simplify(node ast.Node) { + var s simplifier + ast.Walk(&s, node) +} diff --git a/src/cmd/gofmt/test.sh b/src/cmd/gofmt/test.sh index bed46532b..b5f4de1e2 100755 --- a/src/cmd/gofmt/test.sh +++ b/src/cmd/gofmt/test.sh @@ -3,7 +3,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -. "$GOROOT"/src/Make.$GOARCH +eval $(gomake --no-print-directory -f ../../Make.inc go-env) if [ -z "$O" ]; then echo 'missing $O - maybe no Make.$GOARCH?' 1>&2 exit 1 @@ -41,7 +41,8 @@ apply1() { bug106.go | bug121.go | bug125.go | bug133.go | bug160.go | \ bug163.go | bug166.go | bug169.go | bug217.go | bug222.go | \ bug226.go | bug228.go | bug248.go | bug274.go | bug280.go | \ - bug282.go ) return ;; + bug282.go | bug287.go | bug298.go | bug299.go | bug300.go | \ + bug302.go | bug306.go ) return ;; esac # the following directories are skipped because they contain test # cases for syntax errors and thus won't parse in the first place: diff --git a/src/cmd/gofmt/testdata/composites.golden b/src/cmd/gofmt/testdata/composites.golden new file mode 100644 index 000000000..1fd5847c1 --- /dev/null +++ b/src/cmd/gofmt/testdata/composites.golden @@ -0,0 +1,104 @@ +package P + +type T struct { + x, y int +} + +var _ = [42]T{ + {}, + {1, 2}, + {3, 4}, +} + +var _ = [...]T{ + {}, + {1, 2}, + {3, 4}, +} + +var _ = []T{ + {}, + {1, 2}, + {3, 4}, +} + +var _ = []T{ + {}, + 10: {1, 2}, + 20: {3, 4}, +} + +var _ = []struct { + x, y int +}{ + {}, + 10: {1, 2}, + 20: {3, 4}, +} + +var _ = []interface{}{ + T{}, + 10: T{1, 2}, + 20: T{3, 4}, +} + +var _ = [][]int{ + {}, + {1, 2}, + {3, 4}, +} + +var _ = [][]int{ + ([]int{}), + ([]int{1, 2}), + {3, 4}, +} + +var _ = [][][]int{ + {}, + { + {}, + {0, 1, 2, 3}, + {4, 5}, + }, +} + +var _ = map[string]T{ + "foo": {}, + "bar": {1, 2}, + "bal": {3, 4}, +} + +var _ = map[string]struct { + x, y int +}{ + "foo": {}, + "bar": {1, 2}, + "bal": {3, 4}, +} + +var _ = map[string]interface{}{ + "foo": T{}, + "bar": T{1, 2}, + "bal": T{3, 4}, +} + +var _ = map[string][]int{ + "foo": {}, + "bar": {1, 2}, + "bal": {3, 4}, +} + +var _ = map[string][]int{ + "foo": ([]int{}), + "bar": ([]int{1, 2}), + "bal": {3, 4}, +} + +// from exp/4s/data.go +var pieces4 = []Piece{ + {0, 0, Point{4, 1}, []Point{{0, 0}, {1, 0}, {1, 0}, {1, 0}}, nil, nil}, + {1, 0, Point{1, 4}, []Point{{0, 0}, {0, 1}, {0, 1}, {0, 1}}, nil, nil}, + {2, 0, Point{4, 1}, []Point{{0, 0}, {1, 0}, {1, 0}, {1, 0}}, nil, nil}, + {3, 0, Point{1, 4}, []Point{{0, 0}, {0, 1}, {0, 1}, {0, 1}}, nil, nil}, +} diff --git a/src/cmd/gofmt/testdata/composites.input b/src/cmd/gofmt/testdata/composites.input new file mode 100644 index 000000000..15afd9e5c --- /dev/null +++ b/src/cmd/gofmt/testdata/composites.input @@ -0,0 +1,104 @@ +package P + +type T struct { + x, y int +} + +var _ = [42]T{ + T{}, + T{1, 2}, + T{3, 4}, +} + +var _ = [...]T{ + T{}, + T{1, 2}, + T{3, 4}, +} + +var _ = []T{ + T{}, + T{1, 2}, + T{3, 4}, +} + +var _ = []T{ + T{}, + 10: T{1, 2}, + 20: T{3, 4}, +} + +var _ = []struct { + x, y int +}{ + struct{ x, y int }{}, + 10: struct{ x, y int }{1, 2}, + 20: struct{ x, y int }{3, 4}, +} + +var _ = []interface{}{ + T{}, + 10: T{1, 2}, + 20: T{3, 4}, +} + +var _ = [][]int{ + []int{}, + []int{1, 2}, + []int{3, 4}, +} + +var _ = [][]int{ + ([]int{}), + ([]int{1, 2}), + []int{3, 4}, +} + +var _ = [][][]int{ + [][]int{}, + [][]int{ + []int{}, + []int{0, 1, 2, 3}, + []int{4, 5}, + }, +} + +var _ = map[string]T{ + "foo": T{}, + "bar": T{1, 2}, + "bal": T{3, 4}, +} + +var _ = map[string]struct { + x, y int +}{ + "foo": struct{ x, y int }{}, + "bar": struct{ x, y int }{1, 2}, + "bal": struct{ x, y int }{3, 4}, +} + +var _ = map[string]interface{}{ + "foo": T{}, + "bar": T{1, 2}, + "bal": T{3, 4}, +} + +var _ = map[string][]int{ + "foo": []int{}, + "bar": []int{1, 2}, + "bal": []int{3, 4}, +} + +var _ = map[string][]int{ + "foo": ([]int{}), + "bar": ([]int{1, 2}), + "bal": []int{3, 4}, +} + +// from exp/4s/data.go +var pieces4 = []Piece{ + Piece{0, 0, Point{4, 1}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{1, 0}}, nil, nil}, + Piece{1, 0, Point{1, 4}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{0, 1}}, nil, nil}, + Piece{2, 0, Point{4, 1}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{1, 0}}, nil, nil}, + Piece{3, 0, Point{1, 4}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{0, 1}}, nil, nil}, +} diff --git a/src/cmd/gofmt/testdata/test.sh b/src/cmd/gofmt/testdata/test.sh new file mode 100755 index 000000000..a1d5d823e --- /dev/null +++ b/src/cmd/gofmt/testdata/test.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +CMD="../gofmt" +TMP=test_tmp.go +COUNT=0 + + +cleanup() { + rm -f $TMP +} + + +error() { + echo $1 + exit 1 +} + + +count() { + #echo $1 + let COUNT=$COUNT+1 + let M=$COUNT%10 + if [ $M == 0 ]; then + echo -n "." + fi +} + + +test() { + count $1 + + # compare against .golden file + cleanup + $CMD -s $1 > $TMP + cmp -s $TMP $2 + if [ $? != 0 ]; then + diff $TMP $2 + error "Error: simplified $1 does not match $2" + fi + + # make sure .golden is idempotent + cleanup + $CMD -s $2 > $TMP + cmp -s $TMP $2 + if [ $? != 0 ]; then + diff $TMP $2 + error "Error: $2 is not idempotent" + fi +} + + +runtests() { + smoketest=../../../pkg/go/parser/parser.go + test $smoketest $smoketest + test composites.input composites.golden + # add more test cases here +} + + +runtests +cleanup +echo "PASSED ($COUNT tests)" diff --git a/src/cmd/goinstall/Makefile b/src/cmd/goinstall/Makefile index cf4728401..6ddb32be7 100644 --- a/src/cmd/goinstall/Makefile +++ b/src/cmd/goinstall/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=goinstall GOFILES=\ diff --git a/src/cmd/goinstall/doc.go b/src/cmd/goinstall/doc.go index 80b30d5ac..17cc06969 100644 --- a/src/cmd/goinstall/doc.go +++ b/src/cmd/goinstall/doc.go @@ -10,14 +10,33 @@ It maintains a list of public Go packages at http://godashboard.appspot.com/pack Usage: goinstall [flags] importpath... + goinstall [flags] -a Flags and default settings: + -a=false install all previously installed packages -dashboard=true tally public packages on godashboard.appspot.com - -update=false update already-downloaded packages + -log=true log installed packages to $GOROOT/goinstall.log for use by -a + -u=false update already-downloaded packages -v=false verbose operation -Goinstall installs each of the packages identified on the command line. -It installs a package's prerequisites before trying to install the package itself. +Goinstall installs each of the packages identified on the command line. It +installs a package's prerequisites before trying to install the package +itself. Unless -log=false is specified, goinstall logs the import path of each +installed package to $GOROOT/goinstall.log for use by goinstall -a. + +If the -a flag is given, goinstall reinstalls all previously installed +packages, reading the list from $GOROOT/goinstall.log. After updating to a +new Go release, which deletes all package binaries, running + + goinstall -a + +will recompile and reinstall goinstalled packages. + +Another common idiom is to use + + goinstall -a -u + +to update, recompile, and reinstall all goinstalled packages. The source code for a package with import path foo/bar is expected to be in the directory $GOROOT/src/pkg/foo/bar/. If the import @@ -31,8 +50,8 @@ if necessary. The recognized code hosting sites are: GitHub (Git) - import "github.com/user/project.git" - import "github.com/user/project.git/sub/directory" + import "github.com/user/project" + import "github.com/user/project/sub/directory" Google Code Project Hosting (Mercurial, Subversion) @@ -44,17 +63,17 @@ if necessary. The recognized code hosting sites are: Launchpad - import "launchpad.net/project - import "launchpad.net/project/series - import "launchpad.net/project/series/sub/directory + import "launchpad.net/project" + import "launchpad.net/project/series" + import "launchpad.net/project/series/sub/directory" - import "launchpad.net/~user/project/branch - import "launchpad.net/~user/project/branch/sub/directory + import "launchpad.net/~user/project/branch" + import "launchpad.net/~user/project/branch/sub/directory" If the destination directory (e.g., $GOROOT/src/pkg/bitbucket.org/user/project) already exists and contains an appropriate checkout, goinstall will not -attempt to fetch updates. The -update flag changes this behavior, +attempt to fetch updates. The -u flag changes this behavior, causing goinstall to update all remote packages encountered during the installation. diff --git a/src/cmd/goinstall/download.go b/src/cmd/goinstall/download.go index 3422e8186..889f9d857 100644 --- a/src/cmd/goinstall/download.go +++ b/src/cmd/goinstall/download.go @@ -38,16 +38,16 @@ var launchpad = regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A // download checks out or updates pkg from the remote server. func download(pkg string) (string, os.Error) { - if strings.Index(pkg, "..") >= 0 { + if strings.Contains(pkg, "..") { return "", os.ErrorString("invalid path (contains ..)") } - if m := bitbucket.MatchStrings(pkg); m != nil { + if m := bitbucket.FindStringSubmatch(pkg); m != nil { if err := vcsCheckout(&hg, root+m[1], "http://"+m[1], m[1]); err != nil { return "", err } return root + pkg, nil } - if m := googlecode.MatchStrings(pkg); m != nil { + if m := googlecode.FindStringSubmatch(pkg); m != nil { var v *vcs switch m[2] { case "hg": @@ -58,12 +58,12 @@ func download(pkg string) (string, os.Error) { // regexp only allows hg, svn to get through panic("missing case in download: " + pkg) } - if err := vcsCheckout(v, root+m[1], "http://"+m[1], m[1]); err != nil { + if err := vcsCheckout(v, root+m[1], "https://"+m[1], m[1]); err != nil { return "", err } return root + pkg, nil } - if m := github.MatchStrings(pkg); m != nil { + if m := github.FindStringSubmatch(pkg); m != nil { if strings.HasSuffix(m[1], ".git") { return "", os.ErrorString("repository " + pkg + " should not have .git suffix") } @@ -72,7 +72,7 @@ func download(pkg string) (string, os.Error) { } return root + pkg, nil } - if m := launchpad.MatchStrings(pkg); m != nil { + if m := launchpad.FindStringSubmatch(pkg); m != nil { // Either lp.net/<project>[/<series>[/<path>]] // or lp.net/~<user or team>/<project>/<branch>[/<path>] if err := vcsCheckout(&bzr, root+m[1], "https://"+m[1], m[1]); err != nil { @@ -88,6 +88,7 @@ func download(pkg string) (string, os.Error) { type vcs struct { cmd string metadir string + checkout string clone string update string updateReleaseFlag string @@ -101,6 +102,7 @@ type vcs struct { var hg = vcs{ cmd: "hg", metadir: ".hg", + checkout: "checkout", clone: "clone", update: "update", updateReleaseFlag: "release", @@ -113,18 +115,20 @@ var hg = vcs{ var git = vcs{ cmd: "git", metadir: ".git", + checkout: "checkout", clone: "clone", update: "pull", updateReleaseFlag: "release", pull: "fetch", - log: "log", - logLimitFlag: "-n1", + log: "show-ref", + logLimitFlag: "", logReleaseFlag: "release", } var svn = vcs{ cmd: "svn", metadir: ".svn", + checkout: "checkout", clone: "checkout", update: "update", updateReleaseFlag: "release", @@ -136,6 +140,7 @@ var svn = vcs{ var bzr = vcs{ cmd: "bzr", metadir: ".bzr", + checkout: "update", clone: "branch", update: "update", updateReleaseFlag: "-rrelease", @@ -146,6 +151,22 @@ var bzr = vcs{ logReleaseFlag: "-rrelease", } +// Try to detect if a "release" tag exists. If it does, update +// to the tagged version, otherwise just update the current branch. +// NOTE(_nil): svn will always fail because it is trying to get +// the revision history of a file named "release" instead of +// looking for a commit with a release tag +func (v *vcs) updateRepo(dst string) os.Error { + if err := quietRun(dst, nil, v.cmd, v.log, v.logLimitFlag, v.logReleaseFlag); err == nil { + if err := run(dst, nil, v.cmd, v.checkout, v.updateReleaseFlag); err != nil { + return err + } + } else if err := run(dst, nil, v.cmd, v.update); err != nil { + return err + } + return nil +} + // vcsCheckout checks out repo into dst using vcs. // It tries to check out (or update, if the dst already // exists and -u was specified on the command line) @@ -164,8 +185,9 @@ func vcsCheckout(vcs *vcs, dst, repo, dashpath string) os.Error { if err := run("/", nil, vcs.cmd, vcs.clone, repo, dst); err != nil { return err } - quietRun(dst, nil, vcs.cmd, vcs.update, vcs.updateReleaseFlag) - + if err := vcs.updateRepo(dst); err != nil { + return err + } // success on first installation - report maybeReportToDashboard(dashpath) } else if *update { @@ -181,19 +203,8 @@ func vcsCheckout(vcs *vcs, dst, repo, dashpath string) os.Error { } } - // Try to detect if a "release" tag exists. If it does, update - // to the tagged version. If no tag is found, then update to the - // tip afterwards. - // NOTE(gustavo@niemeyer.net): What is the expected behavior with - // svn here? "svn log -l1 release" doesn't make sense in this - // context and will probably fail. - if err := quietRun(dst, nil, vcs.cmd, vcs.log, vcs.logLimitFlag, vcs.logReleaseFlag); err == nil { - if err := run(dst, nil, vcs.cmd, vcs.update, vcs.updateReleaseFlag); err != nil { - // The VCS supports tagging, has the "release" tag, but - // something else went wrong. Report. - return err - } - } else if err := run(dst, nil, vcs.cmd, vcs.update); err != nil { + // Update to release or latest revision + if err := vcs.updateRepo(dst); err != nil { return err } } diff --git a/src/cmd/goinstall/main.go b/src/cmd/goinstall/main.go index 60efdf082..b0f08efdf 100644 --- a/src/cmd/goinstall/main.go +++ b/src/cmd/goinstall/main.go @@ -11,28 +11,37 @@ import ( "exec" "flag" "fmt" + "go/token" "io" + "io/ioutil" "os" "path" + "runtime" "strings" ) func usage() { fmt.Fprint(os.Stderr, "usage: goinstall importpath...\n") + fmt.Fprintf(os.Stderr, "\tgoinstall -a\n") flag.PrintDefaults() os.Exit(2) } var ( - argv0 = os.Args[0] - errors = false - gobin = os.Getenv("GOBIN") - parents = make(map[string]string) - root = os.Getenv("GOROOT") - visit = make(map[string]status) + fset = token.NewFileSet() + argv0 = os.Args[0] + errors = false + parents = make(map[string]string) + root = runtime.GOROOT() + visit = make(map[string]status) + logfile = path.Join(root, "goinstall.log") + installedPkgs = make(map[string]bool) + allpkg = flag.Bool("a", false, "install all previously installed packages") reportToDashboard = flag.Bool("dashboard", true, "report public packages at "+dashboardURL) + logPkgs = flag.Bool("log", true, "log installed packages to $GOROOT/goinstall.log for use by -a") update = flag.Bool("u", false, "update already-downloaded packages") + clean = flag.Bool("clean", false, "clean the package directory before installing") verbose = flag.Bool("v", false, "verbose") ) @@ -51,15 +60,30 @@ func main() { os.Exit(1) } root += "/src/pkg/" - if gobin == "" { - gobin = os.Getenv("HOME") + "/bin" - } // special case - "unsafe" is already installed visit["unsafe"] = done - // install command line arguments args := flag.Args() + if *allpkg || *logPkgs { + readPackageList() + } + if *allpkg { + if len(args) != 0 { + usage() // -a and package list both provided + } + // install all packages that were ever installed + if len(installedPkgs) == 0 { + fmt.Fprintf(os.Stderr, "%s: no installed packages\n", argv0) + os.Exit(1) + } + args = make([]string, len(installedPkgs), len(installedPkgs)) + i := 0 + for pkg := range installedPkgs { + args[i] = pkg + i++ + } + } if len(args) == 0 { usage() } @@ -82,6 +106,29 @@ func printDeps(pkg string) { fmt.Fprintf(os.Stderr, "\t%s ->\n", pkg) } +// readPackageList reads the list of installed packages from goinstall.log +func readPackageList() { + pkglistdata, _ := ioutil.ReadFile(logfile) + pkglist := strings.Fields(string(pkglistdata)) + for _, pkg := range pkglist { + installedPkgs[pkg] = true + } +} + +// logPackage logs the named package as installed in goinstall.log, if the package is not found in there +func logPackage(pkg string) { + if installedPkgs[pkg] { + return + } + fout, err := os.Open(logfile, os.O_WRONLY|os.O_APPEND|os.O_CREAT, 0644) + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %s\n", argv0, err) + return + } + fmt.Fprintf(fout, "%s\n", pkg) + fout.Close() +} + // install installs the package named by path, which is needed by parent. func install(pkg, parent string) { // Make sure we're not already trying to install pkg. @@ -104,6 +151,11 @@ func install(pkg, parent string) { // If remote, download or update it. var dir string local := false + if strings.HasPrefix(pkg, "http://") { + fmt.Fprintf(os.Stderr, "%s: %s: 'http://' used in remote path, try '%s'\n", argv0, pkg, pkg[7:]) + errors = true + return + } if isLocalPath(pkg) { dir = pkg local = true @@ -122,23 +174,25 @@ func install(pkg, parent string) { } // Install prerequisites. - files, m, pkgname, err := goFiles(dir, parent == "") + dirInfo, err := scanDir(dir, parent == "") if err != nil { fmt.Fprintf(os.Stderr, "%s: %s: %s\n", argv0, pkg, err) errors = true visit[pkg] = done return } - if len(files) == 0 { + if len(dirInfo.goFiles) == 0 { fmt.Fprintf(os.Stderr, "%s: %s: package has no files\n", argv0, pkg) errors = true visit[pkg] = done return } - for p := range m { - install(p, pkg) + for _, p := range dirInfo.imports { + if p != "C" { + install(p, pkg) + } } - if pkgname == "main" { + if dirInfo.pkgName == "main" { if !errors { fmt.Fprintf(os.Stderr, "%s: %s's dependencies are installed.\n", argv0, pkg) } @@ -152,9 +206,11 @@ func install(pkg, parent string) { if err := domake(dir, pkg, local); err != nil { fmt.Fprintf(os.Stderr, "%s: installing %s: %s\n", argv0, pkg, err) errors = true + } else if !local && *logPkgs { + // mark this package as installed in $GOROOT/goinstall.log + logPackage(pkg) } } - visit[pkg] = done } @@ -207,6 +263,9 @@ func genRun(dir string, stdin []byte, cmd []string, quiet bool) os.Error { io.Copy(&buf, p.Stdout) w, err := p.Wait(0) p.Close() + if err != nil { + return err + } if !w.Exited() || w.ExitStatus() != 0 { if !quiet || *verbose { if dir != "" { diff --git a/src/cmd/goinstall/make.go b/src/cmd/goinstall/make.go index c15709b31..93a648b2b 100644 --- a/src/cmd/goinstall/make.go +++ b/src/cmd/goinstall/make.go @@ -17,30 +17,65 @@ import ( // For non-local packages or packages without Makefiles, // domake generates a standard Makefile and passes it // to make on standard input. -func domake(dir, pkg string, local bool) os.Error { +func domake(dir, pkg string, local bool) (err os.Error) { + needMakefile := true if local { _, err := os.Stat(dir + "/Makefile") if err == nil { - return run(dir, nil, gobin+"/gomake", "install") + needMakefile = false } } - makefile, err := makeMakefile(dir, pkg) - if err != nil { - return err + cmd := []string{"gomake"} + var makefile []byte + if needMakefile { + if makefile, err = makeMakefile(dir, pkg); err != nil { + return err + } + cmd = append(cmd, "-f-") + } + if *clean { + cmd = append(cmd, "clean") } - return run(dir, makefile, gobin+"/gomake", "-f-", "install") + cmd = append(cmd, "install") + return run(dir, makefile, cmd...) } // makeMakefile computes the standard Makefile for the directory dir // installing as package pkg. It includes all *.go files in the directory // except those in package main and those ending in _test.go. func makeMakefile(dir, pkg string) ([]byte, os.Error) { - files, _, _, err := goFiles(dir, false) + dirInfo, err := scanDir(dir, false) if err != nil { return nil, err } + + if len(dirInfo.cgoFiles) == 0 && len(dirInfo.cFiles) > 0 { + // When using cgo, .c files are compiled with gcc. Without cgo, + // they may be intended for 6c. Just error out for now. + return nil, os.ErrorString("C files found in non-cgo package") + } + + cgoFiles := dirInfo.cgoFiles + isCgo := make(map[string]bool, len(cgoFiles)) + for _, file := range cgoFiles { + isCgo[file] = true + } + + oFiles := make([]string, 0, len(dirInfo.cFiles)) + for _, file := range dirInfo.cFiles { + oFiles = append(oFiles, file[:len(file)-2]+".o") + } + + goFiles := make([]string, 0, len(dirInfo.goFiles)) + for _, file := range dirInfo.goFiles { + if !isCgo[file] { + goFiles = append(goFiles, file) + } + } + var buf bytes.Buffer - if err := makefileTemplate.Execute(&makedata{pkg, files}, &buf); err != nil { + md := makedata{pkg, goFiles, cgoFiles, oFiles} + if err := makefileTemplate.Execute(&md, &buf); err != nil { return nil, err } return buf.Bytes(), nil @@ -48,19 +83,38 @@ func makeMakefile(dir, pkg string) ([]byte, os.Error) { // makedata is the data type for the makefileTemplate. type makedata struct { - pkg string // package import path - files []string // list of .go files + Pkg string // package import path + GoFiles []string // list of non-cgo .go files + CgoFiles []string // list of cgo .go files + OFiles []string // list of ofiles for cgo } var makefileTemplate = template.MustParse(` -include $(GOROOT)/src/Make.$(GOARCH) +include $(GOROOT)/src/Make.inc + +TARG={Pkg} -TARG={pkg} +{.section GoFiles} GOFILES=\ -{.repeated section files} +{.repeated section GoFiles} + {@}\ +{.end} + +{.end} +{.section CgoFiles} +CGOFILES=\ +{.repeated section CgoFiles} {@}\ {.end} +{.end} +{.section OFiles} +CGO_OFILES=\ +{.repeated section OFiles} + {@}\ +{.end} + +{.end} include $(GOROOT)/src/Make.pkg `, nil) diff --git a/src/cmd/goinstall/parse.go b/src/cmd/goinstall/parse.go index ae391ed9a..679edfabc 100644 --- a/src/cmd/goinstall/parse.go +++ b/src/cmd/goinstall/parse.go @@ -16,36 +16,60 @@ import ( "go/parser" ) -// goFiles returns a list of the *.go source files in dir, excluding -// those in package main (unless allowMain is true) or ending in -// _test.go. It also returns a map giving the packages imported by -// those files, and the package name. -// The map keys are the imported paths. The key's value -// is one file that imports that path. -func goFiles(dir string, allowMain bool) (files []string, imports map[string]string, pkgName string, err os.Error) { + +type dirInfo struct { + goFiles []string // .go files within dir (including cgoFiles) + cgoFiles []string // .go files that import "C" + cFiles []string // .c files within dir + imports []string // All packages imported by goFiles + pkgName string // Name of package within dir +} + +// scanDir returns a structure with details about the Go content found +// in the given directory. The list of files will NOT contain the +// following entries: +// +// - Files in package main (unless allowMain is true) +// - Files ending in _test.go +// - Files starting with _ (temporary) +// - Files containing .cgo in their names +// +// The imports map keys are package paths imported by listed Go files, +// and the values are the Go files importing the respective package paths. +func scanDir(dir string, allowMain bool) (info *dirInfo, err os.Error) { f, err := os.Open(dir, os.O_RDONLY, 0) if err != nil { - return nil, nil, "", err + return nil, err } dirs, err := f.Readdir(-1) f.Close() if err != nil { - return nil, nil, "", err + return nil, err } - files = make([]string, 0, len(dirs)) - imports = make(map[string]string) + goFiles := make([]string, 0, len(dirs)) + cgoFiles := make([]string, 0, len(dirs)) + cFiles := make([]string, 0, len(dirs)) + importsm := make(map[string]bool) + pkgName := "" for i := range dirs { d := &dirs[i] + if strings.HasPrefix(d.Name, "_") || strings.Index(d.Name, ".cgo") != -1 { + continue + } + if strings.HasSuffix(d.Name, ".c") { + cFiles = append(cFiles, d.Name) + continue + } if !strings.HasSuffix(d.Name, ".go") || strings.HasSuffix(d.Name, "_test.go") { continue } filename := path.Join(dir, d.Name) - pf, err := parser.ParseFile(filename, nil, nil, parser.ImportsOnly) + pf, err := parser.ParseFile(fset, filename, nil, parser.ImportsOnly) if err != nil { - return nil, nil, "", err + return nil, err } - s := string(pf.Name.Name()) + s := string(pf.Name.Name) if s == "main" && !allowMain { continue } @@ -56,24 +80,31 @@ func goFiles(dir string, allowMain bool) (files []string, imports map[string]str // do we return pkgName=="main". // A mix of main and another package reverts // to the original (allowMain=false) behaviour. - if allowMain && pkgName == "main" { - return goFiles(dir, false) + if s == "main" || pkgName == "main" { + return scanDir(dir, false) } - return nil, nil, "", os.ErrorString("multiple package names in " + dir) + return nil, os.ErrorString("multiple package names in " + dir) } - n := len(files) - files = files[0 : n+1] - files[n] = filename + goFiles = append(goFiles, d.Name) for _, decl := range pf.Decls { for _, spec := range decl.(*ast.GenDecl).Specs { quoted := string(spec.(*ast.ImportSpec).Path.Value) unquoted, err := strconv.Unquote(quoted) if err != nil { - log.Crashf("%s: parser returned invalid quoted string: <%s>", filename, quoted) + log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted) + } + importsm[unquoted] = true + if unquoted == "C" { + cgoFiles = append(cgoFiles, d.Name) } - imports[unquoted] = filename } } } - return files, imports, pkgName, nil + imports := make([]string, len(importsm)) + i := 0 + for p := range importsm { + imports[i] = p + i++ + } + return &dirInfo{goFiles, cgoFiles, cFiles, imports, pkgName}, nil } diff --git a/src/cmd/gomake/doc.go b/src/cmd/gomake/doc.go new file mode 100644 index 000000000..2f35fd9dd --- /dev/null +++ b/src/cmd/gomake/doc.go @@ -0,0 +1,36 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +The gomake command runs GNU make with an appropriate environment +for using the conventional Go makefiles. If $GOROOT is already +set in the environment, running gomake is exactly the same +as running make (or, on BSD systems, running gmake). + +Usage: gomake [ target ... ] + +Common targets are: + + all (default) + build the package or command, but do not install it. + + install + build and install the package or command + + test + run the tests (packages only) + + bench + run benchmarks (packages only) + + clean + remove object files from the current directory + + nuke + make clean and remove the installed package or command + +See http://golang.org/doc/code.html for information about +writing makefiles. +*/ +package documentation diff --git a/src/cmd/gopack/Makefile b/src/cmd/gopack/Makefile index c3c136f42..859809562 100644 --- a/src/cmd/gopack/Makefile +++ b/src/cmd/gopack/Makefile @@ -2,17 +2,14 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) TARG=gopack OFILES=\ ar.$O\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lmach -lbio -l9 +LIB=\ + ../../../lib/libmach.a\ -clean: - rm -f *.$O $(TARG) - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +include ../../Make.ccmd diff --git a/src/cmd/gopack/ar.c b/src/cmd/gopack/ar.c index 377366ec4..063967bd7 100644 --- a/src/cmd/gopack/ar.c +++ b/src/cmd/gopack/ar.c @@ -593,25 +593,43 @@ scanobj(Biobuf *b, Arfile *ap, long size) vlong offset; Dir *d; static int lastobj = -1; + uchar buf[4]; if (!allobj) /* non-object file encountered */ return; offset = Boffset(b); obj = objtype(b, 0); if (obj < 0) { /* not an object file */ + /* maybe a foreign object file */ + Bseek(b, offset, 0); + memset(buf, 0, sizeof buf); + Bread(b, buf, 4); + + /* maybe a foreign object file? that's okay */ + if((buf[0] == 0x7F && buf[1] == 'E' && buf[2] == 'L' && buf[3] == 'F') || // ELF + (buf[0] == 0xFE && buf[1] == 0xED && buf[2] == 0xFA && (buf[3]&~1) == 0xCE) || // Mach-O big-endian + (buf[3] == 0xFE && buf[2] == 0xED && buf[1] == 0xFA && (buf[0]&~1) == 0xCE)) { // Mach-O little-endian + Bseek(b, offset, 0); + return; + } + if (!gflag || strcmp(file, pkgdef) != 0) { /* don't clear allobj if it's pkg defs */ fprint(2, "gopack: non-object file %s\n", file); + errors++; allobj = 0; } d = dirfstat(Bfildes(b)); - if (d != nil && d->length == 0) + if (d != nil && d->length == 0) { fprint(2, "gopack: zero length file %s\n", file); + errors++; + } free(d); Bseek(b, offset, 0); return; } if (lastobj >= 0 && obj != lastobj) { fprint(2, "gopack: inconsistent object file %s\n", file); + errors++; allobj = 0; Bseek(b, offset, 0); return; @@ -619,6 +637,7 @@ scanobj(Biobuf *b, Arfile *ap, long size) lastobj = obj; if (!readar(b, obj, offset+size, 0)) { fprint(2, "gopack: invalid symbol reference in file %s\n", file); + errors++; allobj = 0; Bseek(b, offset, 0); return; @@ -718,8 +737,8 @@ foundstart: first = 1; start = end = 0; for (n=0; n<size; n+=Blinelen(b)) { - line = Brdline(b, '\n'); - if (line == 0) + line = Brdstr(b, '\n', 0); + if (line == nil) goto bad; if (first && strstrn(line, Blinelen(b), "package ")) { if (Blinelen(b) > sizeof(pkgbuf)-1) @@ -742,14 +761,19 @@ foundstart: safe = 0; start = Boffset(b); // after package statement first = 0; + free(line); continue; } - if(line[0] == '$' && line[1] == '$') + if(line[0] == '$' && line[1] == '$') { + free(line); goto foundend; + } end = Boffset(b); // before closing $$ + free(line); } bad: fprint(2, "gopack: bad package import section in %s\n", file); + errors++; return; foundend: @@ -795,6 +819,7 @@ objsym(Sym *s, void *p) if(s->type == 'T' && duplicate(as->name, &ofile)) { dupfound = 1; fprint(2, "duplicate text symbol: %s and %s: %s\n", as->file, ofile, as->name); + errors++; free(as->name); free(as); return; diff --git a/src/cmd/gopack/doc.go b/src/cmd/gopack/doc.go index 0d5ccdb6c..74c272fd2 100644 --- a/src/cmd/gopack/doc.go +++ b/src/cmd/gopack/doc.go @@ -4,7 +4,7 @@ /* -Gopack program is a variant of the Plan 9 ar tool. The original is documented at +Gopack is a variant of the Plan 9 ar tool. The original is documented at http://plan9.bell-labs.com/magic/man2html/1/ar diff --git a/src/cmd/gotest/Makefile b/src/cmd/gotest/Makefile index b20b1daff..74054e974 100644 --- a/src/cmd/gotest/Makefile +++ b/src/cmd/gotest/Makefile @@ -1,14 +1,18 @@ -# Copyright 2009 The Go Authors. All rights reserved. +# Copyright 2010 The Go Authors. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc -TARG=gotest +TARG=install clean: @true -install: $(TARG) - ! test -f "$(GOBIN)"/$(TARG) || chmod u+w "$(GOBIN)"/$(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) +install: install-gotest install-gotry + +install-%: % + ! test -f "$(GOBIN)"/$* || chmod u+w "$(GOBIN)"/$* + sed 's`@@GOROOT@@`$(GOROOT_FINAL)`' $* >"$(GOBIN)"/$* + chmod +x "$(GOBIN)"/$* + diff --git a/src/cmd/gotest/gotest b/src/cmd/gotest/gotest index fec2b4a4a..7572610d2 100755 --- a/src/cmd/gotest/gotest +++ b/src/cmd/gotest/gotest @@ -3,10 +3,10 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Using all the test*.go files in the current directory, write out a file +# Using all the *_test.go files in the current directory, write out a file # _testmain.go that runs all its tests. Compile everything and run the # tests. -# If files are named on the command line, use them instead of test*.go. +# If files are named on the command line, use them instead of *_test.go. # Makes egrep,grep work better in general if we put them # in ordinary C mode instead of what the current language is. @@ -14,16 +14,15 @@ unset LANG export LC_ALL=C export LC_CTYPE=C -GOBIN="${GOBIN:-$HOME/bin}" - -_GC=$GC # Make.$GOARCH will overwrite this +_GC=$GC # Make.inc will overwrite this if [ ! -f [Mm]akefile ]; then echo 'please create a Makefile for gotest; see http://golang.org/doc/code.html for details' 1>&2 exit 2 fi -. "$GOROOT"/src/Make.$GOARCH +export GOROOT=${GOROOT:-"@@GOROOT@@"} +eval $(gomake -j1 --no-print-directory -f "$GOROOT"/src/Make.inc go-env) if [ -z "$O" ]; then echo 'missing $O - maybe no Make.$GOARCH?' 1>&2 exit 2 @@ -31,18 +30,12 @@ fi E="$GORUN" -# TODO(kaib): proper emulator strategy -case x"$GOARCH" in -xarm) - export E=${GORUN:-qemu-arm -cpu cortex-a8} -esac - # Allow overrides -GC="$GOBIN/${_GC:-$GC} -I _test" -GL="$GOBIN/${GL:-$LD} -L _test" -AS="$GOBIN/$AS" -CC="$GOBIN/$CC" -LD="$GOBIN/$LD" +GC="${_GC:-$GC} -I _test" +GL="${GL:-$LD} -L _test" +AS="$AS" +CC="$CC" +LD="$LD" export GC GL O AS CC LD gofiles="" @@ -93,14 +86,13 @@ fi set -e -"$GOBIN"/gomake testpackage-clean -"$GOBIN"/gomake testpackage "GOTESTFILES=$gofiles" +gomake testpackage-clean +gomake testpackage "GOTESTFILES=$gofiles" if $havex; then $GC -o $xofile $xgofiles fi # They all compile; now generate the code to call them. -trap "rm -f _testmain.go _testmain.$O" 0 1 2 3 14 15 # Suppress output to stdout on Linux MAKEFLAGS= @@ -116,18 +108,18 @@ nmgrep() { # Figure out pkg. case "$i" in *.a) - pkg=$("$GOBIN"/gopack p $i __.PKGDEF | sed -n 's/^package //p' | sed 's/ .*//' | sed 1q) + pkg=$(gopack p $i __.PKGDEF | sed -n 's/^package //p' | sed 's/ .*//' | sed 1q) ;; *) pkg=$(sed -n 's/^ .* in package "\(.*\)".*/\1/p' $i | sed 1q) ;; esac - "$GOBIN"/6nm -s "$i" | egrep ' T .*\.'"$pat"'$' | + 6nm -s "$i" | egrep ' T .*\.'"$pat"'$' | sed 's/.* //; /\..*\./d; s/""\./'"$pkg"'./g' done } -importpath=$("$GOBIN"/gomake -s importpath) +importpath=$(gomake -s importpath) { # test functions are named TestFoo # the grep -v eliminates methods and other special names @@ -155,30 +147,43 @@ importpath=$("$GOBIN"/gomake -s importpath) echo 'import "./_xtest_"' fi echo 'import "testing"' + echo 'import __regexp__ "regexp"' # rename in case tested package is called regexp # test array echo - echo 'var tests = []testing.Test {' + echo 'var tests = []testing.InternalTest{' for i in $tests do - echo ' testing.Test{ "'$i'", '$i' },' + echo ' {"'$i'", '$i'},' done echo '}' # benchmark array - echo 'var benchmarks = []testing.Benchmark {' - for i in $benchmarks - do - echo ' testing.Benchmark{ "'$i'", '$i' },' - done - echo '}' - + if [ "$benchmarks" = "" ] + then + # keep the empty array gofmt-safe. + # (not an issue for the test array, which is never empty.) + echo 'var benchmarks = []testing.InternalBenchmark{}' + else + echo 'var benchmarks = []testing.InternalBenchmark{' + for i in $benchmarks + do + echo ' {"'$i'", '$i'},' + done + echo '}' + fi # body echo echo 'func main() {' - echo ' testing.Main(tests);' - echo ' testing.RunBenchmarks(benchmarks)' + echo ' testing.Main(__regexp__.MatchString, tests)' + echo ' testing.RunBenchmarks(__regexp__.MatchString, benchmarks)' echo '}' }>_testmain.go $GC _testmain.go $GL _testmain.$O + +# Set dynamic linker library path, no matter what it's called, +# to include the current directory while running $O.out, +# so that cgo libraries can be tested without installation. +LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH \ +DYLD_LIBRARY_PATH=.:$DYLD_LIBRARY_PATH \ $E ./$O.out "$@" diff --git a/src/cmd/gotest/gotry b/src/cmd/gotest/gotry new file mode 100755 index 000000000..52c5d2d58 --- /dev/null +++ b/src/cmd/gotest/gotry @@ -0,0 +1,167 @@ +#!/usr/bin/env bash +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# Using all the non-test *.go files in the named directory, write +# out a file /tmp/$USER.try.go to evaluate the expressions on the +# command line, perhaps to discover a function or method that +# gives the desired results. See usage message. +# Compile the program and run it. + +# Makes egrep,grep work better in general if we put them +# in ordinary C mode instead of what the current language is. +unset LANG +export LC_ALL=C +export LC_CTYPE=C + +export GOROOT=${GOROOT:-"@@GOROOT@@"} +eval $(gomake -j1 --no-print-directory -f "$GOROOT"/src/Make.inc go-env) +if [ -z "$O" ]; then + echo 'missing $O - maybe no Make.$GOARCH?' 1>&2 + exit 2 +fi + +# Allow overrides +GC="${_GC:-$GC} -I _test" +GL="${GL:-$LD} -L _test" +AS="$AS" +CC="$CC" +LD="$LD" +export GC GL O AS CC LD + +# Macros for tab and quotes for easier readability. +T=' ' +BQ='`' +SQ="'" +DQ='"' +SD="$SQ$DQ" +DS="$DQ$SQ" + +usage="usage: gotry [packagedirectory] expression ... +Given one expression, gotry attempts to evaluate that expression. +Given multiple expressions, gotry treats them as a list of arguments +and result values and attempts to find a function in the package +that, given the first few expressions as arguments, evaluates to +the remaining expressions as results. If the first expression has +methods, it will also search for applicable methods. + +If there are multiple expressions, a package directory must be +specified. If there is a package argument, the expressions are +evaluated in an environment that includes + import . ${DQ}packagedirectory${DQ} + +Examples: + gotry 3+4 + # evaluates to 7 + gotry strings ${SD}abc${DS} ${SD}c${DS} 7-5 + # finds strings.Index etc. + gotry regexp ${SQ}MustCompile(${DQ}^[0-9]+${DQ})${SQ} ${SD}12345${DS} true + # finds Regexp.MatchString + +" + +function fail() { + echo 2>&1 "$@" + exit 2 +} + +case $# in + 0) + fail "$usage" + ;; + *) + case "$1" in + -*help|-*'?'|'?') + fail "$usage" + esac + if test -d "$GOROOT/src/pkg/$1" + then + pkg=$(basename $1) + dir=$GOROOT/src/pkg/$1 + importdir=$1 + shift + case "$pkg" in + os|syscall) + fail "gotry: cannot try packages os or syscall; they are too dangerous" + esac + fi + ;; +esac + +spaces='[ ][ ]*' + +function getFunctions() { + if [ "$pkg" = "" ] + then + return + fi + for file in $dir/*.go + do + case $file in + *_test*) + continue + esac + grep "func$spaces[A-Z]" $file | # TODO: should be Unicode upper case + sed "s/func$spaces//;s/(.*//" + done | sort -u +} + +# Generate list of public functions. +functions=$(getFunctions) + +# Write file to compile +rm -f /tmp/$USER.try.go +( +cat <<'!' +package main + +import ( + "os" + "try" +! + +if [ "$pkg" != "" ] +then + echo "$T" . '"'$importdir'"' +fi + +cat <<! +) +func main() { + try.Main("$pkg", firstArg, functions, args) +} +var functions = map[string] interface{}{ +! + +for i in $functions +do + echo "$T"'"'$i'": '$i',' +done +echo "}" + +echo 'var args = []interface{}{' + +if [ $# = 1 ] +then + echo "${T}toSlice($1)", +else +for i + do + echo "$T$i", + done +fi +echo "}" + +cat <<! +var firstArg = $BQ$1$BQ +var _ os.Error +func toSlice(a ...interface{}) []interface{} { return a } +! + +)>/tmp/$USER.try.go + +$GC -o /tmp/$USER.try.$O /tmp/$USER.try.go && +$GL -o /tmp/$USER.try /tmp/$USER.try.$O && +/tmp/$USER.try "_$@" +rm -f /tmp/$USER.try /tmp/$USER.try.go /tmp/$USER.try.$O diff --git a/src/cmd/govet/Makefile b/src/cmd/govet/Makefile new file mode 100644 index 000000000..291b27197 --- /dev/null +++ b/src/cmd/govet/Makefile @@ -0,0 +1,11 @@ +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../Make.inc + +TARG=govet +GOFILES=\ + govet.go\ + +include ../../Make.cmd diff --git a/src/cmd/govet/doc.go b/src/cmd/govet/doc.go new file mode 100644 index 000000000..5a2489fca --- /dev/null +++ b/src/cmd/govet/doc.go @@ -0,0 +1,38 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + +Govet does simple checking of Go source code. + +It checks for simple errors in calls to functions named + Print Printf Println + Fprint Fprintf Fprintln + Sprint Sprintf Sprintln + Error Errorf + Fatal Fatalf +If the function name ends with an 'f', the function is assumed to take +a format descriptor string in the manner of fmt.Printf. If not, govet +complains about arguments that look like format descriptor strings. + +Usage: + + govet [flag] [file.go ...] + govet [flag] [directory ...] # Scan all .go files under directory, recursively + +The flags are: + -v + Verbose mode + -printfuncs + A comma-separated list of print-like functions to supplement + the standard list. Each entry is in the form Name:N where N + is the zero-based argument position of the first argument + involved in the print: either the format or the first print + argument for non-formatted prints. For example, + if you have Warn and Warnf functions that take an + io.Writer as their first argument, like Fprintf, + -printfuncs=Warn:1,Warnf:1 + +*/ +package documentation diff --git a/src/cmd/govet/govet.go b/src/cmd/govet/govet.go new file mode 100644 index 000000000..2981891eb --- /dev/null +++ b/src/cmd/govet/govet.go @@ -0,0 +1,325 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Govet is a simple checker for static errors in Go source code. +// See doc.go for more information. +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "go/ast" + "go/parser" + "go/token" + "os" + "path" + "strconv" + "strings" +) + +var verbose = flag.Bool("v", false, "verbose") +var printfuncs = flag.String("printfuncs", "", "comma-separated list of print function names to check") +var exitCode = 0 + +// setExit sets the value for os.Exit when it is called, later. It +// remembers the highest value. +func setExit(err int) { + if err > exitCode { + exitCode = err + } +} + +// Usage is a replacement usage function for the flags package. +func Usage() { + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + flag.PrintDefaults() + os.Exit(2) +} + +// File is a wrapper for the state of a file used in the parser. +// The parse tree walkers are all methods of this type. +type File struct { + file *token.File +} + +func main() { + flag.Usage = Usage + flag.Parse() + + if *printfuncs != "" { + for _, name := range strings.Split(*printfuncs, ",", -1) { + if len(name) == 0 { + flag.Usage() + } + skip := 0 + if colon := strings.LastIndex(name, ":"); colon > 0 { + var err os.Error + skip, err = strconv.Atoi(name[colon+1:]) + if err != nil { + error(`illegal format for "Func:N" argument %q; %s`, name, err) + } + name = name[:colon] + } + if name[len(name)-1] == 'f' { + printfList[name] = skip + } else { + printList[name] = skip + } + } + } + + if flag.NArg() == 0 { + doFile("stdin", os.Stdin) + } else { + for _, name := range flag.Args() { + // Is it a directory? + if fi, err := os.Stat(name); err == nil && fi.IsDirectory() { + walkDir(name) + } else { + doFile(name, nil) + } + } + } + os.Exit(exitCode) +} + +// doFile analyzes one file. If the reader is nil, the source code is read from the +// named file. +func doFile(name string, reader io.Reader) { + fs := token.NewFileSet() + parsedFile, err := parser.ParseFile(fs, name, reader, 0) + if err != nil { + error("%s: %s", name, err) + return + } + file := &File{fs.File(parsedFile.Pos())} + file.checkFile(name, parsedFile) +} + +// Visitor for path.Walk - trivial. Just calls doFile on each file. +// TODO: if govet becomes richer, might want to process +// a directory (package) at a time. +type V struct{} + +func (v V) VisitDir(path string, f *os.FileInfo) bool { + return true +} + +func (v V) VisitFile(path string, f *os.FileInfo) { + if strings.HasSuffix(path, ".go") { + doFile(path, nil) + } +} + +// walkDir recursively walks the tree looking for .go files. +func walkDir(root string) { + errors := make(chan os.Error) + done := make(chan bool) + go func() { + for e := range errors { + error("walk error: %s", e) + } + done <- true + }() + path.Walk(root, V{}, errors) + close(errors) + <-done +} + +// error formats the error to standard error, adding program +// identification and a newline +func error(format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, "govet: "+format+"\n", args...) + setExit(2) +} + +// Println is fmt.Println guarded by -v. +func Println(args ...interface{}) { + if !*verbose { + return + } + fmt.Println(args...) +} + +// Printf is fmt.Printf guarded by -v. +func Printf(format string, args ...interface{}) { + if !*verbose { + return + } + fmt.Printf(format+"\n", args...) +} + +// Bad reports an error and sets the exit code.. +func (f *File) Bad(pos token.Pos, args ...interface{}) { + f.Warn(pos, args...) + setExit(1) +} + +// Badf reports a formatted error and sets the exit code. +func (f *File) Badf(pos token.Pos, format string, args ...interface{}) { + f.Warnf(pos, format, args...) + setExit(1) +} + +// Warn reports an error but does not set the exit code. +func (f *File) Warn(pos token.Pos, args ...interface{}) { + loc := f.file.Position(pos).String() + ": " + fmt.Fprint(os.Stderr, loc+fmt.Sprintln(args...)) +} + +// Warnf reports a formatted error but does not set the exit code. +func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) { + loc := f.file.Position(pos).String() + ": " + fmt.Fprintf(os.Stderr, loc+format+"\n", args...) +} + +// checkFile checks all the top-level declarations in a file. +func (f *File) checkFile(name string, file *ast.File) { + Println("Checking file", name) + ast.Walk(f, file) +} + +// Visit implements the ast.Visitor interface. +func (f *File) Visit(node ast.Node) ast.Visitor { + // TODO: could return nil for nodes that cannot contain a CallExpr - + // will shortcut traversal. Worthwhile? + switch n := node.(type) { + case *ast.CallExpr: + f.checkCallExpr(n) + } + return f +} + + +// checkCallExpr checks a call expression. +func (f *File) checkCallExpr(call *ast.CallExpr) { + switch x := call.Fun.(type) { + case *ast.Ident: + f.checkCall(call, x.Name) + case *ast.SelectorExpr: + f.checkCall(call, x.Sel.Name) + } +} + +// printfList records the formatted-print functions. The value is the location +// of the format parameter. +var printfList = map[string]int{ + "Errorf": 0, + "Fatalf": 0, + "Fprintf": 1, + "Printf": 0, + "Sprintf": 0, +} + +// printList records the unformatted-print functions. The value is the location +// of the first parameter to be printed. +var printList = map[string]int{ + "Error": 0, + "Fatal": 0, + "Fprint": 1, "Fprintln": 1, + "Print": 0, "Println": 0, + "Sprint": 0, "Sprintln": 0, +} + +// checkCall triggers the print-specific checks if the call invokes a print function. +func (f *File) checkCall(call *ast.CallExpr, name string) { + if skip, ok := printfList[name]; ok { + f.checkPrintf(call, name, skip) + return + } + if skip, ok := printList[name]; ok { + f.checkPrint(call, name, skip) + return + } +} + +// checkPrintf checks a call to a formatted print routine such as Printf. +// The skip argument records how many arguments to ignore; that is, +// call.Args[skip] is (well, should be) the format argument. +func (f *File) checkPrintf(call *ast.CallExpr, name string, skip int) { + if len(call.Args) <= skip { + return + } + // Common case: literal is first argument. + arg := call.Args[skip] + lit, ok := arg.(*ast.BasicLit) + if !ok { + // Too hard to check. + if *verbose { + f.Warn(call.Pos(), "can't check args for call to", name) + } + return + } + if lit.Kind == token.STRING { + if bytes.IndexByte(lit.Value, '%') < 0 { + if len(call.Args) > skip+1 { + f.Badf(call.Pos(), "no formatting directive in %s call", name) + } + return + } + } + // Hard part: check formats against args. + // Trivial but useful test: count. + numPercent := 0 + for i := 0; i < len(lit.Value); i++ { + if lit.Value[i] == '%' { + if i+1 < len(lit.Value) && lit.Value[i+1] == '%' { + // %% doesn't count. + i++ + } else { + numPercent++ + } + } + } + expect := len(call.Args) - (skip + 1) + if numPercent != expect { + f.Badf(call.Pos(), "wrong number of formatting directives in %s call: %d percent(s) for %d args", name, numPercent, expect) + } +} + +var terminalNewline = []byte(`\n"`) // \n at end of interpreted string + +// checkPrint checks a call to an unformatted print routine such as Println. +// The skip argument records how many arguments to ignore; that is, +// call.Args[skip] is the first argument to be printed. +func (f *File) checkPrint(call *ast.CallExpr, name string, skip int) { + isLn := strings.HasSuffix(name, "ln") + args := call.Args + if len(args) <= skip { + if *verbose && !isLn { + f.Badf(call.Pos(), "no args in %s call", name) + } + return + } + arg := args[skip] + if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { + if bytes.IndexByte(lit.Value, '%') >= 0 { + f.Badf(call.Pos(), "possible formatting directive in %s call", name) + } + } + if isLn { + // The last item, if a string, should not have a newline. + arg = args[len(call.Args)-1] + if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { + if bytes.HasSuffix(lit.Value, terminalNewline) { + f.Badf(call.Pos(), "%s call ends with newline", name) + } + } + } +} + +// This function never executes, but it serves as a simple test for the program. +// Test with govet -printfuncs="Bad:1,Badf:1,Warn:1,Warnf:1" govet.go +func BadFunctionUsedInTests() { + fmt.Println() // niladic call + fmt.Println("%s", "hi") // % in call to Println + fmt.Printf("%s", "hi", 3) // wrong # percents + fmt.Printf("%s%%%d", "hi", 3) // right # percents + Printf("now is the time", "buddy") // no %s + f := new(File) + f.Warn(0, "%s", "hello", 3) // % in call to added function + f.Warnf(0, "%s", "hello", 3) // wrong # %s in call to added function +} diff --git a/src/cmd/goyacc/Makefile b/src/cmd/goyacc/Makefile index 77ac918bc..54b8f3360 100644 --- a/src/cmd/goyacc/Makefile +++ b/src/cmd/goyacc/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=goyacc GOFILES=\ diff --git a/src/cmd/goyacc/goyacc.go b/src/cmd/goyacc/goyacc.go index a5da5f0a1..c9fa6bfb9 100644 --- a/src/cmd/goyacc/goyacc.go +++ b/src/cmd/goyacc/goyacc.go @@ -299,17 +299,17 @@ type Resrv struct { } var resrv = []Resrv{ - Resrv{"binary", BINARY}, - Resrv{"left", LEFT}, - Resrv{"nonassoc", BINARY}, - Resrv{"prec", PREC}, - Resrv{"right", RIGHT}, - Resrv{"start", START}, - Resrv{"term", TERM}, - Resrv{"token", TERM}, - Resrv{"type", TYPEDEF}, - Resrv{"union", UNION}, - Resrv{"struct", UNION}, + {"binary", BINARY}, + {"left", LEFT}, + {"nonassoc", BINARY}, + {"prec", PREC}, + {"right", RIGHT}, + {"start", START}, + {"term", TERM}, + {"token", TERM}, + {"type", TYPEDEF}, + {"union", UNION}, + {"struct", UNION}, } var zznewstate = 0 @@ -1032,7 +1032,7 @@ func chfind(t int, s string) int { func cpyunion() { if !lflag { - fmt.Fprintf(ftable, "\n//line %v %v\n", lineno, infile) + fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno) } fmt.Fprintf(ftable, "type\tyySymType\tstruct") @@ -1075,7 +1075,7 @@ func cpycode() { lineno++ } if !lflag { - fmt.Fprintf(ftable, "\n//line %v %v\n", lineno, infile) + fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno) } for c != EOF { if c == '%' { @@ -1158,7 +1158,7 @@ func dumpprod(curprod []int, max int) { func cpyact(curprod []int, max int) { if !lflag { - fmt.Fprintf(fcode, "\n//line %v %v\n", lineno, infile) + fmt.Fprintf(fcode, "\n//line %v:%v\n", infile, lineno) } lno := lineno @@ -2066,6 +2066,7 @@ nextk: func output() { var c, u, v int + fmt.Fprintf(ftable, "\n//line yacctab:1\n") fmt.Fprintf(ftable, "var\tyyExca = []int {\n") noset := mkset() @@ -2825,8 +2826,10 @@ func others() { c = getrune(finput) } - parts := strings.Split(yaccpar, "yyrun()", 2) // copy yaccpar + fmt.Fprintf(ftable, "\n//line yaccpar:1\n") + + parts := strings.Split(yaccpar, "yyrun()", 2) fmt.Fprintf(ftable, "%v", parts[0]) ftable.Write(fcode.Bytes()) fmt.Fprintf(ftable, "%v", parts[1]) @@ -3035,7 +3038,7 @@ func open(s string) *bufio.Reader { return bufio.NewReader(fi) } -func create(s string, m int) *bufio.Writer { +func create(s string, m uint32) *bufio.Writer { fo, err := os.Open(s, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, m) if err != nil { error("error opening %v: %v", s, err) @@ -3049,7 +3052,7 @@ func create(s string, m int) *bufio.Writer { // func error(s string, v ...interface{}) { nerrors++ - fmt.Fprintf(stderr, s, v) + fmt.Fprintf(stderr, s, v...) fmt.Fprintf(stderr, ": %v:%v\n", infile, lineno) if fatfl != 0 { summary() @@ -3136,7 +3139,7 @@ out: c = yyTok2[1] /* unknown char */ } if yyDebug >= 3 { - fmt.Printf("lex %.4x %s\n", uint(yychar), yyTokname(c)) + fmt.Printf("lex %U %s\n", uint(yychar), yyTokname(c)) } return c } @@ -3241,7 +3244,7 @@ yydefault: Errflag = 3 /* find a state where "error" is a legal shift action */ - for yyp >= len(YYS) { + for yyp >= 0 { yyn = yyPact[YYS[yyp].yys] + yyErrCode if yyn >= 0 && yyn < yyLast { yystate = yyAct[yyn] /* simulate a shift of "error" */ diff --git a/src/cmd/goyacc/units.y b/src/cmd/goyacc/units.y index bd5517e8b..a7d472fc6 100644 --- a/src/cmd/goyacc/units.y +++ b/src/cmd/goyacc/units.y @@ -215,7 +215,7 @@ expr0: type UnitsLex int -func (_ UnitsLex) Lex(yylval *yySymType) int { +func (UnitsLex) Lex(yylval *yySymType) int { var c, i int c = peekrune @@ -280,7 +280,7 @@ numb: return VAL } -func (_ UnitsLex) Error(s string) { +func (UnitsLex) Error(s string) { Error("syntax error, last name: %v", sym) } @@ -298,7 +298,7 @@ func main() { f, err := os.Open(file, os.O_RDONLY, 0) if err != nil { - fmt.Printf("error opening %v: %v\n", file, err) + fmt.Fprintf(os.Stderr, "error opening %v: %v\n", file, err) os.Exit(1) } fi = bufio.NewReader(f) @@ -390,7 +390,7 @@ func rdigit(c int) bool { func Error(s string, v ...interface{}) { fmt.Printf("%v: %v\n\t", lineno, line) - fmt.Printf(s, v) + fmt.Printf(s, v...) fmt.Printf("\n") nerrors++ diff --git a/src/cmd/hgpatch/Makefile b/src/cmd/hgpatch/Makefile index f7d64bc12..1ef98d7f9 100644 --- a/src/cmd/hgpatch/Makefile +++ b/src/cmd/hgpatch/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=hgpatch GOFILES=\ diff --git a/src/cmd/hgpatch/main.go b/src/cmd/hgpatch/main.go index f6ea36da8..bd4b563f9 100644 --- a/src/cmd/hgpatch/main.go +++ b/src/cmd/hgpatch/main.go @@ -153,7 +153,7 @@ func main() { changed[o.Dst] = 1 } if o.Mode != 0 { - chk(os.Chmod(o.Dst, o.Mode&0755)) + chk(os.Chmod(o.Dst, uint32(o.Mode&0755))) undoRevert(o.Dst) changed[o.Dst] = 1 } @@ -192,7 +192,7 @@ func makeParent(name string) { // Copy of os.MkdirAll but adds to undo log after // creating a directory. -func mkdirAll(path string, perm int) os.Error { +func mkdirAll(path string, perm uint32) os.Error { dir, err := os.Lstat(path) if err == nil { if dir.IsDirectory() { @@ -318,11 +318,9 @@ func hgRename(dst, src string) os.Error { return err } -func copy(a []string) []string { +func dup(a []string) []string { b := make([]string, len(a)) - for i, s := range a { - b[i] = s - } + copy(b, a) return b } @@ -379,7 +377,7 @@ func run(argv []string, input []byte) (out string, err os.Error) { return Error: - err = &runError{copy(argv), err} + err = &runError{dup(argv), err} return } diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c new file mode 100644 index 000000000..210f10ab5 --- /dev/null +++ b/src/cmd/ld/data.c @@ -0,0 +1,914 @@ +// Inferno utils/8l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Data layout and relocation. + +#include "l.h" +#include "../ld/lib.h" +#include "../ld/elf.h" +#include "../ld/pe.h" + +void dynreloc(void); + +/* + * divide-and-conquer list-link + * sort of Sym* structures. + * Used for the data block. + */ +int +datcmp(Sym *s1, Sym *s2) +{ + if(s1->type != s2->type) + return (int)s1->type - (int)s2->type; + if(s1->size != s2->size) { + if(s1->size < s2->size) + return -1; + return +1; + } + return strcmp(s1->name, s2->name); +} + +Sym* +datsort(Sym *l) +{ + Sym *l1, *l2, *le; + + if(l == 0 || l->next == 0) + return l; + + l1 = l; + l2 = l; + for(;;) { + l2 = l2->next; + if(l2 == 0) + break; + l2 = l2->next; + if(l2 == 0) + break; + l1 = l1->next; + } + + l2 = l1->next; + l1->next = 0; + l1 = datsort(l); + l2 = datsort(l2); + + /* set up lead element */ + if(datcmp(l1, l2) < 0) { + l = l1; + l1 = l1->next; + } else { + l = l2; + l2 = l2->next; + } + le = l; + + for(;;) { + if(l1 == 0) { + while(l2) { + le->next = l2; + le = l2; + l2 = l2->next; + } + le->next = 0; + break; + } + if(l2 == 0) { + while(l1) { + le->next = l1; + le = l1; + l1 = l1->next; + } + break; + } + if(datcmp(l1, l2) < 0) { + le->next = l1; + le = l1; + l1 = l1->next; + } else { + le->next = l2; + le = l2; + l2 = l2->next; + } + } + le->next = 0; + return l; +} + +Reloc* +addrel(Sym *s) +{ + if(s->nr >= s->maxr) { + if(s->maxr == 0) + s->maxr = 4; + else + s->maxr <<= 1; + s->r = realloc(s->r, s->maxr*sizeof s->r[0]); + if(s->r == 0) { + diag("out of memory"); + errorexit(); + } + memset(s->r+s->nr, 0, (s->maxr-s->nr)*sizeof s->r[0]); + } + return &s->r[s->nr++]; +} + +void +relocsym(Sym *s) +{ + Reloc *r; + Prog p; + int32 i, off, siz, fl; + vlong o; + uchar *cast; + + cursym = s; + memset(&p, 0, sizeof p); + for(r=s->r; r<s->r+s->nr; r++) { + off = r->off; + siz = r->siz; + if(off < 0 || off+(siz&~Rbig) > s->np) { + diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz&~Rbig, 0, s->np); + continue; + } + if(r->sym != S && (r->sym->type == 0 || r->sym->type == SXREF)) { + diag("%s: not defined", r->sym->name); + continue; + } + if(r->type >= 256) + continue; + + if(r->sym != S && r->sym->type == SDYNIMPORT) + diag("unhandled relocation for %s (type %d rtype %d)", r->sym->name, r->sym->type, r->type); + + if(r->sym != S && !r->sym->reachable) + diag("unreachable sym in relocation: %s %s", s->name, r->sym->name); + + switch(r->type) { + default: + o = 0; + if(archreloc(r, s, &o) < 0) + diag("unknown reloc %d", r->type); + break; + case D_ADDR: + o = symaddr(r->sym) + r->add; + break; + case D_PCREL: + o = symaddr(r->sym) + r->add - (s->value + r->off + r->siz); + break; + case D_SIZE: + o = r->sym->size + r->add; + break; + } +//print("relocate %s %p %s => %p %p %p %p [%p]\n", s->name, s->value+off, r->sym ? r->sym->name : "<nil>", (void*)symaddr(r->sym), (void*)s->value, (void*)r->off, (void*)r->siz, (void*)o); + switch(siz) { + default: + cursym = s; + diag("bad reloc size %#ux for %s", siz, r->sym->name); + case 4 + Rbig: + fl = o; + s->p[off] = fl>>24; + s->p[off+1] = fl>>16; + s->p[off+2] = fl>>8; + s->p[off+3] = fl; + break; + case 4 + Rlittle: + fl = o; + s->p[off] = fl; + s->p[off+1] = fl>>8; + s->p[off+2] = fl>>16; + s->p[off+3] = fl>>24; + break; + case 4: + fl = o; + cast = (uchar*)&fl; + for(i=0; i<4; i++) + s->p[off+i] = cast[inuxi4[i]]; + break; + case 8: + cast = (uchar*)&o; + for(i=0; i<8; i++) + s->p[off+i] = cast[inuxi8[i]]; + break; + } + } +} + +void +reloc(void) +{ + Sym *s; + + if(debug['v']) + Bprint(&bso, "%5.2f reloc\n", cputime()); + Bflush(&bso); + + for(s=textp; s!=S; s=s->next) + relocsym(s); + for(s=datap; s!=S; s=s->next) + relocsym(s); +} + +void +dynrelocsym(Sym *s) +{ + Reloc *r; + + for(r=s->r; r<s->r+s->nr; r++) + if(r->sym->type == SDYNIMPORT || r->type >= 256) + adddynrel(s, r); +} + +void +dynreloc(void) +{ + Sym *s; + + if(debug['v']) + Bprint(&bso, "%5.2f reloc\n", cputime()); + Bflush(&bso); + + for(s=textp; s!=S; s=s->next) + dynrelocsym(s); + for(s=datap; s!=S; s=s->next) + dynrelocsym(s); + if(iself) + elfdynhash(); +} + +void +symgrow(Sym *s, int32 siz) +{ + if(s->np >= siz) + return; + + if(s->maxp < siz) { + if(s->maxp == 0) + s->maxp = 8; + while(s->maxp < siz) + s->maxp <<= 1; + s->p = realloc(s->p, s->maxp); + if(s->p == nil) { + diag("out of memory"); + errorexit(); + } + memset(s->p+s->np, 0, s->maxp-s->np); + } + s->np = siz; +} + +void +savedata(Sym *s, Prog *p) +{ + int32 off, siz, i, fl; + uchar *cast; + vlong o; + Reloc *r; + + off = p->from.offset; + siz = p->datasize; + symgrow(s, off+siz); + + switch(p->to.type) { + default: + diag("bad data: %P", p); + break; + + case D_FCONST: + switch(siz) { + default: + case 4: + fl = ieeedtof(&p->to.ieee); + cast = (uchar*)&fl; + for(i=0; i<4; i++) + s->p[off+i] = cast[fnuxi4[i]]; + break; + case 8: + cast = (uchar*)&p->to.ieee; + for(i=0; i<8; i++) + s->p[off+i] = cast[fnuxi8[i]]; + break; + } + break; + + case D_SCONST: + for(i=0; i<siz; i++) + s->p[off+i] = p->to.scon[i]; + break; + + case D_CONST: + if(p->to.sym) + goto Addr; + o = p->to.offset; + fl = o; + cast = (uchar*)&fl; + switch(siz) { + default: + diag("bad nuxi %d\n%P", siz, p); + break; + case 1: + s->p[off] = cast[inuxi1[0]]; + break; + case 2: + for(i=0; i<2; i++) + s->p[off+i] = cast[inuxi2[i]]; + break; + case 4: + for(i=0; i<4; i++) + s->p[off+i] = cast[inuxi4[i]]; + break; + case 8: + cast = (uchar*)&o; + for(i=0; i<8; i++) + s->p[off+i] = cast[inuxi8[i]]; + break; + } + break; + + case D_ADDR: + case D_SIZE: + Addr: + r = addrel(s); + r->off = off; + r->siz = siz; + r->sym = p->to.sym; + r->type = p->to.type; + if(r->type != D_SIZE) + r->type = D_ADDR; + r->add = p->to.offset; + break; + } +} + +static void +blk(Sym *allsym, int32 addr, int32 size) +{ + Sym *sym; + int32 eaddr; + uchar *p, *ep; + + for(sym = allsym; sym != nil; sym = sym->next) + if(!(sym->type&SSUB) && sym->value >= addr) + break; + + eaddr = addr+size; + for(; sym != nil; sym = sym->next) { + if(sym->type&SSUB) + continue; + if(sym->value >= eaddr) + break; + if(sym->value < addr) { + diag("phase error: addr=%#llx but sym=%#llx type=%d", (vlong)addr, (vlong)sym->value, sym->type); + errorexit(); + } + cursym = sym; + for(; addr < sym->value; addr++) + cput(0); + p = sym->p; + ep = p + sym->np; + while(p < ep) + cput(*p++); + addr += sym->np; + for(; addr < sym->value+sym->size; addr++) + cput(0); + if(addr != sym->value+sym->size) { + diag("phase error: addr=%#llx value+size=%#llx", (vlong)addr, (vlong)sym->value+sym->size); + errorexit(); + } + } + + for(; addr < eaddr; addr++) + cput(0); + cflush(); +} + +void +codeblk(int32 addr, int32 size) +{ + Sym *sym; + int32 eaddr, n, epc; + Prog *p; + uchar *q; + + if(debug['a']) + Bprint(&bso, "codeblk [%#x,%#x) at offset %#llx\n", addr, addr+size, seek(cout, 0, 1)); + + blk(textp, addr, size); + + /* again for printing */ + if(!debug['a']) + return; + + for(sym = textp; sym != nil; sym = sym->next) { + if(!sym->reachable) + continue; + if(sym->value >= addr) + break; + } + + eaddr = addr + size; + for(; sym != nil; sym = sym->next) { + if(!sym->reachable) + continue; + if(sym->value >= eaddr) + break; + + if(addr < sym->value) { + Bprint(&bso, "%-20s %.8llux|", "_", (vlong)addr); + for(; addr < sym->value; addr++) + Bprint(&bso, " %.2ux", 0); + Bprint(&bso, "\n"); + } + p = sym->text; + if(p == nil) { + Bprint(&bso, "%.6llux\t%-20s | foreign text\n", (vlong)addr, sym->name); + n = sym->size; + q = sym->p; + + while(n >= 16) { + Bprint(&bso, "%.6ux\t%-20.16I\n", addr, q); + addr += 16; + q += 16; + n -= 16; + } + if(n > 0) + Bprint(&bso, "%.6ux\t%-20.*I\n", addr, n, q); + addr += n; + continue; + } + + Bprint(&bso, "%.6llux\t%-20s | %P\n", (vlong)sym->value, sym->name, p); + for(p = p->link; p != P; p = p->link) { + if(p->link != P) + epc = p->link->pc; + else + epc = sym->value + sym->size; + Bprint(&bso, "%.6ux\t", p->pc); + q = sym->p + p->pc - sym->value; + n = epc - p->pc; + Bprint(&bso, "%-20.*I | %P\n", n, q, p); + addr += n; + } + } + + if(addr < eaddr) { + Bprint(&bso, "%-20s %.8llux|", "_", (vlong)addr); + for(; addr < eaddr; addr++) + Bprint(&bso, " %.2ux", 0); + } + Bflush(&bso); +} + +void +datblk(int32 addr, int32 size) +{ + Sym *sym; + int32 eaddr; + uchar *p, *ep; + + if(debug['a']) + Bprint(&bso, "datblk [%#x,%#x) at offset %#llx\n", addr, addr+size, seek(cout, 0, 1)); + + blk(datap, addr, size); + + /* again for printing */ + if(!debug['a']) + return; + + for(sym = datap; sym != nil; sym = sym->next) + if(sym->value >= addr) + break; + + eaddr = addr + size; + for(; sym != nil; sym = sym->next) { + if(sym->value >= eaddr) + break; + if(addr < sym->value) { + Bprint(&bso, "%-20s %.8ux| 00 ...\n", "(pre-pad)", addr); + addr = sym->value; + } + Bprint(&bso, "%-20s %.8ux|", sym->name, addr); + p = sym->p; + ep = p + sym->np; + while(p < ep) + Bprint(&bso, " %.2ux", *p++); + addr += sym->np; + for(; addr < sym->value+sym->size; addr++) + Bprint(&bso, " %.2ux", 0); + Bprint(&bso, "\n"); + } + + if(addr < eaddr) + Bprint(&bso, "%-20s %.8ux| 00 ...\n", "(post-pad)", addr); + Bprint(&bso, "%-20s %.8ux|\n", "", eaddr); +} + +void +strnput(char *s, int n) +{ + for(; *s && n > 0; s++) { + cput(*s); + n--; + } + while(n > 0) { + cput(0); + n--; + } +} + +vlong +addstring(Sym *s, char *str) +{ + int n; + int32 r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + r = s->size; + n = strlen(str)+1; + if(strcmp(s->name, ".shstrtab") == 0) + elfsetstring(str, r); + symgrow(s, r+n); + memmove(s->p+r, str, n); + s->size += n; + return r; +} + +vlong +adduintxx(Sym *s, uint64 v, int wid) +{ + int32 i, r, fl; + vlong o; + uchar *cast; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + r = s->size; + s->size += wid; + symgrow(s, s->size); + assert(r+wid <= s->size); + fl = v; + cast = (uchar*)&fl; + switch(wid) { + case 1: + s->p[r] = cast[inuxi1[0]]; + break; + case 2: + for(i=0; i<2; i++) + s->p[r+i] = cast[inuxi2[i]]; + break; + case 4: + for(i=0; i<4; i++) + s->p[r+i] = cast[inuxi4[i]]; + break; + case 8: + o = v; + cast = (uchar*)&o; + for(i=0; i<8; i++) + s->p[r+i] = cast[inuxi8[i]]; + break; + } + return r; +} + +vlong +adduint8(Sym *s, uint8 v) +{ + return adduintxx(s, v, 1); +} + +vlong +adduint16(Sym *s, uint16 v) +{ + return adduintxx(s, v, 2); +} + +vlong +adduint32(Sym *s, uint32 v) +{ + return adduintxx(s, v, 4); +} + +vlong +adduint64(Sym *s, uint64 v) +{ + return adduintxx(s, v, 8); +} + +vlong +addaddrplus(Sym *s, Sym *t, int32 add) +{ + vlong i; + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + i = s->size; + s->size += PtrSize; + symgrow(s, s->size); + r = addrel(s); + r->sym = t; + r->off = i; + r->siz = PtrSize; + r->type = D_ADDR; + r->add = add; + return i; +} + +vlong +addpcrelplus(Sym *s, Sym *t, int32 add) +{ + vlong i; + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + i = s->size; + s->size += 4; + symgrow(s, s->size); + r = addrel(s); + r->sym = t; + r->off = i; + r->add = add; + r->type = D_PCREL; + r->siz = 4; + return i; +} + +vlong +addaddr(Sym *s, Sym *t) +{ + return addaddrplus(s, t, 0); +} + +vlong +addsize(Sym *s, Sym *t) +{ + vlong i; + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + i = s->size; + s->size += PtrSize; + symgrow(s, s->size); + r = addrel(s); + r->sym = t; + r->off = i; + r->siz = PtrSize; + r->type = D_SIZE; + return i; +} + +void +dodata(void) +{ + int32 h, t, datsize; + Section *sect; + Sym *s, *last, **l; + + if(debug['v']) + Bprint(&bso, "%5.2f dodata\n", cputime()); + Bflush(&bso); + + last = nil; + datap = nil; + for(h=0; h<NHASH; h++) { + for(s=hash[h]; s!=S; s=s->hash){ + if(!s->reachable || s->special) + continue; + if(STEXT < s->type && s->type < SXREF) { + if(last == nil) + datap = s; + else + last->next = s; + s->next = nil; + last = s; + } + } + } + + for(s = datap; s != nil; s = s->next) { + if(s->np > 0 && s->type == SBSS) // TODO: necessary? + s->type = SDATA; + if(s->np > s->size) + diag("%s: initialize bounds (%lld < %d)", + s->name, (vlong)s->size, s->np); + } + + /* + * now that we have the datap list, but before we start + * to assign addresses, record all the necessary + * dynamic relocations. these will grow the relocation + * symbol, which is itself data. + */ + dynreloc(); + + /* some symbols may no longer belong in datap (Mach-O) */ + for(l=&datap; (s=*l) != nil; ) { + if(s->type <= STEXT || SXREF <= s->type) + *l = s->next; + else + l = &s->next; + } + *l = nil; + + datap = datsort(datap); + + /* + * allocate data sections. list is sorted by type, + * so we can just walk it for each piece we want to emit. + */ + + /* read-only data */ + sect = addsection(&segtext, ".rodata", 06); + sect->vaddr = 0; + datsize = 0; + s = datap; + for(; s != nil && s->type < SDATA; s = s->next) { + s->type = SRODATA; + t = rnd(s->size, 4); + s->size = t; + s->value = datsize; + datsize += t; + } + sect->len = datsize - sect->vaddr; + + /* data */ + datsize = 0; + sect = addsection(&segdata, ".data", 06); + sect->vaddr = 0; + for(; s != nil && s->type < SBSS; s = s->next) { + s->type = SDATA; + t = s->size; + if(t == 0 && s->name[0] != '.') { + diag("%s: no size", s->name); + t = 1; + } + if(t & 1) + ; + else if(t & 2) + datsize = rnd(datsize, 2); + else if(t & 4) + datsize = rnd(datsize, 4); + else + datsize = rnd(datsize, 8); + s->value = datsize; + datsize += t; + } + sect->len = datsize - sect->vaddr; + + /* bss */ + sect = addsection(&segdata, ".bss", 06); + sect->vaddr = datsize; + for(; s != nil; s = s->next) { + if(s->type != SBSS) { + cursym = s; + diag("unexpected symbol type %d", s->type); + } + t = s->size; + if(t & 1) + ; + else if(t & 2) + datsize = rnd(datsize, 2); + else if(t & 4) + datsize = rnd(datsize, 4); + else + datsize = rnd(datsize, 8); + s->size = t; + s->value = datsize; + datsize += t; + } + sect->len = datsize - sect->vaddr; +} + +// assign addresses to text +void +textaddress(void) +{ + uvlong va; + Prog *p; + Section *sect; + Sym *sym, *sub; + + addsection(&segtext, ".text", 05); + + // Assign PCs in text segment. + // Could parallelize, by assigning to text + // and then letting threads copy down, but probably not worth it. + sect = segtext.sect; + va = INITTEXT; + sect->vaddr = va; + for(sym = textp; sym != nil; sym = sym->next) { + if(sym->type & SSUB) + continue; + sym->value = 0; + for(sub = sym; sub != S; sub = sub->sub) { + sub->value += va; + for(p = sub->text; p != P; p = p->link) + p->pc += sub->value; + } + if(sym->size == 0 && sym->sub != S) { + cursym = sym; + } + va += sym->size; + } + sect->len = va - sect->vaddr; +} + +// assign addresses +void +address(void) +{ + Section *s, *text, *data, *rodata, *bss; + Sym *sym, *sub; + uvlong va; + + va = INITTEXT; + segtext.rwx = 05; + segtext.vaddr = va; + segtext.fileoff = HEADR; + for(s=segtext.sect; s != nil; s=s->next) { + s->vaddr = va; + va += s->len; + segtext.len = va - INITTEXT; + va = rnd(va, INITRND); + } + segtext.filelen = segtext.len; + + segdata.rwx = 06; + segdata.vaddr = va; + segdata.fileoff = va - segtext.vaddr + segtext.fileoff; + if(thechar == '8' && HEADTYPE == 10) // Windows PE + segdata.fileoff = segtext.fileoff + rnd(segtext.len, PEFILEALIGN); + if(thechar == '8' && HEADTYPE == 2) { // Plan 9 + segdata.vaddr = va = rnd(va, 4096); + segdata.fileoff = segtext.fileoff + segtext.filelen; + } + for(s=segdata.sect; s != nil; s=s->next) { + s->vaddr = va; + va += s->len; + segdata.len = va - segdata.vaddr; + } + segdata.filelen = segdata.sect->len; // assume .data is first + + text = segtext.sect; + rodata = segtext.sect->next; + data = segdata.sect; + bss = segdata.sect->next; + + for(sym = datap; sym != nil; sym = sym->next) { + cursym = sym; + if(sym->type < SDATA) + sym->value += rodata->vaddr; + else + sym->value += data->vaddr; + for(sub = sym->sub; sub != nil; sub = sub->sub) + sub->value += sym->value; + } + + xdefine("text", STEXT, text->vaddr); + xdefine("etext", STEXT, text->vaddr + text->len); + xdefine("rodata", SRODATA, rodata->vaddr); + xdefine("erodata", SRODATA, rodata->vaddr + rodata->len); + xdefine("data", SBSS, data->vaddr); + xdefine("edata", SBSS, data->vaddr + data->len); + xdefine("end", SBSS, segdata.vaddr + segdata.len); + + sym = lookup("pclntab", 0); + xdefine("epclntab", SRODATA, sym->value + sym->size); + sym = lookup("symtab", 0); + xdefine("esymtab", SRODATA, sym->value + sym->size); +} diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c new file mode 100644 index 000000000..506c6e5db --- /dev/null +++ b/src/cmd/ld/dwarf.c @@ -0,0 +1,2547 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO/NICETOHAVE: +// - eliminate DW_CLS_ if not used +// - package info in compilation units +// - assign global variables and types to their packages +// - (upstream) type info for C parts of runtime +// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg +// ptype struct '[]uint8' and qualifiers need to be quoted away +// - lexical scoping is lost, so gdb gets confused as to which 'main.i' you mean. +// - file:line info for variables +// - make strings a typedef so prettyprinters can see the underlying string type +// +#include "l.h" +#include "lib.h" +#include "../ld/dwarf.h" +#include "../ld/dwarf_defs.h" +#include "../ld/elf.h" +#include "../ld/macho.h" + +/* + * Offsets and sizes of the debug_* sections in the cout file. + */ + +static vlong abbrevo; +static vlong abbrevsize; +static vlong lineo; +static vlong linesize; +static vlong infoo; // also the base for DWDie->offs and reference attributes. +static vlong infosize; +static vlong frameo; +static vlong framesize; +static vlong pubnameso; +static vlong pubnamessize; +static vlong pubtypeso; +static vlong pubtypessize; +static vlong arangeso; +static vlong arangessize; +static vlong gdbscripto; +static vlong gdbscriptsize; + +static char gdbscript[1024]; + +/* + * Basic I/O + */ + +static void +addrput(vlong addr) +{ + switch(PtrSize) { + case 4: + LPUT(addr); + break; + case 8: + VPUT(addr); + break; + } +} + +static int +uleb128enc(uvlong v, char* dst) +{ + uint8 c, len; + + len = 0; + do { + c = v & 0x7f; + v >>= 7; + if (v) + c |= 0x80; + if (dst) + *dst++ = c; + len++; + } while (c & 0x80); + return len; +}; + +static int +sleb128enc(vlong v, char *dst) +{ + uint8 c, s, len; + + len = 0; + do { + c = v & 0x7f; + s = v & 0x40; + v >>= 7; + if ((v != -1 || !s) && (v != 0 || s)) + c |= 0x80; + if (dst) + *dst++ = c; + len++; + } while(c & 0x80); + return len; +} + +static void +uleb128put(vlong v) +{ + char buf[10]; + strnput(buf, uleb128enc(v, buf)); +} + +static void +sleb128put(vlong v) +{ + char buf[10]; + strnput(buf, sleb128enc(v, buf)); +} + +/* + * Defining Abbrevs. This is hardcoded, and there will be + * only a handful of them. The DWARF spec places no restriction on + * the ordering of atributes in the Abbrevs and DIEs, and we will + * always write them out in the order of declaration in the abbrev. + * This implementation relies on tag, attr < 127, so they serialize as + * a char. Higher numbered user-defined tags or attributes can be used + * for storing internal data but won't be serialized. + */ +typedef struct DWAttrForm DWAttrForm; +struct DWAttrForm { + uint8 attr; + uint8 form; +}; + +// Index into the abbrevs table below. +// Keep in sync with ispubname() and ispubtype() below. +// ispubtype considers >= NULLTYPE public +enum +{ + DW_ABRV_NULL, + DW_ABRV_COMPUNIT, + DW_ABRV_FUNCTION, + DW_ABRV_VARIABLE, + DW_ABRV_AUTO, + DW_ABRV_PARAM, + DW_ABRV_STRUCTFIELD, + DW_ABRV_FUNCTYPEPARAM, + DW_ABRV_DOTDOTDOT, + DW_ABRV_ARRAYRANGE, + DW_ABRV_NULLTYPE, + DW_ABRV_BASETYPE, + DW_ABRV_ARRAYTYPE, + DW_ABRV_CHANTYPE, + DW_ABRV_FUNCTYPE, + DW_ABRV_IFACETYPE, + DW_ABRV_MAPTYPE, + DW_ABRV_PTRTYPE, + DW_ABRV_SLICETYPE, + DW_ABRV_STRINGTYPE, + DW_ABRV_STRUCTTYPE, + DW_ABRV_TYPEDECL, + DW_NABRV +}; + +typedef struct DWAbbrev DWAbbrev; +static struct DWAbbrev { + uint8 tag; + uint8 children; + DWAttrForm attr[30]; +} abbrevs[DW_NABRV] = { + /* The mandatory DW_ABRV_NULL entry. */ + { 0 }, + /* COMPUNIT */ + { + DW_TAG_compile_unit, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_language, DW_FORM_data1, + DW_AT_low_pc, DW_FORM_addr, + DW_AT_high_pc, DW_FORM_addr, + DW_AT_stmt_list, DW_FORM_data4, + 0, 0 + }, + /* FUNCTION */ + { + DW_TAG_subprogram, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_low_pc, DW_FORM_addr, + DW_AT_high_pc, DW_FORM_addr, + DW_AT_external, DW_FORM_flag, + 0, 0 + }, + /* VARIABLE */ + { + DW_TAG_variable, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_location, DW_FORM_block1, + DW_AT_type, DW_FORM_ref_addr, + DW_AT_external, DW_FORM_flag, + 0, 0 + }, + /* AUTO */ + { + DW_TAG_variable, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_location, DW_FORM_block1, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + /* PARAM */ + { + DW_TAG_formal_parameter, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_location, DW_FORM_block1, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + /* STRUCTFIELD */ + { + DW_TAG_member, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_data_member_location, DW_FORM_block1, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + /* FUNCTYPEPARAM */ + { + DW_TAG_formal_parameter, DW_CHILDREN_no, + // No name! + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* DOTDOTDOT */ + { + DW_TAG_unspecified_parameters, DW_CHILDREN_no, + 0, 0 + }, + /* ARRAYRANGE */ + { + DW_TAG_subrange_type, DW_CHILDREN_no, + // No name! + DW_AT_type, DW_FORM_ref_addr, + DW_AT_upper_bound, DW_FORM_data1, + 0, 0 + }, + + // Below here are the types considered public by ispubtype + /* NULLTYPE */ + { + DW_TAG_unspecified_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + 0, 0 + }, + /* BASETYPE */ + { + DW_TAG_base_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_encoding, DW_FORM_data1, + DW_AT_byte_size, DW_FORM_data1, + 0, 0 + }, + /* ARRAYTYPE */ + // child is subrange with upper bound + { + DW_TAG_array_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + DW_AT_byte_size, DW_FORM_udata, + 0, 0 + }, + + /* CHANTYPE */ + { + DW_TAG_typedef, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* FUNCTYPE */ + { + DW_TAG_subroutine_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, +// DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* IFACETYPE */ + { + DW_TAG_typedef, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* MAPTYPE */ + { + DW_TAG_typedef, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* PTRTYPE */ + { + DW_TAG_pointer_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, + + /* SLICETYPE */ + { + DW_TAG_structure_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_byte_size, DW_FORM_udata, + 0, 0 + }, + + /* STRINGTYPE */ + { + DW_TAG_structure_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_byte_size, DW_FORM_udata, + 0, 0 + }, + + /* STRUCTTYPE */ + { + DW_TAG_structure_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_byte_size, DW_FORM_udata, + 0, 0 + }, + + /* TYPEDECL */ + { + DW_TAG_typedef, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + DW_AT_type, DW_FORM_ref_addr, + 0, 0 + }, +}; + +static void +writeabbrev(void) +{ + int i, n; + + abbrevo = cpos(); + for (i = 1; i < DW_NABRV; i++) { + // See section 7.5.3 + uleb128put(i); + uleb128put(abbrevs[i].tag); + cput(abbrevs[i].children); + // 0 is not a valid attr or form, and DWAbbrev.attr is + // 0-terminated, so we can treat it as a string + n = strlen((char*)abbrevs[i].attr) / 2; + strnput((char*)abbrevs[i].attr, + (n+1) * sizeof(DWAttrForm)); + } + cput(0); + abbrevsize = cpos() - abbrevo; +} + +/* + * Debugging Information Entries and their attributes. + */ + +enum +{ + HASHSIZE = 107 +}; + +static uint32 +hashstr(char* s) +{ + uint32 h; + + h = 0; + while (*s) + h = h+h+h + *s++; + return h % HASHSIZE; +} + +// For DW_CLS_string and _block, value should contain the length, and +// data the data, for _reference, value is 0 and data is a DWDie* to +// the referenced instance, for all others, value is the whole thing +// and data is null. + +typedef struct DWAttr DWAttr; +struct DWAttr { + DWAttr *link; + uint8 atr; // DW_AT_ + uint8 cls; // DW_CLS_ + vlong value; + char *data; +}; + +typedef struct DWDie DWDie; +struct DWDie { + int abbrev; + DWDie *link; + DWDie *child; + DWAttr *attr; + // offset into .debug_info section, i.e relative to + // infoo. only valid after call to putdie() + vlong offs; + DWDie **hash; // optional index of children by name, enabled by mkindex() + DWDie *hlink; // bucket chain in parent's index +}; + +/* + * Root DIEs for compilation units, types and global variables. + */ + +static DWDie dwroot; +static DWDie dwtypes; +static DWDie dwglobals; + +static DWAttr* +newattr(DWDie *die, uint8 attr, int cls, vlong value, char *data) +{ + DWAttr *a; + + a = mal(sizeof *a); + a->link = die->attr; + die->attr = a; + a->atr = attr; + a->cls = cls; + a->value = value; + a->data = data; + return a; +} + +// Each DIE (except the root ones) has at least 1 attribute: its +// name. getattr moves the desired one to the front so +// frequently searched ones are found faster. +static DWAttr* +getattr(DWDie *die, uint8 attr) +{ + DWAttr *a, *b; + + if (die->attr->atr == attr) + return die->attr; + + a = die->attr; + b = a->link; + while (b != nil) { + if (b->atr == attr) { + a->link = b->link; + b->link = die->attr; + die->attr = b; + return b; + } + a = b; + b = b->link; + } + return nil; +} + +static void +delattr(DWDie *die, uint8 attr) +{ + DWAttr **a; + + a = &die->attr; + while (*a != nil) + if ((*a)->atr == attr) + *a = (*a)->link; + else + a = &((*a)->link); +} + +// Every DIE has at least a DW_AT_name attribute (but it will only be +// written out if it is listed in the abbrev). If its parent is +// keeping an index, the new DIE will be inserted there. +static DWDie* +newdie(DWDie *parent, int abbrev, char *name) +{ + DWDie *die; + int h; + + die = mal(sizeof *die); + die->abbrev = abbrev; + die->link = parent->child; + parent->child = die; + + newattr(die, DW_AT_name, DW_CLS_STRING, strlen(name), name); + + if (parent->hash) { + h = hashstr(name); + die->hlink = parent->hash[h]; + parent->hash[h] = die; + } + + return die; +} + +static void +mkindex(DWDie *die) +{ + die->hash = mal(HASHSIZE * sizeof(DWDie*)); +} + +// Find child by AT_name using hashtable if available or linear scan +// if not. +static DWDie* +find(DWDie *die, char* name) +{ + DWDie *a, *b; + int h; + + if (die->hash == nil) { + for (a = die->child; a != nil; a = a->link) + if (strcmp(name, getattr(a, DW_AT_name)->data) == 0) + return a; + return nil; + } + + h = hashstr(name); + a = die->hash[h]; + + if (a == nil) + return nil; + + + if (strcmp(name, getattr(a, DW_AT_name)->data) == 0) + return a; + + // Move found ones to head of the list. + b = a->hlink; + while (b != nil) { + if (strcmp(name, getattr(b, DW_AT_name)->data) == 0) { + a->hlink = b->hlink; + b->hlink = die->hash[h]; + die->hash[h] = b; + return b; + } + a = b; + b = b->hlink; + } + return nil; +} + +static DWDie* +find_or_diag(DWDie *die, char* name) +{ + DWDie *r; + r = find(die, name); + if (r == nil) + diag("dwarf find: %s has no %s", getattr(die, DW_AT_name)->data, name); + return r; +} + +static DWAttr* +newrefattr(DWDie *die, uint8 attr, DWDie* ref) +{ + if (ref == nil) + return nil; + return newattr(die, attr, DW_CLS_REFERENCE, 0, (char*)ref); +} + +static int fwdcount; + +static void +putattr(int form, int cls, vlong value, char *data) +{ + switch(form) { + case DW_FORM_addr: // address + addrput(value); + break; + + case DW_FORM_block1: // block + value &= 0xff; + cput(value); + while(value--) + cput(*data++); + break; + + case DW_FORM_block2: // block + value &= 0xffff; + WPUT(value); + while(value--) + cput(*data++); + break; + + case DW_FORM_block4: // block + value &= 0xffffffff; + LPUT(value); + while(value--) + cput(*data++); + break; + + case DW_FORM_block: // block + uleb128put(value); + while(value--) + cput(*data++); + break; + + case DW_FORM_data1: // constant + cput(value); + break; + + case DW_FORM_data2: // constant + WPUT(value); + break; + + case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr + LPUT(value); + break; + + case DW_FORM_data8: // constant, {line,loclist,mac,rangelist}ptr + VPUT(value); + break; + + case DW_FORM_sdata: // constant + sleb128put(value); + break; + + case DW_FORM_udata: // constant + uleb128put(value); + break; + + case DW_FORM_string: // string + strnput(data, value+1); + break; + + case DW_FORM_flag: // flag + cput(value?1:0); + break; + + case DW_FORM_ref_addr: // reference to a DIE in the .info section + if (data == nil) { + diag("null dwarf reference"); + LPUT(0); // invalid dwarf, gdb will complain. + } else { + if (((DWDie*)data)->offs == 0) + fwdcount++; + LPUT(((DWDie*)data)->offs); + } + break; + + case DW_FORM_ref1: // reference within the compilation unit + case DW_FORM_ref2: // reference + case DW_FORM_ref4: // reference + case DW_FORM_ref8: // reference + case DW_FORM_ref_udata: // reference + + case DW_FORM_strp: // string + case DW_FORM_indirect: // (see Section 7.5.3) + default: + diag("Unsupported atribute form %d / class %d", form, cls); + errorexit(); + } +} + +// Note that we can (and do) add arbitrary attributes to a DIE, but +// only the ones actually listed in the Abbrev will be written out. +static void +putattrs(int abbrev, DWAttr* attr) +{ + DWAttr *attrs[DW_AT_recursive + 1]; + DWAttrForm* af; + + memset(attrs, 0, sizeof attrs); + for( ; attr; attr = attr->link) + if (attr->atr < nelem(attrs)) + attrs[attr->atr] = attr; + + for(af = abbrevs[abbrev].attr; af->attr; af++) + if (attrs[af->attr]) + putattr(af->form, + attrs[af->attr]->cls, + attrs[af->attr]->value, + attrs[af->attr]->data); + else + putattr(af->form, 0, 0, 0); +} + +static void putdie(DWDie* die); + +static void +putdies(DWDie* die) +{ + for(; die; die = die->link) + putdie(die); +} + +static void +putdie(DWDie* die) +{ + die->offs = cpos() - infoo; + uleb128put(die->abbrev); + putattrs(die->abbrev, die->attr); + if (abbrevs[die->abbrev].children) { + putdies(die->child); + cput(0); + } +} + +static void +reverselist(DWDie** list) +{ + DWDie *curr, *prev; + + curr = *list; + prev = nil; + while(curr != nil) { + DWDie* next = curr->link; + curr->link = prev; + prev = curr; + curr = next; + } + *list = prev; +} + +static void +reversetree(DWDie** list) +{ + DWDie *die; + + reverselist(list); + for (die = *list; die != nil; die = die->link) + if (abbrevs[die->abbrev].children) + reversetree(&die->child); +} + +static void +newmemberoffsetattr(DWDie *die, int32 offs) +{ + char block[10]; + int i; + + i = 0; + if (offs != 0) { + block[i++] = DW_OP_consts; + i += sleb128enc(offs, block+i); + block[i++] = DW_OP_plus; + } + newattr(die, DW_AT_data_member_location, DW_CLS_BLOCK, i, mal(i)); + memmove(die->attr->data, block, i); +} + +// GDB doesn't like DW_FORM_addr for DW_AT_location, so emit a +// location expression that evals to a const. +static void +newabslocexprattr(DWDie *die, vlong addr) +{ + char block[10]; + int i; + + i = 0; + block[i++] = DW_OP_constu; + i += uleb128enc(addr, block+i); + newattr(die, DW_AT_location, DW_CLS_BLOCK, i, mal(i)); + memmove(die->attr->data, block, i); +} + +// Decoding the type.* symbols. This has to be in sync with +// ../../pkg/runtime/type.go, or more specificaly, with what +// ../gc/reflect.c stuffs in these. + +enum { + KindBool = 1, + KindInt, + KindInt8, + KindInt16, + KindInt32, + KindInt64, + KindUint, + KindUint8, + KindUint16, + KindUint32, + KindUint64, + KindUintptr, + KindFloat, + KindFloat32, + KindFloat64, + KindComplex, + KindComplex64, + KindComplex128, + KindArray, + KindChan, + KindFunc, + KindInterface, + KindMap, + KindPtr, + KindSlice, + KindString, + KindStruct, + KindUnsafePointer, + + KindNoPointers = 1<<7, +}; + +static Reloc* +decode_reloc(Sym *s, int32 off) +{ + int i; + + for (i = 0; i < s->nr; i++) + if (s->r[i].off == off) + return s->r + i; + return nil; +} + +static uvlong +decode_inuxi(uchar* p, int sz) +{ + uint64 v; + uint32 l; + uchar *cast, *inuxi; + int i; + + v = l = 0; + cast = nil; + inuxi = nil; + switch (sz) { + case 2: + cast = (uchar*)&l; + inuxi = inuxi2; + break; + case 4: + cast = (uchar*)&l; + inuxi = inuxi4; + break; + case 8: + cast = (uchar*)&v; + inuxi = inuxi8; + break; + default: + diag("decode inuxi %d", sz); + errorexit(); + } + for (i = 0; i < sz; i++) + cast[inuxi[i]] = p[i]; + if (sz == 8) + return v; + return l; +} + +// Type.commonType.kind +static uint8 +decodetype_kind(Sym *s) +{ + return s->p[3*PtrSize + 7] & ~KindNoPointers; // 0x13 / 0x1f +} + +// Type.commonType.size +static vlong +decodetype_size(Sym *s) +{ + return decode_inuxi(s->p + 2*PtrSize, PtrSize); // 0x8 / 0x10 +} + +// Type.ArrayType.elem and Type.SliceType.Elem +static Sym* +decodetype_arrayelem(Sym *s) +{ + return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 +} + +static vlong +decodetype_arraylen(Sym *s) +{ + return decode_inuxi(s->p + 6*PtrSize + 8, PtrSize); +} + +// Type.PtrType.elem +static Sym* +decodetype_ptrelem(Sym *s) +{ + return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 +} + +// Type.MapType.key, elem +static Sym* +decodetype_mapkey(Sym *s) +{ + return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 +} +static Sym* +decodetype_mapvalue(Sym *s) +{ + return decode_reloc(s, 6*PtrSize + 8)->sym; // 0x20 / 0x38 +} + +// Type.ChanType.elem +static Sym* +decodetype_chanelem(Sym *s) +{ + return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 +} + +// Type.FuncType.dotdotdot +static int +decodetype_funcdotdotdot(Sym *s) +{ + return s->p[5*PtrSize + 8]; +} + +// Type.FuncType.in.len +static int +decodetype_funcincount(Sym *s) +{ + return decode_inuxi(s->p + 7*PtrSize + 8, 4); +} + +static int +decodetype_funcoutcount(Sym *s) +{ + return decode_inuxi(s->p + 8*PtrSize + 16, 4); +} + +static Sym* +decodetype_funcintype(Sym *s, int i) +{ + Reloc *r; + + r = decode_reloc(s, 6*PtrSize + 8); + return decode_reloc(r->sym, r->add + i * PtrSize)->sym; +} + +static Sym* +decodetype_funcouttype(Sym *s, int i) +{ + Reloc *r; + + r = decode_reloc(s, 7*PtrSize + 16); + return decode_reloc(r->sym, r->add + i * PtrSize)->sym; +} + +// Type.StructType.fields.Slice::len +static int +decodetype_structfieldcount(Sym *s) +{ + return decode_inuxi(s->p + 6*PtrSize + 8, 4); // 0x20 / 0x38 +} + +// Type.StructType.fields[]-> name, typ and offset. sizeof(structField) = 5*PtrSize +static char* +decodetype_structfieldname(Sym *s, int i) +{ + Reloc* r; + + r = decode_reloc(s, 6*PtrSize + 0x10 + i*5*PtrSize); // go.string."foo" 0x28 / 0x40 + if (r == nil) // embedded structs have a nil name. + return nil; + r = decode_reloc(r->sym, 0); // string."foo" + if (r == nil) // shouldn't happen. + return nil; + return (char*)r->sym->p; // the c-string +} + +static Sym* +decodetype_structfieldtype(Sym *s, int i) +{ + return decode_reloc(s, 8*PtrSize + 0x10 + i*5*PtrSize)->sym; // 0x30 / 0x50 +} + +static vlong +decodetype_structfieldoffs(Sym *s, int i) +{ + return decode_inuxi(s->p + 10*PtrSize + 0x10 + i*5*PtrSize, 4); // 0x38 / 0x60 +} + +// InterfaceTYpe.methods.len +static vlong +decodetype_ifacemethodcount(Sym *s) +{ + return decode_inuxi(s->p + 6*PtrSize + 8, 4); +} + + +// Fake attributes for slices, maps and channel +enum { + DW_AT_internal_elem_type = 250, // channels and slices + DW_AT_internal_key_type = 251, // maps + DW_AT_internal_val_type = 252, // maps + DW_AT_internal_location = 253, // params and locals +}; + +static DWDie* defptrto(DWDie *dwtype); // below + +// Define gotype, for composite ones recurse into constituents. +static DWDie* +defgotype(Sym *gotype) +{ + DWDie *die, *fld; + Sym *s; + char *name, *f; + uint8 kind; + vlong bytesize; + int i, nfields; + + if (gotype == nil) + return find_or_diag(&dwtypes, "<unspecified>"); + + if (strncmp("type.", gotype->name, 5) != 0) { + diag("Type name doesn't start with \".type\": %s", gotype->name); + return find_or_diag(&dwtypes, "<unspecified>"); + } + name = gotype->name + 5; // Altenatively decode from Type.string + + die = find(&dwtypes, name); + if (die != nil) + return die; + + if (0 && debug['v'] > 2) { + print("new type: %s @0x%08x [%d]", gotype->name, gotype->value, gotype->size); + for (i = 0; i < gotype->size; i++) { + if (!(i%8)) print("\n\t%04x ", i); + print("%02x ", gotype->p[i]); + } + print("\n"); + for (i = 0; i < gotype->nr; i++) { + print("\t0x%02x[%x] %d %s[%llx]\n", + gotype->r[i].off, + gotype->r[i].siz, + gotype->r[i].type, + gotype->r[i].sym->name, + (vlong)gotype->r[i].add); + } + } + + kind = decodetype_kind(gotype); + bytesize = decodetype_size(gotype); + + switch (kind) { + case KindBool: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_boolean, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindInt: + case KindInt8: + case KindInt16: + case KindInt32: + case KindInt64: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_signed, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindUint: + case KindUint8: + case KindUint16: + case KindUint32: + case KindUint64: + case KindUintptr: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindFloat: + case KindFloat32: + case KindFloat64: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_float, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindComplex: + case KindComplex64: + case KindComplex128: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_complex_float, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindArray: + die = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + s = decodetype_arrayelem(gotype); + newrefattr(die, DW_AT_type, defgotype(s)); + fld = newdie(die, DW_ABRV_ARRAYRANGE, "range"); + newattr(fld, DW_AT_upper_bound, DW_CLS_CONSTANT, decodetype_arraylen(gotype), 0); + newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr")); + break; + + case KindChan: + die = newdie(&dwtypes, DW_ABRV_CHANTYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + s = decodetype_chanelem(gotype); + newrefattr(die, DW_AT_internal_elem_type, defgotype(s)); + break; + + case KindFunc: + die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name); + newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "void")); + nfields = decodetype_funcincount(gotype); + for (i = 0; i < nfields; i++) { + s = decodetype_funcintype(gotype, i); + fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s->name+5); + newrefattr(fld, DW_AT_type, defgotype(s)); + } + if (decodetype_funcdotdotdot(gotype)) + newdie(die, DW_ABRV_DOTDOTDOT, "..."); + nfields = decodetype_funcoutcount(gotype); + for (i = 0; i < nfields; i++) { + s = decodetype_funcouttype(gotype, i); + fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s->name+5); + newrefattr(fld, DW_AT_type, defptrto(defgotype(s))); + } + die = defptrto(die); + break; + + case KindInterface: + die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + nfields = decodetype_ifacemethodcount(gotype); + if (nfields == 0) + s = lookup("type.runtime.eface", 0); + else + s = lookup("type.runtime.iface", 0); + newrefattr(die, DW_AT_type, defgotype(s)); + break; + + case KindMap: + die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name); + s = decodetype_mapkey(gotype); + newrefattr(die, DW_AT_internal_key_type, defgotype(s)); + s = decodetype_mapvalue(gotype); + newrefattr(die, DW_AT_internal_val_type, defgotype(s)); + break; + + case KindPtr: + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name); + s = decodetype_ptrelem(gotype); + newrefattr(die, DW_AT_type, defgotype(s)); + break; + + case KindSlice: + die = newdie(&dwtypes, DW_ABRV_SLICETYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + s = decodetype_arrayelem(gotype); + newrefattr(die, DW_AT_internal_elem_type, defgotype(s)); + break; + + case KindString: + die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + break; + + case KindStruct: + die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + nfields = decodetype_structfieldcount(gotype); + for (i = 0; i < nfields; i++) { + f = decodetype_structfieldname(gotype, i); + s = decodetype_structfieldtype(gotype, i); + if (f == nil) + f = s->name + 5; // skip "type." + fld = newdie(die, DW_ABRV_STRUCTFIELD, f); + newrefattr(fld, DW_AT_type, defgotype(s)); + newmemberoffsetattr(fld, decodetype_structfieldoffs(gotype, i)); + } + break; + + case KindUnsafePointer: + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name); + newrefattr(die, DW_AT_type, find(&dwtypes, "void")); + break; + + default: + diag("definition of unknown kind %d: %s", kind, gotype->name); + die = newdie(&dwtypes, DW_ABRV_TYPEDECL, name); + newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "<unspecified>")); + } + + return die; +} + +// Find or construct *T given T. +static DWDie* +defptrto(DWDie *dwtype) +{ + char ptrname[1024]; + DWDie *die; + + snprint(ptrname, sizeof ptrname, "*%s", getattr(dwtype, DW_AT_name)->data); + die = find(&dwtypes, ptrname); + if (die == nil) { + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, + strcpy(mal(strlen(ptrname)+1), ptrname)); + newrefattr(die, DW_AT_type, dwtype); + } + return die; +} + +// Copies src's children into dst. Copies attributes by value. +// DWAttr.data is copied as pointer only. +static void +copychildren(DWDie *dst, DWDie *src) +{ + DWDie *c; + DWAttr *a; + + for (src = src->child; src != nil; src = src->link) { + c = newdie(dst, src->abbrev, getattr(src, DW_AT_name)->data); + for (a = src->attr; a != nil; a = a->link) + newattr(c, a->atr, a->cls, a->value, a->data); + copychildren(c, src); + } + reverselist(&dst->child); +} + +// Search children (assumed to have DW_TAG_member) for the one named +// field and set it's DW_AT_type to dwtype +static void +substitutetype(DWDie *structdie, char *field, DWDie* dwtype) +{ + DWDie *child; + DWAttr *a; + + child = find_or_diag(structdie, field); + if (child == nil) + return; + + a = getattr(child, DW_AT_type); + if (a != nil) + a->data = (char*) dwtype; + else + newrefattr(child, DW_AT_type, dwtype); +} + +static void +synthesizestringtypes(DWDie* die) +{ + DWDie *prototype; + + prototype = defgotype(lookup("type.runtime.string_", 0)); + if (prototype == nil) + return; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_STRINGTYPE) + continue; + copychildren(die, prototype); + } +} + +static void +synthesizeslicetypes(DWDie *die) +{ + DWDie *prototype, *elem; + + prototype = defgotype(lookup("type.runtime.slice",0)); + if (prototype == nil) + return; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_SLICETYPE) + continue; + copychildren(die, prototype); + elem = (DWDie*) getattr(die, DW_AT_internal_elem_type)->data; + substitutetype(die, "array", defptrto(elem)); + } +} + +static char* +mkinternaltypename(char *base, char *arg1, char *arg2) +{ + char buf[1024]; + char *n; + + if (arg2 == nil) + snprint(buf, sizeof buf, "%s<%s>", base, arg1); + else + snprint(buf, sizeof buf, "%s<%s,%s>", base, arg1, arg2); + n = mal(strlen(buf) + 1); + memmove(n, buf, strlen(buf)); + return n; +} + + +// synthesizemaptypes is way too closely married to runtime/hashmap.c +enum { + MaxValsize = 256 - 64 +}; + +static void +synthesizemaptypes(DWDie *die) +{ + + DWDie *hash, *hash_subtable, *hash_entry, + *dwh, *dwhs, *dwhe, *keytype, *valtype, *fld; + int hashsize, keysize, valsize, datsize, valsize_in_hash, datavo; + DWAttr *a; + + hash = defgotype(lookup("type.runtime.hash",0)); + hash_subtable = defgotype(lookup("type.runtime.hash_subtable",0)); + hash_entry = defgotype(lookup("type.runtime.hash_entry",0)); + + if (hash == nil || hash_subtable == nil || hash_entry == nil) + return; + + dwh = (DWDie*)getattr(find_or_diag(hash_entry, "hash"), DW_AT_type)->data; + if (dwh == nil) + return; + + hashsize = getattr(dwh, DW_AT_byte_size)->value; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_MAPTYPE) + continue; + + keytype = (DWDie*) getattr(die, DW_AT_internal_key_type)->data; + valtype = (DWDie*) getattr(die, DW_AT_internal_val_type)->data; + + a = getattr(keytype, DW_AT_byte_size); + keysize = a ? a->value : PtrSize; // We don't store size with Pointers + + a = getattr(valtype, DW_AT_byte_size); + valsize = a ? a->value : PtrSize; + + // This is what happens in hash_init and makemap_c + valsize_in_hash = valsize; + if (valsize > MaxValsize) + valsize_in_hash = PtrSize; + datavo = keysize; + if (valsize_in_hash >= PtrSize) + datavo = rnd(keysize, PtrSize); + datsize = datavo + valsize_in_hash; + if (datsize < PtrSize) + datsize = PtrSize; + datsize = rnd(datsize, PtrSize); + + // Construct struct hash_entry<K,V> + dwhe = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hash_entry", + getattr(keytype, DW_AT_name)->data, + getattr(valtype, DW_AT_name)->data)); + copychildren(dwhe, hash_entry); + substitutetype(dwhe, "key", keytype); + if (valsize > MaxValsize) + valtype = defptrto(valtype); + substitutetype(dwhe, "val", valtype); + fld = find_or_diag(dwhe, "val"); + delattr(fld, DW_AT_data_member_location); + newmemberoffsetattr(fld, hashsize + datavo); + newattr(dwhe, DW_AT_byte_size, DW_CLS_CONSTANT, hashsize + datsize, NULL); + + // Construct hash_subtable<hash_entry<K,V>> + dwhs = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hash_subtable", + getattr(keytype, DW_AT_name)->data, + getattr(valtype, DW_AT_name)->data)); + copychildren(dwhs, hash_subtable); + substitutetype(dwhs, "end", defptrto(dwhe)); + substitutetype(dwhs, "entry", dwhe); // todo: []hash_entry with dynamic size + newattr(dwhs, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(hash_subtable, DW_AT_byte_size)->value, NULL); + + // Construct hash<K,V> + dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hash", + getattr(keytype, DW_AT_name)->data, + getattr(valtype, DW_AT_name)->data)); + copychildren(dwh, hash); + substitutetype(dwh, "st", defptrto(dwhs)); + newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(hash, DW_AT_byte_size)->value, NULL); + + newrefattr(die, DW_AT_type, defptrto(dwh)); + } +} + +static void +synthesizechantypes(DWDie *die) +{ + DWDie *sudog, *waitq, *link, *hchan, + *dws, *dww, *dwl, *dwh, *elemtype; + DWAttr *a; + int elemsize, linksize, sudogsize; + + sudog = defgotype(lookup("type.runtime.sudoG",0)); + waitq = defgotype(lookup("type.runtime.waitQ",0)); + link = defgotype(lookup("type.runtime.link",0)); + hchan = defgotype(lookup("type.runtime.hChan",0)); + if (sudog == nil || waitq == nil || link == nil || hchan == nil) + return; + + sudogsize = getattr(sudog, DW_AT_byte_size)->value; + linksize = getattr(link, DW_AT_byte_size)->value; + + for (; die != nil; die = die->link) { + if (die->abbrev != DW_ABRV_CHANTYPE) + continue; + elemtype = (DWDie*) getattr(die, DW_AT_internal_elem_type)->data; + a = getattr(elemtype, DW_AT_byte_size); + elemsize = a ? a->value : PtrSize; + + // sudog<T> + dws = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("sudog", + getattr(elemtype, DW_AT_name)->data, NULL)); + copychildren(dws, sudog); + substitutetype(dws, "elem", elemtype); + newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT, + sudogsize + (elemsize > 8 ? elemsize - 8 : 0), NULL); + + // waitq<T> + dww = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("waitq", getattr(elemtype, DW_AT_name)->data, NULL)); + copychildren(dww, waitq); + substitutetype(dww, "first", defptrto(dws)); + substitutetype(dww, "last", defptrto(dws)); + newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(waitq, DW_AT_byte_size)->value, NULL); + + // link<T> + dwl = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("link", getattr(elemtype, DW_AT_name)->data, NULL)); + copychildren(dwl, link); + substitutetype(dwl, "link", defptrto(dwl)); + substitutetype(dwl, "elem", elemtype); + newattr(dwl, DW_AT_byte_size, DW_CLS_CONSTANT, + linksize + (elemsize > 8 ? elemsize - 8 : 0), NULL); + + // hchan<T> + dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, + mkinternaltypename("hchan", getattr(elemtype, DW_AT_name)->data, NULL)); + copychildren(dwh, hchan); + substitutetype(dwh, "senddataq", defptrto(dwl)); + substitutetype(dwh, "recvdataq", defptrto(dwl)); + substitutetype(dwh, "recvq", dww); + substitutetype(dwh, "sendq", dww); + substitutetype(dwh, "free", dws); + newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, + getattr(hchan, DW_AT_byte_size)->value, NULL); + + newrefattr(die, DW_AT_type, defptrto(dwh)); + } +} + +// For use with pass.c::genasmsym +static void +defdwsymb(Sym* sym, char *s, int t, vlong v, vlong size, int ver, Sym *gotype) +{ + DWDie *dv, *dt; + + if (strncmp(s, "go.string.", 10) == 0) + return; + if (strncmp(s, "string.", 7) == 0) + return; + if (strncmp(s, "type._.", 7) == 0) + return; + + if (strncmp(s, "type.", 5) == 0) { + defgotype(sym); + return; + } + + dv = nil; + + switch (t) { + default: + return; + case 'd': + case 'b': + case 'D': + case 'B': + dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s); + newabslocexprattr(dv, v); + if (ver == 0) + newattr(dv, DW_AT_external, DW_CLS_FLAG, 1, 0); + // fallthrough + case 'a': + case 'p': + dt = defgotype(gotype); + } + + if (dv != nil) + newrefattr(dv, DW_AT_type, dt); +} + +// TODO(lvd) For now, just append them all to the first compilation +// unit (that should be main), in the future distribute them to the +// appropriate compilation units. +static void +movetomodule(DWDie *parent) +{ + DWDie *die; + + for (die = dwroot.child->child; die->link != nil; die = die->link) /* nix */; + die->link = parent->child; +} + +/* + * Filename fragments for the line history stack. + */ + +static char **ftab; +static int ftabsize; + +void +dwarfaddfrag(int n, char *frag) +{ + int s; + + if (n >= ftabsize) { + s = ftabsize; + ftabsize = 1 + n + (n >> 2); + ftab = realloc(ftab, ftabsize * sizeof(ftab[0])); + memset(ftab + s, 0, (ftabsize - s) * sizeof(ftab[0])); + } + + if (*frag == '<') + frag++; + ftab[n] = frag; +} + +// Returns a malloc'ed string, piecewise copied from the ftab. +static char * +decodez(char *s) +{ + int len, o; + char *ss, *f; + char *r, *rb, *re; + + len = 0; + ss = s + 1; // first is 0 + while((o = ((uint8)ss[0] << 8) | (uint8)ss[1]) != 0) { + if (o < 0 || o >= ftabsize) { + diag("corrupt z entry"); + return 0; + } + f = ftab[o]; + if (f == nil) { + diag("corrupt z entry"); + return 0; + } + len += strlen(f) + 1; // for the '/' + ss += 2; + } + + if (len == 0) + return 0; + + r = malloc(len + 1); + rb = r; + re = rb + len + 1; + + s++; + while((o = ((uint8)s[0] << 8) | (uint8)s[1]) != 0) { + f = ftab[o]; + if (rb == r || rb[-1] == '/') + rb = seprint(rb, re, "%s", f); + else + rb = seprint(rb, re, "/%s", f); + s += 2; + } + return r; +} + +/* + * The line history itself + */ + +static char **histfile; // [0] holds "<eof>", DW_LNS_set_file arguments must be > 0. +static int histfilesize; +static int histfilecap; + +static void +clearhistfile(void) +{ + int i; + + // [0] holds "<eof>" + for (i = 1; i < histfilesize; i++) + free(histfile[i]); + histfilesize = 0; +} + +static int +addhistfile(char *zentry) +{ + char *fname; + + if (histfilesize == histfilecap) { + histfilecap = 2 * histfilecap + 2; + histfile = realloc(histfile, histfilecap * sizeof(char*)); + } + if (histfilesize == 0) + histfile[histfilesize++] = "<eof>"; + + fname = decodez(zentry); + if (fname == 0) + return -1; + // Don't fill with duplicates (check only top one). + if (strcmp(fname, histfile[histfilesize-1]) == 0) { + free(fname); + return histfilesize - 1; + } + histfile[histfilesize++] = fname; + return histfilesize - 1; +} + +// if the histfile stack contains ..../runtime/runtime_defs.go +// use that to set gdbscript +static void +finddebugruntimepath() +{ + int i, l; + char *c; + + for (i = 1; i < histfilesize; i++) { + if ((c = strstr(histfile[i], "runtime/runtime_defs.go")) != nil) { + l = c - histfile[i]; + memmove(gdbscript, histfile[i], l); + memmove(gdbscript + l, "runtime/runtime-gdb.py", strlen("runtime/runtime-gdb.py") + 1); + break; + } + } +} + +// Go's runtime C sources are sane, and Go sources nest only 1 level, +// so 16 should be plenty. +static struct { + int file; + vlong line; +} includestack[16]; +static int includetop; +static vlong absline; + +typedef struct Linehist Linehist; +struct Linehist { + Linehist *link; + vlong absline; + vlong line; + int file; +}; + +static Linehist *linehist; + +static void +checknesting(void) +{ + int i; + + if (includetop < 0) { + diag("corrupt z stack"); + errorexit(); + } + if (includetop >= nelem(includestack)) { + diag("nesting too deep"); + for (i = 0; i < nelem(includestack); i++) + diag("\t%s", histfile[includestack[i].file]); + errorexit(); + } +} + +/* + * Return false if the a->link chain contains no history, otherwise + * returns true and finds z and Z entries in the Auto list (of a + * Prog), and resets the history stack + */ +static int +inithist(Auto *a) +{ + Linehist *lh; + + for (; a; a = a->link) + if (a->type == D_FILE) + break; + if (a==nil) + return 0; + + // We have a new history. They are guaranteed to come completely + // at the beginning of the compilation unit. + if (a->aoffset != 1) { + diag("stray 'z' with offset %d", a->aoffset); + return 0; + } + + // Clear the history. + clearhistfile(); + includetop = 0; + includestack[includetop].file = 0; + includestack[includetop].line = -1; + absline = 0; + while (linehist != nil) { + lh = linehist->link; + free(linehist); + linehist = lh; + } + + // Construct the new one. + for (; a; a = a->link) { + if (a->type == D_FILE) { // 'z' + int f = addhistfile(a->asym->name); + if (f < 0) { // pop file + includetop--; + checknesting(); + } else if(f != includestack[includetop].file) { // pushed a new file + includestack[includetop].line += a->aoffset - absline; + includetop++; + checknesting(); + includestack[includetop].file = f; + includestack[includetop].line = 1; + } + absline = a->aoffset; + } else if (a->type == D_FILE1) { // 'Z' + // We could just fixup the current + // linehist->line, but there doesn't appear to + // be a guarantee that every 'Z' is preceded + // by it's own 'z', so do the safe thing and + // update the stack and push a new Linehist + // entry + includestack[includetop].line = a->aoffset; + } else + continue; + if (linehist == 0 || linehist->absline != absline) { + Linehist* lh = malloc(sizeof *lh); + lh->link = linehist; + lh->absline = absline; + linehist = lh; + } + linehist->file = includestack[includetop].file; + linehist->line = includestack[includetop].line; + } + return 1; +} + +static Linehist * +searchhist(vlong absline) +{ + Linehist *lh; + + for (lh = linehist; lh; lh = lh->link) + if (lh->absline <= absline) + break; + return lh; +} + +static int +guesslang(char *s) +{ + if(strlen(s) >= 3 && strcmp(s+strlen(s)-3, ".go") == 0) + return DW_LANG_Go; + + return DW_LANG_C; +} + +/* + * Generate short opcodes when possible, long ones when neccesary. + * See section 6.2.5 + */ + +enum { + LINE_BASE = -1, + LINE_RANGE = 4, + OPCODE_BASE = 5 +}; + +static void +putpclcdelta(vlong delta_pc, vlong delta_lc) +{ + if (LINE_BASE <= delta_lc && delta_lc < LINE_BASE+LINE_RANGE) { + vlong opcode = OPCODE_BASE + (delta_lc - LINE_BASE) + (LINE_RANGE * delta_pc); + if (OPCODE_BASE <= opcode && opcode < 256) { + cput(opcode); + return; + } + } + + if (delta_pc) { + cput(DW_LNS_advance_pc); + sleb128put(delta_pc); + } + + cput(DW_LNS_advance_line); + sleb128put(delta_lc); + cput(DW_LNS_copy); +} + +static void +newcfaoffsetattr(DWDie *die, int32 offs) +{ + char block[10]; + int i; + + i = 0; + + block[i++] = DW_OP_call_frame_cfa; + if (offs != 0) { + block[i++] = DW_OP_consts; + i += sleb128enc(offs, block+i); + block[i++] = DW_OP_plus; + } + newattr(die, DW_AT_location, DW_CLS_BLOCK, i, mal(i)); + memmove(die->attr->data, block, i); +} + +static char* +mkvarname(char* name, int da) +{ + char buf[1024]; + char *n; + + snprint(buf, sizeof buf, "%s#%d", name, da); + n = mal(strlen(buf) + 1); + memmove(n, buf, strlen(buf)); + return n; +} + +/* + * Walk prog table, emit line program and build DIE tree. + */ + +// flush previous compilation unit. +static void +flushunit(DWDie *dwinfo, vlong pc, vlong unitstart) +{ + vlong here; + + if (dwinfo != nil && pc != 0) { + newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc+1, 0); + } + + if (unitstart >= 0) { + cput(0); // start extended opcode + uleb128put(1); + cput(DW_LNE_end_sequence); + cflush(); + + here = cpos(); + seek(cout, unitstart, 0); + LPUT(here - unitstart - sizeof(int32)); + cflush(); + seek(cout, here, 0); + } +} + +static void +writelines(void) +{ + Prog *q; + Sym *s; + Auto *a; + vlong unitstart, offs; + vlong pc, epc, lc, llc, lline; + int currfile; + int i, lang, da, dt; + Linehist *lh; + DWDie *dwinfo, *dwfunc, *dwvar, **dws; + DWDie *varhash[HASHSIZE]; + char *n, *nn; + + unitstart = -1; + epc = pc = 0; + lc = 1; + llc = 1; + currfile = -1; + lineo = cpos(); + dwinfo = nil; + + for(cursym = textp; cursym != nil; cursym = cursym->next) { + s = cursym; + if(s->text == P) + continue; + + // Look for history stack. If we find one, + // we're entering a new compilation unit + + if (inithist(s->autom)) { + flushunit(dwinfo, epc, unitstart); + unitstart = cpos(); + + if(debug['v'] > 1) { + print("dwarf writelines found %s\n", histfile[1]); + Linehist* lh; + for (lh = linehist; lh; lh = lh->link) + print("\t%8lld: [%4lld]%s\n", + lh->absline, lh->line, histfile[lh->file]); + } + + lang = guesslang(histfile[1]); + finddebugruntimepath(); + + dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, strdup(histfile[1])); + newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT,lang, 0); + newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, unitstart - lineo, 0); + newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s->text->pc, 0); + + // Write .debug_line Line Number Program Header (sec 6.2.4) + // Fields marked with (*) must be changed for 64-bit dwarf + LPUT(0); // unit_length (*), will be filled in later. + WPUT(3); // dwarf version (appendix F) + LPUT(11); // header_length (*), starting here. + + cput(1); // minimum_instruction_length + cput(1); // default_is_stmt + cput(LINE_BASE); // line_base + cput(LINE_RANGE); // line_range + cput(OPCODE_BASE); // opcode_base (we only use 1..4) + cput(0); // standard_opcode_lengths[1] + cput(1); // standard_opcode_lengths[2] + cput(1); // standard_opcode_lengths[3] + cput(1); // standard_opcode_lengths[4] + cput(0); // include_directories (empty) + cput(0); // file_names (empty) (emitted by DW_LNE's below) + // header_length ends here. + + for (i=1; i < histfilesize; i++) { + cput(0); // start extended opcode + uleb128put(1 + strlen(histfile[i]) + 4); + cput(DW_LNE_define_file); + strnput(histfile[i], strlen(histfile[i]) + 4); + // 4 zeros: the string termination + 3 fields. + } + + epc = pc = s->text->pc; + currfile = 1; + lc = 1; + llc = 1; + + cput(0); // start extended opcode + uleb128put(1 + PtrSize); + cput(DW_LNE_set_address); + addrput(pc); + } + if(s->text == nil) + continue; + + if (unitstart < 0) { + diag("reachable code before seeing any history: %P", s->text); + continue; + } + + dwfunc = newdie(dwinfo, DW_ABRV_FUNCTION, s->name); + newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s->value, 0); + epc = s->value + s->size; + newattr(dwfunc, DW_AT_high_pc, DW_CLS_ADDRESS, epc, 0); + if (s->version == 0) + newattr(dwfunc, DW_AT_external, DW_CLS_FLAG, 1, 0); + + if(s->text->link == nil) + continue; + + for(q = s->text; q != P; q = q->link) { + lh = searchhist(q->line); + if (lh == nil) { + diag("corrupt history or bad absolute line: %P", q); + continue; + } + + if (lh->file < 1) { // 0 is the past-EOF entry. + // diag("instruction with line number past EOF in %s: %P", histfile[1], q); + continue; + } + + lline = lh->line + q->line - lh->absline; + if (debug['v'] > 1) + print("%6llux %s[%lld] %P\n", (vlong)q->pc, histfile[lh->file], lline, q); + + if (q->line == lc) + continue; + if (currfile != lh->file) { + currfile = lh->file; + cput(DW_LNS_set_file); + uleb128put(currfile); + } + putpclcdelta(q->pc - pc, lline - llc); + pc = q->pc; + lc = q->line; + llc = lline; + } + + da = 0; + dwfunc->hash = varhash; // enable indexing of children by name + memset(varhash, 0, sizeof varhash); + for(a = s->autom; a; a = a->link) { + switch (a->type) { + case D_AUTO: + dt = DW_ABRV_AUTO; + offs = a->aoffset - PtrSize; + break; + case D_PARAM: + dt = DW_ABRV_PARAM; + offs = a->aoffset; + break; + default: + continue; + } + if (strstr(a->asym->name, ".autotmp_")) + continue; + if (find(dwfunc, a->asym->name) != nil) + n = mkvarname(a->asym->name, da); + else + n = a->asym->name; + // Drop the package prefix from locals and arguments. + nn = strrchr(n, '.'); + if (nn) + n = nn + 1; + + dwvar = newdie(dwfunc, dt, n); + newcfaoffsetattr(dwvar, offs); + newrefattr(dwvar, DW_AT_type, defgotype(a->gotype)); + + // push dwvar down dwfunc->child to preserve order + newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, offs, NULL); + dwfunc->child = dwvar->link; // take dwvar out from the top of the list + for (dws = &dwfunc->child; *dws != nil; dws = &(*dws)->link) + if (offs > getattr(*dws, DW_AT_internal_location)->value) + break; + dwvar->link = *dws; + *dws = dwvar; + + da++; + } + + dwfunc->hash = nil; + } + + flushunit(dwinfo, epc, unitstart); + linesize = cpos() - lineo; +} + +/* + * Emit .debug_frame + */ +enum +{ + CIERESERVE = 16, + DATAALIGNMENTFACTOR = -4, // TODO -PtrSize? + FAKERETURNCOLUMN = 16 // TODO gdb6 doesnt like > 15? +}; + +static void +putpccfadelta(vlong deltapc, vlong cfa) +{ + if (deltapc < 0x40) { + cput(DW_CFA_advance_loc + deltapc); + } else if (deltapc < 0x100) { + cput(DW_CFA_advance_loc1); + cput(deltapc); + } else if (deltapc < 0x10000) { + cput(DW_CFA_advance_loc2); + WPUT(deltapc); + } else { + cput(DW_CFA_advance_loc4); + LPUT(deltapc); + } + + cput(DW_CFA_def_cfa_offset_sf); + sleb128put(cfa / DATAALIGNMENTFACTOR); +} + +static void +writeframes(void) +{ + Prog *p, *q; + Sym *s; + vlong fdeo, fdesize, pad, cfa, pc; + + frameo = cpos(); + + // Emit the CIE, Section 6.4.1 + LPUT(CIERESERVE); // initial length, must be multiple of PtrSize + LPUT(0xffffffff); // cid. + cput(3); // dwarf version (appendix F) + cput(0); // augmentation "" + uleb128put(1); // code_alignment_factor + sleb128put(DATAALIGNMENTFACTOR); // guess + uleb128put(FAKERETURNCOLUMN); // return_address_register + + cput(DW_CFA_def_cfa); + uleb128put(DWARFREGSP); // register SP (**ABI-dependent, defined in l.h) + uleb128put(PtrSize); // offset + + cput(DW_CFA_offset + FAKERETURNCOLUMN); // return address + uleb128put(-PtrSize / DATAALIGNMENTFACTOR); // at cfa - x*4 + + // 4 is to exclude the length field. + pad = CIERESERVE + frameo + 4 - cpos(); + if (pad < 0) { + diag("CIERESERVE too small by %lld bytes.", -pad); + errorexit(); + } + strnput("", pad); + + for(cursym = textp; cursym != nil; cursym = cursym->next) { + s = cursym; + if(s->text == nil) + continue; + + fdeo = cpos(); + // Emit a FDE, Section 6.4.1, starting wit a placeholder. + LPUT(0); // length, must be multiple of PtrSize + LPUT(0); // Pointer to the CIE above, at offset 0 + addrput(0); // initial location + addrput(0); // address range + + cfa = PtrSize; // CFA starts at sp+PtrSize + p = s->text; + pc = p->pc; + + for(q = p; q->link != P; q = q->link) { + if (q->spadj == 0) + continue; + cfa += q->spadj; + putpccfadelta(q->link->pc - pc, cfa); + pc = q->link->pc; + } + + fdesize = cpos() - fdeo - 4; // exclude the length field. + pad = rnd(fdesize, PtrSize) - fdesize; + strnput("", pad); + fdesize += pad; + cflush(); + + // Emit the FDE header for real, Section 6.4.1. + seek(cout, fdeo, 0); + LPUT(fdesize); + LPUT(0); + addrput(p->pc); + addrput(s->size); + + cflush(); + seek(cout, fdeo + 4 + fdesize, 0); + } + + cflush(); + framesize = cpos() - frameo; +} + +/* + * Walk DWarfDebugInfoEntries, and emit .debug_info + */ +enum +{ + COMPUNITHEADERSIZE = 4+2+4+1 +}; + +static void +writeinfo(void) +{ + DWDie *compunit; + vlong unitstart, here; + + fwdcount = 0; + + for (compunit = dwroot.child; compunit; compunit = compunit->link) { + unitstart = cpos(); + + // Write .debug_info Compilation Unit Header (sec 7.5.1) + // Fields marked with (*) must be changed for 64-bit dwarf + // This must match COMPUNITHEADERSIZE above. + LPUT(0); // unit_length (*), will be filled in later. + WPUT(3); // dwarf version (appendix F) + LPUT(0); // debug_abbrev_offset (*) + cput(PtrSize); // address_size + + putdie(compunit); + + cflush(); + here = cpos(); + seek(cout, unitstart, 0); + LPUT(here - unitstart - 4); // exclude the length field. + cflush(); + seek(cout, here, 0); + } + +} + +/* + * Emit .debug_pubnames/_types. _info must have been written before, + * because we need die->offs and infoo/infosize; + */ +static int +ispubname(DWDie *die) { + DWAttr *a; + + switch(die->abbrev) { + case DW_ABRV_FUNCTION: + case DW_ABRV_VARIABLE: + a = getattr(die, DW_AT_external); + return a && a->value; + } + return 0; +} + +static int +ispubtype(DWDie *die) { + return die->abbrev >= DW_ABRV_NULLTYPE; +} + +static vlong +writepub(int (*ispub)(DWDie*)) +{ + DWDie *compunit, *die; + DWAttr *dwa; + vlong unitstart, unitend, sectionstart, here; + + sectionstart = cpos(); + + for (compunit = dwroot.child; compunit != nil; compunit = compunit->link) { + unitstart = compunit->offs - COMPUNITHEADERSIZE; + if (compunit->link != nil) + unitend = compunit->link->offs - COMPUNITHEADERSIZE; + else + unitend = infoo + infosize; + + // Write .debug_pubnames/types Header (sec 6.1.1) + LPUT(0); // unit_length (*), will be filled in later. + WPUT(2); // dwarf version (appendix F) + LPUT(unitstart); // debug_info_offset (of the Comp unit Header) + LPUT(unitend - unitstart); // debug_info_length + + for (die = compunit->child; die != nil; die = die->link) { + if (!ispub(die)) continue; + LPUT(die->offs - unitstart); + dwa = getattr(die, DW_AT_name); + strnput(dwa->data, dwa->value + 1); + } + LPUT(0); + + cflush(); + here = cpos(); + seek(cout, sectionstart, 0); + LPUT(here - sectionstart - 4); // exclude the length field. + cflush(); + seek(cout, here, 0); + + } + + return sectionstart; +} + +/* + * emit .debug_aranges. _info must have been written before, + * because we need die->offs of dw_globals. + */ +static vlong +writearanges(void) +{ + DWDie *compunit; + DWAttr *b, *e; + int headersize; + vlong sectionstart; + + sectionstart = cpos(); + headersize = rnd(4+2+4+1+1, PtrSize); // don't count unit_length field itself + + for (compunit = dwroot.child; compunit != nil; compunit = compunit->link) { + b = getattr(compunit, DW_AT_low_pc); + if (b == nil) + continue; + e = getattr(compunit, DW_AT_high_pc); + if (e == nil) + continue; + + // Write .debug_aranges Header + entry (sec 6.1.2) + LPUT(headersize + 4*PtrSize - 4); // unit_length (*) + WPUT(2); // dwarf version (appendix F) + LPUT(compunit->offs - COMPUNITHEADERSIZE); // debug_info_offset + cput(PtrSize); // address_size + cput(0); // segment_size + strnput("", headersize - (4+2+4+1+1)); // align to PtrSize + + addrput(b->value); + addrput(e->value - b->value); + addrput(0); + addrput(0); + } + cflush(); + return sectionstart; +} + +static vlong +writegdbscript(void) +{ + vlong sectionstart; + + sectionstart = cpos(); + + if (gdbscript[0]) { + cput(1); // magic 1 byte? + strnput(gdbscript, strlen(gdbscript)+1); + cflush(); + } + return sectionstart; +} + +/* + * This is the main entry point for generating dwarf. After emitting + * the mandatory debug_abbrev section, it calls writelines() to set up + * the per-compilation unit part of the DIE tree, while simultaneously + * emitting the debug_line section. When the final tree contains + * forward references, it will write the debug_info section in 2 + * passes. + * + */ +void +dwarfemitdebugsections(void) +{ + vlong infoe; + DWDie* die; + + // For diagnostic messages. + newattr(&dwtypes, DW_AT_name, DW_CLS_STRING, strlen("dwtypes"), "dwtypes"); + + mkindex(&dwroot); + mkindex(&dwtypes); + mkindex(&dwglobals); + + // Some types that must exist to define other ones. + newdie(&dwtypes, DW_ABRV_NULLTYPE, "<unspecified>"); + newdie(&dwtypes, DW_ABRV_NULLTYPE, "void"); + newrefattr(newdie(&dwtypes, DW_ABRV_PTRTYPE, "unsafe.Pointer"), + DW_AT_type, find(&dwtypes, "void")); + die = newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr"); // needed for array size + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, PtrSize, 0); + + // Needed by the prettyprinter code for interface inspection. + defgotype(lookup("type.runtime.commonType",0)); + defgotype(lookup("type.runtime.InterfaceType",0)); + defgotype(lookup("type.runtime.itab",0)); + + genasmsym(defdwsymb); + + writeabbrev(); + writelines(); + writeframes(); + + synthesizestringtypes(dwtypes.child); + synthesizeslicetypes(dwtypes.child); + synthesizemaptypes(dwtypes.child); + synthesizechantypes(dwtypes.child); + + reversetree(&dwroot.child); + reversetree(&dwtypes.child); + reversetree(&dwglobals.child); + + movetomodule(&dwtypes); + movetomodule(&dwglobals); + + infoo = cpos(); + writeinfo(); + gdbscripto = arangeso = pubtypeso = pubnameso = infoe = cpos(); + + if (fwdcount > 0) { + if (debug['v']) + Bprint(&bso, "%5.2f dwarf pass 2.\n", cputime()); + seek(cout, infoo, 0); + writeinfo(); + if (fwdcount > 0) { + diag("unresolved references after first dwarf info pass"); + errorexit(); + } + if (infoe != cpos()) { + diag("inconsistent second dwarf info pass"); + errorexit(); + } + } + infosize = infoe - infoo; + + pubnameso = writepub(ispubname); + pubtypeso = writepub(ispubtype); + arangeso = writearanges(); + gdbscripto = writegdbscript(); + + pubnamessize = pubtypeso - pubnameso; + pubtypessize = arangeso - pubtypeso; + arangessize = gdbscripto - arangeso; + gdbscriptsize = cpos() - gdbscripto; +} + +/* + * Elf. + */ +enum +{ + ElfStrDebugAbbrev, + ElfStrDebugAranges, + ElfStrDebugFrame, + ElfStrDebugInfo, + ElfStrDebugLine, + ElfStrDebugLoc, + ElfStrDebugMacinfo, + ElfStrDebugPubNames, + ElfStrDebugPubTypes, + ElfStrDebugRanges, + ElfStrDebugStr, + ElfStrGDBScripts, + NElfStrDbg +}; + +vlong elfstrdbg[NElfStrDbg]; + +void +dwarfaddshstrings(Sym *shstrtab) +{ + elfstrdbg[ElfStrDebugAbbrev] = addstring(shstrtab, ".debug_abbrev"); + elfstrdbg[ElfStrDebugAranges] = addstring(shstrtab, ".debug_aranges"); + elfstrdbg[ElfStrDebugFrame] = addstring(shstrtab, ".debug_frame"); + elfstrdbg[ElfStrDebugInfo] = addstring(shstrtab, ".debug_info"); + elfstrdbg[ElfStrDebugLine] = addstring(shstrtab, ".debug_line"); + elfstrdbg[ElfStrDebugLoc] = addstring(shstrtab, ".debug_loc"); + elfstrdbg[ElfStrDebugMacinfo] = addstring(shstrtab, ".debug_macinfo"); + elfstrdbg[ElfStrDebugPubNames] = addstring(shstrtab, ".debug_pubnames"); + elfstrdbg[ElfStrDebugPubTypes] = addstring(shstrtab, ".debug_pubtypes"); + elfstrdbg[ElfStrDebugRanges] = addstring(shstrtab, ".debug_ranges"); + elfstrdbg[ElfStrDebugStr] = addstring(shstrtab, ".debug_str"); + elfstrdbg[ElfStrGDBScripts] = addstring(shstrtab, ".debug_gdb_scripts"); +} + +void +dwarfaddelfheaders(void) +{ + ElfShdr *sh; + + sh = newElfShdr(elfstrdbg[ElfStrDebugAbbrev]); + sh->type = SHT_PROGBITS; + sh->off = abbrevo; + sh->size = abbrevsize; + sh->addralign = 1; + + sh = newElfShdr(elfstrdbg[ElfStrDebugLine]); + sh->type = SHT_PROGBITS; + sh->off = lineo; + sh->size = linesize; + sh->addralign = 1; + + sh = newElfShdr(elfstrdbg[ElfStrDebugFrame]); + sh->type = SHT_PROGBITS; + sh->off = frameo; + sh->size = framesize; + sh->addralign = 1; + + sh = newElfShdr(elfstrdbg[ElfStrDebugInfo]); + sh->type = SHT_PROGBITS; + sh->off = infoo; + sh->size = infosize; + sh->addralign = 1; + + if (pubnamessize > 0) { + sh = newElfShdr(elfstrdbg[ElfStrDebugPubNames]); + sh->type = SHT_PROGBITS; + sh->off = pubnameso; + sh->size = pubnamessize; + sh->addralign = 1; + } + + if (pubtypessize > 0) { + sh = newElfShdr(elfstrdbg[ElfStrDebugPubTypes]); + sh->type = SHT_PROGBITS; + sh->off = pubtypeso; + sh->size = pubtypessize; + sh->addralign = 1; + } + + if (arangessize) { + sh = newElfShdr(elfstrdbg[ElfStrDebugAranges]); + sh->type = SHT_PROGBITS; + sh->off = arangeso; + sh->size = arangessize; + sh->addralign = 1; + } + + if (gdbscriptsize) { + sh = newElfShdr(elfstrdbg[ElfStrGDBScripts]); + sh->type = SHT_PROGBITS; + sh->off = gdbscripto; + sh->size = gdbscriptsize; + sh->addralign = 1; + } +} + +/* + * Macho + */ +void +dwarfaddmachoheaders(void) +{ + MachoSect *msect; + MachoSeg *ms; + vlong fakestart; + int nsect; + + // Zero vsize segments won't be loaded in memory, even so they + // have to be page aligned in the file. + fakestart = abbrevo & ~0xfff; + + nsect = 4; + if (pubnamessize > 0) + nsect++; + if (pubtypessize > 0) + nsect++; + if (arangessize > 0) + nsect++; + if (gdbscriptsize > 0) + nsect++; + + ms = newMachoSeg("__DWARF", nsect); + ms->fileoffset = fakestart; + ms->filesize = abbrevo-fakestart; + + msect = newMachoSect(ms, "__debug_abbrev"); + msect->off = abbrevo; + msect->size = abbrevsize; + ms->filesize += msect->size; + + msect = newMachoSect(ms, "__debug_line"); + msect->off = lineo; + msect->size = linesize; + ms->filesize += msect->size; + + msect = newMachoSect(ms, "__debug_frame"); + msect->off = frameo; + msect->size = framesize; + ms->filesize += msect->size; + + msect = newMachoSect(ms, "__debug_info"); + msect->off = infoo; + msect->size = infosize; + ms->filesize += msect->size; + + if (pubnamessize > 0) { + msect = newMachoSect(ms, "__debug_pubnames"); + msect->off = pubnameso; + msect->size = pubnamessize; + ms->filesize += msect->size; + } + + if (pubtypessize > 0) { + msect = newMachoSect(ms, "__debug_pubtypes"); + msect->off = pubtypeso; + msect->size = pubtypessize; + ms->filesize += msect->size; + } + + if (arangessize > 0) { + msect = newMachoSect(ms, "__debug_aranges"); + msect->off = arangeso; + msect->size = arangessize; + ms->filesize += msect->size; + } + + // TODO(lvd) fix gdb/python to load MachO (16 char section name limit) + if (gdbscriptsize > 0) { + msect = newMachoSect(ms, "__debug_gdb_scripts"); + msect->off = gdbscripto; + msect->size = gdbscriptsize; + ms->filesize += msect->size; + } +} diff --git a/src/cmd/ld/dwarf.h b/src/cmd/ld/dwarf.h new file mode 100644 index 000000000..7881213c2 --- /dev/null +++ b/src/cmd/ld/dwarf.h @@ -0,0 +1,29 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + * Register 'f' symbol file fragments. Doing this while parsing the + * .6 input saves a pass over the symbol table later. + */ +void dwarfaddfrag(int n, char* frag); + +/* + * Emit debug_abbrevs, debug_info and debug_line sections to current + * offset in cout. + */ +void dwarfemitdebugsections(void); + +/* + * Add the dwarf section names to the ELF + * s[ection]h[eader]str[ing]tab. Prerequisite for + * dwarfaddelfheaders(). + */ +void dwarfaddshstrings(Sym *shstrtab); + +/* + * Add section headers pointing to the sections emitted in + * dwarfemitdebugsections. + */ +void dwarfaddelfheaders(void); +void dwarfaddmachoheaders(void); diff --git a/src/cmd/ld/dwarf_defs.h b/src/cmd/ld/dwarf_defs.h new file mode 100644 index 000000000..eed143dff --- /dev/null +++ b/src/cmd/ld/dwarf_defs.h @@ -0,0 +1,503 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Cut, pasted, tr-and-awk'ed from tables in +// http://dwarfstd.org/doc/Dwarf3.pdf + +// Table 18 +enum +{ + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parameter = 0x05, + DW_TAG_imported_declaration = 0x08, + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + DW_TAG_member = 0x0d, + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + DW_TAG_namelist_item = 0x2c, + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + DW_TAG_template_type_parameter = 0x2f, + DW_TAG_template_value_parameter = 0x30, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + // Dwarf3 + DW_TAG_dwarf_procedure = 0x36, + DW_TAG_restrict_type = 0x37, + DW_TAG_interface_type = 0x38, + DW_TAG_namespace = 0x39, + DW_TAG_imported_module = 0x3a, + DW_TAG_unspecified_type = 0x3b, + DW_TAG_partial_unit = 0x3c, + DW_TAG_imported_unit = 0x3d, + DW_TAG_condition = 0x3f, + DW_TAG_shared_type = 0x40, + // Dwarf4 + DW_TAG_type_unit = 0x41, + DW_TAG_rvalue_reference_type = 0x42, + DW_TAG_template_alias = 0x43, + + // User defined + DW_TAG_lo_user = 0x4080, + DW_TAG_hi_user = 0xffff, + +}; + +// Table 19 +enum +{ + DW_CHILDREN_no = 0x00, + DW_CHILDREN_yes = 0x01, +}; + +// Not from the spec, but logicaly belongs here +enum +{ + DW_CLS_ADDRESS = 0x01, + DW_CLS_BLOCK, + DW_CLS_CONSTANT, + DW_CLS_FLAG, + DW_CLS_PTR, // lineptr, loclistptr, macptr, rangelistptr + DW_CLS_REFERENCE, + DW_CLS_STRING +}; + +// Table 20 +enum +{ + DW_AT_sibling = 0x01, // reference + DW_AT_location = 0x02, // block, loclistptr + DW_AT_name = 0x03, // string + DW_AT_ordering = 0x09, // constant + DW_AT_byte_size = 0x0b, // block, constant, reference + DW_AT_bit_offset = 0x0c, // block, constant, reference + DW_AT_bit_size = 0x0d, // block, constant, reference + DW_AT_stmt_list = 0x10, // lineptr + DW_AT_low_pc = 0x11, // address + DW_AT_high_pc = 0x12, // address + DW_AT_language = 0x13, // constant + DW_AT_discr = 0x15, // reference + DW_AT_discr_value = 0x16, // constant + DW_AT_visibility = 0x17, // constant + DW_AT_import = 0x18, // reference + DW_AT_string_length = 0x19, // block, loclistptr + DW_AT_common_reference = 0x1a, // reference + DW_AT_comp_dir = 0x1b, // string + DW_AT_const_value = 0x1c, // block, constant, string + DW_AT_containing_type = 0x1d, // reference + DW_AT_default_value = 0x1e, // reference + DW_AT_inline = 0x20, // constant + DW_AT_is_optional = 0x21, // flag + DW_AT_lower_bound = 0x22, // block, constant, reference + DW_AT_producer = 0x25, // string + DW_AT_prototyped = 0x27, // flag + DW_AT_return_addr = 0x2a, // block, loclistptr + DW_AT_start_scope = 0x2c, // constant + DW_AT_bit_stride = 0x2e, // constant + DW_AT_upper_bound = 0x2f, // block, constant, reference + DW_AT_abstract_origin = 0x31, // reference + DW_AT_accessibility = 0x32, // constant + DW_AT_address_class = 0x33, // constant + DW_AT_artificial = 0x34, // flag + DW_AT_base_types = 0x35, // reference + DW_AT_calling_convention = 0x36, // constant + DW_AT_count = 0x37, // block, constant, reference + DW_AT_data_member_location = 0x38, // block, constant, loclistptr + DW_AT_decl_column = 0x39, // constant + DW_AT_decl_file = 0x3a, // constant + DW_AT_decl_line = 0x3b, // constant + DW_AT_declaration = 0x3c, // flag + DW_AT_discr_list = 0x3d, // block + DW_AT_encoding = 0x3e, // constant + DW_AT_external = 0x3f, // flag + DW_AT_frame_base = 0x40, // block, loclistptr + DW_AT_friend = 0x41, // reference + DW_AT_identifier_case = 0x42, // constant + DW_AT_macro_info = 0x43, // macptr + DW_AT_namelist_item = 0x44, // block + DW_AT_priority = 0x45, // reference + DW_AT_segment = 0x46, // block, loclistptr + DW_AT_specification = 0x47, // reference + DW_AT_static_link = 0x48, // block, loclistptr + DW_AT_type = 0x49, // reference + DW_AT_use_location = 0x4a, // block, loclistptr + DW_AT_variable_parameter = 0x4b, // flag + DW_AT_virtuality = 0x4c, // constant + DW_AT_vtable_elem_location = 0x4d, // block, loclistptr + // Dwarf3 + DW_AT_allocated = 0x4e, // block, constant, reference + DW_AT_associated = 0x4f, // block, constant, reference + DW_AT_data_location = 0x50, // block + DW_AT_byte_stride = 0x51, // block, constant, reference + DW_AT_entry_pc = 0x52, // address + DW_AT_use_UTF8 = 0x53, // flag + DW_AT_extension = 0x54, // reference + DW_AT_ranges = 0x55, // rangelistptr + DW_AT_trampoline = 0x56, // address, flag, reference, string + DW_AT_call_column = 0x57, // constant + DW_AT_call_file = 0x58, // constant + DW_AT_call_line = 0x59, // constant + DW_AT_description = 0x5a, // string + DW_AT_binary_scale = 0x5b, // constant + DW_AT_decimal_scale = 0x5c, // constant + DW_AT_small = 0x5d, // reference + DW_AT_decimal_sign = 0x5e, // constant + DW_AT_digit_count = 0x5f, // constant + DW_AT_picture_string = 0x60, // string + DW_AT_mutable = 0x61, // flag + DW_AT_threads_scaled = 0x62, // flag + DW_AT_explicit = 0x63, // flag + DW_AT_object_pointer = 0x64, // reference + DW_AT_endianity = 0x65, // constant + DW_AT_elemental = 0x66, // flag + DW_AT_pure = 0x67, // flag + DW_AT_recursive = 0x68, // flag + + DW_AT_lo_user = 0x2000, // --- + DW_AT_hi_user = 0x3fff, // --- + +}; + +// Table 21 +enum +{ + DW_FORM_addr = 0x01, // address + DW_FORM_block2 = 0x03, // block + DW_FORM_block4 = 0x04, // block + DW_FORM_data2 = 0x05, // constant + DW_FORM_data4 = 0x06, // constant, lineptr, loclistptr, macptr, rangelistptr + DW_FORM_data8 = 0x07, // constant, lineptr, loclistptr, macptr, rangelistptr + DW_FORM_string = 0x08, // string + DW_FORM_block = 0x09, // block + DW_FORM_block1 = 0x0a, // block + DW_FORM_data1 = 0x0b, // constant + DW_FORM_flag = 0x0c, // flag + DW_FORM_sdata = 0x0d, // constant + DW_FORM_strp = 0x0e, // string + DW_FORM_udata = 0x0f, // constant + DW_FORM_ref_addr = 0x10, // reference + DW_FORM_ref1 = 0x11, // reference + DW_FORM_ref2 = 0x12, // reference + DW_FORM_ref4 = 0x13, // reference + DW_FORM_ref8 = 0x14, // reference + DW_FORM_ref_udata = 0x15, // reference + DW_FORM_indirect = 0x16, // (see Section 7.5.3) +}; + +// Table 24 (#operands, notes) +enum +{ + DW_OP_addr = 0x03, // 1 constant address (size target specific) + DW_OP_deref = 0x06, // 0 + DW_OP_const1u = 0x08, // 1 1-byte constant + DW_OP_const1s = 0x09, // 1 1-byte constant + DW_OP_const2u = 0x0a, // 1 2-byte constant + DW_OP_const2s = 0x0b, // 1 2-byte constant + DW_OP_const4u = 0x0c, // 1 4-byte constant + DW_OP_const4s = 0x0d, // 1 4-byte constant + DW_OP_const8u = 0x0e, // 1 8-byte constant + DW_OP_const8s = 0x0f, // 1 8-byte constant + DW_OP_constu = 0x10, // 1 ULEB128 constant + DW_OP_consts = 0x11, // 1 SLEB128 constant + DW_OP_dup = 0x12, // 0 + DW_OP_drop = 0x13, // 0 + DW_OP_over = 0x14, // 0 + DW_OP_pick = 0x15, // 1 1-byte stack index + DW_OP_swap = 0x16, // 0 + DW_OP_rot = 0x17, // 0 + DW_OP_xderef = 0x18, // 0 + DW_OP_abs = 0x19, // 0 + DW_OP_and = 0x1a, // 0 + DW_OP_div = 0x1b, // 0 + DW_OP_minus = 0x1c, // 0 + DW_OP_mod = 0x1d, // 0 + DW_OP_mul = 0x1e, // 0 + DW_OP_neg = 0x1f, // 0 + DW_OP_not = 0x20, // 0 + DW_OP_or = 0x21, // 0 + DW_OP_plus = 0x22, // 0 + DW_OP_plus_uconst = 0x23, // 1 ULEB128 addend + DW_OP_shl = 0x24, // 0 + DW_OP_shr = 0x25, // 0 + DW_OP_shra = 0x26, // 0 + DW_OP_xor = 0x27, // 0 + DW_OP_skip = 0x2f, // 1 signed 2-byte constant + DW_OP_bra = 0x28, // 1 signed 2-byte constant + DW_OP_eq = 0x29, // 0 + DW_OP_ge = 0x2a, // 0 + DW_OP_gt = 0x2b, // 0 + DW_OP_le = 0x2c, // 0 + DW_OP_lt = 0x2d, // 0 + DW_OP_ne = 0x2e, // 0 + DW_OP_lit0 = 0x30, // 0 ... + DW_OP_lit31 = 0x4f, // 0 literals 0..31 = (DW_OP_lit0 + + // literal) + DW_OP_reg0 = 0x50, // 0 .. + DW_OP_reg31 = 0x6f, // 0 reg 0..31 = (DW_OP_reg0 + regnum) + DW_OP_breg0 = 0x70, // 1 ... + DW_OP_breg31 = 0x8f, // 1 SLEB128 offset base register 0..31 = (DW_OP_breg0 + regnum) + DW_OP_regx = 0x90, // 1 ULEB128 register + DW_OP_fbreg = 0x91, // 1 SLEB128 offset + DW_OP_bregx = 0x92, // 2 ULEB128 register followed by SLEB128 offset + DW_OP_piece = 0x93, // 1 ULEB128 size of piece addressed + DW_OP_deref_size = 0x94, // 1 1-byte size of data retrieved + DW_OP_xderef_size = 0x95, // 1 1-byte size of data retrieved + DW_OP_nop = 0x96, // 0 + DW_OP_push_object_address = 0x97, // 0 + DW_OP_call2 = 0x98, // 1 2-byte offset of DIE + DW_OP_call4 = 0x99, // 1 4-byte offset of DIE + DW_OP_call_ref = 0x9a, // 1 4- or 8-byte offset of DIE + DW_OP_form_tls_address = 0x9b, // 0 + DW_OP_call_frame_cfa = 0x9c, // 0 + DW_OP_bit_piece = 0x9d, // 2 + DW_OP_lo_user = 0xe0, + DW_OP_hi_user = 0xff, +}; + +// Table 25 +enum +{ + DW_ATE_address = 0x01, + DW_ATE_boolean = 0x02, + DW_ATE_complex_float = 0x03, + DW_ATE_float = 0x04, + DW_ATE_signed = 0x05, + DW_ATE_signed_char = 0x06, + DW_ATE_unsigned = 0x07, + DW_ATE_unsigned_char = 0x08, + DW_ATE_imaginary_float = 0x09, + DW_ATE_packed_decimal = 0x0a, + DW_ATE_numeric_string = 0x0b, + DW_ATE_edited = 0x0c, + DW_ATE_signed_fixed = 0x0d, + DW_ATE_unsigned_fixed = 0x0e, + DW_ATE_decimal_float = 0x0f, + DW_ATE_lo_user = 0x80, + DW_ATE_hi_user = 0xff, +}; + +// Table 26 +enum +{ + DW_DS_unsigned = 0x01, + DW_DS_leading_overpunch = 0x02, + DW_DS_trailing_overpunch = 0x03, + DW_DS_leading_separate = 0x04, + DW_DS_trailing_separate = 0x05, +}; + +// Table 27 +enum +{ + DW_END_default = 0x00, + DW_END_big = 0x01, + DW_END_little = 0x02, + DW_END_lo_user = 0x40, + DW_END_hi_user = 0xff, +}; + +// Table 28 +enum +{ + DW_ACCESS_public = 0x01, + DW_ACCESS_protected = 0x02, + DW_ACCESS_private = 0x03, +}; + +// Table 29 +enum +{ + DW_VIS_local = 0x01, + DW_VIS_exported = 0x02, + DW_VIS_qualified = 0x03, +}; + +// Table 30 +enum +{ + DW_VIRTUALITY_none = 0x00, + DW_VIRTUALITY_virtual = 0x01, + DW_VIRTUALITY_pure_virtual = 0x02, +}; + +// Table 31 +enum +{ + DW_LANG_C89 = 0x0001, + DW_LANG_C = 0x0002, + DW_LANG_Ada83 = 0x0003, + DW_LANG_C_plus_plus = 0x0004, + DW_LANG_Cobol74 = 0x0005, + DW_LANG_Cobol85 = 0x0006, + DW_LANG_Fortran77 = 0x0007, + DW_LANG_Fortran90 = 0x0008, + DW_LANG_Pascal83 = 0x0009, + DW_LANG_Modula2 = 0x000a, + // Dwarf3 + DW_LANG_Java = 0x000b, + DW_LANG_C99 = 0x000c, + DW_LANG_Ada95 = 0x000d, + DW_LANG_Fortran95 = 0x000e, + DW_LANG_PLI = 0x000f, + DW_LANG_ObjC = 0x0010, + DW_LANG_ObjC_plus_plus = 0x0011, + DW_LANG_UPC = 0x0012, + DW_LANG_D = 0x0013, + // Dwarf4 + DW_LANG_Python = 0x0014, + // Dwarf5 + DW_LANG_Go = 0x0016, + + DW_LANG_lo_user = 0x8000, + DW_LANG_hi_user = 0xffff, +}; + +// Table 32 +enum +{ + DW_ID_case_sensitive = 0x00, + DW_ID_up_case = 0x01, + DW_ID_down_case = 0x02, + DW_ID_case_insensitive = 0x03, +}; + +// Table 33 +enum +{ + DW_CC_normal = 0x01, + DW_CC_program = 0x02, + DW_CC_nocall = 0x03, + DW_CC_lo_user = 0x40, + DW_CC_hi_user = 0xff, +}; + +// Table 34 +enum +{ + DW_INL_not_inlined = 0x00, + DW_INL_inlined = 0x01, + DW_INL_declared_not_inlined = 0x02, + DW_INL_declared_inlined = 0x03, +}; + +// Table 35 +enum +{ + DW_ORD_row_major = 0x00, + DW_ORD_col_major = 0x01, +}; + +// Table 36 +enum +{ + DW_DSC_label = 0x00, + DW_DSC_range = 0x01, +}; + +// Table 37 +enum +{ + DW_LNS_copy = 0x01, + DW_LNS_advance_pc = 0x02, + DW_LNS_advance_line = 0x03, + DW_LNS_set_file = 0x04, + DW_LNS_set_column = 0x05, + DW_LNS_negate_stmt = 0x06, + DW_LNS_set_basic_block = 0x07, + DW_LNS_const_add_pc = 0x08, + DW_LNS_fixed_advance_pc = 0x09, + // Dwarf3 + DW_LNS_set_prologue_end = 0x0a, + DW_LNS_set_epilogue_begin = 0x0b, + DW_LNS_set_isa = 0x0c, +}; + +// Table 38 +enum +{ + DW_LNE_end_sequence = 0x01, + DW_LNE_set_address = 0x02, + DW_LNE_define_file = 0x03, + DW_LNE_lo_user = 0x80, + DW_LNE_hi_user = 0xff, +}; + +// Table 39 +enum +{ + DW_MACINFO_define = 0x01, + DW_MACINFO_undef = 0x02, + DW_MACINFO_start_file = 0x03, + DW_MACINFO_end_file = 0x04, + DW_MACINFO_vendor_ext = 0xff, +}; + +// Table 40. +enum +{ // operand,... + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, // address + DW_CFA_advance_loc1 = 0x02, // 1-byte delta + DW_CFA_advance_loc2 = 0x03, // 2-byte delta + DW_CFA_advance_loc4 = 0x04, // 4-byte delta + DW_CFA_offset_extended = 0x05, // ULEB128 register, ULEB128 offset + DW_CFA_restore_extended = 0x06, // ULEB128 register + DW_CFA_undefined = 0x07, // ULEB128 register + DW_CFA_same_value = 0x08, // ULEB128 register + DW_CFA_register = 0x09, // ULEB128 register, ULEB128 register + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, // ULEB128 register, ULEB128 offset + DW_CFA_def_cfa_register = 0x0d, // ULEB128 register + DW_CFA_def_cfa_offset = 0x0e, // ULEB128 offset + DW_CFA_def_cfa_expression = 0x0f, // BLOCK + DW_CFA_expression = 0x10, // ULEB128 register, BLOCK + DW_CFA_offset_extended_sf = 0x11, // ULEB128 register, SLEB128 offset + DW_CFA_def_cfa_sf = 0x12, // ULEB128 register, SLEB128 offset + DW_CFA_def_cfa_offset_sf = 0x13, // SLEB128 offset + DW_CFA_val_offset = 0x14, // ULEB128, ULEB128 + DW_CFA_val_offset_sf = 0x15, // ULEB128, SLEB128 + DW_CFA_val_expression = 0x16, // ULEB128, BLOCK + + DW_CFA_lo_user = 0x1c, + DW_CFA_hi_user = 0x3f, + + // Opcodes that take an addend operand. + DW_CFA_advance_loc = 0x1<<6, // +delta + DW_CFA_offset = 0x2<<6, // +register (ULEB128 offset) + DW_CFA_restore = 0x3<<6, // +register +}; diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c index c5d58576d..d5b0b0311 100644 --- a/src/cmd/ld/elf.c +++ b/src/cmd/ld/elf.c @@ -21,6 +21,16 @@ static ElfPhdr *phdr[NSECT]; static ElfShdr *shdr[NSECT]; static char *interp; +typedef struct Elfstring Elfstring; +struct Elfstring +{ + char *s; + int off; +}; + +static Elfstring elfstr[100]; +static int nelfstr; + /* Initialize the global variable that describes the ELF header. It will be updated as we write section and prog headers. @@ -122,6 +132,18 @@ elfwriteshdrs(void) return hdr.shnum * ELF32SHDRSIZE; } +void +elfsetstring(char *s, int off) +{ + if(nelfstr >= nelem(elfstr)) { + diag("too many elf strings"); + errorexit(); + } + elfstr[nelfstr].s = s; + elfstr[nelfstr].off = off; + nelfstr++; +} + uint32 elfwritephdrs(void) { @@ -142,8 +164,7 @@ newElfPhdr(void) { ElfPhdr *e; - e = malloc(sizeof *e); - memset(e, 0, sizeof *e); + e = mal(sizeof *e); if (hdr.phnum >= NSECT) diag("too many phdrs"); else @@ -167,8 +188,7 @@ newElfShdr(vlong name) { ElfShdr *e; - e = malloc(sizeof *e); - memset(e, 0, sizeof *e); + e = mal(sizeof *e); e->name = name; if (hdr.shnum >= NSECT) { diag("too many shdrs"); @@ -294,7 +314,7 @@ elfwriteinterp(void) n = strlen(interp)+1; seek(cout, ELFRESERVE-n, 0); - write(cout, interp, n); + ewrite(cout, interp, n); return n; } @@ -310,17 +330,25 @@ elfinterp(ElfShdr *sh, uint64 startva, char *p) sh->size = n; } +extern int nelfsym; + void -elfdynhash(int nsym) +elfdynhash(void) { Sym *s, *sy; int i, h, nbucket, b; uchar *pc; uint32 hc, g; uint32 *chain, *buckets; + int nsym; + char *name; + + if(!iself) + return; + nsym = nelfsym; s = lookup(".hash", 0); - s->type = SELFDATA; // TODO: rodata + s->type = SELFDATA; s->reachable = 1; i = nsym; @@ -331,17 +359,24 @@ elfdynhash(int nsym) } chain = malloc(nsym * sizeof(uint32)); - memset(chain, 0, nsym * sizeof(uint32)); buckets = malloc(nbucket * sizeof(uint32)); + if(chain == nil || buckets == nil) { + cursym = nil; + diag("out of memory"); + errorexit(); + } + memset(chain, 0, nsym * sizeof(uint32)); memset(buckets, 0, nbucket * sizeof(uint32)); - i = 1; for(h = 0; h<NHASH; h++) { - for(sy=hash[h]; sy!=S; sy=sy->link) { - if (!sy->reachable || (sy->type != STEXT && sy->type != SDATA && sy->type != SBSS) || sy->dynimpname == nil) + for(sy=hash[h]; sy!=S; sy=sy->hash) { + if (sy->dynid <= 0) continue; hc = 0; - for(pc = (uchar*)sy->dynimpname; *pc; pc++) { + name = sy->dynimpname; + if(name == nil) + name = sy->name; + for(pc = (uchar*)name; *pc; pc++) { hc = (hc<<4) + *pc; g = hc & 0xf0000000; hc ^= g >> 24; @@ -349,9 +384,8 @@ elfdynhash(int nsym) } b = hc % nbucket; - chain[i] = buckets[b]; - buckets[b] = i; - i++; + chain[sy->dynid] = buckets[b]; + buckets[b] = sy->dynid; } } @@ -364,4 +398,64 @@ elfdynhash(int nsym) free(chain); free(buckets); + + elfwritedynent(lookup(".dynamic", 0), DT_NULL, 0); +} + +ElfPhdr* +elfphload(Segment *seg) +{ + ElfPhdr *ph; + + ph = newElfPhdr(); + ph->type = PT_LOAD; + if(seg->rwx & 4) + ph->flags |= PF_R; + if(seg->rwx & 2) + ph->flags |= PF_W; + if(seg->rwx & 1) + ph->flags |= PF_X; + ph->vaddr = seg->vaddr; + ph->paddr = seg->vaddr; + ph->memsz = seg->len; + ph->off = seg->fileoff; + ph->filesz = seg->filelen; + ph->align = INITRND; + + return ph; +} + +ElfShdr* +elfshbits(Section *sect) +{ + int i, off; + ElfShdr *sh; + + for(i=0; i<nelfstr; i++) { + if(strcmp(sect->name, elfstr[i].s) == 0) { + off = elfstr[i].off; + goto found; + } + } + diag("cannot find elf name %s", sect->name); + errorexit(); + return nil; + +found: + sh = newElfShdr(off); + if(sect->vaddr < sect->seg->vaddr + sect->seg->filelen) + sh->type = SHT_PROGBITS; + else + sh->type = SHT_NOBITS; + sh->flags = SHF_ALLOC; + if(sect->rwx & 1) + sh->flags |= SHF_EXECINSTR; + if(sect->rwx & 2) + sh->flags |= SHF_WRITE; + sh->addr = sect->vaddr; + sh->addralign = PtrSize; + sh->size = sect->len; + sh->off = sect->seg->fileoff + sect->vaddr - sect->seg->vaddr; + + return sh; } diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h index 9b5fdb17e..df15cb115 100644 --- a/src/cmd/ld/elf.h +++ b/src/cmd/ld/elf.h @@ -964,7 +964,10 @@ extern int numelfshdr; extern int iself; int elfwriteinterp(void); void elfinterp(ElfShdr*, uint64, char*); -void elfdynhash(int); +void elfdynhash(void); +ElfPhdr* elfphload(Segment*); +ElfShdr* elfshbits(Section*); +void elfsetstring(char*, int); /* * Total amount of space to reserve at the start of the file @@ -972,5 +975,4 @@ void elfdynhash(int); * May waste some. * On FreeBSD, cannot be larger than a page. */ -#define ELFRESERVE 2048 - +#define ELFRESERVE 3072 diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c index 015f34db2..8966b2a1f 100644 --- a/src/cmd/ld/go.c +++ b/src/cmd/ld/go.c @@ -66,12 +66,11 @@ ilookup(char *name) } static void loadpkgdata(char*, char*, char*, int); -static void loaddynimport(char*, char*, int); +static void loaddynimport(char*, char*, char*, int); static void loaddynexport(char*, char*, char*, int); static int parsemethod(char**, char*, char**); static int parsepkgdata(char*, char*, char**, char*, char**, char**, char**); -static int ndynexp; static Sym **dynexp; void @@ -194,7 +193,7 @@ ldpkg(Biobuf *f, char *pkg, int64 len, char *filename, int whence) errorexit(); return; } - loaddynimport(filename, p0 + 1, p1 - (p0+1)); + loaddynimport(filename, pkg, p0 + 1, p1 - (p0+1)); } // look for dynexp section @@ -266,6 +265,10 @@ expandpkg(char *t0, char *pkg) // use malloc, not mal, so that caller can free w0 = malloc(strlen(t0) + strlen(pkg)*n); + if(w0 == nil) { + diag("out of memory"); + errorexit(); + } w = w0; for(p=t=t0; (p=strstr(p, "\"\".")) != nil; p=t) { memmove(w, t, p - t); @@ -282,7 +285,7 @@ static int parsepkgdata(char *file, char *pkg, char **pp, char *ep, char **prefixp, char **namep, char **defp) { char *p, *prefix, *name, *def, *edef, *meth; - int n; + int n, inquote; // skip white space p = *pp; @@ -319,8 +322,19 @@ loop: // name: a.b followed by space name = p; - while(p < ep && *p != ' ') + inquote = 0; + while(p < ep) { + if (*p == ' ' && !inquote) + break; + + if(*p == '\\') + p++; + else if(*p == '"') + inquote = !inquote; + p++; + } + if(p >= ep) return -1; *p++ = '\0'; @@ -397,7 +411,7 @@ parsemethod(char **pp, char *ep, char **methp) } static void -loaddynimport(char *file, char *p, int n) +loaddynimport(char *file, char *pkg, char *p, int n) { char *pend, *next, *name, *def, *p0, *lib; Sym *s; @@ -431,10 +445,21 @@ loaddynimport(char *file, char *p, int n) // successful parse: now can edit the line *strchr(name, ' ') = 0; *strchr(def, ' ') = 0; + + if(strcmp(name, "_") == 0 && strcmp(def, "_") == 0) { + // allow #pragma dynimport _ _ "foo.so" + // to force a link of foo.so. + adddynlib(lib); + continue; + } + name = expandpkg(name, pkg); s = lookup(name, 0); - s->dynimplib = lib; - s->dynimpname = def; + if(s->type == 0 || s->type == SXREF) { + s->dynimplib = lib; + s->dynimpname = def; + s->type = SDYNIMPORT; + } } return; @@ -499,38 +524,19 @@ err: static int markdepth; static void -markdata(Prog *p, Sym *s) -{ - markdepth++; - if(p != P && debug['v'] > 1) - Bprint(&bso, "%d markdata %s\n", markdepth, s->name); - for(; p != P; p=p->dlink) - if(p->to.sym) - mark(p->to.sym); - markdepth--; -} - -static void -marktext(Prog *p) +marktext(Sym *s) { Auto *a; + Prog *p; - if(p == P) + if(s == S) return; - if(p->as != ATEXT) { - diag("marktext: %P", p); - return; - } - for(a=p->to.autom; a; a=a->link) - mark(a->gotype); markdepth++; if(debug['v'] > 1) - Bprint(&bso, "%d marktext %s\n", markdepth, p->from.sym->name); - for(a=p->to.autom; a; a=a->link) + Bprint(&bso, "%d marktext %s\n", markdepth, s->name); + for(a=s->autom; a; a=a->link) mark(a->gotype); - for(p=p->link; p != P; p=p->link) { - if(p->as == ATEXT || p->as == ADATA || p->as == AGLOBL) - break; + for(p=s->text; p != P; p=p->link) { if(p->from.sym) mark(p->from.sym); if(p->to.sym) @@ -542,45 +548,21 @@ marktext(Prog *p) void mark(Sym *s) { + int i; + if(s == S || s->reachable) return; s->reachable = 1; if(s->text) - marktext(s->text); - if(s->data) - markdata(s->data, s); + marktext(s); + for(i=0; i<s->nr; i++) + mark(s->r[i].sym); if(s->gotype) mark(s->gotype); -} - -static void -sweeplist(Prog **first, Prog **last) -{ - int reachable; - Prog *p, *q; - - reachable = 1; - q = P; - for(p=*first; p != P; p=p->link) { - switch(p->as) { - case ATEXT: - case ADATA: - case AGLOBL: - reachable = p->from.sym->reachable; - } - if(reachable) { - if(q == P) - *first = p; - else - q->link = p; - q = p; - } - } - if(q == P) - *first = P; - else - q->link = P; - *last = q; + if(s->sub) + mark(s->sub); + if(s->outer) + mark(s->outer); } static char* @@ -602,10 +584,43 @@ morename[] = "runtime.morestack48", }; +static int +isz(Auto *a) +{ + for(; a; a=a->link) + if(a->type == D_FILE || a->type == D_FILE1) + return 1; + return 0; +} + +static void +addz(Sym *s, Auto *z) +{ + Auto *a, *last; + + // strip out non-z + last = nil; + for(a = z; a != nil; a = a->link) { + if(a->type == D_FILE || a->type == D_FILE1) { + if(last == nil) + z = a; + else + last->link = a; + last = a; + } + } + if(last) { + last->link = s->autom; + s->autom = z; + } +} + void deadcode(void) { int i; + Sym *s, *last; + Auto *z; if(debug['v']) Bprint(&bso, "%5.2f deadcode\n", cputime()); @@ -616,7 +631,38 @@ deadcode(void) for(i=0; i<ndynexp; i++) mark(dynexp[i]); + + // remove dead text but keep file information (z symbols). + last = nil; + z = nil; + for(s = textp; s != nil; s = s->next) { + if(!s->reachable) { + if(isz(s->autom)) + z = s->autom; + continue; + } + if(last == nil) + textp = s; + else + last->next = s; + last = s; + if(z != nil) { + if(!isz(s->autom)) + addz(s, z); + z = nil; + } + } + if(last == nil) + textp = nil; + else + last->next = nil; +} - // remove dead data - sweeplist(&datap, &edatap); +void +addexport(void) +{ + int i; + + for(i=0; i<ndynexp; i++) + adddynsym(dynexp[i]); } diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c new file mode 100644 index 000000000..44bbe68ee --- /dev/null +++ b/src/cmd/ld/ldelf.c @@ -0,0 +1,816 @@ +/* +Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c +http://code.swtch.com/plan9port/src/tip/src/libmach/ + + Copyright © 2004 Russ Cox. + Portions Copyright © 2008-2010 Google Inc. + Portions Copyright © 2010 The Go Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "l.h" +#include "lib.h" +#include "../ld/elf.h" + +enum +{ + ElfClassNone = 0, + ElfClass32, + ElfClass64, + + ElfDataNone = 0, + ElfDataLsb, + ElfDataMsb, + + ElfTypeNone = 0, + ElfTypeRelocatable, + ElfTypeExecutable, + ElfTypeSharedObject, + ElfTypeCore, + /* 0xFF00 - 0xFFFF reserved for processor-specific types */ + + ElfMachNone = 0, + ElfMach32100, /* AT&T WE 32100 */ + ElfMachSparc, /* SPARC */ + ElfMach386, /* Intel 80386 */ + ElfMach68000, /* Motorola 68000 */ + ElfMach88000, /* Motorola 88000 */ + ElfMach486, /* Intel 80486, no longer used */ + ElfMach860, /* Intel 80860 */ + ElfMachMips, /* MIPS RS3000 */ + ElfMachS370, /* IBM System/370 */ + ElfMachMipsLe, /* MIPS RS3000 LE */ + ElfMachParisc = 15, /* HP PA RISC */ + ElfMachVpp500 = 17, /* Fujitsu VPP500 */ + ElfMachSparc32Plus, /* SPARC V8+ */ + ElfMach960, /* Intel 80960 */ + ElfMachPower, /* PowerPC */ + ElfMachPower64, /* PowerPC 64 */ + ElfMachS390, /* IBM System/390 */ + ElfMachV800 = 36, /* NEC V800 */ + ElfMachFr20, /* Fujitsu FR20 */ + ElfMachRh32, /* TRW RH-32 */ + ElfMachRce, /* Motorola RCE */ + ElfMachArm, /* ARM */ + ElfMachAlpha, /* Digital Alpha */ + ElfMachSH, /* Hitachi SH */ + ElfMachSparc9, /* SPARC V9 */ + ElfMachAmd64 = 62, + /* and the list goes on... */ + + ElfAbiNone = 0, + ElfAbiSystemV = 0, /* [sic] */ + ElfAbiHPUX, + ElfAbiNetBSD, + ElfAbiLinux, + ElfAbiSolaris = 6, + ElfAbiAix, + ElfAbiIrix, + ElfAbiFreeBSD, + ElfAbiTru64, + ElfAbiModesto, + ElfAbiOpenBSD, + ElfAbiARM = 97, + ElfAbiEmbedded = 255, + + /* some of sections 0xFF00 - 0xFFFF reserved for various things */ + ElfSectNone = 0, + ElfSectProgbits, + ElfSectSymtab, + ElfSectStrtab, + ElfSectRela, + ElfSectHash, + ElfSectDynamic, + ElfSectNote, + ElfSectNobits, + ElfSectRel, + ElfSectShlib, + ElfSectDynsym, + + ElfSectFlagWrite = 0x1, + ElfSectFlagAlloc = 0x2, + ElfSectFlagExec = 0x4, + /* 0xF0000000 are reserved for processor specific */ + + ElfSymBindLocal = 0, + ElfSymBindGlobal, + ElfSymBindWeak, + /* 13-15 reserved */ + + ElfSymTypeNone = 0, + ElfSymTypeObject, + ElfSymTypeFunc, + ElfSymTypeSection, + ElfSymTypeFile, + /* 13-15 reserved */ + + ElfSymShnNone = 0, + ElfSymShnAbs = 0xFFF1, + ElfSymShnCommon = 0xFFF2, + /* 0xFF00-0xFF1F reserved for processors */ + /* 0xFF20-0xFF3F reserved for operating systems */ + + ElfProgNone = 0, + ElfProgLoad, + ElfProgDynamic, + ElfProgInterp, + ElfProgNote, + ElfProgShlib, + ElfProgPhdr, + + ElfProgFlagExec = 0x1, + ElfProgFlagWrite = 0x2, + ElfProgFlagRead = 0x4, + + ElfNotePrStatus = 1, + ElfNotePrFpreg = 2, + ElfNotePrPsinfo = 3, + ElfNotePrTaskstruct = 4, + ElfNotePrAuxv = 6, + ElfNotePrXfpreg = 0x46e62b7f /* for gdb/386 */ +}; + +typedef struct ElfHdrBytes ElfHdrBytes; +typedef struct ElfSectBytes ElfSectBytes; +typedef struct ElfProgBytes ElfProgBytes; +typedef struct ElfSymBytes ElfSymBytes; + +typedef struct ElfHdrBytes64 ElfHdrBytes64; +typedef struct ElfSectBytes64 ElfSectBytes64; +typedef struct ElfProgBytes64 ElfProgBytes64; +typedef struct ElfSymBytes64 ElfSymBytes64; + +struct ElfHdrBytes +{ + uchar ident[16]; + uchar type[2]; + uchar machine[2]; + uchar version[4]; + uchar entry[4]; + uchar phoff[4]; + uchar shoff[4]; + uchar flags[4]; + uchar ehsize[2]; + uchar phentsize[2]; + uchar phnum[2]; + uchar shentsize[2]; + uchar shnum[2]; + uchar shstrndx[2]; +}; + +struct ElfHdrBytes64 +{ + uchar ident[16]; + uchar type[2]; + uchar machine[2]; + uchar version[4]; + uchar entry[8]; + uchar phoff[8]; + uchar shoff[8]; + uchar flags[4]; + uchar ehsize[2]; + uchar phentsize[2]; + uchar phnum[2]; + uchar shentsize[2]; + uchar shnum[2]; + uchar shstrndx[2]; +}; + +struct ElfSectBytes +{ + uchar name[4]; + uchar type[4]; + uchar flags[4]; + uchar addr[4]; + uchar off[4]; + uchar size[4]; + uchar link[4]; + uchar info[4]; + uchar align[4]; + uchar entsize[4]; +}; + +struct ElfSectBytes64 +{ + uchar name[4]; + uchar type[4]; + uchar flags[8]; + uchar addr[8]; + uchar off[8]; + uchar size[8]; + uchar link[4]; + uchar info[4]; + uchar align[8]; + uchar entsize[8]; +}; + +struct ElfSymBytes +{ + uchar name[4]; + uchar value[4]; + uchar size[4]; + uchar info; /* top4: bind, bottom4: type */ + uchar other; + uchar shndx[2]; +}; + +struct ElfSymBytes64 +{ + uchar name[4]; + uchar info; /* top4: bind, bottom4: type */ + uchar other; + uchar shndx[2]; + uchar value[8]; + uchar size[8]; +}; + +typedef struct ElfSect ElfSect; +typedef struct ElfObj ElfObj; +typedef struct ElfSym ElfSym; + +struct ElfSect +{ + char *name; + uint32 type; + uint64 flags; + uint64 addr; + uint64 off; + uint64 size; + uint32 link; + uint32 info; + uint64 align; + uint64 entsize; + uchar *base; + Sym *sym; +}; + +struct ElfObj +{ + Biobuf *f; + int64 base; // offset in f where ELF begins + int64 len; // length of ELF + int is64; + char *name; + + Endian *e; + ElfSect *sect; + uint nsect; + char *shstrtab; + int nsymtab; + ElfSect *symtab; + ElfSect *symstr; + + uint32 type; + uint32 machine; + uint32 version; + uint64 entry; + uint64 phoff; + uint64 shoff; + uint32 flags; + uint32 ehsize; + uint32 phentsize; + uint32 phnum; + uint32 shentsize; + uint32 shnum; + uint32 shstrndx; +}; + +struct ElfSym +{ + char* name; + uint64 value; + uint64 size; + uchar bind; + uchar type; + uchar other; + uint16 shndx; + Sym* sym; +}; + +uchar ElfMagic[4] = { 0x7F, 'E', 'L', 'F' }; + +static ElfSect* section(ElfObj*, char*); +static int map(ElfObj*, ElfSect*); +static int readsym(ElfObj*, int i, ElfSym*); +static int reltype(char*, int, uchar*); + +void +ldelf(Biobuf *f, char *pkg, int64 len, char *pn) +{ + int32 base; + uint64 add, info; + char *name; + int i, j, rela, is64, n; + uchar hdrbuf[64]; + uchar *p, *dp; + ElfHdrBytes *hdr; + ElfObj *obj; + ElfSect *sect, *rsect; + ElfSym sym; + Endian *e; + Reloc *r, *rp; + Sym *s; + + if(debug['v']) + Bprint(&bso, "%5.2f ldelf %s\n", cputime(), pn); + + version++; + base = Boffset(f); + + if(Bread(f, &hdrbuf, sizeof hdrbuf) != sizeof hdrbuf) + goto bad; + hdr = (ElfHdrBytes*)&hdrbuf; + if(memcmp(hdr->ident, ElfMagic, 4) != 0) + goto bad; + switch(hdr->ident[5]) { + case ElfDataLsb: + e = ≤ + break; + case ElfDataMsb: + e = &be; + break; + default: + goto bad; + } + + // read header + obj = mal(sizeof *obj); + obj->e = e; + obj->f = f; + obj->base = base; + obj->len = len; + obj->name = pn; + + is64 = 0; + if(hdr->ident[4] == ElfClass64) { + ElfHdrBytes64* hdr; + + is64 = 1; + hdr = (ElfHdrBytes64*)hdrbuf; + obj->type = e->e16(hdr->type); + obj->machine = e->e16(hdr->machine); + obj->version = e->e32(hdr->version); + obj->phoff = e->e64(hdr->phoff); + obj->shoff = e->e64(hdr->shoff); + obj->flags = e->e32(hdr->flags); + obj->ehsize = e->e16(hdr->ehsize); + obj->phentsize = e->e16(hdr->phentsize); + obj->phnum = e->e16(hdr->phnum); + obj->shentsize = e->e16(hdr->shentsize); + obj->shnum = e->e16(hdr->shnum); + obj->shstrndx = e->e16(hdr->shstrndx); + } else { + obj->type = e->e16(hdr->type); + obj->machine = e->e16(hdr->machine); + obj->version = e->e32(hdr->version); + obj->entry = e->e32(hdr->entry); + obj->phoff = e->e32(hdr->phoff); + obj->shoff = e->e32(hdr->shoff); + obj->flags = e->e32(hdr->flags); + obj->ehsize = e->e16(hdr->ehsize); + obj->phentsize = e->e16(hdr->phentsize); + obj->phnum = e->e16(hdr->phnum); + obj->shentsize = e->e16(hdr->shentsize); + obj->shnum = e->e16(hdr->shnum); + obj->shstrndx = e->e16(hdr->shstrndx); + } + obj->is64 = is64; + + if(hdr->ident[6] != obj->version) + goto bad; + + if(e->e16(hdr->type) != ElfTypeRelocatable) { + diag("%s: elf but not elf relocatable object"); + return; + } + + switch(thechar) { + default: + diag("%s: elf %s unimplemented", thestring); + return; + case '5': + if(e != &le || obj->machine != ElfMachArm || hdr->ident[4] != ElfClass32) { + diag("%s: elf object but not arm", pn); + return; + } + break; + case '6': + if(e != &le || obj->machine != ElfMachAmd64 || hdr->ident[4] != ElfClass64) { + diag("%s: elf object but not amd64", pn); + return; + } + break; + case '8': + if(e != &le || obj->machine != ElfMach386 || hdr->ident[4] != ElfClass32) { + diag("%s: elf object but not 386", pn); + return; + } + break; + } + + // load section list into memory. + obj->sect = mal(obj->shnum*sizeof obj->sect[0]); + obj->nsect = obj->shnum; + for(i=0; i<obj->nsect; i++) { + if(Bseek(f, base+obj->shoff+i*obj->shentsize, 0) < 0) + goto bad; + sect = &obj->sect[i]; + if(is64) { + ElfSectBytes64 b; + + werrstr("short read"); + if(Bread(f, &b, sizeof b) != sizeof b) + goto bad; + + sect->name = (char*)(uintptr)e->e32(b.name); + sect->type = e->e32(b.type); + sect->flags = e->e64(b.flags); + sect->addr = e->e64(b.addr); + sect->off = e->e64(b.off); + sect->size = e->e64(b.size); + sect->link = e->e32(b.link); + sect->info = e->e32(b.info); + sect->align = e->e64(b.align); + sect->entsize = e->e64(b.entsize); + } else { + ElfSectBytes b; + + werrstr("short read"); + if(Bread(f, &b, sizeof b) != sizeof b) + goto bad; + + sect->name = (char*)(uintptr)e->e32(b.name); + sect->type = e->e32(b.type); + sect->flags = e->e32(b.flags); + sect->addr = e->e32(b.addr); + sect->off = e->e32(b.off); + sect->size = e->e32(b.size); + sect->link = e->e32(b.link); + sect->info = e->e32(b.info); + sect->align = e->e32(b.align); + sect->entsize = e->e32(b.entsize); + } + } + + // read section string table and translate names + if(obj->shstrndx >= obj->nsect) { + werrstr("shstrndx out of range %d >= %d", obj->shstrndx, obj->nsect); + goto bad; + } + sect = &obj->sect[obj->shstrndx]; + if(map(obj, sect) < 0) + goto bad; + for(i=0; i<obj->nsect; i++) + if(obj->sect[i].name != nil) + obj->sect[i].name = (char*)sect->base + (uintptr)obj->sect[i].name; + + // load string table for symbols into memory. + obj->symtab = section(obj, ".symtab"); + if(obj->symtab == nil) { + // our work is done here - no symbols means nothing can refer to this file + return; + } + if(obj->symtab->link <= 0 || obj->symtab->link >= obj->nsect) { + diag("%s: elf object has symbol table with invalid string table link", pn); + return; + } + obj->symstr = &obj->sect[obj->symtab->link]; + if(is64) + obj->nsymtab = obj->symtab->size / sizeof(ElfSymBytes64); + else + obj->nsymtab = obj->symtab->size / sizeof(ElfSymBytes); + + if(map(obj, obj->symtab) < 0) + goto bad; + if(map(obj, obj->symstr) < 0) + goto bad; + + // load text and data segments into memory. + // they are not as small as the section lists, but we'll need + // the memory anyway for the symbol images, so we might + // as well use one large chunk. + + // create symbols for mapped sections + for(i=0; i<obj->nsect; i++) { + sect = &obj->sect[i]; + if((sect->type != ElfSectProgbits && sect->type != ElfSectNobits) || !(sect->flags&ElfSectFlagAlloc)) + continue; + if(sect->type != ElfSectNobits && map(obj, sect) < 0) + goto bad; + + name = smprint("%s(%s)", pn, sect->name); + s = lookup(name, version); + free(name); + switch(sect->flags&(ElfSectFlagAlloc|ElfSectFlagWrite|ElfSectFlagExec)) { + default: + werrstr("unexpected flags for ELF section %s", sect->name); + goto bad; + case ElfSectFlagAlloc: + s->type = SRODATA; + break; + case ElfSectFlagAlloc + ElfSectFlagWrite: + s->type = SDATA; + break; + case ElfSectFlagAlloc + ElfSectFlagExec: + s->type = STEXT; + break; + } + if(sect->type == ElfSectProgbits) { + s->p = sect->base; + s->np = sect->size; + } + s->size = sect->size; + if(s->type == STEXT) { + if(etextp) + etextp->next = s; + else + textp = s; + etextp = s; + } + sect->sym = s; + } + + // load relocations + for(i=0; i<obj->nsect; i++) { + rsect = &obj->sect[i]; + if(rsect->type != ElfSectRela && rsect->type != ElfSectRel) + continue; + if(rsect->info >= obj->nsect || obj->sect[rsect->info].base == nil) + continue; + sect = &obj->sect[rsect->info]; + if(map(obj, rsect) < 0) + goto bad; + rela = rsect->type == ElfSectRela; + n = rsect->size/(4+4*is64)/(2+rela); + r = mal(n*sizeof r[0]); + p = rsect->base; + dp = sect->base; + for(j=0; j<n; j++) { + add = 0; + rp = &r[j]; + if(is64) { + // 64-bit rel/rela + rp->off = e->e64(p); + p += 8; + info = e->e64(p); + p += 8; + if(rela) { + add = e->e64(p); + p += 8; + } + } else { + // 32-bit rel/rela + rp->off = e->e32(p); + p += 4; + info = e->e32(p); + info = info>>8<<32 | (info&0xff); // convert to 64-bit info + p += 4; + if(rela) { + add = e->e32(p); + p += 4; + } + } + if(readsym(obj, info>>32, &sym) < 0) + goto bad; + if(sym.sym == nil) { + werrstr("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", + sect->sym->name, j, (int)(info>>32), sym.name, sym.shndx, sym.type); + goto bad; + } + rp->sym = sym.sym; + rp->type = reltype(pn, (uint32)info, &rp->siz); + if(rela) + rp->add = add; + else { + // load addend from image + if(rp->siz == 4) + rp->add = e->e32(sect->base+rp->off); + else if(rp->siz == 8) + rp->add = e->e64(sect->base+rp->off); + else + diag("invalid rela size %d", rp->siz); + } + } + qsort(r, n, sizeof r[0], rbyoff); // just in case + + s = sect->sym; + s->r = r; + s->nr = n; + } + + // enter sub-symbols into symbol table. + // symbol 0 is the null symbol. + for(i=1; i<obj->nsymtab; i++) { + if(readsym(obj, i, &sym) < 0) + goto bad; + if(sym.type != ElfSymTypeFunc && sym.type != ElfSymTypeObject && sym.type != ElfSymTypeNone) + continue; + if(sym.shndx == ElfSymShnCommon) { + s = sym.sym; + if(s->size < sym.size) + s->size = sym.size; + if(s->type == 0 || s->type == SXREF) + s->type = SBSS; + continue; + } + if(sym.shndx >= obj->nsect || sym.shndx == 0) + continue; + sect = obj->sect+sym.shndx; + if(sect->sym == nil) { + diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type); + continue; + } + s = sym.sym; + s->sub = sect->sym->sub; + sect->sym->sub = s; + s->type = sect->sym->type | SSUB; + if(!s->dynexport) { + s->dynimplib = nil; // satisfy dynimport + s->dynimpname = nil; // satisfy dynimport + } + s->value = sym.value; + s->size = sym.size; + s->outer = sect->sym; + if(sect->sym->type == STEXT) { + Prog *p; + + if(s->text != P) + diag("%s: duplicate definition of %s", pn, s->name); + // build a TEXT instruction with a unique pc + // just to make the rest of the linker happy. + p = prg(); + p->as = ATEXT; + p->from.type = D_EXTERN; + p->from.sym = s; + p->textflag = 7; + p->to.type = D_CONST; + p->link = nil; + p->pc = pc++; + s->text = p; + + etextp->next = s; + etextp = s; + } + } + return; + +bad: + diag("%s: malformed elf file: %r", pn); +} + +static ElfSect* +section(ElfObj *obj, char *name) +{ + int i; + + for(i=0; i<obj->nsect; i++) + if(obj->sect[i].name && name && strcmp(obj->sect[i].name, name) == 0) + return &obj->sect[i]; + return nil; +} + +static int +map(ElfObj *obj, ElfSect *sect) +{ + if(sect->base != nil) + return 0; + + if(sect->off+sect->size > obj->len) { + werrstr("elf section past end of file"); + return -1; + } + + sect->base = mal(sect->size); + werrstr("short read"); + if(Bseek(obj->f, obj->base+sect->off, 0) < 0 || Bread(obj->f, sect->base, sect->size) != sect->size) + return -1; + + return 0; +} + +static int +readsym(ElfObj *obj, int i, ElfSym *sym) +{ + Sym *s; + + if(i >= obj->nsymtab || i < 0) { + werrstr("invalid elf symbol index"); + return -1; + } + + if(obj->is64) { + ElfSymBytes64 *b; + + b = (ElfSymBytes64*)(obj->symtab->base + i*sizeof *b); + sym->name = (char*)obj->symstr->base + obj->e->e32(b->name); + sym->value = obj->e->e64(b->value); + sym->size = obj->e->e64(b->size); + sym->shndx = obj->e->e16(b->shndx); + sym->bind = b->info>>4; + sym->type = b->info&0xf; + sym->other = b->other; + } else { + ElfSymBytes *b; + + b = (ElfSymBytes*)(obj->symtab->base + i*sizeof *b); + sym->name = (char*)obj->symstr->base + obj->e->e32(b->name); + sym->value = obj->e->e32(b->value); + sym->size = obj->e->e32(b->size); + sym->shndx = obj->e->e16(b->shndx); + sym->bind = b->info>>4; + sym->type = b->info&0xf; + sym->other = b->other; + } + + s = nil; + if(strcmp(sym->name, "_GLOBAL_OFFSET_TABLE_") == 0) + sym->name = ".got"; + if(strcmp(sym->name, "__stack_chk_fail_local") == 0) + sym->other = 0; // rewrite hidden -> default visibility + switch(sym->type) { + case ElfSymTypeSection: + s = obj->sect[sym->shndx].sym; + break; + case ElfSymTypeObject: + case ElfSymTypeFunc: + case ElfSymTypeNone: + switch(sym->bind) { + case ElfSymBindGlobal: + if(sym->other != 2) { + s = lookup(sym->name, 0); + break; + } + // fall through + case ElfSymBindLocal: + s = lookup(sym->name, version); + break; + default: + werrstr("%s: invalid symbol binding %d", sym->name, sym->bind); + return -1; + } + break; + } + if(s != nil && s->type == 0 && sym->type != ElfSymTypeSection) + s->type = SXREF; + sym->sym = s; + + return 0; +} + +int +rbyoff(const void *va, const void *vb) +{ + Reloc *a, *b; + + a = (Reloc*)va; + b = (Reloc*)vb; + if(a->off < b->off) + return -1; + if(a->off > b->off) + return +1; + return 0; +} + +#define R(x, y) ((x)|((y)<<24)) + +static int +reltype(char *pn, int elftype, uchar *siz) +{ + switch(R(thechar, elftype)) { + default: + diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype); + case R('6', R_X86_64_PC32): + case R('6', R_X86_64_PLT32): + case R('6', R_X86_64_GOTPCREL): + case R('8', R_386_32): + case R('8', R_386_PC32): + case R('8', R_386_GOT32): + case R('8', R_386_PLT32): + case R('8', R_386_GOTOFF): + case R('8', R_386_GOTPC): + *siz = 4; + break; + case R('6', R_X86_64_64): + *siz = 8; + break; + } + + return 256+elftype; +} diff --git a/src/cmd/ld/ldmacho.c b/src/cmd/ld/ldmacho.c new file mode 100644 index 000000000..7e38db0e4 --- /dev/null +++ b/src/cmd/ld/ldmacho.c @@ -0,0 +1,794 @@ +/* +Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c +http://code.swtch.com/plan9port/src/tip/src/libmach/ + + Copyright © 2004 Russ Cox. + Portions Copyright © 2008-2010 Google Inc. + Portions Copyright © 2010 The Go Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "l.h" +#include "lib.h" + +enum { + MACHO_FAKE_GOTPCREL = 100, // from macho.h + + N_EXT = 0x01, + N_TYPE = 0x1e, + N_STAB = 0xe0, +}; + +typedef struct MachoObj MachoObj; +typedef struct MachoCmd MachoCmd; +typedef struct MachoSeg MachoSeg; +typedef struct MachoSect MachoSect; +typedef struct MachoRel MachoRel; +typedef struct MachoSymtab MachoSymtab; +typedef struct MachoSym MachoSym; +typedef struct MachoDysymtab MachoDysymtab; + +enum +{ + MachoCpuVax = 1, + MachoCpu68000 = 6, + MachoCpu386 = 7, + MachoCpuAmd64 = 0x1000007, + MachoCpuMips = 8, + MachoCpu98000 = 10, + MachoCpuHppa = 11, + MachoCpuArm = 12, + MachoCpu88000 = 13, + MachoCpuSparc = 14, + MachoCpu860 = 15, + MachoCpuAlpha = 16, + MachoCpuPower = 18, + + MachoCmdSegment = 1, + MachoCmdSymtab = 2, + MachoCmdSymseg = 3, + MachoCmdThread = 4, + MachoCmdDysymtab = 11, + MachoCmdSegment64 = 25, + + MachoFileObject = 1, + MachoFileExecutable = 2, + MachoFileFvmlib = 3, + MachoFileCore = 4, + MachoFilePreload = 5, +}; + +struct MachoSeg +{ + char name[16+1]; + uint64 vmaddr; + uint64 vmsize; + uint32 fileoff; + uint32 filesz; + uint32 maxprot; + uint32 initprot; + uint32 nsect; + uint32 flags; + MachoSect *sect; +}; + +struct MachoSect +{ + char name[16+1]; + char segname[16+1]; + uint64 addr; + uint64 size; + uint32 off; + uint32 align; + uint32 reloff; + uint32 nreloc; + uint32 flags; + uint32 res1; + uint32 res2; + Sym *sym; + + MachoRel *rel; +}; + +struct MachoRel +{ + uint32 addr; + uint32 symnum; + uint8 pcrel; + uint8 length; + uint8 extrn; + uint8 type; + uint8 scattered; + uint32 value; +}; + +struct MachoSymtab +{ + uint32 symoff; + uint32 nsym; + uint32 stroff; + uint32 strsize; + + char *str; + MachoSym *sym; +}; + +struct MachoSym +{ + char *name; + uint8 type; + uint8 sectnum; + uint16 desc; + char kind; + uint64 value; + Sym *sym; +}; + +struct MachoDysymtab +{ + uint32 ilocalsym; + uint32 nlocalsym; + uint32 iextdefsym; + uint32 nextdefsym; + uint32 iundefsym; + uint32 nundefsym; + uint32 tocoff; + uint32 ntoc; + uint32 modtaboff; + uint32 nmodtab; + uint32 extrefsymoff; + uint32 nextrefsyms; + uint32 indirectsymoff; + uint32 nindirectsyms; + uint32 extreloff; + uint32 nextrel; + uint32 locreloff; + uint32 nlocrel; + uint32 *indir; +}; + +struct MachoCmd +{ + int type; + uint32 off; + uint32 size; + MachoSeg seg; + MachoSymtab sym; + MachoDysymtab dsym; +}; + +struct MachoObj +{ + Biobuf *f; + int64 base; // off in f where Mach-O begins + int64 len; // length of Mach-O + int is64; + char *name; + + Endian *e; + uint cputype; + uint subcputype; + uint32 filetype; + uint32 flags; + MachoCmd *cmd; + uint ncmd; +}; + +static int +unpackcmd(uchar *p, MachoObj *m, MachoCmd *c, uint type, uint sz) +{ + uint32 (*e4)(uchar*); + uint64 (*e8)(uchar*); + MachoSect *s; + int i; + + e4 = m->e->e32; + e8 = m->e->e64; + + c->type = type; + c->size = sz; + switch(type){ + default: + return -1; + case MachoCmdSegment: + if(sz < 56) + return -1; + strecpy(c->seg.name, c->seg.name+sizeof c->seg.name, (char*)p+8); + c->seg.vmaddr = e4(p+24); + c->seg.vmsize = e4(p+28); + c->seg.fileoff = e4(p+32); + c->seg.filesz = e4(p+36); + c->seg.maxprot = e4(p+40); + c->seg.initprot = e4(p+44); + c->seg.nsect = e4(p+48); + c->seg.flags = e4(p+52); + c->seg.sect = mal(c->seg.nsect * sizeof c->seg.sect[0]); + if(sz < 56+c->seg.nsect*68) + return -1; + p += 56; + for(i=0; i<c->seg.nsect; i++) { + s = &c->seg.sect[i]; + strecpy(s->name, s->name+sizeof s->name, (char*)p+0); + strecpy(s->segname, s->segname+sizeof s->segname, (char*)p+16); + s->addr = e4(p+32); + s->size = e4(p+36); + s->off = e4(p+40); + s->align = e4(p+44); + s->reloff = e4(p+48); + s->nreloc = e4(p+52); + s->flags = e4(p+56); + s->res1 = e4(p+60); + s->res2 = e4(p+64); + p += 68; + } + break; + case MachoCmdSegment64: + if(sz < 72) + return -1; + strecpy(c->seg.name, c->seg.name+sizeof c->seg.name, (char*)p+8); + c->seg.vmaddr = e8(p+24); + c->seg.vmsize = e8(p+32); + c->seg.fileoff = e8(p+40); + c->seg.filesz = e8(p+48); + c->seg.maxprot = e4(p+56); + c->seg.initprot = e4(p+60); + c->seg.nsect = e4(p+64); + c->seg.flags = e4(p+68); + c->seg.sect = mal(c->seg.nsect * sizeof c->seg.sect[0]); + if(sz < 72+c->seg.nsect*80) + return -1; + p += 72; + for(i=0; i<c->seg.nsect; i++) { + s = &c->seg.sect[i]; + strecpy(s->name, s->name+sizeof s->name, (char*)p+0); + strecpy(s->segname, s->segname+sizeof s->segname, (char*)p+16); + s->addr = e8(p+32); + s->size = e8(p+40); + s->off = e4(p+48); + s->align = e4(p+52); + s->reloff = e4(p+56); + s->nreloc = e4(p+60); + s->flags = e4(p+64); + s->res1 = e4(p+68); + s->res2 = e4(p+72); + // p+76 is reserved + p += 80; + } + break; + case MachoCmdSymtab: + if(sz < 24) + return -1; + c->sym.symoff = e4(p+8); + c->sym.nsym = e4(p+12); + c->sym.stroff = e4(p+16); + c->sym.strsize = e4(p+20); + break; + case MachoCmdDysymtab: + if(sz < 80) + return -1; + c->dsym.ilocalsym = e4(p+8); + c->dsym.nlocalsym = e4(p+12); + c->dsym.iextdefsym = e4(p+16); + c->dsym.nextdefsym = e4(p+20); + c->dsym.iundefsym = e4(p+24); + c->dsym.nundefsym = e4(p+28); + c->dsym.tocoff = e4(p+32); + c->dsym.ntoc = e4(p+36); + c->dsym.modtaboff = e4(p+40); + c->dsym.nmodtab = e4(p+44); + c->dsym.extrefsymoff = e4(p+48); + c->dsym.nextrefsyms = e4(p+52); + c->dsym.indirectsymoff = e4(p+56); + c->dsym.nindirectsyms = e4(p+60); + c->dsym.extreloff = e4(p+64); + c->dsym.nextrel = e4(p+68); + c->dsym.locreloff = e4(p+72); + c->dsym.nlocrel = e4(p+76); + break; + } + return 0; +} + +static int +macholoadrel(MachoObj *m, MachoSect *sect) +{ + MachoRel *rel, *r; + uchar *buf, *p; + int i, n; + uint32 v; + + if(sect->rel != nil || sect->nreloc == 0) + return 0; + rel = mal(sect->nreloc * sizeof r[0]); + n = sect->nreloc * 8; + buf = mal(n); + if(Bseek(m->f, m->base + sect->reloff, 0) < 0 || Bread(m->f, buf, n) != n) + return -1; + for(i=0; i<sect->nreloc; i++) { + r = &rel[i]; + p = buf+i*8; + r->addr = m->e->e32(p); + + // TODO(rsc): Wrong interpretation for big-endian bitfields? + if(r->addr & 0x80000000) { + // scatterbrained relocation + r->scattered = 1; + v = r->addr >> 24; + r->addr &= 0xFFFFFF; + r->type = v & 0xF; + v >>= 4; + r->length = 1<<(v&3); + v >>= 2; + r->pcrel = v & 1; + r->value = m->e->e32(p+4); + } else { + v = m->e->e32(p+4); + r->symnum = v & 0xFFFFFF; + v >>= 24; + r->pcrel = v&1; + v >>= 1; + r->length = 1<<(v&3); + v >>= 2; + r->extrn = v&1; + v >>= 1; + r->type = v; + } + } + sect->rel = rel; + return 0; +} + +static int +macholoaddsym(MachoObj *m, MachoDysymtab *d) +{ + uchar *p; + int i, n; + + n = d->nindirectsyms; + + p = mal(n*4); + if(Bseek(m->f, m->base + d->indirectsymoff, 0) < 0 || Bread(m->f, p, n*4) != n*4) + return -1; + + d->indir = (uint32*)p; + for(i=0; i<n; i++) + d->indir[i] = m->e->e32(p+4*i); + return 0; +} + +static int +macholoadsym(MachoObj *m, MachoSymtab *symtab) +{ + char *strbuf; + uchar *symbuf, *p; + int i, n, symsize; + MachoSym *sym, *s; + uint32 v; + + if(symtab->sym != nil) + return 0; + + strbuf = mal(symtab->strsize); + if(Bseek(m->f, m->base + symtab->stroff, 0) < 0 || Bread(m->f, strbuf, symtab->strsize) != symtab->strsize) + return -1; + + symsize = 12; + if(m->is64) + symsize = 16; + n = symtab->nsym * symsize; + symbuf = mal(n); + if(Bseek(m->f, m->base + symtab->symoff, 0) < 0 || Bread(m->f, symbuf, n) != n) + return -1; + sym = mal(symtab->nsym * sizeof sym[0]); + p = symbuf; + for(i=0; i<symtab->nsym; i++) { + s = &sym[i]; + v = m->e->e32(p); + if(v >= symtab->strsize) + return -1; + s->name = strbuf + v; + s->type = p[4]; + s->sectnum = p[5]; + s->desc = m->e->e16(p+6); + if(m->is64) + s->value = m->e->e64(p+8); + else + s->value = m->e->e32(p+8); + p += symsize; + } + symtab->str = strbuf; + symtab->sym = sym; + return 0; +} + +void +ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) +{ + int i, j, is64; + uchar hdr[7*4], *cmdp; + uchar tmp[4]; + uchar *dat; + ulong ncmd, cmdsz, ty, sz, off; + MachoObj *m; + Endian *e; + int64 base; + MachoSect *sect; + MachoRel *rel; + Sym *s, *outer; + MachoCmd *c; + MachoSymtab *symtab; + MachoDysymtab *dsymtab; + MachoSym *sym; + Reloc *r, *rp; + char *name; + + version++; + base = Boffset(f); + if(Bread(f, hdr, sizeof hdr) != sizeof hdr) + goto bad; + + if((be.e32(hdr)&~1) == 0xFEEDFACE){ + e = &be; + }else if((le.e32(hdr)&~1) == 0xFEEDFACE){ + e = ≤ + }else{ + werrstr("bad magic - not mach-o file"); + goto bad; + } + + is64 = e->e32(hdr) == 0xFEEDFACF; + ncmd = e->e32(hdr+4*4); + cmdsz = e->e32(hdr+5*4); + if(ncmd > 0x10000 || cmdsz >= 0x01000000){ + werrstr("implausible mach-o header ncmd=%lud cmdsz=%lud", ncmd, cmdsz); + goto bad; + } + if(is64) + Bread(f, tmp, 4); // skip reserved word in header + + m = mal(sizeof(*m)+ncmd*sizeof(MachoCmd)+cmdsz); + m->f = f; + m->e = e; + m->cputype = e->e32(hdr+1*4); + m->subcputype = e->e32(hdr+2*4); + m->filetype = e->e32(hdr+3*4); + m->ncmd = ncmd; + m->flags = e->e32(hdr+6*4); + m->is64 = is64; + m->base = base; + m->len = len; + m->name = pn; + + switch(thechar) { + default: + diag("%s: mach-o %s unimplemented", thestring); + return; + case '6': + if(e != &le || m->cputype != MachoCpuAmd64) { + diag("%s: mach-o object but not amd64", pn); + return; + } + break; + case '8': + if(e != &le || m->cputype != MachoCpu386) { + diag("%s: mach-o object but not 386", pn); + return; + } + break; + } + + m->cmd = (MachoCmd*)(m+1); + off = sizeof hdr; + cmdp = (uchar*)(m->cmd+ncmd); + if(Bread(f, cmdp, cmdsz) != cmdsz){ + werrstr("reading cmds: %r"); + goto bad; + } + + // read and parse load commands + c = nil; + symtab = nil; + dsymtab = nil; + for(i=0; i<ncmd; i++){ + ty = e->e32(cmdp); + sz = e->e32(cmdp+4); + m->cmd[i].off = off; + unpackcmd(cmdp, m, &m->cmd[i], ty, sz); + cmdp += sz; + off += sz; + if(ty == MachoCmdSymtab) { + if(symtab != nil) { + werrstr("multiple symbol tables"); + goto bad; + } + symtab = &m->cmd[i].sym; + macholoadsym(m, symtab); + } + if(ty == MachoCmdDysymtab) { + dsymtab = &m->cmd[i].dsym; + macholoaddsym(m, dsymtab); + } + if((is64 && ty == MachoCmdSegment64) || (!is64 && ty == MachoCmdSegment)) { + if(c != nil) { + werrstr("multiple load commands"); + goto bad; + } + c = &m->cmd[i]; + } + } + + // load text and data segments into memory. + // they are not as small as the load commands, but we'll need + // the memory anyway for the symbol images, so we might + // as well use one large chunk. + if(c == nil) { + werrstr("no load command"); + goto bad; + } + if(symtab == nil) { + // our work is done here - no symbols means nothing can refer to this file + return; + } + + if(c->seg.fileoff+c->seg.filesz >= len) { + werrstr("load segment out of range"); + goto bad; + } + + dat = mal(c->seg.filesz); + if(Bseek(f, m->base + c->seg.fileoff, 0) < 0 || Bread(f, dat, c->seg.filesz) != c->seg.filesz) { + werrstr("cannot load object data: %r"); + goto bad; + } + + for(i=0; i<c->seg.nsect; i++) { + sect = &c->seg.sect[i]; + if(strcmp(sect->segname, "__TEXT") != 0 && strcmp(sect->segname, "__DATA") != 0) + continue; + if(strcmp(sect->name, "__eh_frame") == 0) + continue; + name = smprint("%s(%s/%s)", pn, sect->segname, sect->name); + s = lookup(name, version); + if(s->type != 0) { + werrstr("duplicate %s/%s", sect->segname, sect->name); + goto bad; + } + free(name); + s->p = dat + sect->addr - c->seg.vmaddr; + s->np = sect->size; + s->size = s->np; + + if(strcmp(sect->segname, "__TEXT") == 0) { + if(strcmp(sect->name, "__text") == 0) + s->type = STEXT; + else + s->type = SRODATA; + } else { + s->type = SDATA; + } + if(s->type == STEXT) { + if(etextp) + etextp->next = s; + else + textp = s; + etextp = s; + } + sect->sym = s; + } + + // enter sub-symbols into symbol table. + // have to guess sizes from next symbol. + for(i=0; i<symtab->nsym; i++) { + int v; + sym = &symtab->sym[i]; + if(sym->type&N_STAB) + continue; + // TODO: check sym->type against outer->type. + name = sym->name; + if(name[0] == '_' && name[1] != '\0') + name++; + v = 0; + if(!(sym->type&N_EXT)) + v = version; + s = lookup(name, v); + sym->sym = s; + if(sym->sectnum == 0) // undefined + continue; + if(sym->sectnum > c->seg.nsect) { + werrstr("reference to invalid section %d", sym->sectnum); + goto bad; + } + sect = &c->seg.sect[sym->sectnum-1]; + outer = sect->sym; + if(outer == nil) { + werrstr("reference to invalid section %s/%s", sect->segname, sect->name); + continue; + } + s->type = outer->type | SSUB; + s->sub = outer->sub; + outer->sub = s; + s->outer = outer; + s->value = sym->value - sect->addr; + if(i+1 < symtab->nsym) + s->size = (sym+1)->value - sym->value; + else + s->size = sect->addr + sect->size - sym->value; + if(!s->dynexport) { + s->dynimplib = nil; // satisfy dynimport + s->dynimpname = nil; // satisfy dynimport + } + if(outer->type == STEXT) { + Prog *p; + + if(s->text != P) + diag("%s sym#%d: duplicate definition of %s", pn, i, s->name); + // build a TEXT instruction with a unique pc + // just to make the rest of the linker happy. + // TODO: this is too 6l-specific ? + p = prg(); + p->as = ATEXT; + p->from.type = D_EXTERN; + p->from.sym = s; + p->textflag = 7; + p->to.type = D_CONST; + p->link = nil; + p->pc = pc++; + s->text = p; + + etextp->next = s; + etextp = s; + } + sym->sym = s; + } + + // load relocations + for(i=0; i<c->seg.nsect; i++) { + sect = &c->seg.sect[i]; + if((s = sect->sym) == S) + continue; + macholoadrel(m, sect); + if(sect->rel == nil) + continue; + r = mal(sect->nreloc*sizeof r[0]); + rp = r; + rel = sect->rel; + for(j=0; j<sect->nreloc; j++, rel++) { + if(rel->scattered) { + int k; + MachoSect *ks; + + if(thechar != '8') + diag("unexpected scattered relocation"); + + // on 386, rewrite scattered 4/1 relocation into + // the pseudo-pc-relative reference that it is. + // assume that the second in the pair is in this section + // and use that as the pc-relative base. + if(thechar != '8' || rel->type != 4 || j+1 >= sect->nreloc || + !(rel+1)->scattered || (rel+1)->type != 1 || + (rel+1)->value < sect->addr || (rel+1)->value >= sect->addr+sect->size) { + werrstr("unsupported scattered relocation %d/%d", (int)rel->type, (int)(rel+1)->type); + goto bad; + } + rp->siz = rel->length; + rp->off = rel->addr; + + // NOTE(rsc): I haven't worked out why (really when) + // we should ignore the addend on a + // scattered relocation, but it seems that the + // common case is we ignore it. + // It's likely that this is not strictly correct + // and that the math should look something + // like the non-scattered case below. + rp->add = 0; + + // want to make it pc-relative aka relative to rp->off+4 + // but the scatter asks for relative to off = (rel+1)->value - sect->addr. + // adjust rp->add accordingly. + rp->type = D_PCREL; + rp->add += (rp->off+4) - ((rel+1)->value - sect->addr); + + // now consider the desired symbol. + // find the section where it lives. + for(k=0; k<c->seg.nsect; k++) { + ks = &c->seg.sect[k]; + if(ks->addr <= rel->value && rel->value < ks->addr+ks->size) + goto foundk; + } + werrstr("unsupported scattered relocation: invalid address %#ux", rel->addr); + goto bad; + foundk: + if(ks->sym != S) { + rp->sym = ks->sym; + rp->add += rel->value - ks->addr; + } else if(strcmp(ks->segname, "__IMPORT") == 0 && strcmp(ks->name, "__pointers") == 0) { + // handle reference to __IMPORT/__pointers. + // how much worse can this get? + // why are we supporting 386 on the mac anyway? + rp->type = 512 + MACHO_FAKE_GOTPCREL; + // figure out which pointer this is a reference to. + k = ks->res1 + (rel->value - ks->addr) / 4; + // load indirect table for __pointers + // fetch symbol number + if(dsymtab == nil || k < 0 || k >= dsymtab->nindirectsyms || dsymtab->indir == nil) { + werrstr("invalid scattered relocation: indirect symbol reference out of range"); + goto bad; + } + k = dsymtab->indir[k]; + if(k < 0 || k >= symtab->nsym) { + werrstr("invalid scattered relocation: symbol reference out of range"); + goto bad; + } + rp->sym = symtab->sym[k].sym; + } else { + werrstr("unsupported scattered relocation: reference to %s/%s", ks->segname, ks->name); + goto bad; + } + rp++; + // skip #1 of 2 rel; continue skips #2 of 2. + rel++; + j++; + continue; + } + + rp->siz = rel->length; + rp->type = 512 + (rel->type<<1) + rel->pcrel; + rp->off = rel->addr; + + rp->add = e->e32(s->p+rp->off); + // For i386 Mach-O PC-relative, the addend is written such that + // it *is* the PC being subtracted. Use that to make + // it match our version of PC-relative. + if(rel->pcrel && thechar == '8') + rp->add += rp->off+rp->siz; + if(!rel->extrn) { + if(rel->symnum < 1 || rel->symnum > c->seg.nsect) { + werrstr("invalid relocation: section reference out of range %d vs %d", rel->symnum, c->seg.nsect); + goto bad; + } + rp->sym = c->seg.sect[rel->symnum-1].sym; + if(rp->sym == nil) { + werrstr("invalid relocation: %s", c->seg.sect[rel->symnum-1].name); + goto bad; + } + // References to symbols in other sections + // include that information in the addend. + // We only care about the delta from the + // section base. + if(thechar == '8') + rp->add -= c->seg.sect[rel->symnum-1].addr; + } else { + if(rel->symnum >= symtab->nsym) { + werrstr("invalid relocation: symbol reference out of range"); + goto bad; + } + rp->sym = symtab->sym[rel->symnum].sym; + } + rp++; + } + qsort(r, rp - r, sizeof r[0], rbyoff); + s->r = r; + s->nr = rp - r; + } + return; + +bad: + diag("%s: malformed mach-o file: %r", pn); +} diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 1af9f7a41..ae77247c3 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -1,5 +1,6 @@ -// Derived from Inferno utils/6l/obj.c +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c // http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c // // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) @@ -118,7 +119,7 @@ addlib(char *src, char *obj) } for(; i<histfrogp; i++) { - snprint(comp, sizeof comp, histfrog[i]->name+1); + snprint(comp, sizeof comp, "%s", histfrog[i]->name+1); for(;;) { p = strstr(comp, "$O"); if(p == 0) @@ -146,6 +147,22 @@ addlib(char *src, char *obj) strcat(name, comp); } cleanname(name); + + // runtime.a -> runtime + p = nil; + if(strlen(name) > 2 && name[strlen(name)-2] == '.') { + p = name+strlen(name)-2; + *p = '\0'; + } + + // already loaded? + for(i=0; i<libraryp; i++) + if(strcmp(library[i].pkg, name) == 0) + return; + + // runtime -> runtime.a for search + if(p != nil) + *p = '.'; if(search) { // try dot, -L "libdir", and then goroot. @@ -159,8 +176,8 @@ addlib(char *src, char *obj) cleanname(pname); /* runtime.a -> runtime */ - if(strlen(name) > 2 && name[strlen(name)-2] == '.') - name[strlen(name)-2] = '\0'; + if(p != nil) + *p = '\0'; if(debug['v']) Bprint(&bso, "%5.2f addlib: %s %s pulls in %s\n", cputime(), obj, src, pname); @@ -186,9 +203,9 @@ addlibpath(char *srcref, char *objref, char *file, char *pkg) if(strcmp(file, library[i].file) == 0) return; - if(debug['v']) + if(debug['v'] > 1) Bprint(&bso, "%5.2f addlibpath: srcref: %s objref: %s file: %s pkg: %s\n", - cputime(), srcref, objref, file, pkg); + cputime(), srcref, objref, file, pkg); if(libraryp == nlibrary){ nlibrary = 50 + 2*libraryp; @@ -219,8 +236,6 @@ loadlib(void) { char pname[1024]; int i, found; - int32 h; - Sym *s; found = 0; for(i=0; i<nlibdir; i++) { @@ -233,47 +248,51 @@ loadlib(void) break; } } - if(!found) Bprint(&bso, "warning: unable to find runtime.a\n"); + if(!found) + Bprint(&bso, "warning: unable to find runtime.a\n"); -loop: - xrefresolv = 0; for(i=0; i<libraryp; i++) { if(debug['v']) Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i].file, library[i].objref); objfile(library[i].file, library[i].pkg); } +} - if(xrefresolv) - for(h=0; h<nelem(hash); h++) - for(s = hash[h]; s != S; s = s->link) - if(s->type == SXREF) - goto loop; - +/* + * look for the next file in an archive. + * adapted from libmach. + */ +int +nextar(Biobuf *bp, int off, struct ar_hdr *a) +{ + int r; + int32 arsize; + + if (off&01) + off++; + Bseek(bp, off, 0); + r = Bread(bp, a, SAR_HDR); + if(r != SAR_HDR) + return 0; + if(strncmp(a->fmag, ARFMAG, sizeof(a->fmag))) + return -1; + arsize = strtol(a->size, 0, 0); + if (arsize&1) + arsize++; + return arsize + SAR_HDR; } void objfile(char *file, char *pkg) { - int32 off, esym, cnt, l; - int work; + int32 off, l; Biobuf *f; - Sym *s; char magbuf[SARMAG]; - char name[100], pname[150]; + char pname[150]; struct ar_hdr arhdr; - char *e, *start, *stop, *x; pkg = smprint("%i", pkg); - if(file[0] == '-' && file[1] == 'l') { // TODO: fix this - 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 (%s)\n", cputime(), file, pkg); Bflush(&bso); @@ -291,9 +310,10 @@ objfile(char *file, char *pkg) Bterm(f); return; } - - l = Bread(f, &arhdr, SAR_HDR); - if(l != SAR_HDR) { + + /* skip over __.SYMDEF */ + off = Boffset(f); + if((l = nextar(f, off, &arhdr)) <= 0) { diag("%s: short read on archive file symbol header", file); goto out; } @@ -301,88 +321,52 @@ objfile(char *file, char *pkg) diag("%s: first entry not symbol header", file); goto out; } - - esym = SARMAG + SAR_HDR + atolwhex(arhdr.size); - off = SARMAG + SAR_HDR; - - if(debug['u']) { - struct ar_hdr pkghdr; - int n; - - // Read next ar header to check for package safe bit. - Bseek(f, esym+(esym&1), 0); - l = Bread(f, &pkghdr, SAR_HDR); - if(l != SAR_HDR) { - diag("%s: short read on second archive header", file); - goto out; - } - if(strncmp(pkghdr.name, pkgname, strlen(pkgname))) { - diag("%s: second entry not package header", file); - goto out; - } - n = atolwhex(pkghdr.size); - ldpkg(f, pkg, n, file, Pkgdef); + off += l; + + /* skip over (or process) __.PKGDEF */ + if((l = nextar(f, off, &arhdr)) <= 0) { + diag("%s: short read on archive file symbol header", file); + goto out; } + if(strncmp(arhdr.name, pkgname, strlen(pkgname))) { + diag("%s: second entry not package header", file); + goto out; + } + off += l; + + if(debug['u']) + ldpkg(f, pkg, atolwhex(arhdr.size), file, Pkgdef); /* - * just bang the whole symbol file into memory + * load all the object files from the archive now. + * this gives us sequential file access and keeps us + * from needing to come back later to pick up more + * objects. it breaks the usual C archive model, but + * this is Go, not C. the common case in Go is that + * we need to load all the objects, and then we throw away + * the individual symbols that are unused. + * + * loading every object will also make it possible to + * load foreign objects not referenced by __.SYMDEF. */ - Bseek(f, off, 0); - cnt = esym - off; - start = mal(cnt + 10); - cnt = Bread(f, start, cnt); - if(cnt <= 0){ - Bterm(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) { - x = expandpkg(e+5, pkg); - s = lookup(x, 0); - if(x != e+5) - free(x); - 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; - Bseek(f, l, 0); - l = Bread(f, &arhdr, SAR_HDR); - if(l != SAR_HDR) - goto bad; - if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag))) - goto bad; - l = SARNAME; - while(l > 0 && arhdr.name[l-1] == ' ') - l--; - sprint(pname, "%s(%.*s)", file, l, arhdr.name); - l = atolwhex(arhdr.size); - ldobj(f, pkg, l, pname, ArchiveObj); - if(s->type == SXREF) { - diag("%s: failed to load: %s", file, s->name); - errorexit(); - } - work = 1; - xrefresolv = 1; + for(;;) { + l = nextar(f, off, &arhdr); + if(l == 0) + break; + if(l < 0) { + diag("%s: malformed archive", file); + goto out; } + off += l; + + l = SARNAME; + while(l > 0 && arhdr.name[l-1] == ' ') + l--; + snprint(pname, sizeof pname, "%s(%.*s)", file, utfnlen(arhdr.name, l), arhdr.name); + l = atolwhex(arhdr.size); + ldobj(f, pkg, l, pname, ArchiveObj); } - return; -bad: - diag("%s: bad or out of date archive", file); out: Bterm(f); } @@ -390,32 +374,38 @@ out: void ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) { - static int files; - static char **filen; - char **nfilen, *line; - int i, n, c1, c2, c3; + char *line; + int n, c1, c2, c3, c4; + uint32 magic; vlong import0, import1, eof; char src[1024]; eof = Boffset(f) + len; src[0] = '\0'; - // don't load individual object more than once. - // happens with import of .6 files because of loop in xresolv. - // doesn't happen with .a because SYMDEF is consulted - // first to decide whether each individual object file is needed. - for(i=0; i<files; i++) - if(strcmp(filen[i], pn) == 0) - return; + pn = strdup(pn); + + USED(c4); + USED(magic); - if((files&15) == 0){ - nfilen = malloc((files+16)*sizeof(char*)); - memmove(nfilen, filen, files*sizeof(char*)); - free(filen); - filen = nfilen; + c1 = Bgetc(f); + c2 = Bgetc(f); + c3 = Bgetc(f); + c4 = Bgetc(f); + Bungetc(f); + Bungetc(f); + Bungetc(f); + Bungetc(f); + + magic = c1<<24 | c2<<16 | c3<<8 | c4; + if(magic == 0x7f454c46) { // \x7F E L F + ldelf(f, pkg, len, pn); + return; + } + if((magic&~1) == 0xfeedface || (magic&~0x01000000) == 0xcefaedfe) { + ldmacho(f, pkg, len, pn); + return; } - pn = strdup(pn); - filen[files++] = pn; /* check the header */ line = Brdline(f, '\n'); @@ -478,7 +468,7 @@ lookup(char *symb, int v) // not if(h < 0) h = ~h, because gcc 4.3 -O2 miscompiles it. h &= 0xffffff; h %= NHASH; - for(s = hash[h]; s != S; s = s->link) + for(s = hash[h]; s != S; s = s->hash) if(s->version == v) if(memcmp(s->name, symb, l) == 0) return s; @@ -487,14 +477,18 @@ lookup(char *symb, int v) if(debug['v'] > 1) Bprint(&bso, "lookup %s\n", symb); + s->dynid = -1; + s->plt = -1; + s->got = -1; s->name = mal(l + 1); memmove(s->name, symb, l); - s->link = hash[h]; + s->hash = hash[h]; s->type = 0; s->version = v; s->value = 0; s->sig = 0; + s->size = 0; hash[h] = s; nsymbol++; return s; @@ -607,8 +601,13 @@ nuxiinit(void) if(i < 1) inuxi1[i] = c; inuxi4[i] = c; - inuxi8[i] = c; - inuxi8[i+4] = c+4; + if(c == i) { + inuxi8[i] = c; + inuxi8[i+4] = c+4; + } else { + inuxi8[i] = c+4; + inuxi8[i+4] = c; + } fnuxi4[i] = c; fnuxi8[i] = c; fnuxi8[i+4] = c+4; @@ -732,21 +731,6 @@ ieeedtod(Ieee *ieeep) } 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; @@ -755,47 +739,6 @@ zerosig(char *sp) 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); -} - int32 Bget4(Biobuf *f) { @@ -829,15 +772,22 @@ mal(uint32 n) { void *v; - while(n & 7) - n++; + n = (n+7)&~7; if(n > NHUNK) { v = malloc(n); + if(v == nil) { + diag("out of memory"); + errorexit(); + } memset(v, 0, n); return v; } if(n > nhunk) { hunk = malloc(NHUNK); + if(hunk == nil) { + diag("out of memory"); + errorexit(); + } nhunk = NHUNK; } @@ -849,6 +799,16 @@ mal(uint32 n) return v; } +void +unmal(void *v, uint32 n) +{ + n = (n+7)&~7; + if(hunk - n == v) { + hunk -= n; + nhunk += n; + } +} + // Copied from ../gc/subr.c:/^pathtoprefix; must stay in sync. /* * Convert raw string to the prefix that will be used in the symbol table. @@ -901,3 +861,211 @@ iconv(Fmt *fp) return 0; } +void +mangle(char *file) +{ + fprint(2, "%s: mangled input file\n", file); + errorexit(); +} + +Section* +addsection(Segment *seg, char *name, int rwx) +{ + Section **l; + Section *sect; + + for(l=&seg->sect; *l; l=&(*l)->next) + ; + sect = mal(sizeof *sect); + sect->rwx = rwx; + sect->name = name; + sect->seg = seg; + *l = sect; + return sect; +} + +void +ewrite(int fd, void *buf, int n) +{ + if(write(fd, buf, n) < 0) { + diag("write error: %r"); + errorexit(); + } +} + +void +pclntab(void) +{ + vlong oldpc; + Prog *p; + int32 oldlc, v, s; + Sym *sym; + uchar *bp; + + sym = lookup("pclntab", 0); + sym->type = SRODATA; + sym->reachable = 1; + if(debug['s']) + return; + + oldpc = INITTEXT; + oldlc = 0; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + for(p = cursym->text; p != P; p = p->link) { + if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) { + if(debug['O']) + Bprint(&bso, "%6llux %P\n", + (vlong)p->pc, p); + continue; + } + if(debug['O']) + Bprint(&bso, "\t\t%6d", lcsize); + v = (p->pc - oldpc) / MINLC; + while(v) { + s = 127; + if(v < 127) + s = v; + symgrow(sym, lcsize+1); + bp = sym->p + lcsize; + *bp = s+128; /* 129-255 +pc */ + if(debug['O']) + Bprint(&bso, " pc+%d*%d(%d)", s, MINLC, s+128); + v -= s; + lcsize++; + } + s = p->line - oldlc; + oldlc = p->line; + oldpc = p->pc + MINLC; + if(s > 64 || s < -64) { + symgrow(sym, lcsize+5); + bp = sym->p + lcsize; + *bp++ = 0; /* 0 vv +lc */ + *bp++ = s>>24; + *bp++ = s>>16; + *bp++ = s>>8; + *bp = s; + if(debug['O']) { + if(s > 0) + Bprint(&bso, " lc+%d(%d,%d)\n", + s, 0, s); + else + Bprint(&bso, " lc%d(%d,%d)\n", + s, 0, s); + Bprint(&bso, "%6llux %P\n", + (vlong)p->pc, p); + } + lcsize += 5; + continue; + } + symgrow(sym, lcsize+1); + bp = sym->p + lcsize; + if(s > 0) { + *bp = 0+s; /* 1-64 +lc */ + if(debug['O']) { + Bprint(&bso, " lc+%d(%d)\n", s, 0+s); + Bprint(&bso, "%6llux %P\n", + (vlong)p->pc, p); + } + } else { + *bp = 64-s; /* 65-128 -lc */ + if(debug['O']) { + Bprint(&bso, " lc%d(%d)\n", s, 64-s); + Bprint(&bso, "%6llux %P\n", + (vlong)p->pc, p); + } + } + lcsize++; + } + } + if(lcsize & 1) { + symgrow(sym, lcsize+1); + sym->p[lcsize] = 129; + lcsize++; + } + sym->size = lcsize; + lcsize = 0; + + if(debug['v'] || debug['O']) + Bprint(&bso, "lcsize = %d\n", lcsize); + Bflush(&bso); +} + +#define LOG 5 +void +mkfwd(void) +{ + Prog *p; + int i; + int32 dwn[LOG], cnt[LOG]; + Prog *lst[LOG], *last; + + for(i=0; i<LOG; i++) { + if(i == 0) + cnt[i] = 1; + else + cnt[i] = LOG * cnt[i-1]; + dwn[i] = 1; + lst[i] = P; + } + i = 0; + last = nil; + for(cursym = textp; cursym != nil; cursym = cursym->next) { + for(p = cursym->text; p != P; p = p->link) { + if(p->link == P) { + if(cursym->next) + p->forwd = cursym->next->text; + break; + } + i--; + if(i < 0) + i = LOG-1; + p->forwd = P; + dwn[i]--; + if(dwn[i] <= 0) { + dwn[i] = cnt[i]; + if(lst[i] != P) + lst[i]->forwd = p; + lst[i] = p; + } + } + } +} + +uint16 +le16(uchar *b) +{ + return b[0] | b[1]<<8; +} + +uint32 +le32(uchar *b) +{ + return b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24; +} + +uint64 +le64(uchar *b) +{ + return le32(b) | (uint64)le32(b+4)<<32; +} + +uint16 +be16(uchar *b) +{ + return b[0]<<8 | b[1]; +} + +uint32 +be32(uchar *b) +{ + return b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3]; +} + +uint64 +be64(uchar *b) +{ + return (uvlong)be32(b)<<32 | be32(b+4); +} + +Endian be = { be16, be32, be64 }; +Endian le = { le16, le32, le64 }; diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index 652d845fb..bcf297116 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -40,6 +40,34 @@ struct Library char *pkg; // import path }; +// Terrible but standard terminology. +// A segment describes a block of file to load into memory. +// A section further describes the pieces of that block for +// use in debuggers and such. + +typedef struct Segment Segment; +typedef struct Section Section; + +struct Segment +{ + uchar rwx; // permission as usual unix bits (5 = r-x etc) + uvlong vaddr; // virtual address + uvlong len; // length in memory + uvlong fileoff; // file offset + uvlong filelen; // length on disk + Section* sect; +}; + +struct Section +{ + uchar rwx; + char *name; + uvlong vaddr; + uvlong len; + Section *next; // in segment list + Segment *seg; +}; + extern char symname[]; extern char *libdir[]; extern int nlibdir; @@ -64,11 +92,18 @@ EXTERN uchar inuxi8[8]; EXTERN char* outfile; EXTERN int32 nsymbol; EXTERN char* thestring; +EXTERN int ndynexp; + +EXTERN Segment segtext; +EXTERN Segment segdata; +EXTERN Segment segsym; void addlib(char *src, char *obj); void addlibpath(char *srcref, char *objref, char *file, char *pkg); +Section* addsection(Segment*, char*, int); void copyhistfrog(char *buf, int nbuf); void addhist(int32 line, int type); +void asmlc(void); void histtoauto(void); void collapsefrog(Sym *s); Sym* lookup(char *symb, int v); @@ -83,20 +118,82 @@ void readundefs(char *f, int t); int32 Bget4(Biobuf *f); void loadlib(void); void errorexit(void); +void mangle(char*); void objfile(char *file, char *pkg); void libinit(void); +void pclntab(void); +void symtab(void); void Lflag(char *arg); void usage(void); +void adddynrel(Sym*, Reloc*); void ldobj1(Biobuf *f, char*, int64 len, char *pn); void ldobj(Biobuf*, char*, int64, char*, int); +void ldelf(Biobuf*, char*, int64, char*); +void ldmacho(Biobuf*, char*, int64, char*); void ldpkg(Biobuf*, char*, int64, char*, int); void mark(Sym *s); +void mkfwd(void); char* expandpkg(char*, char*); void deadcode(void); +void ewrite(int, void*, int); +Reloc* addrel(Sym*); +void codeblk(int32, int32); +void datblk(int32, int32); +Sym* datsort(Sym*); +void reloc(void); +void relocsym(Sym*); +void savedata(Sym*, Prog*); +void symgrow(Sym*, int32); +vlong addstring(Sym*, char*); +vlong adduint32(Sym*, uint32); +vlong adduint64(Sym*, uint64); +vlong addaddr(Sym*, Sym*); +vlong addaddrplus(Sym*, Sym*, int32); +vlong addpcrelplus(Sym*, Sym*, int32); +vlong addsize(Sym*, Sym*); +vlong adduint8(Sym*, uint8); +vlong adduint16(Sym*, uint16); +void asmsym(void); +void asmelfsym64(void); +void strnput(char*, int); +void dodata(void); +void address(void); +void textaddress(void); +void genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)); +vlong datoff(vlong); +void adddynlib(char*); +int archreloc(Reloc*, Sym*, vlong*); +void adddynsym(Sym*); +void addexport(void); int pathchar(void); void* mal(uint32); +void unmal(void*, uint32); void mywhatsys(void); +int rbyoff(const void*, const void*); + +uint16 le16(uchar*); +uint32 le32(uchar*); +uint64 le64(uchar*); +uint16 be16(uchar*); +uint32 be32(uchar*); +uint64 be64(uchar*); + +typedef struct Endian Endian; +struct Endian +{ + uint16 (*e16)(uchar*); + uint32 (*e32)(uchar*); + uint64 (*e64)(uchar*); +}; + +extern Endian be, le; + +// relocation size bits +enum { + Rbig = 128, + Rlittle = 64, +}; /* set by call to mywhatsys() */ extern char* goroot; diff --git a/src/cmd/ld/macho.c b/src/cmd/ld/macho.c index 24400cf14..402e0ec63 100644 --- a/src/cmd/ld/macho.c +++ b/src/cmd/ld/macho.c @@ -6,6 +6,7 @@ // http://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html #include "l.h" +#include "../ld/dwarf.h" #include "../ld/lib.h" #include "../ld/macho.h" @@ -46,6 +47,10 @@ newMachoLoad(uint32 type, uint32 ndata) diag("too many loads"); errorexit(); } + + if(macho64 && (ndata & 1)) + ndata++; + l = &load[nload++]; l->type = type; l->ndata = ndata; @@ -97,22 +102,6 @@ newMachoDebug(void) // Generic linking code. -static uchar *linkdata; -static uint32 nlinkdata; -static uint32 mlinkdata; - -static uchar *strtab; -static uint32 nstrtab; -static uint32 mstrtab; - -struct Expsym -{ - int off; - Sym* s; -} *expsym; -static int nexpsym; -static int nimpsym; - static char **dylib; static int ndylib; @@ -129,7 +118,7 @@ machowrite(void) MachoDebug *d; MachoLoad *l; - o1 = Boffset(&bso); + o1 = cpos(); loadsize = 4*4*ndebug; for(i=0; i<nload; i++) @@ -194,8 +183,8 @@ machowrite(void) LPUT(t->reloc); LPUT(t->nreloc); LPUT(t->flag); - LPUT(0); /* reserved */ - LPUT(0); /* reserved */ + LPUT(t->res1); /* reserved */ + LPUT(t->res2); /* reserved */ LPUT(0); /* reserved */ } else { strnput(t->name, 16); @@ -207,8 +196,8 @@ machowrite(void) LPUT(t->reloc); LPUT(t->nreloc); LPUT(t->flag); - LPUT(0); /* reserved */ - LPUT(0); /* reserved */ + LPUT(t->res1); /* reserved */ + LPUT(t->res2); /* reserved */ } } } @@ -229,224 +218,71 @@ machowrite(void) LPUT(d->filesize); } - return Boffset(&bso) - o1; -} - -static void* -grow(uchar **dat, uint32 *ndat, uint32 *mdat, uint32 n) -{ - uchar *p; - uint32 old; - - if(*ndat+n > *mdat) { - old = *mdat; - *mdat = (*ndat+n)*2 + 128; - *dat = realloc(*dat, *mdat); - if(*dat == 0) { - diag("out of memory"); - errorexit(); - } - memset(*dat+old, 0, *mdat-old); - } - p = *dat + *ndat; - *ndat += n; - return p; -} - -static int -needlib(char *name) -{ - char *p; - Sym *s; - - /* reuse hash code in symbol table */ - p = smprint(".machoload.%s", name); - s = lookup(p, 0); - if(s->type == 0) { - s->type = 100; // avoid SDATA, etc. - return 1; - } - return 0; + return cpos() - o1; } void domacho(void) { - int h, ptrsize, t; - char *p; - uchar *dat; - uint32 x; Sym *s; - Sym **impsym; - ptrsize = 4; - if(macho64) - ptrsize = 8; + if(debug['d']) + return; // empirically, string table must begin with " \x00". - if(!debug['d']) - *(char*)grow(&strtab, &nstrtab, &mstrtab, 2) = ' '; - - impsym = nil; - for(h=0; h<NHASH; h++) { - for(s=hash[h]; s!=S; s=s->link) { - if(!s->reachable || (s->type != STEXT && s->type != SDATA && s->type != SBSS) || s->dynimpname == nil) - continue; - if(debug['d']) { - diag("cannot use dynamic loading and -d"); - errorexit(); - } - if(!s->dynexport) { - if(nimpsym%32 == 0) { - impsym = realloc(impsym, (nimpsym+32)*sizeof impsym[0]); - if(impsym == nil) { - diag("out of memory"); - errorexit(); - } - } - impsym[nimpsym++] = s; - continue; - } - - /* symbol table entry - darwin still puts _ prefixes on all C symbols */ - x = nstrtab; - p = grow(&strtab, &nstrtab, &mstrtab, 1+strlen(s->dynimpname)+1); - *p++ = '_'; - strcpy(p, s->dynimpname); - - dat = grow(&linkdata, &nlinkdata, &mlinkdata, 8+ptrsize); - dat[0] = x; - dat[1] = x>>8; - dat[2] = x>>16; - dat[3] = x>>24; - - dat[4] = 0x0f; // type: N_SECT | N_EXT - external, defined in sect - switch(s->type) { - default: - case STEXT: - t = 1; - break; - case SDATA: - t = 2; - break; - case SBSS: - t = 4; - break; - } - dat[5] = t; // sect: section number - - if (nexpsym%32 == 0) { - expsym = realloc(expsym, (nexpsym+32)*sizeof expsym[0]); - if (expsym == nil) { - diag("out of memory"); - errorexit(); - } - } - expsym[nexpsym].off = nlinkdata - ptrsize; - expsym[nexpsym++].s = s; - } - } - - for(h=0; h<nimpsym; h++) { - s = impsym[h]; - s->type = SMACHO; - s->value = (nexpsym+h) * ptrsize; - - /* symbol table entry - darwin still puts _ prefixes on all C symbols */ - x = nstrtab; - p = grow(&strtab, &nstrtab, &mstrtab, 1+strlen(s->dynimpname)+1); - *p++ = '_'; - strcpy(p, s->dynimpname); - - dat = grow(&linkdata, &nlinkdata, &mlinkdata, 8+ptrsize); - dat[0] = x; - dat[1] = x>>8; - dat[2] = x>>16; - dat[3] = x>>24; - - dat[4] = 0x01; // type: N_EXT - external symbol - - if(needlib(s->dynimplib)) { - if(ndylib%32 == 0) { - dylib = realloc(dylib, (ndylib+32)*sizeof dylib[0]); - if(dylib == nil) { - diag("out of memory"); - errorexit(); - } - } - dylib[ndylib++] = s->dynimplib; - } - } - free(impsym); - - /* - * list of symbol table indexes. - * we don't take advantage of the opportunity - * to order the symbol table differently from - * this list, so it is boring: 0 1 2 3 4 ... - */ - for(x=0; x<nexpsym+nimpsym; x++) { - dat = grow(&linkdata, &nlinkdata, &mlinkdata, 4); - dat[0] = x; - dat[1] = x>>8; - dat[2] = x>>16; - dat[3] = x>>24; - } - - dynptrsize = (nexpsym+nimpsym) * ptrsize; + s = lookup(".dynstr", 0); + s->type = SMACHODYNSTR; + s->reachable = 1; + adduint8(s, ' '); + adduint8(s, '\0'); + + s = lookup(".dynsym", 0); + s->type = SMACHODYNSYM; + s->reachable = 1; + + s = lookup(".plt", 0); // will be __symbol_stub + s->type = SMACHOPLT; + s->reachable = 1; + + s = lookup(".got", 0); // will be __nl_symbol_ptr + s->type = SMACHOGOT; + s->reachable = 1; + + s = lookup(".linkedit.plt", 0); // indirect table for .plt + s->type = SMACHOINDIRECTPLT; + s->reachable = 1; + + s = lookup(".linkedit.got", 0); // indirect table for .got + s->type = SMACHOINDIRECTGOT; + s->reachable = 1; } -vlong -domacholink(void) +void +machoadddynlib(char *lib) { - int i; - uchar *p; - Sym *s; - uint64 val; - - linkoff = 0; - if(nlinkdata > 0) { - linkoff = rnd(HEADR+textsize, INITRND) + rnd(datsize, INITRND); - seek(cout, linkoff, 0); - - for(i = 0; i<nexpsym; ++i) { - s = expsym[i].s; - val = s->value; - if(s->type == SUNDEF) - diag("export of undefined symbol %s", s->name); - if (s->type != STEXT) - val += INITDAT; - p = linkdata+expsym[i].off; - p[0] = val; - p[1] = val >> 8; - p[2] = val >> 16; - p[3] = val >> 24; - if (macho64) { - p[4] = val >> 32; - p[5] = val >> 40; - p[6] = val >> 48; - p[7] = val >> 56; - } + if(ndylib%32 == 0) { + dylib = realloc(dylib, (ndylib+32)*sizeof dylib[0]); + if(dylib == nil) { + diag("out of memory"); + errorexit(); } - - write(cout, linkdata, nlinkdata); - write(cout, strtab, nstrtab); } - return rnd(nlinkdata+nstrtab, INITRND); + dylib[ndylib++] = lib; } void -asmbmacho(vlong symdatva, vlong symo) +asmbmacho(void) { vlong v, w; vlong va; - int a, i, ptrsize; + int a, i; char *pkgroot; MachoHdr *mh; MachoSect *msect; MachoSeg *ms; MachoDebug *md; MachoLoad *ml; + Sym *s; /* apple MACH */ va = INITTEXT - HEADR; @@ -458,12 +294,10 @@ asmbmacho(vlong symdatva, vlong symo) case '6': mh->cpu = MACHO_CPU_AMD64; mh->subcpu = MACHO_SUBCPU_X86; - ptrsize = 8; break; case '8': mh->cpu = MACHO_CPU_386; mh->subcpu = MACHO_SUBCPU_X86; - ptrsize = 4; break; } @@ -472,8 +306,8 @@ asmbmacho(vlong symdatva, vlong symo) ms->vsize = va; /* text */ - v = rnd(HEADR+textsize, INITRND); - ms = newMachoSeg("__TEXT", 1); + v = rnd(HEADR+segtext.len, INITRND); + ms = newMachoSeg("__TEXT", 2); ms->vaddr = va; ms->vsize = v; ms->filesize = v; @@ -482,45 +316,50 @@ asmbmacho(vlong symdatva, vlong symo) msect = newMachoSect(ms, "__text"); msect->addr = INITTEXT; - msect->size = textsize; + msect->size = segtext.sect->len; msect->off = INITTEXT - va; msect->flag = 0x400; /* flag - some instructions */ + + s = lookup(".plt", 0); + if(s->size > 0) { + msect = newMachoSect(ms, "__symbol_stub1"); + msect->addr = symaddr(s); + msect->size = s->size; + msect->off = ms->fileoffset + msect->addr - ms->vaddr; + msect->flag = 0x80000408; /* flag */ + msect->res1 = 0; /* index into indirect symbol table */ + msect->res2 = 6; /* size of stubs */ + } /* data */ - w = datsize+dynptrsize+bsssize; - ms = newMachoSeg("__DATA", 2+(dynptrsize>0)); + w = segdata.len; + ms = newMachoSeg("__DATA", 3); ms->vaddr = va+v; ms->vsize = w; ms->fileoffset = v; - ms->filesize = datsize; + ms->filesize = segdata.filelen; ms->prot1 = 7; ms->prot2 = 3; msect = newMachoSect(ms, "__data"); msect->addr = va+v; - msect->size = datsize; + msect->size = symaddr(lookup(".got", 0)) - msect->addr; msect->off = v; - if(dynptrsize > 0) { + s = lookup(".got", 0); + if(s->size > 0) { msect = newMachoSect(ms, "__nl_symbol_ptr"); - msect->addr = va+v+datsize; - msect->size = dynptrsize; + msect->addr = symaddr(s); + msect->size = s->size; + msect->off = datoff(msect->addr); msect->align = 2; msect->flag = 6; /* section with nonlazy symbol pointers */ - /* - * The reserved1 field is supposed to be the index of - * the first entry in the list of symbol table indexes - * in isymtab for the symbols we need. We only use - * pointers, so we need the entire list, so the index - * here should be 0, which luckily is what the Mach-O - * writing code emits by default for this not really reserved field. - msect->reserved1 = 0; - first indirect symbol table entry we need - */ + msect->res1 = lookup(".linkedit.plt", 0)->size / 4; /* offset into indirect symbol table */ } msect = newMachoSect(ms, "__bss"); - msect->addr = va+v+datsize+dynptrsize; - msect->size = bsssize; + msect->addr = va+v+segdata.filelen; + msect->size = segdata.len - segdata.filelen; msect->flag = 1; /* flag - zero fill */ switch(thechar) { @@ -532,7 +371,7 @@ asmbmacho(vlong symdatva, vlong symo) ml->data[0] = 4; /* thread type */ ml->data[1] = 42; /* word count */ ml->data[2+32] = entryvalue(); /* start pc */ - ml->data[2+32+1] = entryvalue()>>32; + ml->data[2+32+1] = entryvalue()>>16>>16; // hide >>32 for 8l break; case '8': ml = newMachoLoad(5, 16+2); /* unix thread */ @@ -543,39 +382,43 @@ asmbmacho(vlong symdatva, vlong symo) } if(!debug['d']) { - int nsym; + Sym *s1, *s2, *s3, *s4; - nsym = dynptrsize/ptrsize; + // must match domacholink below + s1 = lookup(".dynsym", 0); + s2 = lookup(".dynstr", 0); + s3 = lookup(".linkedit.plt", 0); + s4 = lookup(".linkedit.got", 0); ms = newMachoSeg("__LINKEDIT", 0); - ms->vaddr = va+v+rnd(datsize+dynptrsize+bsssize, INITRND); - ms->vsize = nlinkdata+nstrtab; + ms->vaddr = va+v+rnd(segdata.len, INITRND); + ms->vsize = s1->size + s2->size + s3->size + s4->size; ms->fileoffset = linkoff; - ms->filesize = nlinkdata+nstrtab; + ms->filesize = ms->vsize; ms->prot1 = 7; ms->prot2 = 3; ml = newMachoLoad(2, 4); /* LC_SYMTAB */ ml->data[0] = linkoff; /* symoff */ - ml->data[1] = nsym; /* nsyms */ - ml->data[2] = linkoff + nlinkdata; /* stroff */ - ml->data[3] = nstrtab; /* strsize */ + ml->data[1] = s1->size / (macho64 ? 16 : 12); /* nsyms */ + ml->data[2] = linkoff + s1->size; /* stroff */ + ml->data[3] = s2->size; /* strsize */ ml = newMachoLoad(11, 18); /* LC_DYSYMTAB */ ml->data[0] = 0; /* ilocalsym */ ml->data[1] = 0; /* nlocalsym */ ml->data[2] = 0; /* iextdefsym */ - ml->data[3] = nexpsym; /* nextdefsym */ - ml->data[4] = nexpsym; /* iundefsym */ - ml->data[5] = nimpsym; /* nundefsym */ + ml->data[3] = ndynexp; /* nextdefsym */ + ml->data[4] = ndynexp; /* iundefsym */ + ml->data[5] = (s1->size / (macho64 ? 16 : 12)) - ndynexp; /* nundefsym */ ml->data[6] = 0; /* tocoffset */ ml->data[7] = 0; /* ntoc */ ml->data[8] = 0; /* modtaboff */ ml->data[9] = 0; /* nmodtab */ ml->data[10] = 0; /* extrefsymoff */ ml->data[11] = 0; /* nextrefsyms */ - ml->data[12] = linkoff + nlinkdata - nsym*4; /* indirectsymoff */ - ml->data[13] = nsym; /* nindirectsyms */ + ml->data[12] = linkoff + s1->size + s2->size; /* indirectsymoff */ + ml->data[13] = (s3->size + s4->size) / 4; /* nindirectsyms */ ml->data[14] = 0; /* extreloff */ ml->data[15] = 0; /* nextrel */ ml->data[16] = 0; /* locreloff */ @@ -602,24 +445,53 @@ asmbmacho(vlong symdatva, vlong symo) } if(!debug['s']) { - ms = newMachoSeg("__SYMDAT", 1); - ms->vaddr = symdatva; - ms->vsize = 8+symsize+lcsize; - ms->fileoffset = symo; - ms->filesize = 8+symsize+lcsize; - ms->prot1 = 7; - ms->prot2 = 5; + Sym *s; md = newMachoDebug(); - md->fileoffset = symo+8; - md->filesize = symsize; + s = lookup("symtab", 0); + md->fileoffset = datoff(s->value); + md->filesize = s->size; md = newMachoDebug(); - md->fileoffset = symo+8+symsize; - md->filesize = lcsize; + s = lookup("pclntab", 0); + md->fileoffset = datoff(s->value); + md->filesize = s->size; + + dwarfaddmachoheaders(); } a = machowrite(); if(a > MACHORESERVE) diag("MACHORESERVE too small: %d > %d", a, MACHORESERVE); } + +vlong +domacholink(void) +{ + int size; + Sym *s1, *s2, *s3, *s4; + + // write data that will be linkedit section + s1 = lookup(".dynsym", 0); + relocsym(s1); + s2 = lookup(".dynstr", 0); + s3 = lookup(".linkedit.plt", 0); + s4 = lookup(".linkedit.got", 0); + + while(s2->size%4) + adduint8(s2, 0); + + size = s1->size + s2->size + s3->size + s4->size; + + if(size > 0) { + linkoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND); + seek(cout, linkoff, 0); + + ewrite(cout, s1->p, s1->size); + ewrite(cout, s2->p, s2->size); + ewrite(cout, s3->p, s3->size); + ewrite(cout, s4->p, s4->size); + } + + return rnd(size, INITRND); +} diff --git a/src/cmd/ld/macho.h b/src/cmd/ld/macho.h index a96b2a383..4cc7edc80 100644 --- a/src/cmd/ld/macho.h +++ b/src/cmd/ld/macho.h @@ -18,6 +18,8 @@ struct MachoSect { uint32 reloc; uint32 nreloc; uint32 flag; + uint32 res1; + uint32 res2; }; typedef struct MachoSeg MachoSeg; @@ -70,8 +72,23 @@ enum { MACHO32SYMSIZE = 12, MACHO64SYMSIZE = 16, + + MACHO_X86_64_RELOC_UNSIGNED = 0, + MACHO_X86_64_RELOC_SIGNED = 1, + MACHO_X86_64_RELOC_BRANCH = 2, + MACHO_X86_64_RELOC_GOT_LOAD = 3, + MACHO_X86_64_RELOC_GOT = 4, + MACHO_X86_64_RELOC_SUBTRACTOR = 5, + MACHO_X86_64_RELOC_SIGNED_1 = 6, + MACHO_X86_64_RELOC_SIGNED_2 = 7, + MACHO_X86_64_RELOC_SIGNED_4 = 8, + + MACHO_GENERIC_RELOC_VANILLA = 0, + + MACHO_FAKE_GOTPCREL = 100, }; void domacho(void); vlong domacholink(void); -void asmbmacho(vlong, vlong); +void asmbmacho(void); +void machoadddynlib(char*); diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c index d3439abfb..82c6941f2 100644 --- a/src/cmd/ld/pe.c +++ b/src/cmd/ld/pe.c @@ -33,18 +33,39 @@ static char dosstub[] = 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +int32 PESECTHEADR; +int32 PEFILEHEADR; + static int pe64; static int nsect; -static int sect_virt_begin; -static int sect_raw_begin = PERESERVE; +static int nextsectoff; +static int nextfileoff; static IMAGE_FILE_HEADER fh; static IMAGE_OPTIONAL_HEADER oh; static IMAGE_SECTION_HEADER sh[16]; -static IMAGE_SECTION_HEADER *textsect, *datsect, *bsssect; + +typedef struct Imp Imp; +struct Imp { + Sym* s; + long va; + long vb; + Imp* next; +}; + +typedef struct Dll Dll; +struct Dll { + char* name; + int count; + Imp* ms; + Dll* next; +}; + +static Dll* dr; +static int ndll, nimp, nsize; static IMAGE_SECTION_HEADER* -new_section(char *name, int size, int noraw) +addpesection(char *name, int sectsize, int filesize, Segment *s) { IMAGE_SECTION_HEADER *h; @@ -54,15 +75,23 @@ new_section(char *name, int size, int noraw) } h = &sh[nsect++]; strncpy((char*)h->Name, name, sizeof(h->Name)); - h->VirtualSize = size; - if(!sect_virt_begin) - sect_virt_begin = 0x1000; - h->VirtualAddress = sect_virt_begin; - sect_virt_begin = rnd(sect_virt_begin+size, 0x1000); - if(!noraw) { - h->SizeOfRawData = rnd(size, PEALIGN); - h->PointerToRawData = sect_raw_begin; - sect_raw_begin += h->SizeOfRawData; + h->VirtualSize = sectsize; + h->VirtualAddress = nextsectoff; + nextsectoff = rnd(nextsectoff+sectsize, PESECTALIGN); + h->PointerToRawData = nextfileoff; + if(filesize > 0) { + h->SizeOfRawData = rnd(filesize, PEFILEALIGN); + nextfileoff += h->SizeOfRawData; + } + if(s) { + if(s->vaddr-PEBASE != h->VirtualAddress) { + diag("%s.VirtualAddress = %#llux, want %#llux", name, (vlong)h->VirtualAddress, (vlong)(s->vaddr-PEBASE)); + errorexit(); + } + if(s->fileoff != h->PointerToRawData) { + diag("%s.PointerToRawData = %#llux, want %#llux", name, (vlong)h->PointerToRawData, (vlong)(s->fileoff)); + errorexit(); + } } return h; } @@ -79,6 +108,11 @@ peinit(void) default: break; } + + PEFILEHEADR = rnd(sizeof(dosstub)+sizeof(fh)+sizeof(oh)+sizeof(sh), PEFILEALIGN); + PESECTHEADR = rnd(PEFILEHEADR, PESECTALIGN); + nextsectoff = PESECTHEADR; + nextfileoff = PEFILEHEADR; } static void @@ -86,7 +120,8 @@ pewrite(void) { int i, j; - write(cout, dosstub, sizeof dosstub); + seek(cout, 0, 0); + ewrite(cout, dosstub, sizeof dosstub); strnput("PE", 4); for (i=0; i<sizeof(fh); i++) @@ -98,24 +133,6 @@ pewrite(void) cput(((char*)&sh[i])[j]); } -void -dope(void) -{ - textsect = new_section(".text", textsize, 0); - textsect->Characteristics = IMAGE_SCN_CNT_CODE| - IMAGE_SCN_CNT_INITIALIZED_DATA| - IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ; - - datsect = new_section(".data", datsize, 0); - datsect->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA| - IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; - INITDAT = PEBASE+datsect->VirtualAddress; - - bsssect = new_section(".bss", bsssize, 1); - bsssect->Characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA| - IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; -} - static void strput(char *s) { @@ -124,73 +141,167 @@ strput(char *s) cput('\0'); } -static void -add_import_table(void) +static Dll* +initdynimport(void) { - IMAGE_IMPORT_DESCRIPTOR ds[2], *d; - char *dllname = "kernel32.dll"; - struct { - char *name; - uint32 thunk; - } *f, fs[] = { - { "GetProcAddress", 0 }, - { "LoadLibraryExA", 0 }, - { 0, 0 } - }; - - uint32 size = 0; - memset(ds, 0, sizeof(ds)); - size += sizeof(ds); - ds[0].Name = size; - size += strlen(dllname) + 1; - for(f=fs; f->name; f++) { - f->thunk = size; - size += sizeof(uint16) + strlen(f->name) + 1; + Imp *m; + Dll *d; + Sym *s; + int i; + Sym *dynamic; + + dr = nil; + ndll = 0; + nimp = 0; + nsize = 0; + + for(i=0; i<NHASH; i++) + for(s = hash[i]; s != S; s = s->hash) { + if(!s->reachable || !s->dynimpname) + continue; + nimp++; + for(d = dr; d != nil; d = d->next) { + if(strcmp(d->name,s->dynimplib) == 0) { + m = mal(sizeof *m); + m->s = s; + m->next = d->ms; + d->ms = m; + d->count++; + nsize += strlen(s->dynimpname)+2+1; + break; + } + } + if(d == nil) { + d = mal(sizeof *d); + d->name = s->dynimplib; + d->count = 1; + d->next = dr; + dr = d; + m = mal(sizeof *m); + m->s = s; + m->next = 0; + d->ms = m; + ndll++; + nsize += strlen(s->dynimpname)+2+1; + nsize += strlen(s->dynimplib)+1; + } + } + + nsize += 20*ndll + 20; + nsize += 4*nimp + 4*ndll; + + dynamic = lookup(".windynamic", 0); + dynamic->reachable = 1; + dynamic->type = SWINDOWS; + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) { + m->s->type = SWINDOWS | SSUB; + m->s->sub = dynamic->sub; + dynamic->sub = m->s; + m->s->value = dynamic->size; + dynamic->size += 4; + } + dynamic->size += 4; } - ds[0].FirstThunk = size; - for(f=fs; f->name; f++) - size += sizeof(fs[0].thunk); + + return dr; +} +static void +addimports(vlong fileoff, IMAGE_SECTION_HEADER *datsect) +{ IMAGE_SECTION_HEADER *isect; - isect = new_section(".idata", size, 0); + uint32 va; + int noff, aoff, o, last_fn, last_name_off, iat_off; + Imp *m; + Dll *d; + Sym* dynamic; + + isect = addpesection(".idata", nsize, nsize, 0); isect->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA| IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; - - uint32 va = isect->VirtualAddress; + va = isect->VirtualAddress; oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = va; oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect->VirtualSize; - ds[0].Name += va; - ds[0].FirstThunk += va; - for(f=fs; f->name; f++) - f->thunk += va; + seek(cout, fileoff, 0); - vlong off = seek(cout, 0, 1); - seek(cout, 0, 2); - for(d=ds; ; d++) { - lputl(d->OriginalFirstThunk); - lputl(d->TimeDateStamp); - lputl(d->ForwarderChain); - lputl(d->Name); - lputl(d->FirstThunk); - if(!d->Name) - break; + dynamic = lookup(".windynamic", 0); + iat_off = dynamic->value - PEBASE; // FirstThunk allocated in .data + oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = iat_off; + oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = dynamic->size; + + noff = va + 20*ndll + 20; + aoff = noff + 4*nimp + 4*ndll; + last_fn = 0; + last_name_off = aoff; + for(d = dr; d != nil; d = d->next) { + lputl(noff); + lputl(0); + lputl(0); + lputl(last_name_off); + lputl(iat_off); + last_fn = d->count; + noff += 4*last_fn + 4; + aoff += 4*last_fn + 4; + iat_off += 4*last_fn + 4; + last_name_off += strlen(d->name)+1; } - strput(dllname); - for(f=fs; f->name; f++) { - wputl(0); - strput(f->name); + lputl(0); //end + lputl(0); + lputl(0); + lputl(0); + lputl(0); + + // put OriginalFirstThunk + o = last_name_off; + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) { + lputl(o); + o += 2 + strlen(m->s->dynimpname) + 1; + } + lputl(0); + } + // put names + for(d = dr; d != nil; d = d->next) { + strput(d->name); + } + // put hint+name + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) { + wputl(0); + strput(m->s->dynimpname); + } + } + + strnput("", isect->SizeOfRawData - nsize); + cflush(); + + // put FirstThunk + o = last_name_off; + seek(cout, datsect->PointerToRawData + dynamic->value - PEBASE - datsect->VirtualAddress, 0); + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) { + lputl(o); + o += 2 + strlen(m->s->dynimpname) + 1; + } + lputl(0); } - for(f=fs; f->name; f++) - lputl(f->thunk); - strnput("", isect->SizeOfRawData - size); cflush(); - seek(cout, off, 0); + seek(cout, 0, 2); +} + +void +dope(void) +{ + initdynimport(); } void asmbpe(void) { + IMAGE_SECTION_HEADER *t, *d; + switch(thechar) { default: diag("unknown PE architecture"); @@ -203,14 +314,16 @@ asmbpe(void) break; } - if(!debug['s']) { - IMAGE_SECTION_HEADER *symsect; - symsect = new_section(".symdat", 8+symsize+lcsize, 0); - symsect->Characteristics = IMAGE_SCN_MEM_READ| - IMAGE_SCN_CNT_INITIALIZED_DATA; - } + t = addpesection(".text", segtext.len, segtext.len, &segtext); + t->Characteristics = IMAGE_SCN_CNT_CODE| + IMAGE_SCN_CNT_INITIALIZED_DATA| + IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ; + + d = addpesection(".data", segdata.len, segdata.filelen, &segdata); + d->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA| + IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; - add_import_table(); + addimports(nextfileoff, d); fh.NumberOfSections = nsect; fh.TimeDateStamp = time(0); @@ -223,24 +336,24 @@ asmbpe(void) oh.Magic = 0x10b; // PE32 oh.MajorLinkerVersion = 1; oh.MinorLinkerVersion = 0; - oh.SizeOfCode = textsect->SizeOfRawData; - oh.SizeOfInitializedData = datsect->SizeOfRawData; - oh.SizeOfUninitializedData = bsssect->SizeOfRawData; + oh.SizeOfCode = t->SizeOfRawData; + oh.SizeOfInitializedData = d->SizeOfRawData; + oh.SizeOfUninitializedData = 0; oh.AddressOfEntryPoint = entryvalue()-PEBASE; - oh.BaseOfCode = textsect->VirtualAddress; - oh.BaseOfData = datsect->VirtualAddress; + oh.BaseOfCode = t->VirtualAddress; + oh.BaseOfData = d->VirtualAddress; oh.ImageBase = PEBASE; - oh.SectionAlignment = 0x00001000; - oh.FileAlignment = PEALIGN; + oh.SectionAlignment = PESECTALIGN; + oh.FileAlignment = PEFILEALIGN; oh.MajorOperatingSystemVersion = 4; oh.MinorOperatingSystemVersion = 0; oh.MajorImageVersion = 1; oh.MinorImageVersion = 0; oh.MajorSubsystemVersion = 4; oh.MinorSubsystemVersion = 0; - oh.SizeOfImage = sect_virt_begin; - oh.SizeOfHeaders = PERESERVE; + oh.SizeOfImage = nextsectoff; + oh.SizeOfHeaders = PEFILEHEADR; oh.Subsystem = 3; // WINDOWS_CUI oh.SizeOfStackReserve = 0x00200000; oh.SizeOfStackCommit = 0x00001000; diff --git a/src/cmd/ld/pe.h b/src/cmd/ld/pe.h index b64dd97c0..f8161cc4a 100644 --- a/src/cmd/ld/pe.h +++ b/src/cmd/ld/pe.h @@ -72,9 +72,16 @@ typedef struct { uint32 FirstThunk; } IMAGE_IMPORT_DESCRIPTOR; -#define PERESERVE 0x400 -#define PEALIGN 0x200 #define PEBASE 0x00400000 +// SectionAlignment must be greater than or equal to FileAlignment. +// The default is the page size for the architecture. +#define PESECTALIGN 0x1000 +// FileAlignment should be a power of 2 between 512 and 64 K, inclusive. +// The default is 512. If the SectionAlignment is less than +// the architecture's page size, then FileAlignment must match SectionAlignment. +#define PEFILEALIGN (2<<8) +extern int32 PESECTHEADR; +extern int32 PEFILEHEADR; enum { IMAGE_FILE_MACHINE_I386 = 0x14c, @@ -112,5 +119,6 @@ enum { }; void peinit(void); -void dope(void); void asmbpe(void); +void dope(void); + diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c new file mode 100644 index 000000000..26e4def64 --- /dev/null +++ b/src/cmd/ld/symtab.c @@ -0,0 +1,259 @@ +// Inferno utils/6l/span.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Symbol table. + +#include "l.h" +#include "../ld/lib.h" +#include "../ld/elf.h" + +char *elfstrdat; +int elfstrsize; +int maxelfstr; +int elftextsh; + +int +putelfstr(char *s) +{ + int off, n; + + if(elfstrsize == 0 && s[0] != 0) { + // first entry must be empty string + putelfstr(""); + } + + n = strlen(s)+1; + if(elfstrsize+n > maxelfstr) { + maxelfstr = 2*(elfstrsize+n+(1<<20)); + elfstrdat = realloc(elfstrdat, maxelfstr); + } + off = elfstrsize; + elfstrsize += n; + memmove(elfstrdat+off, s, n); + return off; +} + +void +putelfsym64(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) +{ + int bind, type, shndx, stroff; + + bind = STB_GLOBAL; + switch(t) { + default: + return; + case 'T': + type = STT_FUNC; + shndx = elftextsh + 0; + break; + case 'D': + type = STT_OBJECT; + shndx = elftextsh + 1; + break; + case 'B': + type = STT_OBJECT; + shndx = elftextsh + 2; + break; + } + + stroff = putelfstr(s); + LPUT(stroff); // string + cput((bind<<4)|(type&0xF)); + cput(0); + WPUT(shndx); + VPUT(addr); + VPUT(size); +} + +void +asmelfsym64(void) +{ + genasmsym(putelfsym64); +} + +void +putelfsym32(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) +{ + int bind, type, shndx, stroff; + + bind = STB_GLOBAL; + switch(t) { + default: + return; + case 'T': + type = STT_FUNC; + shndx = elftextsh + 0; + break; + case 'D': + type = STT_OBJECT; + shndx = elftextsh + 1; + break; + case 'B': + type = STT_OBJECT; + shndx = elftextsh + 2; + break; + } + + stroff = putelfstr(s); + LPUT(stroff); // string + LPUT(addr); + LPUT(size); + cput((bind<<4)|(type&0xF)); + cput(0); + WPUT(shndx); +} + +void +asmelfsym32(void) +{ + genasmsym(putelfsym32); +} + + +static Sym *symt; + +static void +scput(int b) +{ + uchar *p; + + symgrow(symt, symt->size+1); + p = symt->p + symt->size; + *p = b; + symt->size++; +} + +static void +slputb(int32 v) +{ + uchar *p; + + symgrow(symt, symt->size+4); + p = symt->p + symt->size; + *p++ = v>>24; + *p++ = v>>16; + *p++ = v>>8; + *p = v; + symt->size += 4; +} + +void +putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) +{ + int i, f, l; + Reloc *rel; + + if(t == 'f') + name++; + l = 4; +// if(!debug['8']) +// l = 8; + if(s != nil) { + rel = addrel(symt); + rel->siz = l + Rbig; + rel->sym = s; + rel->type = D_ADDR; + rel->off = symt->size; + v = 0; + } + if(l == 8) + slputb(v>>32); + slputb(v); + if(ver) + t += 'a' - 'A'; + scput(t+0x80); /* 0x80 is variable length */ + + if(t == 'Z' || t == 'z') { + scput(name[0]); + for(i=1; name[i] != 0 || name[i+1] != 0; i += 2) { + scput(name[i]); + scput(name[i+1]); + } + scput(0); + scput(0); + i++; + } + else { + for(i=0; name[i]; i++) + scput(name[i]); + scput(0); + } + if(typ) { + if(!typ->reachable) + diag("unreachable type %s", typ->name); + rel = addrel(symt); + rel->siz = l; + rel->sym = typ; + rel->type = D_ADDR; + rel->off = symt->size; + } + if(l == 8) + slputb(0); + slputb(0); + + if(debug['n']) { + if(t == 'z' || t == 'Z') { + Bprint(&bso, "%c %.8llux ", t, v); + for(i=1; name[i] != 0 || name[i+1] != 0; i+=2) { + f = ((name[i]&0xff) << 8) | (name[i+1]&0xff); + Bprint(&bso, "/%x", f); + } + Bprint(&bso, "\n"); + return; + } + if(ver) + Bprint(&bso, "%c %.8llux %s<%d> %s\n", t, v, s, ver, typ ? typ->name : ""); + else + Bprint(&bso, "%c %.8llux %s %s\n", t, v, s, typ ? typ->name : ""); + } +} + +void +symtab(void) +{ + // Define these so that they'll get put into the symbol table. + // data.c:/^address will provide the actual values. + xdefine("text", STEXT, 0); + xdefine("etext", STEXT, 0); + xdefine("rodata", SRODATA, 0); + xdefine("erodata", SRODATA, 0); + xdefine("data", SBSS, 0); + xdefine("edata", SBSS, 0); + xdefine("end", SBSS, 0); + xdefine("epclntab", SRODATA, 0); + xdefine("esymtab", SRODATA, 0); + + symt = lookup("symtab", 0); + symt->type = SRODATA; + symt->size = 0; + symt->reachable = 1; + + genasmsym(putsymb); +} diff --git a/src/cmd/make.bash b/src/cmd/make.bash index d0fda7d18..63da74625 100755 --- a/src/cmd/make.bash +++ b/src/cmd/make.bash @@ -7,9 +7,7 @@ set -e bash clean.bash -GOBIN="${GOBIN:-$HOME/bin}" - -. "$GOROOT"/src/Make.$GOARCH +eval $(gomake --no-print-directory -f ../Make.inc go-env) if [ -z "$O" ]; then echo 'missing $O - maybe no Make.$GOARCH?' 1>&2 exit 1 @@ -17,16 +15,16 @@ fi cd ${O}l bash mkenam -"$GOBIN"/gomake enam.o +gomake enam.o cd .. # Note: commands written in Go are not listed here. -# They are in ../make.bash so that they can be built +# They are in ../pkg/Makefile so that they can be built # after the Go libraries on which they depend. for i in cc ${O}l ${O}a ${O}c gc ${O}g cov godefs gopack gotest nm prof do echo; echo; echo %%%% making $i %%%%; echo cd $i - "$GOBIN"/gomake install + gomake install cd .. done diff --git a/src/cmd/nm/Makefile b/src/cmd/nm/Makefile index bb1545122..383dbd973 100644 --- a/src/cmd/nm/Makefile +++ b/src/cmd/nm/Makefile @@ -2,23 +2,17 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) # The directory is nm because the source is portable and general. -# We call the binary 6nm to avoid confusion and because this binary -# is linked only with amd64 and x86 support. +# We call the binary 6nm to avoid confusion with the host nm. TARG=6nm OFILES=\ nm.$O\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lmach -lbio -l9 +LIB=\ + ../../../lib/libmach.a\ -clean: - rm -f *.$O $(TARG) - -install: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) - -$(OFILES): $(HFILES) +include ../../Make.ccmd diff --git a/src/cmd/nm/nm.c b/src/cmd/nm/nm.c index 978218bff..845b6c773 100644 --- a/src/cmd/nm/nm.c +++ b/src/cmd/nm/nm.c @@ -126,31 +126,34 @@ void doar(Biobuf *bp) { int offset, size, obj; - char membername[SARNAME]; + char name[SARNAME]; multifile = 1; for (offset = Boffset(bp);;offset += size) { - size = nextar(bp, offset, membername); + size = nextar(bp, offset, name); if (size < 0) { - error("phase error on ar header %ld", offset); + error("phase error on ar header %d", offset); return; } if (size == 0) return; - if (strcmp(membername, symname) == 0) + if (strcmp(name, symname) == 0) continue; obj = objtype(bp, 0); if (obj < 0) { + // perhaps foreign object + if(strlen(name) > 2 && strcmp(name+strlen(name)-2, ".o") == 0) + return; error("inconsistent file %s in %s", - membername, filename); + name, filename); return; } if (!readar(bp, obj, offset+size, 1)) { error("invalid symbol reference in file %s", - membername); + name); return; } - filename = membername; + filename = name; nsym=0; objtraverse(psym, 0); printsyms(symptr, nsym); diff --git a/src/cmd/prof/Makefile b/src/cmd/prof/Makefile index 602c07da6..e643f267c 100644 --- a/src/cmd/prof/Makefile +++ b/src/cmd/prof/Makefile @@ -2,7 +2,8 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.conf +include ../../Make.inc +O:=$(HOST_O) # The directory is prof because the source is portable and general. # We call the binary 6prof to avoid confusion and because this binary @@ -12,19 +13,22 @@ TARG=6prof OFILES=\ main.$O\ -#HFILES=\ -# defs.h\ -# fns.h\ +LIB=\ + ../../../lib/libmach.a\ -$(TARG): $(OFILES) - $(LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) -lmach -lbio -l9 +NOINSTALL=1 +include ../../Make.ccmd -clean: - rm -f *.$O $(TARG) +ifeq ($(GOOS),windows) +NAME=windows +else +NAME=$(shell uname | tr A-Z a-z) +endif -install: install-$(shell uname | tr A-Z a-z) install-pprof +install: install-$(NAME) install-pprof install-linux: install-default install-freebsd: install-default +install-windows: install-default # on Darwin, have to install and setgid; see $GOROOT/src/sudo.bash install-darwin: $(TARG) @@ -33,7 +37,5 @@ install-darwin: $(TARG) install-default: $(TARG) cp $(TARG) "$(GOBIN)"/$(TARG) -$(OFILES): $(HFILES) - install-pprof: gopprof cp gopprof "$(GOBIN)"/gopprof diff --git a/src/cmd/prof/gopprof b/src/cmd/prof/gopprof index dffeeffa1..4bcfa5800 100755 --- a/src/cmd/prof/gopprof +++ b/src/cmd/prof/gopprof @@ -2736,6 +2736,7 @@ sub IsSymbolizedProfileFile { sub CheckSymbolPage { my $url = SymbolPageURL(); +print STDERR "Read $url\n"; open(SYMBOL, "$CURL -s '$url' |"); my $line = <SYMBOL>; $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines @@ -2816,7 +2817,7 @@ sub ResolveRedirectionForCurl { # $main::prog to have the correct program name. sub ReadSymbols { my $in = shift; - my $map = {}; + my $map = shift; while (<$in>) { s/\r//g; # turn windows-looking lines into unix-looking lines # Removes all the leading zeroes from the symbols, see comment below. @@ -2858,20 +2859,30 @@ sub FetchSymbols { my @pcs = grep { !$seen{$_}++ } keys(%$pcset); # uniq if (!defined($symbol_map)) { - my $post_data = join("+", sort((map {"0x" . "$_"} @pcs))); - - open(POSTFILE, ">$main::tmpfile_sym"); - print POSTFILE $post_data; - close(POSTFILE); - - my $url = SymbolPageURL(); - $url = ResolveRedirectionForCurl($url); - my $command_line = "$CURL -sd '\@$main::tmpfile_sym' '$url'"; - # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols. - my $cppfilt = $obj_tool_map{"c++filt"}; - open(SYMBOL, "$command_line | $cppfilt |") or error($command_line); - $symbol_map = ReadSymbols(*SYMBOL{IO}); - close(SYMBOL); + $symbol_map = {}; + my @toask = @pcs; + while (@toask > 0) { + my $n = @toask; + if ($n > 49) { $n = 49; } + my @thisround = @toask[0..$n]; +my $t = @toask; +print STDERR "$n $t\n"; + @toask = @toask[($n+1)..(@toask-1)]; + my $post_data = join("+", sort((map {"0x" . "$_"} @thisround))); + open(POSTFILE, ">$main::tmpfile_sym"); + print POSTFILE $post_data; + close(POSTFILE); + +print STDERR "SYMBL!\n"; + my $url = SymbolPageURL(); + $url = ResolveRedirectionForCurl($url); + my $command_line = "$CURL -sd '\@$main::tmpfile_sym' '$url'"; + # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols. + my $cppfilt = $obj_tool_map{"c++filt"}; + open(SYMBOL, "$command_line | $cppfilt |") or error($command_line); + ReadSymbols(*SYMBOL{IO}, $symbol_map); + close(SYMBOL); + } } my $symbols = {}; diff --git a/src/cmd/prof/main.c b/src/cmd/prof/main.c index 2bb67f596..f36759cd3 100644 --- a/src/cmd/prof/main.c +++ b/src/cmd/prof/main.c @@ -53,9 +53,10 @@ Map *map[32]; // thread maps void Usage(void) { - fprint(2, "Usage: prof -p pid [-t total_secs] [-d delta_msec] [6.out args ...]\n"); + fprint(2, "Usage: prof -p pid [-t total_secs] [-d delta_msec]\n"); + fprint(2, " prof [-t total_secs] [-d delta_msec] 6.out args ...\n"); fprint(2, "\tformats (default -h):\n"); - fprint(2, "\t\t-c file.prof: write [c]pprof output to file.prof\n"); + fprint(2, "\t\t-P file.prof: write [c]pprof output to file.prof\n"); fprint(2, "\t\t-h: histograms\n"); fprint(2, "\t\t-f: dynamic functions\n"); fprint(2, "\t\t-l: dynamic file and line numbers\n"); @@ -192,22 +193,22 @@ amd64_ppword(uvlong w) void x86_regprint(void) { - fprint(2, "ax\t0x%llux\n", ureg_x86.ax); - fprint(2, "bx\t0x%llux\n", ureg_x86.bx); - fprint(2, "cx\t0x%llux\n", ureg_x86.cx); - fprint(2, "dx\t0x%llux\n", ureg_x86.dx); - fprint(2, "si\t0x%llux\n", ureg_x86.si); - fprint(2, "di\t0x%llux\n", ureg_x86.di); - fprint(2, "bp\t0x%llux\n", ureg_x86.bp); - fprint(2, "ds\t0x%llux\n", ureg_x86.ds); - fprint(2, "es\t0x%llux\n", ureg_x86.es); - fprint(2, "fs\t0x%llux\n", ureg_x86.fs); - fprint(2, "gs\t0x%llux\n", ureg_x86.gs); - fprint(2, "cs\t0x%llux\n", ureg_x86.cs); - fprint(2, "flags\t0x%llux\n", ureg_x86.flags); - fprint(2, "pc\t0x%llux\n", ureg_x86.pc); - fprint(2, "sp\t0x%llux\n", ureg_x86.sp); - fprint(2, "ss\t0x%llux\n", ureg_x86.ss); + fprint(2, "ax\t0x%ux\n", ureg_x86.ax); + fprint(2, "bx\t0x%ux\n", ureg_x86.bx); + fprint(2, "cx\t0x%ux\n", ureg_x86.cx); + fprint(2, "dx\t0x%ux\n", ureg_x86.dx); + fprint(2, "si\t0x%ux\n", ureg_x86.si); + fprint(2, "di\t0x%ux\n", ureg_x86.di); + fprint(2, "bp\t0x%ux\n", ureg_x86.bp); + fprint(2, "ds\t0x%ux\n", ureg_x86.ds); + fprint(2, "es\t0x%ux\n", ureg_x86.es); + fprint(2, "fs\t0x%ux\n", ureg_x86.fs); + fprint(2, "gs\t0x%ux\n", ureg_x86.gs); + fprint(2, "cs\t0x%ux\n", ureg_x86.cs); + fprint(2, "flags\t0x%ux\n", ureg_x86.flags); + fprint(2, "pc\t0x%ux\n", ureg_x86.pc); + fprint(2, "sp\t0x%ux\n", ureg_x86.sp); + fprint(2, "ss\t0x%ux\n", ureg_x86.ss); } int |