diff options
Diffstat (limited to 'src/cmd')
91 files changed, 2639 insertions, 1137 deletions
diff --git a/src/cmd/5a/a.h b/src/cmd/5a/a.h index bc4f433e1..550b61dcf 100644 --- a/src/cmd/5a/a.h +++ b/src/cmd/5a/a.h @@ -54,7 +54,6 @@ typedef struct Hist Hist; #define NSYMB 8192 #define BUFSIZ 8192 #define HISTSZ 20 -#define NHUNK 10000 #define EOF (-1) #define IGN (-2) #define GETC() ((--fi.c < 0)? filbuf(): *fi.p++ & 0xff) diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c index 1328f4be6..032409bae 100644 --- a/src/cmd/5g/cgen.c +++ b/src/cmd/5g/cgen.c @@ -169,7 +169,7 @@ cgen(Node *n, Node *res) case OREAL: case OIMAG: - case OCMPLX: + case OCOMPLEX: fatal("unexpected complex"); break; @@ -567,7 +567,8 @@ agen(Node *n, Node *res) regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); } - } else if(nl->addable) { + } else + if(nl->addable) { if(!isconst(nr, CTINT)) { tempname(&tmp, types[TINT32]); p2 = cgenindex(nr, &tmp); @@ -671,7 +672,8 @@ agen(Node *n, Node *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) { + } else + if(isslice(nl->type) || nl->type->etype == TSTRING) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; @@ -813,6 +815,28 @@ agenr(Node *n, Node *a, Node *res) agen(n, a); } +void +gencmp0(Node *n, Type *t, int o, Prog *to) +{ + Node n1, n2, n3; + int a; + + regalloc(&n1, t, N); + cgen(n, &n1); + a = optoas(OCMP, t); + if(a != ACMP) { + nodconst(&n2, t, 0); + regalloc(&n3, t, N); + gmove(&n2, &n3); + gcmp(a, &n1, &n3); + regfree(&n3); + } else + gins(ATST, &n1, N); + a = optoas(o, t); + patch(gbranch(a, t), to); + regfree(&n1); +} + /* * generate: * if(n == true) goto to; @@ -855,19 +879,10 @@ bgen(Node *n, int true, Prog *to) switch(n->op) { default: - def: - regalloc(&n1, n->type, N); - cgen(n, &n1); - nodconst(&n2, n->type, 0); - regalloc(&n3, n->type, N); - gmove(&n2, &n3); - gcmp(optoas(OCMP, n->type), &n1, &n3); - a = ABNE; + a = ONE; if(!true) - a = ABEQ; - patch(gbranch(a, n->type), to); - regfree(&n1); - regfree(&n3); + a = OEQ; + gencmp0(n, n->type, a, to); goto ret; case OLITERAL: @@ -876,23 +891,6 @@ bgen(Node *n, int true, Prog *to) patch(gbranch(AB, T), to); goto ret; - case ONAME: - if(n->addable == 0) - goto def; - nodconst(&n1, n->type, 0); - regalloc(&n2, n->type, N); - regalloc(&n3, n->type, N); - gmove(&n1, &n2); - cgen(n, &n3); - gcmp(optoas(OCMP, n->type), &n2, &n3); - a = ABNE; - if(!true) - a = ABEQ; - patch(gbranch(a, n->type), to); - regfree(&n2); - regfree(&n3); - goto ret; - case OANDAND: if(!true) goto caseor; @@ -975,6 +973,16 @@ bgen(Node *n, int true, Prog *to) yyerror("illegal array comparison"); break; } + + regalloc(&n1, types[tptr], N); + agen(nl, &n1); + n2 = n1; + n2.op = OINDREG; + n2.xoffset = Array_array; + gencmp0(&n2, types[tptr], a, to); + regfree(&n1); + break; + a = optoas(a, types[tptr]); regalloc(&n1, types[tptr], N); regalloc(&n3, types[tptr], N); @@ -1000,6 +1008,16 @@ bgen(Node *n, int true, Prog *to) yyerror("illegal interface comparison"); break; } + + regalloc(&n1, types[tptr], N); + agen(nl, &n1); + n2 = n1; + n2.op = OINDREG; + n2.xoffset = 0; + gencmp0(&n2, types[tptr], a, to); + regfree(&n1); + break; + a = optoas(a, types[tptr]); regalloc(&n1, types[tptr], N); regalloc(&n3, types[tptr], N); @@ -1039,6 +1057,17 @@ bgen(Node *n, int true, Prog *to) break; } + if(nr->op == OLITERAL) { + if(nr->val.ctype == CTINT && mpgetfix(nr->val.u.xval) == 0) { + gencmp0(nl, nl->type, a, to); + break; + } + if(nr->val.ctype == CTNIL) { + gencmp0(nl, nl->type, a, to); + break; + } + } + a = optoas(a, nr->type); if(nr->ullman >= UINF) { @@ -1063,11 +1092,17 @@ bgen(Node *n, int true, Prog *to) break; } + tempname(&n3, nl->type); + cgen(nl, &n3); + + tempname(&tmp, nr->type); + cgen(nr, &tmp); + regalloc(&n1, nl->type, N); - cgen(nl, &n1); + gmove(&n3, &n1); regalloc(&n2, nr->type, N); - cgen(nr, &n2); + gmove(&tmp, &n2); gcmp(optoas(OCMP, nr->type), &n1, &n2); if(isfloat[nl->type->etype]) { @@ -1080,7 +1115,6 @@ bgen(Node *n, int true, Prog *to) } else { patch(gbranch(a, nr->type), to); } - regfree(&n1); regfree(&n2); break; diff --git a/src/cmd/5g/cgen64.c b/src/cmd/5g/cgen64.c index 716ec5ed5..78f2f4aeb 100644 --- a/src/cmd/5g/cgen64.c +++ b/src/cmd/5g/cgen64.c @@ -233,8 +233,7 @@ cgen64(Node *n, Node *res) // shift is >= 1<<32 split64(r, &cl, &ch); gmove(&ch, &s); - p1 = gins(AMOVW, &s, &s); - p1->scond |= C_SBIT; + p1 = gins(ATST, &s, N); p6 = gbranch(ABNE, T); gmove(&cl, &s); splitclean(); @@ -242,8 +241,7 @@ cgen64(Node *n, Node *res) gmove(r, &s); p6 = P; } - p1 = gins(AMOVW, &s, &s); - p1->scond |= C_SBIT; + p1 = gins(ATST, &s, N); // shift == 0 p1 = gins(AMOVW, &bl, &al); @@ -390,8 +388,7 @@ olsh_break: // shift is >= 1<<32 split64(r, &cl, &ch); gmove(&ch, &s); - p1 = gins(AMOVW, &s, &s); - p1->scond |= C_SBIT; + p1 = gins(ATST, &s, N); p6 = gbranch(ABNE, T); gmove(&cl, &s); splitclean(); @@ -399,8 +396,7 @@ olsh_break: gmove(r, &s); p6 = P; } - p1 = gins(AMOVW, &s, &s); - p1->scond |= C_SBIT; + p1 = gins(ATST, &s, N); // shift == 0 p1 = gins(AMOVW, &bl, &al); diff --git a/src/cmd/5g/galign.c b/src/cmd/5g/galign.c index 9c8760aea..0fece9a08 100644 --- a/src/cmd/5g/galign.c +++ b/src/cmd/5g/galign.c @@ -17,8 +17,6 @@ Typedef typedefs[] = "int", TINT, TINT32, "uint", TUINT, TUINT32, "uintptr", TUINTPTR, TUINT32, - "float", TFLOAT, TFLOAT32, - "complex", TCOMPLEX, TCOMPLEX64, 0 }; diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c index 42a89415d..182d7f147 100644 --- a/src/cmd/5g/ggen.c +++ b/src/cmd/5g/ggen.c @@ -172,7 +172,7 @@ ginscall(Node *f, int proc) p->to.reg = REGSP; p->to.offset = 8; - nodconst(&con, types[TINT32], argsize(f->type) + 4); + nodconst(&con, types[TINT32], argsize(f->type)); gins(AMOVW, &con, &r); p = gins(AMOVW, &r, N); p->to.type = D_OREG; @@ -595,8 +595,7 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) } // test for shift being 0 - p1 = gins(AMOVW, &n1, &n1); - p1->scond |= C_SBIT; + p1 = gins(ATST, &n1, N); p3 = gbranch(ABEQ, T); // test and fix up large shifts diff --git a/src/cmd/5g/opt.h b/src/cmd/5g/opt.h index 9a4e17571..7a0070fc9 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 *r, Adr *a, int); +Bits mkvar(Reg *r, Adr *a); 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 index 32333e8a9..f619a6206 100644 --- a/src/cmd/5g/peep.c +++ b/src/cmd/5g/peep.c @@ -89,11 +89,11 @@ loop1: /* * elide shift into D_SHIFT operand of subsequent instruction */ - if(shiftprop(r)) { - excise(r); - t++; - break; - } +// if(shiftprop(r)) { +// excise(r); +// t++; +// break; +// } break; case AMOVW: @@ -101,10 +101,10 @@ loop1: case AMOVD: if(!regtyp(&p->to)) break; - if(isdconst(&p->from)) { - constprop(&p->from, &p->to, r->s1); - 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) @@ -166,87 +166,89 @@ loop1: 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; - } - } +// 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(); } @@ -331,10 +333,13 @@ subprop(Reg *r0) case ABL: return 0; - case ACMP: + case AMULLU: + case AMULA: + case ACMN: case AADD: case ASUB: + case ASBC: case ARSB: case ASLL: case ASRL: @@ -342,12 +347,14 @@ subprop(Reg *r0) case AORR: case AAND: case AEOR: + case AMVN: case AMUL: + case AMULU: case ADIV: case ADIVU: + case AMOD: + case AMODU: - case ACMPF: - case ACMPD: case AADDD: case AADDF: case ASUBD: @@ -622,8 +629,8 @@ shiftprop(Reg *r) case AADC: case AORR: case ASUB: - case ARSB: case ASBC: + case ARSB: case ARSC: if(p1->reg == n || (p1->reg == NREG && p1->to.type == D_REG && p1->to.reg == n)) { if(p1->from.type != D_REG) @@ -648,6 +655,7 @@ shiftprop(Reg *r) print("\t=>%P", p1); } case ABIC: + case ATST: case ACMP: case ACMN: if(p1->reg == n) @@ -922,8 +930,7 @@ copyu(Prog *p, Adr *v, Adr *s) switch(p->as) { default: - if(debug['P']) - print(" (?)"); + print("copyu: cant find %A\n", p->as); return 2; case AMOVM: @@ -983,7 +990,7 @@ copyu(Prog *p, Adr *v, Adr *s) return 2; } else { if(p->to.reg == v->reg) - return 2; + return 2; } } if(s != A) { @@ -1005,8 +1012,14 @@ copyu(Prog *p, Adr *v, Adr *s) return 1; return 0; + case AMULLU: /* read, read, write, write */ + case AMULA: + return 2; + case AADD: /* read, read, write */ + case AADC: case ASUB: + case ASBC: case ARSB: case ASLL: case ASRL: @@ -1014,9 +1027,13 @@ copyu(Prog *p, Adr *v, Adr *s) case AORR: case AAND: case AEOR: + case AMVN: case AMUL: + case AMULU: case ADIV: case ADIVU: + case AMOD: + case AMODU: case AADDF: case AADDD: case ASUBF: @@ -1028,6 +1045,7 @@ copyu(Prog *p, Adr *v, Adr *s) case ACMPF: case ACMPD: + case ATST: case ACMP: case ACMN: case ACASE: @@ -1138,9 +1156,13 @@ a2type(Prog *p) switch(p->as) { + case ATST: case ACMP: case ACMN: + case AMULLU: + case AMULA: + case AADD: case ASUB: case ARSB: @@ -1150,9 +1172,13 @@ a2type(Prog *p) case AORR: case AAND: case AEOR: + case AMVN: case AMUL: + case AMULU: case ADIV: case ADIVU: + case AMOD: + case AMODU: return D_REG; case ACMPF: @@ -1369,12 +1395,15 @@ int modifiescpsr(Prog *p) { switch(p->as) { - case ATST: + case AMULLU: + case AMULA: + case AMULU: + case ADIVU: + case ATEQ: case ACMN: + case ATST: case ACMP: - case AMULU: - case ADIVU: case AMUL: case ADIV: case AMOD: diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c index 5011e75cc..eaf02b237 100644 --- a/src/cmd/5g/reg.c +++ b/src/cmd/5g/reg.c @@ -83,7 +83,7 @@ setoutvar(void) n = nodarg(t, 1); a = zprog.from; naddr(n, &a, 0); - bit = mkvar(R, &a, 0); + bit = mkvar(R, &a); for(z=0; z<BITS; z++) ovar.b[z] |= bit.b[z]; t = structnext(&save); @@ -137,14 +137,13 @@ regopt(Prog *firstp) uint32 vreg; Bits bit; -return; // disabled for the moment if(first == 0) { fmtinstall('Q', Qconv); } first++; if(debug['K']) { - if(first != 20) + if(first != 13) return; // debug['R'] = 2; // debug['P'] = 2; @@ -166,7 +165,7 @@ return; // disabled for the moment firstr = R; lastr = R; nvar = 0; - regbits = 0; + regbits = RtoB(REGSP)|RtoB(REGLINK)|RtoB(REGPC); for(z=0; z<BITS; z++) { externs.b[z] = 0; params.b[z] = 0; @@ -221,14 +220,14 @@ return; // disabled for the moment /* * left side always read */ - bit = mkvar(r, &p->from, p->as==AMOVW); + bit = mkvar(r, &p->from); for(z=0; z<BITS; z++) r->use1.b[z] |= bit.b[z]; /* * right side depends on opcode */ - bit = mkvar(r, &p->to, 0); + bit = mkvar(r, &p->to); if(bany(&bit)) switch(p->as) { default: @@ -254,8 +253,7 @@ return; // disabled for the moment * funny */ case ABL: - for(z=0; z<BITS; z++) - addrs.b[z] |= bit.b[z]; + setaddrs(bit); break; } @@ -273,6 +271,18 @@ return; // disabled for the moment if(firstr == R) return; + for(i=0; i<nvar; i++) { + Var *v = var+i; + if(v->addr) { + bit = blsh(i); + for(z=0; z<BITS; z++) + addrs.b[z] |= bit.b[z]; + } + +// print("bit=%2d addr=%d et=%-6E w=%-2d s=%S + %lld\n", +// i, v->addr, v->etype, v->width, v->sym, v->offset); + } + /* * pass 2 * turn branch references to pointers @@ -298,6 +308,7 @@ return; // disabled for the moment if(debug['R']) { p = firstr->prog; print("\n%L %D\n", p->lineno, &p->from); + print(" addr = %Q\n", addrs); } /* @@ -361,6 +372,7 @@ loop2: r->refahead.b[z] | r->calahead.b[z] | r->refbehind.b[z] | r->calbehind.b[z] | r->use1.b[z] | r->use2.b[z]; + bit.b[z] &= ~addrs.b[z]; } if(bany(&bit)) { @@ -486,18 +498,61 @@ brk: * last pass * eliminate nops * free aux structures + * adjust the stack pointer + * MOVW.W R1,-12(R13) <<- start + * MOVW R0,R1 + * MOVW R1,8(R13) + * MOVW $0,R1 + * MOVW R1,4(R13) + * BL ,runtime.newproc+0(SB) + * MOVW &ft+-32(SP),R7 <<- adjust + * MOVW &j+-40(SP),R6 <<- adjust + * MOVW autotmp_0003+-24(SP),R5 <<- adjust + * MOVW $12(R13),R13 <<- finish */ + vreg = 0; 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(p->as == AMOVW && p->to.reg == 13) { + if(p->scond & C_WBIT) { + vreg = -p->to.offset; // in adjust region +// print("%P adjusting %d\n", p, vreg); + continue; + } + if(p->from.type == D_CONST && p->to.type == D_REG) { + if(p->from.offset != vreg) + print("in and out different\n"); +// print("%P finish %d\n", p, vreg); + vreg = 0; // done adjust region + continue; + } + +// print("%P %d %d from type\n", p, p->from.type, D_CONST); +// print("%P %d %d to type\n\n", p, p->to.type, D_REG); + } + + if(p->as == AMOVW && vreg != 0) { + if(p->from.sym != S) + if(p->from.name == D_AUTO || p->from.name == D_PARAM) { + p->from.offset += vreg; +// print("%P adjusting from %d %d\n", p, vreg, p->from.type); + } + if(p->to.sym != S) + if(p->to.name == D_AUTO || p->to.name == D_PARAM) { + p->to.offset += vreg; +// print("%P adjusting to %d %d\n", p, vreg, p->from.type); + } + } } if(r1 != R) { r1->link = freer; freer = firstr; } + } void @@ -557,24 +612,31 @@ addmove(Reg *r, int bn, int rn, int f) if(a->etype == TARRAY || a->sym == S) a->type = D_CONST; + if(v->addr) + fatal("addmove: shouldnt be doing this %A\n", a); + 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 TBOOL: + case TUINT8: + p1->as = AMOVBU; + break; case TINT16: - case TUINT16: p1->as = AMOVH; break; + case TUINT16: + p1->as = AMOVHU; + break; + case TINT32: + case TUINT32: + case TPTR32: + p1->as = AMOVW; + break; case TFLOAT32: p1->as = AMOVF; break; @@ -598,7 +660,7 @@ addmove(Reg *r, int bn, int rn, int f) a->type = D_FREG; a->reg = rn-NREG; } - if(v->etype == TUINT8) + if(v->etype == TUINT8 || v->etype == TBOOL) p1->as = AMOVBU; if(v->etype == TUINT16) p1->as = AMOVHU; @@ -622,7 +684,7 @@ overlap(int32 o1, int w1, int32 o2, int w2) } Bits -mkvar(Reg *r, Adr *a, int docon) +mkvar(Reg *r, Adr *a) { Var *v; int i, t, n, et, z, w, flag; @@ -634,29 +696,33 @@ mkvar(Reg *r, Adr *a, int docon) t = a->type; n = D_NONE; + flag = 0; +// if(a->pun) +// flag = 1; + 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; + break; + + case D_CONST: + flag = 1; + goto onereg; case D_REGREG: if(a->offset != NREG) r->regu |= RtoB(a->offset); - // fallthrough + goto onereg; case D_REG: case D_SHIFT: case D_OREG: + onereg: if(a->reg != NREG) r->regu |= RtoB(a->reg); break; @@ -679,10 +745,6 @@ mkvar(Reg *r, Adr *a, int docon) break; } - flag = 0; -// if(a->pun) -// flag = 1; - s = a->sym; if(s == S) goto none; @@ -737,7 +799,6 @@ mkvar(Reg *r, Adr *a, int docon) 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++) @@ -746,22 +807,6 @@ out: 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: @@ -1021,7 +1066,6 @@ allreg(uint32 b, Rgn *r) case TFLOAT32: case TFLOAT64: - case TFLOAT: i = BtoF(~b); if(i && r->cost >= 0) { r->regno = i+NREG; @@ -1195,6 +1239,7 @@ paint3(Reg *r, int bn, int32 rb, int rn) 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; @@ -1243,6 +1288,9 @@ void addreg(Adr *a, int rn) { + if(a->type == D_CONST) + fatal("addreg: cant do this %D %d\n", a, rn); + a->sym = 0; a->name = D_NONE; a->type = D_REG; diff --git a/src/cmd/5l/Makefile b/src/cmd/5l/Makefile index 71798724b..c11ebe990 100644 --- a/src/cmd/5l/Makefile +++ b/src/cmd/5l/Makefile @@ -14,6 +14,7 @@ OFILES=\ enam.$O\ ldelf.$O\ ldmacho.$O\ + ldpe.$O\ lib.$O\ list.$O\ noop.$O\ diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index 7ceea59b6..34565629f 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -448,7 +448,9 @@ asmb(void) sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; sh->addralign = 1; - elfinterp(sh, startva, linuxdynld); + if(interpreter == nil) + interpreter = linuxdynld; + elfinterp(sh, startva, interpreter); ph = newElfPhdr(); ph->type = PT_INTERP; @@ -793,7 +795,8 @@ if(debug['G']) print("%ux: %s: arm %d %d %d\n", (uint32)(p->pc), p->from.sym->na rt = 0; if(p->as == AMOVW || p->as == AMVN) r = 0; - else if(r == NREG) + else + if(r == NREG) r = rt; o1 |= rf | (r<<16) | (rt<<12); break; @@ -1558,6 +1561,10 @@ if(debug['G']) print("%ux: %s: arm %d %d %d\n", (uint32)(p->pc), p->from.sym->na o1 |= (p->from.reg<<16); o1 |= (p->to.reg<<12); break; + case 90: /* tst reg */ + o1 = oprrr(ACMP+AEND, p->scond); + o1 |= p->from.reg<<16; + break; } out[0] = o1; @@ -1709,6 +1716,8 @@ oprrr(int a, int sc) 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); + case ACMP+AEND: // cmp imm + return o | (0x3<<24) | (0x5<<20); } diag("bad rrr %d", a); prasm(curp); diff --git a/src/cmd/5l/doc.go b/src/cmd/5l/doc.go index 6f7408116..d266b9233 100644 --- a/src/cmd/5l/doc.go +++ b/src/cmd/5l/doc.go @@ -20,6 +20,11 @@ Original options are listed in the link above. Options new in this version: +-F + Force use of software floating point. + Also implied by setting GOARM=5 in the environment. +-I interpreter + Set the ELF dynamic linker to use. -L dir1 -L dir2 Search for libraries (package files) in dir1, dir2, etc. The default is the single location $GOROOT/pkg/$GOOS_arm. diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h index 4e7ccea88..c31028416 100644 --- a/src/cmd/5l/l.h +++ b/src/cmd/5l/l.h @@ -276,7 +276,6 @@ enum STRINGSZ = 200, NHASH = 10007, - NHUNK = 100000, MINSIZ = 64, NENT = 100, MAXIO = 8192, @@ -333,6 +332,7 @@ EXTERN Oprang thumboprange[ALAST]; EXTERN char* outfile; EXTERN int32 pc; EXTERN uchar repop[ALAST]; +EXTERN char* interpreter; EXTERN char* rpath; EXTERN uint32 stroffset; EXTERN int32 symsize; diff --git a/src/cmd/5l/noop.c b/src/cmd/5l/noop.c index 5def0d3f1..a9439c27a 100644 --- a/src/cmd/5l/noop.c +++ b/src/cmd/5l/noop.c @@ -330,23 +330,23 @@ noops(void) 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; + /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */ + p->from.offset = autosize+160; p->to.type = D_REG; p->to.reg = 1; - // MOVW.LO $args +4, R2 - // also need to store the extra 4 bytes. + // MOVW.LO $args, R2 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->from.offset = (cursym->text->to.offset2 + 3) & ~3; p->to.type = D_REG; p->to.reg = 2; @@ -391,12 +391,12 @@ noops(void) p->to.type = D_REG; p->to.reg = 1; - // MOVW $args +4, R2 + // MOVW $args, 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->from.offset = (cursym->text->to.offset2 + 3) & ~3; p->to.type = D_REG; p->to.reg = 2; diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c index cb9ad9805..5b778d777 100644 --- a/src/cmd/5l/obj.c +++ b/src/cmd/5l/obj.c @@ -61,7 +61,7 @@ linkername[] = void usage(void) { - fprint(2, "usage: 5l [-E entry] [-H head] [-L dir] [-T text] [-D data] [-R rnd] [-r path] [-o out] main.5\n"); + fprint(2, "usage: 5l [-E entry] [-H head] [-I interpreter] [-L dir] [-T text] [-D data] [-R rnd] [-r path] [-o out] main.5\n"); errorexit(); } @@ -69,6 +69,7 @@ void main(int argc, char *argv[]) { int c, i; + char *p; Binit(&bso, 1, OWRITE); cout = -1; @@ -80,6 +81,10 @@ main(int argc, char *argv[]) INITDAT = -1; INITRND = -1; INITENTRY = 0; + + p = getenv("GOARM"); + if(p != nil && strcmp(p, "5") == 0) + debug['F'] = 1; ARGBEGIN { default: @@ -95,6 +100,9 @@ main(int argc, char *argv[]) case 'E': INITENTRY = EARGF(usage()); break; + case 'I': + interpreter = EARGF(usage()); + break; case 'L': Lflag(EARGF(usage())); break; diff --git a/src/cmd/5l/optab.c b/src/cmd/5l/optab.c index 96b216837..9ad0193ac 100644 --- a/src/cmd/5l/optab.c +++ b/src/cmd/5l/optab.c @@ -64,7 +64,7 @@ Optab optab[] = { AB, C_NONE, C_NONE, C_ROREG, 6, 4, 0, LPOOL }, { ABL, C_NONE, C_NONE, C_ROREG, 7, 8, 0 }, { ABX, C_NONE, C_NONE, C_ROREG, 75, 12, 0 }, - { ABXRET, C_NONE, C_NONE, C_ROREG, 76, 4, 0 }, + { ABXRET, C_NONE, C_NONE, C_ROREG, 76, 4, 0 }, { ASLL, C_RCON, C_REG, C_REG, 8, 4, 0 }, { ASLL, C_RCON, C_NONE, C_REG, 8, 4, 0 }, @@ -251,5 +251,7 @@ Optab optab[] = { AMOVW, C_REG, C_NONE, C_FREG, 88, 4, 0 }, { AMOVW, C_FREG, C_NONE, C_REG, 89, 4, 0 }, + { ATST, C_REG, C_NONE, C_NONE, 90, 4, 0 }, + { AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0 }, }; diff --git a/src/cmd/5l/span.c b/src/cmd/5l/span.c index be0f5e8b3..220140f43 100644 --- a/src/cmd/5l/span.c +++ b/src/cmd/5l/span.c @@ -962,7 +962,6 @@ buildop(void) oprange[ABIC] = oprange[r]; break; case ACMP: - oprange[ATST] = oprange[r]; oprange[ATEQ] = oprange[r]; oprange[ACMN] = oprange[r]; break; @@ -1055,6 +1054,7 @@ buildop(void) case ALDREX: case ASTREX: + case ATST: break; } } diff --git a/src/cmd/6a/a.h b/src/cmd/6a/a.h index 9030081ca..2d4272646 100644 --- a/src/cmd/6a/a.h +++ b/src/cmd/6a/a.h @@ -57,7 +57,6 @@ typedef struct Gen2 Gen2; #define NSYMB 500 #define BUFSIZ 8192 #define HISTSZ 20 -#define NHUNK 10000 #define EOF (-1) #define IGN (-2) #define GETC() ((--fi.c < 0)? filbuf(): *fi.p++ & 0xff) diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c index d4d22fd61..47f3374f5 100644 --- a/src/cmd/6g/cgen.c +++ b/src/cmd/6g/cgen.c @@ -431,9 +431,6 @@ agen(Node *n, Node *res) if(n == N || n->type == T) return; - if(!isptr[res->type->etype] && res->type->etype != TUINTPTR) - fatal("agen: not tptr: %T", res->type); - while(n->op == OCONVNOP) n = n->left; diff --git a/src/cmd/6g/galign.c b/src/cmd/6g/galign.c index bdfc9947e..97bfb58e8 100644 --- a/src/cmd/6g/galign.c +++ b/src/cmd/6g/galign.c @@ -17,8 +17,6 @@ Typedef typedefs[] = "int", TINT, TINT32, "uint", TUINT, TUINT32, "uintptr", TUINTPTR, TUINT64, - "float", TFLOAT, TFLOAT32, - "complex", TCOMPLEX, TCOMPLEX64, 0 }; diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c index ebb61ea94..c3dac1fdc 100644 --- a/src/cmd/6g/gsubr.c +++ b/src/cmd/6g/gsubr.c @@ -246,7 +246,7 @@ anyregalloc(void) { int i, j; - for(i=D_AL; i<=D_DI; i++) { + for(i=D_AX; i<=D_R15; i++) { if(reg[i] == 0) goto ok; for(j=0; j<nelem(resvd); j++) diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c index 464627066..1e1d64c59 100644 --- a/src/cmd/6g/reg.c +++ b/src/cmd/6g/reg.c @@ -748,7 +748,6 @@ addmove(Reg *r, int bn, int rn, int f) case TPTR64: p1->as = AMOVQ; break; - case TFLOAT: case TFLOAT32: p1->as = AMOVSS; break; @@ -1180,7 +1179,6 @@ allreg(uint32 b, Rgn *r) case TFLOAT32: case TFLOAT64: - case TFLOAT: i = BtoF(~b); if(i && r->cost > 0) { r->regno = i; diff --git a/src/cmd/6l/Makefile b/src/cmd/6l/Makefile index fba1b42ae..abe204d4f 100644 --- a/src/cmd/6l/Makefile +++ b/src/cmd/6l/Makefile @@ -16,12 +16,14 @@ OFILES=\ go.$O\ ldelf.$O\ ldmacho.$O\ + ldpe.$O\ lib.$O\ list.$O\ macho.$O\ obj.$O\ optab.$O\ pass.$O\ + pe.$O\ prof.$O\ span.$O\ symtab.$O\ @@ -33,6 +35,7 @@ HFILES=\ ../ld/elf.h\ ../ld/macho.h\ ../ld/dwarf.h\ + ../ld/pe.h\ include ../../Make.ccmd diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index 9726d227c..d6ffa4ff9 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -35,6 +35,7 @@ #include "../ld/elf.h" #include "../ld/dwarf.h" #include "../ld/macho.h" +#include "../ld/pe.h" #define Dbufslop 100 @@ -205,15 +206,16 @@ adddynrel(Sym *s, Reloc *r) 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); + if(r->off >= 2 && s->p[r->off-2] == 0x8b) { + // turn MOVQ of GOT entry into LEAQ of symbol itself + s->p[r->off-2] = 0x8d; + r->type = D_PCREL; + r->add += 4; return; } - s->p[r->off-2] = 0x8d; - r->type = D_PCREL; - r->add += 4; - return; + // fall back to using GOT and hope for the best (CMOV*) + // TODO: just needs relocation, no need to put in .dynsym + targ->dynimpname = targ->name; } addgotsym(targ); r->type = D_PCREL; @@ -782,6 +784,8 @@ asmb(void) if(!debug['d']) elftextsh += 10; break; + case 10: + break; } symsize = 0; @@ -807,6 +811,10 @@ asmb(void) symo = rnd(HEADR+segtext.len, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); break; + case 10: + symo = rnd(HEADR+segtext.filelen, PEFILEALIGN)+segdata.filelen; + symo = rnd(symo, PEFILEALIGN); + break; } /* * the symbol information is stored as @@ -829,7 +837,7 @@ asmb(void) lputl(symsize); lputl(lcsize); cflush(); - if(!debug['s']) { + if(HEADTYPE != 10 && !debug['s']) { elfsymo = symo+8+symsize+lcsize; seek(cout, elfsymo, 0); asmelfsym64(); @@ -907,14 +915,17 @@ asmb(void) sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; sh->addralign = 1; - switch(HEADTYPE) { - case 7: - elfinterp(sh, startva, linuxdynld); - break; - case 9: - elfinterp(sh, startva, freebsddynld); - break; + if(interpreter == nil) { + switch(HEADTYPE) { + case 7: + interpreter = linuxdynld; + break; + case 9: + interpreter = freebsddynld; + break; + } } + elfinterp(sh, startva, interpreter); ph = newElfPhdr(); ph->type = PT_INTERP; @@ -1090,6 +1101,9 @@ asmb(void) if(a+elfwriteinterp() > ELFRESERVE) diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); break; + case 10: + asmbpe(); + break; } cflush(); } @@ -1143,6 +1157,7 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) case SDATA: case SELFDATA: case SMACHOGOT: + case SWINDOWS: if(!s->reachable) continue; put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); diff --git a/src/cmd/6l/doc.go b/src/cmd/6l/doc.go index 501317f36..97fa2cc5a 100644 --- a/src/cmd/6l/doc.go +++ b/src/cmd/6l/doc.go @@ -32,6 +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) +-I interpreter + Set the ELF dynamic linker to use. -L dir1 -L dir2 Search for libraries (package files) in dir1, dir2, etc. The default is the single location $GOROOT/pkg/$GOOS_amd64. diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h index 1c52ea89d..7f22493e0 100644 --- a/src/cmd/6l/l.h +++ b/src/cmd/6l/l.h @@ -183,6 +183,7 @@ enum SRODATA, SDATA, SMACHOGOT, + SWINDOWS, SBSS, SXREF, @@ -196,7 +197,6 @@ enum SSUB = 1<<8, NHASH = 10007, - NHUNK = 100000, MINSIZ = 8, STRINGSZ = 200, MINLC = 1, @@ -352,6 +352,7 @@ EXTERN int nerrors; EXTERN char* noname; EXTERN char* outfile; EXTERN vlong pc; +EXTERN char* interpreter; EXTERN char* rpath; EXTERN int32 spsize; EXTERN Sym* symlist; diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c index 96d78c3b9..f9e257842 100644 --- a/src/cmd/6l/obj.c +++ b/src/cmd/6l/obj.c @@ -36,6 +36,7 @@ #include "../ld/elf.h" #include "../ld/macho.h" #include "../ld/dwarf.h" +#include "../ld/pe.h" #include <ar.h> char *noname = "<none>"; @@ -57,7 +58,7 @@ char* paramspace = "FP"; void usage(void) { - fprint(2, "usage: 6l [-options] [-E entry] [-H head] [-L dir] [-T text] [-R rnd] [-r path] [-o out] main.6\n"); + fprint(2, "usage: 6l [-options] [-E entry] [-H head] [-I interpreter] [-L dir] [-T text] [-R rnd] [-r path] [-o out] main.6\n"); exits("usage"); } @@ -95,6 +96,9 @@ main(int argc, char *argv[]) case 'H': HEADTYPE = atolwhex(EARGF(usage())); break; + case 'I': + interpreter = EARGF(usage()); + break; case 'L': Lflag(EARGF(usage())); break; @@ -133,6 +137,9 @@ main(int argc, char *argv[]) if(strcmp(goos, "freebsd") == 0) HEADTYPE = 9; else + if(strcmp(goos, "windows") == 0) + HEADTYPE = 10; + else print("goos is not known: %s\n", goos); } @@ -200,6 +207,16 @@ main(int argc, char *argv[]) if(INITRND == -1) INITRND = 4096; break; + case 10: /* PE executable */ + peinit(); + HEADR = PEFILEHEADR; + if(INITTEXT == -1) + INITTEXT = PEBASE+PESECTHEADR; + if(INITDAT == -1) + INITDAT = 0; + if(INITRND == -1) + INITRND = PESECTALIGN; + break; } if(INITDAT != 0 && INITRND != 0) print("warning: -D0x%llux is ignored because of -R0x%ux\n", @@ -245,6 +262,8 @@ main(int argc, char *argv[]) else doprof2(); span(); + if(HEADTYPE == 10) + dope(); addexport(); textaddress(); pclntab(); diff --git a/src/cmd/6l/pass.c b/src/cmd/6l/pass.c index 5c4ed00a6..5eb221a35 100644 --- a/src/cmd/6l/pass.c +++ b/src/cmd/6l/pass.c @@ -277,6 +277,29 @@ patch(void) vexit = s->value; 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 0x58(GS), 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 + && p->from.offset != 0x58) { + 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 = AMOVQ; + p->from.type = D_INDIR+D_GS; + p->from.offset = 0x58; + } + } if(HEADTYPE == 7 || HEADTYPE == 9) { // ELF uses FS instead of GS. if(p->from.type == D_INDIR+D_GS) @@ -411,6 +434,21 @@ dostkoff(void) p->from.type = D_INDIR+D_GS; p->from.offset = tlsoffset+0; p->to.type = D_CX; + if(HEADTYPE == 10) { // Windows + // movq %gs:0x58, %rcx + // movq (%rcx), %rcx + p->as = AMOVQ; + p->from.type = D_INDIR+D_GS; + p->from.offset = 0x58; + p->to.type = D_CX; + + + p = appendp(p); + p->as = AMOVQ; + p->from.type = D_INDIR+D_CX; + p->from.offset = 0; + p->to.type = D_CX; + } if(debug['K']) { // 6l -K means check not only for stack diff --git a/src/cmd/8a/a.h b/src/cmd/8a/a.h index fe6b17280..3cb30f4c2 100644 --- a/src/cmd/8a/a.h +++ b/src/cmd/8a/a.h @@ -57,7 +57,6 @@ typedef struct Gen2 Gen2; #define NSYMB 500 #define BUFSIZ 8192 #define HISTSZ 20 -#define NHUNK 10000 #define EOF (-1) #define IGN (-2) #define GETC() ((--fi.c < 0)? filbuf(): *fi.p++ & 0xff) diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c index 875d434fa..9c326e8ef 100644 --- a/src/cmd/8g/cgen.c +++ b/src/cmd/8g/cgen.c @@ -174,7 +174,7 @@ cgen(Node *n, Node *res) case OREAL: case OIMAG: - case OCMPLX: + case OCOMPLEX: fatal("unexpected complex"); return; diff --git a/src/cmd/8g/galign.c b/src/cmd/8g/galign.c index 1c14dfe47..48edfdf3c 100644 --- a/src/cmd/8g/galign.c +++ b/src/cmd/8g/galign.c @@ -17,8 +17,6 @@ Typedef typedefs[] = "int", TINT, TINT32, "uint", TUINT, TUINT32, "uintptr", TUINTPTR, TUINT32, - "float", TFLOAT, TFLOAT32, - "complex", TCOMPLEX, TCOMPLEX64, 0 }; diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c index e1dacf55a..1465d372c 100644 --- a/src/cmd/8g/reg.c +++ b/src/cmd/8g/reg.c @@ -1095,7 +1095,6 @@ allreg(uint32 b, Rgn *r) case TFLOAT32: case TFLOAT64: - case TFLOAT: break; } return 0; diff --git a/src/cmd/8l/Makefile b/src/cmd/8l/Makefile index 84976ba18..a85e3ffa7 100644 --- a/src/cmd/8l/Makefile +++ b/src/cmd/8l/Makefile @@ -16,6 +16,7 @@ OFILES=\ go.$O\ ldelf.$O\ ldmacho.$O\ + ldpe.$O\ lib.$O\ list.$O\ macho.$O\ diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index cdb5a33e6..6e83d8dea 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -520,7 +520,7 @@ adddynsym(Sym *s) adduint8(d, 0); // section adduint16(d, 0); // desc adduint32(d, 0); // value - } else { + } else if(HEADTYPE != 10) { diag("adddynsym: unsupported binary format"); } } @@ -540,7 +540,7 @@ adddynlib(char *lib) elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib)); } else if(HEADTYPE == 6) { // Mach-O machoadddynlib(lib); - } else { + } else if(HEADTYPE != 10) { diag("adddynlib: unsupported binary format"); } } @@ -936,14 +936,17 @@ asmb(void) sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; sh->addralign = 1; - switch(HEADTYPE) { - case 7: - elfinterp(sh, startva, linuxdynld); - break; - case 9: - elfinterp(sh, startva, freebsddynld); - break; + if(interpreter == nil) { + switch(HEADTYPE) { + case 7: + interpreter = linuxdynld; + break; + case 9: + interpreter = freebsddynld; + break; + } } + elfinterp(sh, startva, interpreter); ph = newElfPhdr(); ph->type = PT_INTERP; diff --git a/src/cmd/8l/doc.go b/src/cmd/8l/doc.go index 0bf6f151f..ef5ebc31d 100644 --- a/src/cmd/8l/doc.go +++ b/src/cmd/8l/doc.go @@ -29,6 +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) +-I interpreter + Set the ELF dynamic linker to use. -L dir1 -L dir2 Search for libraries (package files) in dir1, dir2, etc. The default is the single location $GOROOT/pkg/$GOOS_386. diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index daede8879..e0746fc75 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -190,7 +190,6 @@ enum SSUB = 1<<8, /* sub-symbol, linked from parent via ->sub list */ NHASH = 10007, - NHUNK = 100000, MINSIZ = 4, STRINGSZ = 200, MINLC = 1, @@ -316,6 +315,7 @@ EXTERN int maxop; EXTERN int nerrors; EXTERN char* noname; EXTERN int32 pc; +EXTERN char* interpreter; EXTERN char* rpath; EXTERN int32 spsize; EXTERN Sym* symlist; diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c index 18b2112fe..fefb6d8b0 100644 --- a/src/cmd/8l/obj.c +++ b/src/cmd/8l/obj.c @@ -64,7 +64,7 @@ char *thestring = "386"; void usage(void) { - fprint(2, "usage: 8l [-options] [-E entry] [-H head] [-L dir] [-T text] [-R rnd] [-r path] [-o out] main.8\n"); + fprint(2, "usage: 8l [-options] [-E entry] [-H head] [-I interpreter] [-L dir] [-T text] [-R rnd] [-r path] [-o out] main.8\n"); exits("usage"); } @@ -102,6 +102,9 @@ main(int argc, char *argv[]) case 'H': HEADTYPE = atolwhex(EARGF(usage())); break; + case 'I': + interpreter = EARGF(usage()); + break; case 'L': Lflag(EARGF(usage())); break; diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c index 6e387b0b5..878a73dac 100644 --- a/src/cmd/8l/pass.c +++ b/src/cmd/8l/pass.c @@ -38,8 +38,15 @@ static void xfol(Prog*, Prog**); // see ../../pkg/runtime/proc.c:/StackGuard enum { +#ifdef __WINDOWS__ + // use larger stacks to compensate for larger stack guard, + // needed for exception handling. + StackSmall = 256, + StackBig = 8192, +#else StackSmall = 128, StackBig = 4096, +#endif }; Prog* @@ -510,7 +517,7 @@ dostkoff(void) 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) + if(autoffset+160+cursym->text->to.offset2 > 4096) p->from.offset = (autoffset+160) & ~7LL; p = appendp(p); // save arg size in AX diff --git a/src/cmd/cc/Makefile b/src/cmd/cc/Makefile index 71f23383d..8327d9516 100644 --- a/src/cmd/cc/Makefile +++ b/src/cmd/cc/Makefile @@ -20,7 +20,7 @@ OFILES=\ mac.$O\ dcl.$O\ acid.$O\ - pickle.$O\ + godefs.$O\ bits.$O\ com.$O\ scon.$O\ diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h index 3649bf5f6..8e8f6af44 100644 --- a/src/cmd/cc/cc.h +++ b/src/cmd/cc/cc.h @@ -59,7 +59,6 @@ typedef struct Bits Bits; typedef struct Dynimp Dynimp; typedef struct Dynexp Dynexp; -#define NHUNK 50000L #define BUFSIZ 8192 #define NSYMB 500 #define NHASH 1024 @@ -745,9 +744,11 @@ void acidtype(Type*); void acidvar(Sym*); /* - * pickle.c + * godefs.c */ -void pickletype(Type*); +int Uconv(Fmt*); +void godeftype(Type*); +void godefvar(Sym*); /* * bits.c diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c index f629925d1..d7604b649 100644 --- a/src/cmd/cc/dcl.c +++ b/src/cmd/cc/dcl.c @@ -130,6 +130,7 @@ loop: if(debug['d']) dbgdecl(s); acidvar(s); + godefvar(s); s->varlineno = lineno; break; } @@ -587,7 +588,7 @@ sualign(Type *t) t->width = w; t->align = maxal; acidtype(t); - pickletype(t); + godeftype(t); return; case TUNION: @@ -610,7 +611,7 @@ sualign(Type *t) t->width = w; t->align = maxal; acidtype(t); - pickletype(t); + godeftype(t); return; default: @@ -1538,6 +1539,7 @@ doenum(Sym *s, Node *n) if(debug['d']) dbgdecl(s); acidvar(s); + godefvar(s); } void diff --git a/src/cmd/cc/dpchk.c b/src/cmd/cc/dpchk.c index 6eb5fb409..d78a72a2b 100644 --- a/src/cmd/cc/dpchk.c +++ b/src/cmd/cc/dpchk.c @@ -399,6 +399,7 @@ dpcheck(Node *n) return; i = l->param; + a = nil; b = n->right; a = Z; while(i > 0) { diff --git a/src/cmd/cc/godefs.c b/src/cmd/cc/godefs.c new file mode 100644 index 000000000..9503cb2f2 --- /dev/null +++ b/src/cmd/cc/godefs.c @@ -0,0 +1,387 @@ +// cmd/cc/godefs.cc +// +// derived from pickle.cc which itself was derived from acid.cc. +// +// 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-2011 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 "cc.h" + +static int upper; + +static char *kwd[] = +{ + "_bool", + "_break", + "_byte", + "_case", + "_chan", + "_complex128", + "_complex64", + "_const", + "_continue", + "_default", + "_defer", + "_else", + "_fallthrough", + "_false", + "_float32", + "_float64", + "_for", + "_func", + "_go", + "_goto", + "_if", + "_import", + "_int", + "_int16", + "_int32", + "_int64", + "_int8", + "_interface", + "_intptr", + "_map", + "_package", + "_panic", + "_range", + "_return", + "_select", + "_string", + "_struct", + "_switch", + "_true", + "_type", + "_uint", + "_uint16", + "_uint32", + "_uint64", + "_uint8", + "_uintptr", + "_var", +}; + +static char* +pmap(char *s) +{ + int i, bot, top, mid; + + bot = -1; + top = nelem(kwd); + while(top - bot > 1){ + mid = (bot + top) / 2; + i = strcmp(kwd[mid]+1, s); + if(i == 0) + return kwd[mid]; + if(i < 0) + bot = mid; + else + top = mid; + } + + return s; +} + + +int +Uconv(Fmt *fp) +{ + char str[STRINGSZ+1]; + char *s, *n; + int i; + + str[0] = 0; + s = va_arg(fp->args, char*); + + // strip package name + n = strrchr(s, '.'); + if(n != nil) + s = n + 1; + + if(s && *s) { + if(upper) + str[0] = toupper(*s); + else + str[0] = tolower(*s); + for(i = 1; i < STRINGSZ && s[i] != 0; i++) + str[i] = tolower(s[i]); + str[i] = 0; + } + + return fmtstrcpy(fp, pmap(str)); +} + + +static Sym* +findsue(Type *t) +{ + int h; + Sym *s; + + if(t != T) + for(h=0; h<nelem(hash); h++) + for(s = hash[h]; s != S; s = s->link) + if(s->suetag && s->suetag->link == t) + return s; + return 0; +} + +static void +printtypename(Type *t) +{ + Sym *s; + Type *t1; + int w; + char *n; + + for( ; t != nil; t = t->link) { + switch(t->etype) { + case TIND: + // Special handling of *void. + if(t->link != nil && t->link->etype==TVOID) { + Bprint(&outbuf, "unsafe.Pointer"); + return; + } + // *func == func + if(t->link != nil && t->link->etype==TFUNC) + continue; + Bprint(&outbuf, "*"); + continue; + case TARRAY: + w = t->width; + if(t->link && t->link->width) + w /= t->link->width; + Bprint(&outbuf, "[%d]", w); + continue; + } + break; + } + + if(t == nil) { + Bprint(&outbuf, "bad // should not happen"); + return; + } + + switch(t->etype) { + case TINT: + Bprint(&outbuf, "int"); + break; + case TUINT: + Bprint(&outbuf, "uint"); + break; + case TCHAR: + Bprint(&outbuf, "int8"); + break; + case TUCHAR: + Bprint(&outbuf, "uint8"); + break; + case TSHORT: + Bprint(&outbuf, "int16"); + break; + case TUSHORT: + Bprint(&outbuf, "uint16"); + break; + case TLONG: + Bprint(&outbuf, "int32"); + break; + case TULONG: + Bprint(&outbuf, "uint32"); + break; + case TVLONG: + Bprint(&outbuf, "int64"); + break; + case TUVLONG: + Bprint(&outbuf, "uint64"); + break; + case TFLOAT: + Bprint(&outbuf, "float32"); + break; + case TDOUBLE: + Bprint(&outbuf, "float64"); + break; + case TUNION: + case TSTRUCT: + s = findsue(t->link); + n = "bad"; + if(s != S) + n = s->name; + else if(t->tag) + n = t->tag->name; + if(strcmp(n, "String") == 0){ + Bprint(&outbuf, "string"); + } else if(strcmp(n, "Slice") == 0){ + Bprint(&outbuf, "[]byte"); + } else + Bprint(&outbuf, "%U", n); + break; + case TFUNC: + Bprint(&outbuf, "func(", t); + for(t1 = t->down; t1 != T; t1 = t1->down) { + if(t1->etype == TVOID) + break; + if(t1 != t->down) + Bprint(&outbuf, ", "); + printtypename(t1); + } + Bprint(&outbuf, ")"); + if(t->link && t->link->etype != TVOID) { + Bprint(&outbuf, " "); + printtypename(t->link); + } + break; + case TDOT: + Bprint(&outbuf, "...interface{}"); + break; + default: + Bprint(&outbuf, " weird<%T>", t); + } +} + +static int +dontrun(void) +{ + Io *i; + int n; + + if(!debug['q'] && !debug['Q']) + return 1; + if(debug['q'] + debug['Q'] > 1) { + n = 0; + for(i=iostack; i; i=i->link) + n++; + if(n > 1) + return 1; + } + + upper = debug['Q']; + return 0; +} + +void +godeftype(Type *t) +{ + Sym *s; + Type *l; + int gotone; + + if(dontrun()) + return; + + switch(t->etype) { + case TUNION: + case TSTRUCT: + s = findsue(t->link); + if(s == S) { + Bprint(&outbuf, "/* can't find %T */\n\n", t); + return; + } + + gotone = 0; // for unions, take first member of size equal to union + Bprint(&outbuf, "type %U struct {\n", s->name); + for(l = t->link; l != T; l = l->down) { + Bprint(&outbuf, "\t"); + if(t->etype == TUNION) { + if(!gotone && l->width == t->width) + gotone = 1; + else + Bprint(&outbuf, "// (union)\t"); + } + if(l->sym != nil) // not anonymous field + Bprint(&outbuf, "%U\t", l->sym->name); + printtypename(l); + Bprint(&outbuf, "\n"); + } + Bprint(&outbuf, "}\n\n"); + break; + + default: + Bprint(&outbuf, "/* %T */\n\n", t); + break; + } +} + +void +godefvar(Sym *s) +{ + Type *t, *t1; + char n; + + if(dontrun()) + return; + + t = s->type; + if(t == nil) + return; + + switch(t->etype) { + case TENUM: + if(!typefd[t->etype]) + Bprint(&outbuf, "const %U = %lld\n", s->name, s->vconst); + else + Bprint(&outbuf, "const %U = %f\n;", s->name, s->fconst); + break; + + case TFUNC: + Bprint(&outbuf, "func %U(", s->name); + n = 'a'; + for(t1 = t->down; t1 != T; t1 = t1->down) { + if(t1->etype == TVOID) + break; + if(t1 != t->down) + Bprint(&outbuf, ", "); + Bprint(&outbuf, "%c ", n++); + printtypename(t1); + } + Bprint(&outbuf, ")"); + if(t->link && t->link->etype != TVOID) { + Bprint(&outbuf, " "); + printtypename(t->link); + } + Bprint(&outbuf, "\n"); + break; + + default: + switch(s->class) { + case CTYPEDEF: + if(!typesu[t->etype]) { + Bprint(&outbuf, "// type %U\t", s->name); + printtypename(t); + Bprint(&outbuf, "\n"); + } + break; + case CSTATIC: + case CEXTERN: + case CGLOBL: + if(strchr(s->name, '$') != nil) // TODO(lvd) + break; + Bprint(&outbuf, "var %U\t", s->name); + printtypename(t); + Bprint(&outbuf, "\n"); + break; + } + break; + } +} diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c index 3b413c246..dba8ff634 100644 --- a/src/cmd/cc/lex.c +++ b/src/cmd/cc/lex.c @@ -59,15 +59,20 @@ pathchar(void) * -d print declarations * -D name define * -F format specification check + * -G print pgen stuff + * -g print cgen trees * -i print initialization * -I path include * -l generate little-endian code * -L print every NAME symbol * -M constant multiplication * -m print add/sub/mul trees - * -n print acid to file (%.c=%.acid) (with -a or -aa) + * -n print acid or godefs to file (%.c=%.acid) (with -a or -aa) * -o file output file * -p use standard cpp ANSI preprocessor (not on windows) + * -p something with peepholes + * -q print equivalent Go code for variables and types (lower-case identifiers) + * -Q print equivalent Go code for variables and types (upper-case identifiers) * -r print registerization * -s print structure offsets (with -a or -aa) * -S print assembly @@ -121,7 +126,7 @@ main(int argc, char *argv[]) p = ARGF(); if(p) { if(ndef%8 == 0) - defs = allocn(defs, ndef*sizeof(char *), + defs = allocn(defs, ndef*sizeof(char *), 8*sizeof(char *)); defs[ndef++] = p; dodefine(p); @@ -147,7 +152,7 @@ main(int argc, char *argv[]) * if we're writing acid to standard output, don't compile * concurrently, to avoid interleaving output. */ - if(((!debug['a'] && !debug['Z']) || debug['n']) && + if(((!debug['a'] && !debug['q'] && !debug['Q']) || debug['n']) && (p = getenv("NPROC")) != nil) nproc = atol(p); /* */ c = 0; @@ -220,8 +225,8 @@ compile(char *file, char **defs, int ndef) p = utfrune(outfile, 0); if(debug['a'] && debug['n']) strcat(p, ".acid"); - else if(debug['Z'] && debug['n']) - strcat(p, "_pickle.c"); + else if((debug['q'] || debug['Q']) && debug['n']) + strcat(p, ".go"); else { p[0] = '.'; p[1] = thechar; @@ -246,7 +251,7 @@ compile(char *file, char **defs, int ndef) * if we're writing acid to standard output, don't keep scratching * outbuf. */ - if((debug['a'] || debug['Z']) && !debug['n']) { + if((debug['a'] || debug['q'] || debug['Q']) && !debug['n']) { if (first) { outfile = 0; Binit(&outbuf, dup(1, -1), OWRITE); @@ -325,7 +330,7 @@ compile(char *file, char **defs, int ndef) newfile(file, -1); } yyparse(); - if(!debug['a'] && !debug['Z']) + if(!debug['a'] && !debug['q'] && !debug['Q']) gclean(); return nerrors; } @@ -1309,6 +1314,7 @@ cinit(void) fmtinstall('L', Lconv); fmtinstall('Q', Qconv); fmtinstall('|', VBconv); + fmtinstall('U', Uconv); } int @@ -1554,7 +1560,7 @@ setinclude(char *p) return; if(ninclude%8 == 0) - include = allocn(include, ninclude*sizeof(char *), + include = allocn(include, ninclude*sizeof(char *), 8*sizeof(char *)); include[ninclude++] = p; } @@ -1595,7 +1601,7 @@ ensuresymb(int32 n) if(symb == nil) { symb = alloc(NSYMB+1); nsymb = NSYMB; - } + } if(n > nsymb) { symb = allocn(symb, nsymb, n+1-nsymb); diff --git a/src/cmd/cc/lexbody b/src/cmd/cc/lexbody index 0bccc1733..24f9bdc85 100644 --- a/src/cmd/cc/lexbody +++ b/src/cmd/cc/lexbody @@ -88,47 +88,32 @@ pragincomplete(void) ; } -void -gethunk(void) -{ - hunk = malloc(NHUNK); - memset(hunk, 0, NHUNK); - nhunk = NHUNK; -} - void* alloc(int32 n) { void *p; - while((uintptr)hunk & MAXALIGN) { - hunk++; - nhunk--; + p = malloc(n); + if(p == nil) { + print("alloc out of mem\n"); + exit(1); } - while(nhunk < n) - gethunk(); - p = hunk; - nhunk -= n; - hunk += n; + memset(p, 0, n); return p; } void* -allocn(void *p, int32 on, int32 n) +allocn(void *p, int32 n, int32 d) { - void *q; - - q = (uchar*)p + on; - if(q != hunk || nhunk < n) { - while(nhunk < on+n) - gethunk(); - memmove(hunk, p, on); - p = hunk; - hunk += on; - nhunk -= on; + if(p == nil) + return alloc(n+d); + p = realloc(p, n+d); + if(p == nil) { + print("allocn out of mem\n"); + exit(1); } - hunk += n; - nhunk -= n; + if(d > 0) + memset((char*)p+n, 0, d); return p; } diff --git a/src/cmd/cc/pgen.c b/src/cmd/cc/pgen.c index a9d7f1ef4..5d17cafc9 100644 --- a/src/cmd/cc/pgen.c +++ b/src/cmd/cc/pgen.c @@ -586,8 +586,7 @@ bcomplex(Node *n, Node *c) *b->right = *nodconst(0); b->right->type = n->type; b->type = types[TLONG]; - cgen(b, Z); - return 0; + n = b; } bool64(n); boolgen(n, 1, Z); diff --git a/src/cmd/cc/pickle.c b/src/cmd/cc/pickle.c deleted file mode 100644 index 82cf5eb05..000000000 --- a/src/cmd/cc/pickle.c +++ /dev/null @@ -1,298 +0,0 @@ -// Inferno utils/cc/pickle.c -// http://code.google.com/p/inferno-os/source/browse/utils/cc/pickle.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 "cc.h" - -static char *kwd[] = -{ - "$adt", "$aggr", "$append", "$complex", "$defn", - "$delete", "$do", "$else", "$eval", "$head", "$if", - "$local", "$loop", "$return", "$tail", "$then", - "$union", "$whatis", "$while", -}; -static char picklestr[] = "\tbp = pickle(bp, ep, un, "; - -static char* -pmap(char *s) -{ - int i, bot, top, new; - - bot = 0; - top = bot + nelem(kwd) - 1; - while(bot <= top){ - new = bot + (top - bot)/2; - i = strcmp(kwd[new]+1, s); - if(i == 0) - return kwd[new]; - - if(i < 0) - bot = new + 1; - else - top = new - 1; - } - return s; -} - -Sym* -picklesue(Type *t) -{ - int h; - Sym *s; - - if(t != T) - for(h=0; h<nelem(hash); h++) - for(s = hash[h]; s != S; s = s->link) - if(s->suetag && s->suetag->link == t) - return s; - return 0; -} - -Sym* -picklefun(Type *t) -{ - int h; - Sym *s; - - for(h=0; h<nelem(hash); h++) - for(s = hash[h]; s != S; s = s->link) - if(s->type == t) - return s; - return 0; -} - -char picklechar[NTYPE]; -Init picklecinit[] = -{ - TCHAR, 'C', 0, - TUCHAR, 'b', 0, - TSHORT, 'd', 0, - TUSHORT, 'u', 0, - TLONG, 'D', 0, - TULONG, 'U', 0, - TVLONG, 'V', 0, - TUVLONG, 'W', 0, - TFLOAT, 'f', 0, - TDOUBLE, 'F', 0, - TARRAY, 'a', 0, - TIND, 'X', 0, - -1, 0, 0, -}; - -static void -pickleinit(void) -{ - Init *p; - - for(p=picklecinit; p->code >= 0; p++) - picklechar[p->code] = p->value; - - picklechar[TINT] = picklechar[TLONG]; - picklechar[TUINT] = picklechar[TULONG]; - if(types[TINT]->width != types[TLONG]->width) { - picklechar[TINT] = picklechar[TSHORT]; - picklechar[TUINT] = picklechar[TUSHORT]; - if(types[TINT]->width != types[TSHORT]->width) - warn(Z, "picklemember int not long or short"); - } - -} - -void -picklemember(Type *t, int32 off) -{ - Sym *s, *s1; - static int picklecharinit = 0; - - if(picklecharinit == 0) { - pickleinit(); - picklecharinit = 1; - } - s = t->sym; - switch(t->etype) { - default: - Bprint(&outbuf, " T%d\n", t->etype); - break; - - case TIND: - if(s == S) - Bprint(&outbuf, - "%s\"p\", (char*)addr+%d+_i*%d);\n", - picklestr, t->offset+off, t->width); - else - Bprint(&outbuf, - "%s\"p\", &addr->%s);\n", - picklestr, pmap(s->name)); - break; - - case TINT: - case TUINT: - case TCHAR: - case TUCHAR: - case TSHORT: - case TUSHORT: - case TLONG: - case TULONG: - case TVLONG: - case TUVLONG: - case TFLOAT: - case TDOUBLE: - if(s == S) - 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 < %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"); - break; - - case TSTRUCT: - case TUNION: - s1 = picklesue(t->link); - if(s1 == S) - break; - if(s == S) { - 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", - pmap(s1->name), pmap(s->name)); - } - break; - } -} - -void -pickletype(Type *t) -{ - Sym *s; - Type *l; - Io *i; - int n; - char *an; - - if(!debug['P']) - return; - if(debug['P'] > 1) { - n = 0; - for(i=iostack; i; i=i->link) - n++; - if(n > 1) - return; - } - s = picklesue(t->link); - if(s == S) - return; - switch(t->etype) { - default: - Bprint(&outbuf, "T%d\n", t->etype); - return; - - case TUNION: - case TSTRUCT: - if(debug['s']) - goto asmstr; - an = pmap(s->name); - - Bprint(&outbuf, "char *\npickle_%s(char *bp, char *ep, int un, %s *addr)\n{\n\tint _i = 0;\n\n\tUSED(_i);\n", an, an); - for(l = t->link; l != T; l = l->down) - picklemember(l, 0); - Bprint(&outbuf, "\treturn bp;\n}\n\n"); - break; - asmstr: - if(s == S) - break; - for(l = t->link; l != T; l = l->down) - if(l->sym != S) - Bprint(&outbuf, "#define\t%s.%s\t%d\n", - s->name, - l->sym->name, - l->offset); - break; - } -} - -void -picklevar(Sym *s) -{ - int n; - Io *i; - Type *t; - Sym *s1, *s2; - - if(!debug['P'] || debug['s']) - return; - if(debug['P'] > 1) { - n = 0; - for(i=iostack; i; i=i->link) - n++; - if(n > 1) - return; - } - t = s->type; - while(t && t->etype == TIND) - t = t->link; - if(t == T) - return; - if(t->etype == TENUM) { - Bprint(&outbuf, "%s = ", pmap(s->name)); - if(!typefd[t->etype]) - Bprint(&outbuf, "%lld;\n", s->vconst); - else - Bprint(&outbuf, "%f\n;", s->fconst); - return; - } - if(!typesu[t->etype]) - return; - s1 = picklesue(t->link); - if(s1 == S) - return; - switch(s->class) { - case CAUTO: - case CPARAM: - s2 = picklefun(thisfn); - if(s2) - Bprint(&outbuf, "complex %s %s:%s;\n", - pmap(s1->name), pmap(s2->name), pmap(s->name)); - break; - - case CSTATIC: - case CEXTERN: - case CGLOBL: - case CLOCAL: - Bprint(&outbuf, "complex %s %s;\n", - pmap(s1->name), pmap(s->name)); - break; - } -} diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go index 8689ac3da..2eae22aed 100644 --- a/src/cmd/cgo/ast.go +++ b/src/cmd/cgo/ast.go @@ -35,6 +35,10 @@ func parse(name string, flags uint) *ast.File { return ast1 } +func sourceLine(n ast.Node) int { + return fset.Position(n.Pos()).Line +} + // 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, @@ -69,10 +73,13 @@ func (f *File) ReadGo(name string) { if s.Name != nil { error(s.Path.Pos(), `cannot rename import "C"`) } - if s.Doc != nil { - f.Preamble += doc.CommentText(s.Doc) + "\n" - } else if len(d.Specs) == 1 && d.Doc != nil { - f.Preamble += doc.CommentText(d.Doc) + "\n" + cg := s.Doc + if cg == nil && len(d.Specs) == 1 { + cg = d.Doc + } + if cg != nil { + f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name) + f.Preamble += doc.CommentText(cg) + "\n" } } } @@ -298,6 +305,9 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{} f.walk(n.Stmt, "stmt", visit) case *ast.ExprStmt: f.walk(&n.X, "expr", visit) + case *ast.SendStmt: + f.walk(&n.Chan, "expr", visit) + f.walk(&n.Value, "expr", visit) case *ast.IncDecStmt: f.walk(&n.X, "expr", visit) case *ast.AssignStmt: @@ -336,8 +346,7 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{} f.walk(n.Assign, "stmt", visit) f.walk(n.Body, "stmt", visit) case *ast.CommClause: - f.walk(n.Lhs, "expr", visit) - f.walk(n.Rhs, "expr", visit) + f.walk(n.Comm, "stmt", visit) f.walk(n.Body, "stmt", visit) case *ast.SelectStmt: f.walk(n.Body, "stmt", visit) diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index 0f9204d7f..c4868345c 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -23,6 +23,15 @@ the package. For example: // #include <errno.h> import "C" +CFLAGS and LDFLAGS may be defined with pseudo #cgo directives +within these comments to tweak the behavior of gcc. Values defined +in multiple directives are concatenated together. For example: + + // #cgo CFLAGS: -DPNG_DEBUG=1 + // #cgo LDFLAGS: -lpng + // #include <png.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. diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index be3b8fe64..cadc6fae9 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -21,19 +21,22 @@ import ( "os" "strconv" "strings" + "unicode" ) 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", + "schar": "signed char", + "uchar": "unsigned char", + "ushort": "unsigned short", + "uint": "unsigned int", + "ulong": "unsigned long", + "longlong": "long long", + "ulonglong": "unsigned long long", + "complexfloat": "float complex", + "complexdouble": "double complex", } // cname returns the C name to use for C.s. @@ -57,6 +60,107 @@ func cname(s string) string { return s } +// ParseFlags extracts #cgo CFLAGS and LDFLAGS options from the file +// preamble. Multiple occurrences are concatenated with a separating space, +// even across files. +func (p *Package) ParseFlags(f *File, srcfile string) { + linesIn := strings.Split(f.Preamble, "\n", -1) + linesOut := make([]string, 0, len(linesIn)) + for _, line := range linesIn { + l := strings.TrimSpace(line) + if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(int(l[4])) { + linesOut = append(linesOut, line) + continue + } + + l = strings.TrimSpace(l[4:]) + fields := strings.Split(l, ":", 2) + if len(fields) != 2 { + fatal("%s: bad #cgo line: %s", srcfile, line) + } + + k := fields[0] + v := strings.TrimSpace(fields[1]) + if k != "CFLAGS" && k != "LDFLAGS" { + fatal("%s: unsupported #cgo option %s", srcfile, k) + } + args, err := splitQuoted(v) + if err != nil { + fatal("%s: bad #cgo option %s: %s", srcfile, k, err.String()) + } + if oldv, ok := p.CgoFlags[k]; ok { + p.CgoFlags[k] = oldv + " " + v + } else { + p.CgoFlags[k] = v + } + if k == "CFLAGS" { + p.GccOptions = append(p.GccOptions, args...) + } + } + f.Preamble = strings.Join(linesOut, "\n") +} + +// splitQuoted splits the string s around each instance of one or more consecutive +// white space characters while taking into account quotes and escaping, and +// returns an array of substrings of s or an empty list if s contains only white space. +// Single quotes and double quotes are recognized to prevent splitting within the +// quoted region, and are removed from the resulting substrings. If a quote in s +// isn't closed err will be set and r will have the unclosed argument as the +// last element. The backslash is used for escaping. +// +// For example, the following string: +// +// `a b:"c d" 'e''f' "g\""` +// +// Would be parsed as: +// +// []string{"a", "b:c d", "ef", `g"`} +// +func splitQuoted(s string) (r []string, err os.Error) { + var args []string + arg := make([]int, len(s)) + escaped := false + quoted := false + quote := 0 + i := 0 + for _, rune := range s { + switch { + case escaped: + escaped = false + case rune == '\\': + escaped = true + continue + case quote != 0: + if rune == quote { + quote = 0 + continue + } + case rune == '"' || rune == '\'': + quoted = true + quote = rune + continue + case unicode.IsSpace(rune): + if quoted || i > 0 { + quoted = false + args = append(args, string(arg[:i])) + i = 0 + } + continue + } + arg[i] = rune + i++ + } + if quoted || i > 0 { + args = append(args, string(arg[:i])) + } + if quote != 0 { + err = os.ErrorString("unclosed quote") + } else if escaped { + err = os.ErrorString("unfinished escaping") + } + return args, err +} + // 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. @@ -205,9 +309,7 @@ func (p *Package) guessKinds(f *File) []*Name { 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()) - } + // the user will see any compiler errors when the code is compiled later. continue } line = line[9:] @@ -568,10 +670,6 @@ func runGcc(stdin []byte, args []string) (string, string) { os.Stderr.Write(stderr) } if !ok { - 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) } @@ -591,6 +689,7 @@ type typeConv struct { int8, int16, int32, int64 ast.Expr uint8, uint16, uint32, uint64, uintptr ast.Expr float32, float64 ast.Expr + complex64, complex128 ast.Expr void ast.Expr unsafePointer ast.Expr string ast.Expr @@ -617,6 +716,8 @@ func (c *typeConv) Init(ptrSize int64) { c.uintptr = c.Ident("uintptr") c.float32 = c.Ident("float32") c.float64 = c.Ident("float64") + c.complex64 = c.Ident("complex64") + c.complex128 = c.Ident("complex128") c.unsafePointer = c.Ident("unsafe.Pointer") c.void = c.Ident("void") c.string = c.Ident("string") @@ -648,6 +749,8 @@ var dwarfToName = map[string]string{ "long long int": "longlong", "long long unsigned int": "ulonglong", "signed char": "schar", + "float complex": "complexfloat", + "double complex": "complexdouble", } // Type returns a *Type with the same memory layout as @@ -749,6 +852,19 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { t.Align = c.ptrSize } + case *dwarf.ComplexType: + switch t.Size { + default: + fatal("unexpected: %d-byte complex type - %s", t.Size, dtype) + case 8: + t.Go = c.complex64 + case 16: + t.Go = c.complex128 + } + if t.Align = t.Size; t.Align >= c.ptrSize { + t.Align = c.ptrSize + } + case *dwarf.FuncType: // No attempt at translation: would enable calls // directly between worlds, but we need to moderate those. diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index 942bda5f4..b15d34527 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -20,6 +20,7 @@ import ( "os" "reflect" "strings" + "runtime" ) // A Package collects information about the package we're going to write. @@ -28,6 +29,7 @@ type Package struct { PackagePath string PtrSize int64 GccOptions []string + CgoFlags map[string]string // #cgo flags (CFLAGS, LDFLAGS) Written map[string]bool Name map[string]*Name // accumulated Name from Files Typedef map[string]ast.Expr // accumulated Typedef from Files @@ -97,7 +99,8 @@ type FuncType struct { } func usage() { - fmt.Fprint(os.Stderr, "usage: cgo [compiler options] file.go ...\n") + fmt.Fprint(os.Stderr, "usage: cgo -- [compiler options] file.go ...\n") + flag.PrintDefaults() os.Exit(2) } @@ -127,6 +130,13 @@ func main() { // specialized knowledge gcc has about where to look for imported // symbols and which ones to use. syms, imports := dynimport(*dynobj) + if runtime.GOOS == "windows" { + for _, sym := range syms { + ss := strings.Split(sym, ":", -1) + fmt.Printf("#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1])) + } + return + } for _, sym := range syms { fmt.Printf("#pragma dynimport %s %s %q\n", sym, sym, "") } @@ -152,7 +162,12 @@ func main() { if i == len(args) { usage() } - gccOptions, goFiles := args[0:i], args[i:] + + // Copy it to a new slice so it can grow. + gccOptions := make([]string, i) + copy(gccOptions, args[0:i]) + + goFiles := args[i:] arch := os.Getenv("GOARCH") if arch == "" { @@ -171,6 +186,7 @@ func main() { p := &Package{ PtrSize: ptrSize, GccOptions: gccOptions, + CgoFlags: make(map[string]string), Written: make(map[string]bool), } @@ -190,11 +206,17 @@ func main() { } cPrefix = fmt.Sprintf("_%x", h.Sum()[0:6]) - for _, input := range goFiles { + fs := make([]*File, len(goFiles)) + for i, input := range goFiles { + // Parse flags for all files before translating due to CFLAGS. f := new(File) - // Reset f.Preamble so that we don't end up with conflicting headers / defines - f.Preamble = "" f.ReadGo(input) + p.ParseFlags(f, input) + fs[i] = f + } + + for i, input := range goFiles { + f := fs[i] p.Translate(f) for _, cref := range f.Ref { switch cref.Context { diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index c3f9ae60b..ede8f57d8 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -8,6 +8,7 @@ import ( "bytes" "debug/elf" "debug/macho" + "debug/pe" "fmt" "go/ast" "go/printer" @@ -32,9 +33,17 @@ func (p *Package) writeDefs() { fc := creat("_cgo_defun.c") fm := creat("_cgo_main.c") + fflg := creat("_cgo_flags") + for k, v := range p.CgoFlags { + fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, v) + } + fflg.Close() + // 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") + fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n") + fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n") + fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n") // Write second Go output: definitions of _C_xxx. // In a separate file so that the import of "unsafe" does not @@ -101,12 +110,14 @@ func dynimport(obj string) (syms, imports []string) { ImportedSymbols() ([]string, os.Error) } var isMacho bool - var err1, err2 os.Error + var err1, err2, err3 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) + if f, err2 = pe.Open(obj); err2 != nil { + if f, err3 = macho.Open(obj); err3 != nil { + fatal("cannot parse %s as ELF (%v) or PE (%v) or Mach-O (%v)", obj, err1, err2, err3) + } + isMacho = true } - isMacho = true } var err os.Error diff --git a/src/cmd/ebnflint/ebnflint.go b/src/cmd/ebnflint/ebnflint.go index 10cb5b387..5eb398735 100644 --- a/src/cmd/ebnflint/ebnflint.go +++ b/src/cmd/ebnflint/ebnflint.go @@ -88,6 +88,7 @@ func main() { src, err := ioutil.ReadFile(filename) if err != nil { scanner.PrintError(os.Stderr, err) + os.Exit(1) } if path.Ext(filename) == ".html" { @@ -97,9 +98,11 @@ func main() { grammar, err := ebnf.Parse(fset, filename, src) if err != nil { scanner.PrintError(os.Stderr, err) + os.Exit(1) } if err = ebnf.Verify(fset, grammar, *start); err != nil { scanner.PrintError(os.Stderr, err) + os.Exit(1) } } diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c index a3785e871..ed20e7e8b 100644 --- a/src/cmd/gc/align.c +++ b/src/cmd/gc/align.c @@ -412,11 +412,9 @@ typeinit(void) isfloat[TFLOAT32] = 1; isfloat[TFLOAT64] = 1; - isfloat[TFLOAT] = 1; iscomplex[TCOMPLEX64] = 1; iscomplex[TCOMPLEX128] = 1; - iscomplex[TCOMPLEX] = 1; isptr[TPTR32] = 1; isptr[TPTR64] = 1; diff --git a/src/cmd/gc/builtin.c.boot b/src/cmd/gc/builtin.c.boot index 380abc642..48f45293f 100644 --- a/src/cmd/gc/builtin.c.boot +++ b/src/cmd/gc/builtin.c.boot @@ -66,16 +66,18 @@ char *runtimeimport = "func \"\".mapiter2 (hiter *any) (key any, val any)\n" "func \"\".makechan (elem *uint8, hint int64) chan any\n" "func \"\".chanrecv1 (hchan <-chan any) any\n" - "func \"\".chanrecv2 (hchan <-chan any) (elem any, pres bool)\n" + "func \"\".chanrecv3 (hchan <-chan any) (elem any, closed bool)\n" "func \"\".chansend1 (hchan chan<- any, elem any)\n" - "func \"\".chansend2 (hchan chan<- any, elem any) bool\n" "func \"\".closechan (hchan any)\n" "func \"\".closedchan (hchan any) bool\n" + "func \"\".selectnbsend (hchan chan<- any, elem any) bool\n" + "func \"\".selectnbrecv (elem *any, hchan <-chan any) bool\n" "func \"\".newselect (size int) *uint8\n" "func \"\".selectsend (sel *uint8, hchan chan<- any, elem any) bool\n" "func \"\".selectrecv (sel *uint8, hchan <-chan any, elem *any) bool\n" "func \"\".selectdefault (sel *uint8) bool\n" "func \"\".selectgo (sel *uint8)\n" + "func \"\".block ()\n" "func \"\".makeslice (typ *uint8, nel int64, cap int64) []any\n" "func \"\".sliceslice1 (old []any, lb uint64, width uint64) []any\n" "func \"\".sliceslice (old []any, lb uint64, hb uint64, width uint64) []any\n" diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c index 72e67a634..0ee693c02 100644 --- a/src/cmd/gc/const.c +++ b/src/cmd/gc/const.c @@ -980,10 +980,10 @@ defaultlit(Node **np, Type *t) n->type = types[TINT]; goto num; case CTFLT: - n->type = types[TFLOAT]; + n->type = types[TFLOAT64]; goto num; case CTCPLX: - n->type = types[TCOMPLEX]; + n->type = types[TCOMPLEX128]; goto num; num: if(t != T) { @@ -1034,13 +1034,13 @@ defaultlit2(Node **lp, Node **rp, int force) if(!force) return; if(isconst(l, CTCPLX) || isconst(r, CTCPLX)) { - convlit(lp, types[TCOMPLEX]); - convlit(rp, types[TCOMPLEX]); + convlit(lp, types[TCOMPLEX128]); + convlit(rp, types[TCOMPLEX128]); return; } if(isconst(l, CTFLT) || isconst(r, CTFLT)) { - convlit(lp, types[TFLOAT]); - convlit(rp, types[TFLOAT]); + convlit(lp, types[TFLOAT64]); + convlit(rp, types[TFLOAT64]); return; } convlit(lp, types[TINT]); diff --git a/src/cmd/gc/cplx.c b/src/cmd/gc/cplx.c index e25f3cabb..3ec9fe5a2 100644 --- a/src/cmd/gc/cplx.c +++ b/src/cmd/gc/cplx.c @@ -84,7 +84,7 @@ maybe: case OSUB: case OMUL: case OMINUS: - case OCMPLX: + case OCOMPLEX: case OREAL: case OIMAG: goto yes; @@ -120,7 +120,7 @@ complexgen(Node *n, Node *res) // pick off float/complex opcodes switch(n->op) { - case OCMPLX: + case OCOMPLEX: if(res->addable) { subnode(&n1, &n2, res); tempname(&tmp, n1.type); @@ -195,7 +195,7 @@ complexgen(Node *n, Node *res) case OSUB: case OMUL: case OMINUS: - case OCMPLX: + case OCOMPLEX: case OREAL: case OIMAG: break; diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 73ea5b976..bf84c12a1 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -356,7 +356,7 @@ enum OARRAY, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, - OAS, OAS2, OAS2MAPW, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE, OASOP, + OAS, OAS2, OAS2MAPW, OAS2FUNC, OAS2RECVCLOSED, OAS2MAPR, OAS2DOTTYPE, OASOP, OBAD, OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OCAP, @@ -383,14 +383,14 @@ enum ONOT, OCOM, OPLUS, OMINUS, OOROR, OPANIC, OPRINT, OPRINTN, - OSEND, OSENDNB, + OSEND, OSLICE, OSLICEARR, OSLICESTR, ORECOVER, ORECV, ORUNESTR, OSELRECV, OIOTA, - OREAL, OIMAG, OCMPLX, + OREAL, OIMAG, OCOMPLEX, // stmts OBLOCK, @@ -440,11 +440,9 @@ enum TCOMPLEX64, // 12 TCOMPLEX128, - TCOMPLEX, TFLOAT32, // 15 TFLOAT64, - TFLOAT, TBOOL, // 18 diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index 917265758..994840ee8 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -93,9 +93,10 @@ static void fixlbrace(int); %type <type> hidden_type_func %type <type> hidden_type_recv_chan hidden_type_non_recv_chan +%left LCOMM /* outside the usual hierarchy; here for good error messages */ + %left LOROR %left LANDAND -%left LCOMM %left LEQ LNE LLE LGE LLT LGT %left '+' '-' '|' '^' %left '*' '/' '%' '&' LLSH LRSH LANDNOT @@ -421,11 +422,18 @@ simple_stmt: | expr_list LCOLAS expr_list { if($3->n->op == OTYPESW) { + Node *n; + + n = N; if($3->next != nil) yyerror("expr.(type) must be alone in list"); - else if($1->next != nil) + if($1->next != nil) yyerror("argument count mismatch: %d = %d", count($1), 1); - $$ = nod(OTYPESW, $1->n, $3->n->right); + else if($1->n->op != ONAME && $1->n->op != OTYPE && $1->n->op != ONONAME) + yyerror("invalid variable name %#N in type switch", $1->n); + else + n = $1->n; + $$ = nod(OTYPESW, n, $3->n->right); break; } $$ = colas($1, $3); @@ -764,6 +772,7 @@ expr: { $$ = nod(ORSH, $1, $3); } + /* not an expression anymore, but left in so we can give a good error */ | expr LCOMM expr { $$ = nod(OSEND, $1, $3); diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 0f1acd2fc..45b1257fa 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -1531,7 +1531,7 @@ static struct "cap", LNAME, Txxx, OCAP, "close", LNAME, Txxx, OCLOSE, "closed", LNAME, Txxx, OCLOSED, - "cmplx", LNAME, Txxx, OCMPLX, + "complex", LNAME, Txxx, OCOMPLEX, "copy", LNAME, Txxx, OCOPY, "imag", LNAME, Txxx, OIMAG, "len", LNAME, Txxx, OLEN, diff --git a/src/cmd/gc/print.c b/src/cmd/gc/print.c index 6bb1f026b..695a5a397 100644 --- a/src/cmd/gc/print.c +++ b/src/cmd/gc/print.c @@ -48,6 +48,7 @@ exprfmt(Fmt *f, Node *n, int prec) case ODOTMETH: case ODOTTYPE: case ODOTTYPE2: + case OXDOT: case OARRAYBYTESTR: case OCAP: case OCLOSE: @@ -365,8 +366,8 @@ exprfmt(Fmt *f, Node *n, int prec) fmtprint(f, ")"); break; - case OCMPLX: - fmtprint(f, "cmplx("); + case OCOMPLEX: + fmtprint(f, "complex("); exprfmt(f, n->left, 0); fmtprint(f, ", "); exprfmt(f, n->right, 0); diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c index dca3a5454..4ee8f39a7 100644 --- a/src/cmd/gc/range.c +++ b/src/cmd/gc/range.c @@ -93,6 +93,7 @@ walkrange(Node *n) Node *ohv1, *hv1, *hv2; // hidden (old) val 1, 2 Node *ha, *hit; // hidden aggregate, iterator Node *hn, *hp; // hidden len, pointer + Node *hb; // hidden bool Node *a, *v1, *v2; // not hidden aggregate, val 1, 2 Node *fn, *tmp; NodeList *body, *init; @@ -199,9 +200,15 @@ walkrange(Node *n) case TCHAN: hv1 = nod(OXXX, N, n); tempname(hv1, t->type); - - n->ntest = nod(ONOT, nod(OCLOSED, ha, N), N); - n->ntest->ninit = list1(nod(OAS, hv1, nod(ORECV, ha, N))); + hb = nod(OXXX, N, N); + tempname(hb, types[TBOOL]); + + n->ntest = nod(ONOT, hb, N); + a = nod(OAS2RECVCLOSED, N, N); + a->typecheck = 1; + a->list = list(list1(hv1), hb); + a->rlist = list1(nod(ORECV, ha, N)); + n->ntest->ninit = list1(a); body = list1(nod(OAS, v1, hv1)); break; diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c index b31eb5154..36c245d47 100644 --- a/src/cmd/gc/reflect.c +++ b/src/cmd/gc/reflect.c @@ -419,10 +419,8 @@ enum { KindUint32, KindUint64, KindUintptr, - KindFloat, KindFloat32, KindFloat64, - KindComplex, KindComplex64, KindComplex128, KindArray, @@ -453,7 +451,6 @@ kinds[] = [TINT64] = KindInt64, [TUINT64] = KindUint64, [TUINTPTR] = KindUintptr, - [TFLOAT] = KindFloat, [TFLOAT32] = KindFloat32, [TFLOAT64] = KindFloat64, [TBOOL] = KindBool, @@ -466,7 +463,6 @@ kinds[] = [TMAP] = KindMap, [TARRAY] = KindArray, [TFUNC] = KindFunc, - [TCOMPLEX] = KindComplex, [TCOMPLEX64] = KindComplex64, [TCOMPLEX128] = KindComplex128, }; @@ -485,10 +481,8 @@ structnames[] = [TINT64] = "*runtime.IntType", [TUINT64] = "*runtime.UintType", [TUINTPTR] = "*runtime.UintType", - [TCOMPLEX] = "*runtime.ComplexType", [TCOMPLEX64] = "*runtime.ComplexType", [TCOMPLEX128] = "*runtime.ComplexType", - [TFLOAT] = "*runtime.FloatType", [TFLOAT32] = "*runtime.FloatType", [TFLOAT64] = "*runtime.FloatType", [TBOOL] = "*runtime.BoolType", @@ -542,7 +536,6 @@ haspointers(Type *t) case TINT64: case TUINT64: case TUINTPTR: - case TFLOAT: case TFLOAT32: case TFLOAT64: case TBOOL: diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index 174bc050e..bf7d045c0 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -92,17 +92,20 @@ func mapiter2(hiter *any) (key any, val any) // *byte is really *runtime.Type func makechan(elem *byte, hint int64) (hchan chan any) func chanrecv1(hchan <-chan any) (elem any) -func chanrecv2(hchan <-chan any) (elem any, pres bool) +func chanrecv3(hchan <-chan any) (elem any, closed bool) func chansend1(hchan chan<- any, elem any) -func chansend2(hchan chan<- any, elem any) (pres bool) func closechan(hchan any) func closedchan(hchan any) bool +func selectnbsend(hchan chan<- any, elem any) bool +func selectnbrecv(elem *any, hchan <-chan any) bool + func newselect(size int) (sel *byte) func selectsend(sel *byte, hchan chan<- any, elem any) (selected bool) func selectrecv(sel *byte, hchan <-chan any, elem *any) (selected bool) func selectdefault(sel *byte) (selected bool) func selectgo(sel *byte) +func block() func makeslice(typ *byte, nel int64, cap int64) (ary []any) func sliceslice1(old []any, lb uint64, width uint64) (ary []any) diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c index 1a3771311..5686e9599 100644 --- a/src/cmd/gc/select.c +++ b/src/cmd/gc/select.c @@ -45,27 +45,23 @@ typecheckselect(Node *sel) 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. + // convert x = <-c into OSELRECV(x, <-c). + // remove implicit conversions; the eventual assignment + // will reintroduce them. 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; } n->op = OSELRECV; - n->right = n->right->left; break; case ORECV: - // convert <-c into OSELRECV(N, c) - n->op = OSELRECV; - n->right = n->left; - n->left = N; + // convert <-c into OSELRECV(N, <-c) + n = nod(OSELRECV, N, n); + ncase->left = n; break; case OSEND: @@ -81,11 +77,149 @@ typecheckselect(Node *sel) void walkselect(Node *sel) { - int lno; - Node *n, *ncase, *r, *a, *tmp, *var; + int lno, i; + Node *n, *r, *a, *tmp, *var, *cas, *dflt, *ch; NodeList *l, *init; - + + if(sel->list == nil && sel->xoffset != 0) + fatal("double walkselect"); // already rewrote + lno = setlineno(sel); + i = count(sel->list); + + // optimization: zero-case select + if(i == 0) { + sel->nbody = list1(mkcall("block", nil, nil)); + goto out; + } + + // optimization: one-case select: single op. + if(i == 1) { + cas = sel->list->n; + l = cas->ninit; + if(cas->left != N) { // not default: + n = cas->left; + l = concat(l, n->ninit); + n->ninit = nil; + switch(n->op) { + default: + fatal("select %O", n->op); + + case OSEND: + ch = cheapexpr(n->left, &l); + n->left = ch; + break; + + case OSELRECV: + r = n->right; + ch = cheapexpr(r->left, &l); + r->left = ch; + + if(n->left == N) + n = r; + else { + n = nod(OAS, n->left, r); + typecheck(&n, Etop); + } + break; + } + + // if ch == nil { block() }; n; + a = nod(OIF, N, N); + a->ntest = nod(OEQ, ch, nodnil()); + a->nbody = list1(mkcall("block", nil, &l)); + typecheck(&a, Etop); + l = list(l, a); + l = list(l, n); + } + l = concat(l, cas->nbody); + sel->nbody = l; + goto out; + } + + // introduce temporary variables for OSELRECV where needed. + // this rewrite is used by both the general code and the next optimization. + for(l=sel->list; l; l=l->next) { + cas = l->n; + n = cas->left; + if(n == N) + continue; + switch(n->op) { + case OSELRECV: + ch = n->right->left; + + // If we can use the address of the target without + // violating addressability or order of operations, do so. + // Otherwise introduce a temporary. + // Also introduce a temporary for := variables that escape, + // so that we can delay the heap allocation until the case + // is selected. + if(n->left == N || isblank(n->left)) + n->left = nodnil(); + else if(n->left->op == ONAME && + (!n->colas || (n->class&PHEAP) == 0) && + convertop(ch->type->type, n->left->type, nil) == OCONVNOP) { + n->left = nod(OADDR, n->left, N); + n->left->etype = 1; // pointer does not escape + typecheck(&n->left, Erv); + } else { + tmp = nod(OXXX, N, N); + tempname(tmp, ch->type->type); + a = nod(OADDR, tmp, N); + a->etype = 1; // pointer does not escape + typecheck(&a, Erv); + r = nod(OAS, n->left, tmp); + typecheck(&r, Etop); + cas->nbody = concat(n->ninit, cas->nbody); + n->ninit = nil; + cas->nbody = concat(list1(r), cas->nbody); + n->left = a; + } + } + } + + // optimization: two-case select but one is default: single non-blocking op. + if(i == 2 && (sel->list->n->left == nil || sel->list->next->n->left == nil)) { + if(sel->list->n->left == nil) { + cas = sel->list->next->n; + dflt = sel->list->n; + } else { + dflt = sel->list->next->n; + cas = sel->list->n; + } + + n = cas->left; + r = nod(OIF, N, N); + r->ninit = cas->ninit; + switch(n->op) { + default: + fatal("select %O", n->op); + + case OSEND: + // if c != nil && selectnbsend(c, v) { body } else { default body } + ch = cheapexpr(n->left, &r->ninit); + r->ntest = nod(OANDAND, nod(ONE, ch, nodnil()), + mkcall1(chanfn("selectnbsend", 2, ch->type), + types[TBOOL], &r->ninit, ch, n->right)); + break; + + case OSELRECV: + // if c != nil && selectnbrecv(&v, c) { body } else { default body } + r = nod(OIF, N, N); + r->ninit = cas->ninit; + ch = cheapexpr(n->right->left, &r->ninit); + r->ntest = nod(OANDAND, nod(ONE, ch, nodnil()), + mkcall1(chanfn("selectnbrecv", 2, ch->type), + types[TBOOL], &r->ninit, n->left, ch)); + break; + } + typecheck(&r->ntest, Erv); + r->nbody = cas->nbody; + r->nelse = concat(dflt->ninit, dflt->nbody); + sel->nbody = list1(r); + goto out; + } + init = sel->ninit; sel->ninit = nil; @@ -96,16 +230,13 @@ walkselect(Node *sel) typecheck(&r, Etop); init = list(init, r); - if(sel->list == nil && sel->xoffset != 0) - fatal("double walkselect"); // already rewrote - // register cases for(l=sel->list; l; l=l->next) { - ncase = l->n; - n = ncase->left; + cas = l->n; + n = cas->left; r = nod(OIF, N, N); - r->nbody = ncase->ninit; - ncase->ninit = nil; + r->nbody = cas->ninit; + cas->ninit = nil; if(n != nil) { r->nbody = concat(r->nbody, n->ninit); n->ninit = nil; @@ -113,29 +244,24 @@ walkselect(Node *sel) if(n == nil) { // selectdefault(sel *byte); r->ntest = mkcall("selectdefault", types[TBOOL], &init, var); - } else if(n->op == OSEND) { - // selectsend(sel *byte, hchan *chan any, elem any) (selected bool); - r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL], &init, var, n->left, n->right); - } else if(n->op == OSELRECV) { - tmp = N; - if(n->left == N) - a = nodnil(); - else { - // introduce temporary until we're sure this will succeed. - tmp = nod(OXXX, N, N); - tempname(tmp, n->right->type->type); - a = nod(OADDR, tmp, N); - } - // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); - r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->type), types[TBOOL], &init, var, n->right, a); - if(tmp != N) { - a = nod(OAS, n->left, tmp); - typecheck(&a, Etop); - r->nbody = list(r->nbody, a); + } else { + switch(n->op) { + default: + fatal("select %O", n->op); + + case OSEND: + // selectsend(sel *byte, hchan *chan any, elem any) (selected bool); + r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL], + &init, var, n->left, n->right); + break; + case OSELRECV: + // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); + r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->left->type), types[TBOOL], + &init, var, n->right->left, n->left); + break; } - } else - fatal("select %O", n->op); - r->nbody = concat(r->nbody, ncase->nbody); + } + r->nbody = concat(r->nbody, cas->nbody); r->nbody = list(r->nbody, nod(OBREAK, N, N)); init = list(init, r); } @@ -143,8 +269,9 @@ walkselect(Node *sel) // run the select init = list(init, mkcall("selectgo", T, nil, var)); sel->nbody = init; - sel->list = nil; - walkstmtlist(init); +out: + sel->list = nil; + walkstmtlist(sel->nbody); lineno = lno; } diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c index 19ee3327b..31781646d 100644 --- a/src/cmd/gc/sinit.c +++ b/src/cmd/gc/sinit.c @@ -94,7 +94,7 @@ init1(Node *n, NodeList **out) case OAS2FUNC: case OAS2MAPR: case OAS2DOTTYPE: - case OAS2RECV: + case OAS2RECVCLOSED: if(n->defn->initorder) break; n->defn->initorder = 1; @@ -917,14 +917,12 @@ gen_as_init(Node *n) case TPTR64: case TFLOAT32: case TFLOAT64: - case TFLOAT: gused(N); // in case the data is the dest of a goto gdata(&nam, nr, nr->type->width); break; case TCOMPLEX64: case TCOMPLEX128: - case TCOMPLEX: gused(N); // in case the data is the dest of a goto gdatacomplex(&nam, nr->val.u.cval); break; diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 3c4501096..cb5e2a831 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -836,7 +836,7 @@ goopnames[] = [OCASE] = "case", [OCLOSED] = "closed", [OCLOSE] = "close", - [OCMPLX] = "cmplx", + [OCOMPLEX] = "complex", [OCOM] = "^", [OCONTINUE] = "continue", [OCOPY] = "copy", @@ -993,10 +993,8 @@ etnames[] = [TINT64] = "INT64", [TUINT64] = "UINT64", [TUINTPTR] = "UINTPTR", - [TFLOAT] = "FLOAT", [TFLOAT32] = "FLOAT32", [TFLOAT64] = "FLOAT64", - [TCOMPLEX] = "COMPLEX", [TCOMPLEX64] = "COMPLEX64", [TCOMPLEX128] = "COMPLEX128", [TBOOL] = "BOOL", @@ -1117,10 +1115,8 @@ basicnames[] = [TINT64] = "int64", [TUINT64] = "uint64", [TUINTPTR] = "uintptr", - [TFLOAT] = "float", [TFLOAT32] = "float32", [TFLOAT64] = "float64", - [TCOMPLEX] = "complex", [TCOMPLEX64] = "complex64", [TCOMPLEX128] = "complex128", [TBOOL] = "bool", @@ -1752,8 +1748,6 @@ int cplxsubtype(int et) { switch(et) { - case TCOMPLEX: - return TFLOAT; case TCOMPLEX64: return TFLOAT32; case TCOMPLEX128: diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index ca114d47c..931d0327a 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -18,7 +18,7 @@ static int onearg(Node*, char*, ...); static int twoarg(Node*); static int lookdot(Node*, Type*, int); static int looktypedot(Node*, Type*, int); -static void typecheckaste(int, int, Type*, NodeList*, char*); +static void typecheckaste(int, Node*, int, Type*, NodeList*, char*); static Type* lookdot1(Sym *s, Type *t, Type *f, int); static int nokeys(NodeList*); static void typecheckcomplit(Node**); @@ -56,6 +56,34 @@ typechecklist(NodeList *l, int top) typecheck(&l->n, top); } +static char* typekind[] = { + [TINT] = "int", + [TUINT] = "uint", + [TINT8] = "int8", + [TUINT8] = "uint8", + [TINT16] = "int16", + [TUINT16] = "uint16", + [TINT32] = "int32", + [TUINT32] = "uint32", + [TINT64] = "int64", + [TUINT64] = "uint64", + [TUINTPTR] = "uintptr", + [TCOMPLEX64] = "complex64", + [TCOMPLEX128] = "complex128", + [TFLOAT32] = "float32", + [TFLOAT64] = "float64", + [TBOOL] = "bool", + [TSTRING] = "string", + [TPTR32] = "pointer", + [TPTR64] = "pointer", + [TSTRUCT] = "struct", + [TINTER] = "interface", + [TCHAN] = "chan", + [TMAP] = "map", + [TARRAY] = "array", + [TFUNC] = "func", +}; + /* * type check node *np. * replaces *np with a new pointer in some cases. @@ -372,21 +400,25 @@ reswitch: et = t->etype; } if(t->etype != TIDEAL && !eqtype(l->type, r->type)) { - badbinary: defaultlit2(&l, &r, 1); - yyerror("invalid operation: %#N (type %T %#O %T)", n, l->type, op, r->type); + yyerror("invalid operation: %#N (mismatched types %T and %T)", n, l->type, r->type); + goto error; + } + if(!okfor[op][et]) { + notokfor: + yyerror("invalid operation: %#N (operator %#O not defined on %s)", n, op, typekind[et]); goto error; } - if(!okfor[op][et]) - goto badbinary; // okfor allows any array == array; // restrict to slice == nil and nil == slice. if(l->type->etype == TARRAY && !isslice(l->type)) - goto badbinary; + goto notokfor; if(r->type->etype == TARRAY && !isslice(r->type)) - goto badbinary; - if(isslice(l->type) && !isnil(l) && !isnil(r)) - goto badbinary; + goto notokfor; + if(isslice(l->type) && !isnil(l) && !isnil(r)) { + yyerror("invalid operation: %#N (slice can only be compared to nil)", n); + goto error; + } t = l->type; if(iscmp[n->op]) { evconst(n); @@ -472,7 +504,7 @@ reswitch: l = n->left; if((t = l->type) == T) goto error; - if(!(top & Eindir)) + if(!(top & Eindir) && !n->etype) addrescapes(n->left); n->type = ptrto(t); goto ret; @@ -636,6 +668,10 @@ reswitch: goto ret; case OSEND: + if(top & Erv) { + yyerror("send statement %#N used as value; use select for non-blocking send", n); + goto error; + } ok |= Etop | Erv; l = typecheck(&n->left, Erv); typecheck(&n->right, Erv); @@ -659,10 +695,6 @@ reswitch: // TODO: more aggressive n->etype = 0; n->type = T; - if(top & Erv) { - n->op = OSENDNB; - n->type = types[TBOOL]; - } goto ret; case OSLICE: @@ -769,7 +801,7 @@ reswitch: case ODOTMETH: n->op = OCALLMETH; - typecheckaste(OCALL, 0, getthisx(t), list1(l->left), "method receiver"); + typecheckaste(OCALL, n->left, 0, getthisx(t), list1(l->left), "method receiver"); break; default: @@ -780,7 +812,7 @@ reswitch: } break; } - typecheckaste(OCALL, n->isddd, getinargx(t), n->list, "function argument"); + typecheckaste(OCALL, n->left, n->isddd, getinargx(t), n->list, "function argument"); ok |= Etop; if(t->outtuple == 0) goto ret; @@ -852,7 +884,7 @@ reswitch: n->type = types[TINT]; goto ret; - case OCMPLX: + case OCOMPLEX: ok |= Erv; if(twoarg(n) < 0) goto error; @@ -865,7 +897,7 @@ reswitch: n->right = r; if(l->type->etype != r->type->etype) { badcmplx: - yyerror("invalid operation: %#N (cmplx of types %T, %T)", n, l->type, r->type); + yyerror("invalid operation: %#N (complex of types %T, %T)", n, l->type, r->type); goto error; } switch(l->type->etype) { @@ -874,9 +906,6 @@ reswitch: case TIDEAL: t = types[TIDEAL]; break; - case TFLOAT: - t = types[TCOMPLEX]; - break; case TFLOAT32: t = types[TCOMPLEX64]; break; @@ -1217,7 +1246,7 @@ reswitch: } if(curfn->type->outnamed && n->list == nil) goto ret; - typecheckaste(ORETURN, 0, getoutargx(curfn->type), n->list, "return argument"); + typecheckaste(ORETURN, nil, 0, getoutargx(curfn->type), n->list, "return argument"); goto ret; case OSELECT: @@ -1562,7 +1591,7 @@ nokeys(NodeList *l) * typecheck assignment: type list = expression list */ static void -typecheckaste(int op, int isddd, Type *tstruct, NodeList *nl, char *desc) +typecheckaste(int op, Node *call, int isddd, Type *tstruct, NodeList *nl, char *desc) { Type *t, *tl, *tn; Node *n; @@ -1581,16 +1610,24 @@ typecheckaste(int op, int isddd, Type *tstruct, NodeList *nl, char *desc) if(tl->isddd) { 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); + if(assignop(tn->type, tl->type->type, &why) == 0) { + if(call != N) + yyerror("cannot use %T as type %T in argument to %#N%s", tn->type, tl->type->type, desc, call, why); + else + yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type->type, desc, why); + } } 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); + if(assignop(tn->type, tl->type, &why) == 0) { + if(call != N) + yyerror("cannot use %T as type %T in argument to %#N%s", tn->type, tl->type, desc, call, why); + else + yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type, desc, why); + } tn = tn->down; } if(tn != T) @@ -1635,19 +1672,29 @@ typecheckaste(int op, int isddd, Type *tstruct, NodeList *nl, char *desc) } if(nl != nil) goto toomany; - if(isddd) - yyerror("invalid use of ... in %#O", op); + if(isddd) { + if(call != N) + yyerror("invalid use of ... in call to %#N", call); + else + yyerror("invalid use of ... in %#O", op); + } out: lineno = lno; return; notenough: - yyerror("not enough arguments to %#O", op); + if(call != N) + yyerror("not enough arguments in call to %#N", call); + else + yyerror("not enough arguments to %#O", op); goto out; toomany: - yyerror("too many arguments to %#O", op); + if(call != N) + yyerror("too many arguments in call to %#N", call); + else + yyerror("too many arguments to %#O", op); goto out; } @@ -2329,8 +2376,8 @@ typecheckas2(Node *n) n->op = OAS2MAPR; goto common; case ORECV: - n->op = OAS2RECV; - goto common; + yyerror("cannot use multiple-value assignment for non-blocking receive; use select"); + goto out; case ODOTTYPE: n->op = OAS2DOTTYPE; r->op = ODOTTYPE2; diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index fa3e5d5e4..b32b6fff5 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -269,9 +269,15 @@ walkdef(Node *n) } t = n->type; if(t != T) { - convlit(&e, t); - if(!okforconst[t->etype]) + if(!okforconst[t->etype]) { yyerror("invalid constant type %T", t); + goto ret; + } + if(!isideal(e->type) && !eqtype(t, e->type)) { + yyerror("cannot use %+N as type %T in const initializer", e, t); + goto ret; + } + convlit(&e, t); } n->val = e->val; n->type = e->type; @@ -397,7 +403,7 @@ walkstmt(Node **np) case OAS: case OAS2: case OAS2DOTTYPE: - case OAS2RECV: + case OAS2RECVCLOSED: case OAS2FUNC: case OAS2MAPW: case OAS2MAPR: @@ -664,7 +670,7 @@ walkexpr(Node **np, NodeList **init) case OGE: case OGT: case OADD: - case OCMPLX: + case OCOMPLEX: walkexpr(&n->left, init); walkexpr(&n->right, init); goto ret; @@ -816,14 +822,14 @@ walkexpr(Node **np, NodeList **init) n = liststmt(concat(concat(list1(r), ll), lpost)); goto ret; - case OAS2RECV: - // a,b = <-c + case OAS2RECVCLOSED: + // a = <-c; b = closed(c) but atomic *init = concat(*init, n->ninit); n->ninit = nil; r = n->rlist->n; walkexprlistsafe(n->list, init); walkexpr(&r->left, init); - fn = chanfn("chanrecv2", 2, r->left->type); + fn = chanfn("chanrecv3", 2, r->left->type); r = mkcall1(fn, getoutargx(fn->type), init, r->left); n->rlist->n = r; n->op = OAS2FUNC; @@ -1401,10 +1407,6 @@ walkexpr(Node **np, NodeList **init) n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, n->left, n->right); goto ret; - case OSENDNB: - n = mkcall1(chanfn("chansend2", 2, n->left->type), n->type, init, n->left, n->right); - goto ret; - case OCLOSURE: n = walkclosure(n, init); goto ret; diff --git a/src/cmd/godefs/main.c b/src/cmd/godefs/main.c index 69ee1be5d..d4163421d 100644 --- a/src/cmd/godefs/main.c +++ b/src/cmd/godefs/main.c @@ -196,7 +196,7 @@ main(int argc, char **argv) av[n++] = "gcc"; av[n++] = "-fdollars-in-identifiers"; av[n++] = "-S"; // write assembly - av[n++] = "-gstabs"; // include stabs info + av[n++] = "-gstabs+"; // include stabs info av[n++] = "-o"; // to ... av[n++] = "-"; // ... stdout av[n++] = "-xc"; // read C diff --git a/src/cmd/godefs/stabs.c b/src/cmd/godefs/stabs.c index 1bc96d4c8..f2bb57eb6 100644 --- a/src/cmd/godefs/stabs.c +++ b/src/cmd/godefs/stabs.c @@ -102,6 +102,23 @@ parsetypenum(char **pp, vlong *n1p, vlong *n2p) return 0; } +// Written to parse max/min of vlong correctly. +static vlong +parseoctal(char **pp) +{ + char *p; + vlong n; + + p = *pp; + if(*p++ != '0') + return 0; + n = 0; + while(*p >= '0' && *p <= '9') + n = n << 3 | *p++ - '0'; + *pp = p; + return n; +} + // Integer types are represented in stabs as a "range" // type with a lo and a hi value. The lo and hi used to // be lo and hi for the type, but there are now odd @@ -112,31 +129,24 @@ parsetypenum(char **pp, vlong *n1p, vlong *n2p) typedef struct Intrange Intrange; struct Intrange { - int signlo; // sign of lo vlong lo; - int signhi; // sign of hi vlong hi; int kind; }; -// NOTE(rsc): Iant says that these might be different depending -// on the gcc mode, though I haven't observed this yet. Intrange intranges[] = { - '+', 0, '+', 127, Int8, // char - '-', 128, '+', 127, Int8, // signed char - '+', 0, '+', 255, Uint8, - '-', 32768, '+', 32767, Int16, - '+', 0, '+', 65535, Uint16, - '-', 2147483648LL, '+', 2147483647LL, Int32, - '+', 0, '+', 4294967295LL, Uint32, - - // abnormal cases - '-', 0, '+', 4294967295LL, Int64, - '+', 0, '-', 1, Uint64, - - '+', 4, '+', 0, Float32, - '+', 8, '+', 0, Float64, - '+', 16, '+', 0, Void, + 0, 127, Int8, // char + -128, 127, Int8, // signed char + 0, 255, Uint8, + -32768, 32767, Int16, + 0, 65535, Uint16, + -2147483648LL, 2147483647LL, Int32, + 0, 4294967295LL, Uint32, + 1LL << 63, ~(1LL << 63), Int64, + 0, -1, Uint64, + 4, 0, Float32, + 8, 0, Float64, + 16, 0, Void, }; static int kindsize[] = { @@ -158,7 +168,7 @@ parsedef(char **pp, char *name) { char *p; Type *t, *tt; - int i, signlo, signhi; + int i; vlong n1, n2, lo, hi; Field *f; Intrange *r; @@ -213,6 +223,11 @@ parsedef(char **pp, char *name) *pp = ""; return t; + case '@': // type attribute + while (*++p != ';'); + *pp = ++p; + return parsedef(pp, nil); + case '*': // pointer p++; t->kind = Ptr; @@ -269,6 +284,10 @@ parsedef(char **pp, char *name) return nil; break; + case 'k': // const + ++*pp; + return parsedef(pp, nil); + case 'r': // sub-range (used for integers) p++; if(parsedef(&p, nil) == nil) @@ -280,23 +299,19 @@ parsedef(char **pp, char *name) fprint(2, "range expected number: %s\n", p); return nil; } - if(*p == '-') { - signlo = '-'; - p++; - } else - signlo = '+'; - lo = strtoll(p, &p, 10); + if(*p == '0') + lo = parseoctal(&p); + else + lo = strtoll(p, &p, 10); if(*p != ';' || *++p == ';') { if(stabsdebug) fprint(2, "range expected number: %s\n", p); return nil; } - if(*p == '-') { - signhi = '-'; - p++; - } else - signhi = '+'; - hi = strtoll(p, &p, 10); + if(*p == '0') + hi = parseoctal(&p); + else + hi = strtoll(p, &p, 10); if(*p != ';') { if(stabsdebug) fprint(2, "range expected trailing semi: %s\n", p); @@ -306,7 +321,7 @@ parsedef(char **pp, char *name) t->size = hi+1; // might be array size for(i=0; i<nelem(intranges); i++) { r = &intranges[i]; - if(r->signlo == signlo && r->signhi == signhi && r->lo == lo && r->hi == hi) { + if(r->lo == lo && r->hi == hi) { t->kind = r->kind; break; } diff --git a/src/cmd/godoc/doc.go b/src/cmd/godoc/doc.go index 02779384c..f0006e750 100644 --- a/src/cmd/godoc/doc.go +++ b/src/cmd/godoc/doc.go @@ -47,8 +47,9 @@ The flags are: width of tabs in units of spaces -timestamps=true show timestamps with directory listings - -fulltext=false - build full text index for regular expression queries + -maxresults=10000 + maximum number of full text search results shown + (no full text index is built if maxresults <= 0) -path="" additional package directories (colon-separated) -html diff --git a/src/cmd/godoc/format.go b/src/cmd/godoc/format.go index f68c67b24..66b01aa64 100644 --- a/src/cmd/godoc/format.go +++ b/src/cmd/godoc/format.go @@ -62,12 +62,48 @@ func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links 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 + + // Text segments are written in a delayed fashion + // such that consecutive segments belonging to the + // same selection can be combined (peephole optimization). + // last describes the last segment which has not yet been written. + var last struct { + begin, end int // valid if begin < end + bitset int + } + + // flush writes the last delayed text segment + flush := func() { + if last.begin < last.end { + sw(w, text[last.begin:last.end], last.bitset) + } + last.begin = last.end // invalidate last + } + + // segment runs the segment [lastOffs, end) with the selection + // indicated by bitset through the segment peephole optimizer. + segment := func(end int) { + if lastOffs < end { // ignore empty segments + if last.end != lastOffs || last.bitset != bitset { + // the last segment is not adjacent to or + // differs from the new one + flush() + // start a new segment + last.begin = lastOffs + } + last.end = end + last.bitset = bitset + } + } + for { // get the next segment change index, offs, start := changes.next() @@ -81,14 +117,15 @@ func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, // 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) + segment(offs) + flush() 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) + segment(offs) lastOffs = offs mask := 1 << uint(index) if start { @@ -98,7 +135,8 @@ func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, } } } - sw(w, text[lastOffs:], bitset) + segment(len(text)) + flush() } @@ -201,7 +239,9 @@ func lineSelection(text []byte) Selection { // func commentSelection(src []byte) Selection { var s scanner.Scanner - file := s.Init(token.NewFileSet(), "", src, nil, scanner.ScanComments+scanner.InsertSemis) + fset := token.NewFileSet() + file := fset.AddFile("", fset.Base(), len(src)) + s.Init(file, src, nil, scanner.ScanComments+scanner.InsertSemis) return func() (seg []int) { for { pos, tok, lit := s.Scan() @@ -283,17 +323,15 @@ 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 - } + 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) } + template.HTMLEscape(w, text) } @@ -322,12 +360,12 @@ func FormatText(text []byte, line int, goSource bool, pattern string, selection if pattern != "" { highlights = regexpSelection(text, pattern) } - if comments != nil || highlights != nil || selection != nil { + if line >= 0 || 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) + fmt.Fprintf(w, "<a id=\"L%d\"></a><span class=\"ln\">%6d</span>\t", line, line) line++ } } diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go index d6054ab9d..6a00a3e70 100644 --- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -25,7 +25,6 @@ import ( "strings" "template" "time" - "utf8" ) @@ -56,7 +55,7 @@ var ( // 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)") + pkgPath = 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 @@ -64,7 +63,7 @@ var ( // layout control 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") + maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown") // file system mapping fsMap Mapping // user-defined mapping @@ -80,7 +79,7 @@ var ( func initHandlers() { - fsMap.Init(*path) + fsMap.Init(*pkgPath) fileServer = http.FileServer(*goroot, "") cmdHandler = httpHandler{"/cmd/", pathutil.Join(*goroot, "src/cmd"), false} pkgHandler = httpHandler{"/pkg/", pathutil.Join(*goroot, "src/pkg"), true} @@ -626,11 +625,11 @@ func readTemplate(name string) *template.Template { path := pathutil.Join(*goroot, "lib/godoc/"+name) data, err := ioutil.ReadFile(path) if err != nil { - log.Exitf("ReadFile %s: %v", path, err) + log.Fatalf("ReadFile %s: %v", path, err) } t, err := template.Parse(string(data), fmap) if err != nil { - log.Exitf("%s: %v", name, err) + log.Fatalf("%s: %v", name, err) } return t } @@ -768,53 +767,6 @@ func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) { } -// TODO(gri): Should have a mapping from extension to handler, eventually. - -// textExt[x] is true if the extension x indicates a text file, and false otherwise. -var textExt = map[string]bool{ - ".css": false, // must be served raw - ".js": false, // must be served raw -} - - -func isTextFile(path string) bool { - // if the extension is known, use it for decision making - if isText, found := textExt[pathutil.Ext(path)]; found { - return isText - } - - // the extension is not known; read an initial chunk of - // file and check if it looks like correct UTF-8; if it - // does, it's probably a text file - f, err := os.Open(path, os.O_RDONLY, 0) - if err != nil { - return false - } - defer f.Close() - - var buf [1024]byte - n, err := f.Read(buf[0:]) - if err != nil { - return false - } - - s := string(buf[0:n]) - n -= utf8.UTFMax // make sure there's enough bytes for a complete unicode char - for i, c := range s { - if i > n { - break - } - if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' { - // decoding error or control character - not a text file - return false - } - } - - // likely a text file - return true -} - - func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) { src, err := ioutil.ReadFile(abspath) if err != nil { @@ -1159,41 +1111,47 @@ type SearchResult struct { 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 - } + index, timestamp := searchIndex.get() + if index != nil { + index := index.(*Index) - if index, timestamp := searchIndex.get(); index != nil { // 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 + var err os.Error + result.Hit, result.Alt, err = index.Lookup(query) + if err != nil && *maxResults <= 0 { + // ignore the error if full text search is enabled + // since the query may be a valid 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" + // full text search + if *maxResults > 0 && query != "" { + rx, err := regexp.Compile(query) + if err != nil { + result.Alert = "Error in query regular expression: " + err.String() + return + } + // If we get maxResults+1 results we know that there are more than + // maxResults results and thus the result may be incomplete (to be + // precise, we should remove one result from the result set, but + // nobody is going to count the results on the result page). + result.Found, result.Textual = index.LookupRegexp(rx, *maxResults+1) + result.Complete = result.Found <= *maxResults + if !result.Complete { + result.Found-- // since we looked for maxResults+1 + } } } + + // is the result accurate? + if _, ts := fsModified.get(); timestamp < ts { + // The index is older than the latest file system change + // under godoc's observation. Indexing may be in progress + // or start shortly (see indexer()). + result.Alert = "Indexing in progress: result may be inaccurate" + } + return } @@ -1278,7 +1236,7 @@ func indexer() { log.Printf("updating index...") } start := time.Nanoseconds() - index := NewIndex(fsDirnames(), *fulltextIndex) + index := NewIndex(fsDirnames(), *maxResults > 0) stop := time.Nanoseconds() searchIndex.set(index) if *verbose { diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go index ba6fe9acd..581409cde 100644 --- a/src/cmd/godoc/index.go +++ b/src/cmd/godoc/index.go @@ -47,7 +47,7 @@ import ( "index/suffixarray" "io/ioutil" "os" - pathutil "path" + "path" "regexp" "sort" "strings" @@ -430,8 +430,9 @@ func (a *AltWords) filter(s string) *AltWords { // Indexer // Adjust these flags as seems best. -const excludeMainPackages = false -const excludeTestFiles = false +const includeNonGoFiles = true +const includeMainPackages = true +const includeTestFiles = true type IndexResult struct { @@ -619,11 +620,14 @@ func pkgName(filename string) string { } -func (x *Indexer) addFile(filename string) *ast.File { +// addFile adds a file to the index if possible and returns the file set file +// and the file's AST if it was successfully parsed as a Go file. If addFile +// failed (that is, if the file was not added), it returns file == nil. +func (x *Indexer) addFile(filename string, goFile bool) (file *token.File, ast *ast.File) { // open file f, err := os.Open(filename, os.O_RDONLY, 0) if err != nil { - return nil + return } defer f.Close() @@ -643,59 +647,127 @@ func (x *Indexer) addFile(filename string) *ast.File { 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 - } + // append file contents (src) to x.sources + if _, err := x.sources.ReadFrom(f); err == nil { + src := x.sources.Bytes()[base:] - // 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 + if goFile { + // parse the file and in the process add it to the file set + if ast, err = parser.ParseFile(x.fset, filename, src, parser.ParseComments); err == nil { + file = x.fset.File(ast.Pos()) // ast.Pos() is inside the file + return + } + // file has parse errors, and the AST may be incorrect - + // set lines information explicitly and index as ordinary + // text file (cannot fall through to the text case below + // because the file has already been added to the file set + // by the parser) + file = x.fset.File(token.Pos(base)) // token.Pos(base) is inside the file + file.SetLinesForContent(src) + ast = nil + return + } + + if isText(src) { + // only add the file to the file set (for the full text index) + file = x.fset.AddFile(filename, x.fset.Base(), len(src)) + file.SetLinesForContent(src) + return + } } - return file + // discard possibly added data + x.sources.Truncate(base - 1) // -1 to remove added byte 0 since no file was added + return } -func (x *Indexer) visitFile(dirname string, f *os.FileInfo) { - if !isGoFile(f) { - return +// Design note: Using an explicit white list of permitted files for indexing +// makes sure that the important files are included and massively reduces the +// number of files to index. The advantage over a blacklist is that unexpected +// (non-blacklisted) files won't suddenly explode the index. +// +// TODO(gri): We may want to make this list customizable, perhaps via a flag. + +// Files are whitelisted if they have a file name or extension +// present as key in whitelisted. +var whitelisted = map[string]bool{ + ".bash": true, + ".c": true, + ".css": true, + ".go": true, + ".goc": true, + ".h": true, + ".html": true, + ".js": true, + ".out": true, + ".py": true, + ".s": true, + ".sh": true, + ".txt": true, + ".xml": true, + "AUTHORS": true, + "CONTRIBUTORS": true, + "LICENSE": true, + "Makefile": true, + "PATENTS": true, + "README": true, +} + + +// isWhitelisted returns true if a file is on the list +// of "permitted" files for indexing. The filename must +// be the directory-local name of the file. +func isWhitelisted(filename string) bool { + key := path.Ext(filename) + if key == "" { + // file has no extension - use entire filename + key = filename } + return whitelisted[key] +} - path := pathutil.Join(dirname, f.Name) - if excludeTestFiles && (!isPkgFile(f) || strings.HasPrefix(path, "test/")) { + +func (x *Indexer) visitFile(dirname string, f *os.FileInfo) { + if !f.IsRegular() { return } - if excludeMainPackages && pkgName(path) == "main" { + filename := path.Join(dirname, f.Name) + goFile := false + + switch { + case isGoFile(f): + if !includeTestFiles && (!isPkgFile(f) || strings.HasPrefix(filename, "test/")) { + return + } + if !includeMainPackages && pkgName(filename) == "main" { + return + } + goFile = true + + case !includeNonGoFiles || !isWhitelisted(f.Name): return } - file := x.addFile(path) + file, fast := x.addFile(filename, goFile) if file == nil { - return + return // addFile failed } - // 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} - x.file = &File{path, pak} - ast.Walk(x, file) + if fast != nil { + // we've got a Go file to index + x.current = file + dir, _ := path.Split(filename) + pak := Pak{dir, fast.Name.Name} + x.file = &File{filename, pak} + ast.Walk(x, fast) + } // update statistics - // (count real file size as opposed to using the padded x.sources.Len()) - x.stats.Bytes += x.current.Size() + x.stats.Bytes += file.Size() x.stats.Files++ - x.stats.Lines += x.current.LineCount() + x.stats.Lines += file.LineCount() } @@ -817,7 +889,8 @@ func (x *Index) LookupWord(w string) (match *LookupResult, alt *AltWords) { func isIdentifier(s string) bool { var S scanner.Scanner - S.Init(token.NewFileSet(), "", []byte(s), nil, 0) + fset := token.NewFileSet() + S.Init(fset.AddFile("", fset.Base(), len(s)), []byte(s), nil, 0) if _, tok, _ := S.Scan(); tok == token.IDENT { _, tok, _ := S.Scan() return tok == token.EOF diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go index fe3d22fb9..f1b11a760 100644 --- a/src/cmd/godoc/main.go +++ b/src/cmd/godoc/main.go @@ -227,7 +227,7 @@ func main() { } if *tabwidth < 0 { - log.Exitf("negative tabwidth %d", *tabwidth) + log.Fatalf("negative tabwidth %d", *tabwidth) } initHandlers() @@ -242,8 +242,8 @@ func main() { log.Printf("address = %s", *httpAddr) log.Printf("goroot = %s", *goroot) log.Printf("tabwidth = %d", *tabwidth) - if *fulltextIndex { - log.Print("full text index enabled") + if *maxResults > 0 { + log.Printf("maxresults = %d (full text index enabled)", *maxResults) } if !fsMap.IsEmpty() { log.Print("user-defined mapping:") @@ -284,7 +284,7 @@ func main() { // Start http server. if err := http.ListenAndServe(*httpAddr, handler); err != nil { - log.Exitf("ListenAndServe %s: %v", *httpAddr, err) + log.Fatalf("ListenAndServe %s: %v", *httpAddr, err) } return @@ -301,7 +301,7 @@ func main() { for i := 0; i < flag.NArg(); i++ { res, err := remoteSearch(flag.Arg(i)) if err != nil { - log.Exitf("remoteSearch: %s", err) + log.Fatalf("remoteSearch: %s", err) } io.Copy(os.Stdout, res.Body) } @@ -344,7 +344,7 @@ func main() { info = cmdHandler.getPageInfo(abspath, relpath, "", mode) } if info.Err != nil { - log.Exitf("%v", info.Err) + log.Fatalf("%v", info.Err) } // If we have more than one argument, use the remaining arguments for filtering @@ -352,7 +352,7 @@ func main() { args := flag.Args()[1:] rx := makeRx(args) if rx == nil { - log.Exitf("illegal regular expression from %v", args) + log.Fatalf("illegal regular expression from %v", args) } filter := func(s string) bool { return rx.MatchString(s) } diff --git a/src/cmd/godoc/snippet.go b/src/cmd/godoc/snippet.go index 6a12febe1..c2838ed5a 100755 --- a/src/cmd/godoc/snippet.go +++ b/src/cmd/godoc/snippet.go @@ -26,7 +26,7 @@ type Snippet struct { 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, fset, decl, true) + writeNode(&buf, fset, decl, false) return &Snippet{fset.Position(id.Pos()).Line, FormatText(buf.Bytes(), -1, true, id.Name, nil)} } diff --git a/src/cmd/godoc/spec.go b/src/cmd/godoc/spec.go index b1c1a883f..a533c1e0a 100644 --- a/src/cmd/godoc/spec.go +++ b/src/cmd/godoc/spec.go @@ -156,7 +156,8 @@ func (p *ebnfParser) parse(fset *token.FileSet, out io.Writer, src []byte) { // initialize ebnfParser p.out = out p.src = src - p.file = p.scanner.Init(fset, "", src, p, 0) + p.file = fset.AddFile("", fset.Base(), len(src)) + p.scanner.Init(p.file, src, p, 0) p.next() // initializes pos, tok, lit // process source diff --git a/src/cmd/godoc/utils.go b/src/cmd/godoc/utils.go index 55cf87841..a032bd331 100644 --- a/src/cmd/godoc/utils.go +++ b/src/cmd/godoc/utils.go @@ -15,11 +15,13 @@ import ( "strings" "sync" "time" + "utf8" ) // 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{} @@ -107,3 +109,63 @@ func writeFileAtomically(filename string, data []byte) os.Error { } return os.Rename(f.Name(), filename) } + + +// isText returns true if a significant prefix of s looks like correct UTF-8; +// that is, if it is likely that s is human-readable text. +// +func isText(s []byte) bool { + const max = 1024 // at least utf8.UTFMax + if len(s) > max { + s = s[0:max] + } + for i, c := range string(s) { + if i+utf8.UTFMax > len(s) { + // last char may be incomplete - ignore + break + } + if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' { + // decoding error or control character - not a text file + return false + } + } + return true +} + + +// TODO(gri): Should have a mapping from extension to handler, eventually. + +// textExt[x] is true if the extension x indicates a text file, and false otherwise. +var textExt = map[string]bool{ + ".css": false, // must be served raw + ".js": false, // must be served raw +} + + +// isTextFile returns true if the file has a known extension indicating +// a text file, or if a significant chunk of the specified file looks like +// correct UTF-8; that is, if it is likely that the file contains human- +// readable text. +// +func isTextFile(filename string) bool { + // if the extension is known, use it for decision making + if isText, found := textExt[pathutil.Ext(filename)]; found { + return isText + } + + // the extension is not known; read an initial chunk + // of the file and check if it looks like text + f, err := os.Open(filename, os.O_RDONLY, 0) + if err != nil { + return false + } + defer f.Close() + + var buf [1024]byte + n, err := f.Read(buf[0:]) + if err != nil { + return false + } + + return isText(buf[0:n]) +} diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go index 8ea5334e9..fbcd46aa2 100644 --- a/src/cmd/gofmt/rewrite.go +++ b/src/cmd/gofmt/rewrite.go @@ -124,9 +124,9 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool { // Wildcard matches any expression. If it appears multiple // times in the pattern, it must match the same expression // each time. - if m != nil && pattern.Type() == identType { + if m != nil && pattern != nil && pattern.Type() == identType { name := pattern.Interface().(*ast.Ident).Name - if isWildcard(name) { + if isWildcard(name) && val != nil { // wildcards only match expressions if _, ok := val.Interface().(ast.Expr); ok { if old, ok := m[name]; ok { diff --git a/src/cmd/goinstall/main.go b/src/cmd/goinstall/main.go index b0f08efdf..f13aeb3bc 100644 --- a/src/cmd/goinstall/main.go +++ b/src/cmd/goinstall/main.go @@ -244,8 +244,7 @@ func quietRun(dir string, stdin []byte, cmd ...string) os.Error { func genRun(dir string, stdin []byte, cmd []string, quiet bool) os.Error { bin, err := exec.LookPath(cmd[0]) if err != nil { - // report binary as well as the error - return os.NewError(cmd[0] + ": " + err.String()) + return err } p, err := exec.Run(bin, cmd, os.Environ(), dir, exec.Pipe, exec.Pipe, exec.MergeWithStdout) if *verbose { diff --git a/src/cmd/gopack/ar.c b/src/cmd/gopack/ar.c index 063967bd7..a16e98cfe 100644 --- a/src/cmd/gopack/ar.c +++ b/src/cmd/gopack/ar.c @@ -607,6 +607,7 @@ scanobj(Biobuf *b, Arfile *ap, long size) /* maybe a foreign object file? that's okay */ if((buf[0] == 0x7F && buf[1] == 'E' && buf[2] == 'L' && buf[3] == 'F') || // ELF + (buf[0] == 0x4c && buf[1] == 0x01 || buf[0] == 0x64 && buf[1] == 0x86) || // Windows PE (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); diff --git a/src/cmd/gotest/gotest b/src/cmd/gotest/gotest index 7572610d2..87c680089 100755 --- a/src/cmd/gotest/gotest +++ b/src/cmd/gotest/gotest @@ -180,10 +180,4 @@ importpath=$(gomake -s importpath) $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/govet/govet.go b/src/cmd/govet/govet.go index 2981891eb..5619b12ba 100644 --- a/src/cmd/govet/govet.go +++ b/src/cmd/govet/govet.go @@ -210,6 +210,7 @@ var printfList = map[string]int{ "Errorf": 0, "Fatalf": 0, "Fprintf": 1, + "Panicf": 0, "Printf": 0, "Sprintf": 0, } @@ -220,6 +221,7 @@ var printList = map[string]int{ "Error": 0, "Fatal": 0, "Fprint": 1, "Fprintln": 1, + "Panic": 0, "Panicln": 0, "Print": 0, "Println": 0, "Sprint": 0, "Sprintln": 0, } diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 210f10ab5..0551232cf 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -240,6 +240,33 @@ void dynrelocsym(Sym *s) { Reloc *r; + + if(thechar == '8' && HEADTYPE == 10) { // Windows PE + Sym *rel, *targ; + + rel = lookup(".rel", 0); + if(s == rel) + return; + for(r=s->r; r<s->r+s->nr; r++) { + targ = r->sym; + if(r->sym->plt == -2) { // make dynimport JMP table for PE object files. + targ->plt = rel->size; + r->sym = rel; + r->add = targ->plt; + + // jmp *addr + adduint8(rel, 0xff); + adduint8(rel, 0x25); + addaddr(rel, targ); + adduint8(rel, 0x90); + adduint8(rel, 0x90); + } else if(r->sym->plt >= 0) { + r->sym = rel; + r->add = targ->plt; + } + } + return; + } for(r=s->r; r<s->r+s->nr; r++) if(r->sym->type == SDYNIMPORT || r->type >= 256) @@ -871,7 +898,7 @@ address(void) segdata.rwx = 06; segdata.vaddr = va; segdata.fileoff = va - segtext.vaddr + segtext.fileoff; - if(thechar == '8' && HEADTYPE == 10) // Windows PE + if((thechar == '6' || 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); diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c index 506c6e5db..5df3515f5 100644 --- a/src/cmd/ld/dwarf.c +++ b/src/cmd/ld/dwarf.c @@ -19,6 +19,7 @@ #include "../ld/dwarf_defs.h" #include "../ld/elf.h" #include "../ld/macho.h" +#include "../ld/pe.h" /* * Offsets and sizes of the debug_* sections in the cout file. @@ -453,19 +454,6 @@ getattr(DWDie *die, uint8 attr) 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. @@ -768,10 +756,8 @@ enum { KindUint32, KindUint64, KindUintptr, - KindFloat, KindFloat32, KindFloat64, - KindComplex, KindComplex64, KindComplex128, KindArray, @@ -799,6 +785,17 @@ decode_reloc(Sym *s, int32 off) return nil; } +static Sym* +decode_reloc_sym(Sym *s, int32 off) +{ + Reloc *r; + + r = decode_reloc(s,off); + if (r == nil) + return nil; + return r->sym; +} + static uvlong decode_inuxi(uchar* p, int sz) { @@ -852,7 +849,7 @@ decodetype_size(Sym *s) static Sym* decodetype_arrayelem(Sym *s) { - return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 + return decode_reloc_sym(s, 5*PtrSize + 8); // 0x1c / 0x30 } static vlong @@ -865,26 +862,26 @@ decodetype_arraylen(Sym *s) static Sym* decodetype_ptrelem(Sym *s) { - return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 + return decode_reloc_sym(s, 5*PtrSize + 8); // 0x1c / 0x30 } // Type.MapType.key, elem static Sym* decodetype_mapkey(Sym *s) { - return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 + return decode_reloc_sym(s, 5*PtrSize + 8); // 0x1c / 0x30 } static Sym* decodetype_mapvalue(Sym *s) { - return decode_reloc(s, 6*PtrSize + 8)->sym; // 0x20 / 0x38 + return decode_reloc_sym(s, 6*PtrSize + 8); // 0x20 / 0x38 } // Type.ChanType.elem static Sym* decodetype_chanelem(Sym *s) { - return decode_reloc(s, 5*PtrSize + 8)->sym; // 0x1c / 0x30 + return decode_reloc_sym(s, 5*PtrSize + 8); // 0x1c / 0x30 } // Type.FuncType.dotdotdot @@ -913,7 +910,9 @@ 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; + if (r == nil) + return nil; + return decode_reloc_sym(r->sym, r->add + i * PtrSize); } static Sym* @@ -922,7 +921,9 @@ 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; + if (r == nil) + return nil; + return decode_reloc_sym(r->sym, r->add + i * PtrSize); } // Type.StructType.fields.Slice::len @@ -936,21 +937,20 @@ decodetype_structfieldcount(Sym *s) 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. + // go.string."foo" 0x28 / 0x40 + s = decode_reloc_sym(s, 6*PtrSize + 0x10 + i*5*PtrSize); + if (s == nil) // embedded structs have a nil name. return nil; - r = decode_reloc(r->sym, 0); // string."foo" - if (r == nil) // shouldn't happen. + s = decode_reloc_sym(s, 0); // string."foo" + if (s == nil) // shouldn't happen. return nil; - return (char*)r->sym->p; // the c-string + return (char*)s->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 + return decode_reloc_sym(s, 8*PtrSize + 0x10 + i*5*PtrSize); // 0x30 / 0x50 } static vlong @@ -977,6 +977,20 @@ enum { static DWDie* defptrto(DWDie *dwtype); // below +// Lookup predefined types +static Sym* +lookup_or_diag(char *n) +{ + Sym *s; + + s = lookup(n, 0); + if (s->size == 0) { + diag("dwarf: missing type: %s", n); + errorexit(); + } + return s; +} + // Define gotype, for composite ones recurse into constituents. static DWDie* defgotype(Sym *gotype) @@ -995,7 +1009,7 @@ defgotype(Sym *gotype) 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 + name = gotype->name + 5; // could also decode from Type.string die = find(&dwtypes, name); if (die != nil) @@ -1049,7 +1063,6 @@ defgotype(Sym *gotype) 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); @@ -1057,7 +1070,6 @@ defgotype(Sym *gotype) 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); @@ -1107,9 +1119,9 @@ defgotype(Sym *gotype) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); nfields = decodetype_ifacemethodcount(gotype); if (nfields == 0) - s = lookup("type.runtime.eface", 0); + s = lookup_or_diag("type.runtime.eface"); else - s = lookup("type.runtime.iface", 0); + s = lookup_or_diag("type.runtime.iface"); newrefattr(die, DW_AT_type, defgotype(s)); break; @@ -1226,7 +1238,7 @@ synthesizestringtypes(DWDie* die) { DWDie *prototype; - prototype = defgotype(lookup("type.runtime.string_", 0)); + prototype = defgotype(lookup_or_diag("type.runtime._string")); if (prototype == nil) return; @@ -1242,7 +1254,7 @@ synthesizeslicetypes(DWDie *die) { DWDie *prototype, *elem; - prototype = defgotype(lookup("type.runtime.slice",0)); + prototype = defgotype(lookup_or_diag("type.runtime.slice")); if (prototype == nil) return; @@ -1281,22 +1293,22 @@ synthesizemaptypes(DWDie *die) { DWDie *hash, *hash_subtable, *hash_entry, - *dwh, *dwhs, *dwhe, *keytype, *valtype, *fld; + *dwh, *dwhs, *dwhe, *dwhash, *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)); + hash = defgotype(lookup_or_diag("type.runtime.hmap")); + hash_subtable = defgotype(lookup_or_diag("type.runtime.hash_subtable")); + hash_entry = defgotype(lookup_or_diag("type.runtime.hash_entry")); 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) + dwhash = (DWDie*)getattr(find_or_diag(hash_entry, "hash"), DW_AT_type)->data; + if (dwhash == nil) return; - hashsize = getattr(dwh, DW_AT_byte_size)->value; + hashsize = getattr(dwhash, DW_AT_byte_size)->value; for (; die != nil; die = die->link) { if (die->abbrev != DW_ABRV_MAPTYPE) @@ -1328,13 +1340,19 @@ synthesizemaptypes(DWDie *die) mkinternaltypename("hash_entry", getattr(keytype, DW_AT_name)->data, getattr(valtype, DW_AT_name)->data)); - copychildren(dwhe, hash_entry); - substitutetype(dwhe, "key", keytype); + + fld = newdie(dwhe, DW_ABRV_STRUCTFIELD, "hash"); + newrefattr(fld, DW_AT_type, dwhash); + newmemberoffsetattr(fld, 0); + + fld = newdie(dwhe, DW_ABRV_STRUCTFIELD, "key"); + newrefattr(fld, DW_AT_type, keytype); + newmemberoffsetattr(fld, hashsize); + + fld = newdie(dwhe, DW_ABRV_STRUCTFIELD, "val"); if (valsize > MaxValsize) valtype = defptrto(valtype); - substitutetype(dwhe, "val", valtype); - fld = find_or_diag(dwhe, "val"); - delattr(fld, DW_AT_data_member_location); + newrefattr(fld, DW_AT_type, valtype); newmemberoffsetattr(fld, hashsize + datavo); newattr(dwhe, DW_AT_byte_size, DW_CLS_CONSTANT, hashsize + datsize, NULL); @@ -1371,10 +1389,10 @@ synthesizechantypes(DWDie *die) 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)); + sudog = defgotype(lookup_or_diag("type.runtime.sudog")); + waitq = defgotype(lookup_or_diag("type.runtime.waitq")); + link = defgotype(lookup_or_diag("type.runtime.link")); + hchan = defgotype(lookup_or_diag("type.runtime.hchan")); if (sudog == nil || waitq == nil || link == nil || hchan == nil) return; @@ -2281,6 +2299,13 @@ writegdbscript(void) return sectionstart; } +static void +align(vlong size) +{ + if((thechar == '6' || thechar == '8') && HEADTYPE == 10) // Only Windows PE need section align. + strnput("", rnd(size, PEFILEALIGN) - size); +} + /* * This is the main entry point for generating dwarf. After emitting * the mandatory debug_abbrev section, it calls writelines() to set up @@ -2313,15 +2338,18 @@ dwarfemitdebugsections(void) 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)); + defgotype(lookup_or_diag("type.runtime.commonType")); + defgotype(lookup_or_diag("type.runtime.InterfaceType")); + defgotype(lookup_or_diag("type.runtime.itab")); genasmsym(defdwsymb); writeabbrev(); + align(abbrevsize); writelines(); + align(linesize); writeframes(); + align(framesize); synthesizestringtypes(dwtypes.child); synthesizeslicetypes(dwtypes.child); @@ -2354,16 +2382,23 @@ dwarfemitdebugsections(void) } } infosize = infoe - infoo; + align(infosize); pubnameso = writepub(ispubname); + pubnamessize = cpos() - pubnameso; + align(pubnamessize); + pubtypeso = writepub(ispubtype); + pubtypessize = cpos() - pubtypeso; + align(pubtypessize); + arangeso = writearanges(); - gdbscripto = writegdbscript(); + arangessize = cpos() - arangeso; + align(arangessize); - pubnamessize = pubtypeso - pubnameso; - pubtypessize = arangeso - pubtypeso; - arangessize = gdbscripto - arangeso; + gdbscripto = writegdbscript(); gdbscriptsize = cpos() - gdbscripto; + align(gdbscriptsize); } /* @@ -2545,3 +2580,24 @@ dwarfaddmachoheaders(void) ms->filesize += msect->size; } } + +/* + * Windows PE + */ +void +dwarfaddpeheaders(void) +{ + dwarfemitdebugsections(); + newPEDWARFSection(".debug_abbrev", abbrevsize); + newPEDWARFSection(".debug_line", linesize); + newPEDWARFSection(".debug_frame", framesize); + newPEDWARFSection(".debug_info", infosize); + if (pubnamessize > 0) + newPEDWARFSection(".debug_pubnames", pubnamessize); + if (pubtypessize > 0) + newPEDWARFSection(".debug_pubtypes", pubtypessize); + if (arangessize > 0) + newPEDWARFSection(".debug_aranges", arangessize); + if (gdbscriptsize > 0) + newPEDWARFSection(".debug_gdb_scripts", gdbscriptsize); +} diff --git a/src/cmd/ld/dwarf.h b/src/cmd/ld/dwarf.h index 7881213c2..f0df2f9b1 100644 --- a/src/cmd/ld/dwarf.h +++ b/src/cmd/ld/dwarf.h @@ -27,3 +27,4 @@ void dwarfaddshstrings(Sym *shstrtab); */ void dwarfaddelfheaders(void); void dwarfaddmachoheaders(void); +void dwarfaddpeheaders(void); diff --git a/src/cmd/ld/ldpe.c b/src/cmd/ld/ldpe.c new file mode 100644 index 000000000..d8b0a6fc2 --- /dev/null +++ b/src/cmd/ld/ldpe.c @@ -0,0 +1,406 @@ +// 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 "l.h" +#include "lib.h" +#include "../ld/pe.h" + +#define IMAGE_SCN_MEM_DISCARDABLE 0x2000000 + +#define IMAGE_SYM_UNDEFINED 0 +#define IMAGE_SYM_ABSOLUTE (-1) +#define IMAGE_SYM_DEBUG (-2) +#define IMAGE_SYM_TYPE_NULL 0 +#define IMAGE_SYM_TYPE_VOID 1 +#define IMAGE_SYM_TYPE_CHAR 2 +#define IMAGE_SYM_TYPE_SHORT 3 +#define IMAGE_SYM_TYPE_INT 4 +#define IMAGE_SYM_TYPE_LONG 5 +#define IMAGE_SYM_TYPE_FLOAT 6 +#define IMAGE_SYM_TYPE_DOUBLE 7 +#define IMAGE_SYM_TYPE_STRUCT 8 +#define IMAGE_SYM_TYPE_UNION 9 +#define IMAGE_SYM_TYPE_ENUM 10 +#define IMAGE_SYM_TYPE_MOE 11 +#define IMAGE_SYM_TYPE_BYTE 12 +#define IMAGE_SYM_TYPE_WORD 13 +#define IMAGE_SYM_TYPE_UINT 14 +#define IMAGE_SYM_TYPE_DWORD 15 +#define IMAGE_SYM_TYPE_PCODE 32768 +#define IMAGE_SYM_DTYPE_NULL 0 +#define IMAGE_SYM_DTYPE_POINTER 0x10 +#define IMAGE_SYM_DTYPE_FUNCTION 0x20 +#define IMAGE_SYM_DTYPE_ARRAY 0x30 +#define IMAGE_SYM_CLASS_END_OF_FUNCTION (-1) +#define IMAGE_SYM_CLASS_NULL 0 +#define IMAGE_SYM_CLASS_AUTOMATIC 1 +#define IMAGE_SYM_CLASS_EXTERNAL 2 +#define IMAGE_SYM_CLASS_STATIC 3 +#define IMAGE_SYM_CLASS_REGISTER 4 +#define IMAGE_SYM_CLASS_EXTERNAL_DEF 5 +#define IMAGE_SYM_CLASS_LABEL 6 +#define IMAGE_SYM_CLASS_UNDEFINED_LABEL 7 +#define IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 8 +#define IMAGE_SYM_CLASS_ARGUMENT 9 +#define IMAGE_SYM_CLASS_STRUCT_TAG 10 +#define IMAGE_SYM_CLASS_MEMBER_OF_UNION 11 +#define IMAGE_SYM_CLASS_UNION_TAG 12 +#define IMAGE_SYM_CLASS_TYPE_DEFINITION 13 +#define IMAGE_SYM_CLASS_UNDEFINED_STATIC 14 +#define IMAGE_SYM_CLASS_ENUM_TAG 15 +#define IMAGE_SYM_CLASS_MEMBER_OF_ENUM 16 +#define IMAGE_SYM_CLASS_REGISTER_PARAM 17 +#define IMAGE_SYM_CLASS_BIT_FIELD 18 +#define IMAGE_SYM_CLASS_FAR_EXTERNAL 68 /* Not in PECOFF v8 spec */ +#define IMAGE_SYM_CLASS_BLOCK 100 +#define IMAGE_SYM_CLASS_FUNCTION 101 +#define IMAGE_SYM_CLASS_END_OF_STRUCT 102 +#define IMAGE_SYM_CLASS_FILE 103 +#define IMAGE_SYM_CLASS_SECTION 104 +#define IMAGE_SYM_CLASS_WEAK_EXTERNAL 105 +#define IMAGE_SYM_CLASS_CLR_TOKEN 107 + +#define IMAGE_REL_I386_ABSOLUTE 0x0000 +#define IMAGE_REL_I386_DIR16 0x0001 +#define IMAGE_REL_I386_REL16 0x0002 +#define IMAGE_REL_I386_DIR32 0x0006 +#define IMAGE_REL_I386_DIR32NB 0x0007 +#define IMAGE_REL_I386_SEG12 0x0009 +#define IMAGE_REL_I386_SECTION 0x000A +#define IMAGE_REL_I386_SECREL 0x000B +#define IMAGE_REL_I386_TOKEN 0x000C +#define IMAGE_REL_I386_SECREL7 0x000D +#define IMAGE_REL_I386_REL32 0x0014 + +typedef struct PeSym PeSym; +typedef struct PeSect PeSect; +typedef struct PeObj PeObj; + +struct PeSym { + char* name; + uint32 value; + uint16 sectnum; + uint16 type; + uint8 sclass; + uint8 aux; + Sym* sym; +}; + +struct PeSect { + char* name; + uchar* base; + uint64 size; + Sym* sym; + IMAGE_SECTION_HEADER sh; +}; + +struct PeObj { + Biobuf *f; + char *name; + uint32 base; + + PeSect *sect; + uint nsect; + PeSym *pesym; + uint npesym; + + IMAGE_FILE_HEADER fh; + char* snames; +}; + +static int map(PeObj *obj, PeSect *sect); +static int readsym(PeObj *obj, int i, PeSym **sym); + +void +ldpe(Biobuf *f, char *pkg, int64 len, char *pn) +{ + char *name; + int32 base; + int i, j, l, numaux; + PeObj *obj; + PeSect *sect, *rsect; + IMAGE_SECTION_HEADER sh; + uchar symbuf[18]; + Sym *s; + Reloc *r, *rp; + PeSym *sym; + + if(debug['v']) + Bprint(&bso, "%5.2f ldpe %s\n", cputime(), pn); + + version++; + base = Boffset(f); + + obj = mal(sizeof *obj); + obj->f = f; + obj->base = base; + obj->name = pn; + // read header + if(Bread(f, &obj->fh, sizeof obj->fh) != sizeof obj->fh) + goto bad; + // load section list + obj->sect = mal(obj->fh.NumberOfSections*sizeof obj->sect[0]); + obj->nsect = obj->fh.NumberOfSections; + for(i=0; i < obj->fh.NumberOfSections; i++) { + if(Bread(f, &obj->sect[i].sh, sizeof sh) != sizeof sh) + goto bad; + obj->sect[i].size = obj->sect[i].sh.SizeOfRawData; + obj->sect[i].name = (char*)obj->sect[i].sh.Name; + // TODO return error if found .cormeta .rsrc + } + // load string table + Bseek(f, base+obj->fh.PointerToSymbolTable+18*obj->fh.NumberOfSymbols, 0); + if(Bread(f, &l, sizeof l) != sizeof l) + goto bad; + obj->snames = mal(l); + Bseek(f, base+obj->fh.PointerToSymbolTable+18*obj->fh.NumberOfSymbols, 0); + if(Bread(f, obj->snames, l) != l) + goto bad; + // read symbols + obj->pesym = mal(obj->fh.NumberOfSymbols*sizeof obj->pesym[0]); + obj->npesym = obj->fh.NumberOfSymbols; + Bseek(f, base+obj->fh.PointerToSymbolTable, 0); + for(i=0; i<obj->fh.NumberOfSymbols; i+=numaux+1) { + Bseek(f, base+obj->fh.PointerToSymbolTable+sizeof(symbuf)*i, 0); + if(Bread(f, symbuf, sizeof symbuf) != sizeof symbuf) + goto bad; + + if((symbuf[0] == 0) && (symbuf[1] == 0) && + (symbuf[2] == 0) && (symbuf[3] == 0)) { + l = le32(&symbuf[4]); + obj->pesym[i].name = (char*)&obj->snames[l]; + } else { // sym name length <= 8 + obj->pesym[i].name = mal(9); + strncpy(obj->pesym[i].name, (char*)symbuf, 8); + obj->pesym[i].name[8] = 0; + } + obj->pesym[i].value = le32(&symbuf[8]); + obj->pesym[i].sectnum = le16(&symbuf[12]); + obj->pesym[i].sclass = symbuf[16]; + obj->pesym[i].aux = symbuf[17]; + obj->pesym[i].type = le16(&symbuf[14]); + numaux = obj->pesym[i].aux; + if (numaux < 0) + numaux = 0; + } + // create symbols for mapped sections + for(i=0; i<obj->nsect; i++) { + sect = &obj->sect[i]; + if(sect->sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE) + continue; + if(map(obj, sect) < 0) + goto bad; + + name = smprint("%s(%s)", pn, sect->name); + s = lookup(name, version); + free(name); + switch(sect->sh.Characteristics&(IMAGE_SCN_CNT_UNINITIALIZED_DATA|IMAGE_SCN_CNT_INITIALIZED_DATA| + IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE|IMAGE_SCN_CNT_CODE|IMAGE_SCN_MEM_EXECUTE)) { + case IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ: //.rdata + s->type = SRODATA; + break; + case IMAGE_SCN_CNT_UNINITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE: //.bss + case IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE: //.data + s->type = SDATA; + break; + case IMAGE_SCN_CNT_CODE|IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ: //.text + s->type = STEXT; + break; + default: + werrstr("unexpected flags for PE section %s", sect->name); + goto bad; + } + 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->sym == 0 || rsect->sh.NumberOfRelocations == 0) + continue; + if(rsect->sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE) + continue; + r = mal(rsect->sh.NumberOfRelocations*sizeof r[0]); + Bseek(f, obj->base+rsect->sh.PointerToRelocations, 0); + for(j=0; j<rsect->sh.NumberOfRelocations; j++) { + rp = &r[j]; + if(Bread(f, symbuf, 10) != 10) + goto bad; + + uint32 rva, symindex; + uint16 type; + rva = le32(&symbuf[0]); + symindex = le32(&symbuf[4]); + type = le16(&symbuf[8]); + if(readsym(obj, symindex, &sym) < 0) + goto bad; + if(sym->sym == nil) { + werrstr("reloc of invalid sym %s idx=%d type=%d", sym->name, symindex, sym->type); + goto bad; + } + rp->sym = sym->sym; + rp->siz = 4; + rp->off = rva; + switch(type) { + default: + diag("%s: unknown relocation type %d;", pn, type); + case IMAGE_REL_I386_REL32: + rp->type = D_PCREL; + rp->add = 0; + break; + case IMAGE_REL_I386_DIR32: + rp->type = D_ADDR; + // load addend from image + rp->add = le32(rsect->base+rp->off); + break; + } + } + qsort(r, rsect->sh.NumberOfRelocations, sizeof r[0], rbyoff); + + s = rsect->sym; + s->r = r; + s->nr = rsect->sh.NumberOfRelocations; + } + + // enter sub-symbols into symbol table. + // frist 2 entry is file name. + for(i=2; i<obj->npesym; i++) { + if(obj->pesym[i].name == 0) + continue; + if(obj->pesym[i].name[0] == '.') //skip section + continue; + if(obj->pesym[i].sectnum > 0) { + sect = &obj->sect[obj->pesym[i].sectnum-1]; + if(sect->sym == 0) + continue; + } + if(readsym(obj, i, &sym) < 0) + goto bad; + + s = sym->sym; + if(sym->sectnum == 0) {// extern + if(s->type == SDYNIMPORT) + s->plt = -2; // flag for dynimport in PE object files. + continue; + } else if (sym->sectnum > 0) { + sect = &obj->sect[sym->sectnum-1]; + if(sect->sym == 0) + diag("%s: %s sym == 0!", pn, s->name); + } else { + diag("%s: %s sectnum <0!", pn, s->name, sym->sectnum); + } + + s->sub = sect->sym->sub; + sect->sym->sub = s; + s->type = sect->sym->type | SSUB; + s->value = sym->value; + s->size = 4; + 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 pe file: %r", pn); +} + +static int +map(PeObj *obj, PeSect *sect) +{ + if(sect->base != nil) + return 0; + + sect->base = mal(sect->sh.SizeOfRawData); + werrstr("short read"); + if(Bseek(obj->f, obj->base+sect->sh.PointerToRawData, 0) < 0 || + Bread(obj->f, sect->base, sect->sh.SizeOfRawData) != sect->sh.SizeOfRawData) + return -1; + + return 0; +} + +static int +readsym(PeObj *obj, int i, PeSym **y) +{ + Sym *s; + PeSym *sym; + char *name, *p; + + if(i >= obj->npesym || i < 0) { + werrstr("invalid pe symbol index"); + return -1; + } + + sym = &obj->pesym[i]; + *y = sym; + s = nil; + + name = sym->name; + if(sym->sclass == IMAGE_SYM_CLASS_STATIC && sym->value == 0) // section + name = obj->sect[sym->sectnum-1].sym->name; + if(strncmp(sym->name, "__imp__", 6) == 0) + name = &sym->name[7]; // __imp__Name => Name + else if(sym->name[0] == '_') + name = &sym->name[1]; // _Name => Name + // remove last @XXX + p = strchr(name, '@'); + if(p) + *p = 0; + + switch(sym->type) { + default: + werrstr("%s: invalid symbol type %d", sym->name, sym->type); + return -1; + case IMAGE_SYM_DTYPE_FUNCTION: + case IMAGE_SYM_DTYPE_NULL: + switch(sym->sclass) { + case IMAGE_SYM_CLASS_EXTERNAL: //global + s = lookup(name, 0); + break; + case IMAGE_SYM_CLASS_NULL: + case IMAGE_SYM_CLASS_STATIC: + s = lookup(name, version); + break; + default: + werrstr("%s: invalid symbol binding %d", sym->name, sym->sclass); + return -1; + } + break; + } + + if(s != nil && s->type == 0 && !(sym->sclass == IMAGE_SYM_CLASS_STATIC && sym->value == 0)) + s->type = SXREF; + sym->sym = s; + + return 0; +} diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index ae77247c3..b1a62f25e 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -406,6 +406,10 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) ldmacho(f, pkg, len, pn); return; } + if(c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86) { + ldpe(f, pkg, len, pn); + return; + } /* check the header */ line = Brdline(f, '\n'); diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index bcf297116..4ac5d37f9 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -130,6 +130,7 @@ 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 ldpe(Biobuf*, char*, int64, char*); void ldpkg(Biobuf*, char*, int64, char*, int); void mark(Sym *s); void mkfwd(void); diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c index 82c6941f2..2c34daab4 100644 --- a/src/cmd/ld/pe.c +++ b/src/cmd/ld/pe.c @@ -10,6 +10,7 @@ #include "l.h" #include "../ld/lib.h" #include "../ld/pe.h" +#include "../ld/dwarf.h" // DOS stub that prints out // "This program cannot be run in DOS mode." @@ -33,6 +34,9 @@ static char dosstub[] = 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static char symnames[256]; +static int nextsymoff; + int32 PESECTHEADR; int32 PEFILEHEADR; @@ -43,26 +47,33 @@ static int nextfileoff; static IMAGE_FILE_HEADER fh; static IMAGE_OPTIONAL_HEADER oh; +static PE64_IMAGE_OPTIONAL_HEADER oh64; static IMAGE_SECTION_HEADER sh[16]; +static IMAGE_DATA_DIRECTORY* dd; + +#define set(n, v) (pe64 ? (oh64.n = v) : (oh.n = v)) +#define put(v) (pe64 ? vputl(v) : lputl(v)) typedef struct Imp Imp; struct Imp { Sym* s; - long va; - long vb; + uvlong off; Imp* next; }; typedef struct Dll Dll; struct Dll { char* name; - int count; + uvlong nameoff; + uvlong thunkoff; Imp* ms; Dll* next; }; static Dll* dr; -static int ndll, nimp, nsize; + +static Sym *dexport[1024]; +static int nexport; static IMAGE_SECTION_HEADER* addpesection(char *name, int sectsize, int filesize, Segment *s) @@ -99,17 +110,23 @@ addpesection(char *name, int sectsize, int filesize, Segment *s) void peinit(void) { + int32 l; + switch(thechar) { // 64-bit architectures case '6': pe64 = 1; + l = sizeof(oh64); + dd = oh64.DataDirectory; break; // 32-bit architectures default: + l = sizeof(oh); + dd = oh.DataDirectory; break; } - - PEFILEHEADR = rnd(sizeof(dosstub)+sizeof(fh)+sizeof(oh)+sizeof(sh), PEFILEALIGN); + + PEFILEHEADR = rnd(sizeof(dosstub)+sizeof(fh)+l+sizeof(sh), PEFILEALIGN); PESECTHEADR = rnd(PEFILEHEADR, PESECTALIGN); nextsectoff = PESECTHEADR; nextfileoff = PEFILEHEADR; @@ -118,27 +135,34 @@ peinit(void) static void pewrite(void) { - int i, j; - seek(cout, 0, 0); ewrite(cout, dosstub, sizeof dosstub); strnput("PE", 4); - - for (i=0; i<sizeof(fh); i++) - cput(((char*)&fh)[i]); - for (i=0; i<sizeof(oh); i++) - cput(((char*)&oh)[i]); - for (i=0; i<nsect; i++) - for (j=0; j<sizeof(sh[i]); j++) - cput(((char*)&sh[i])[j]); + cflush(); + // TODO: This code should not assume that the + // memory representation is little-endian or + // that the structs are packed identically to + // their file representation. + ewrite(cout, &fh, sizeof fh); + if(pe64) + ewrite(cout, &oh64, sizeof oh64); + else + ewrite(cout, &oh, sizeof oh); + ewrite(cout, &sh, nsect * sizeof sh[0]); } static void strput(char *s) { - while(*s) + int n; + + for(n=0; *s; n++) cput(*s++); cput('\0'); + n++; + // string must be padded to even size + if(n%2) + cput('\0'); } static Dll* @@ -146,50 +170,33 @@ initdynimport(void) { Imp *m; Dll *d; - Sym *s; + Sym *s, *dynamic; 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) + if(!s->reachable || !s->dynimpname || s->dynexport) 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; } + m->s = s; + m->next = d->ms; + d->ms = m; } - nsize += 20*ndll + 20; - nsize += 4*nimp + 4*ndll; - dynamic = lookup(".windynamic", 0); dynamic->reachable = 1; dynamic->type = SWINDOWS; @@ -199,9 +206,9 @@ initdynimport(void) m->s->sub = dynamic->sub; dynamic->sub = m->s; m->s->value = dynamic->size; - dynamic->size += 4; + dynamic->size += PtrSize; } - dynamic->size += 4; + dynamic->size += PtrSize; } return dr; @@ -211,90 +218,245 @@ static void addimports(vlong fileoff, IMAGE_SECTION_HEADER *datsect) { IMAGE_SECTION_HEADER *isect; - uint32 va; - int noff, aoff, o, last_fn, last_name_off, iat_off; + uvlong n, oftbase, ftbase; Imp *m; Dll *d; Sym* dynamic; - isect = addpesection(".idata", nsize, nsize, 0); + dynamic = lookup(".windynamic", 0); + + // skip import descriptor table (will write it later) + n = 0; + for(d = dr; d != nil; d = d->next) + n++; + seek(cout, fileoff + sizeof(IMAGE_IMPORT_DESCRIPTOR) * (n + 1), 0); + + // write dll names + for(d = dr; d != nil; d = d->next) { + d->nameoff = cpos() - fileoff; + strput(d->name); + } + + // write function names + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) { + m->off = nextsectoff + cpos() - fileoff; + wputl(0); // hint + strput(m->s->dynimpname); + } + } + + // write OriginalFirstThunks + oftbase = cpos() - fileoff; + n = cpos(); + for(d = dr; d != nil; d = d->next) { + d->thunkoff = cpos() - n; + for(m = d->ms; m != nil; m = m->next) + put(m->off); + put(0); + } + + // add pe section and pad it at the end + n = cpos() - fileoff; + isect = addpesection(".idata", n, n, 0); isect->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA| IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; - va = isect->VirtualAddress; - oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = va; - oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect->VirtualSize; + strnput("", isect->SizeOfRawData - n); + cflush(); + // write FirstThunks (allocated in .data section) + ftbase = dynamic->value - datsect->VirtualAddress - PEBASE; + seek(cout, datsect->PointerToRawData + ftbase, 0); + for(d = dr; d != nil; d = d->next) { + for(m = d->ms; m != nil; m = m->next) + put(m->off); + put(0); + } + cflush(); + + // finally write import descriptor table seek(cout, fileoff, 0); - - 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(isect->VirtualAddress + oftbase + d->thunkoff); 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; + lputl(isect->VirtualAddress + d->nameoff); + lputl(datsect->VirtualAddress + ftbase + d->thunkoff); } lputl(0); //end lputl(0); lputl(0); lputl(0); lputl(0); + cflush(); - // 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); + // update data directory + dd[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect->VirtualAddress; + dd[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect->VirtualSize; + dd[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = dynamic->value - PEBASE; + dd[IMAGE_DIRECTORY_ENTRY_IAT].Size = dynamic->size; + + seek(cout, 0, 2); +} + +static int +scmp(const void *p1, const void *p2) +{ + Sym *s1, *s2; + + s1 = *(Sym**)p1; + s2 = *(Sym**)p2; + return strcmp(s1->dynimpname, s2->dynimpname); +} + +static void +initdynexport(void) +{ + int i; + Sym *s; + + nexport = 0; + for(i=0; i<NHASH; i++) + for(s = hash[i]; s != S; s = s->hash) { + if(!s->reachable || !s->dynimpname || !s->dynexport) + continue; + if(nexport+1 > sizeof(dexport)/sizeof(dexport[0])) { + diag("pe dynexport table is full"); + errorexit(); } + + dexport[nexport] = s; + nexport++; } - strnput("", isect->SizeOfRawData - nsize); - cflush(); + qsort(dexport, nexport, sizeof dexport[0], scmp); +} - // 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); +void +addexports(vlong fileoff) +{ + IMAGE_SECTION_HEADER *sect; + IMAGE_EXPORT_DIRECTORY e; + int size, i, va, va_name, va_addr, va_na, v; + + size = sizeof e + 10*nexport + strlen(outfile) + 1; + for(i=0; i<nexport; i++) + size += strlen(dexport[i]->dynimpname) + 1; + + if (nexport == 0) + return; + + sect = addpesection(".edata", size, size, 0); + sect->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ; + va = sect->VirtualAddress; + dd[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = va; + dd[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect->VirtualSize; + + seek(cout, fileoff, 0); + va_name = va + sizeof e + nexport*4; + va_addr = va + sizeof e; + va_na = va + sizeof e + nexport*8; + + e.Characteristics = 0; + e.MajorVersion = 0; + e.MinorVersion = 0; + e.NumberOfFunctions = nexport; + e.NumberOfNames = nexport; + e.Name = va + sizeof e + nexport*10; // Program names. + e.Base = 1; + e.AddressOfFunctions = va_addr; + e.AddressOfNames = va_name; + e.AddressOfNameOrdinals = va_na; + // put IMAGE_EXPORT_DIRECTORY + for (i=0; i<sizeof(e); i++) + cput(((char*)&e)[i]); + // put EXPORT Address Table + for(i=0; i<nexport; i++) + lputl(dexport[i]->value - PEBASE); + // put EXPORT Name Pointer Table + v = e.Name + strlen(outfile)+1; + for(i=0; i<nexport; i++) { + lputl(v); + v += strlen(dexport[i]->dynimpname)+1; } + // put EXPORT Ordinal Table + for(i=0; i<nexport; i++) + wputl(i); + // put Names + strnput(outfile, strlen(outfile)+1); + for(i=0; i<nexport; i++) + strnput(dexport[i]->dynimpname, strlen(dexport[i]->dynimpname)+1); + strnput("", sect->SizeOfRawData - size); cflush(); + seek(cout, 0, 2); } void dope(void) { + Sym *rel; + + /* relocation table */ + rel = lookup(".rel", 0); + rel->reachable = 1; + rel->type = SELFDATA; + initdynimport(); + initdynexport(); +} + +/* + * For more than 8 characters section names, name contains a slash (/) that is + * followed by an ASCII representation of a decimal number that is an offset into + * the string table. + * reference: pecoff_v8.docx Page 24. + * <http://www.microsoft.com/whdc/system/platform/firmware/PECOFFdwn.mspx> + */ +IMAGE_SECTION_HEADER* +newPEDWARFSection(char *name, vlong size) +{ + IMAGE_SECTION_HEADER *h; + char s[8]; + + if(nextsymoff+strlen(name)+1 > sizeof(symnames)) { + diag("pe string table is full"); + errorexit(); + } + + strcpy(&symnames[nextsymoff], name); + sprint(s, "/%d\0", nextsymoff+4); + nextsymoff += strlen(name); + symnames[nextsymoff] = 0; + nextsymoff ++; + h = addpesection(s, size, size, 0); + h->Characteristics = IMAGE_SCN_MEM_READ| + IMAGE_SCN_MEM_DISCARDABLE; + + return h; +} + +static void +addsymtable(void) +{ + IMAGE_SECTION_HEADER *h; + int i, size; + + if(nextsymoff == 0) + return; + + size = nextsymoff + 4; + h = addpesection(".symtab", size, size, 0); + h->Characteristics = IMAGE_SCN_MEM_READ| + IMAGE_SCN_MEM_DISCARDABLE; + fh.PointerToSymbolTable = cpos(); + fh.NumberOfSymbols = 0; + // put symbol string table + lputl(size); + for (i=0; i<nextsymoff; i++) + cput(symnames[i]); + strnput("", h->SizeOfRawData - size); + cflush(); } void @@ -324,42 +486,51 @@ asmbpe(void) IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; addimports(nextfileoff, d); + + addexports(nextfileoff); + + if(!debug['s']) + dwarfaddpeheaders(); + addsymtable(); + fh.NumberOfSections = nsect; fh.TimeDateStamp = time(0); - fh.SizeOfOptionalHeader = sizeof(oh); fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED| IMAGE_FILE_EXECUTABLE_IMAGE|IMAGE_FILE_DEBUG_STRIPPED; - if(thechar == '8') + if (pe64) { + fh.SizeOfOptionalHeader = sizeof(oh64); + set(Magic, 0x20b); // PE32+ + } else { + fh.SizeOfOptionalHeader = sizeof(oh); fh.Characteristics |= IMAGE_FILE_32BIT_MACHINE; - - oh.Magic = 0x10b; // PE32 - oh.MajorLinkerVersion = 1; - oh.MinorLinkerVersion = 0; - oh.SizeOfCode = t->SizeOfRawData; - oh.SizeOfInitializedData = d->SizeOfRawData; - oh.SizeOfUninitializedData = 0; - oh.AddressOfEntryPoint = entryvalue()-PEBASE; - oh.BaseOfCode = t->VirtualAddress; - oh.BaseOfData = d->VirtualAddress; - - oh.ImageBase = PEBASE; - 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 = nextsectoff; - oh.SizeOfHeaders = PEFILEHEADR; - oh.Subsystem = 3; // WINDOWS_CUI - oh.SizeOfStackReserve = 0x00200000; - oh.SizeOfStackCommit = 0x00001000; - oh.SizeOfHeapReserve = 0x00100000; - oh.SizeOfHeapCommit = 0x00001000; - oh.NumberOfRvaAndSizes = 16; + set(Magic, 0x10b); // PE32 + oh.BaseOfData = d->VirtualAddress; + } + set(MajorLinkerVersion, 1); + set(MinorLinkerVersion, 0); + set(SizeOfCode, t->SizeOfRawData); + set(SizeOfInitializedData, d->SizeOfRawData); + set(SizeOfUninitializedData, 0); + set(AddressOfEntryPoint, entryvalue()-PEBASE); + set(BaseOfCode, t->VirtualAddress); + set(ImageBase, PEBASE); + set(SectionAlignment, PESECTALIGN); + set(FileAlignment, PEFILEALIGN); + set(MajorOperatingSystemVersion, 4); + set(MinorOperatingSystemVersion, 0); + set(MajorImageVersion, 1); + set(MinorImageVersion, 0); + set(MajorSubsystemVersion, 4); + set(MinorSubsystemVersion, 0); + set(SizeOfImage, nextsectoff); + set(SizeOfHeaders, PEFILEHEADR); + set(Subsystem, 3); // WINDOWS_CUI + set(SizeOfStackReserve, 0x00200000); + set(SizeOfStackCommit, 0x00001000); + set(SizeOfHeapReserve, 0x00100000); + set(SizeOfHeapCommit, 0x00001000); + set(NumberOfRvaAndSizes, 16); pewrite(); } diff --git a/src/cmd/ld/pe.h b/src/cmd/ld/pe.h index f8161cc4a..6dbf6a5be 100644 --- a/src/cmd/ld/pe.h +++ b/src/cmd/ld/pe.h @@ -72,6 +72,20 @@ typedef struct { uint32 FirstThunk; } IMAGE_IMPORT_DESCRIPTOR; +typedef struct _IMAGE_EXPORT_DIRECTORY { + uint32 Characteristics; + uint32 TimeDateStamp; + uint16 MajorVersion; + uint16 MinorVersion; + uint32 Name; + uint32 Base; + uint32 NumberOfFunctions; + uint32 NumberOfNames; + uint32 AddressOfFunctions; + uint32 AddressOfNames; + uint32 AddressOfNameOrdinals; +} IMAGE_EXPORT_DIRECTORY; + #define PEBASE 0x00400000 // SectionAlignment must be greater than or equal to FileAlignment. // The default is the page size for the architecture. @@ -99,6 +113,7 @@ enum { IMAGE_SCN_MEM_EXECUTE = 0x20000000, IMAGE_SCN_MEM_READ = 0x40000000, IMAGE_SCN_MEM_WRITE = 0x80000000, + IMAGE_SCN_MEM_DISCARDABLE = 0x2000000, IMAGE_DIRECTORY_ENTRY_EXPORT = 0, IMAGE_DIRECTORY_ENTRY_IMPORT = 1, @@ -122,3 +137,38 @@ void peinit(void); void asmbpe(void); void dope(void); +IMAGE_SECTION_HEADER* newPEDWARFSection(char *name, vlong size); + +// X64 +typedef struct { + uint16 Magic; + uint8 MajorLinkerVersion; + uint8 MinorLinkerVersion; + uint32 SizeOfCode; + uint32 SizeOfInitializedData; + uint32 SizeOfUninitializedData; + uint32 AddressOfEntryPoint; + uint32 BaseOfCode; + uint64 ImageBase; + uint32 SectionAlignment; + uint32 FileAlignment; + uint16 MajorOperatingSystemVersion; + uint16 MinorOperatingSystemVersion; + uint16 MajorImageVersion; + uint16 MinorImageVersion; + uint16 MajorSubsystemVersion; + uint16 MinorSubsystemVersion; + uint32 Win32VersionValue; + uint32 SizeOfImage; + uint32 SizeOfHeaders; + uint32 CheckSum; + uint16 Subsystem; + uint16 DllCharacteristics; + uint64 SizeOfStackReserve; + uint64 SizeOfStackCommit; + uint64 SizeOfHeapReserve; + uint64 SizeOfHeapCommit; + uint32 LoaderFlags; + uint32 NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[16]; +} PE64_IMAGE_OPTIONAL_HEADER; |