| // 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"; |
| } |