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 
25 Symbol	**fnames;		/* file path translation table */
26 Symbol	**symptr;
27 int	nsym;
28 Biobuf	bout;
29 
30 int	cmp(void*, void*);
31 void	error(char*, ...);
32 void	execsyms(int);
33 void	psym(Symbol*, void*);
34 void	printsyms(Symbol**, long);
35 void	doar(Biobuf*);
36 void	dofile(Biobuf*);
37 void	zenter(Symbol*);
38 
39 void
main(int argc,char * argv[])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", 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
doar(Biobuf * bp)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", 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",
99 					membername, filename);
100 			return;
101 		}
102 		if (!readar(bp, obj, offset+size, 1)) {
103 			error("invalid symbol reference in file %s",
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
dofile(Biobuf * bp)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
cmp(void * vs,void * vt)138 cmp(void *vs, void *vt)
139 {
140 	Symbol **s, **t;
141 
142 	s = vs;
143 	t = vt;
144 	if(nflag)
145 		if((*s)->value < (*t)->value)
146 			return -1;
147 		else
148 			return (*s)->value > (*t)->value;
149 	return strcmp((*s)->name, (*t)->name);
150 }
151 /*
152  * enter a symbol in the table of filename elements
153  */
154 void
zenter(Symbol * s)155 zenter(Symbol *s)
156 {
157 	static int maxf = 0;
158 
159 	if (s->value > maxf) {
160 		maxf = (s->value+CHUNK-1) &~ (CHUNK-1);
161 		fnames = realloc(fnames, (maxf+1)*sizeof(*fnames));
162 		if(fnames == 0) {
163 			error("out of memory", argv0);
164 			exits("memory");
165 		}
166 	}
167 	fnames[s->value] = s;
168 }
169 
170 /*
171  * get the symbol table from an executable file, if it has one
172  */
173 void
execsyms(int fd)174 execsyms(int fd)
175 {
176 	Fhdr f;
177 	Symbol *s;
178 	long n;
179 
180 	seek(fd, 0, 0);
181 	if (crackhdr(fd, &f) == 0) {
182 		error("Can't read header for %s", filename);
183 		return;
184 	}
185 	if (syminit(fd, &f) < 0)
186 		return;
187 	s = symbase(&n);
188 	nsym = 0;
189 	while(n--)
190 		psym(s++, 0);
191 
192 	printsyms(symptr, nsym);
193 }
194 
195 void
psym(Symbol * s,void * p)196 psym(Symbol *s, void* p)
197 {
198 	USED(p);
199 	switch(s->type) {
200 	case 'T':
201 	case 'L':
202 	case 'D':
203 	case 'B':
204 		if (uflag)
205 			return;
206 		if (!aflag && ((s->name[0] == '.' || s->name[0] == '$')))
207 			return;
208 		break;
209 	case 'b':
210 	case 'd':
211 	case 'l':
212 	case 't':
213 		if (uflag || gflag)
214 			return;
215 		if (!aflag && ((s->name[0] == '.' || s->name[0] == '$')))
216 			return;
217 		break;
218 	case 'U':
219 		if (gflag)
220 			return;
221 		break;
222 	case 'Z':
223 		if (!aflag)
224 			return;
225 		break;
226 	case 'm':
227 	case 'f':	/* we only see a 'z' when the following is true*/
228 		if(!aflag || uflag || gflag)
229 			return;
230 		if (strcmp(s->name, ".frame"))
231 			zenter(s);
232 		break;
233 	case 'a':
234 	case 'p':
235 	case 'z':
236 	default:
237 		if(!aflag || uflag || gflag)
238 			return;
239 		break;
240 	}
241 	symptr = realloc(symptr, (nsym+1)*sizeof(Sym*));
242 	if (symptr == 0) {
243 		error("out of memory");
244 		exits("memory");
245 	}
246 	symptr[nsym++] = s;
247 }
248 
249 void
printsyms(Symbol ** symptr,long nsym)250 printsyms(Symbol **symptr, long nsym)
251 {
252 	Symbol *s;
253 	char *cp;
254 	char path[512];
255 
256 	if(!sflag)
257 		qsort(symptr, nsym, sizeof(*symptr), cmp);
258 	while (nsym-- > 0) {
259 		s = *symptr++;
260 		if (multifile && !hflag)
261 			Bprint(&bout, "%s:", filename);
262 		if (s->type == 'z') {
263 			fileelem(fnames, (uchar *) s->name, path, 512);
264 			cp = path;
265 		} else
266 			cp = s->name;
267 		if (s->value || s->type == 'a' || s->type == 'p')
268 			Bprint(&bout, "%8lux %c %s\n", s->value, s->type, cp);
269 		else
270 			Bprint(&bout, "         %c %s\n", s->type, cp);
271 	}
272 }
273 
274 void
error(char * fmt,...)275 error(char *fmt, ...)
276 {
277 	Fmt f;
278 	char buf[128];
279 	va_list arg;
280 
281 	fmtfdinit(&f, 2, buf, sizeof buf);
282 	fmtprint(&f, "%s: ", argv0);
283 	va_start(arg, fmt);
284 	fmtvprint(&f, fmt, arg);
285 	va_end(arg);
286 	fmtprint(&f, "\n");
287 	fmtfdflush(&f);
288 	errs = "errors";
289 }
290