aboutsummaryrefslogtreecommitdiff
path: root/src/libmach/nm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libmach/nm.c')
-rw-r--r--src/libmach/nm.c289
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";
+}