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
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
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>
41 #include <machine/elf.h>
46 #include <sys/link_aout.h>
50 #define N_SETA 0x14 /* Absolute set element symbol */
51 #endif /* This is input to LD, in a .o file. */
54 #define N_SETT 0x16 /* Text set element symbol */
55 #endif /* This is input to LD, in a .o file. */
58 #define N_SETD 0x18 /* Data set element symbol */
59 #endif /* This is input to LD, in a .o file. */
62 #define N_SETB 0x1A /* Bss set element symbol */
63 #endif /* This is input to LD, in a .o file. */
66 #define N_SETV 0x1C /* Pointer to set vector in data area. */
67 #endif /* This is output from LD. */
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);
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 */
131 main(int argc
, char *argv
[])
135 for (i
= 1; i
< argc
; ++i
)
138 return error_count
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;
146 dump_file(const char *fname
)
152 if (stat(fname
, &sb
) == -1) {
153 warnx("cannot stat \"%s\"", fname
);
158 if ((sb
.st_mode
& S_IFMT
) != S_IFREG
) {
159 warnx("\"%s\" is not a regular file", fname
);
164 if ((fd
= open(fname
, O_RDONLY
, 0)) == -1) {
165 warnx("cannot open \"%s\"", fname
);
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
);
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
);
185 munmap(objbase
, sb
.st_size
);
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
));
198 warnx("%s: bad magic number", fname
);
200 munmap(objbase
, sb
.st_size
);
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
),
231 printf(" Data offset = %lx, address = %lx\n", N_DATOFF(*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
256 text_addr
= text_base
;
257 data_addr
= data_base
;
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
;
269 printf(" Program, origin = %lx\n", origin
);
270 } else if (N_GETFLAG(*ex
) & EX_DYNAMIC
)
271 printf(" Shared library, origin = %lx\n", origin
);
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
);
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
;
305 dump_rels("Relocations", rel_base
, rel_count
, sym_name
, sym_used
);
308 dump_rels("Run-time relocations", rtrel_base
, rtrel_count
, rtsym_name
,
312 if (rtsym_used
!= NULL
) {
316 if (sym_used
!= NULL
) {
320 munmap(objbase
, sb
.st_size
);
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
)
330 printf(" %s:\n", label
);
331 for (i
= 0; i
< count
; ++i
) {
332 const struct relocation_info
*r
= &base
[i
];
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.
347 snprintf(contents
, sizeof contents
, " [%02x]",
348 *(unsigned char *)(text_addr
+ r
->r_address
));
351 snprintf(contents
, sizeof contents
, " [%04x]",
352 *(unsigned short *)(text_addr
+ r
->r_address
));
355 snprintf(contents
, sizeof contents
, "[%08lx]",
356 *(unsigned long *)(text_addr
+ r
->r_address
));
360 snprintf(contents
, sizeof contents
, " ");
362 printf(" %6lu %8x/%u %s %c%c%c%c%c%c", i
,
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;
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
));
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
);
423 if (dyn
== NULL
) /* Not a shared object */
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
,
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
);
450 dump_sym(const struct nlist
*np
)
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;
499 snprintf(type
, sizeof type
, "%#02x", np
->n_type
);
503 if (np
->n_type
& N_EXT
&& type
[0] != '0')
504 for (p
= type
; *p
!= '\0'; ++p
)
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
);
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
));
533 rtsym_name(unsigned long n
)
535 assert(n
< rtsym_count
);
536 if (rtsym_base
[n
].nz_strx
== 0)
538 return rtstr_base
+ rtsym_base
[n
].nz_strx
;
542 sym_name(unsigned long n
)
544 assert(n
< sym_count
);
545 if (sym_base
[n
].n_un
.n_strx
== 0)
547 return str_base
+ sym_base
[n
].n_un
.n_strx
;