diff options
Diffstat (limited to 'src/cmd/pack')
-rw-r--r-- | src/cmd/pack/Makefile | 12 | ||||
-rw-r--r-- | src/cmd/pack/ar.c | 1766 | ||||
-rw-r--r-- | src/cmd/pack/doc.go | 26 |
3 files changed, 1804 insertions, 0 deletions
diff --git a/src/cmd/pack/Makefile b/src/cmd/pack/Makefile new file mode 100644 index 0000000000..12909727fc --- /dev/null +++ b/src/cmd/pack/Makefile @@ -0,0 +1,12 @@ +# 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. + +include ../../Make.inc +O:=$(HOST_O) + +TARG=pack +OFILES=\ + ar.$O\ + +include ../../Make.ctool diff --git a/src/cmd/pack/ar.c b/src/cmd/pack/ar.c new file mode 100644 index 0000000000..40c99f6c7c --- /dev/null +++ b/src/cmd/pack/ar.c @@ -0,0 +1,1766 @@ +// Inferno utils/iar/ar.c +// http://code.google.com/p/inferno-os/source/browse/utils/iar/ar.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +/* + * ar - portable (ascii) format version + */ + +/* protect a couple of our names */ +#define select your_select +#define rcmd your_rcmd + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <mach.h> +#include "../../libmach/obj.h" +#include <ar.h> + +#undef select +#undef rcmd + +/* + * The algorithm uses up to 3 temp files. The "pivot member" is the + * archive member specified by and a, b, or i option. The temp files are + * astart - contains existing members up to and including the pivot member. + * amiddle - contains new files moved or inserted behind the pivot. + * aend - contains the existing members that follow the pivot member. + * When all members have been processed, function 'install' streams the + * temp files, in order, back into the archive. + */ + +typedef struct Arsymref +{ + char *name; + char *file; + int type; + int len; + vlong offset; + struct Arsymref *next; +} Arsymref; + +typedef struct Armember /* Temp file entry - one per archive member */ +{ + struct Armember *next; + struct ar_hdr hdr; + long size; + long date; + void *member; +} Armember; + +typedef struct Arfile /* Temp file control block - one per tempfile */ +{ + int paged; /* set when some data paged to disk */ + char *fname; /* paging file name */ + int fd; /* paging file descriptor */ + vlong size; + Armember *head; /* head of member chain */ + Armember *tail; /* tail of member chain */ + Arsymref *sym; /* head of defined symbol chain */ +} Arfile; + +typedef struct Hashchain +{ + char *name; + char *file; + struct Hashchain *next; +} Hashchain; + +#define NHASH 1024 + +/* + * macro to portably read/write archive header. + * 'cmd' is read/write/Bread/Bwrite, etc. + */ +#define HEADER_IO(cmd, f, h) cmd(f, h.name, sizeof(h.name)) != sizeof(h.name)\ + || cmd(f, h.date, sizeof(h.date)) != sizeof(h.date)\ + || cmd(f, h.uid, sizeof(h.uid)) != sizeof(h.uid)\ + || cmd(f, h.gid, sizeof(h.gid)) != sizeof(h.gid)\ + || cmd(f, h.mode, sizeof(h.mode)) != sizeof(h.mode)\ + || cmd(f, h.size, sizeof(h.size)) != sizeof(h.size)\ + || cmd(f, h.fmag, sizeof(h.fmag)) != sizeof(h.fmag) + + /* constants and flags */ +char *man = "mrxtdpq"; +char *opt = "uvnbailogS"; +char artemp[] = "/tmp/vXXXXX"; +char movtemp[] = "/tmp/v1XXXXX"; +char tailtemp[] = "/tmp/v2XXXXX"; +char symdef[] = "__.SYMDEF"; +char pkgdef[] = "__.PKGDEF"; + +int aflag; /* command line flags */ +int bflag; +int cflag; +int gflag; +int oflag; +int uflag; +int vflag; +int Pflag; /* remove leading file prefix */ +int Sflag; /* force mark Go package as safe */ + +int errors; + +Arfile *astart, *amiddle, *aend; /* Temp file control block pointers */ +int allobj = 1; /* set when all members are object files of the same type */ +int symdefsize; /* size of symdef file */ +char *pkgstmt; /* string "package foo" */ +char *objhdr; /* string "go object darwin 386 release.2010-01-01 2345+" */ +int dupfound; /* flag for duplicate symbol */ +Hashchain *hash[NHASH]; /* hash table of text symbols */ + +#define ARNAMESIZE sizeof(astart->tail->hdr.name) + +char poname[ARNAMESIZE+1]; /* name of pivot member */ +char *file; /* current file or member being worked on */ +Biobuf bout; +Biobuf bar; +char *prefix; +int pkgdefsafe; /* was __.PKGDEF marked safe? */ + +void arcopy(Biobuf*, Arfile*, Armember*); +int arcreate(char*); +void arfree(Arfile*); +void arinsert(Arfile*, Armember*); +void *armalloc(int); +char *arstrdup(char*); +void armove(Biobuf*, Arfile*, Armember*); +void arread(Biobuf*, Armember*); +void arstream(int, Arfile*); +int arwrite(int, Armember*); +int bamatch(char*, char*); +int duplicate(char*, char**); +Armember *getdir(Biobuf*); +void getpkgdef(char**, int*); +int getspace(void); +void install(char*, Arfile*, Arfile*, Arfile*, int); +void loadpkgdata(char*, int); +void longt(Armember*); +int match(int, char**); +void mesg(int, char*); +Arfile *newtempfile(char*); +Armember *newmember(void); +void objsym(Sym*, void*); +int openar(char*, int, int); +int page(Arfile*); +void pmode(long); +void rl(int); +void scanobj(Biobuf*, Arfile*, long); +void scanpkg(Biobuf*, long); +void select(int*, long); +void setcom(void(*)(char*, int, char**)); +void skip(Biobuf*, vlong); +void checksafe(Biobuf*, vlong); +int symcomp(void*, void*); +void trim(char*, char*, int); +void usage(void); +void wrerr(void); +void wrsym(Biobuf*, long, Arsymref*); +int arread_cutprefix(Biobuf*, Armember*); + +void rcmd(char*, int, char**); /* command processing */ +void dcmd(char*, int, char**); +void xcmd(char*, int, char**); +void tcmd(char*, int, char**); +void pcmd(char*, int, char**); +void mcmd(char*, int, char**); +void qcmd(char*, int, char**); +void (*comfun)(char*, int, char**); + +void +main(int argc, char *argv[]) +{ + char *cp; + + Binit(&bout, 1, OWRITE); + if(argc < 3) + usage(); + for (cp = argv[1]; *cp; cp++) { + switch(*cp) { + case 'a': aflag = 1; break; + case 'b': bflag = 1; break; + case 'c': cflag = 1; break; + case 'd': setcom(dcmd); break; + case 'g': gflag = 1; break; + case 'i': bflag = 1; break; + case 'l': + strcpy(artemp, "vXXXXX"); + strcpy(movtemp, "v1XXXXX"); + strcpy(tailtemp, "v2XXXXX"); + break; + case 'm': setcom(mcmd); break; + case 'o': oflag = 1; break; + case 'p': setcom(pcmd); break; + case 'q': setcom(qcmd); break; + case 'r': setcom(rcmd); break; + case 't': setcom(tcmd); break; + case 'u': uflag = 1; break; + case 'v': vflag = 1; break; + case 'x': setcom(xcmd); break; + case 'S': Sflag = 1; break; + case 'P': Pflag = 1; break; + default: + fprint(2, "gopack: bad option `%c'\n", *cp); + exits("error"); + } + } + if (aflag && bflag) { + fprint(2, "gopack: only one of 'a' and 'b' can be specified\n"); + usage(); + } + if(aflag || bflag) { + trim(argv[2], poname, sizeof(poname)); + argv++; + argc--; + if(argc < 3) + usage(); + } + if(Pflag) { + if(argc < 4) { + fprint(2, "gopack: P flag requires prefix argument\n"); + usage(); + } + prefix = argv[2]; + argv++; + argc--; + } + if(comfun == 0) { + if(uflag == 0) { + fprint(2, "gopack: one of [%s] must be specified\n", man); + usage(); + } + setcom(rcmd); + } + cp = argv[2]; + argc -= 3; + argv += 3; + (*comfun)(cp, argc, argv); /* do the command */ + if(errors && cflag) + remove(cp); + cp = 0; + while (argc--) { + if (*argv) { + fprint(2, "gopack: %s not found\n", *argv); + cp = "error"; + } + argv++; + } + if (errors) + cp = "error"; + exits(cp); +} +/* + * select a command + */ +void +setcom(void (*fun)(char *, int, char**)) +{ + + if(comfun != 0) { + fprint(2, "gopack: only one of [%s] allowed\n", man); + usage(); + } + comfun = fun; +} +/* + * perform the 'r' and 'u' commands + */ +void +rcmd(char *arname, int count, char **files) +{ + int fd; + int i; + Arfile *ap; + Armember *bp; + Dir *d; + Biobuf *bfile; + + fd = openar(arname, ORDWR, 1); + if (fd >= 0) { + Binit(&bar, fd, OREAD); + Bseek(&bar,seek(fd,0,1), 1); + } + astart = newtempfile(artemp); + ap = astart; + aend = 0; + for(i = 0; fd >= 0; i++) { + bp = getdir(&bar); + if (!bp) + break; + if (bamatch(file, poname)) { /* check for pivot */ + aend = newtempfile(tailtemp); + ap = aend; + } + /* pitch symdef file */ + if (i == 0 && strcmp(file, symdef) == 0) { + skip(&bar, bp->size); + continue; + } + /* pitch pkgdef file but remember whether it was marked safe */ + if (gflag && strcmp(file, pkgdef) == 0) { + checksafe(&bar, bp->size); + continue; + } + /* + * the plan 9 ar treats count == 0 as equivalent + * to listing all the archive's files on the command line: + * it will try to open every file name in the archive + * and copy that file into the archive if it exists. + * for go we disable that behavior, because we use + * r with no files to make changes to the archive itself, + * using the S or P flags. + */ + if (!match(count, files)) { + scanobj(&bar, ap, bp->size); + arcopy(&bar, ap, bp); + continue; + } + bfile = Bopen(file, OREAD); + if (!bfile) { + if (count != 0) { + fprint(2, "gopack: cannot open %s\n", file); + errors++; + } + scanobj(&bar, ap, bp->size); + arcopy(&bar, ap, bp); + continue; + } + d = dirfstat(Bfildes(bfile)); + if(d == nil) + fprint(2, "gopack: cannot stat %s: %r\n", file); + if (uflag && (d==nil || d->mtime <= bp->date)) { + scanobj(&bar, ap, bp->size); + arcopy(&bar, ap, bp); + Bterm(bfile); + free(d); + continue; + } + mesg('r', file); + skip(&bar, bp->size); + scanobj(bfile, ap, d->length); + free(d); + armove(bfile, ap, bp); + Bterm(bfile); + } + if(fd >= 0) + close(fd); + /* copy in remaining files named on command line */ + for (i = 0; i < count; i++) { + file = files[i]; + if(file == 0) + continue; + files[i] = 0; + bfile = Bopen(file, OREAD); + if (!bfile) { + fprint(2, "gopack: cannot open %s\n", file); + errors++; + } else { + mesg('a', file); + d = dirfstat(Bfildes(bfile)); + if (d == nil) + fprint(2, "can't stat %s\n", file); + else { + scanobj(bfile, astart, d->length); + armove(bfile, astart, newmember()); + free(d); + } + Bterm(bfile); + } + } + if(fd < 0 && !cflag) + install(arname, astart, 0, aend, 1); /* issue 'creating' msg */ + else + install(arname, astart, 0, aend, 0); +} + +void +dcmd(char *arname, int count, char **files) +{ + Armember *bp; + int fd, i; + + if (!count) + return; + fd = openar(arname, ORDWR, 0); + Binit(&bar, fd, OREAD); + Bseek(&bar,seek(fd,0,1), 1); + astart = newtempfile(artemp); + for (i = 0; bp = getdir(&bar); i++) { + if(match(count, files)) { + mesg('d', file); + skip(&bar, bp->size); + if (strcmp(file, symdef) == 0) + allobj = 0; + } else if (i == 0 && strcmp(file, symdef) == 0) { + skip(&bar, bp->size); + } else if (gflag && strcmp(file, pkgdef) == 0) { + skip(&bar, bp->size); + } else { + scanobj(&bar, astart, bp->size); + arcopy(&bar, astart, bp); + } + } + close(fd); + install(arname, astart, 0, 0, 0); +} + +void +xcmd(char *arname, int count, char **files) +{ + int fd, f, mode, i; + Armember *bp; + Dir dx; + + fd = openar(arname, OREAD, 0); + Binit(&bar, fd, OREAD); + Bseek(&bar,seek(fd,0,1), 1); + i = 0; + while (bp = getdir(&bar)) { + if(count == 0 || match(count, files)) { + mode = strtoul(bp->hdr.mode, 0, 8) & 0777; + f = create(file, OWRITE, mode); + if(f < 0) { + fprint(2, "gopack: %s cannot create\n", file); + skip(&bar, bp->size); + } else { + mesg('x', file); + arcopy(&bar, 0, bp); + if (write(f, bp->member, bp->size) < 0) + wrerr(); + if(oflag && bp->date != 0) { + nulldir(&dx); + dx.atime = bp->date; + dx.mtime = bp->date; + if(dirwstat(file, &dx) < 0) + perror(file); + } + free(bp->member); + close(f); + } + free(bp); + if (count && ++i >= count) + break; + } else { + skip(&bar, bp->size); + free(bp); + } + } + close(fd); +} +void +pcmd(char *arname, int count, char **files) +{ + int fd; + Armember *bp; + + fd = openar(arname, OREAD, 0); + Binit(&bar, fd, OREAD); + Bseek(&bar,seek(fd,0,1), 1); + while(bp = getdir(&bar)) { + if(count == 0 || match(count, files)) { + if(vflag) + print("\n<%s>\n\n", file); + arcopy(&bar, 0, bp); + if (write(1, bp->member, bp->size) < 0) + wrerr(); + } else + skip(&bar, bp->size); + free(bp); + } + close(fd); +} +void +mcmd(char *arname, int count, char **files) +{ + int fd, i; + Arfile *ap; + Armember *bp; + + if (count == 0) + return; + fd = openar(arname, ORDWR, 0); + Binit(&bar, fd, OREAD); + Bseek(&bar,seek(fd,0,1), 1); + astart = newtempfile(artemp); + amiddle = newtempfile(movtemp); + aend = 0; + ap = astart; + for (i = 0; bp = getdir(&bar); i++) { + if (bamatch(file, poname)) { + aend = newtempfile(tailtemp); + ap = aend; + } + if(match(count, files)) { + mesg('m', file); + scanobj(&bar, amiddle, bp->size); + arcopy(&bar, amiddle, bp); + } else if (ap == astart && i == 0 && strcmp(file, symdef) == 0) { + /* + * pitch the symdef file if it is at the beginning + * of the archive and we aren't inserting in front + * of it (ap == astart). + */ + skip(&bar, bp->size); + } else if (ap == astart && gflag && strcmp(file, pkgdef) == 0) { + /* + * pitch the pkgdef file if we aren't inserting in front + * of it (ap == astart). + */ + skip(&bar, bp->size); + } else { + scanobj(&bar, ap, bp->size); + arcopy(&bar, ap, bp); + } + } + close(fd); + if (poname[0] && aend == 0) + fprint(2, "gopack: %s not found - files moved to end.\n", poname); + install(arname, astart, amiddle, aend, 0); +} +void +tcmd(char *arname, int count, char **files) +{ + int fd; + Armember *bp; + char name[ARNAMESIZE+1]; + + fd = openar(arname, OREAD, 0); + Binit(&bar, fd, OREAD); + Bseek(&bar,seek(fd,0,1), 1); + while(bp = getdir(&bar)) { + if(count == 0 || match(count, files)) { + if(vflag) + longt(bp); + trim(file, name, ARNAMESIZE); + Bprint(&bout, "%s\n", name); + } + skip(&bar, bp->size); + free(bp); + } + close(fd); +} +void +qcmd(char *arname, int count, char **files) +{ + int fd, i; + Armember *bp; + Biobuf *bfile; + + if(aflag || bflag) { + fprint(2, "gopack: abi not allowed with q\n"); + exits("error"); + } + fd = openar(arname, ORDWR, 1); + if (fd < 0) { + if(!cflag) + fprint(2, "gopack: creating %s\n", arname); + fd = arcreate(arname); + } + Binit(&bar, fd, OREAD); + Bseek(&bar,seek(fd,0,1), 1); + /* leave note group behind when writing archive; i.e. sidestep interrupts */ + rfork(RFNOTEG); + Bseek(&bar, 0, 2); + bp = newmember(); + for(i=0; i<count && files[i]; i++) { + file = files[i]; + files[i] = 0; + bfile = Bopen(file, OREAD); + if(!bfile) { + fprint(2, "gopack: cannot open %s\n", file); + errors++; + } else { + mesg('q', file); + armove(bfile, 0, bp); + if (!arwrite(fd, bp)) + wrerr(); + free(bp->member); + bp->member = 0; + Bterm(bfile); + } + } + free(bp); + close(fd); +} + +/* + * does the object header line p match the last one we saw? + * update *lastp if it gets more specific. + */ +int +matchhdr(char *p, char **lastp) +{ + int n; + char *last; + + // no information? + last = *lastp; + if(last == nil) { + *lastp = strdup(p); + return 1; + } + + // identical match? + if(strcmp(last, p) == 0) + return 1; + + // last has extra fields + n = strlen(p); + if(n < strlen(last) && last[n] == ' ') + return 1; + + // p has extra fields - save in last + n = strlen(last); + if(n < strlen(p) && p[n] == ' ') { + free(last); + *lastp = strdup(p); + return 1; + } + + return 0; +} + +/* + * extract the symbol references from an object file + */ +void +scanobj(Biobuf *b, Arfile *ap, long size) +{ + int obj, goobject; + vlong offset, offset1; + Dir *d; + static int lastobj = -1; + uchar buf[4]; + char *p; + + if (!allobj) /* non-object file encountered */ + return; + offset = Boffset(b); + obj = objtype(b, 0); + if (obj < 0) { /* not an object file */ + /* maybe a foreign object file */ + Bseek(b, offset, 0); + memset(buf, 0, sizeof buf); + Bread(b, buf, 4); + + /* maybe a foreign object file? that's okay */ + if((buf[0] == 0x7F && buf[1] == 'E' && buf[2] == 'L' && buf[3] == 'F') || // ELF + (buf[0] == 0x4c && buf[1] == 0x01 || buf[0] == 0x64 && buf[1] == 0x86) || // Windows PE + (buf[0] == 0xFE && buf[1] == 0xED && buf[2] == 0xFA && (buf[3]&~1) == 0xCE) || // Mach-O big-endian + (buf[3] == 0xFE && buf[2] == 0xED && buf[1] == 0xFA && (buf[0]&~1) == 0xCE)) { // Mach-O little-endian + Bseek(b, offset, 0); + return; + } + + if (!gflag || strcmp(file, pkgdef) != 0) { /* don't clear allobj if it's pkg defs */ + fprint(2, "gopack: non-object file %s\n", file); + errors++; + allobj = 0; + } + d = dirfstat(Bfildes(b)); + if (d != nil && d->length == 0) { + fprint(2, "gopack: zero length file %s\n", file); + errors++; + } + free(d); + Bseek(b, offset, 0); + return; + } + + goobject = 1; + offset1 = Boffset(b); + Bseek(b, offset, 0); + p = Brdstr(b, '\n', 1); + + // After the go object header comes the Go metadata, + // followed by ! on a line by itself. If this is not a Go object, + // the ! comes immediately. Catch that so we can avoid + // the call to scanpkg below, since scanpkg assumes that the + // Go metadata is present. + if(Bgetc(b) == '!') + goobject = 0; + + 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 (!matchhdr(p, &objhdr)) { + fprint(2, "gopack: inconsistent object file %s: [%s] vs [%s]\n", file, p, objhdr); + errors++; + allobj = 0; + free(p); + return; + } + free(p); + + // Old check. Should be impossible since objhdrs match, but keep the check anyway. + if (lastobj >= 0 && obj != lastobj) { + fprint(2, "gopack: inconsistent object file %s\n", file); + errors++; + allobj = 0; + return; + } + lastobj = obj; + + if (!readar(b, obj, offset+size, 0)) { + fprint(2, "gopack: invalid symbol reference in file %s\n", file); + errors++; + allobj = 0; + Bseek(b, offset, 0); + return; + } + Bseek(b, offset, 0); + objtraverse(objsym, ap); + if (gflag && goobject) { + scanpkg(b, size); + Bseek(b, offset, 0); + } +} + +/* + * does line contain substring (length-limited) + */ +int +strstrn(char *line, int len, char *sub) +{ + int i; + int sublen; + + sublen = strlen(sub); + for (i = 0; i < len - sublen; i++) + if (memcmp(line+i, sub, sublen) == 0) + return 1; + return 0; +} + +/* + * package import data + */ +int safe = 1; +char* pkgname; +char* importblock; + +void +getpkgdef(char **datap, int *lenp) +{ + char *tag, *hdr; + + if(pkgname == nil) { + pkgname = "__emptyarchive__"; + importblock = ""; + } + + tag = ""; + if(safe || Sflag) + tag = "safe"; + + 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); +} + +/* + * extract the package definition data from an object file. + * there can be only one. + */ +void +scanpkg(Biobuf *b, long size) +{ + long n; + int c; + long start, end, pkgsize; + char *data, *line, pkgbuf[1024], *pkg; + int first; + + /* + * scan until $$ + */ + for (n=0; n<size; ) { + c = Bgetc(b); + if(c == Beof) + break; + n++; + if(c != '$') + continue; + c = Bgetc(b); + if(c == Beof) + break; + n++; + if(c != '$') + continue; + goto foundstart; + } + // fprint(2, "gopack: warning: no package import section in %s\n", file); + if(b != &bar || !pkgdefsafe) + safe = 0; // non-Go file (C or assembly) + return; + +foundstart: + /* found $$; skip rest of line */ + while((c = Bgetc(b)) != '\n') + if(c == Beof) + goto bad; + + /* how big is it? */ + first = 1; + start = end = 0; + for (n=0; n<size; n+=Blinelen(b)) { + line = Brdstr(b, '\n', 0); + if (line == nil) + goto bad; + if (first && strstrn(line, Blinelen(b), "package ")) { + if (Blinelen(b) > sizeof(pkgbuf)-1) + goto bad; + memmove(pkgbuf, line, Blinelen(b)); + pkgbuf[Blinelen(b)] = '\0'; + pkg = pkgbuf; + while(*pkg == ' ' || *pkg == '\t') + pkg++; + if(strncmp(pkg, "package ", 8) != 0) + goto bad; + pkg += 8; + data = pkg; + while(*pkg != ' ' && *pkg != '\n' && *pkg != '\0') + pkg++; + pkgname = armalloc(pkg - data + 1); + memmove(pkgname, data, pkg - data); + pkgname[pkg-data] = '\0'; + if(strcmp(pkg, " safe\n") != 0 && (b != &bar || !pkgdefsafe)) + safe = 0; + start = Boffset(b); // after package statement + first = 0; + free(line); + continue; + } + if(line[0] == '$' && line[1] == '$') { + free(line); + goto foundend; + } + end = Boffset(b); // before closing $$ + free(line); + } +bad: + fprint(2, "gopack: bad package import section in %s\n", file); + errors++; + return; + +foundend: + if (start == 0) + return; + if (end == 0) + goto bad; + if(importblock != nil) { + fprint(2, "gopack: multiple Go object files\n"); + errors++; + return; + } + pkgsize = end-start; + data = armalloc(end - start + 1); + Bseek(b, start, 0); + if (Bread(b, data, pkgsize) != pkgsize) { + fprint(2, "gopack: error reading package import section in %s\n", file); + errors++; + return; + } + data[end-start] = '\0'; + importblock = data; +} + +/* + * add text and data symbols to the symbol list + */ +void +objsym(Sym *s, void *p) +{ + int n; + Arsymref *as; + Arfile *ap; + char *ofile; + + if (s->type != 'T' && s->type != 'D') + return; + ap = (Arfile*)p; + as = armalloc(sizeof(Arsymref)); + as->offset = ap->size; + as->name = arstrdup(s->name); + as->file = arstrdup(file); + if(s->type == 'T' && duplicate(as->name, &ofile)) { + dupfound = 1; + fprint(2, "duplicate text symbol: %s and %s: %s\n", as->file, ofile, as->name); + errors++; + free(as->name); + free(as); + return; + } + as->type = s->type; + n = strlen(s->name); + symdefsize += 4+(n+1)+1; + as->len = n; + as->next = ap->sym; + ap->sym = as; +} + +/* + * Check the symbol table for duplicate text symbols + */ +int +hashstr(char *name) +{ + int h; + char *cp; + + h = 0; + for(cp = name; *cp; h += *cp++) + h *= 1119; + + // the code used to say + // if(h < 0) + // h = ~h; + // but on gcc 4.3 with -O2 on some systems, + // the if(h < 0) gets compiled away as not possible. + // use a mask instead, leaving plenty of bits but + // definitely not the sign bit. + + return h & 0xfffffff; +} + +int +duplicate(char *name, char **ofile) +{ + Hashchain *p; + int h; + + h = hashstr(name) % NHASH; + + for(p = hash[h]; p; p = p->next) + if(strcmp(p->name, name) == 0) { + *ofile = p->file; + return 1; + } + p = armalloc(sizeof(Hashchain)); + p->next = hash[h]; + p->name = name; + p->file = file; + hash[h] = p; + *ofile = nil; + return 0; +} + +/* + * open an archive and validate its header + */ +int +openar(char *arname, int mode, int errok) +{ + int fd; + char mbuf[SARMAG]; + + fd = open(arname, mode); + if(fd >= 0){ + if(read(fd, mbuf, SARMAG) != SARMAG || strncmp(mbuf, ARMAG, SARMAG)) { + fprint(2, "gopack: %s not in archive format\n", arname); + exits("error"); + } + }else if(!errok){ + fprint(2, "gopack: cannot open %s: %r\n", arname); + exits("error"); + } + return fd; +} + +/* + * create an archive and set its header + */ +int +arcreate(char *arname) +{ + int fd; + + fd = create(arname, OWRITE, 0664); + if(fd < 0){ + fprint(2, "gopack: cannot create %s: %r\n", arname); + exits("error"); + } + if(write(fd, ARMAG, SARMAG) != SARMAG) + wrerr(); + return fd; +} + +/* + * error handling + */ +void +wrerr(void) +{ + perror("gopack: write error"); + exits("error"); +} + +void +rderr(void) +{ + perror("gopack: read error"); + exits("error"); +} + +void +phaseerr(int offset) +{ + fprint(2, "gopack: phase error at offset %d\n", offset); + exits("error"); +} + +void +usage(void) +{ + fprint(2, "usage: gopack [%s][%s][P prefix] archive files ...\n", opt, man); + exits("error"); +} + +/* + * read the header for the next archive member + */ +Armember * +getdir(Biobuf *b) +{ + Armember *bp; + char *cp; + static char name[ARNAMESIZE+1]; + + bp = newmember(); + if(HEADER_IO(Bread, b, bp->hdr)) { + free(bp); + return 0; + } + if(strncmp(bp->hdr.fmag, ARFMAG, sizeof(bp->hdr.fmag))) + phaseerr(Boffset(b)); + strncpy(name, bp->hdr.name, sizeof(bp->hdr.name)); + cp = name+sizeof(name)-1; + while(*--cp==' ') + ; + cp[1] = '\0'; + file = arstrdup(name); + bp->date = strtol(bp->hdr.date, 0, 0); + bp->size = strtol(bp->hdr.size, 0, 0); + return bp; +} + +/* + * Copy the file referenced by fd to the temp file + */ +void +armove(Biobuf *b, Arfile *ap, Armember *bp) +{ + char *cp; + Dir *d; + vlong n; + + d = dirfstat(Bfildes(b)); + if (d == nil) { + fprint(2, "gopack: cannot stat %s\n", file); + return; + } + + trim(file, bp->hdr.name, sizeof(bp->hdr.name)); + for (cp = strchr(bp->hdr.name, 0); /* blank pad on right */ + cp < bp->hdr.name+sizeof(bp->hdr.name); cp++) + *cp = ' '; + sprint(bp->hdr.date, "%-12ld", 0L); // was d->mtime but removed for idempotent builds + sprint(bp->hdr.uid, "%-6d", 0); + sprint(bp->hdr.gid, "%-6d", 0); + sprint(bp->hdr.mode, "%-8lo", d->mode); + sprint(bp->hdr.size, "%-10lld", d->length); + strncpy(bp->hdr.fmag, ARFMAG, 2); + bp->size = d->length; + arread(b, bp); + n = bp->size; + if (n&1) + n++; + if (ap) { + arinsert(ap, bp); + ap->size += n+SAR_HDR; + } + free(d); +} + +/* + * Copy the archive member at the current offset into the temp file. + */ +void +arcopy(Biobuf *b, Arfile *ap, Armember *bp) +{ + long n; + + arread(b, bp); + n = bp->size; + if (n & 01) + n++; + if (ap) { + arinsert(ap, bp); + ap->size += n+SAR_HDR; + } +} + +/* + * Skip an archive member + */ +void +skip(Biobuf *bp, vlong len) +{ + if (len & 01) + len++; + Bseek(bp, len, 1); +} + +void +checksafe(Biobuf *bp, vlong len) +{ + char *p; + vlong end; + + if (len & 01) + len++; + end = Boffset(bp) + len; + + p = Brdline(bp, '\n'); + if(p == nil || strncmp(p, "go object ", 10) != 0) + goto done; + for(;;) { + p = Brdline(bp, '\n'); + if(p == nil || Boffset(bp) >= end) + goto done; + if(strncmp(p, "$$\n", 3) == 0) + break; + } + p = Brdline(bp, '\n'); + if(p == nil || Boffset(bp) > end) + goto done; + if(Blinelen(bp) > 8+6 && strncmp(p, "package ", 8) == 0 && strncmp(p+Blinelen(bp)-6, " safe\n", 6) == 0) + pkgdefsafe = 1; + +done: + Bseek(bp, end, 0); +} + +/* + * Stream the three temp files to an archive + */ +void +install(char *arname, Arfile *astart, Arfile *amiddle, Arfile *aend, int createflag) +{ + int fd; + + if(allobj && dupfound) { + fprint(2, "%s not changed\n", arname); + return; + } + /* leave note group behind when copying back; i.e. sidestep interrupts */ + rfork(RFNOTEG); + + if(createflag) + fprint(2, "gopack: creating %s\n", arname); + fd = arcreate(arname); + + if(allobj) + rl(fd); + + if (astart) { + arstream(fd, astart); + arfree(astart); + } + if (amiddle) { + arstream(fd, amiddle); + arfree(amiddle); + } + if (aend) { + arstream(fd, aend); + arfree(aend); + } + close(fd); +} + +void +rl(int fd) +{ + Biobuf b; + char *cp; + struct ar_hdr a; + long len; + int headlen; + char *pkgdefdata; + int pkgdefsize; + + pkgdefdata = nil; + pkgdefsize = 0; + + Binit(&b, fd, OWRITE); + Bseek(&b,seek(fd,0,1), 0); + + len = symdefsize; + if(len&01) + len++; + sprint(a.date, "%-12ld", 0L); // time(0) + sprint(a.uid, "%-6d", 0); + sprint(a.gid, "%-6d", 0); + sprint(a.mode, "%-8lo", 0644L); + sprint(a.size, "%-10ld", len); + strncpy(a.fmag, ARFMAG, 2); + strcpy(a.name, symdef); + for (cp = strchr(a.name, 0); /* blank pad on right */ + cp < a.name+sizeof(a.name); cp++) + *cp = ' '; + if(HEADER_IO(Bwrite, &b, a)) + wrerr(); + + headlen = Boffset(&b); + len += headlen; + if (gflag) { + getpkgdef(&pkgdefdata, &pkgdefsize); + len += SAR_HDR + pkgdefsize; + if (len & 1) + len++; + } + if (astart) { + wrsym(&b, len, astart->sym); + len += astart->size; + } + if(amiddle) { + wrsym(&b, len, amiddle->sym); + len += amiddle->size; + } + if(aend) + wrsym(&b, len, aend->sym); + + if(symdefsize&0x01) + Bputc(&b, 0); + + if (gflag) { + len = pkgdefsize; + sprint(a.date, "%-12ld", 0L); // time(0) + sprint(a.uid, "%-6d", 0); + sprint(a.gid, "%-6d", 0); + sprint(a.mode, "%-8lo", 0644L); + sprint(a.size, "%-10ld", (len + 1) & ~1); + strncpy(a.fmag, ARFMAG, 2); + strcpy(a.name, pkgdef); + for (cp = strchr(a.name, 0); /* blank pad on right */ + cp < a.name+sizeof(a.name); cp++) + *cp = ' '; + if(HEADER_IO(Bwrite, &b, a)) + wrerr(); + + if (Bwrite(&b, pkgdefdata, pkgdefsize) != pkgdefsize) + wrerr(); + if(len&0x01) + Bputc(&b, 0); + } + Bterm(&b); +} + +/* + * Write the defined symbols to the symdef file + */ +void +wrsym(Biobuf *bp, long offset, Arsymref *as) +{ + int off; + + while(as) { + Bputc(bp, as->type); + off = as->offset+offset; + Bputc(bp, off); + Bputc(bp, off>>8); + Bputc(bp, off>>16); + Bputc(bp, off>>24); + if (Bwrite(bp, as->name, as->len+1) != as->len+1) + wrerr(); + as = as->next; + } +} + +/* + * Check if the archive member matches an entry on the command line. + */ +int +match(int count, char **files) +{ + int i; + char name[ARNAMESIZE+1]; + + for(i=0; i<count; i++) { + if(files[i] == 0) + continue; + trim(files[i], name, ARNAMESIZE); + if(strncmp(name, file, ARNAMESIZE) == 0) { + file = files[i]; + files[i] = 0; + return 1; + } + } + return 0; +} + +/* + * compare the current member to the name of the pivot member + */ +int +bamatch(char *file, char *pivot) +{ + static int state = 0; + + switch(state) + { + case 0: /* looking for position file */ + if (aflag) { + if (strncmp(file, pivot, ARNAMESIZE) == 0) + state = 1; + } else if (bflag) { + if (strncmp(file, pivot, ARNAMESIZE) == 0) { + state = 2; /* found */ + return 1; + } + } + break; + case 1: /* found - after previous file */ + state = 2; + return 1; + case 2: /* already found position file */ + break; + } + return 0; +} + +/* + * output a message, if 'v' option was specified + */ +void +mesg(int c, char *file) +{ + + if(vflag) + Bprint(&bout, "%c - %s\n", c, file); +} + +/* + * isolate file name by stripping leading directories and trailing slashes + */ +void +trim(char *s, char *buf, int n) +{ + char *p; + + for(;;) { + p = strrchr(s, '/'); + if (!p) { /* no slash in name */ + strncpy(buf, s, n); + return; + } + if (p[1] != 0) { /* p+1 is first char of file name */ + strncpy(buf, p+1, n); + return; + } + *p = 0; /* strip trailing slash */ + } +} + +/* + * utilities for printing long form of 't' command + */ +#define SUID 04000 +#define SGID 02000 +#define ROWN 0400 +#define WOWN 0200 +#define XOWN 0100 +#define RGRP 040 +#define WGRP 020 +#define XGRP 010 +#define ROTH 04 +#define WOTH 02 +#define XOTH 01 +#define STXT 01000 + +void +longt(Armember *bp) +{ + char *cp; + + pmode(strtoul(bp->hdr.mode, 0, 8)); + Bprint(&bout, "%3ld/%1ld", strtol(bp->hdr.uid, 0, 0), strtol(bp->hdr.gid, 0, 0)); + Bprint(&bout, "%7ld", bp->size); + cp = ctime(bp->date); + Bprint(&bout, " %-12.12s %-4.4s ", cp+4, cp+24); +} + +int m1[] = { 1, ROWN, 'r', '-' }; +int m2[] = { 1, WOWN, 'w', '-' }; +int m3[] = { 2, SUID, 's', XOWN, 'x', '-' }; +int m4[] = { 1, RGRP, 'r', '-' }; +int m5[] = { 1, WGRP, 'w', '-' }; +int m6[] = { 2, SGID, 's', XGRP, 'x', '-' }; +int m7[] = { 1, ROTH, 'r', '-' }; +int m8[] = { 1, WOTH, 'w', '-' }; +int m9[] = { 2, STXT, 't', XOTH, 'x', '-' }; + +int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9}; + +void +pmode(long mode) +{ + int **mp; + + for(mp = &m[0]; mp < &m[9];) + select(*mp++, mode); +} + +void +select(int *ap, long mode) +{ + int n; + + n = *ap++; + while(--n>=0 && (mode&*ap++)==0) + ap++; + Bputc(&bout, *ap); +} + +/* + * Temp file I/O subsystem. We attempt to cache all three temp files in + * core. When we run out of memory we spill to disk. + * The I/O model assumes that temp files: + * 1) are only written on the end + * 2) are only read from the beginning + * 3) are only read after all writing is complete. + * The architecture uses one control block per temp file. Each control + * block anchors a chain of buffers, each containing an archive member. + */ +Arfile * +newtempfile(char *name) /* allocate a file control block */ +{ + Arfile *ap; + + ap = armalloc(sizeof(Arfile)); + ap->fname = name; + return ap; +} + +Armember * +newmember(void) /* allocate a member buffer */ +{ + return armalloc(sizeof(Armember)); +} + +void +arread(Biobuf *b, Armember *bp) /* read an image into a member buffer */ +{ + int i; + vlong off; + + bp->member = armalloc(bp->size); + + // If P flag is set, let arread_cutprefix try. + // If it succeeds, we're done. If not, fall back + // to a direct copy. + off = Boffset(b); + if(Pflag && arread_cutprefix(b, bp)) + return; + Bseek(b, off, 0); + + i = Bread(b, bp->member, bp->size); + if (i < 0) { + free(bp->member); + bp->member = 0; + rderr(); + } + if(bp->size&1) + Bgetc(b); +} + +/* + * insert a member buffer into the member chain + */ +void +arinsert(Arfile *ap, Armember *bp) +{ + bp->next = 0; + if (!ap->tail) + ap->head = bp; + else + ap->tail->next = bp; + ap->tail = bp; +} + +/* + * stream the members in a temp file to the file referenced by 'fd'. + */ +void +arstream(int fd, Arfile *ap) +{ + Armember *bp; + int i; + char buf[8192]; + + if (ap->paged) { /* copy from disk */ + seek(ap->fd, 0, 0); + for (;;) { + i = read(ap->fd, buf, sizeof(buf)); + if (i < 0) + rderr(); + if (i == 0) + break; + if (write(fd, buf, i) != i) + wrerr(); + } + close(ap->fd); + ap->paged = 0; + } + /* dump the in-core buffers */ + for (bp = ap->head; bp; bp = bp->next) { + if (!arwrite(fd, bp)) + wrerr(); + } +} + +/* + * write a member to 'fd'. + */ +int +arwrite(int fd, Armember *bp) +{ + int len; + + if(HEADER_IO(write, fd, bp->hdr)) + return 0; + len = bp->size; + if (len & 01) + len++; + if (write(fd, bp->member, len) != len) + return 0; + return 1; +} + +/* + * Spill a member to a disk copy of a temp file + */ +int +page(Arfile *ap) +{ + USED(ap); + + sysfatal("page"); + return 1; +} + +/* + * try to reclaim space by paging. we try to spill the start, middle, + * and end files, in that order. there is no particular reason for the + * ordering. + */ +int +getspace(void) +{ +fprint(2, "IN GETSPACE\n"); + if (astart && astart->head && page(astart)) + return 1; + if (amiddle && amiddle->head && page(amiddle)) + return 1; + if (aend && aend->head && page(aend)) + return 1; + return 0; +} + +void +arfree(Arfile *ap) /* free a member buffer */ +{ + Armember *bp, *next; + + for (bp = ap->head; bp; bp = next) { + next = bp->next; + if (bp->member) + free(bp->member); + free(bp); + } + free(ap); +} + +/* + * allocate space for a control block or member buffer. if the malloc + * fails we try to reclaim space by spilling previously allocated + * member buffers. + */ +void * +armalloc(int n) +{ + char *cp; + + // bump so that arwrite can do the same + if(n&1) + n++; + + do { + cp = malloc(n); + if (cp) { + memset(cp, 0, n); + return cp; + } + } while (getspace()); + fprint(2, "gopack: out of memory\n"); + exits("malloc"); + return 0; +} + +char * +arstrdup(char *s) +{ + char *t; + + t = armalloc(strlen(s) + 1); + strcpy(t, s); + return t; +} + + +/* + * Parts of libmach we're not supposed + * to look at but need for arread_cutprefix. + */ +extern int _read5(Biobuf*, Prog*); +extern int _read6(Biobuf*, Prog*); +extern int _read8(Biobuf*, Prog*); +int (*reader[256])(Biobuf*, Prog*) = { + [ObjArm] = _read5, + [ObjAmd64] = _read6, + [Obj386] = _read8, +}; + +/* + * copy b into bp->member but rewrite object + * during copy to drop prefix from all file names. + * return 1 if b was recognized as an object file + * and copied successfully, 0 otherwise. + */ +int +arread_cutprefix(Biobuf *b, Armember *bp) +{ + vlong offset, o, end; + int n, t; + int (*rd)(Biobuf*, Prog*); + char *w, *inprefix; + Prog p; + + offset = Boffset(b); + end = offset + bp->size; + t = objtype(b, nil); + if(t < 0) + return 0; + if((rd = reader[t]) == nil) + return 0; + + // copy header + w = bp->member; + n = Boffset(b) - offset; + Bseek(b, -n, 1); + if(Bread(b, w, n) != n) + return 0; + offset += n; + w += n; + + // read object file one pseudo-instruction at a time, + // eliding the file name instructions that refer to + // the prefix. + memset(&p, 0, sizeof p); + inprefix = nil; + while(Boffset(b) < end && rd(b, &p)) { + if(p.kind == aName && p.type == UNKNOWN && p.sym == 1 && p.id[0] == '<') { + // part of a file path. + // we'll keep continuing (skipping the copy) + // around the loop until either we get to a + // name piece that should be kept or we see + // the whole prefix. + + if(inprefix == nil && prefix[0] == '/' && p.id[1] == '/' && p.id[2] == '\0') { + // leading / + inprefix = prefix+1; + } else if(inprefix != nil) { + // handle subsequent elements + n = strlen(p.id+1); + if(strncmp(p.id+1, inprefix, n) == 0 && (inprefix[n] == '/' || inprefix[n] == '\0')) { + inprefix += n; + if(inprefix[0] == '/') + inprefix++; + } + } + + if(inprefix && inprefix[0] == '\0') { + // reached end of prefix. + // if we another path element follows, + // nudge the offset to skip over the prefix we saw. + // if not, leave offset alone, to emit the whole name. + // additional name elements will not be skipped + // because inprefix is now nil and we won't see another + // leading / in this name. + inprefix = nil; + o = Boffset(b); + if(o < end && rd(b, &p) && p.kind == aName && p.type == UNKNOWN && p.sym == 1 && p.id[0] == '<') { + // print("skip %lld-%lld\n", offset, o); + offset = o; + } + } + } else { + // didn't find the whole prefix. + // give up and let it emit the entire name. + inprefix = nil; + } + + // copy instructions + if(!inprefix) { + n = Boffset(b) - offset; + Bseek(b, -n, 1); + if(Bread(b, w, n) != n) + return 0; + offset += n; + w += n; + } + } + bp->size = w - (char*)bp->member; + sprint(bp->hdr.size, "%-10lld", (vlong)bp->size); + strncpy(bp->hdr.fmag, ARFMAG, 2); + Bseek(b, end, 0); + if(Boffset(b)&1) + Bgetc(b); + return 1; +} diff --git a/src/cmd/pack/doc.go b/src/cmd/pack/doc.go new file mode 100644 index 0000000000..6cbc357db9 --- /dev/null +++ b/src/cmd/pack/doc.go @@ -0,0 +1,26 @@ +// 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. + +/* + +Pack is a variant of the Plan 9 ar tool. The original is documented at + + http://plan9.bell-labs.com/magic/man2html/1/ar + +It adds a special Go-specific section __.PKGDEF that collects all the +Go type information from the files in the archive; that section is +used by the compiler when importing the package during compilation. + +Usage: go tool pack [uvnbailogS][mrxtdpq][P prefix] archive files ... + +The new option 'g' causes pack to maintain the __.PKGDEF section +as files are added to the archive. + +The new option 'S' forces pack to mark the archive as safe. + +The new option 'P' causes pack to remove the given prefix +from file names in the line number information in object files +that are already stored in or added to the archive. +*/ +package documentation |