| // 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 name[SARNAME]; | |
| multifile = 1; | |
| for (offset = Boffset(bp);;offset += size) { | |
| size = nextar(bp, offset, name); | |
| if (size < 0) { | |
| error("phase error on ar header %d", offset); | |
| return; | |
| } | |
| if (size == 0) | |
| return; | |
| if (strcmp(name, symname) == 0) | |
| continue; | |
| obj = objtype(bp, 0); | |
| if (obj < 0) { | |
| // perhaps foreign object | |
| if(strlen(name) > 2 && strcmp(name+strlen(name)-2, ".o") == 0) | |
| return; | |
| error("inconsistent file %s in %s", | |
| name, filename); | |
| return; | |
| } | |
| if (!readar(bp, obj, offset+size, 1)) { | |
| error("invalid symbol reference in file %s", | |
| name); | |
| return; | |
| } | |
| filename = name; | |
| 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"; | |
| } |