Subversion Repositories planix.SVN

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * 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;
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 'u':       uflag = 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)
                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(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;
        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(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, wid;
        Sym *s;
        char *cp;
        char path[512];

        if(!sflag)
                qsort(symptr, nsym, sizeof(*symptr), 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, "");
                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";
}