--- /dev/null
+// 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_amd64.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;
+ 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, wid;
+ Sym *s;
+ char *cp;
+ char path[512];
+
+ if(!sflag)
+ 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, "");
+ 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";
+}