|  | // Inferno utils/nm/nm.c | 
|  | // http://code.google.com/p/inferno-os/source/browse/utils/nm/nm.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. | 
|  |  | 
|  | /* | 
|  | * nm.c -- drive nm | 
|  | */ | 
|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <ar.h> | 
|  | #include <bio.h> | 
|  | #include <mach.h> | 
|  |  | 
|  | enum{ | 
|  | CHUNK	=	256	/* must be power of 2 */ | 
|  | }; | 
|  |  | 
|  | char	*errs;			/* exit status */ | 
|  | char	*filename;		/* current file */ | 
|  | char	symname[]="__.SYMDEF";	/* table of contents file name */ | 
|  | int	multifile;		/* processing multiple files */ | 
|  | int	aflag; | 
|  | int	gflag; | 
|  | int	hflag; | 
|  | int	nflag; | 
|  | int	sflag; | 
|  | int	Sflag; | 
|  | int	uflag; | 
|  | int	Tflag; | 
|  | int	tflag; | 
|  |  | 
|  | Sym	**fnames;		/* file path translation table */ | 
|  | Sym	**symptr; | 
|  | int	nsym; | 
|  | Biobuf	bout; | 
|  |  | 
|  | int	cmp(void*, void*); | 
|  | void	error(char*, ...); | 
|  | void	execsyms(int); | 
|  | void	psym(Sym*, void*); | 
|  | void	printsyms(Sym**, long); | 
|  | void	doar(Biobuf*); | 
|  | void	dofile(Biobuf*); | 
|  | void	zenter(Sym*); | 
|  |  | 
|  | void | 
|  | usage(void) | 
|  | { | 
|  | fprint(2, "usage: nm [-aghnsTu] file ...\n"); | 
|  | exits("usage"); | 
|  | } | 
|  |  | 
|  | void | 
|  | main(int argc, char *argv[]) | 
|  | { | 
|  | int i; | 
|  | Biobuf	*bin; | 
|  |  | 
|  | Binit(&bout, 1, OWRITE); | 
|  | argv0 = argv[0]; | 
|  | ARGBEGIN { | 
|  | default:	usage(); | 
|  | case 'a':	aflag = 1; break; | 
|  | case 'g':	gflag = 1; break; | 
|  | case 'h':	hflag = 1; break; | 
|  | case 'n':	nflag = 1; break; | 
|  | case 's':	sflag = 1; break; | 
|  | case 'S':	nflag = Sflag = 1; break; | 
|  | case 'u':	uflag = 1; break; | 
|  | case 't':	tflag = 1; break; | 
|  | case 'T':	Tflag = 1; break; | 
|  | } ARGEND | 
|  | if (argc == 0) | 
|  | usage(); | 
|  | if (argc > 1) | 
|  | multifile++; | 
|  | for(i=0; i<argc; i++){ | 
|  | filename = argv[i]; | 
|  | bin = Bopen(filename, OREAD); | 
|  | if(bin == 0){ | 
|  | error("cannot open %s", filename); | 
|  | continue; | 
|  | } | 
|  | if (isar(bin)) | 
|  | doar(bin); | 
|  | else{ | 
|  | Bseek(bin, 0, 0); | 
|  | dofile(bin); | 
|  | } | 
|  | Bterm(bin); | 
|  | } | 
|  | exits(errs); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * read an archive file, | 
|  | * processing the symbols for each intermediate file in it. | 
|  | */ | 
|  | void | 
|  | doar(Biobuf *bp) | 
|  | { | 
|  | int offset, size, obj; | 
|  | char membername[SARNAME]; | 
|  |  | 
|  | multifile = 1; | 
|  | for (offset = Boffset(bp);;offset += size) { | 
|  | size = nextar(bp, offset, membername); | 
|  | if (size < 0) { | 
|  | error("phase error on ar header %ld", offset); | 
|  | return; | 
|  | } | 
|  | if (size == 0) | 
|  | return; | 
|  | if (strcmp(membername, symname) == 0) | 
|  | continue; | 
|  | obj = objtype(bp, 0); | 
|  | if (obj < 0) { | 
|  | error("inconsistent file %s in %s", | 
|  | membername, filename); | 
|  | return; | 
|  | } | 
|  | if (!readar(bp, obj, offset+size, 1)) { | 
|  | error("invalid symbol reference in file %s", | 
|  | membername); | 
|  | return; | 
|  | } | 
|  | filename = membername; | 
|  | nsym=0; | 
|  | objtraverse(psym, 0); | 
|  | printsyms(symptr, nsym); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * process symbols in a file | 
|  | */ | 
|  | void | 
|  | dofile(Biobuf *bp) | 
|  | { | 
|  | int obj; | 
|  |  | 
|  | obj = objtype(bp, 0); | 
|  | if (obj < 0) | 
|  | execsyms(Bfildes(bp)); | 
|  | else | 
|  | if (readobj(bp, obj)) { | 
|  | nsym = 0; | 
|  | objtraverse(psym, 0); | 
|  | printsyms(symptr, nsym); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * comparison routine for sorting the symbol table | 
|  | *	this screws up on 'z' records when aflag == 1 | 
|  | */ | 
|  | int | 
|  | cmp(void *vs, void *vt) | 
|  | { | 
|  | Sym **s, **t; | 
|  |  | 
|  | s = vs; | 
|  | t = vt; | 
|  | if(nflag)	// sort on address (numeric) order | 
|  | if((*s)->value < (*t)->value) | 
|  | return -1; | 
|  | else | 
|  | return (*s)->value > (*t)->value; | 
|  | if(sflag)	// sort on file order (sequence) | 
|  | return (*s)->sequence - (*t)->sequence; | 
|  | return strcmp((*s)->name, (*t)->name); | 
|  | } | 
|  | /* | 
|  | * enter a symbol in the table of filename elements | 
|  | */ | 
|  | void | 
|  | zenter(Sym *s) | 
|  | { | 
|  | static int maxf = 0; | 
|  |  | 
|  | if (s->value > maxf) { | 
|  | maxf = (s->value+CHUNK-1) &~ (CHUNK-1); | 
|  | fnames = realloc(fnames, (maxf+1)*sizeof(*fnames)); | 
|  | if(fnames == 0) { | 
|  | error("out of memory", argv0); | 
|  | exits("memory"); | 
|  | } | 
|  | } | 
|  | fnames[s->value] = s; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * get the symbol table from an executable file, if it has one | 
|  | */ | 
|  | void | 
|  | execsyms(int fd) | 
|  | { | 
|  | Fhdr f; | 
|  | Sym *s; | 
|  | int32 n; | 
|  |  | 
|  | seek(fd, 0, 0); | 
|  | if (crackhdr(fd, &f) == 0) { | 
|  | error("Can't read header for %s", filename); | 
|  | return; | 
|  | } | 
|  | if (syminit(fd, &f) < 0) | 
|  | return; | 
|  | s = symbase(&n); | 
|  | nsym = 0; | 
|  | while(n--) | 
|  | psym(s++, 0); | 
|  |  | 
|  | printsyms(symptr, nsym); | 
|  | } | 
|  |  | 
|  | void | 
|  | psym(Sym *s, void* p) | 
|  | { | 
|  | USED(p); | 
|  | switch(s->type) { | 
|  | case 'T': | 
|  | case 'L': | 
|  | case 'D': | 
|  | case 'B': | 
|  | if (uflag) | 
|  | return; | 
|  | if (!aflag && ((s->name[0] == '.' || s->name[0] == '$'))) | 
|  | return; | 
|  | break; | 
|  | case 'b': | 
|  | case 'd': | 
|  | case 'l': | 
|  | case 't': | 
|  | if (uflag || gflag) | 
|  | return; | 
|  | if (!aflag && ((s->name[0] == '.' || s->name[0] == '$'))) | 
|  | return; | 
|  | break; | 
|  | case 'U': | 
|  | if (gflag) | 
|  | return; | 
|  | break; | 
|  | case 'Z': | 
|  | if (!aflag) | 
|  | return; | 
|  | break; | 
|  | case 'm': | 
|  | case 'f':	/* we only see a 'z' when the following is true*/ | 
|  | if(!aflag || uflag || gflag) | 
|  | return; | 
|  | if (strcmp(s->name, ".frame")) | 
|  | zenter(s); | 
|  | break; | 
|  | case 'a': | 
|  | case 'p': | 
|  | case 'z': | 
|  | default: | 
|  | if(!aflag || uflag || gflag) | 
|  | return; | 
|  | break; | 
|  | } | 
|  | symptr = realloc(symptr, (nsym+1)*sizeof(Sym*)); | 
|  | if (symptr == 0) { | 
|  | error("out of memory"); | 
|  | exits("memory"); | 
|  | } | 
|  | symptr[nsym++] = s; | 
|  | } | 
|  |  | 
|  | void | 
|  | printsyms(Sym **symptr, long nsym) | 
|  | { | 
|  | int i, j, wid; | 
|  | Sym *s; | 
|  | char *cp; | 
|  | char path[512]; | 
|  |  | 
|  | qsort(symptr, nsym, sizeof(*symptr), (void*)cmp); | 
|  |  | 
|  | wid = 0; | 
|  | for (i=0; i<nsym; i++) { | 
|  | s = symptr[i]; | 
|  | if (s->value && wid == 0) | 
|  | wid = 8; | 
|  | else if (s->value >= 0x100000000LL && wid == 8) | 
|  | wid = 16; | 
|  | } | 
|  | for (i=0; i<nsym; i++) { | 
|  | s = symptr[i]; | 
|  | if (multifile && !hflag) | 
|  | Bprint(&bout, "%s:", filename); | 
|  | if (s->type == 'z') { | 
|  | fileelem(fnames, (uchar *) s->name, path, 512); | 
|  | cp = path; | 
|  | } else | 
|  | cp = s->name; | 
|  | if (Tflag) | 
|  | Bprint(&bout, "%8ux ", s->sig); | 
|  | if (s->value || s->type == 'a' || s->type == 'p') | 
|  | Bprint(&bout, "%*llux ", wid, s->value); | 
|  | else | 
|  | Bprint(&bout, "%*s ", wid, ""); | 
|  | if(Sflag) { | 
|  | vlong siz; | 
|  |  | 
|  | siz = 0; | 
|  | for(j=i+1; j<nsym; j++) { | 
|  | if(symptr[j]->type != 'a' && symptr[j]->type != 'p') { | 
|  | siz = symptr[j]->value - s->value; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if(siz > 0) | 
|  | Bprint(&bout, "%*llud ", wid, siz); | 
|  | } | 
|  | Bprint(&bout, "%c %s", s->type, cp); | 
|  | if(tflag && s->gotype) | 
|  | Bprint(&bout, " %*llux", wid, s->gotype); | 
|  | Bprint(&bout, "\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | error(char *fmt, ...) | 
|  | { | 
|  | Fmt f; | 
|  | char buf[128]; | 
|  | va_list arg; | 
|  |  | 
|  | fmtfdinit(&f, 2, buf, sizeof buf); | 
|  | fmtprint(&f, "%s: ", argv0); | 
|  | va_start(arg, fmt); | 
|  | fmtvprint(&f, fmt, arg); | 
|  | va_end(arg); | 
|  | fmtprint(&f, "\n"); | 
|  | fmtfdflush(&f); | 
|  | errs = "errors"; | 
|  | } |