1 // Inferno utils/nm/nm.c
2 // http://code.google.com/p/inferno-os/source/browse/utils/nm/nm.c
3 //
4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
6 // Portions Copyright © 1997-1999 Vita Nuova Limited
7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
8 // Portions Copyright © 2004,2006 Bruce Ellis
9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
11 // Portions Copyright © 2009 The Go Authors. All rights reserved.
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining a copy
14 // of this software and associated documentation files (the "Software"), to deal
15 // in the Software without restriction, including without limitation the rights
16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 // copies of the Software, and to permit persons to whom the Software is
18 // furnished to do so, subject to the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be included in
21 // all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 // THE SOFTWARE.
30
31 /*
32 * nm.c -- drive nm
33 */
34 #include <u.h>
35 #include <libc.h>
36 #include <ar.h>
37 #include <bio.h>
38 #include <mach.h>
39
40 enum{
41 CHUNK = 256 /* must be power of 2 */
42 };
43
44 char *errs; /* exit status */
45 char *filename; /* current file */
46 char symname[]="__.SYMDEF"; /* table of contents file name */
47 int multifile; /* processing multiple files */
48 int aflag;
49 int gflag;
50 int hflag;
51 int nflag;
52 int sflag;
53 int Sflag;
54 int uflag;
55 int Tflag;
56 int tflag;
57
58 Sym **fnames; /* file path translation table */
59 Sym **symptr;
60 int nsym;
61 Biobuf bout;
62
63 int cmp(void*, void*);
64 void error(char*, ...);
65 void execsyms(int);
66 void psym(Sym*, void*);
67 void printsyms(Sym**, long);
68 void doar(Biobuf*);
69 void dofile(Biobuf*);
70 void zenter(Sym*);
71
72 void
73 usage(void)
74 {
75 fprint(2, "usage: nm [-aghnsTu] file ...\n");
76 exits("usage");
77 }
78
79 void
80 main(int argc, char *argv[])
81 {
82 int i;
83 Biobuf *bin;
84
85 Binit(&bout, 1, OWRITE);
86 argv0 = argv[0];
87 ARGBEGIN {
88 default: usage();
89 case 'a': aflag = 1; break;
90 case 'g': gflag = 1; break;
91 case 'h': hflag = 1; break;
92 case 'n': nflag = 1; break;
93 case 's': sflag = 1; break;
94 case 'S': nflag = Sflag = 1; break;
95 case 'u': uflag = 1; break;
96 case 't': tflag = 1; break;
97 case 'T': Tflag = 1; break;
98 } ARGEND
99 if (argc == 0)
100 usage();
101 if (argc > 1)
102 multifile++;
103 for(i=0; i<argc; i++){
104 filename = argv[i];
105 bin = Bopen(filename, OREAD);
106 if(bin == 0){
107 error("cannot open %s", filename);
108 continue;
109 }
110 if (isar(bin))
111 doar(bin);
112 else{
113 Bseek(bin, 0, 0);
114 dofile(bin);
115 }
116 Bterm(bin);
117 }
118 exits(errs);
119 }
120
121 /*
122 * read an archive file,
123 * processing the symbols for each intermediate file in it.
124 */
125 void
126 doar(Biobuf *bp)
127 {
128 int offset, size, obj;
129 char name[SARNAME];
130
131 multifile = 1;
132 for (offset = Boffset(bp);;offset += size) {
133 size = nextar(bp, offset, name);
134 if (size < 0) {
135 error("phase error on ar header %d", offset);
136 return;
137 }
138 if (size == 0)
139 return;
140 if (strcmp(name, symname) == 0)
141 continue;
142 obj = objtype(bp, 0);
143 if (obj < 0) {
144 // perhaps foreign object
145 if(strlen(name) > 2 && strcmp(name+strlen(name)-2, ".o") == 0)
146 return;
147 error("inconsistent file %s in %s",
148 name, filename);
149 return;
150 }
151 if (!readar(bp, obj, offset+size, 1)) {
152 error("invalid symbol reference in file %s",
153 name);
154 return;
155 }
156 filename = name;
157 nsym=0;
158 objtraverse(psym, 0);
159 printsyms(symptr, nsym);
160 }
161 }
162
163 /*
164 * process symbols in a file
165 */
166 void
167 dofile(Biobuf *bp)
168 {
169 int obj;
170
171 obj = objtype(bp, 0);
172 if (obj < 0)
173 execsyms(Bfildes(bp));
174 else
175 if (readobj(bp, obj)) {
176 nsym = 0;
177 objtraverse(psym, 0);
178 printsyms(symptr, nsym);
179 }
180 }
181
182 /*
183 * comparison routine for sorting the symbol table
184 * this screws up on 'z' records when aflag == 1
185 */
186 int
187 cmp(void *vs, void *vt)
188 {
189 Sym **s, **t;
190
191 s = vs;
192 t = vt;
193 if(nflag) // sort on address (numeric) order
194 if((*s)->value < (*t)->value)
195 return -1;
196 else
197 return (*s)->value > (*t)->value;
198 if(sflag) // sort on file order (sequence)
199 return (*s)->sequence - (*t)->sequence;
200 return strcmp((*s)->name, (*t)->name);
201 }
202 /*
203 * enter a symbol in the table of filename elements
204 */
205 void
206 zenter(Sym *s)
207 {
208 static int maxf = 0;
209
210 if (s->value > maxf) {
211 maxf = (s->value+CHUNK-1) &~ (CHUNK-1);
212 fnames = realloc(fnames, (maxf+1)*sizeof(*fnames));
213 if(fnames == 0) {
214 error("out of memory", argv0);
215 exits("memory");
216 }
217 }
218 fnames[s->value] = s;
219 }
220
221 /*
222 * get the symbol table from an executable file, if it has one
223 */
224 void
225 execsyms(int fd)
226 {
227 Fhdr f;
228 Sym *s;
229 int32 n;
230
231 seek(fd, 0, 0);
232 if (crackhdr(fd, &f) == 0) {
233 error("Can't read header for %s", filename);
234 return;
235 }
236 if (syminit(fd, &f) < 0)
237 return;
238 s = symbase(&n);
239 nsym = 0;
240 while(n--)
241 psym(s++, 0);
242
243 printsyms(symptr, nsym);
244 }
245
246 void
247 psym(Sym *s, void* p)
248 {
249 USED(p);
250 switch(s->type) {
251 case 'T':
252 case 'L':
253 case 'D':
254 case 'B':
255 if (uflag)
256 return;
257 if (!aflag && ((s->name[0] == '.' || s->name[0] == '$')))
258 return;
259 break;
260 case 'b':
261 case 'd':
262 case 'l':
263 case 't':
264 if (uflag || gflag)
265 return;
266 if (!aflag && ((s->name[0] == '.' || s->name[0] == '$')))
267 return;
268 break;
269 case 'U':
270 if (gflag)
271 return;
272 break;
273 case 'Z':
274 if (!aflag)
275 return;
276 break;
277 case 'm':
278 case 'f': /* we only see a 'z' when the following is true*/
279 if(!aflag || uflag || gflag)
280 return;
281 if (strcmp(s->name, ".frame"))
282 zenter(s);
283 break;
284 case 'a':
285 case 'p':
286 case 'z':
287 default:
288 if(!aflag || uflag || gflag)
289 return;
290 break;
291 }
292 symptr = realloc(symptr, (nsym+1)*sizeof(Sym*));
293 if (symptr == 0) {
294 error("out of memory");
295 exits("memory");
296 }
297 symptr[nsym++] = s;
298 }
299
300 void
301 printsyms(Sym **symptr, long nsym)
302 {
303 int i, j, wid;
304 Sym *s;
305 char *cp;
306 char path[512];
307
308 qsort(symptr, nsym, sizeof(*symptr), (void*)cmp);
309
310 wid = 0;
311 for (i=0; i<nsym; i++) {
312 s = symptr[i];
313 if (s->value && wid == 0)
314 wid = 8;
315 else if (s->value >= 0x100000000LL && wid == 8)
316 wid = 16;
317 }
318 for (i=0; i<nsym; i++) {
319 s = symptr[i];
320 if (multifile && !hflag)
321 Bprint(&bout, "%s:", filename);
322 if (s->type == 'z') {
323 fileelem(fnames, (uchar *) s->name, path, 512);
324 cp = path;
325 } else
326 cp = s->name;
327 if (Tflag)
328 Bprint(&bout, "%8ux ", s->sig);
329 if (s->value || s->type == 'a' || s->type == 'p')
330 Bprint(&bout, "%*llux ", wid, s->value);
331 else
332 Bprint(&bout, "%*s ", wid, "");
333 if(Sflag) {
334 vlong siz;
335
336 siz = 0;
337 for(j=i+1; j<nsym; j++) {
338 if(symptr[j]->type != 'a' && symptr[j]->type != 'p') {
339 siz = symptr[j]->value - s->value;
340 break;
341 }
342 }
343 if(siz > 0)
344 Bprint(&bout, "%*llud ", wid, siz);
345 }
346 Bprint(&bout, "%c %s", s->type, cp);
347 if(tflag && s->gotype)
348 Bprint(&bout, " %*llux", wid, s->gotype);
349 Bprint(&bout, "\n");
350 }
351 }
352
353 void
354 error(char *fmt, ...)
355 {
356 Fmt f;
357 char buf[128];
358 va_list arg;
359
360 fmtfdinit(&f, 2, buf, sizeof buf);
361 fmtprint(&f, "%s: ", argv0);
362 va_start(arg, fmt);
363 fmtvprint(&f, fmt, arg);
364 va_end(arg);
365 fmtprint(&f, "\n");
366 fmtfdflush(&f);
367 errs = "errors";
368 }