Commit | Line | Data |
---|---|---|
2fa0b342 DHW |
1 | /*** nm.c -- Describe symbol table of a rel file. */ |
2 | #include "sysdep.h" | |
3 | #include "bfd.h" | |
4 | #include "getopt.h" | |
5 | #include "stab.gnu.h" | |
6 | #include <ranlib.h> | |
7 | ||
8 | ||
9 | ||
10 | PROTO(static boolean, display_file, (char *filename)); | |
11 | PROTO(static boolean, do_one_rel_file, (bfd *file)); | |
12 | PROTO(static unsigned int, filter_symbols, (bfd *file, asymbol **syms, | |
13 | unsigned long symcount)); | |
14 | ||
15 | PROTO(static void, print_symbols, (bfd *file, asymbol **syms, | |
16 | unsigned long symcount)); | |
17 | extern PROTO(int, (*sorters[2][2]), (char *x, char *y)); | |
18 | PROTO(static void, print_symdef_entry, (bfd * abfd)); | |
19 | ||
20 | /* Command options. */ | |
21 | ||
22 | int external_only = 0; /* print external symbols only */ | |
23 | int file_on_each_line = 0; /* print file name on each line */ | |
24 | int no_sort = 0; /* don't sort; print syms in order found */ | |
25 | int print_debug_syms = 0; /* print debugger-only symbols too */ | |
26 | int print_armap = 0; /* describe __.SYMDEF data in archive files. */ | |
27 | int reverse_sort = 0; /* sort in downward(alpha or numeric) order */ | |
28 | int sort_numerically = 0; /* sort in numeric rather than alpha order */ | |
29 | int undefined_only = 0; /* print undefined symbols only */ | |
30 | ||
31 | boolean print_each_filename = false; /* Ick. Used in archives. */ | |
32 | ||
33 | /* IMPORT */ | |
34 | extern char *program_name; | |
35 | extern char *program_version; | |
36 | extern char *target; | |
37 | ||
38 | struct option long_options[] = { | |
39 | {"debug-syms", 0, &print_debug_syms, 1}, | |
40 | {"extern-only", 0, &external_only, 1}, | |
41 | {"no-sort", 0, &no_sort, 1}, | |
42 | {"numeric-sort", 0, &sort_numerically, 1}, | |
43 | {"print-armap", 0, &print_armap, 1}, | |
44 | {"print-file-name", 0, &file_on_each_line, 1}, | |
45 | {"reverse-sort", 0, &reverse_sort, 1}, | |
46 | {"target", 2, NULL, NULL}, | |
47 | {"undefined-only", 0, &undefined_only, 1}, | |
48 | {0, 0, 0, 0} | |
49 | }; | |
50 | \f | |
51 | /* Some error-reporting functions */ | |
52 | ||
53 | void | |
54 | usage () | |
55 | { | |
56 | fprintf(stderr, "nm %s\nUsage: %s [-agnoprsu] filename...\n", | |
57 | program_version, program_name); | |
58 | exit(0); | |
59 | } | |
60 | ||
61 | int | |
62 | main (argc, argv) | |
63 | int argc; | |
64 | char **argv; | |
65 | { | |
66 | int c; /* sez which option char */ | |
67 | int ind = 0; /* used by getopt and ignored by us */ | |
68 | extern int optind; /* steps thru options */ | |
69 | ||
70 | program_name = *argv; | |
71 | ||
72 | while ((c = getopt_long(argc, argv, "agnoprsu", long_options, &ind)) != EOF) { | |
73 | switch (c) { | |
74 | case 'a': print_debug_syms = 1; break; | |
75 | case 'g': external_only = 1; break; | |
76 | case 'n': sort_numerically = 1; break; | |
77 | case 'o': file_on_each_line = 1; break; | |
78 | case 'p': no_sort = 1; break; | |
79 | case 'r': reverse_sort = 1; break; | |
80 | case 's': print_armap = 1; break; | |
81 | case 'u': undefined_only = 1; break; | |
82 | ||
83 | case 0: | |
84 | if (!strcmp("target",(long_options[option_index]).name)) { | |
85 | target = optarg; | |
86 | } | |
87 | ||
88 | break; /* we've been given a long option */ | |
89 | ||
90 | default: | |
91 | usage (); | |
92 | } | |
93 | } | |
94 | ||
95 | /* Strangely, for the shell you should return only a nonzero value | |
96 | on sucess -- the inverse of the C sense. */ | |
97 | ||
98 | /* OK, all options now parsed. If no filename specified, do a.out. */ | |
99 | if (optind == argc) return !display_file ("a.out"); | |
100 | ||
101 | /* We were given several filenames to do: */ | |
102 | while (optind < argc) | |
103 | if (!display_file (argv[optind++])) return 1; | |
104 | ||
105 | return 0; | |
106 | } | |
107 | \f | |
108 | /** Display a file's stats */ | |
109 | ||
110 | /* goto here is marginally cleaner than the nested if syntax */ | |
111 | ||
112 | static boolean | |
113 | display_file (filename) | |
114 | char *filename; | |
115 | { | |
116 | boolean retval = false; | |
117 | bfd *file; | |
118 | bfd *arfile = NULL; | |
119 | ||
120 | file = bfd_openr(filename, target); | |
121 | if (file == NULL) { | |
122 | bfd_fatal (filename); | |
123 | } | |
124 | ||
125 | if (bfd_check_format(file, bfd_object)) { | |
126 | retval = do_one_rel_file (file); | |
127 | goto closer; | |
128 | } | |
129 | ||
130 | if (!bfd_check_format (file, bfd_archive)) { | |
131 | fprintf (stderr, "%s: %s: unknown format.\n", program_name, filename); | |
132 | retval = false; | |
133 | goto closer; | |
134 | } | |
135 | ||
136 | printf("In archive %s:\n", filename); | |
137 | if (print_armap) print_symdef_entry (file); | |
138 | for (;;) { | |
139 | arfile = bfd_openr_next_archived_file (file, arfile); | |
140 | ||
141 | if (arfile == NULL) { | |
142 | if (bfd_error != no_more_archived_files) | |
143 | bfd_fatal (filename); | |
144 | goto closer; | |
145 | } | |
146 | ||
147 | if (!bfd_check_format(arfile, bfd_object)) | |
148 | printf("%s: not an object file\n", arfile->filename); | |
149 | else { | |
150 | printf ("\n%s:\n", arfile->filename); | |
151 | if (!do_one_rel_file (arfile)) return false; | |
152 | } | |
153 | } | |
154 | ||
155 | closer: | |
156 | if (bfd_close(file) == false) | |
157 | bfd_fatal (filename); | |
158 | ||
159 | return retval; | |
160 | } | |
161 | \f | |
162 | ||
163 | static boolean | |
164 | do_one_rel_file (abfd) | |
165 | bfd *abfd; | |
166 | { | |
167 | unsigned int storage; | |
168 | asymbol **syms; | |
169 | unsigned int symcount = 0; | |
170 | ||
171 | if (!(bfd_get_file_flags (abfd) & HAS_SYMS)) { | |
172 | (void) printf ("No symbols in \"%s\".\n", bfd_get_filename (abfd)); | |
173 | return true; | |
174 | } | |
175 | ||
176 | ||
177 | storage = get_symtab_upper_bound (abfd); | |
178 | if (storage == 0) { | |
179 | nosymz: | |
180 | fprintf (stderr, "%s: Symflags set but there are none?\n", | |
181 | bfd_get_filename (abfd)); | |
182 | exit (1); | |
183 | } | |
184 | ||
185 | syms = (asymbol **) xmalloc (storage); | |
186 | ||
187 | symcount = bfd_canonicalize_symtab (abfd, syms); | |
188 | if (symcount == 0) goto nosymz; | |
189 | ||
190 | /* Discard the symbols we don't want to print. | |
191 | It's OK to do this in place; we'll free the storage anyway | |
192 | (after printing) */ | |
193 | ||
194 | symcount = filter_symbols (abfd, syms, symcount); | |
195 | ||
196 | if (!no_sort) | |
197 | qsort((char *) syms, symcount, sizeof (asymbol *), | |
198 | sorters[sort_numerically][reverse_sort]); | |
199 | ||
200 | if (print_each_filename && !file_on_each_line) | |
201 | printf("\n%s:\n", bfd_get_filename(abfd)); | |
202 | ||
203 | print_symbols (abfd, syms, symcount); | |
204 | free (syms); | |
205 | return true; | |
206 | } | |
207 | \f | |
208 | /* Symbol-sorting predicates */ | |
209 | #define valueof(x) ((x)->section ? (x)->section->vma + (x)->value : (x)->value) | |
210 | int | |
211 | numeric_forward (x, y) | |
212 | char *x; | |
213 | char *y; | |
214 | { | |
215 | ||
216 | return (valueof(*(asymbol **)x) - valueof(*(asymbol **) y));; | |
217 | } | |
218 | ||
219 | int | |
220 | numeric_reverse (x, y) | |
221 | char *x; | |
222 | char *y; | |
223 | { | |
224 | return (valueof(*(asymbol **)y) - valueof(*(asymbol **) x)); | |
225 | ||
226 | } | |
227 | ||
228 | int | |
229 | non_numeric_forward (x, y) | |
230 | char *x; | |
231 | char *y; | |
232 | { | |
233 | char *xn = (*(asymbol **) x)->name; | |
234 | char *yn = (*(asymbol **) y)->name; | |
235 | ||
236 | return ((xn == NULL) ? ((yn == NULL) ? 0 : -1) : | |
237 | ((yn == NULL) ? 1 : strcmp (xn, yn))); | |
238 | } | |
239 | ||
240 | int | |
241 | non_numeric_reverse (x, y) | |
242 | char *x; | |
243 | char *y; | |
244 | { | |
245 | return -(non_numeric_forward (x, y)); | |
246 | } | |
247 | ||
248 | int (*sorters[2][2])() = { | |
249 | {non_numeric_forward, non_numeric_reverse}, | |
250 | {numeric_forward, numeric_reverse}, | |
251 | }; | |
252 | \f | |
253 | ||
254 | /* Choose which symbol entries to print; | |
255 | compact them downward to get rid of the rest. | |
256 | Return the number of symbols to be printed. */ | |
257 | static unsigned int | |
258 | filter_symbols (abfd, syms, symcount) | |
259 | bfd *abfd; | |
260 | asymbol **syms; | |
261 | unsigned long symcount; | |
262 | { | |
263 | asymbol **from, **to; | |
264 | unsigned int dst_count = 0; | |
265 | unsigned int src_count; | |
266 | for (from = to = syms, src_count = 0; src_count <symcount; src_count++) { | |
267 | int keep = 0; | |
268 | flagword flags = (from[src_count])->flags; | |
269 | ||
270 | if (undefined_only) { | |
271 | keep = (flags & BSF_UNDEFINED); | |
272 | } else if (external_only) { | |
273 | keep = ((flags & BSF_GLOBAL) || (flags & BSF_UNDEFINED) || | |
274 | (flags & BSF_FORT_COMM)); | |
275 | } else { | |
276 | keep = 1; | |
277 | } | |
278 | ||
279 | if (!print_debug_syms && ((flags & BSF_DEBUGGING) != 0)) { | |
280 | keep = 0; | |
281 | } | |
282 | ||
283 | if (keep) { | |
284 | to[dst_count++] = from[src_count]; | |
285 | } | |
286 | } | |
287 | ||
288 | return dst_count; | |
289 | } | |
290 | \f | |
291 | ||
292 | /* Return a lower-case character corresponding to the symbol class of sym */ | |
293 | char | |
294 | decode_symclass (sym) | |
295 | asymbol *sym; | |
296 | { | |
297 | flagword flags = sym->flags; | |
298 | ||
299 | if ((sym->value == 0) && (sym->section != NULL)) | |
300 | /* Huh? All section names don't begin with "." */ | |
301 | return (sym->section->name)[1]; | |
302 | ||
303 | if (flags & BSF_FORT_COMM) return 'C'; | |
304 | if (flags & BSF_UNDEFINED) return 'U'; | |
305 | if (flags & BSF_ABSOLUTE) return 'a'; | |
306 | ||
307 | ||
308 | if ( (flags & BSF_GLOBAL) || (flags & BSF_LOCAL) ){ | |
309 | if ( !strcmp(sym->section->name, ".text") ){ | |
310 | return 't'; | |
311 | } else if ( !strcmp(sym->section->name, ".data") ){ | |
312 | return 'd'; | |
313 | } else if ( !strcmp(sym->section->name, ".bss") ){ | |
314 | return 'b'; | |
315 | } else { | |
316 | return 'o'; | |
317 | } | |
318 | } | |
319 | ||
320 | /* We don't have to handle these cases just yet, but we will soon: | |
321 | N_SETV: 'v'; | |
322 | N_SETA: 'l'; | |
323 | N_SETT: 'x'; | |
324 | N_SETD: 'z'; | |
325 | N_SETB: 's'; | |
326 | N_INDR: 'i'; | |
327 | */ | |
328 | ||
329 | return '?'; | |
330 | } | |
331 | ||
332 | static void | |
333 | print_symbols (abfd, syms, symcount) | |
334 | bfd *abfd; | |
335 | asymbol **syms; | |
336 | unsigned long symcount; | |
337 | { | |
338 | asymbol **sym = syms, **end = syms + symcount; | |
339 | char class; | |
340 | ||
341 | for (; sym < end; ++sym) { | |
342 | if (file_on_each_line) printf("%s:", bfd_get_filename(abfd)); | |
343 | ||
344 | if (undefined_only) { | |
345 | if ((*sym)->flags & BSF_UNDEFINED) | |
346 | puts ((*sym)->name); | |
347 | } | |
348 | else { | |
349 | asymbol *p = *sym; | |
350 | if (p) { | |
351 | class = decode_symclass (p); | |
352 | ||
353 | if (p->flags & BSF_GLOBAL) | |
354 | class = toupper (class); | |
355 | ||
356 | if (p->value || ((p->flags & BSF_UNDEFINED) != BSF_UNDEFINED)) | |
357 | printf ("%08lx ", (p->section ? p->value + p->section->vma : p->value)); | |
358 | else fputs (" ", stdout); | |
359 | ||
360 | printf ("%c %s\n", class, p->name); | |
361 | } | |
362 | } | |
363 | } | |
364 | } | |
365 | ||
366 | static void | |
367 | print_symdef_entry (abfd) | |
368 | bfd * abfd; | |
369 | { | |
370 | symindex idx = BFD_NO_MORE_SYMBOLS; | |
371 | carsym *thesym; | |
372 | boolean everprinted = false; | |
373 | ||
374 | for (idx = bfd_get_next_mapent (abfd, idx, &thesym); | |
375 | idx != BFD_NO_MORE_SYMBOLS; | |
376 | idx = bfd_get_next_mapent (abfd, idx, &thesym)) { | |
377 | bfd *elt; | |
378 | if (!everprinted) { | |
379 | printf ("\nArchive index:\n"); | |
380 | everprinted = true; | |
381 | } | |
382 | elt = bfd_get_elt_at_index (abfd, idx); | |
383 | if (thesym->name != (char *)NULL) { | |
384 | printf ("%s in %s\n", thesym->name, bfd_get_filename (elt)); | |
385 | } | |
386 | } | |
387 | } |