1 /*
2 * nm.c -- drive nm
3 */
4 #include <u.h>
5 #include <libc.h>
6 #include <ar.h>
7 #include <bio.h>
8 #include <mach.h>
9
10 enum{
11 CHUNK = 256 /* must be power of 2 */
12 };
13
14 char *errs; /* exit status */
15 char *filename; /* current file */
16 char symname[]="__.SYMDEF"; /* table of contents file name */
17 int multifile; /* processing multiple files */
18 int aflag;
19 int gflag;
20 int hflag;
21 int nflag;
22 int sflag;
23 int uflag;
24 int Tflag;
25
26 Sym **fnames; /* file path translation table */
27 Sym **symptr;
28 int nsym;
29 Biobuf bout;
30
31 int cmp(void*, void*);
32 void error(char*, ...);
33 void execsyms(int);
34 void psym(Sym*, void*);
35 void printsyms(Sym**, long);
36 void doar(Biobuf*);
37 void dofile(Biobuf*);
38 void zenter(Sym*);
39
40 void
usage(void)41 usage(void)
42 {
43 fprint(2, "usage: nm [-aghnsTu] file ...\n");
44 exits("usage");
45 }
46
47 void
main(int argc,char * argv[])48 main(int argc, char *argv[])
49 {
50 int i;
51 Biobuf *bin;
52
53 Binit(&bout, 1, OWRITE);
54 argv0 = argv[0];
55 ARGBEGIN {
56 default: usage();
57 case 'a': aflag = 1; break;
58 case 'g': gflag = 1; break;
59 case 'h': hflag = 1; break;
60 case 'n': nflag = 1; break;
61 case 's': sflag = 1; break;
62 case 'u': uflag = 1; break;
63 case 'T': Tflag = 1; break;
64 } ARGEND
65 if (argc == 0)
66 usage();
67 if (argc > 1)
68 multifile++;
69 for(i=0; i<argc; i++){
70 filename = argv[i];
71 bin = Bopen(filename, OREAD);
72 if(bin == 0){
73 error("cannot open %s", filename);
74 continue;
75 }
76 if (isar(bin))
77 doar(bin);
78 else{
79 Bseek(bin, 0, 0);
80 dofile(bin);
81 }
82 Bterm(bin);
83 }
84 exits(errs);
85 }
86
87 /*
88 * read an archive file,
89 * processing the symbols for each intermediate file in it.
90 */
91 void
doar(Biobuf * bp)92 doar(Biobuf *bp)
93 {
94 int offset, size, obj;
95 char membername[SARNAME];
96
97 multifile = 1;
98 for (offset = Boffset(bp);;offset += size) {
99 size = nextar(bp, offset, membername);
100 if (size < 0) {
101 error("phase error on ar header %ld", offset);
102 return;
103 }
104 if (size == 0)
105 return;
106 if (strcmp(membername, symname) == 0)
107 continue;
108 obj = objtype(bp, 0);
109 if (obj < 0) {
110 error("inconsistent file %s in %s",
111 membername, filename);
112 return;
113 }
114 if (!readar(bp, obj, offset+size, 1)) {
115 error("invalid symbol reference in file %s",
116 membername);
117 return;
118 }
119 filename = membername;
120 nsym=0;
121 objtraverse(psym, 0);
122 printsyms(symptr, nsym);
123 }
124 }
125
126 /*
127 * process symbols in a file
128 */
129 void
dofile(Biobuf * bp)130 dofile(Biobuf *bp)
131 {
132 int obj;
133
134 obj = objtype(bp, 0);
135 if (obj < 0)
136 execsyms(Bfildes(bp));
137 else
138 if (readobj(bp, obj)) {
139 nsym = 0;
140 objtraverse(psym, 0);
141 printsyms(symptr, nsym);
142 }
143 }
144
145 /*
146 * comparison routine for sorting the symbol table
147 * this screws up on 'z' records when aflag == 1
148 */
149 int
cmp(void * vs,void * vt)150 cmp(void *vs, void *vt)
151 {
152 Sym **s, **t;
153
154 s = vs;
155 t = vt;
156 if(nflag)
157 if((*s)->value < (*t)->value)
158 return -1;
159 else
160 return (*s)->value > (*t)->value;
161 return strcmp((*s)->name, (*t)->name);
162 }
163 /*
164 * enter a symbol in the table of filename elements
165 */
166 void
zenter(Sym * s)167 zenter(Sym *s)
168 {
169 static int maxf = 0;
170
171 if (s->value > maxf) {
172 maxf = (s->value+CHUNK-1) &~ (CHUNK-1);
173 fnames = realloc(fnames, (maxf+1)*sizeof(*fnames));
174 if(fnames == 0) {
175 error("out of memory", argv0);
176 exits("memory");
177 }
178 }
179 fnames[s->value] = s;
180 }
181
182 /*
183 * get the symbol table from an executable file, if it has one
184 */
185 void
execsyms(int fd)186 execsyms(int fd)
187 {
188 Fhdr f;
189 Sym *s;
190 long n;
191
192 seek(fd, 0, 0);
193 if (crackhdr(fd, &f) == 0) {
194 error("Can't read header for %s", filename);
195 return;
196 }
197 if (syminit(fd, &f) < 0)
198 return;
199 s = symbase(&n);
200 nsym = 0;
201 while(n--)
202 psym(s++, 0);
203
204 printsyms(symptr, nsym);
205 }
206
207 void
psym(Sym * s,void * p)208 psym(Sym *s, void* p)
209 {
210 USED(p);
211 switch(s->type) {
212 case 'T':
213 case 'L':
214 case 'D':
215 case 'B':
216 if (uflag)
217 return;
218 if (!aflag && ((s->name[0] == '.' || s->name[0] == '$')))
219 return;
220 break;
221 case 'b':
222 case 'd':
223 case 'l':
224 case 't':
225 if (uflag || gflag)
226 return;
227 if (!aflag && ((s->name[0] == '.' || s->name[0] == '$')))
228 return;
229 break;
230 case 'U':
231 if (gflag)
232 return;
233 break;
234 case 'Z':
235 if (!aflag)
236 return;
237 break;
238 case 'm':
239 case 'f': /* we only see a 'z' when the following is true*/
240 if(!aflag || uflag || gflag)
241 return;
242 if (strcmp(s->name, ".frame"))
243 zenter(s);
244 break;
245 case 'a':
246 case 'p':
247 case 'z':
248 default:
249 if(!aflag || uflag || gflag)
250 return;
251 break;
252 }
253 symptr = realloc(symptr, (nsym+1)*sizeof(Sym*));
254 if (symptr == 0) {
255 error("out of memory");
256 exits("memory");
257 }
258 symptr[nsym++] = s;
259 }
260
261 void
printsyms(Sym ** symptr,long nsym)262 printsyms(Sym **symptr, long nsym)
263 {
264 int i, wid;
265 Sym *s;
266 char *cp;
267 char path[512];
268
269 if(!sflag)
270 qsort(symptr, nsym, sizeof(*symptr), cmp);
271
272 wid = 0;
273 for (i=0; i<nsym; i++) {
274 s = symptr[i];
275 if (s->value && wid == 0)
276 wid = 8;
277 else if (s->value >= 0x100000000LL && wid == 8)
278 wid = 16;
279 }
280 for (i=0; i<nsym; i++) {
281 s = symptr[i];
282 if (multifile && !hflag)
283 Bprint(&bout, "%s:", filename);
284 if (s->type == 'z') {
285 fileelem(fnames, (uchar *) s->name, path, 512);
286 cp = path;
287 } else
288 cp = s->name;
289 if (Tflag)
290 Bprint(&bout, "%8ux ", s->sig);
291 if (s->value || s->type == 'a' || s->type == 'p')
292 Bprint(&bout, "%*llux ", wid, s->value);
293 else
294 Bprint(&bout, "%*s ", wid, "");
295 Bprint(&bout, "%c %s\n", s->type, cp);
296 }
297 }
298
299 void
error(char * fmt,...)300 error(char *fmt, ...)
301 {
302 Fmt f;
303 char buf[128];
304 va_list arg;
305
306 fmtfdinit(&f, 2, buf, sizeof buf);
307 fmtprint(&f, "%s: ", argv0);
308 va_start(arg, fmt);
309 fmtvprint(&f, fmt, arg);
310 va_end(arg);
311 fmtprint(&f, "\n");
312 fmtfdflush(&f);
313 errs = "errors";
314 }
315