1 |
|
|
/* $OpenBSD: nm.c,v 1.51 2015/12/09 19:28:34 mmcc Exp $ */ |
2 |
|
|
/* $NetBSD: nm.c,v 1.7 1996/01/14 23:04:03 pk Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* Copyright (c) 1989, 1993 |
6 |
|
|
* The Regents of the University of California. All rights reserved. |
7 |
|
|
* |
8 |
|
|
* This code is derived from software contributed to Berkeley by |
9 |
|
|
* Hans Huebner. |
10 |
|
|
* |
11 |
|
|
* Redistribution and use in source and binary forms, with or without |
12 |
|
|
* modification, are permitted provided that the following conditions |
13 |
|
|
* are met: |
14 |
|
|
* 1. Redistributions of source code must retain the above copyright |
15 |
|
|
* notice, this list of conditions and the following disclaimer. |
16 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
17 |
|
|
* notice, this list of conditions and the following disclaimer in the |
18 |
|
|
* documentation and/or other materials provided with the distribution. |
19 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
20 |
|
|
* may be used to endorse or promote products derived from this software |
21 |
|
|
* without specific prior written permission. |
22 |
|
|
* |
23 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
24 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
25 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
26 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
27 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
28 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
29 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
30 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
31 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
32 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
33 |
|
|
* SUCH DAMAGE. |
34 |
|
|
*/ |
35 |
|
|
|
36 |
|
|
#include <sys/types.h> |
37 |
|
|
#include <sys/mman.h> |
38 |
|
|
#include <a.out.h> |
39 |
|
|
#include <elf_abi.h> |
40 |
|
|
#include <ar.h> |
41 |
|
|
#include <ranlib.h> |
42 |
|
|
#include <unistd.h> |
43 |
|
|
#include <err.h> |
44 |
|
|
#include <errno.h> |
45 |
|
|
#include <ctype.h> |
46 |
|
|
#include <link.h> |
47 |
|
|
|
48 |
|
|
#include <stdio.h> |
49 |
|
|
#include <stdlib.h> |
50 |
|
|
#include <string.h> |
51 |
|
|
#include <getopt.h> |
52 |
|
|
#include "util.h" |
53 |
|
|
#include "elfuncs.h" |
54 |
|
|
|
55 |
|
|
#define SYMTABMAG "/ " |
56 |
|
|
#define STRTABMAG "//" |
57 |
|
|
#define SYM64MAG "/SYM64/ " |
58 |
|
|
|
59 |
|
|
union hdr { |
60 |
|
|
Elf32_Ehdr elf32; |
61 |
|
|
Elf64_Ehdr elf64; |
62 |
|
|
}; |
63 |
|
|
|
64 |
|
|
int armap; |
65 |
|
|
int demangle; |
66 |
|
|
int non_object_warning; |
67 |
|
|
int print_only_external_symbols; |
68 |
|
|
int print_only_undefined_symbols; |
69 |
|
|
int print_all_symbols; |
70 |
|
|
int print_file_each_line; |
71 |
|
|
int show_extensions; |
72 |
|
|
int issize; |
73 |
|
|
char posix_fmtstr[6]; |
74 |
|
|
int posix_output; |
75 |
|
|
char posix_radix = 'x'; |
76 |
|
|
int usemmap = 1; |
77 |
|
|
int dynamic_only; |
78 |
|
|
|
79 |
|
|
/* size vars */ |
80 |
|
|
unsigned long total_text, total_data, total_bss, total_total; |
81 |
|
|
int non_object_warning, print_totals; |
82 |
|
|
|
83 |
|
|
int rev; |
84 |
|
|
int fname(const void *, const void *); |
85 |
|
|
int rname(const void *, const void *); |
86 |
|
|
int value(const void *, const void *); |
87 |
|
|
char *otherstring(struct xnlist *); |
88 |
|
|
int (*sfunc)(const void *, const void *) = fname; |
89 |
|
|
char typeletter(struct xnlist *); |
90 |
|
|
int mmbr_name(struct ar_hdr *, char **, int, int *, FILE *); |
91 |
|
|
int show_symtab(off_t, u_long, const char *, FILE *); |
92 |
|
|
int show_symdef(off_t, u_long, const char *, FILE *); |
93 |
|
|
|
94 |
|
|
/* some macros for symbol type (nlist.n_type) handling */ |
95 |
|
|
#define IS_EXTERNAL(x) ((x) & N_EXT) |
96 |
|
|
#define SYMBOL_TYPE(x) ((x) & (N_TYPE | N_STAB)) |
97 |
|
|
|
98 |
|
|
void pipe2cppfilt(void); |
99 |
|
|
void usage(void); |
100 |
|
|
char *symname(struct xnlist *); |
101 |
|
|
int process_file(int, const char *); |
102 |
|
|
int show_archive(int, const char *, FILE *); |
103 |
|
|
int show_file(int, int, const char *, FILE *fp, off_t, union hdr *); |
104 |
|
|
void print_symbol(const char *, struct xnlist *); |
105 |
|
|
|
106 |
|
|
#define OPTSTRING_NM "aABCDegnopPrst:uvw" |
107 |
|
|
const struct option longopts_nm[] = { |
108 |
|
|
{ "debug-syms", no_argument, 0, 'a' }, |
109 |
|
|
{ "demangle", no_argument, 0, 'C' }, |
110 |
|
|
{ "dynamic", no_argument, 0, 'D' }, |
111 |
|
|
{ "extern-only", no_argument, 0, 'g' }, |
112 |
|
|
/* { "line-numbers", no_argument, 0, 'l' }, */ |
113 |
|
|
{ "no-sort", no_argument, 0, 'p' }, |
114 |
|
|
{ "numeric-sort", no_argument, 0, 'n' }, |
115 |
|
|
{ "print-armap", no_argument, 0, 's' }, |
116 |
|
|
{ "print-file-name", no_argument, 0, 'o' }, |
117 |
|
|
{ "reverse-sort", no_argument, 0, 'r' }, |
118 |
|
|
/* { "size-sort", no_argument, &szval, 1 }, */ |
119 |
|
|
{ "undefined-only", no_argument, 0, 'u' }, |
120 |
|
|
{ "help", no_argument, 0, '?' }, |
121 |
|
|
{ NULL } |
122 |
|
|
}; |
123 |
|
|
|
124 |
|
|
/* |
125 |
|
|
* main() |
126 |
|
|
* parse command line, execute process_file() for each file |
127 |
|
|
* specified on the command line. |
128 |
|
|
*/ |
129 |
|
|
int |
130 |
|
|
main(int argc, char *argv[]) |
131 |
|
|
{ |
132 |
|
|
extern char *__progname; |
133 |
|
|
extern int optind; |
134 |
|
|
const char *optstr; |
135 |
|
|
const struct option *lopts; |
136 |
|
|
int ch, eval; |
137 |
|
|
|
138 |
|
|
if (pledge("stdio rpath proc exec wpath cpath", NULL) == -1) |
139 |
|
|
err(1, "pledge"); |
140 |
|
|
|
141 |
|
|
optstr = OPTSTRING_NM; |
142 |
|
|
lopts = longopts_nm; |
143 |
|
|
if (!strcmp(__progname, "size")) { |
144 |
|
|
if (pledge("stdio rpath wpath cpath", NULL) == -1) |
145 |
|
|
err(1, "pledge"); |
146 |
|
|
|
147 |
|
|
issize = 1; |
148 |
|
|
optstr = "tw"; |
149 |
|
|
lopts = NULL; |
150 |
|
|
} |
151 |
|
|
|
152 |
|
|
while ((ch = getopt_long(argc, argv, optstr, lopts, NULL)) != -1) { |
153 |
|
|
switch (ch) { |
154 |
|
|
case 'a': |
155 |
|
|
print_all_symbols = 1; |
156 |
|
|
break; |
157 |
|
|
case 'B': |
158 |
|
|
/* no-op, compat with gnu-nm */ |
159 |
|
|
break; |
160 |
|
|
case 'C': |
161 |
|
|
demangle = 1; |
162 |
|
|
break; |
163 |
|
|
case 'D': |
164 |
|
|
dynamic_only = 1; |
165 |
|
|
break; |
166 |
|
|
case 'e': |
167 |
|
|
show_extensions = 1; |
168 |
|
|
break; |
169 |
|
|
case 'g': |
170 |
|
|
print_only_external_symbols = 1; |
171 |
|
|
break; |
172 |
|
|
case 'n': |
173 |
|
|
case 'v': |
174 |
|
|
sfunc = value; |
175 |
|
|
break; |
176 |
|
|
case 'A': |
177 |
|
|
case 'o': |
178 |
|
|
print_file_each_line = 1; |
179 |
|
|
break; |
180 |
|
|
case 'p': |
181 |
|
|
sfunc = NULL; |
182 |
|
|
break; |
183 |
|
|
case 'P': |
184 |
|
|
posix_output = 1; |
185 |
|
|
break; |
186 |
|
|
case 'r': |
187 |
|
|
rev = 1; |
188 |
|
|
break; |
189 |
|
|
case 's': |
190 |
|
|
armap = 1; |
191 |
|
|
break; |
192 |
|
|
case 'u': |
193 |
|
|
print_only_undefined_symbols = 1; |
194 |
|
|
break; |
195 |
|
|
case 'w': |
196 |
|
|
non_object_warning = 1; |
197 |
|
|
break; |
198 |
|
|
case 't': |
199 |
|
|
if (issize) { |
200 |
|
|
print_totals = 1; |
201 |
|
|
} else { |
202 |
|
|
posix_radix = *optarg; |
203 |
|
|
if (strlen(optarg) != 1 || |
204 |
|
|
(posix_radix != 'd' && posix_radix != 'o' && |
205 |
|
|
posix_radix != 'x')) |
206 |
|
|
usage(); |
207 |
|
|
} |
208 |
|
|
break; |
209 |
|
|
case '?': |
210 |
|
|
default: |
211 |
|
|
usage(); |
212 |
|
|
} |
213 |
|
|
} |
214 |
|
|
|
215 |
|
|
if (posix_output) |
216 |
|
|
(void)snprintf(posix_fmtstr, sizeof posix_fmtstr, "%%%c %%%c", |
217 |
|
|
posix_radix, posix_radix); |
218 |
|
|
if (demangle) |
219 |
|
|
pipe2cppfilt(); |
220 |
|
|
|
221 |
|
|
if (pledge("stdio rpath wpath cpath", NULL) == -1) |
222 |
|
|
err(1, "pledge"); |
223 |
|
|
|
224 |
|
|
argv += optind; |
225 |
|
|
argc -= optind; |
226 |
|
|
|
227 |
|
|
if (rev && sfunc == fname) |
228 |
|
|
sfunc = rname; |
229 |
|
|
|
230 |
|
|
eval = 0; |
231 |
|
|
if (*argv) |
232 |
|
|
do { |
233 |
|
|
eval |= process_file(argc, *argv); |
234 |
|
|
} while (*++argv); |
235 |
|
|
else |
236 |
|
|
eval |= process_file(1, "a.out"); |
237 |
|
|
|
238 |
|
|
if (issize && print_totals) |
239 |
|
|
printf("\n%lu\t%lu\t%lu\t%lu\t%lx\tTOTAL\n", |
240 |
|
|
total_text, total_data, total_bss, |
241 |
|
|
total_total, total_total); |
242 |
|
|
exit(eval); |
243 |
|
|
} |
244 |
|
|
|
245 |
|
|
/* |
246 |
|
|
* process_file() |
247 |
|
|
* show symbols in the file given as an argument. Accepts archive and |
248 |
|
|
* object files as input. |
249 |
|
|
*/ |
250 |
|
|
int |
251 |
|
|
process_file(int count, const char *fname) |
252 |
|
|
{ |
253 |
|
|
union hdr exec_head; |
254 |
|
|
FILE *fp; |
255 |
|
|
int retval; |
256 |
|
|
size_t bytes; |
257 |
|
|
char magic[SARMAG]; |
258 |
|
|
|
259 |
|
|
if (!(fp = fopen(fname, "r"))) { |
260 |
|
|
warn("cannot read %s", fname); |
261 |
|
|
return(1); |
262 |
|
|
} |
263 |
|
|
|
264 |
|
|
if (!issize && count > 1) |
265 |
|
|
(void)printf("\n%s:\n", fname); |
266 |
|
|
|
267 |
|
|
/* |
268 |
|
|
* first check whether this is an object file - read a object |
269 |
|
|
* header, and skip back to the beginning |
270 |
|
|
*/ |
271 |
|
|
bzero(&exec_head, sizeof(exec_head)); |
272 |
|
|
bytes = fread((char *)&exec_head, 1, sizeof(exec_head), fp); |
273 |
|
|
if (bytes < sizeof(exec_head)) { |
274 |
|
|
if (bytes < sizeof(exec_head.elf32) || IS_ELF(exec_head.elf32)) { |
275 |
|
|
warnx("%s: bad format", fname); |
276 |
|
|
(void)fclose(fp); |
277 |
|
|
return(1); |
278 |
|
|
} |
279 |
|
|
} |
280 |
|
|
rewind(fp); |
281 |
|
|
|
282 |
|
|
/* this could be an archive */ |
283 |
|
|
if (!IS_ELF(exec_head.elf32)) { |
284 |
|
|
if (fread(magic, sizeof(magic), (size_t)1, fp) != 1 || |
285 |
|
|
strncmp(magic, ARMAG, SARMAG)) { |
286 |
|
|
warnx("%s: not object file or archive", fname); |
287 |
|
|
(void)fclose(fp); |
288 |
|
|
return(1); |
289 |
|
|
} |
290 |
|
|
retval = show_archive(count, fname, fp); |
291 |
|
|
} else |
292 |
|
|
retval = show_file(count, 1, fname, fp, 0, &exec_head); |
293 |
|
|
(void)fclose(fp); |
294 |
|
|
return(retval); |
295 |
|
|
} |
296 |
|
|
|
297 |
|
|
char *nametab; |
298 |
|
|
|
299 |
|
|
/* |
300 |
|
|
* |
301 |
|
|
* given the archive member header -- produce member name |
302 |
|
|
*/ |
303 |
|
|
int |
304 |
|
|
mmbr_name(struct ar_hdr *arh, char **name, int baselen, int *namelen, FILE *fp) |
305 |
|
|
{ |
306 |
|
|
char *p = *name + strlen(*name); |
307 |
|
|
long i; |
308 |
|
|
|
309 |
|
|
if (nametab && arh->ar_name[0] == '/') { |
310 |
|
|
int len; |
311 |
|
|
|
312 |
|
|
i = atol(&arh->ar_name[1]); |
313 |
|
|
len = strlen(&nametab[i]); |
314 |
|
|
if (len > *namelen) { |
315 |
|
|
p -= (long)*name; |
316 |
|
|
if ((*name = realloc(*name, baselen+len)) == NULL) |
317 |
|
|
err(1, NULL); |
318 |
|
|
*namelen = len; |
319 |
|
|
p += (long)*name; |
320 |
|
|
} |
321 |
|
|
strlcpy(p, &nametab[i], len); |
322 |
|
|
p += len; |
323 |
|
|
} else |
324 |
|
|
#ifdef AR_EFMT1 |
325 |
|
|
/* |
326 |
|
|
* BSD 4.4 extended AR format: #1/<namelen>, with name as the |
327 |
|
|
* first <namelen> bytes of the file |
328 |
|
|
*/ |
329 |
|
|
if ((arh->ar_name[0] == '#') && |
330 |
|
|
(arh->ar_name[1] == '1') && |
331 |
|
|
(arh->ar_name[2] == '/') && |
332 |
|
|
(isdigit((unsigned char)arh->ar_name[3]))) { |
333 |
|
|
int len = atoi(&arh->ar_name[3]); |
334 |
|
|
|
335 |
|
|
if (len > *namelen) { |
336 |
|
|
p -= (long)*name; |
337 |
|
|
if ((*name = realloc(*name, baselen+len)) == NULL) |
338 |
|
|
err(1, NULL); |
339 |
|
|
*namelen = len; |
340 |
|
|
p += (long)*name; |
341 |
|
|
} |
342 |
|
|
if (fread(p, len, 1, fp) != 1) { |
343 |
|
|
warnx("%s: premature EOF", *name); |
344 |
|
|
free(*name); |
345 |
|
|
return(1); |
346 |
|
|
} |
347 |
|
|
p += len; |
348 |
|
|
} else |
349 |
|
|
#endif |
350 |
|
|
for (i = 0; i < sizeof(arh->ar_name); ++i) |
351 |
|
|
if (arh->ar_name[i] && arh->ar_name[i] != ' ') |
352 |
|
|
*p++ = arh->ar_name[i]; |
353 |
|
|
*p = '\0'; |
354 |
|
|
if (p[-1] == '/') |
355 |
|
|
*--p = '\0'; |
356 |
|
|
|
357 |
|
|
return (0); |
358 |
|
|
} |
359 |
|
|
|
360 |
|
|
/* |
361 |
|
|
* show_symtab() |
362 |
|
|
* show archive ranlib index (fs5) |
363 |
|
|
*/ |
364 |
|
|
int |
365 |
|
|
show_symtab(off_t off, u_long len, const char *name, FILE *fp) |
366 |
|
|
{ |
367 |
|
|
struct ar_hdr ar_head; |
368 |
|
|
int *symtab, *ps; |
369 |
|
|
char *strtab, *p; |
370 |
|
|
int num, rval = 0; |
371 |
|
|
int namelen; |
372 |
|
|
off_t restore; |
373 |
|
|
|
374 |
|
|
restore = ftello(fp); |
375 |
|
|
|
376 |
|
|
MMAP(symtab, len, PROT_READ, MAP_PRIVATE|MAP_FILE, fileno(fp), off); |
377 |
|
|
if (symtab == MAP_FAILED) |
378 |
|
|
return (1); |
379 |
|
|
|
380 |
|
|
namelen = sizeof(ar_head.ar_name); |
381 |
|
|
if ((p = malloc(sizeof(ar_head.ar_name))) == NULL) { |
382 |
|
|
warn("%s: malloc", name); |
383 |
|
|
MUNMAP(symtab, len); |
384 |
|
|
} |
385 |
|
|
|
386 |
|
|
printf("\nArchive index:\n"); |
387 |
|
|
num = betoh32(*symtab); |
388 |
|
|
strtab = (char *)(symtab + num + 1); |
389 |
|
|
for (ps = symtab + 1; num--; ps++, strtab += strlen(strtab) + 1) { |
390 |
|
|
if (fseeko(fp, betoh32(*ps), SEEK_SET)) { |
391 |
|
|
warn("%s: fseeko", name); |
392 |
|
|
rval = 1; |
393 |
|
|
break; |
394 |
|
|
} |
395 |
|
|
|
396 |
|
|
if (fread(&ar_head, sizeof(ar_head), 1, fp) != 1 || |
397 |
|
|
memcmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) { |
398 |
|
|
warnx("%s: member fseeko", name); |
399 |
|
|
rval = 1; |
400 |
|
|
break; |
401 |
|
|
} |
402 |
|
|
|
403 |
|
|
*p = '\0'; |
404 |
|
|
if (mmbr_name(&ar_head, &p, 0, &namelen, fp)) { |
405 |
|
|
rval = 1; |
406 |
|
|
break; |
407 |
|
|
} |
408 |
|
|
|
409 |
|
|
printf("%s in %s\n", strtab, p); |
410 |
|
|
} |
411 |
|
|
|
412 |
|
|
fseeko(fp, restore, SEEK_SET); |
413 |
|
|
|
414 |
|
|
free(p); |
415 |
|
|
MUNMAP(symtab, len); |
416 |
|
|
return (rval); |
417 |
|
|
} |
418 |
|
|
|
419 |
|
|
/* |
420 |
|
|
* show_symdef() |
421 |
|
|
* show archive ranlib index (gob) |
422 |
|
|
*/ |
423 |
|
|
int |
424 |
|
|
show_symdef(off_t off, u_long len, const char *name, FILE *fp) |
425 |
|
|
{ |
426 |
|
|
struct ranlib *prn, *eprn; |
427 |
|
|
struct ar_hdr ar_head; |
428 |
|
|
char *symdef; |
429 |
|
|
char *strtab, *p; |
430 |
|
|
u_long size; |
431 |
|
|
int namelen, rval = 0; |
432 |
|
|
|
433 |
|
|
MMAP(symdef, len, PROT_READ, MAP_PRIVATE|MAP_FILE, fileno(fp), off); |
434 |
|
|
if (symdef == MAP_FAILED) |
435 |
|
|
return (1); |
436 |
|
|
if (usemmap) |
437 |
|
|
(void)madvise(symdef, len, MADV_SEQUENTIAL); |
438 |
|
|
|
439 |
|
|
namelen = sizeof(ar_head.ar_name); |
440 |
|
|
if ((p = malloc(sizeof(ar_head.ar_name))) == NULL) { |
441 |
|
|
warn("%s: malloc", name); |
442 |
|
|
MUNMAP(symdef, len); |
443 |
|
|
return (1); |
444 |
|
|
} |
445 |
|
|
|
446 |
|
|
size = *(u_long *)symdef; |
447 |
|
|
prn = (struct ranlib *)(symdef + sizeof(u_long)); |
448 |
|
|
eprn = prn + size / sizeof(*prn); |
449 |
|
|
strtab = symdef + sizeof(u_long) + size + sizeof(u_long); |
450 |
|
|
|
451 |
|
|
printf("\nArchive index:\n"); |
452 |
|
|
for (; prn < eprn; prn++) { |
453 |
|
|
if (fseeko(fp, prn->ran_off, SEEK_SET)) { |
454 |
|
|
warn("%s: fseeko", name); |
455 |
|
|
rval = 1; |
456 |
|
|
break; |
457 |
|
|
} |
458 |
|
|
|
459 |
|
|
if (fread(&ar_head, sizeof(ar_head), 1, fp) != 1 || |
460 |
|
|
memcmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) { |
461 |
|
|
warnx("%s: member fseeko", name); |
462 |
|
|
rval = 1; |
463 |
|
|
break; |
464 |
|
|
} |
465 |
|
|
|
466 |
|
|
*p = '\0'; |
467 |
|
|
if (mmbr_name(&ar_head, &p, 0, &namelen, fp)) { |
468 |
|
|
rval = 1; |
469 |
|
|
break; |
470 |
|
|
} |
471 |
|
|
|
472 |
|
|
printf("%s in %s\n", strtab + prn->ran_un.ran_strx, p); |
473 |
|
|
} |
474 |
|
|
|
475 |
|
|
free(p); |
476 |
|
|
MUNMAP(symdef, len); |
477 |
|
|
return (rval); |
478 |
|
|
} |
479 |
|
|
|
480 |
|
|
/* |
481 |
|
|
* show_archive() |
482 |
|
|
* show symbols in the given archive file |
483 |
|
|
*/ |
484 |
|
|
int |
485 |
|
|
show_archive(int count, const char *fname, FILE *fp) |
486 |
|
|
{ |
487 |
|
|
struct ar_hdr ar_head; |
488 |
|
|
union hdr exec_head; |
489 |
|
|
int i, rval; |
490 |
|
|
off_t last_ar_off, foff, symtaboff; |
491 |
|
|
char *name; |
492 |
|
|
int baselen, namelen; |
493 |
|
|
u_long mmbrlen, symtablen; |
494 |
|
|
|
495 |
|
|
baselen = strlen(fname) + 3; |
496 |
|
|
if (posix_output) |
497 |
|
|
baselen += 2; |
498 |
|
|
namelen = sizeof(ar_head.ar_name); |
499 |
|
|
if ((name = malloc(baselen + namelen)) == NULL) |
500 |
|
|
err(1, NULL); |
501 |
|
|
|
502 |
|
|
rval = 0; |
503 |
|
|
nametab = NULL; |
504 |
|
|
symtaboff = 0; |
505 |
|
|
symtablen = 0; |
506 |
|
|
|
507 |
|
|
/* while there are more entries in the archive */ |
508 |
|
|
while (fread(&ar_head, sizeof(ar_head), 1, fp) == 1) { |
509 |
|
|
/* bad archive entry - stop processing this archive */ |
510 |
|
|
if (memcmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) { |
511 |
|
|
warnx("%s: bad format archive header", fname); |
512 |
|
|
rval = 1; |
513 |
|
|
break; |
514 |
|
|
} |
515 |
|
|
|
516 |
|
|
/* remember start position of current archive object */ |
517 |
|
|
last_ar_off = ftello(fp); |
518 |
|
|
mmbrlen = atol(ar_head.ar_size); |
519 |
|
|
|
520 |
|
|
if (strncmp(ar_head.ar_name, RANLIBMAG, |
521 |
|
|
sizeof(RANLIBMAG) - 1) == 0) { |
522 |
|
|
if (!issize && armap && |
523 |
|
|
show_symdef(last_ar_off, mmbrlen, fname, fp)) { |
524 |
|
|
rval = 1; |
525 |
|
|
break; |
526 |
|
|
} |
527 |
|
|
goto skip; |
528 |
|
|
} else if (strncmp(ar_head.ar_name, SYMTABMAG, |
529 |
|
|
sizeof(SYMTABMAG) - 1) == 0) { |
530 |
|
|
/* if nametab hasn't been seen yet -- doit later */ |
531 |
|
|
if (!nametab) { |
532 |
|
|
symtablen = mmbrlen; |
533 |
|
|
symtaboff = last_ar_off; |
534 |
|
|
goto skip; |
535 |
|
|
} |
536 |
|
|
|
537 |
|
|
/* load the Sys5 long names table */ |
538 |
|
|
} else if (strncmp(ar_head.ar_name, STRTABMAG, |
539 |
|
|
sizeof(STRTABMAG) - 1) == 0) { |
540 |
|
|
char *p; |
541 |
|
|
|
542 |
|
|
if ((nametab = malloc(mmbrlen)) == NULL) { |
543 |
|
|
warn("%s: nametab", fname); |
544 |
|
|
rval = 1; |
545 |
|
|
break; |
546 |
|
|
} |
547 |
|
|
|
548 |
|
|
if (fread(nametab, mmbrlen, (size_t)1, fp) != 1) { |
549 |
|
|
warnx("%s: premature EOF", fname); |
550 |
|
|
rval = 1; |
551 |
|
|
break; |
552 |
|
|
} |
553 |
|
|
|
554 |
|
|
for (p = nametab, i = mmbrlen; i--; p++) |
555 |
|
|
if (*p == '\n') |
556 |
|
|
*p = '\0'; |
557 |
|
|
|
558 |
|
|
if (issize || !armap || !symtablen || !symtaboff) |
559 |
|
|
goto skip; |
560 |
|
|
} |
561 |
|
|
#ifdef __mips64 |
562 |
|
|
else if (memcmp(ar_head.ar_name, SYM64MAG, |
563 |
|
|
sizeof(ar_head.ar_name)) == 0) { |
564 |
|
|
/* IRIX6-compatible archive map */ |
565 |
|
|
goto skip; |
566 |
|
|
} |
567 |
|
|
#endif |
568 |
|
|
|
569 |
|
|
if (!issize && armap && symtablen && symtaboff) { |
570 |
|
|
if (show_symtab(symtaboff, symtablen, fname, fp)) { |
571 |
|
|
rval = 1; |
572 |
|
|
break; |
573 |
|
|
} else { |
574 |
|
|
symtaboff = 0; |
575 |
|
|
symtablen = 0; |
576 |
|
|
} |
577 |
|
|
} |
578 |
|
|
|
579 |
|
|
/* |
580 |
|
|
* construct a name of the form "archive.a:obj.o:" for the |
581 |
|
|
* current archive entry if the object name is to be printed |
582 |
|
|
* on each output line |
583 |
|
|
*/ |
584 |
|
|
*name = '\0'; |
585 |
|
|
if (posix_output) |
586 |
|
|
snprintf(name, baselen - 1, "%s[", fname); |
587 |
|
|
else if (count > 1) |
588 |
|
|
snprintf(name, baselen - 1, "%s:", fname); |
589 |
|
|
|
590 |
|
|
if (mmbr_name(&ar_head, &name, baselen, &namelen, fp)) { |
591 |
|
|
rval = 1; |
592 |
|
|
break; |
593 |
|
|
} |
594 |
|
|
|
595 |
|
|
if (posix_output) |
596 |
|
|
strlcat(name, "]", baselen + namelen); |
597 |
|
|
|
598 |
|
|
foff = ftello(fp); |
599 |
|
|
|
600 |
|
|
/* get and check current object's header */ |
601 |
|
|
if (fread((char *)&exec_head, sizeof(exec_head), |
602 |
|
|
(size_t)1, fp) != 1) { |
603 |
|
|
warnx("%s: premature EOF", fname); |
604 |
|
|
rval = 1; |
605 |
|
|
break; |
606 |
|
|
} |
607 |
|
|
|
608 |
|
|
rval |= show_file(2, non_object_warning, name, fp, foff, &exec_head); |
609 |
|
|
/* |
610 |
|
|
* skip to next archive object - it starts at the next |
611 |
|
|
* even byte boundary |
612 |
|
|
*/ |
613 |
|
|
#define even(x) (((x) + 1) & ~1) |
614 |
|
|
skip: if (fseeko(fp, last_ar_off + even(mmbrlen), SEEK_SET)) { |
615 |
|
|
warn("%s", fname); |
616 |
|
|
rval = 1; |
617 |
|
|
break; |
618 |
|
|
} |
619 |
|
|
} |
620 |
|
|
free(nametab); |
621 |
|
|
nametab = NULL; |
622 |
|
|
free(name); |
623 |
|
|
return(rval); |
624 |
|
|
} |
625 |
|
|
|
626 |
|
|
char *stab; |
627 |
|
|
|
628 |
|
|
/* |
629 |
|
|
* show_file() |
630 |
|
|
* show symbols from the object file pointed to by fp. The current |
631 |
|
|
* file pointer for fp is expected to be at the beginning of an object |
632 |
|
|
* file header. |
633 |
|
|
*/ |
634 |
|
|
int |
635 |
|
|
show_file(int count, int warn_fmt, const char *name, FILE *fp, off_t foff, union hdr *head) |
636 |
|
|
{ |
637 |
|
|
u_long text, data, bss, total; |
638 |
|
|
struct xnlist *np, *names, **snames; |
639 |
|
|
int i, nrawnames, nnames; |
640 |
|
|
size_t stabsize; |
641 |
|
|
|
642 |
|
|
if (IS_ELF(head->elf32) && |
643 |
|
|
head->elf32.e_ident[EI_CLASS] == ELFCLASS32 && |
644 |
|
|
head->elf32.e_ident[EI_VERSION] == ELF_TARG_VER) { |
645 |
|
|
void *shdr; |
646 |
|
|
|
647 |
|
|
if (!(shdr = elf32_load_shdrs(name, fp, foff, &head->elf32))) |
648 |
|
|
return (1); |
649 |
|
|
|
650 |
|
|
i = issize? |
651 |
|
|
elf32_size(&head->elf32, shdr, &text, &data, &bss) : |
652 |
|
|
elf32_symload(name, fp, foff, &head->elf32, shdr, |
653 |
|
|
&names, &snames, &stabsize, &nrawnames); |
654 |
|
|
free(shdr); |
655 |
|
|
if (i) |
656 |
|
|
return (i); |
657 |
|
|
|
658 |
|
|
} else if (IS_ELF(head->elf64) && |
659 |
|
|
head->elf64.e_ident[EI_CLASS] == ELFCLASS64 && |
660 |
|
|
head->elf64.e_ident[EI_VERSION] == ELF_TARG_VER) { |
661 |
|
|
void *shdr; |
662 |
|
|
|
663 |
|
|
if (!(shdr = elf64_load_shdrs(name, fp, foff, &head->elf64))) |
664 |
|
|
return (1); |
665 |
|
|
|
666 |
|
|
i = issize? |
667 |
|
|
elf64_size(&head->elf64, shdr, &text, &data, &bss) : |
668 |
|
|
elf64_symload(name, fp, foff, &head->elf64, shdr, |
669 |
|
|
&names, &snames, &stabsize, &nrawnames); |
670 |
|
|
free(shdr); |
671 |
|
|
if (i) |
672 |
|
|
return (i); |
673 |
|
|
} else { |
674 |
|
|
if (warn_fmt) |
675 |
|
|
warnx("%s: bad format", name); |
676 |
|
|
return (1); |
677 |
|
|
} |
678 |
|
|
|
679 |
|
|
if (issize) { |
680 |
|
|
static int first = 1; |
681 |
|
|
|
682 |
|
|
if (first) { |
683 |
|
|
first = 0; |
684 |
|
|
printf("text\tdata\tbss\tdec\thex\n"); |
685 |
|
|
} |
686 |
|
|
|
687 |
|
|
total = text + data + bss; |
688 |
|
|
printf("%lu\t%lu\t%lu\t%lu\t%lx", |
689 |
|
|
text, data, bss, total, total); |
690 |
|
|
if (count > 1) |
691 |
|
|
(void)printf("\t%s", name); |
692 |
|
|
|
693 |
|
|
total_text += text; |
694 |
|
|
total_data += data; |
695 |
|
|
total_bss += bss; |
696 |
|
|
total_total += total; |
697 |
|
|
|
698 |
|
|
printf("\n"); |
699 |
|
|
return (0); |
700 |
|
|
} |
701 |
|
|
/* else we are nm */ |
702 |
|
|
|
703 |
|
|
/* |
704 |
|
|
* it seems that string table is sequential |
705 |
|
|
* relative to the symbol table order |
706 |
|
|
*/ |
707 |
|
|
if (sfunc == NULL && usemmap) |
708 |
|
|
(void)madvise(stab, stabsize, MADV_SEQUENTIAL); |
709 |
|
|
|
710 |
|
|
/* |
711 |
|
|
* fix up the symbol table and filter out unwanted entries |
712 |
|
|
* |
713 |
|
|
* common symbols are characterized by a n_type of N_UNDF and a |
714 |
|
|
* non-zero n_value -- change n_type to N_COMM for all such |
715 |
|
|
* symbols to make life easier later. |
716 |
|
|
* |
717 |
|
|
* filter out all entries which we don't want to print anyway |
718 |
|
|
*/ |
719 |
|
|
for (np = names, i = nnames = 0; i < nrawnames; np++, i++) { |
720 |
|
|
/* |
721 |
|
|
* make n_un.n_name a character pointer by adding the string |
722 |
|
|
* table's base to n_un.n_strx |
723 |
|
|
* |
724 |
|
|
* don't mess with zero offsets |
725 |
|
|
*/ |
726 |
|
|
if (np->nl.n_un.n_strx) |
727 |
|
|
np->nl.n_un.n_name = stab + np->nl.n_un.n_strx; |
728 |
|
|
else |
729 |
|
|
np->nl.n_un.n_name = ""; |
730 |
|
|
if (print_only_external_symbols && !IS_EXTERNAL(np->nl.n_type)) |
731 |
|
|
continue; |
732 |
|
|
if (print_only_undefined_symbols && |
733 |
|
|
SYMBOL_TYPE(np->nl.n_type) != N_UNDF) |
734 |
|
|
continue; |
735 |
|
|
|
736 |
|
|
snames[nnames++] = np; |
737 |
|
|
} |
738 |
|
|
|
739 |
|
|
/* sort the symbol table if applicable */ |
740 |
|
|
if (sfunc) |
741 |
|
|
qsort(snames, (size_t)nnames, sizeof(*snames), sfunc); |
742 |
|
|
|
743 |
|
|
if (count > 1) |
744 |
|
|
(void)printf("\n%s:\n", name); |
745 |
|
|
|
746 |
|
|
/* print out symbols */ |
747 |
|
|
for (i = 0; i < nnames; i++) |
748 |
|
|
print_symbol(name, snames[i]); |
749 |
|
|
|
750 |
|
|
free(snames); |
751 |
|
|
free(names); |
752 |
|
|
MUNMAP(stab, stabsize); |
753 |
|
|
return(0); |
754 |
|
|
} |
755 |
|
|
|
756 |
|
|
char * |
757 |
|
|
symname(struct xnlist *sym) |
758 |
|
|
{ |
759 |
|
|
return sym->nl.n_un.n_name; |
760 |
|
|
} |
761 |
|
|
|
762 |
|
|
/* |
763 |
|
|
* print_symbol() |
764 |
|
|
* show one symbol |
765 |
|
|
*/ |
766 |
|
|
void |
767 |
|
|
print_symbol(const char *name, struct xnlist *sym) |
768 |
|
|
{ |
769 |
|
|
if (print_file_each_line) { |
770 |
|
|
if (posix_output) |
771 |
|
|
(void)printf("%s: ", name); |
772 |
|
|
else |
773 |
|
|
(void)printf("%s:", name); |
774 |
|
|
} |
775 |
|
|
|
776 |
|
|
if (posix_output) { |
777 |
|
|
(void)printf("%s %c ", symname(sym), typeletter(sym)); |
778 |
|
|
if (SYMBOL_TYPE(sym->nl.n_type) != N_UNDF) |
779 |
|
|
(void)printf(posix_fmtstr, sym->nl.n_value, |
780 |
|
|
sym->n_size); |
781 |
|
|
(void)printf("\n"); |
782 |
|
|
} else { |
783 |
|
|
/* |
784 |
|
|
* handle undefined-only format especially (no space is |
785 |
|
|
* left for symbol values, no type field is printed) |
786 |
|
|
*/ |
787 |
|
|
if (!print_only_undefined_symbols) { |
788 |
|
|
/* print symbol's value */ |
789 |
|
|
if (SYMBOL_TYPE(sym->nl.n_type) == N_UNDF) |
790 |
|
|
(void)printf(" "); |
791 |
|
|
else |
792 |
|
|
(void)printf("%08lx", sym->nl.n_value); |
793 |
|
|
|
794 |
|
|
/* print type information */ |
795 |
|
|
if (show_extensions) |
796 |
|
|
(void)printf(" %c ", typeletter(sym)); |
797 |
|
|
else |
798 |
|
|
(void)printf(" %c ", typeletter(sym)); |
799 |
|
|
} |
800 |
|
|
|
801 |
|
|
(void)puts(symname(sym)); |
802 |
|
|
} |
803 |
|
|
} |
804 |
|
|
|
805 |
|
|
/* |
806 |
|
|
* typeletter() |
807 |
|
|
* return a description letter for the given basic type code of an |
808 |
|
|
* symbol table entry. The return value will be upper case for |
809 |
|
|
* external, lower case for internal symbols. |
810 |
|
|
*/ |
811 |
|
|
char |
812 |
|
|
typeletter(struct xnlist *np) |
813 |
|
|
{ |
814 |
|
|
int ext = IS_EXTERNAL(np->nl.n_type); |
815 |
|
|
|
816 |
|
|
if (np->nl.n_other) |
817 |
|
|
return np->nl.n_other; |
818 |
|
|
|
819 |
|
|
switch(SYMBOL_TYPE(np->nl.n_type)) { |
820 |
|
|
case N_ABS: |
821 |
|
|
return(ext? 'A' : 'a'); |
822 |
|
|
case N_BSS: |
823 |
|
|
return(ext? 'B' : 'b'); |
824 |
|
|
case N_COMM: |
825 |
|
|
return(ext? 'C' : 'c'); |
826 |
|
|
case N_DATA: |
827 |
|
|
return(ext? 'D' : 'd'); |
828 |
|
|
case N_FN: |
829 |
|
|
/* NOTE: N_FN == N_WARNING, |
830 |
|
|
* in this case, the N_EXT bit is to considered as |
831 |
|
|
* part of the symbol's type itself. |
832 |
|
|
*/ |
833 |
|
|
return(ext? 'F' : 'W'); |
834 |
|
|
case N_TEXT: |
835 |
|
|
return(ext? 'T' : 't'); |
836 |
|
|
case N_SIZE: |
837 |
|
|
return(ext? 'S' : 's'); |
838 |
|
|
case N_UNDF: |
839 |
|
|
return(ext? 'U' : 'u'); |
840 |
|
|
} |
841 |
|
|
return('?'); |
842 |
|
|
} |
843 |
|
|
|
844 |
|
|
int |
845 |
|
|
fname(const void *a0, const void *b0) |
846 |
|
|
{ |
847 |
|
|
struct xnlist * const *a = a0, * const *b = b0; |
848 |
|
|
|
849 |
|
|
return(strcmp((*a)->nl.n_un.n_name, (*b)->nl.n_un.n_name)); |
850 |
|
|
} |
851 |
|
|
|
852 |
|
|
int |
853 |
|
|
rname(const void *a0, const void *b0) |
854 |
|
|
{ |
855 |
|
|
struct xnlist * const *a = a0, * const *b = b0; |
856 |
|
|
|
857 |
|
|
return(strcmp((*b)->nl.n_un.n_name, (*a)->nl.n_un.n_name)); |
858 |
|
|
} |
859 |
|
|
|
860 |
|
|
int |
861 |
|
|
value(const void *a0, const void *b0) |
862 |
|
|
{ |
863 |
|
|
struct xnlist * const *a = a0, * const *b = b0; |
864 |
|
|
|
865 |
|
|
if (SYMBOL_TYPE((*a)->nl.n_type) == N_UNDF) |
866 |
|
|
if (SYMBOL_TYPE((*b)->nl.n_type) == N_UNDF) |
867 |
|
|
return(0); |
868 |
|
|
else |
869 |
|
|
return(-1); |
870 |
|
|
else if (SYMBOL_TYPE((*b)->nl.n_type) == N_UNDF) |
871 |
|
|
return(1); |
872 |
|
|
if (rev) { |
873 |
|
|
if ((*a)->nl.n_value == (*b)->nl.n_value) |
874 |
|
|
return(rname(a0, b0)); |
875 |
|
|
return((*b)->nl.n_value > (*a)->nl.n_value ? 1 : -1); |
876 |
|
|
} else { |
877 |
|
|
if ((*a)->nl.n_value == (*b)->nl.n_value) |
878 |
|
|
return(fname(a0, b0)); |
879 |
|
|
return((*a)->nl.n_value > (*b)->nl.n_value ? 1 : -1); |
880 |
|
|
} |
881 |
|
|
} |
882 |
|
|
|
883 |
|
|
#define CPPFILT "/usr/bin/c++filt" |
884 |
|
|
|
885 |
|
|
void |
886 |
|
|
pipe2cppfilt(void) |
887 |
|
|
{ |
888 |
|
|
int pip[2]; |
889 |
|
|
char *argv[2]; |
890 |
|
|
|
891 |
|
|
argv[0] = "c++filt"; |
892 |
|
|
argv[1] = NULL; |
893 |
|
|
|
894 |
|
|
if (pipe(pip) == -1) |
895 |
|
|
err(1, "pipe"); |
896 |
|
|
switch(fork()) { |
897 |
|
|
case -1: |
898 |
|
|
err(1, "fork"); |
899 |
|
|
default: |
900 |
|
|
dup2(pip[0], 0); |
901 |
|
|
close(pip[0]); |
902 |
|
|
close(pip[1]); |
903 |
|
|
execve(CPPFILT, argv, NULL); |
904 |
|
|
err(1, "execve"); |
905 |
|
|
case 0: |
906 |
|
|
dup2(pip[1], 1); |
907 |
|
|
close(pip[1]); |
908 |
|
|
close(pip[0]); |
909 |
|
|
} |
910 |
|
|
} |
911 |
|
|
|
912 |
|
|
void |
913 |
|
|
usage(void) |
914 |
|
|
{ |
915 |
|
|
extern char *__progname; |
916 |
|
|
|
917 |
|
|
if (issize) |
918 |
|
|
fprintf(stderr, "usage: %s [-tw] [file ...]\n", __progname); |
919 |
|
|
else |
920 |
|
|
fprintf(stderr, "usage: %s [-AaCDegnoPprsuw] [-t d|o|x] [file ...]\n", |
921 |
|
|
__progname); |
922 |
|
|
exit(1); |
923 |
|
|
} |