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.
This commit is contained in:
parent
9fb9409527
commit
dba54b89e1
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
502
gnu/usr.bin/ld/ldd/sods.c
Normal file
502
gnu/usr.bin/ld/ldd/sods.c
Normal file
@ -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;
|
||||
}
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
502
usr.bin/ldd/sods.c
Normal file
502
usr.bin/ldd/sods.c
Normal file
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user