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