| // 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 |
| 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 = strdup(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*, 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, "%s: too much pkg data in %s\n", argv0, filename); |
| return; |
| } |
| data = mal(len+1); |
| if(Bread(f, data, len) != len) { |
| fprint(2, "%s: short pkg read %s\n", argv0, filename); |
| return; |
| } |
| data[len] = '\0'; |
| |
| // 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, "%s: cannot find end of exports in %s\n", argv0, filename); |
| return; |
| } |
| while(*p0 == ' ' || *p0 == '\t' || *p0 == '\n') |
| p0++; |
| if(strncmp(p0, "package ", 8) != 0) { |
| fprint(2, "%s: bad package section in %s\n", argv0, 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, "%s: cannot find end of local types in %s\n", argv0, filename); |
| return; |
| } |
| |
| loadpkgdata(filename, p0, p1 - p0); |
| } |
| |
| /* |
| * a and b don't match. |
| * is one a forward declaration and the other a valid completion? |
| * if so, return the one to keep. |
| */ |
| char* |
| forwardfix(char *a, char *b) |
| { |
| char *t; |
| |
| if(strlen(a) > strlen(b)) { |
| t = a; |
| a = b; |
| b = t; |
| } |
| if(strcmp(a, "struct") == 0 && strncmp(b, "struct ", 7) == 0) |
| return b; |
| if(strcmp(a, "interface") == 0 && strncmp(b, "interface ", 10) == 0) |
| return b; |
| return nil; |
| } |
| |
| static void |
| loadpkgdata(char *file, char *data, int len) |
| { |
| char *p, *ep, *prefix, *name, *def, *ndef; |
| Import *x; |
| |
| file = strdup(file); |
| p = data; |
| ep = data + len; |
| while(parsepkgdata(file, &p, ep, &prefix, &name, &def) > 0) { |
| x = ilookup(name); |
| if(x->prefix == nil) { |
| x->prefix = prefix; |
| x->def = def; |
| x->file = file; |
| } else if(strcmp(x->prefix, prefix) != 0) { |
| fprint(2, "%s: conflicting definitions for %s\n", argv0, 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) { |
| // fine |
| } else if((ndef = forwardfix(x->def, def)) != nil) { |
| x->def = ndef; |
| } else { |
| fprint(2, "%d: conflicting definitions for %s\n", argv0, 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++; |
| } |
| } |
| } |
| |
| static int |
| parsepkgdata(char *file, char **pp, char *ep, 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; |
| |
| // 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, "%s: confused in pkg data near <<%.20s>>\n", argv0, 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, "%s: %s: expected methods to be indented %p %p %.10s\n", argv0, |
| 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, "%s: lost end of line in method definition\n", argv0); |
| *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; i<NIHASH; i++) |
| for(x=ihash[i]; x; x=x->hash) |
| if(strcmp(x->prefix, "type") == 0) |
| n++; |
| all = mal(n*sizeof all[0]); |
| j = 0; |
| for(i=0; i<NIHASH; i++) |
| for(x=ihash[i]; x; x=x->hash) |
| 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; i<n; i++) { |
| p = strchr(all[i]->def, '\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+=100) { |
| w = 100; |
| if(w > 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; i<NHASH; i++) |
| for(x = hash[i]; x; x=x->link) |
| if(memcmp(x->name, "sigt·", 6) == 0 && x->type != Sxxx) |
| n++; |
| all = mal(n*sizeof all[0]); |
| j = 0; |
| for(i=0; i<NHASH; i++) |
| for(x = hash[i]; x; x=x->link) |
| 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. |
| for(i=0; i<n; i++) { |
| prog = newdata(s, PtrSize*i, PtrSize, D_EXTERN); |
| prog->to.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(p->as == ATEXT) { |
| // keeping this function; link into textp list |
| if(etextp == P) |
| textp = p; |
| else |
| etextp->pcond = p; |
| etextp = p; |
| etextp->pcond = P; |
| } |
| 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; |
| } |
| |
| static char* |
| morename[] = |
| { |
| "sys·morestack", |
| "sys·morestackx", |
| |
| "sys·morestack00", |
| "sys·morestack10", |
| "sys·morestack01", |
| "sys·morestack11", |
| |
| "sys·morestack8", |
| "sys·morestack16", |
| "sys·morestack24", |
| "sys·morestack32", |
| "sys·morestack40", |
| "sys·morestack48", |
| }; |
| |
| void |
| deadcode(void) |
| { |
| int i; |
| |
| if(debug['v']) |
| Bprint(&bso, "%5.2f deadcode\n", cputime()); |
| |
| mark(lookup(INITENTRY, 0)); |
| for(i=0; i<nelem(morename); i++) |
| mark(lookup(morename[i], 0)); |
| |
| // remove dead code. |
| // sweeplist will rebuild the list of functions at textp |
| textp = P; |
| etextp = P; |
| |
| // follow is going to redo the firstp, lastp list |
| // but update it anyway just to keep things consistent. |
| sweeplist(&firstp, &lastp); |
| |
| // remove dead data |
| sweeplist(&datap, &edatap); |
| } |