summaryrefslogtreecommitdiffstats
path: root/usr.bin/ldd
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>1996-10-01 02:16:16 +0000
committerpeter <peter@FreeBSD.org>1996-10-01 02:16:16 +0000
commit519e9b0911db8a2c11a96565cdaf9726957e36f8 (patch)
tree4a1ee76d65283f0bc07bcd87034f55b5f0a43ffc /usr.bin/ldd
parent7523d23ee7b9610928e383200f010b495d600ed5 (diff)
downloadFreeBSD-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 'usr.bin/ldd')
-rw-r--r--usr.bin/ldd/Makefile4
-rw-r--r--usr.bin/ldd/ldd.111
-rw-r--r--usr.bin/ldd/ldd.c20
-rw-r--r--usr.bin/ldd/sods.c502
4 files changed, 533 insertions, 4 deletions
diff --git a/usr.bin/ldd/Makefile b/usr.bin/ldd/Makefile
index 282a8fd..0335103 100644
--- a/usr.bin/ldd/Makefile
+++ b/usr.bin/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/usr.bin/ldd/ldd.1 b/usr.bin/ldd/ldd.1
index 8253470..1a09103 100644
--- a/usr.bin/ldd/ldd.1
+++ b/usr.bin/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/usr.bin/ldd/ldd.c b/usr.bin/ldd/ldd.c
index 79a229b..841baf8 100644
--- a/usr.bin/ldd/ldd.c
+++ b/usr.bin/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/usr.bin/ldd/sods.c b/usr.bin/ldd/sods.c
new file mode 100644
index 0000000..4a9e904
--- /dev/null
+++ b/usr.bin/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;
+}
OpenPOWER on IntegriCloud