Commit 3f61184e authored by Russ Cox's avatar Russ Cox

gc, ld: detect stale or incompatible object files

The object files begin with a header that is

        $GOARCH

on a line by itself.  This CL changes that header to

        go object $GOOS $GOARCH release.2011-01-01 4567+

where the final two fields are the most recent release
tag and the current hg version number.

All objects imported into a Go compilation or linked into an
executable must have the same header line, and that header
line must match the compiler and linker versions.

The effect of this will be that if you update and run all.bash
and then try to link in objects compiled with an earlier version
of the compiler (or invoke the wrong version of the compiler),
you will get an error showing the different headers instead
of perhaps silent incompatibility.

Normal usage with all.bash should be unaffected, because
all.bash deletes all the object files in $GOROOT/pkg/$GOOS_$GOARCH
and cleans all intermediate object files before starting.

This change is intended to diagnose stale objects arising when
users maintaining alternate installation directories forget to
rebuild some of their files after updating.

It should help make the adoption of $GOPATH (CL 3780043)
less error-prone.

R=ken2, r
CC=golang-dev
https://golang.org/cl/4023063
parent a6736cae
...@@ -187,7 +187,7 @@ assemble(char *file) ...@@ -187,7 +187,7 @@ assemble(char *file)
pass = 1; pass = 1;
pinit(file); pinit(file);
Bprint(&obuf, "%s\n", thestring); Bprint(&obuf, "go object %s %s %s\n", getgoos(), thestring, getgoversion());
for(i=0; i<nDlist; i++) for(i=0; i<nDlist; i++)
dodefine(Dlist[i]); dodefine(Dlist[i]);
......
...@@ -373,7 +373,7 @@ outcode(void) ...@@ -373,7 +373,7 @@ outcode(void)
} }
} }
Bprint(&outbuf, "%s\n", thestring); Bprint(&outbuf, "go object %s %s %s\n", getgoos(), thestring, getgoversion());
if(ndynimp > 0 || ndynexp > 0) { if(ndynimp > 0 || ndynexp > 0) {
int i; int i;
......
...@@ -189,7 +189,7 @@ assemble(char *file) ...@@ -189,7 +189,7 @@ assemble(char *file)
pass = 1; pass = 1;
pinit(file); pinit(file);
Bprint(&obuf, "%s\n", thestring); Bprint(&obuf, "go object %s %s %s\n", getgoos(), thestring, getgoversion());
for(i=0; i<nDlist; i++) for(i=0; i<nDlist; i++)
dodefine(Dlist[i]); dodefine(Dlist[i]);
......
...@@ -231,7 +231,7 @@ outcode(void) ...@@ -231,7 +231,7 @@ outcode(void)
} }
Binit(&b, f, OWRITE); Binit(&b, f, OWRITE);
Bprint(&b, "%s\n", thestring); Bprint(&b, "go object %s %s %s\n", getgoos(), thestring, getgoversion());
if(ndynimp > 0 || ndynexp > 0) { if(ndynimp > 0 || ndynexp > 0) {
int i; int i;
......
...@@ -189,7 +189,7 @@ assemble(char *file) ...@@ -189,7 +189,7 @@ assemble(char *file)
pass = 1; pass = 1;
pinit(file); pinit(file);
Bprint(&obuf, "%s\n", thestring); Bprint(&obuf, "go object %s %s %s\n", getgoos(), thestring, getgoversion());
for(i=0; i<nDlist; i++) for(i=0; i<nDlist; i++)
dodefine(Dlist[i]); dodefine(Dlist[i]);
......
...@@ -230,7 +230,7 @@ outcode(void) ...@@ -230,7 +230,7 @@ outcode(void)
} }
Binit(&b, f, OWRITE); Binit(&b, f, OWRITE);
Bprint(&b, "%s\n", thestring); Bprint(&b, "go object %s %s %s\n", getgoos(), thestring, getgoversion());
if(ndynimp > 0 || ndynexp > 0) { if(ndynimp > 0 || ndynexp > 0) {
int i; int i;
......
...@@ -54,7 +54,8 @@ widstruct(Type *t, uint32 o, int flag) ...@@ -54,7 +54,8 @@ widstruct(Type *t, uint32 o, int flag)
if(f->type->width < 0) if(f->type->width < 0)
fatal("invalid width %lld", f->type->width); fatal("invalid width %lld", f->type->width);
w = f->type->width; w = f->type->width;
o = rnd(o, f->type->align); if(f->type->align > 0)
o = rnd(o, f->type->align);
f->width = o; // really offset for TFIELD f->width = o; // really offset for TFIELD
if(f->nname != N) { if(f->nname != N) {
// this same stackparam logic is in addrescapes // this same stackparam logic is in addrescapes
......
...@@ -405,7 +405,7 @@ void ...@@ -405,7 +405,7 @@ void
importfile(Val *f, int line) importfile(Val *f, int line)
{ {
Biobuf *imp; Biobuf *imp;
char *file; char *file, *p, *q;
int32 c; int32 c;
int len; int len;
Strlit *path; Strlit *path;
...@@ -432,7 +432,7 @@ importfile(Val *f, int line) ...@@ -432,7 +432,7 @@ importfile(Val *f, int line)
cannedimports("unsafe.6", unsafeimport); cannedimports("unsafe.6", unsafeimport);
return; return;
} }
path = f->u.sval; path = f->u.sval;
if(islocalname(path)) { if(islocalname(path)) {
cleanbuf = mal(strlen(pathname) + strlen(path->s) + 2); cleanbuf = mal(strlen(pathname) + strlen(path->s) + 2);
...@@ -459,10 +459,25 @@ importfile(Val *f, int line) ...@@ -459,10 +459,25 @@ importfile(Val *f, int line)
len = strlen(namebuf); len = strlen(namebuf);
if(len > 2 && namebuf[len-2] == '.' && namebuf[len-1] == 'a') { if(len > 2 && namebuf[len-2] == '.' && namebuf[len-1] == 'a') {
if(!skiptopkgdef(imp)) { if(!skiptopkgdef(imp)) {
yyerror("import not package file: %s", namebuf); yyerror("import %s: not a package file", file);
errorexit(); errorexit();
} }
} }
// check object header
p = Brdstr(imp, '\n', 1);
if(strcmp(p, "empty archive") != 0) {
if(strncmp(p, "go object ", 10) != 0) {
yyerror("import %s: not a go object file", file);
errorexit();
}
q = smprint("%s %s %s", getgoos(), thestring, getgoversion());
if(strcmp(p+10, q) != 0) {
yyerror("import %s: object is [%s] expected [%s]", file, p+10, q);
errorexit();
}
free(q);
}
// assume files move (get installed) // assume files move (get installed)
// so don't record the full path. // so don't record the full path.
...@@ -479,6 +494,7 @@ importfile(Val *f, int line) ...@@ -479,6 +494,7 @@ importfile(Val *f, int line)
curio.infile = file; curio.infile = file;
curio.nlsemi = 0; curio.nlsemi = 0;
typecheckok = 1; typecheckok = 1;
for(;;) { for(;;) {
c = getc(); c = getc();
if(c == EOF) if(c == EOF)
......
...@@ -21,7 +21,7 @@ dumpobj(void) ...@@ -21,7 +21,7 @@ dumpobj(void)
errorexit(); errorexit();
} }
Bprint(bout, "%s\n", thestring); Bprint(bout, "go object %s %s %s\n", getgoos(), thestring, getgoversion());
Bprint(bout, " exports automatically generated from\n"); Bprint(bout, " exports automatically generated from\n");
Bprint(bout, " %s in package \"%s\"\n", curio.infile, localpkg->name); Bprint(bout, " %s in package \"%s\"\n", curio.infile, localpkg->name);
dumpexport(); dumpexport();
......
...@@ -203,6 +203,7 @@ fatal(char *fmt, ...) ...@@ -203,6 +203,7 @@ fatal(char *fmt, ...)
flusherrors(); flusherrors();
*(int*)0=0;
print("%L: internal compiler error: ", lineno); print("%L: internal compiler error: ", lineno);
va_start(arg, fmt); va_start(arg, fmt);
vfprint(1, fmt, arg); vfprint(1, fmt, arg);
...@@ -213,7 +214,7 @@ fatal(char *fmt, ...) ...@@ -213,7 +214,7 @@ fatal(char *fmt, ...)
if(strncmp(getgoversion(), "release", 7) == 0) { if(strncmp(getgoversion(), "release", 7) == 0) {
print("\n"); print("\n");
print("Please file a bug report including a short program that triggers the error.\n"); print("Please file a bug report including a short program that triggers the error.\n");
print("http://code.google.com/p/go/issues/entry?template=compilerbug"); print("http://code.google.com/p/go/issues/entry?template=compilerbug\n");
} }
hcrash(); hcrash();
errorexit(); errorexit();
......
...@@ -131,6 +131,7 @@ Arfile *astart, *amiddle, *aend; /* Temp file control block pointers */ ...@@ -131,6 +131,7 @@ Arfile *astart, *amiddle, *aend; /* Temp file control block pointers */
int allobj = 1; /* set when all members are object files of the same type */ int allobj = 1; /* set when all members are object files of the same type */
int symdefsize; /* size of symdef file */ int symdefsize; /* size of symdef file */
char *pkgstmt; /* string "package foo" */ char *pkgstmt; /* string "package foo" */
char *objhdr; /* string "go object darwin 386 release.2010-01-01 2345+" */
int dupfound; /* flag for duplicate symbol */ int dupfound; /* flag for duplicate symbol */
Hashchain *hash[NHASH]; /* hash table of text symbols */ Hashchain *hash[NHASH]; /* hash table of text symbols */
...@@ -246,6 +247,8 @@ main(int argc, char *argv[]) ...@@ -246,6 +247,8 @@ main(int argc, char *argv[])
argc -= 3; argc -= 3;
argv += 3; argv += 3;
(*comfun)(cp, argc, argv); /* do the command */ (*comfun)(cp, argc, argv); /* do the command */
if(errors && cflag)
remove(cp);
cp = 0; cp = 0;
while (argc--) { while (argc--) {
if (*argv) { if (*argv) {
...@@ -590,10 +593,11 @@ void ...@@ -590,10 +593,11 @@ void
scanobj(Biobuf *b, Arfile *ap, long size) scanobj(Biobuf *b, Arfile *ap, long size)
{ {
int obj; int obj;
vlong offset; vlong offset, offset1;
Dir *d; Dir *d;
static int lastobj = -1; static int lastobj = -1;
uchar buf[4]; uchar buf[4];
char *p, *t;
if (!allobj) /* non-object file encountered */ if (!allobj) /* non-object file encountered */
return; return;
...@@ -628,14 +632,32 @@ scanobj(Biobuf *b, Arfile *ap, long size) ...@@ -628,14 +632,32 @@ scanobj(Biobuf *b, Arfile *ap, long size)
Bseek(b, offset, 0); Bseek(b, offset, 0);
return; return;
} }
if (lastobj >= 0 && obj != lastobj) {
offset1 = Boffset(b);
Bseek(b, offset, 0);
p = Brdstr(b, '\n', 1);
Bseek(b, offset1, 0);
if(p == nil || strncmp(p, "go object ", 10) != 0) {
fprint(2, "gopack: malformed object file %s\n", file);
errors++;
Bseek(b, offset, 0);
free(p);
return;
}
if ((lastobj >= 0 && obj != lastobj) || (objhdr != nil && strcmp(p, objhdr) != 0)) {
fprint(2, "gopack: inconsistent object file %s\n", file); fprint(2, "gopack: inconsistent object file %s\n", file);
errors++; errors++;
allobj = 0; allobj = 0;
Bseek(b, offset, 0); free(p);
return; return;
} }
lastobj = obj; lastobj = obj;
if(objhdr == nil)
objhdr = p;
else
free(p);
if (!readar(b, obj, offset+size, 0)) { if (!readar(b, obj, offset+size, 0)) {
fprint(2, "gopack: invalid symbol reference in file %s\n", file); fprint(2, "gopack: invalid symbol reference in file %s\n", file);
errors++; errors++;
...@@ -677,7 +699,7 @@ char* importblock; ...@@ -677,7 +699,7 @@ char* importblock;
void void
getpkgdef(char **datap, int *lenp) getpkgdef(char **datap, int *lenp)
{ {
char *tag; char *tag, *hdr;
if(pkgname == nil) { if(pkgname == nil) {
pkgname = "__emptyarchive__"; pkgname = "__emptyarchive__";
...@@ -688,7 +710,11 @@ getpkgdef(char **datap, int *lenp) ...@@ -688,7 +710,11 @@ getpkgdef(char **datap, int *lenp)
if(safe || Sflag) if(safe || Sflag)
tag = "safe"; tag = "safe";
*datap = smprint("import\n$$\npackage %s %s\n%s\n$$\n", pkgname, tag, importblock); hdr = "empty archive";
if(objhdr != nil)
hdr = objhdr;
*datap = smprint("%s\nimport\n$$\npackage %s %s\n%s\n$$\n", hdr, pkgname, tag, importblock);
*lenp = strlen(*datap); *lenp = strlen(*datap);
} }
......
...@@ -378,10 +378,10 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) ...@@ -378,10 +378,10 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence)
int n, c1, c2, c3, c4; int n, c1, c2, c3, c4;
uint32 magic; uint32 magic;
vlong import0, import1, eof; vlong import0, import1, eof;
char src[1024]; char *fld[10], *s, *t;
int nfld;
eof = Boffset(f) + len; eof = Boffset(f) + len;
src[0] = '\0';
pn = strdup(pn); pn = strdup(pn);
...@@ -415,22 +415,34 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) ...@@ -415,22 +415,34 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence)
line = Brdline(f, '\n'); line = Brdline(f, '\n');
if(line == nil) { if(line == nil) {
if(Blinelen(f) > 0) { if(Blinelen(f) > 0) {
diag("%s: malformed object file", pn); diag("%s: not an object file", pn);
return; return;
} }
goto eof; goto eof;
} }
n = Blinelen(f) - 1; n = Blinelen(f) - 1;
if(n != strlen(thestring) || strncmp(line, thestring, n) != 0) { line[n] = '\0';
if(line) if(strncmp(line, "go object ", 10) != 0) {
line[n] = '\0';
if(strlen(pn) > 3 && strcmp(pn+strlen(pn)-3, ".go") == 0) { if(strlen(pn) > 3 && strcmp(pn+strlen(pn)-3, ".go") == 0) {
print("%cl: input %s is not .%c file (use %cg to compile .go files)\n", thechar, pn, thechar, thechar); print("%cl: input %s is not .%c file (use %cg to compile .go files)\n", thechar, pn, thechar, thechar);
errorexit(); errorexit();
} }
diag("file not %s [%s]\n", thestring, line); if(strcmp(line, thestring) == 0) {
// old header format: just $GOOS
diag("%s: stale object file", pn);
return;
}
diag("%s: not an object file", pn);
return;
}
t = smprint("%s %s %s", getgoos(), thestring, getgoversion());
if(strcmp(line+10, t) != 0) {
diag("%s: object is [%s] expected [%s]", pn, line+10, t);
free(t);
return; return;
} }
free(t);
line[n] = '\n';
/* skip over exports and other info -- ends with \n!\n */ /* skip over exports and other info -- ends with \n!\n */
import0 = Boffset(f); import0 = Boffset(f);
......
...@@ -116,34 +116,24 @@ objtype(Biobuf *bp, char **name) ...@@ -116,34 +116,24 @@ objtype(Biobuf *bp, char **name)
int i; int i;
char buf[MAXIS]; char buf[MAXIS];
int c; int c;
char *p;
Retry:
if(Bread(bp, buf, MAXIS) < MAXIS)
return -1;
Bseek(bp, -MAXIS, 1);
for (i = 0; i < Maxobjtype; i++) {
if (obj[i].is && (*obj[i].is)(buf)) {
if (name)
*name = obj[i].name;
return i;
}
}
/* /*
* Maybe there's an import block we need to skip * Look for import block.
*/ */
for(i = 0; i < MAXIS; i++) { p = Brdline(bp, '\n');
if(isalpha(buf[i]) || isdigit(buf[i])) if(p == nil)
continue; return -1;
if(i == 0 || buf[i] != '\n') if(Blinelen(bp) < 10 || strncmp(p, "go object ", 10) != 0)
return -1; return -1;
break; Bseek(bp, -1, 1);
}
/* /*
* Found one. Skip until "\n!\n" * Found one. Skip until "\n!\n"
*/ */
while((c = Bgetc(bp)) != Beof) { for(;;) {
if((c = Bgetc(bp)) == Beof)
return -1;
if(c != '\n') if(c != '\n')
continue; continue;
c = Bgetc(bp); c = Bgetc(bp);
...@@ -156,8 +146,20 @@ Retry: ...@@ -156,8 +146,20 @@ Retry:
Bungetc(bp); Bungetc(bp);
continue; continue;
} }
goto Retry; break;
} }
if(Bread(bp, buf, MAXIS) < MAXIS)
return -1;
Bseek(bp, -MAXIS, 1);
for (i = 0; i < Maxobjtype; i++) {
if (obj[i].is && (*obj[i].is)(buf)) {
if (name)
*name = obj[i].name;
return i;
}
}
return -1; return -1;
} }
......
...@@ -11,17 +11,16 @@ fi ...@@ -11,17 +11,16 @@ fi
# Get numerical revision # Get numerical revision
VERSION=$(hg identify -n 2>/dev/null) VERSION=$(hg identify -n 2>/dev/null)
if [ $? = 0 ]; then if [ $? != 0 ]; then
TAG=$(hg identify -t | sed 's!/release!!')
else
OLD=$(hg identify | sed 1q) OLD=$(hg identify | sed 1q)
VERSION=$(echo $OLD | awk '{print $1}') VERSION=$(echo $OLD | awk '{print $1}')
TAG=$(echo $OLD | awk '{print $2}' | sed 's!/release!!')
fi fi
# Append tag if not 'tip' # Find most recent known release tag.
if [[ "$TAG" != "tip" ]]; then TAG=$(hg tags | awk '$1~/^release\./ {print $1}' | sed -n 1p)
VERSION="$VERSION $TAG"
if [ "$TAG" != "" ]; then
VERSION="$TAG $VERSION"
fi fi
echo $VERSION echo $VERSION
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment