2009-03-31 01:20:07 -06:00
|
|
|
// 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.
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
// 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);
|
2009-06-04 14:33:29 -06:00
|
|
|
x->name = strdup(name);
|
2009-03-31 01:20:07 -06:00
|
|
|
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**);
|
2009-06-29 16:13:37 -06:00
|
|
|
static int parsepkgdata(char*, char**, char*, char**, char**, char**);
|
2009-03-31 01:20:07 -06:00
|
|
|
|
|
|
|
void
|
|
|
|
ldpkg(Biobuf *f, int64 len, char *filename)
|
|
|
|
{
|
|
|
|
char *data, *p0, *p1;
|
|
|
|
|
|
|
|
if(debug['g'])
|
|
|
|
return;
|
|
|
|
|
|
|
|
if((int)len != len) {
|
2009-06-05 11:59:55 -06:00
|
|
|
fprint(2, "%s: too much pkg data in %s\n", argv0, filename);
|
2009-03-31 01:20:07 -06:00
|
|
|
return;
|
|
|
|
}
|
2009-05-05 13:43:00 -06:00
|
|
|
data = mal(len+1);
|
2009-03-31 01:20:07 -06:00
|
|
|
if(Bread(f, data, len) != len) {
|
2009-06-05 11:59:55 -06:00
|
|
|
fprint(2, "%s: short pkg read %s\n", argv0, filename);
|
2009-03-31 01:20:07 -06:00
|
|
|
return;
|
|
|
|
}
|
2009-05-05 13:43:00 -06:00
|
|
|
data[len] = '\0';
|
2009-03-31 01:20:07 -06:00
|
|
|
|
|
|
|
// 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) {
|
2009-06-05 11:59:55 -06:00
|
|
|
fprint(2, "%s: cannot find end of exports in %s\n", argv0, filename);
|
2009-03-31 01:20:07 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
while(*p0 == ' ' || *p0 == '\t' || *p0 == '\n')
|
|
|
|
p0++;
|
|
|
|
if(strncmp(p0, "package ", 8) != 0) {
|
2009-06-05 11:59:55 -06:00
|
|
|
fprint(2, "%s: bad package section in %s\n", argv0, filename);
|
2009-03-31 01:20:07 -06:00
|
|
|
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) {
|
2009-06-05 11:59:55 -06:00
|
|
|
fprint(2, "%s: cannot find end of local types in %s\n", argv0, filename);
|
2009-03-31 01:20:07 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
loadpkgdata(filename, p0, p1 - p0);
|
|
|
|
}
|
|
|
|
|
2009-06-29 16:13:37 -06:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2009-03-31 01:20:07 -06:00
|
|
|
static void
|
|
|
|
loadpkgdata(char *file, char *data, int len)
|
|
|
|
{
|
2009-06-29 16:13:37 -06:00
|
|
|
char *p, *ep, *prefix, *name, *def, *ndef;
|
2009-03-31 01:20:07 -06:00
|
|
|
Import *x;
|
|
|
|
|
|
|
|
file = strdup(file);
|
|
|
|
p = data;
|
|
|
|
ep = data + len;
|
2009-06-29 16:13:37 -06:00
|
|
|
while(parsepkgdata(file, &p, ep, &prefix, &name, &def) > 0) {
|
2009-03-31 01:20:07 -06:00
|
|
|
x = ilookup(name);
|
|
|
|
if(x->prefix == nil) {
|
|
|
|
x->prefix = prefix;
|
|
|
|
x->def = def;
|
|
|
|
x->file = file;
|
2009-06-29 16:13:37 -06:00
|
|
|
} 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;
|
2009-03-31 01:20:07 -06:00
|
|
|
} else {
|
2009-07-07 12:48:22 -06:00
|
|
|
fprint(2, "%s: conflicting definitions for %s\n", argv0, name);
|
2009-06-29 16:13:37 -06:00
|
|
|
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++;
|
2009-03-31 01:20:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-06-29 16:13:37 -06:00
|
|
|
parsepkgdata(char *file, char **pp, char *ep, char **prefixp, char **namep, char **defp)
|
2009-03-31 01:20:07 -06:00
|
|
|
{
|
|
|
|
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{
|
2009-06-05 11:59:55 -06:00
|
|
|
fprint(2, "%s: confused in pkg data near <<%.20s>>\n", argv0, p);
|
2009-03-31 01:20:07 -06:00
|
|
|
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.
|
2009-06-05 11:59:55 -06:00
|
|
|
fprint(2, "%s: %s: expected methods to be indented %p %p %.10s\n", argv0,
|
2009-03-31 01:20:07 -06:00
|
|
|
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) {
|
2009-06-05 11:59:55 -06:00
|
|
|
fprint(2, "%s: lost end of line in method definition\n", argv0);
|
2009-03-31 01:20:07 -06:00
|
|
|
*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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2009-05-05 13:43:00 -06:00
|
|
|
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;
|
2009-03-31 01:20:07 -06:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-05-03 20:09:14 -06:00
|
|
|
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",
|
|
|
|
};
|
|
|
|
|
2009-03-31 01:20:07 -06:00
|
|
|
void
|
|
|
|
deadcode(void)
|
|
|
|
{
|
2009-05-03 20:09:14 -06:00
|
|
|
int i;
|
|
|
|
|
2009-03-31 01:20:07 -06:00
|
|
|
if(debug['v'])
|
|
|
|
Bprint(&bso, "%5.2f deadcode\n", cputime());
|
|
|
|
|
|
|
|
mark(lookup(INITENTRY, 0));
|
2009-05-03 20:09:14 -06:00
|
|
|
for(i=0; i<nelem(morename); i++)
|
|
|
|
mark(lookup(morename[i], 0));
|
2009-03-31 01:20:07 -06:00
|
|
|
|
2009-05-05 13:43:00 -06:00
|
|
|
// 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.
|
2009-04-02 18:56:23 -06:00
|
|
|
sweeplist(&firstp, &lastp);
|
2009-05-05 13:43:00 -06:00
|
|
|
|
|
|
|
// remove dead data
|
2009-04-02 18:56:23 -06:00
|
|
|
sweeplist(&datap, &edatap);
|
2009-03-31 01:20:07 -06:00
|
|
|
}
|