diff options
author | peter <peter@FreeBSD.org> | 1996-10-01 02:16:16 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 1996-10-01 02:16:16 +0000 |
commit | 519e9b0911db8a2c11a96565cdaf9726957e36f8 (patch) | |
tree | 4a1ee76d65283f0bc07bcd87034f55b5f0a43ffc /gnu | |
parent | 7523d23ee7b9610928e383200f010b495d600ed5 (diff) | |
download | FreeBSD-src-519e9b0911db8a2c11a96565cdaf9726957e36f8.zip FreeBSD-src-519e9b0911db8a2c11a96565cdaf9726957e36f8.tar.gz |
Incorporate John Polstra's sods.c display of the details about the
dynamic linking information in the executable. It's quite extensive.
It's connected to ldd's (new) -v option.
Diffstat (limited to 'gnu')
-rw-r--r-- | gnu/usr.bin/ld/ldd/Makefile | 4 | ||||
-rw-r--r-- | gnu/usr.bin/ld/ldd/ldd.1 | 11 | ||||
-rw-r--r-- | gnu/usr.bin/ld/ldd/ldd.c | 20 | ||||
-rw-r--r-- | gnu/usr.bin/ld/ldd/sods.c | 502 |
4 files changed, 533 insertions, 4 deletions
diff --git a/gnu/usr.bin/ld/ldd/Makefile b/gnu/usr.bin/ld/ldd/Makefile index 282a8fd..0335103 100644 --- a/gnu/usr.bin/ld/ldd/Makefile +++ b/gnu/usr.bin/ld/ldd/Makefile @@ -1,7 +1,7 @@ -# $Id: Makefile,v 1.2 1993/11/09 04:19:24 paul Exp $ +# $Id: Makefile,v 1.3 1993/12/16 21:51:27 nate Exp $ PROG= ldd -SRCS= ldd.c +SRCS= ldd.c sods.c BINDIR= /usr/bin .include <bsd.prog.mk> diff --git a/gnu/usr.bin/ld/ldd/ldd.1 b/gnu/usr.bin/ld/ldd/ldd.1 index 8253470..1a09103 100644 --- a/gnu/usr.bin/ld/ldd/ldd.1 +++ b/gnu/usr.bin/ld/ldd/ldd.1 @@ -7,6 +7,7 @@ .Sh SYNOPSIS .Nm ldd .Op Fl f Ar format +.Op Fl v .Ar program ... .Sh DESCRIPTION .Nm ldd @@ -25,6 +26,12 @@ and allows customization of output. See .Xr rtld 1 for a list of recognised conversion characters. +.Pp +The +.Fl v +option displays an verbose listing of the dynamic linking headers +encoded in the executable. See the source code and include +files for the definitive meaning of all the fields. .Sh SEE ALSO .Xr ld 1 , .Xr ld.so 1 , @@ -34,3 +41,7 @@ A .Nm ldd utility first appeared in SunOS 4.0, it appeared in its current form in FreeBSD 1.1. +.Pp +The +.Fl v +support is based on code written by John Polstra <jdp@polstra.com> diff --git a/gnu/usr.bin/ld/ldd/ldd.c b/gnu/usr.bin/ld/ldd/ldd.c index 79a229b..841baf8 100644 --- a/gnu/usr.bin/ld/ldd/ldd.c +++ b/gnu/usr.bin/ld/ldd/ldd.c @@ -27,7 +27,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: ldd.c,v 1.5 1994/12/23 22:31:31 nate Exp $ + * $Id: ldd.c,v 1.6 1996/10/01 01:34:32 peter Exp $ */ #include <sys/types.h> @@ -44,6 +44,9 @@ #include <string.h> #include <unistd.h> +extern void dump_filename __P((const char *)); +extern int error_count; + void usage() { @@ -61,9 +64,13 @@ char *argv[]; char *fmt1 = NULL, *fmt2 = NULL; int rval; int c; + int vflag; - while ((c = getopt(argc, argv, "f:")) != EOF) { + while ((c = getopt(argc, argv, "vf:")) != EOF) { switch (c) { + case 'v': + vflag++; + break; case 'f': if (fmt1) { if (fmt2) @@ -80,11 +87,20 @@ char *argv[]; argc -= optind; argv += optind; + if (vflag && fmt1) + errx(1, "-v may not be used with -f"); + if (argc <= 0) { usage(); /*NOTREACHED*/ } + if (vflag) { + for (c = 0; c < argc; c++) + dump_file(argv[c]); + exit(error_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE); + } + /* ld.so magic */ setenv("LD_TRACE_LOADED_OBJECTS", "", 1); if (fmt1) diff --git a/gnu/usr.bin/ld/ldd/sods.c b/gnu/usr.bin/ld/ldd/sods.c new file mode 100644 index 0000000..4a9e904 --- /dev/null +++ b/gnu/usr.bin/ld/ldd/sods.c @@ -0,0 +1,502 @@ +/* + * Copyright (C) 1996 John D. Polstra. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JOHN D. POLSTRA AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JOHN D. POLSTRA OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#include <assert.h> +#include <ctype.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> + +#include <a.out.h> +#include <link.h> +#include <stab.h> + +#ifndef N_SETA +#define N_SETA 0x14 /* Absolute set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETT +#define N_SETT 0x16 /* Text set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETD +#define N_SETD 0x18 /* Data set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETB +#define N_SETB 0x1A /* Bss set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETV +#define N_SETV 0x1C /* Pointer to set vector in data area. */ +#endif /* This is output from LD. */ + +#ifdef STANDALONE +static +#endif +void dump_file(const char *); + +static void dump_rels(const char *, const struct relocation_info *, + unsigned long, const char *(*)(unsigned long), unsigned char *); +static void dump_segs(); +static void dump_sods(); +static void dump_sym(const struct nlist *); +static void dump_syms(); + +static void dump_rtrels(); +static void dump_rtsyms(); + +static void error(const char *, ...); +static const char *rtsym_name(unsigned long); +static const char *sym_name(unsigned long); + +#ifdef STANDALONE +static +#endif +int error_count; + +/* + * Variables ending in _base are pointers to things in our address space, + * i.e., in the file itself. + * + * Variables ending in _addr are adjusted according to where things would + * actually appear in memory if the file were loaded. + */ +static const char *file_base; +static const char *text_base; +static const char *data_base; +static const struct relocation_info *rel_base; +static const struct nlist *sym_base; +static const char *str_base; + +static const struct relocation_info *rtrel_base; +static const struct nzlist *rtsym_base; +static const char *rtstr_base; + +static const struct exec *ex; +static const struct _dynamic *dyn; +static const struct section_dispatch_table *sdt; + +static const char *text_addr; +static const char *data_addr; + +static unsigned long rel_count; +static unsigned long sym_count; + +static unsigned long rtrel_count; +static unsigned long rtsym_count; + +/* Dynamically allocated flags, 1 byte per symbol, to record whether each + symbol was referenced by a relocation entry. */ +static unsigned char *sym_used; +static unsigned char *rtsym_used; + +static unsigned long origin; /* What values are relocated relative to */ + +#ifdef STANDALONE +main(int argc, char *argv[]) +{ + int i; + + for(i = 1; i < argc; ++i) + dump_file(argv[i]); + + return error_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} +#endif + +#ifdef STANDALONE +static +#endif +void +dump_file(const char *fname) +{ + int fd; + struct stat sb; + caddr_t objbase; + long load_offset; + + if(stat(fname, &sb) == -1) { + error("Cannot stat \"%s\"", fname); + return; + } + + if((sb.st_mode & S_IFMT) != S_IFREG) { + error("\"%s\" is not a regular file", fname); + return; + } + + if((fd = open(fname, O_RDONLY, 0)) == -1) { + error("Cannot open \"%s\"", fname); + return; + } + + objbase = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); + if(objbase == (caddr_t) -1) { + error("Cannot mmap \"%s\"", fname); + close(fd); + return; + } + + close(fd); + + file_base = (const char *) objbase; /* Makes address arithmetic easier */ + + ex = (const struct exec *) file_base; + + printf("%s: a_midmag = 0x%lx\n", fname, ex->a_midmag); + printf(" magic = 0x%x = 0%o, netmagic = 0x%x = 0%o\n", + N_GETMAGIC(*ex), N_GETMAGIC(*ex), + N_GETMAGIC_NET(*ex), N_GETMAGIC_NET(*ex)); + + if(N_BADMAG(*ex)) { + error("%s: Bad magic number", fname); + munmap(objbase, sb.st_size); + return; + } + + printf(" a_text = 0x%lx\n", ex->a_text); + printf(" a_data = 0x%lx\n", ex->a_data); + printf(" a_bss = 0x%lx\n", ex->a_bss); + printf(" a_syms = 0x%lx\n", ex->a_syms); + printf(" a_entry = 0x%lx\n", ex->a_entry); + printf(" a_trsize = 0x%lx\n", ex->a_trsize); + printf(" a_drsize = 0x%lx\n", ex->a_drsize); + + load_offset = N_TXTADDR(*ex) - N_TXTOFF(*ex); + + text_base = file_base + N_TXTOFF(*ex); + data_base = file_base + N_DATOFF(*ex); + rel_base = (const struct relocation_info *) (file_base + N_RELOFF(*ex)); + sym_base = (const struct nlist *) (file_base + N_SYMOFF(*ex)); + str_base = file_base + N_STROFF(*ex); + + rel_count = (ex->a_trsize + ex->a_drsize) / sizeof rel_base[0]; + assert(rel_count * sizeof rel_base[0] == ex->a_trsize + ex->a_drsize); + sym_count = ex->a_syms / sizeof sym_base[0]; + assert(sym_count * sizeof sym_base[0] == ex->a_syms); + + if(sym_count != 0) { + sym_used = (unsigned char *) calloc(sym_count, sizeof(unsigned char)); + assert(sym_used != NULL); + } + + printf(" Entry = 0x%x, load offset = 0x%lx\n", + ex->a_entry, load_offset); + printf(" Text offset = %lx, address = %lx\n", N_TXTOFF(*ex), + N_TXTADDR(*ex)); + printf(" Data offset = %lx, address = %lx\n", N_DATOFF(*ex), + N_DATADDR(*ex)); + + /* + * DEBUG + * + * In an executable program file, everything is relocated relative to + * the assumed run-time load address, i.e., N_TXTADDR(*ex), i.e., 0x1000. + * + * In a shared library file, everything is relocated relative to the + * start of the file, i.e., N_TXTOFF(*ex), i.e., 0. + * + * The way to tell the difference is by looking at ex->a_entry. If it + * is >= 0x1000, then we have an executable program. Otherwise, we + * have a shared library. + * + * When a program is executed, the entire file is mapped into memory, + * including the a.out header and so forth. But it is not mapped at + * address 0; rather it is mapped at address 0x1000. The first page + * of the user's address space is left unmapped in order to catch null + * pointer dereferences. + * + * In this program, when we map in an executable program, we have to + * simulate the empty page by decrementing our assumed base address by + * a pagesize. + */ + + text_addr = text_base; + data_addr = data_base; + origin = 0; + + if(ex->a_entry >= load_offset) { /* Executable, not a shared library */ + /* + * The fields in the object have already been relocated on the + * assumption that the object will be loaded at N_TXTADDR(*ex). + * We have to compensate for that. + */ + text_addr -= load_offset; + data_addr -= load_offset; + origin = load_offset; + printf(" Program, origin = %lx\n", origin); + } else + printf(" Library, origin = %lx\n", origin); + + if(N_GETFLAG(*ex) & EX_DYNAMIC) { + dyn = (const struct _dynamic *) data_base; + sdt = (const struct section_dispatch_table *) + (text_addr + (unsigned long) dyn->d_un.d_sdt); + + rtrel_base = + (const struct relocation_info *) (text_addr + sdt->sdt_rel); + rtrel_count = (sdt->sdt_hash - sdt->sdt_rel) / sizeof rtrel_base[0]; + assert(rtrel_count * sizeof rtrel_base[0] == + sdt->sdt_hash - sdt->sdt_rel); + + rtsym_base = (const struct nzlist *) (text_addr + sdt->sdt_nzlist); + rtsym_count = (sdt->sdt_strings - sdt->sdt_nzlist) / + sizeof rtsym_base[0]; + assert(rtsym_count * sizeof rtsym_base[0] == + sdt->sdt_strings - sdt->sdt_nzlist); + + if(rtsym_count != 0) { + rtsym_used = (unsigned char *) calloc(rtsym_count, + sizeof(unsigned char)); + assert(rtsym_used != NULL); + } + + rtstr_base = text_addr + sdt->sdt_strings; + } + + dump_segs(); + dump_sods(); + dump_rels("Relocations", rel_base, rel_count, sym_name, sym_used); + dump_syms(); + + dump_rels("Run-time relocations", rtrel_base, rtrel_count, rtsym_name, + rtsym_used); + dump_rtsyms(); + + if(rtsym_used != NULL) { + free(rtsym_used); + rtsym_used = NULL; + } + if(sym_used != NULL) { + free(sym_used); + sym_used = NULL; + } + munmap(objbase, sb.st_size); +} + +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) +{ + unsigned long i; + + printf(" %s:\n", label); + for(i = 0; i < count; ++i) { + const struct relocation_info *r = &base[i]; + + printf(" %6lu %8x/%u %c%c%c%c%c%c", i, + r->r_address, 1u << r->r_length, + r->r_extern ? 'e' : '-', + r->r_jmptable ? 'j' : '-', + r->r_relative ? 'r' : '-', + r->r_baserel ? 'b' : '-', + r->r_pcrel ? 'p' : '-', + r->r_copy ? 'c' : '-'); + + if(r->r_extern || r->r_baserel || r->r_jmptable || r->r_copy) { + printf(" %4u %s", r->r_symbolnum, name(r->r_symbolnum)); + sym_used_flags[r->r_symbolnum] = 1; + } + + printf("\n"); + } +} + +static void +dump_rtsyms() +{ + unsigned long i; + + printf(" Run-time symbols:\n"); + for(i = 0; i < rtsym_count; ++i) { + printf(" %6lu%c ", i, rtsym_used[i] ? '*' : ' '); + dump_sym(&rtsym_base[i].nlist); + printf("/%-5ld %s\n", rtsym_base[i].nz_size, rtsym_name(i)); + } +} + +static void +dump_segs() +{ + printf(" Text segment starts at address %lx\n", origin + N_TXTOFF(*ex)); + if(N_GETFLAG(*ex) & EX_DYNAMIC) { + printf(" rel starts at %lx\n", sdt->sdt_rel); + printf(" hash starts at %lx\n", sdt->sdt_hash); + printf(" nzlist starts at %lx\n", sdt->sdt_nzlist); + printf(" strings starts at %lx\n", sdt->sdt_strings); + } + + printf(" Data segment starts at address %lx\n", origin + N_DATOFF(*ex)); + if(N_GETFLAG(*ex) & EX_DYNAMIC) { + printf(" _dynamic starts at %lx\n", origin + N_DATOFF(*ex)); + printf(" so_debug starts at %lx\n", (unsigned long) dyn->d_debug); + printf(" sdt starts at %lx\n", (unsigned long) dyn->d_un.d_sdt); + printf(" got starts at %lx\n", sdt->sdt_got); + printf(" plt starts at %lx\n", sdt->sdt_plt); + printf(" rest of stuff starts at %lx\n", + sdt->sdt_plt + sdt->sdt_plt_sz); + } +} + +static void +dump_sods() +{ + long sod_offset; + long paths_offset; + + if(dyn == NULL) /* Not a shared object */ + return; + + sod_offset = sdt->sdt_sods; + printf(" Shared object dependencies:\n"); + while(sod_offset != 0) { + const struct sod *sodp = (const struct sod *) (text_addr + sod_offset); + const char *name = (const char *) (text_addr + sodp->sod_name); + + printf(" -l%-16s version %d.%d\n", name, sodp->sod_major, + sodp->sod_minor); + sod_offset = sodp->sod_next; + } + paths_offset = sdt->sdt_paths; + printf(" Shared object additional paths:\n"); + if (paths_offset != 0) { + char *path = (char *)(text_addr + paths_offset); + printf(" %s\n", path); + } else { + printf(" NULL\n"); + } +} + +static void +dump_sym(const struct nlist *np) +{ + char type[8]; + char *p; + + switch(np->n_type & ~N_EXT) { + case N_UNDF: strcpy(type, "undf"); break; + case N_ABS: strcpy(type, "abs"); break; + case N_TEXT: strcpy(type, "text"); break; + case N_DATA: strcpy(type, "data"); break; + case N_BSS: strcpy(type, "bss"); break; + case N_INDR: strcpy(type, "indr"); break; + case N_SIZE: strcpy(type, "size"); break; + case N_COMM: strcpy(type, "comm"); break; + case N_SETA: strcpy(type, "seta"); break; + case N_SETT: strcpy(type, "sett"); break; + case N_SETD: strcpy(type, "setd"); break; + case N_SETB: strcpy(type, "setb"); break; + case N_SETV: strcpy(type, "setv"); break; + case N_FN: strcpy(type, np->n_type&N_EXT ? "fn" : "warn"); break; + case N_GSYM: strcpy(type, "gsym"); break; + case N_FNAME: strcpy(type, "fname"); break; + case N_FUN: strcpy(type, "fun"); break; + case N_STSYM: strcpy(type, "stsym"); break; + case N_LCSYM: strcpy(type, "lcsym"); break; + case N_MAIN: strcpy(type, "main"); break; + case N_PC: strcpy(type, "pc"); break; + case N_RSYM: strcpy(type, "rsym"); break; + case N_SLINE: strcpy(type, "sline"); break; + case N_DSLINE: strcpy(type, "dsline"); break; + case N_BSLINE: strcpy(type, "bsline"); break; + case N_SSYM: strcpy(type, "ssym"); break; + case N_SO: strcpy(type, "so"); break; + case N_LSYM: strcpy(type, "lsym"); break; + case N_BINCL: strcpy(type, "bincl"); break; + case N_SOL: strcpy(type, "sol"); break; + case N_PSYM: strcpy(type, "psym"); break; + case N_EINCL: strcpy(type, "eincl"); break; + case N_ENTRY: strcpy(type, "entry"); break; + case N_LBRAC: strcpy(type, "lbrac"); break; + case N_EXCL: strcpy(type, "excl"); break; + case N_RBRAC: strcpy(type, "rbrac"); break; + case N_BCOMM: strcpy(type, "bcomm"); break; + case N_ECOMM: strcpy(type, "ecomm"); break; + case N_ECOML: strcpy(type, "ecoml"); break; + case N_LENG: strcpy(type, "leng"); break; + default: sprintf(type, "0x%02x", np->n_type); + } + + if(np->n_type & N_EXT && type[0] != '0') + for(p = type; *p != '\0'; ++p) + *p = toupper(*p); + + printf("%-5s %8lx", type, np->n_value); +} + +static void +dump_syms() +{ + unsigned long i; + + printf(" Symbols:\n"); + for(i = 0; i < sym_count; ++i) { + printf(" %6lu%c ", i, sym_used[i] ? '*' : ' '); + dump_sym(&sym_base[i]); + printf(" %s\n", sym_name(i)); + } +} + +static void +error(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + putc('\n', stderr); + + ++error_count; +} + +static const char * +rtsym_name(unsigned long n) +{ + assert(n < rtsym_count); + if(rtsym_base[n].nz_strx == 0) + return ""; + return rtstr_base + rtsym_base[n].nz_strx; +} + +static const char * +sym_name(unsigned long n) +{ + assert(n < sym_count); + if(sym_base[n].n_un.n_strx == 0) + return ""; + return str_base + sym_base[n].n_un.n_strx; +} |