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