diff options
Diffstat (limited to 'src/libmach/nm.c')
-rw-r--r-- | src/libmach/nm.c | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/src/libmach/nm.c b/src/libmach/nm.c new file mode 100644 index 00000000..b5e369cb --- /dev/null +++ b/src/libmach/nm.c @@ -0,0 +1,289 @@ +/* + * 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 uflag; + +Symbol **fnames; /* file path translation table */ +Symbol **symptr; +int nsym; +Biobuf bout; + +int cmp(void*, void*); +void error(char*, ...); +void execsyms(int); +void psym(Symbol*, void*); +void printsyms(Symbol**, long); +void doar(Biobuf*); +void dofile(Biobuf*); +void zenter(Symbol*); + +void +main(int argc, char *argv[]) +{ + int i; + Biobuf *bin; + + Binit(&bout, 1, OWRITE); + argv0 = argv[0]; + ARGBEGIN { + 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 'u': uflag = 1; break; + } ARGEND + 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) +{ + Symbol **s, **t; + + s = vs; + t = vt; + if(nflag) + if((*s)->value < (*t)->value) + return -1; + else + return (*s)->value > (*t)->value; + return strcmp((*s)->name, (*t)->name); +} +/* + * enter a symbol in the table of filename elements + */ +void +zenter(Symbol *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; + Symbol *s; + long 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(Symbol *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(Symbol **symptr, long nsym) +{ + Symbol *s; + char *cp; + char path[512]; + + if(!sflag) + qsort(symptr, nsym, sizeof(*symptr), cmp); + while (nsym-- > 0) { + s = *symptr++; + 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 (s->value || s->type == 'a' || s->type == 'p') + Bprint(&bout, "%8lux %c %s\n", s->value, s->type, cp); + else + Bprint(&bout, " %c %s\n", s->type, cp); + } +} + +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"; +} |