1
0
mirror of https://github.com/golang/go synced 2024-10-04 19:21:21 -06:00
go/src/cmd/ld/go.c
Russ Cox b87e3e8b7f * move go-specific loader code
into gc directory, where it gets included as ../gc/ldbody
this is similar to the assemblers including ../cc/lexbody
and ../cc/macbody.

* hook go-specific loader code into 8l.

* make current 8.out.h and 6.out.h backward compatible
with plan 9's versions.  i had added some constants in
the middle of enums and have now moved them to the end.
this keeps us from invalidating old .8 and .6 files.
not sure how much it really matters, but easy to do.

R=r
DELTA=1314  (667 added, 623 deleted, 24 changed)
OCL=26938
CL=26941
2009-03-31 00:20:07 -07:00

588 lines
12 KiB
C

// 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; 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.
enum { PtrSize = 8 };
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(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);
}