1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (C) 1996-1997 John D. Polstra. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY JOHN D. POLSTRA AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL JOHN D. POLSTRA OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/types.h>
32 #include <sys/mman.h>
33 #include <sys/stat.h>
34
35 #include <machine/elf.h>
36
37 #include <arpa/inet.h>
38
39 #include <a.out.h>
40 #include <assert.h>
41 #include <ctype.h>
42 #include <err.h>
43 #include <fcntl.h>
44 #include <sys/link_aout.h>
45 #include <stab.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50
51 #include "extern.h"
52
53 #define PAGE_SIZE 4096 /* i386 specific */
54
55 #ifndef N_SETA
56 #define N_SETA 0x14 /* Absolute set element symbol */
57 #endif /* This is input to LD, in a .o file. */
58
59 #ifndef N_SETT
60 #define N_SETT 0x16 /* Text set element symbol */
61 #endif /* This is input to LD, in a .o file. */
62
63 #ifndef N_SETD
64 #define N_SETD 0x18 /* Data set element symbol */
65 #endif /* This is input to LD, in a .o file. */
66
67 #ifndef N_SETB
68 #define N_SETB 0x1A /* Bss set element symbol */
69 #endif /* This is input to LD, in a .o file. */
70
71 #ifndef N_SETV
72 #define N_SETV 0x1C /* Pointer to set vector in data area. */
73 #endif /* This is output from LD. */
74
75 #ifdef STANDALONE
76 static
77 #endif
78
79 static void dump_rels(const char *, const struct relocation_info *,
80 unsigned long, const char *(*)(unsigned long), unsigned char *);
81 static void dump_segs(void);
82 static void dump_sods(void);
83 static void dump_sym(const struct nlist *);
84 static void dump_syms(void);
85
86 static void dump_rtsyms(void);
87
88 static const char *rtsym_name(unsigned long);
89 static const char *sym_name(unsigned long);
90
91 #ifdef STANDALONE
92 static
93 #endif
94 int error_count;
95
96 /*
97 * Variables ending in _base are pointers to things in our address space,
98 * i.e., in the file itself.
99 *
100 * Variables ending in _addr are adjusted according to where things would
101 * actually appear in memory if the file were loaded.
102 */
103 static const char *file_base;
104 static const char *text_base;
105 static const char *data_base;
106 static const struct relocation_info *rel_base;
107 static const struct nlist *sym_base;
108 static const char *str_base;
109
110 static const struct relocation_info *rtrel_base;
111 static const struct nzlist *rtsym_base;
112 static const char *rtstr_base;
113
114 static const struct exec *ex;
115 static const struct _dynamic *dyn;
116 static const struct section_dispatch_table *sdt;
117
118 static const char *text_addr;
119 static const char *data_addr;
120
121 static unsigned long rel_count;
122 static unsigned long sym_count;
123
124 static unsigned long rtrel_count;
125 static unsigned long rtsym_count;
126
127 /* Dynamically allocated flags, 1 byte per symbol, to record whether each
128 symbol was referenced by a relocation entry. */
129 static unsigned char *sym_used;
130 static unsigned char *rtsym_used;
131
132 static unsigned long origin; /* What values are relocated relative to */
133
134 #ifdef STANDALONE
135 int
main(int argc,char * argv[])136 main(int argc, char *argv[])
137 {
138 int i;
139
140 for (i = 1; i < argc; ++i)
141 dump_file(argv[i]);
142
143 return error_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
144 }
145 #endif
146
align_struct(const void * expr)147 static inline const void *align_struct(const void *expr)
148 {
149 assert(!(((int)expr) & 3));
150 return expr;
151 }
152
align_long(const void * expr)153 static inline const void *align_long(const void *expr)
154 {
155 assert(!(((int)expr) & 3));
156 return expr;
157 }
158
align_short(const void * expr)159 static inline const void *align_short(const void *expr)
160 {
161 assert(!(((int)expr) & 1));
162 return expr;
163 }
164
165 #ifdef STANDALONE
166 static
167 #endif
168 void
dump_file(const char * fname)169 dump_file(const char *fname)
170 {
171 int fd;
172 struct stat sb;
173 caddr_t objbase;
174
175 if (stat(fname, &sb) == -1) {
176 warnx("cannot stat \"%s\"", fname);
177 ++error_count;
178 return;
179 }
180
181 if ((sb.st_mode & S_IFMT) != S_IFREG) {
182 warnx("\"%s\" is not a regular file", fname);
183 ++error_count;
184 return;
185 }
186
187 if ((fd = open(fname, O_RDONLY, 0)) == -1) {
188 warnx("cannot open \"%s\"", fname);
189 ++error_count;
190 return;
191 }
192
193 objbase = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
194 if (objbase == (caddr_t) -1) {
195 warnx("cannot mmap \"%s\"", fname);
196 ++error_count;
197 close(fd);
198 return;
199 }
200
201 close(fd);
202
203 file_base = (const char *) objbase; /* Makes address arithmetic easier */
204
205 if (IS_ELF(*(const Elf32_Ehdr*) align_struct(file_base))) {
206 warnx("%s: this is an ELF program; use readelf to examine", fname);
207 ++error_count;
208 munmap(objbase, sb.st_size);
209 return;
210 }
211
212 ex = (const struct exec *) align_struct(file_base);
213
214 printf("%s: a_midmag = 0x%lx\n", fname, (long)ex->a_midmag);
215 printf(" magic = 0x%lx = 0%lo, netmagic = 0x%lx = 0%lo\n",
216 (long)N_GETMAGIC(*ex), (long)N_GETMAGIC(*ex),
217 (long)N_GETMAGIC_NET(*ex), (long)N_GETMAGIC_NET(*ex));
218
219 if (N_BADMAG(*ex)) {
220 warnx("%s: bad magic number", fname);
221 ++error_count;
222 munmap(objbase, sb.st_size);
223 return;
224 }
225
226 printf(" a_text = 0x%lx\n", (long)ex->a_text);
227 printf(" a_data = 0x%lx\n", (long)ex->a_data);
228 printf(" a_bss = 0x%lx\n", (long)ex->a_bss);
229 printf(" a_syms = 0x%lx\n", (long)ex->a_syms);
230 printf(" a_entry = 0x%lx\n", (long)ex->a_entry);
231 printf(" a_trsize = 0x%lx\n", (long)ex->a_trsize);
232 printf(" a_drsize = 0x%lx\n", (long)ex->a_drsize);
233
234 text_base = file_base + N_TXTOFF(*ex);
235 data_base = file_base + N_DATOFF(*ex);
236 rel_base = (const struct relocation_info *)
237 align_struct(file_base + N_RELOFF(*ex));
238 sym_base = (const struct nlist *) align_struct(file_base + N_SYMOFF(*ex));
239 str_base = file_base + N_STROFF(*ex);
240
241 rel_count = (ex->a_trsize + ex->a_drsize) / sizeof rel_base[0];
242 assert(rel_count * sizeof rel_base[0] == ex->a_trsize + ex->a_drsize);
243 sym_count = ex->a_syms / sizeof sym_base[0];
244 assert(sym_count * sizeof sym_base[0] == ex->a_syms);
245
246 if (sym_count != 0) {
247 sym_used = (unsigned char *) calloc(sym_count, sizeof(unsigned char));
248 assert(sym_used != NULL);
249 }
250
251 printf(" Entry = 0x%lx\n", (long)ex->a_entry);
252 printf(" Text offset = %x, address = %lx\n", N_TXTOFF(*ex),
253 (long)N_TXTADDR(*ex));
254 printf(" Data offset = %lx, address = %lx\n", (long)N_DATOFF(*ex),
255 (long)N_DATADDR(*ex));
256
257 /*
258 * In an executable program file, everything is relocated relative to
259 * the assumed run-time load address, i.e., N_TXTADDR(*ex), i.e., 0x1000.
260 *
261 * In a shared library file, everything is relocated relative to the
262 * start of the file, i.e., N_TXTOFF(*ex), i.e., 0.
263 *
264 * The way to tell the difference is by looking at ex->a_entry. If it
265 * is >= 0x1000, then we have an executable program. Otherwise, we
266 * have a shared library.
267 *
268 * When a program is executed, the entire file is mapped into memory,
269 * including the a.out header and so forth. But it is not mapped at
270 * address 0; rather it is mapped at address 0x1000. The first page
271 * of the user's address space is left unmapped in order to catch null
272 * pointer dereferences.
273 *
274 * In this program, when we map in an executable program, we have to
275 * simulate the empty page by decrementing our assumed base address by
276 * a pagesize.
277 */
278
279 text_addr = text_base;
280 data_addr = data_base;
281 origin = 0;
282
283 if (ex->a_entry >= PAGE_SIZE) { /* Executable, not a shared library */
284 /*
285 * The fields in the object have already been relocated on the
286 * assumption that the object will be loaded at N_TXTADDR(*ex).
287 * We have to compensate for that.
288 */
289 text_addr -= PAGE_SIZE;
290 data_addr -= PAGE_SIZE;
291 origin = PAGE_SIZE;
292 printf(" Program, origin = %lx\n", origin);
293 } else if (N_GETFLAG(*ex) & EX_DYNAMIC)
294 printf(" Shared library, origin = %lx\n", origin);
295 else
296 printf(" Object file, origin = %lx\n", origin);
297
298 if (N_GETFLAG(*ex) & EX_DYNAMIC) {
299 dyn = (const struct _dynamic *) align_struct(data_base);
300 printf(" Dynamic version = %d\n", dyn->d_version);
301
302 sdt = (const struct section_dispatch_table *)
303 align_struct(text_addr + (unsigned long) dyn->d_un.d_sdt);
304
305 rtrel_base = (const struct relocation_info *)
306 align_struct(text_addr + sdt->sdt_rel);
307 rtrel_count = (sdt->sdt_hash - sdt->sdt_rel) / sizeof rtrel_base[0];
308 assert(rtrel_count * sizeof rtrel_base[0] ==
309 (size_t)(sdt->sdt_hash - sdt->sdt_rel));
310
311 rtsym_base = (const struct nzlist *)
312 align_struct(text_addr + sdt->sdt_nzlist);
313 rtsym_count = (sdt->sdt_strings - sdt->sdt_nzlist) /
314 sizeof rtsym_base[0];
315 assert(rtsym_count * sizeof rtsym_base[0] ==
316 (size_t)(sdt->sdt_strings - sdt->sdt_nzlist));
317
318 if (rtsym_count != 0) {
319 rtsym_used = (unsigned char *) calloc(rtsym_count,
320 sizeof(unsigned char));
321 assert(rtsym_used != NULL);
322 }
323
324 rtstr_base = text_addr + sdt->sdt_strings;
325 }
326
327 dump_segs();
328 dump_sods();
329 dump_rels("Relocations", rel_base, rel_count, sym_name, sym_used);
330 dump_syms();
331
332 dump_rels("Run-time relocations", rtrel_base, rtrel_count, rtsym_name,
333 rtsym_used);
334 dump_rtsyms();
335
336 if (rtsym_used != NULL) {
337 free(rtsym_used);
338 rtsym_used = NULL;
339 }
340 if (sym_used != NULL) {
341 free(sym_used);
342 sym_used = NULL;
343 }
344 munmap(objbase, sb.st_size);
345 }
346
347 static void
dump_rels(const char * label,const struct relocation_info * base,unsigned long count,const char * (* name)(unsigned long),unsigned char * sym_used_flags)348 dump_rels(const char *label, const struct relocation_info *base,
349 unsigned long count, const char *(*name)(unsigned long),
350 unsigned char *sym_used_flags)
351 {
352 unsigned long i;
353
354 printf(" %s:\n", label);
355 for (i = 0; i < count; ++i) {
356 const struct relocation_info *r = &base[i];
357 unsigned int size;
358 char contents[16];
359
360 size = 1u << r->r_length;
361
362 if (origin <= (unsigned long)r->r_address
363 && (unsigned long)r->r_address < origin + ex->a_text + ex->a_data
364 && 1 <= size && size <= 4) {
365 /*
366 * XXX - This can cause unaligned accesses. OK for the
367 * i386, not so for other architectures.
368 */
369 switch (size) {
370 case 1:
371 snprintf(contents, sizeof contents, " [%02x]",
372 *(unsigned const char *)(text_addr + r->r_address));
373 break;
374 case 2:
375 snprintf(contents, sizeof contents, " [%04x]",
376 *(unsigned const short *)
377 align_short(text_addr + r->r_address));
378 break;
379 case 4:
380 snprintf(contents, sizeof contents, "[%08lx]",
381 *(unsigned const long *)
382 align_long(text_addr + r->r_address));
383 break;
384 }
385 } else
386 snprintf(contents, sizeof contents, " ");
387
388 printf(" %6lu %8x/%u %s %c%c%c%c%c%c", i,
389 r->r_address, size,
390 contents,
391 r->r_extern ? 'e' : '-',
392 r->r_jmptable ? 'j' : '-',
393 r->r_relative ? 'r' : '-',
394 r->r_baserel ? 'b' : '-',
395 r->r_pcrel ? 'p' : '-',
396 r->r_copy ? 'c' : '-');
397
398 if (r->r_extern || r->r_baserel || r->r_jmptable || r->r_copy) {
399 printf(" %4u %s", r->r_symbolnum, name(r->r_symbolnum));
400 sym_used_flags[r->r_symbolnum] = 1;
401 }
402
403 printf("\n");
404 }
405 }
406
407 static void
dump_rtsyms(void)408 dump_rtsyms(void)
409 {
410 unsigned long i;
411
412 printf(" Run-time symbols:\n");
413 for (i = 0; i < rtsym_count; ++i) {
414 printf(" %6lu%c ", i, rtsym_used[i] ? '*' : ' ');
415 dump_sym(&rtsym_base[i].nlist);
416 printf("/%-5ld %s\n", rtsym_base[i].nz_size, rtsym_name(i));
417 }
418 }
419
420 static void
dump_segs(void)421 dump_segs(void)
422 {
423 printf(" Text segment starts at address %lx\n", origin + N_TXTOFF(*ex));
424 if (N_GETFLAG(*ex) & EX_DYNAMIC) {
425 printf(" rel starts at %lx\n", sdt->sdt_rel);
426 printf(" hash starts at %lx\n", sdt->sdt_hash);
427 printf(" nzlist starts at %lx\n", sdt->sdt_nzlist);
428 printf(" strings starts at %lx\n", sdt->sdt_strings);
429 }
430
431 printf(" Data segment starts at address %lx\n", origin + N_DATOFF(*ex));
432 if (N_GETFLAG(*ex) & EX_DYNAMIC) {
433 printf(" _dynamic starts at %lx\n", origin + N_DATOFF(*ex));
434 printf(" so_debug starts at %lx\n", (unsigned long) dyn->d_debug);
435 printf(" sdt starts at %lx\n", (unsigned long) dyn->d_un.d_sdt);
436 printf(" got starts at %lx\n", sdt->sdt_got);
437 printf(" plt starts at %lx\n", sdt->sdt_plt);
438 printf(" rest of stuff starts at %lx\n",
439 sdt->sdt_plt + sdt->sdt_plt_sz);
440 }
441 }
442
443 static void
dump_sods(void)444 dump_sods(void)
445 {
446 long sod_offset;
447 long paths_offset;
448
449 if (dyn == NULL) /* Not a shared object */
450 return;
451
452 sod_offset = sdt->sdt_sods;
453 printf(" Shared object dependencies:\n");
454 while (sod_offset != 0) {
455 const struct sod *sodp = (const struct sod *) align_struct((text_addr + sod_offset));
456 const char *name = (const char *) (text_addr + sodp->sod_name);
457
458 if (sodp->sod_library)
459 printf(" -l%-16s version %d.%d\n", name, sodp->sod_major,
460 sodp->sod_minor);
461 else
462 printf(" %s\n", name);
463 sod_offset = sodp->sod_next;
464 }
465 paths_offset = sdt->sdt_paths;
466 printf(" Shared object additional paths:\n");
467 if (paths_offset != 0) {
468 printf(" %s\n", (const char *)(text_addr + paths_offset));
469 } else {
470 printf(" (none)\n");
471 }
472 }
473
474 static void
dump_sym(const struct nlist * np)475 dump_sym(const struct nlist *np)
476 {
477 char type[8];
478 char aux[8];
479 char weak;
480 char *p;
481
482 switch (np->n_type & ~N_EXT) {
483 case N_UNDF: strcpy(type, "undf"); break;
484 case N_ABS: strcpy(type, "abs"); break;
485 case N_TEXT: strcpy(type, "text"); break;
486 case N_DATA: strcpy(type, "data"); break;
487 case N_BSS: strcpy(type, "bss"); break;
488 case N_INDR: strcpy(type, "indr"); break;
489 case N_SIZE: strcpy(type, "size"); break;
490 case N_COMM: strcpy(type, "comm"); break;
491 case N_SETA: strcpy(type, "seta"); break;
492 case N_SETT: strcpy(type, "sett"); break;
493 case N_SETD: strcpy(type, "setd"); break;
494 case N_SETB: strcpy(type, "setb"); break;
495 case N_SETV: strcpy(type, "setv"); break;
496 case N_FN: strcpy(type, np->n_type&N_EXT ? "fn" : "warn"); break;
497 case N_GSYM: strcpy(type, "gsym"); break;
498 case N_FNAME: strcpy(type, "fname"); break;
499 case N_FUN: strcpy(type, "fun"); break;
500 case N_STSYM: strcpy(type, "stsym"); break;
501 case N_LCSYM: strcpy(type, "lcsym"); break;
502 case N_MAIN: strcpy(type, "main"); break;
503 case N_PC: strcpy(type, "pc"); break;
504 case N_RSYM: strcpy(type, "rsym"); break;
505 case N_SLINE: strcpy(type, "sline"); break;
506 case N_DSLINE: strcpy(type, "dsline"); break;
507 case N_BSLINE: strcpy(type, "bsline"); break;
508 case N_SSYM: strcpy(type, "ssym"); break;
509 case N_SO: strcpy(type, "so"); break;
510 case N_LSYM: strcpy(type, "lsym"); break;
511 case N_BINCL: strcpy(type, "bincl"); break;
512 case N_SOL: strcpy(type, "sol"); break;
513 case N_PSYM: strcpy(type, "psym"); break;
514 case N_EINCL: strcpy(type, "eincl"); break;
515 case N_ENTRY: strcpy(type, "entry"); break;
516 case N_LBRAC: strcpy(type, "lbrac"); break;
517 case N_EXCL: strcpy(type, "excl"); break;
518 case N_RBRAC: strcpy(type, "rbrac"); break;
519 case N_BCOMM: strcpy(type, "bcomm"); break;
520 case N_ECOMM: strcpy(type, "ecomm"); break;
521 case N_ECOML: strcpy(type, "ecoml"); break;
522 case N_LENG: strcpy(type, "leng"); break;
523 default:
524 snprintf(type, sizeof type, "%#02x", np->n_type);
525 break;
526 }
527
528 if (np->n_type & N_EXT && type[0] != '0')
529 for (p = type; *p != '\0'; ++p)
530 *p = toupper(*p);
531
532 switch (N_AUX(np)) {
533 case 0: strcpy(aux, ""); break;
534 case AUX_OBJECT: strcpy(aux, "objt"); break;
535 case AUX_FUNC: strcpy(aux, "func"); break;
536 default: snprintf(aux, sizeof aux, "%#01x", N_AUX(np)); break;
537 }
538
539 weak = N_BIND(np) == BIND_WEAK ? 'w' : ' ';
540
541 printf("%c%-6s %-4s %8lx", weak, type, aux, np->n_value);
542 }
543
544 static void
dump_syms(void)545 dump_syms(void)
546 {
547 unsigned long i;
548
549 printf(" Symbols:\n");
550 for (i = 0; i < sym_count; ++i) {
551 printf(" %6lu%c ", i, sym_used[i] ? '*' : ' ');
552 dump_sym(&sym_base[i]);
553 printf(" %s\n", sym_name(i));
554 }
555 }
556
557 static const char *
rtsym_name(unsigned long n)558 rtsym_name(unsigned long n)
559 {
560 assert(n < rtsym_count);
561 if (rtsym_base[n].nz_strx == 0)
562 return "";
563 return rtstr_base + rtsym_base[n].nz_strx;
564 }
565
566 static const char *
sym_name(unsigned long n)567 sym_name(unsigned long n)
568 {
569 assert(n < sym_count);
570 if (sym_base[n].n_un.n_strx == 0)
571 return "";
572 return str_base + sym_base[n].n_un.n_strx;
573 }
574