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