# /* * n m . c */ /*)BUILD $(TKBOPTIONS) = { TASK = ...NMX } */ /* * nm [-1bcdfgruv] file * * nm file[.obj] * * This program reads through an object file or a TKB symbol table and * prints a list of all the symbols it finds. I have found that these are * a reasonable alternative to load maps, except for the fact that TKB does * not put the undefined symbols in the symbol table file. * * Note: modified 25-Apr-80 for the newer C format. * * It has dozens of options. They are: * * -1 Output names in 1 column, default is 4 column * -b Include RSX control block symbols. These are * symbols of the form x.xxxx x$xxxx xx.xxx and * xx$xxx. Normally these symbols are supressed. * -c Include C compiler internal symbols. These are * symbols that begin with a "." and contain * only digits. Normally supressed. * -d Differences. The symbol table is sorted by * address, each symbol is replaced by the * difference between it and the next symbol. * This is used to see how large your routines * are. * -f Print symbols in file order. No sort is * performed. * -g Print only global symbols. The default is * to print globals and internals. * -m Map underscore to '_' for C symbols. * -r Reverse the direction of the sort. * -u Print undefined symbols. Undefined symbols * are normally supressed. * -x Cross reference list. * -v Sort by value, the default is sort by name. * */ #ifdef DOCUMENTATION title nm Print Object Module Symbol Names index nm Print Object Module Symbol Names synopsis nm [-options] file[.obj] description nm (NameList) reads through an object file or a TKB symbol table and prints a list of all the symbols it finds. This is a reasonable alternative to load maps and is useful in determining what global symbols are defined or referenced by an object module. .s It has dozens of options. They are: .lm +8 .s.i -8;-1 Output names in one column, default is four columns per line. .s.i -8;-b Include RSX-11M control block symbols. These are of the form "x.xxxx", "x$xxxx", "xx.xxx", and "xx$xxx". Normally, these are supressed by nm. Note that -b is appropriate if you are examining objects produced by C compilers. .s.i -8;-c Include C compiler internal symbols. These begin with a "." and contain only digits. Normally supressed. .s.i -8;-d Differences. The symbol table is sorted by address and the symbol value printed is the difference between it and the next symbol. This may be used to see how large each module is. .s.i -8;-f Print symbols in file order. No sort is performed. .s.i -8;-g Print only global symbols. The default is to print both globals and internals. .s.i -8;-m Map Radix-50 "." to underscore for C symbols. .s.i -8;-r Reverse the direction of the sort. .s.i -8;-u Print undefined symbols. These are normally supressed. .s.i -8;-v Sort by value; the default is sort by name. .s.i -8;-x Print an inter-module cross reference list of all symbols defined in all input files. Forces -b and -g. .s.lm -8 nm accepts multiple input file names. Names may contain wildcards. Note that, if compiled under RT11, nm accepts only "RT11-style" object modules, while, if compiled under RSX, nm accepts only "RSX-style" object modules. Vax native has never been tested; it might read RSX (compatiblity) modules, however. diagnostics .lm +8 .s.i -8;"file name": cannot open .s.i -8;"Usage: ..." .s This message is printed on illegal flags or other command errors. .s.i -8;"file name": bad file format .s The input file was not in the necessary object-module format. This could mean that the program was compiled by the wrong operating system. .s.i -8;Too many symbols .s.i -8;Too many sections .s .lm -8 All errors are fatal. author David Conroy, Martin Minow bugs #endif #include #define NSY 500 #define NPS 20 #define AB0 0127400 #define AB1 0003243 #define AB2 0127401 #define AB3 0007624 #define RBMAX 128 #define GSD 1 #define G_CSN 1 #define G_ISN 2 #define G_GSN 4 #define G_PSN 5 #define G_DEF 010 #define FALSE 0 #define TRUE 1 #define EOS 0 typedef struct symbol { struct symbol *s_next; char *s_name; struct module *s_defn; /* Where it's defined */ struct refer *s_refer; /* Where it's referenced */ } SYMBOL; typedef struct module { struct module *m_next; char *m_name; int m_index; } MODULE; typedef struct refer { struct refer *r_next; struct module *r_module; } REFERENCE; extern char *strsave(); extern char *myalloc(); typedef unsigned addr_t; typedef unsigned r50_t; struct rb { int type; char data[RBMAX]; }; struct gsde { r50_t g_name[2]; char g_flag; char g_type; addr_t g_value; }; struct sym { char s_name[6]; char s_flag; char s_type; int s_ps; addr_t s_value; }; struct pst { r50_t p_name[2]; addr_t p_size; }; struct rb rb; struct sym sym[NSY]; struct pst pst[NPS]; char *rbe; int nsy; int nps; int cps; char *fn; FILE *fp; int oflag; int bflag; int cflag; int dflag; int fflag; int gflag; int mflag; int rflag; int uflag; int vflag; int xflag; char filename[80]; SYMBOL *sfirst = NULL; MODULE *mfirst = NULL; REFERENCE *rfirst = NULL; char workingtext[133]; char wildname[80]; #define CROSS_MAX 30 int cross[CROSS_MAX][CROSS_MAX]; char *mname[CROSS_MAX]; int cmax = -1; main(argc, argv) char *argv[]; { register int i, c; register char *p; MODULE *mp, *newmodule(); int multi, nwild, nfiles; extern char *instr(); multi = 0; fn = NULL; for(i=1; i 1) { printf("%s%s\n\n", ((nfiles + nwild) > 0) ? "\f" : "", filename); } output(); } } if (nfiles == 0) { fprintf(stderr, "no files matched \"%s\"\n", wildname); } nwild++; } if (nwild == 0) { fprintf(stderr, "no files present\n"); } else if (xflag) { outref(); } } /* * Put out usage message. * Just exit. */ usage() { error("Usage: nm [-1bcdfgmruvx] file_list\n"); } /* * Read through the object or * symbol table file. * Save all of the symbols in * the big buffer. * Just exit if the file format * is wrong, or if a table * overflows. */ readin(mp) MODULE *mp; /* Module pointer if non-null */ { register struct gsde *p; register struct sym *sp; register int t; char ab[6]; if(!getrec() || rb.type != GSD) { fprintf(stderr, "%s: bad file format\n", filename); return; } do { if (rb.type != GSD) continue; for (p = rb.data; p < rbe; ++p) { t = p->g_type; if (t == G_CSN || t == G_PSN) { if (!xflag) { cps = lookup(p); } } else if (t == G_GSN || t == G_ISN) { if (gflag && t == G_ISN) continue; if (uflag && (p->g_flag & G_DEF) != 0) continue; convert(ab, p->g_name); if(!cflag && csym(ab)) continue; if(!bflag && bsym(ab)) continue; if (xflag) { newref(ab, (p->g_flag & G_DEF) != 0, mp); continue; } if(nsy >= NSY) error("Too many symbols!\n"); sp = &sym[nsy++]; copy(sp->s_name, ab, 6); sp->s_type = t; sp->s_flag = p->g_flag; sp->s_ps = cps; sp->s_value = p->g_value; } } } while(getrec()); } /* * Read a record from the file into the record buffer. * True return if the read was ok. * Set up rbe to point just beyond the record. * * RT11 object module format: * (optional string of NULL bytes) * 001 000 Header * low high Size low, high bytes * (size-4 bytes) Data record * byte Checksum (ignored) */ int getrec() { #ifdef rt11 register int n; register char *rbp; register int c; while (!feof(fp)) { if ((c = getc(fp)) != 0) /* Skip leading nulls */ break; } if (feof(fp)) return(FALSE); if (c != 1) { /* Header ok? */ fprintf(stderr, "Illegal rt11 .obj format in \"%s\"\n", filename); return(FALSE); } getc(fp); /* Should be a zero */ c = getc(fp) & 0377; /* Size low byte */ n = (getc(fp) << 8) + c - 4; /* Record size */ rbp = (char *)&rb; while (--n >= 0) { *rbp++ = getc(fp); } getc(fp); /* Skip over checksum */ if (feof(fp)) { fprintf(stderr, "Incomplete final record in %s\n", filename); return (FALSE); } else { rbe = rbp; return (TRUE); } #else register int n; n = fget(&rb, sizeof(rb), fp); if(feof(fp)) return(FALSE); rbe = (char *)&rb; rbe += n; return(TRUE); #endif } /* * Convert 6 radix 50 characters * to ascii. * If -m, map `.' to `_' to agree * with the (new) C format. */ convert(a, r) register char *a; r50_t *r; { register int n; r50toa(a, r, 2); n = 6; do { if (mflag && *a == '.') *a = '_'; if (*a >= 'A' && *a <= 'Z') *a += ('a' - 'A'); ++a; } while(--n); } /* * Test if a 6 character symbol * is a C internal symbol. * These symbols are all numeric * except for a leading dot. */ csym(p) register char *p; { register int c, n; if(*p++ != '.') return(0); n = 5; do { c = *p++; if(c!=' ' && (c<'0' || c>'9')) return(0); } while(--n); return(1); } /* * Test if a 6 character symbol * is an RSX control block * symbol. * These symbols have the form * x.xxxx xx.xxx x_xxxx xx_xxx x$xxxx or xx$xxx */ bsym(p) register char *p; { if(p[1] == '.' || p[1] == '_' || p[1] == '$' || p[2] == '.' || p[2] == '_' || p[2] == '$') return(1); return(0); } /* * Lookup a psection. * Make new entry if new psection. * In any casee return the * psection number. */ lookup(g) register struct gsde *g; { register struct pst *p; register int i; for(i=0; ig_name)) return(i); if(nps >= NPS) error("Too many sections!\n"); p = &pst[nps]; p->p_name[0] = g->g_name[0]; p->p_name[1] = g->g_name[1]; p->p_size = g->g_value; return(nps++); } /* * Compare psection names. * Names are 2 words of (ugh) * radix 50. */ match(a, b) register int *a, *b; { if(a[0]==b[0] && a[1]==b[1]) return(1); return(0); } /* * Run through the symbols, * computing the first order * differencees for the -d option. * The last symbol requires * special handling. * For unknown reasons, the * last symbol sometimes gets * a funny size in spite of * this. */ deltas() { register struct sym *p; sort(1, 0); for(p = &sym[0]; p < &sym[nsy-1]; ++p) p->s_value = (p+1)->s_value - p->s_value; p->s_value = pst[p->s_ps].p_size - p->s_value; } /* * Sort symbols. * v is 1 for sort on value. * r is 1 for reverse sort. */ sort(v, r) { register struct sym *b, *m, *p; struct sym ts; for(b = &sym[0]; b < &sym[nsy-1]; ++b) { m = b; for(p=b+1; p<&sym[nsy]; ++p) if(cmp(v, r, m, p)) m = p; if(m != b) { copy(&ts, m, sizeof(ts)); copy(m, b, sizeof(ts)); copy(b, &ts, sizeof(ts)); } } } /* * Compare routine for * sort. */ cmp(v, r, a, b) register struct sym *a, *b; { register int n; int c; c = 0; if(v) { if(a->s_value > b->s_value) c = 1; } else for(n=0; n<6; ++n) { if(a->s_name[n] > b->s_name[n]) { c = 1; break; } if(a->s_name[n] < b->s_name[n]) break; } if(r) c = !c; return(c); } /* * Walk through the symbols, * formatting them and writing * the result to the standard * output. * Undefined symbols get stars * as a value. */ output() { register struct sym *p; register r50_t *np; register int ntl; int max; char ab[7]; /* newref() needs the 7'th byte */ max = oflag ? 1 : 3; ntl = 0; for(p = &sym[0]; p < &sym[nsy]; ++p) { if(ntl >= max) { putchar('\n'); ntl = 0; } else if(p != &sym[0]) putchar(' '); printf("%.6s", p->s_name); printf(" %c ", (p->s_type == G_GSN) ? 'g' : ' '); if((p->s_flag & G_DEF) != 0) printf("%06o", p->s_value); else printf("******"); np = pst[p->s_ps].p_name; if((np[0]==AB0 && np[1]==AB1) || (np[0]==AB2 && np[1]==AB3)) printf(" "); else { convert(ab, np); printf(" (%.6s)", ab); } ++ntl; } if(ntl) putchar('\n'); } char * instr(item, c) register char *item; register char c; /* * If 'c' is in item, return a pointer to it, else NULL. */ { while (*item) { if (*item == c) return (item); item++; } return (NULL); } /* * Stuff for the cross reference listing */ newref(name, value, mp) char name[7]; int value; MODULE *mp; { register SYMBOL *sy; extern SYMBOL *sylookup(); register char *np; /* * First, strip out trailing ' ' from name */ name[6] = EOS; if ((np = instr(&name[0], ' ')) != NULL) *np = EOS; sy = sylookup(&name[0]); if (!value) { addref(sy, mp); /* Undefined here */ } else { /* Defined here */ if (sy->s_defn != NULL) { printf("** symbol %s defined in %s and %s\n", sy->s_name, (sy->s_defn)->m_name, mp->m_name); if (!ftty(stdout)) { fprintf(stderr, "** symbol %s defined in %s and %s\n", sy->s_name, (sy->s_defn)->m_name, mp->m_name); } } else { sy->s_defn = mp; } } } outref() { register SYMBOL *sy; register int nsymb; register REFERENCE *rp; int i, j; mnames(); /* * Pass 1 for "library" symbols only */ printf("The following are not defined:\n"); nsymb = 0; for (sy = sfirst; sy != NULL; sy = sy->s_next) { if (sy->s_defn == NULL) { printf("%c%s", ((nsymb & 7) == 0) ? '\n' : '\t', sy->s_name); nsymb++; } } printf("\n\n"); /* * Pass 2 for symbols not used elsewhere */ printf("The following are not used outside of their module:\n"); nsymb = 0; for (sy = sfirst; sy != NULL; sy = sy->s_next) { if (sy->s_defn != NULL && sy->s_refer == NULL) { printf("%c%s\t%s", ((nsymb & 3) == 0) ? '\n' : '\t', sy->s_name, (sy->s_defn)->m_name); nsymb++; } } /* * Pass 3 -- all the rest */ printf("\n\nThe following are defined and used elsewhere\n"); for (sy = sfirst; sy != NULL; sy = sy->s_next) { if (sy->s_defn == NULL || sy->s_refer == NULL) { continue; } i = (sy->s_defn)->m_index; printf("\n%s\t%s:", sy->s_name, (sy->s_defn)->m_name); nsymb = 0; for (rp = sy->s_refer; rp != NULL; rp = rp->r_next) { printf("%s%s", ((++nsymb % 6) == 0) ? "\n\t\t" : "\t", (rp->r_module)->m_name); j = (rp->r_module)->m_index; cross[i][j]++; } } printf("\n"); /* * Now for the map */ printf("\nmodule"); for (i = 0; i <= cmax; i++) { printf("\n%s\t", mname[i]); for (j = 0; j < i; j++) { if ((nsymb = cross[i][j] + cross[j][i]) == 0) printf(" "); else printf("%4d", nsymb); } printf(" %s", mname[i]); } } /* * Manage symbol table */ MODULE * newmodule() /* * Make a new module entry for this file. */ { register MODULE *mp; register MODULE **mplast; register MODULE *new; int i; getfilename(fp, workingtext); new = myalloc(sizeof (MODULE)); new->m_name = strsave(workingtext); for (mplast = &mfirst; (mp = *mplast) != NULL; mplast = &mp->m_next) { if ((i = strcmp(workingtext, mp->m_name)) < 0) break; else if (i == 0) { fprintf(stderr, "duplicate module name \"%s\"\n", workingtext); return(mp); } } new->m_next = mp; *mplast = new; cmax++; if (cmax >= CROSS_MAX) error("too many modules"); return(new); } addref(sy, mp) SYMBOL *sy; MODULE *mp; /* * The module references this symbol. */ { register REFERENCE *rp; REFERENCE **rplast; register REFERENCE *new; register int i; new = myalloc(sizeof (REFERENCE)); new->r_module = mp; for (rplast = &sy->s_refer; (rp = *rplast) != NULL; rplast = &rp->r_next) { if ((i = strcmp(mp->m_name, (rp->r_module)->m_name)) == 0) { fprintf(stderr, "\"%s\" references \"%s\" twice\n", sy->s_name, mp->m_name); } else if (i < 0) { break; } } new->r_next = rp; *rplast = new; } SYMBOL * sylookup(text) char *text; /* * Return pointer to this symbol, insert if new */ { register SYMBOL *sy; SYMBOL **sylast; register SYMBOL *new; register int i; for (sylast = &sfirst; (sy = *sylast) != NULL; sylast = &sy->s_next) { if ((i = strcmp(text, sy->s_name)) == 0) { return(sy); } else if (i < 0) { break; } } new = myalloc(sizeof (SYMBOL)); new->s_name = strsave(text); new->s_next = sy; *sylast = new; return (new); } mnames() /* * Build mname[] vector */ { register int i; register MODULE *mp; i = 0; for (mp = mfirst; mp != NULL; mp = mp->m_next, i++) { mname[i] = mp->m_name; mp->m_index = i; } } /* * Support routines */ getfilename(filedes, buffer) FILE *filedes; char *buffer; { register char *tp; register char c; fgetname(filedes, buffer); /* * Skip over device name */ for (tp = buffer; (c = *tp) != EOS && c != ':'; tp++); if (c) tp++; else tp = buffer; /* * Skip over [UIC] or * or [PPN] if present */ if (*tp == '[' || *tp == '(') { while ((c = *tp++) && c != ']' && c != ')'); if (c == 0) { error("Can't happen"); tp--; } } strcpy(buffer, tp); /* * Don't include version */ for (tp = buffer; (c = *tp) && c != ';'; tp++); *tp = 0; /* * Don't include .ext */ for (tp = buffer; (c = *tp) && c != '.'; tp++); *tp = 0; /* * Now, buffer has the file name, * tp - buffer, its length. */ return(buffer); } char * strsave(text) char *text; /* * Save this text */ { register char *p; if ((p = malloc(strlen(text) + 1)) == NULL) error("No room for string alloc."); strcpy(p, text); return (p); } char * myalloc(size) int size; /* * Allocate or die */ { register char *p; if ((p = calloc(1, size)) == NULL) { fprintf(stderr, "Can't allocate %d bytes\n", size); error("Fatal"); } return (p); }