37678f1b06d33746c1554905d8a433c88563f610
[dragonfly.git] / usr.bin / ldd / sods.c
1 /*
2  * Copyright (C) 1996-1997 John D. Polstra.  All rights reserved.
3  *
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.
12  *
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.
24  *
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.3 2003/10/04 20:36:47 hmp Exp $
27  */
28
29 #include <assert.h>
30 #include <ctype.h>
31 #include <err.h>
32 #include <fcntl.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36
37 #include <sys/mman.h>
38 #include <sys/stat.h>
39 #include <machine/elf.h>
40
41 #define FREEBSD_AOUT
42
43 #include <a.out.h>
44 #include <link.h>
45 #include <stab.h>
46
47 #define PAGE_SIZE       4096    /* i386 specific */
48
49 #ifndef N_SETA
50 #define N_SETA  0x14            /* Absolute set element symbol */
51 #endif                          /* This is input to LD, in a .o file.  */
52
53 #ifndef N_SETT
54 #define N_SETT  0x16            /* Text set element symbol */
55 #endif                          /* This is input to LD, in a .o file.  */
56
57 #ifndef N_SETD
58 #define N_SETD  0x18            /* Data set element symbol */
59 #endif                          /* This is input to LD, in a .o file. */
60
61 #ifndef N_SETB
62 #define N_SETB  0x1A            /* Bss set element symbol */
63 #endif                          /* This is input to LD, in a .o file. */
64
65 #ifndef N_SETV
66 #define N_SETV  0x1C            /* Pointer to set vector in data area. */
67 #endif                          /* This is output from LD. */
68
69 #ifdef STANDALONE
70 static
71 #endif
72 void dump_file(const char *);
73
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();
80
81 static void dump_rtsyms();
82
83 static const char *rtsym_name(unsigned long);
84 static const char *sym_name(unsigned long);
85
86 #ifdef STANDALONE
87 static
88 #endif
89 int error_count;
90
91 /*
92  * Variables ending in _base are pointers to things in our address space,
93  * i.e., in the file itself.
94  *
95  * Variables ending in _addr are adjusted according to where things would
96  * actually appear in memory if the file were loaded.
97  */
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;
104
105 static const struct relocation_info *rtrel_base;
106 static const struct nzlist *rtsym_base;
107 static const char *rtstr_base;
108
109 static const struct exec *ex;
110 static const struct _dynamic *dyn;
111 static const struct section_dispatch_table *sdt;
112
113 static const char *text_addr;
114 static const char *data_addr;
115
116 static unsigned long rel_count;
117 static unsigned long sym_count;
118
119 static unsigned long rtrel_count;
120 static unsigned long rtsym_count;
121
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;
126
127 static unsigned long origin;    /* What values are relocated relative to */
128
129 #ifdef STANDALONE
130 int
131 main(int argc, char *argv[])
132 {
133     int i;
134
135     for (i = 1;  i < argc;  ++i)
136         dump_file(argv[i]);
137
138     return error_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
139 }
140 #endif
141
142 #ifdef STANDALONE
143 static
144 #endif
145 void
146 dump_file(const char *fname)
147 {
148     int fd;
149     struct stat sb;
150     caddr_t objbase;
151
152     if (stat(fname, &sb) == -1) {
153         warnx("cannot stat \"%s\"", fname);
154         ++error_count;
155         return;
156     }
157
158     if ((sb.st_mode & S_IFMT) != S_IFREG) {
159         warnx("\"%s\" is not a regular file", fname);
160         ++error_count;
161         return;
162     }
163
164     if ((fd = open(fname, O_RDONLY, 0)) == -1) {
165         warnx("cannot open \"%s\"", fname);
166         ++error_count;
167         return;
168     }
169
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;
176     }
177
178     close(fd);
179
180     file_base = (const char *) objbase; /* Makes address arithmetic easier */
181
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;
188     }
189
190     ex = (const struct exec *) file_base;
191
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));
196
197     if (N_BADMAG(*ex)) {
198         warnx("%s: bad magic number", fname);
199         ++error_count;
200         munmap(objbase, sb.st_size);
201         return;
202     }
203
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);
211
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);
217
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);
222
223     if (sym_count != 0) {
224         sym_used = (unsigned char *) calloc(sym_count, sizeof(unsigned char));
225         assert(sym_used != NULL);
226     }
227
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));
233
234     /*
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.
237      *
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.
240      *
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.
244      *
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.
250      *
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.
254      */
255
256     text_addr = text_base;
257     data_addr = data_base;
258     origin = 0;
259
260     if (ex->a_entry >= PAGE_SIZE) {     /* Executable, not a shared library */
261         /*
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.
265          */
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);
274
275     if (N_GETFLAG(*ex) & EX_DYNAMIC) {
276         dyn = (const struct _dynamic *) data_base;
277         printf("  Dynamic version = %d\n", dyn->d_version);
278
279         sdt = (const struct section_dispatch_table *)
280             (text_addr + (unsigned long) dyn->d_un.d_sdt);
281
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);
287
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);
293
294         if (rtsym_count != 0) {
295             rtsym_used = (unsigned char *) calloc(rtsym_count,
296                 sizeof(unsigned char));
297             assert(rtsym_used != NULL);
298         }
299
300         rtstr_base = text_addr + sdt->sdt_strings;
301     }
302
303     dump_segs();
304     dump_sods();
305     dump_rels("Relocations", rel_base, rel_count, sym_name, sym_used);
306     dump_syms();
307
308     dump_rels("Run-time relocations", rtrel_base, rtrel_count, rtsym_name,
309         rtsym_used);
310     dump_rtsyms();
311
312     if (rtsym_used != NULL) {
313         free(rtsym_used);
314         rtsym_used = NULL;
315     }
316     if (sym_used != NULL) {
317         free(sym_used);
318         sym_used = NULL;
319     }
320     munmap(objbase, sb.st_size);
321 }
322
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)
327 {
328     unsigned long i;
329
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];
335
336         size = 1u << r->r_length;
337
338         if (origin <= r->r_address
339           && r->r_address < origin + ex->a_text + ex->a_data
340           && 1 <= size && size <= 4) {
341             /*
342              * XXX - This can cause unaligned accesses.  OK for the
343              * i386, not so for other architectures.
344              */
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;
358             }
359         } else
360             snprintf(contents, sizeof contents, "          ");
361
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' : '-');
371
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;
375         }
376
377         printf("\n");
378     }
379 }
380
381 static void
382 dump_rtsyms(void)
383 {
384     unsigned long i;
385
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));
391     }
392 }
393
394 static void
395 dump_segs(void)
396 {
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);
403     }
404
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);
414     }
415 }
416
417 static void
418 dump_sods(void)
419 {
420     long sod_offset;
421     long paths_offset;
422
423     if (dyn == NULL)            /* Not a shared object */
424         return;
425
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);
431
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;
438     }
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");
446     }
447 }
448
449 static void
450 dump_sym(const struct nlist *np)
451 {
452     char type[8];
453     char aux[8];
454     char weak;
455     char *p;
456
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;
501     }
502
503     if (np->n_type & N_EXT && type[0] != '0')
504         for (p = type;  *p != '\0';  ++p)
505             *p = toupper(*p);
506
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;
512     }
513
514     weak = N_BIND(np) == BIND_WEAK ? 'w' : ' ';
515
516     printf("%c%-6s %-4s %8lx", weak, type, aux, np->n_value);
517 }
518
519 static void
520 dump_syms(void)
521 {
522     unsigned long i;
523
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));
529     }
530 }
531
532 static const char *
533 rtsym_name(unsigned long n)
534 {
535     assert(n < rtsym_count);
536     if (rtsym_base[n].nz_strx == 0)
537         return "";
538     return rtstr_base + rtsym_base[n].nz_strx;
539 }
540
541 static const char *
542 sym_name(unsigned long n)
543 {
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;
548 }