xref: /freebsd-11-stable/usr.bin/ldd/sods.c (revision 414e871411aaeaf1571477b17df270efdb56d78b)
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