diff --git a/src/cmd/6l/6.out.h b/src/cmd/6l/6.out.h index 0c22944fa9..15815f4e0d 100644 --- a/src/cmd/6l/6.out.h +++ b/src/cmd/6l/6.out.h @@ -820,10 +820,10 @@ enum D_FILE, D_FILE1, - D_SBIG, /* internal use by 6l only */ - D_INDIR, /* additive */ + D_SBIG = D_INDIR + D_INDIR, + T_TYPE = 1<<0, T_INDEX = 1<<1, T_OFFSET = 1<<2, diff --git a/src/cmd/6l/Makefile b/src/cmd/6l/Makefile index 5db1ba8bd2..eba976c638 100644 --- a/src/cmd/6l/Makefile +++ b/src/cmd/6l/Makefile @@ -36,3 +36,5 @@ clean: install: $(TARG) cp $(TARG) $(BIN)/$(TARG) + +go.o: ../ld/go.c diff --git a/src/cmd/6l/go.c b/src/cmd/6l/go.c index 961a3320b0..82a216520f 100644 --- a/src/cmd/6l/go.c +++ b/src/cmd/6l/go.c @@ -2,603 +2,5 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// go-specific - -// accumulate all type information from .6 files. -// check for inconsistencies. -// define gotypestrings variable if needed. -// define gotypesigs variable if needed. - -// TODO: -// include type info for non-exported types. -// generate debugging section in binary. -// once the dust settles, try to move some code to -// libmach, so that other linkers and ar can share. -// try to make this completely portable and shared -// across linkers - #include "l.h" - -/* - * package import data - */ -typedef struct Import Import; -struct Import -{ - Import *hash; // next in hash table - int export; // marked as export? - char *prefix; // "type", "var", "func", "const" - char *name; - char *def; - char *file; -}; -enum { - NIHASH = 1024 -}; -static Import *ihash[NIHASH]; -static int nimport; - -static int -hashstr(char *name) -{ - int h; - char *cp; - - h = 0; - for(cp = name; *cp; h += *cp++) - h *= 1119; - if(h < 0) - h = ~h; - return h; -} - -static Import * -ilookup(char *name) -{ - int h; - Import *x; - - h = hashstr(name) % NIHASH; - for(x=ihash[h]; x; x=x->hash) - if(x->name[0] == name[0] && strcmp(x->name, name) == 0) - return x; - x = mal(sizeof *x); - x->name = name; - x->hash = ihash[h]; - ihash[h] = x; - nimport++; - return x; -} - -char* -gotypefor(char *name) -{ - Import *x; - char *s, *p; - - s = strdup(name); - p = utfrune(s, 0xB7); // center dot - if(p == nil) - return nil; - *p++ = '.'; - memmove(p, p+1, strlen(p)); - x = ilookup(s); - free(s); - if(x == nil || x->prefix == nil) - return nil; - if(strcmp(x->prefix, "var") != 0 && strcmp(x->prefix, "func") != 0) - return nil; - return x->def; -} - -static void loadpkgdata(char*, char*, int); -static int parsemethod(char**, char*, char**); -static int parsepkgdata(char*, char**, char*, int*, char**, char**, char**); - -void -ldpkg(Biobuf *f, int64 len, char *filename) -{ - char *data, *p0, *p1; - - if(debug['g']) - return; - - if((int)len != len) { - fprint(2, "6l: too much pkg data in %s\n", filename); - return; - } - data = mal(len); - if(Bread(f, data, len) != len) { - fprint(2, "6l: short pkg read %s\n", filename); - return; - } - - // first \n$$ marks beginning of exports - skip rest of line - p0 = strstr(data, "\n$$"); - if(p0 == nil) - return; - p0 += 3; - while(*p0 != '\n' && *p0 != '\0') - p0++; - - // second marks end of exports / beginning of local data - p1 = strstr(p0, "\n$$"); - if(p1 == nil) { - fprint(2, "6l: cannot find end of exports in %s\n", filename); - return; - } - while(*p0 == ' ' || *p0 == '\t' || *p0 == '\n') - p0++; - if(strncmp(p0, "package ", 8) != 0) { - fprint(2, "6l: bad package section in %s\n", filename); - return; - } - p0 += 8; - while(*p0 == ' ' || *p0 == '\t' || *p0 == '\n') - p0++; - while(*p0 != ' ' && *p0 != '\t' && *p0 != '\n') - p0++; - - loadpkgdata(filename, p0, p1 - p0); - - // local types begin where exports end. - // skip rest of line after $$ we found above - p0 = p1 + 3; - while(*p0 != '\n' && *p0 != '\0') - p0++; - - // local types end at next \n$$. - p1 = strstr(p0, "\n$$"); - if(p1 == nil) { - fprint(2, "6l: cannot find end of local types in %s\n", filename); - return; - } - - loadpkgdata(filename, p0, p1 - p0); -} - -static void -loadpkgdata(char *file, char *data, int len) -{ - int export; - char *p, *ep, *prefix, *name, *def; - Import *x; - - file = strdup(file); - p = data; - ep = data + len; - while(parsepkgdata(file, &p, ep, &export, &prefix, &name, &def) > 0) { - x = ilookup(name); - if(x->prefix == nil) { - x->prefix = prefix; - x->def = def; - x->file = file; - x->export = export; - } else { - if(strcmp(x->prefix, prefix) != 0) { - fprint(2, "6l: conflicting definitions for %s\n", name); - fprint(2, "%s:\t%s %s ...\n", x->file, x->prefix, name); - fprint(2, "%s:\t%s %s ...\n", file, prefix, name); - nerrors++; - } - else if(strcmp(x->def, def) != 0) { - fprint(2, "6l: conflicting definitions for %s\n", name); - fprint(2, "%s:\t%s %s %s\n", x->file, x->prefix, name, x->def); - fprint(2, "%s:\t%s %s %s\n", file, prefix, name, def); - nerrors++; - } - - // okay if some .6 say export and others don't. - // all it takes is one. - if(export) - x->export = 1; - } - } -} - -static int -parsepkgdata(char *file, char **pp, char *ep, int *exportp, char **prefixp, char **namep, char **defp) -{ - char *p, *prefix, *name, *def, *edef, *meth; - int n; - - // skip white space - p = *pp; - while(p < ep && (*p == ' ' || *p == '\t' || *p == '\n')) - p++; - if(p == ep || strncmp(p, "$$\n", 3) == 0) - return 0; - - // [export|package ] - *exportp = 0; - if(p + 7 <= ep && strncmp(p, "export ", 7) == 0) { - *exportp = 1; - p += 7; - } - else if(p + 8 <= ep && strncmp(p, "package ", 8) == 0) { - *exportp = 2; - p += 8; - } - - // prefix: (var|type|func|const) - prefix = p; - - prefix = p; - if(p + 6 > ep) - return -1; - if(strncmp(p, "var ", 4) == 0) - p += 4; - else if(strncmp(p, "type ", 5) == 0) - p += 5; - else if(strncmp(p, "func ", 5) == 0) - p += 5; - else if(strncmp(p, "const ", 6) == 0) - p += 6; - else{ - fprint(2, "6l: confused in pkg data near <<%.20s>>\n", p); - nerrors++; - return -1; - } - p[-1] = '\0'; - - // name: a.b followed by space - name = p; - while(p < ep && *p != ' ') - p++; - if(p >= ep) - return -1; - *p++ = '\0'; - - // def: free form to new line - def = p; - while(p < ep && *p != '\n') - p++; - if(p >= ep) - return -1; - edef = p; - *p++ = '\0'; - - // include methods on successive lines in def of named type - while(parsemethod(&p, ep, &meth) > 0) { - *edef++ = '\n'; // overwrites '\0' - if(edef+1 > meth) { - // We want to indent methods with a single \t. - // 6g puts at least one char of indent before all method defs, - // so there will be room for the \t. If the method def wasn't - // indented we could do something more complicated, - // but for now just diagnose the problem and assume - // 6g will keep indenting for us. - fprint(2, "6l: %s: expected methods to be indented %p %p %.10s\n", - file, edef, meth, meth); - nerrors++; - return -1; - } - *edef++ = '\t'; - n = strlen(meth); - memmove(edef, meth, n); - edef += n; - } - - // done - *pp = p; - *prefixp = prefix; - *namep = name; - *defp = def; - return 1; -} - -static int -parsemethod(char **pp, char *ep, char **methp) -{ - char *p; - - // skip white space - p = *pp; - while(p < ep && (*p == ' ' || *p == '\t')) - p++; - if(p == ep) - return 0; - - // if it says "func (", it's a method - if(p + 6 >= ep || strncmp(p, "func (", 6) != 0) - return 0; - - // definition to end of line - *methp = p; - while(p < ep && *p != '\n') - p++; - if(p >= ep) { - fprint(2, "6l: lost end of line in method definition\n"); - *pp = ep; - return -1; - } - *p++ = '\0'; - *pp = p; - return 1; -} - -static int -importcmp(const void *va, const void *vb) -{ - Import *a, *b; - - a = *(Import**)va; - b = *(Import**)vb; - return strcmp(a->name, b->name); -} - -static int -symcmp(const void *va, const void *vb) -{ - Sym *a, *b; - - a = *(Sym**)va; - b = *(Sym**)vb; - return strcmp(a->name, b->name); -} - -// if there is an undefined reference to gotypestrings, -// create it. c declaration is -// extern char gotypestrings[]; -// ironically, gotypestrings is a c variable, because there -// is no way to forward declare a string in go. -void -definetypestrings(void) -{ - int i, j, len, n, w; - char *p; - Import **all, *x; - Fmt f; - Prog *prog; - Sym *s; - - if(debug['g']) - return; - - if(debug['v']) - Bprint(&bso, "%5.2f definetypestrings\n", cputime()); - - s = lookup("gotypestrings", 0); - if(s->type == 0) - return; - if(s->type != SXREF) { - diag("gotypestrings already defined"); - return; - } - s->type = SDATA; - - // make a list of all the type exports - n = 0; - for(i=0; ihash) - if(strcmp(x->prefix, "type") == 0) - n++; - all = mal(n*sizeof all[0]); - j = 0; - for(i=0; ihash) - if(strcmp(x->prefix, "type") == 0) - all[j++] = x; - - // sort them by name - qsort(all, n, sizeof all[0], importcmp); - - // make a big go string containing all the types - fmtstrinit(&f); - fmtprint(&f, "xxxx"); // 4-byte length - for(i=0; idef, '\n'); - if(p) - len = p - all[i]->def; - else - len = strlen(all[i]->def); - fmtprint(&f, "%s %.*s\n", all[i]->name, utfnlen(all[i]->def, len), all[i]->def); - } - p = fmtstrflush(&f); - n = strlen(p); - s->value = n; - - // go strings begin with 4-byte length. - // amd64 is little-endian. - len = n - 4; - p[0] = len; - p[1] = len >> 8; - p[2] = len >> 16; - p[3] = len >> 24; - - // have data, need to create linker representation. - // linker stores big data as sequence of pieces - // with int8 length, so break p into 100-byte chunks. - // (had to add D_SBIG even to do that; the compiler - // would have generated 8-byte chunks.) - for(i=0; i n - i) - w = n - i; - prog = newdata(s, i, w, D_EXTERN); - prog->to.type = D_SBIG; - prog->to.sbig = p + i; - } - - if(debug['v']) - Bprint(&bso, "%5.2f typestrings %d\n", cputime(), n); -} - -// if there is an undefined reference to gotypesigs, create it. -// c declaration is -// extern Sigt *gotypesigs[]; -// extern int ngotypesigs; -// used by sys.unreflect runtime. -void -definetypesigs(void) -{ - int i, j, n; - Sym **all, *s, *x; - Prog *prog; - - if(debug['g']) - return; - - if(debug['v']) - Bprint(&bso, "%5.2f definetypesigs\n", cputime()); - - s = lookup("gotypesigs", 0); - if(s->type == 0) - return; - if(s->type != SXREF) { - diag("gotypesigs already defined"); - return; - } - s->type = SDATA; - - // make a list of all the sigt symbols. - n = 0; - for(i=0; ilink) - if(memcmp(x->name, "sigt·", 6) == 0 && x->type != Sxxx) - n++; - all = mal(n*sizeof all[0]); - j = 0; - for(i=0; ilink) - if(memcmp(x->name, "sigt·", 6) == 0 && x->type != Sxxx) - all[j++] = x; - - // sort them by name - qsort(all, n, sizeof all[0], symcmp); - - // emit array as sequence of references. - enum { PtrSize = 8 }; - for(i=0; ito.type = D_ADDR; - prog->to.index = D_EXTERN; - prog->to.sym = all[i]; - } - s->value = PtrSize*n; - if(n == 0) - s->value = 1; // must have non-zero size or 6l complains - - // emit count - s = lookup("ngotypesigs", 0); - s->type = SDATA; - s->value = sizeof(int32); - prog = newdata(s, 0, sizeof(int32), D_EXTERN); - prog->to.offset = n; - - if(debug['v']) - Bprint(&bso, "%5.2f typesigs %d\n", cputime(), n); -} - -int -isinitfunc(Sym *s) -{ - char *p; - - p = utfrune(s->name, 0xb7); // 0xb7 = '·' - if(p == nil) - return 0; - if(memcmp(p, "·Init·", 8) == 0 || memcmp(p, "·init·", 8) == 0) - return 1; - return 0; -} - -static void mark(Sym*); -static int markdepth; - -static void -markdata(Prog *p, Sym *s) -{ - markdepth++; - if(p != P && debug['v'] > 1) - Bprint(&bso, "%d markdata %s\n", markdepth, s->name); - for(; p != P; p=p->dlink) - if(p->to.sym) - mark(p->to.sym); - markdepth--; -} - -static void -marktext(Prog *p) -{ - if(p == P) - return; - if(p->as != ATEXT) { - diag("marktext: %P", p); - return; - } - markdepth++; - if(debug['v'] > 1) - Bprint(&bso, "%d marktext %s\n", markdepth, p->from.sym->name); - for(p=p->link; p != P; p=p->link) { - if(p->as == ATEXT || p->as == ADATA || p->as == AGLOBL) - break; - if(p->from.sym) - mark(p->from.sym); - if(p->to.sym) - mark(p->to.sym); - } - markdepth--; -} - -static void -mark(Sym *s) -{ - if(s == S || s->reachable) - return; - s->reachable = 1; - if(s->text) - marktext(s->text); - if(s->data) - markdata(s->data, s); -} - -static void -sweeplist(Prog **first, Prog **last) -{ - int reachable; - Prog *p, *q; - - reachable = 1; - q = P; - for(p=*first; p != P; p=p->link) { - switch(p->as) { - case ATEXT: - case ADATA: - case AGLOBL: - reachable = p->from.sym->reachable; - if(!reachable) { - if(debug['v'] > 1) - Bprint(&bso, "discard %s\n", p->from.sym->name); - p->from.sym->type = Sxxx; - } - break; - } - if(reachable) { - if(q == P) - *first = p; - else - q->link = p; - q = p; - } - } - if(q == P) - *first = P; - else - q->link = P; - *last = q; -} - -void -deadcode(void) -{ - if(debug['v']) - Bprint(&bso, "%5.2f deadcode\n", cputime()); - - mark(lookup(INITENTRY, 0)); - mark(lookup("sys·morestack", 0)); - - sweeplist(&firstp, &lastp); - sweeplist(&datap, &edatap); -} - +#include "../ld/go.c" diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h index 83c28bd4b7..2d3cd535a1 100644 --- a/src/cmd/6l/l.h +++ b/src/cmd/6l/l.h @@ -441,7 +441,6 @@ void xdefine(char*, int, vlong); void xfol(Prog*); void zaddr(Biobuf*, Adr*, Sym*[]); void zerosig(char*); -int isinitfunc(Sym*); void machseg(char*, vlong, vlong, vlong, vlong, uint32, uint32, uint32, uint32); void machsymseg(uint32, uint32); diff --git a/src/cmd/8l/8.out.h b/src/cmd/8l/8.out.h index f7750129d8..1b9c88b1d9 100644 --- a/src/cmd/8l/8.out.h +++ b/src/cmd/8l/8.out.h @@ -79,9 +79,6 @@ enum as ACMPSB, ACMPSL, ACMPSW, - ACMPXCHGB, - ACMPXCHGL, - ACMPXCHGW, ADAA, ADAS, ADATA, @@ -387,6 +384,10 @@ enum as ASIGNAME, + ACMPXCHGB, + ACMPXCHGL, + ACMPXCHGW, + ALAST }; @@ -441,13 +442,15 @@ enum D_FCONST = 66, D_SCONST = 67, D_ADDR = 68, - D_CONST2 = 69, D_FILE, D_FILE1, D_INDIR, /* additive */ + D_SBIG = D_INDIR + D_INDIR, + D_CONST2, + T_TYPE = 1<<0, T_INDEX = 1<<1, T_OFFSET = 1<<2, diff --git a/src/cmd/8l/Makefile b/src/cmd/8l/Makefile index e11b6687a9..6b8c73ae2e 100644 --- a/src/cmd/8l/Makefile +++ b/src/cmd/8l/Makefile @@ -16,6 +16,7 @@ OFILES=\ optab.$O\ pass.$O\ span.$O\ + go.$O\ HFILES=\ l.h\ @@ -35,3 +36,6 @@ clean: install: $(TARG) cp $(TARG) $(BIN)/$(TARG) + +go.o: ../ld/go.c + diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index 114c663690..9afd78faa2 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -58,6 +58,7 @@ struct Adr char u0scon[8]; Prog *u0cond; /* not used, but should be D_BRANCH */ Ieee u0ieee; + char *u0sbig; } u0; union { @@ -74,6 +75,7 @@ struct Adr #define scon u0.u0scon #define cond u0.u0cond #define ieee u0.u0ieee +#define sbig u0.u0sbig #define autom u1.u1autom #define sym u1.u1sym @@ -84,6 +86,7 @@ struct Prog Adr to; Prog *forwd; Prog* link; + Prog* dlink; Prog* pcond; /* work on this */ int32 pc; int32 line; @@ -108,10 +111,14 @@ struct Sym short become; short frame; uchar subtype; + uchar dupok; + uchar reachable; ushort file; int32 value; int32 sig; Sym* link; + Prog* text; + Prog* data; }; struct Optab { @@ -123,7 +130,9 @@ struct Optab enum { - STEXT = 1, + Sxxx, + + STEXT, SDATA, SBSS, SDATA1, @@ -352,6 +361,8 @@ void lputl(int32); void main(int, char*[]); void mkfwd(void); void* mal(uint32); +Prog* newdata(Sym*, int, int, int); +Prog* newtext(Prog*, Sym*); void nuxiinit(void); void objfile(char*); int opsize(Prog*); @@ -375,6 +386,16 @@ uint32 machheadr(void); uint32 elfheadr(void); void whatsys(void); +/* + * go.c + */ +void deadcode(void); +void definetypestrings(void); +void definetypesigs(void); +char* gotypefor(char *name); +void ldpkg(Biobuf *f, int64 len, char *filename); + + /* set by call to whatsys() */ extern char* goroot; extern char* goarch; diff --git a/src/cmd/8l/list.c b/src/cmd/8l/list.c index 873f7c1f85..79777d8b1f 100644 --- a/src/cmd/8l/list.c +++ b/src/cmd/8l/list.c @@ -99,7 +99,7 @@ Dconv(Fmt *fp) a = va_arg(fp->args, Adr*); i = a->type; - if(i >= D_INDIR) { + if(i >= D_INDIR && i < 2*D_INDIR) { if(a->offset) sprint(str, "%ld(%R)", a->offset, i-D_INDIR); else diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c index c4aa3486be..a14f52d6f7 100644 --- a/src/cmd/8l/obj.c +++ b/src/cmd/8l/obj.c @@ -333,6 +333,10 @@ main(int argc, char *argv[]) sprint(a, "%s/lib/lib_%s_%s.a", goroot, goarch, goos); objfile(a); } + definetypestrings(); + definetypesigs(); + deadcode(); + firstp = firstp->link; if(firstp == P) errorexit(); @@ -848,7 +852,7 @@ ldobj(Biobuf *f, int32 len, char *pn) import1 = Boffset(f); Bseek(f, import0, 0); -// ldpkg(f, import1 - import0 - 2, pn); // -2 for !\n + ldpkg(f, import1 - import0 - 2, pn); // -2 for !\n Bseek(f, import1, 0); newloop: @@ -1017,6 +1021,20 @@ loop: case ADATA: data: + // Assume that AGLOBL comes after ADATA. + // If we've seen an AGLOBL that said this sym was DUPOK, + // ignore any more ADATA we see, which must be + // redefinitions. + s = p->from.sym; + if(s != S && s->dupok) { + if(debug['v']) + Bprint(&bso, "skipping %s in %s: dupok", s->name, pn); + goto loop; + } + if(s != S) { + p->dlink = s->data; + s->data = p; + } if(edatap == P) datap = p; else @@ -1056,19 +1074,7 @@ loop: } diag("%s: redefinition: %s\n%P", pn, s->name, p); } - s->type = STEXT; - s->value = pc; - lastp->link = p; - lastp = p; - p->pc = pc; - pc++; - if(textp == P) { - textp = p; - etextp = p; - goto loop; - } - etextp->pcond = p; - etextp = p; + newtext(p, s); goto loop; case AFMOVF: diff --git a/src/cmd/8l/optab.c b/src/cmd/8l/optab.c index 934f14e9bc..df5ba629fc 100644 --- a/src/cmd/8l/optab.c +++ b/src/cmd/8l/optab.c @@ -388,9 +388,6 @@ Optab optab[] = { ACMPSB, ynone, Pb, 0xa6 }, { ACMPSL, ynone, Px, 0xa7 }, { ACMPSW, ynone, Pe, 0xa7 }, - { ACMPXCHGB, yrb_mb, Pm, 0xb0 }, - { ACMPXCHGL, yrl_ml, Pm, 0xb1 }, - { ACMPXCHGW, yrl_ml, Pm, 0xb1 }, { ADAA, ynone, Px, 0x27 }, { ADAS, ynone, Px, 0x2f }, { ADATA }, @@ -688,5 +685,11 @@ Optab optab[] = { AFYL2X, ynone, Px, 0xd9, 0xf1 }, { AFYL2XP1, ynone, Px, 0xd9, 0xf9 }, { AEND }, + { ADYNT }, + { AINIT }, + { ASIGNAME }, + { ACMPXCHGB, yrb_mb, Pm, 0xb0 }, + { ACMPXCHGL, yrl_ml, Pm, 0xb1 }, + { ACMPXCHGW, yrl_ml, Pm, 0xb1 }, 0 }; diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c index 0726d1ea8f..2e52edc0cc 100644 --- a/src/cmd/8l/pass.c +++ b/src/cmd/8l/pass.c @@ -802,7 +802,7 @@ ckoff(Sym *s, int32 v) diag("relocation offset %ld for %s out of range", v, s->name); } -static Prog* +Prog* newdata(Sym *s, int o, int w, int t) { Prog *p; @@ -820,6 +820,30 @@ newdata(Sym *s, int o, int w, int t) p->from.sym = s; p->from.offset = o; p->to.type = D_CONST; + p->dlink = s->data; + s->data = p; + return p; +} + +Prog* +newtext(Prog *p, Sym *s) +{ + if(p == P) { + p = prg(); + p->as = ATEXT; + p->from.sym = s; + } + s->type = STEXT; + s->text = p; + s->value = pc; + lastp->link = p; + lastp = p; + p->pc = pc++; + if(textp == P) + textp = p; + else + etextp->pcond = p; + etextp = p; return p; } diff --git a/src/cmd/8l/span.c b/src/cmd/8l/span.c index 81703d3046..6adf5295ab 100644 --- a/src/cmd/8l/span.c +++ b/src/cmd/8l/span.c @@ -146,12 +146,9 @@ xdefine(char *p, int t, int32 v) } void -putsymb(char *s, int t, int32 v, int ver) +putsymb(char *s, int t, int32 v, int ver, char *go) { int i, j, f; - char *go; - - go = nil; // TODO if(t == 'f') s++; @@ -211,25 +208,25 @@ asmsym(void) s = lookup("etext", 0); if(s->type == STEXT) - putsymb(s->name, 'T', s->value, s->version); + putsymb(s->name, 'T', s->value, s->version, nil); for(h=0; hlink) switch(s->type) { case SCONST: - putsymb(s->name, 'D', s->value, s->version); + putsymb(s->name, 'D', s->value, s->version, gotypefor(s->name)); continue; case SDATA: - putsymb(s->name, 'D', s->value+INITDAT, s->version); + putsymb(s->name, 'D', s->value+INITDAT, s->version, gotypefor(s->name)); continue; case SBSS: - putsymb(s->name, 'B', s->value+INITDAT, s->version); + putsymb(s->name, 'B', s->value+INITDAT, s->version, gotypefor(s->name)); continue; case SFILE: - putsymb(s->name, 'f', s->value, s->version); + putsymb(s->name, 'f', s->value, s->version, nil); continue; } @@ -241,22 +238,22 @@ asmsym(void) /* filenames first */ for(a=p->to.autom; a; a=a->link) if(a->type == D_FILE) - putsymb(a->asym->name, 'z', a->aoffset, 0); + putsymb(a->asym->name, 'z', a->aoffset, 0, nil); else if(a->type == D_FILE1) - putsymb(a->asym->name, 'Z', a->aoffset, 0); + putsymb(a->asym->name, 'Z', a->aoffset, 0, nil); - putsymb(s->name, 'T', s->value, s->version); + putsymb(s->name, 'T', s->value, s->version, gotypefor(s->name)); /* frame, auto and param after */ - putsymb(".frame", 'm', p->to.offset+4, 0); + putsymb(".frame", 'm', p->to.offset+4, 0, nil); for(a=p->to.autom; a; a=a->link) if(a->type == D_AUTO) - putsymb(a->asym->name, 'a', -a->aoffset, 0); + putsymb(a->asym->name, 'a', -a->aoffset, 0, nil); else if(a->type == D_PARAM) - putsymb(a->asym->name, 'p', a->aoffset, 0); + putsymb(a->asym->name, 'p', a->aoffset, 0, nil); } if(debug['v'] || debug['n']) Bprint(&bso, "symsize = %lud\n", symsize); @@ -366,7 +363,7 @@ oclass(Adr *a) { int32 v; - if(a->type >= D_INDIR || a->index != D_NONE) { + if((a->type >= D_INDIR && a->type < 2*D_INDIR) || a->index != D_NONE) { if(a->index != D_NONE && a->scale == 0) { if(a->type == D_ADDR) { switch(a->index) { @@ -615,7 +612,7 @@ asmand(Adr *a, int r) v = a->offset; t = a->type; if(a->index != D_NONE) { - if(t >= D_INDIR) { + if(t >= D_INDIR && t < 2*D_INDIR) { t -= D_INDIR; if(t == D_NONE) { *andptr++ = (0 << 6) | (4 << 0) | (r << 3); @@ -663,7 +660,7 @@ asmand(Adr *a, int r) *andptr++ = (3 << 6) | (reg[t] << 0) | (r << 3); return; } - if(t >= D_INDIR) { + if(t >= D_INDIR && t < 2*D_INDIR) { t -= D_INDIR; if(t == D_NONE || (D_CS <= t && t <= D_GS)) { *andptr++ = (0 << 6) | (5 << 0) | (r << 3); diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c new file mode 100644 index 0000000000..e8366e8252 --- /dev/null +++ b/src/cmd/ld/go.c @@ -0,0 +1,587 @@ +// Copyright 2009 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. + +// go-specific code shared across loaders (5l, 6l, 8l). + +// accumulate all type information from .6 files. +// check for inconsistencies. +// define gotypestrings variable if needed. +// define gotypesigs variable if needed. + +// TODO: +// include type info for non-exported types. +// generate debugging section in binary. +// once the dust settles, try to move some code to +// libmach, so that other linkers and ar can share. + +/* + * package import data + */ +typedef struct Import Import; +struct Import +{ + Import *hash; // next in hash table + int export; // marked as export? + char *prefix; // "type", "var", "func", "const" + char *name; + char *def; + char *file; +}; +enum { + NIHASH = 1024 +}; +static Import *ihash[NIHASH]; +static int nimport; + +static int +hashstr(char *name) +{ + int h; + char *cp; + + h = 0; + for(cp = name; *cp; h += *cp++) + h *= 1119; + if(h < 0) + h = ~h; + return h; +} + +static Import * +ilookup(char *name) +{ + int h; + Import *x; + + h = hashstr(name) % NIHASH; + for(x=ihash[h]; x; x=x->hash) + if(x->name[0] == name[0] && strcmp(x->name, name) == 0) + return x; + x = mal(sizeof *x); + x->name = name; + x->hash = ihash[h]; + ihash[h] = x; + nimport++; + return x; +} + +char* +gotypefor(char *name) +{ + Import *x; + char *s, *p; + + s = strdup(name); + p = utfrune(s, 0xB7); // center dot + if(p == nil) + return nil; + *p++ = '.'; + memmove(p, p+1, strlen(p)); + x = ilookup(s); + free(s); + if(x == nil || x->prefix == nil) + return nil; + if(strcmp(x->prefix, "var") != 0 && strcmp(x->prefix, "func") != 0) + return nil; + return x->def; +} + +static void loadpkgdata(char*, char*, int); +static int parsemethod(char**, char*, char**); +static int parsepkgdata(char*, char**, char*, int*, char**, char**, char**); + +void +ldpkg(Biobuf *f, int64 len, char *filename) +{ + char *data, *p0, *p1; + + if(debug['g']) + return; + + if((int)len != len) { + fprint(2, "6l: too much pkg data in %s\n", filename); + return; + } + data = mal(len); + if(Bread(f, data, len) != len) { + fprint(2, "6l: short pkg read %s\n", filename); + return; + } + + // first \n$$ marks beginning of exports - skip rest of line + p0 = strstr(data, "\n$$"); + if(p0 == nil) + return; + p0 += 3; + while(*p0 != '\n' && *p0 != '\0') + p0++; + + // second marks end of exports / beginning of local data + p1 = strstr(p0, "\n$$"); + if(p1 == nil) { + fprint(2, "6l: cannot find end of exports in %s\n", filename); + return; + } + while(*p0 == ' ' || *p0 == '\t' || *p0 == '\n') + p0++; + if(strncmp(p0, "package ", 8) != 0) { + fprint(2, "6l: bad package section in %s\n", filename); + return; + } + p0 += 8; + while(*p0 == ' ' || *p0 == '\t' || *p0 == '\n') + p0++; + while(*p0 != ' ' && *p0 != '\t' && *p0 != '\n') + p0++; + + loadpkgdata(filename, p0, p1 - p0); + + // local types begin where exports end. + // skip rest of line after $$ we found above + p0 = p1 + 3; + while(*p0 != '\n' && *p0 != '\0') + p0++; + + // local types end at next \n$$. + p1 = strstr(p0, "\n$$"); + if(p1 == nil) { + fprint(2, "6l: cannot find end of local types in %s\n", filename); + return; + } + + loadpkgdata(filename, p0, p1 - p0); +} + +static void +loadpkgdata(char *file, char *data, int len) +{ + int export; + char *p, *ep, *prefix, *name, *def; + Import *x; + + file = strdup(file); + p = data; + ep = data + len; + while(parsepkgdata(file, &p, ep, &export, &prefix, &name, &def) > 0) { + x = ilookup(name); + if(x->prefix == nil) { + x->prefix = prefix; + x->def = def; + x->file = file; + x->export = export; + } else { + if(strcmp(x->prefix, prefix) != 0) { + fprint(2, "6l: conflicting definitions for %s\n", name); + fprint(2, "%s:\t%s %s ...\n", x->file, x->prefix, name); + fprint(2, "%s:\t%s %s ...\n", file, prefix, name); + nerrors++; + } + else if(strcmp(x->def, def) != 0) { + fprint(2, "6l: conflicting definitions for %s\n", name); + fprint(2, "%s:\t%s %s %s\n", x->file, x->prefix, name, x->def); + fprint(2, "%s:\t%s %s %s\n", file, prefix, name, def); + nerrors++; + } + + // okay if some .6 say export and others don't. + // all it takes is one. + if(export) + x->export = 1; + } + } +} + +static int +parsepkgdata(char *file, char **pp, char *ep, int *exportp, char **prefixp, char **namep, char **defp) +{ + char *p, *prefix, *name, *def, *edef, *meth; + int n; + + // skip white space + p = *pp; + while(p < ep && (*p == ' ' || *p == '\t' || *p == '\n')) + p++; + if(p == ep || strncmp(p, "$$\n", 3) == 0) + return 0; + + // [export|package ] + *exportp = 0; + if(p + 7 <= ep && strncmp(p, "export ", 7) == 0) { + *exportp = 1; + p += 7; + } + else if(p + 8 <= ep && strncmp(p, "package ", 8) == 0) { + *exportp = 2; + p += 8; + } + + // prefix: (var|type|func|const) + prefix = p; + + prefix = p; + if(p + 6 > ep) + return -1; + if(strncmp(p, "var ", 4) == 0) + p += 4; + else if(strncmp(p, "type ", 5) == 0) + p += 5; + else if(strncmp(p, "func ", 5) == 0) + p += 5; + else if(strncmp(p, "const ", 6) == 0) + p += 6; + else{ + fprint(2, "6l: confused in pkg data near <<%.20s>>\n", p); + nerrors++; + return -1; + } + p[-1] = '\0'; + + // name: a.b followed by space + name = p; + while(p < ep && *p != ' ') + p++; + if(p >= ep) + return -1; + *p++ = '\0'; + + // def: free form to new line + def = p; + while(p < ep && *p != '\n') + p++; + if(p >= ep) + return -1; + edef = p; + *p++ = '\0'; + + // include methods on successive lines in def of named type + while(parsemethod(&p, ep, &meth) > 0) { + *edef++ = '\n'; // overwrites '\0' + if(edef+1 > meth) { + // We want to indent methods with a single \t. + // 6g puts at least one char of indent before all method defs, + // so there will be room for the \t. If the method def wasn't + // indented we could do something more complicated, + // but for now just diagnose the problem and assume + // 6g will keep indenting for us. + fprint(2, "6l: %s: expected methods to be indented %p %p %.10s\n", + file, edef, meth, meth); + nerrors++; + return -1; + } + *edef++ = '\t'; + n = strlen(meth); + memmove(edef, meth, n); + edef += n; + } + + // done + *pp = p; + *prefixp = prefix; + *namep = name; + *defp = def; + return 1; +} + +static int +parsemethod(char **pp, char *ep, char **methp) +{ + char *p; + + // skip white space + p = *pp; + while(p < ep && (*p == ' ' || *p == '\t')) + p++; + if(p == ep) + return 0; + + // if it says "func (", it's a method + if(p + 6 >= ep || strncmp(p, "func (", 6) != 0) + return 0; + + // definition to end of line + *methp = p; + while(p < ep && *p != '\n') + p++; + if(p >= ep) { + fprint(2, "6l: lost end of line in method definition\n"); + *pp = ep; + return -1; + } + *p++ = '\0'; + *pp = p; + return 1; +} + +static int +importcmp(const void *va, const void *vb) +{ + Import *a, *b; + + a = *(Import**)va; + b = *(Import**)vb; + return strcmp(a->name, b->name); +} + +static int +symcmp(const void *va, const void *vb) +{ + Sym *a, *b; + + a = *(Sym**)va; + b = *(Sym**)vb; + return strcmp(a->name, b->name); +} + +// if there is an undefined reference to gotypestrings, +// create it. c declaration is +// extern char gotypestrings[]; +// ironically, gotypestrings is a c variable, because there +// is no way to forward declare a string in go. +void +definetypestrings(void) +{ + int i, j, len, n, w; + char *p; + Import **all, *x; + Fmt f; + Prog *prog; + Sym *s; + + if(debug['g']) + return; + + if(debug['v']) + Bprint(&bso, "%5.2f definetypestrings\n", cputime()); + + s = lookup("gotypestrings", 0); + if(s->type == 0) + return; + if(s->type != SXREF) { + diag("gotypestrings already defined"); + return; + } + s->type = SDATA; + + // make a list of all the type exports + n = 0; + for(i=0; ihash) + if(strcmp(x->prefix, "type") == 0) + n++; + all = mal(n*sizeof all[0]); + j = 0; + for(i=0; ihash) + if(strcmp(x->prefix, "type") == 0) + all[j++] = x; + + // sort them by name + qsort(all, n, sizeof all[0], importcmp); + + // make a big go string containing all the types + fmtstrinit(&f); + fmtprint(&f, "xxxx"); // 4-byte length + for(i=0; idef, '\n'); + if(p) + len = p - all[i]->def; + else + len = strlen(all[i]->def); + fmtprint(&f, "%s %.*s\n", all[i]->name, utfnlen(all[i]->def, len), all[i]->def); + } + p = fmtstrflush(&f); + n = strlen(p); + s->value = n; + + // go strings begin with 4-byte length. + // amd64 is little-endian. + len = n - 4; + p[0] = len; + p[1] = len >> 8; + p[2] = len >> 16; + p[3] = len >> 24; + + // have data, need to create linker representation. + // linker stores big data as sequence of pieces + // with int8 length, so break p into 100-byte chunks. + // (had to add D_SBIG even to do that; the compiler + // would have generated 8-byte chunks.) + for(i=0; i n - i) + w = n - i; + prog = newdata(s, i, w, D_EXTERN); + prog->to.type = D_SBIG; + prog->to.sbig = p + i; + } + + if(debug['v']) + Bprint(&bso, "%5.2f typestrings %d\n", cputime(), n); +} + +// if there is an undefined reference to gotypesigs, create it. +// c declaration is +// extern Sigt *gotypesigs[]; +// extern int ngotypesigs; +// used by sys.unreflect runtime. +void +definetypesigs(void) +{ + int i, j, n; + Sym **all, *s, *x; + Prog *prog; + + if(debug['g']) + return; + + if(debug['v']) + Bprint(&bso, "%5.2f definetypesigs\n", cputime()); + + s = lookup("gotypesigs", 0); + if(s->type == 0) + return; + if(s->type != SXREF) { + diag("gotypesigs already defined"); + return; + } + s->type = SDATA; + + // make a list of all the sigt symbols. + n = 0; + for(i=0; ilink) + if(memcmp(x->name, "sigt·", 6) == 0 && x->type != Sxxx) + n++; + all = mal(n*sizeof all[0]); + j = 0; + for(i=0; ilink) + if(memcmp(x->name, "sigt·", 6) == 0 && x->type != Sxxx) + all[j++] = x; + + // sort them by name + qsort(all, n, sizeof all[0], symcmp); + + // emit array as sequence of references. + enum { PtrSize = 8 }; + for(i=0; ito.type = D_ADDR; + prog->to.index = D_EXTERN; + prog->to.sym = all[i]; + } + s->value = PtrSize*n; + if(n == 0) + s->value = 1; // must have non-zero size or 6l complains + + // emit count + s = lookup("ngotypesigs", 0); + s->type = SDATA; + s->value = sizeof(int32); + prog = newdata(s, 0, sizeof(int32), D_EXTERN); + prog->to.offset = n; + + if(debug['v']) + Bprint(&bso, "%5.2f typesigs %d\n", cputime(), n); +} + +static void mark(Sym*); +static int markdepth; + +static void +markdata(Prog *p, Sym *s) +{ + markdepth++; + if(p != P && debug['v'] > 1) + Bprint(&bso, "%d markdata %s\n", markdepth, s->name); + for(; p != P; p=p->dlink) + if(p->to.sym) + mark(p->to.sym); + markdepth--; +} + +static void +marktext(Prog *p) +{ + if(p == P) + return; + if(p->as != ATEXT) { + diag("marktext: %P", p); + return; + } + markdepth++; + if(debug['v'] > 1) + Bprint(&bso, "%d marktext %s\n", markdepth, p->from.sym->name); + for(p=p->link; p != P; p=p->link) { + if(p->as == ATEXT || p->as == ADATA || p->as == AGLOBL) + break; + if(p->from.sym) + mark(p->from.sym); + if(p->to.sym) + mark(p->to.sym); + } + markdepth--; +} + +static void +mark(Sym *s) +{ + if(s == S || s->reachable) + return; + s->reachable = 1; + if(s->text) + marktext(s->text); + if(s->data) + markdata(s->data, s); +} + +static void +sweeplist(Prog **first, Prog **last) +{ + int reachable; + Prog *p, *q; + + reachable = 1; + q = P; + for(p=*first; p != P; p=p->link) { + switch(p->as) { + case ATEXT: + case ADATA: + case AGLOBL: + reachable = p->from.sym->reachable; + if(!reachable) { + if(debug['v'] > 1) + Bprint(&bso, "discard %s\n", p->from.sym->name); + p->from.sym->type = Sxxx; + } + break; + } + if(reachable) { + if(q == P) + *first = p; + else + q->link = p; + q = p; + } + } + if(q == P) + *first = P; + else + q->link = P; + *last = q; +} + +void +deadcode(void) +{ + if(debug['v']) + Bprint(&bso, "%5.2f deadcode\n", cputime()); + + mark(lookup(INITENTRY, 0)); + mark(lookup("sys·morestack", 0)); + +// sweeplist(&firstp, &lastp); +// sweeplist(&datap, &edatap); +} +