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