1570 lines
39 KiB
C
Executable File
1570 lines
39 KiB
C
Executable File
/*
|
|
* Copyright (c) 1980,1986 Regents of the University of California.
|
|
* All rights reserved. The Berkeley software License Agreement
|
|
* specifies the terms and conditions for redistribution.
|
|
*
|
|
* The code that creates the namelist file with the module symbol tables
|
|
* added (those modules present at the time of the crash) was written
|
|
* at Sun Microsystems, Inc. All rights reserved.
|
|
*
|
|
* Copyright (c) 1991-1993, by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#pragma ident "@(#)savecore.c 1.50 94/06/23 SMI" /* UCB 5.8 5/26/86 */
|
|
|
|
/*
|
|
* savecore - save kernel crash dump image.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <nlist.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <syslog.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/vfs.h>
|
|
#include <sys/time.h>
|
|
#include <sys/file.h>
|
|
#include <sys/dumphdr.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/statvfs.h>
|
|
#include <sys/utsname.h>
|
|
#include <kvm.h>
|
|
#include <sys/kobj.h>
|
|
#include <sys/modctl.h>
|
|
#include <sys/systeminfo.h>
|
|
#include <libelf.h>
|
|
|
|
#define COREFSIZE 100
|
|
#define DEFPATHSIZE 200
|
|
#define FD_MAX 200
|
|
#define DAY (60L*60L*24L)
|
|
#define LEEWAY (3*DAY)
|
|
|
|
#define eq(a, b) (strcmp(a, b) == 0)
|
|
#define ALIGN(x, a) ((a) == 0 ? (int)(x) : \
|
|
(((int)(x) + (a) - 1) & ~((a) - 1)))
|
|
|
|
struct nlist current_nl[] = { /* namelist for currently running system */
|
|
#define X_VERSION 0
|
|
{ "utsname" },
|
|
{ "" },
|
|
};
|
|
|
|
int cursyms[] =
|
|
{ X_VERSION, -1 };
|
|
|
|
/*
|
|
* data specific to a module, obtained from the core file.
|
|
*/
|
|
struct core_data {
|
|
int nglbs; /* number of global symbols */
|
|
int nlcls; /* number of local symbols */
|
|
int sym_size; /* size of symbol table */
|
|
int str_size; /* size of string table */
|
|
};
|
|
|
|
/*
|
|
* list of modules loaded at the time of the crash.
|
|
*/
|
|
struct loaded {
|
|
struct loaded *next;
|
|
char *path; /* absolute path of located module */
|
|
struct module *mp; /* module struct */
|
|
char *modname; /* module name */
|
|
struct core_data cd; /* per module info from core file */
|
|
};
|
|
|
|
int read_dumphdr(void);
|
|
int good_dumphdr(struct dumphdr *);
|
|
void read_kmem(void);
|
|
void nullterm(char *, int);
|
|
void check_kmem(void);
|
|
int get_crashtime(void);
|
|
char *path(char *);
|
|
int check_space(void);
|
|
int read_number(char *);
|
|
int save_core(void);
|
|
int save_a_page(int, int, char *);
|
|
#ifdef SAVESWAP
|
|
void save_swap(int);
|
|
#endif /* SAVESWAP */
|
|
void clear_dump(void);
|
|
void usage(int);
|
|
static void print_modname(const char *const);
|
|
int build_namelist(int, char *);
|
|
void get_mod_list(kvm_t *, u_long, char *, struct loaded **);
|
|
int find_module(struct loaded *, char *);
|
|
kvm_t *Kvm_open(char *, char *, char *, int);
|
|
void Kvm_nlist(kvm_t *, struct nlist nl[]);
|
|
void Kvm_read(kvm_t *, u_long, char *, u_int);
|
|
int Open(char *, int);
|
|
int Read(int, char *, int);
|
|
int Create(char *, int);
|
|
void Write(int, char *, int);
|
|
void Fsync(int);
|
|
off_t Lseek(int, off_t, int);
|
|
void *Malloc(size_t);
|
|
|
|
char *sysname; /* alternate name of kernel obj */
|
|
char *dirname; /* directory to save dumps in */
|
|
char *ddname = "/dev/dump"; /* name of dump device */
|
|
int dumpsize; /* amount of memory dumped */
|
|
time_t now; /* current date */
|
|
char *progname; /* argv[0] */
|
|
int Verbose;
|
|
extern int errno;
|
|
int dfd; /* dump file descriptor */
|
|
struct dumphdr *dhp; /* pointer to dump header */
|
|
int pagesize; /* dump pagesize */
|
|
int fflag;
|
|
struct utsname utsname;
|
|
int damnit;
|
|
|
|
main(int argc, char *argv[])
|
|
{
|
|
register int c, opt;
|
|
register int error = 0;
|
|
|
|
progname = argv[0];
|
|
while ((c = getopt(argc, argv, "vdf:")) != EOF) {
|
|
switch (c) {
|
|
case 'v':
|
|
Verbose++;
|
|
break;
|
|
case 'd': /* ignore dump time and valid flag - private */
|
|
damnit = 1;
|
|
break;
|
|
case 'f': /* Undocumented! */
|
|
ddname = optarg;
|
|
break;
|
|
case '?':
|
|
error++;
|
|
break;
|
|
}
|
|
if (error != 0) {
|
|
usage(c);
|
|
return (1);
|
|
}
|
|
}
|
|
opt = argc - optind;
|
|
if (opt < 1 || opt > 2) {
|
|
usage(0);
|
|
return (1);
|
|
} else {
|
|
dirname = argv[optind];
|
|
if (opt == 2)
|
|
sysname = argv[optind + 1];
|
|
else {
|
|
static char kernname[MAXPATHLEN];
|
|
char platform[MAXNAMELEN];
|
|
|
|
if (sysinfo(SI_PLATFORM, platform,
|
|
sizeof (platform)) == -1) {
|
|
(void) fprintf(stderr, "%s: could not get "
|
|
"platform name.\n", progname);
|
|
}
|
|
(void) sprintf(kernname, "/platform/%s/kernel/unix",
|
|
platform);
|
|
sysname = kernname;
|
|
}
|
|
}
|
|
openlog(progname, LOG_ODELAY, LOG_AUTH);
|
|
if (access(dirname, W_OK) < 0) {
|
|
int oerrno = errno;
|
|
|
|
perror(dirname);
|
|
errno = oerrno;
|
|
syslog(LOG_ERR, "%s: %m", dirname);
|
|
return (1);
|
|
}
|
|
if (read_dumphdr() == 0) {
|
|
if (Verbose)
|
|
(void) fprintf(stderr, "%s: No dump exists.\n",
|
|
progname);
|
|
return (0);
|
|
}
|
|
read_kmem();
|
|
(void) time(&now);
|
|
check_kmem();
|
|
if (*dump_panicstring(dhp) != 0)
|
|
syslog(LOG_CRIT, "reboot after panic: %s",
|
|
dump_panicstring(dhp));
|
|
else
|
|
syslog(LOG_CRIT, "reboot");
|
|
if (get_crashtime() == 0 || check_space() == 0)
|
|
return (1);
|
|
if (save_core() != 0)
|
|
clear_dump();
|
|
(void) close(dfd);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
read_dumphdr(void)
|
|
{
|
|
struct dumphdr dumphdr1;
|
|
struct dumphdr *dhp1 = &dumphdr1;
|
|
|
|
pagesize = sysconf(_SC_PAGESIZE); /* first guess */
|
|
|
|
dfd = Open(ddname, fflag ? O_RDONLY : O_RDWR); /* dump */
|
|
(void) Lseek(dfd, -pagesize, SEEK_END);
|
|
(void) Read(dfd, (char *)dhp1, sizeof (*dhp1)); /* read the header */
|
|
dhp = dhp1; /* temporarily */
|
|
|
|
/* Check that this is a valid header */
|
|
if (!good_dumphdr(dhp1))
|
|
return (0);
|
|
|
|
/*
|
|
* So far, so good. Reposition file, read real header, and
|
|
* compare.
|
|
*/
|
|
pagesize = dhp1->dump_pagesize; /* get dump pagesize */
|
|
dhp = malloc(dhp1->dump_headersize);
|
|
if (dhp == (struct dumphdr *)0) {
|
|
(void) fprintf(stderr, "%s: Can't allocate dumphdr buffer.\n",
|
|
progname);
|
|
return (0);
|
|
}
|
|
(void) Lseek(dfd, -(dhp1->dump_dumpsize), SEEK_END);
|
|
(void) Read(dfd, (char *)dhp, (int)dhp1->dump_headersize);
|
|
|
|
if (memcmp(dhp, dhp1, sizeof (*dhp)) && !damnit)
|
|
printf("memcmp failed\n");
|
|
|
|
if (!good_dumphdr(dhp))
|
|
return (0);
|
|
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Check for a valid header:
|
|
* 1. Valid magic number
|
|
* 2. A version number we understand.
|
|
* 3. "dump valid flag" on
|
|
*/
|
|
int
|
|
good_dumphdr(struct dumphdr *dhp)
|
|
{
|
|
if (dhp->dump_magic != DUMP_MAGIC) {
|
|
if (Verbose)
|
|
(void) fprintf(stderr,
|
|
"magic number mismatch: 0x%lx != 0x%lx\n",
|
|
dhp->dump_magic, DUMP_MAGIC);
|
|
return (0);
|
|
}
|
|
|
|
if (dhp->dump_version > DUMP_VERSION) {
|
|
(void) fprintf(stderr,
|
|
"Warning: %s version (%d) is older than dumpsys "
|
|
"version (%ld)\n",
|
|
progname, DUMP_VERSION, dhp->dump_version);
|
|
}
|
|
|
|
if (dhp->dump_pagesize != pagesize) {
|
|
(void) fprintf(stderr,
|
|
"Warning: dump pagesize (%ld) != system pagesize (%d)\n",
|
|
dhp->dump_pagesize, pagesize);
|
|
}
|
|
|
|
if (((dhp->dump_flags & DF_VALID) == 0) && !damnit) {
|
|
if (Verbose)
|
|
(void) fprintf(stderr,
|
|
"dump already processed by %s.\n", progname);
|
|
return (0);
|
|
}
|
|
|
|
/* no fatal errors */
|
|
return (1);
|
|
}
|
|
|
|
void
|
|
read_kmem(void)
|
|
{
|
|
int i;
|
|
kvm_t *lkd;
|
|
|
|
lkd = Kvm_open(sysname, NULL, NULL, O_RDONLY);
|
|
Kvm_nlist(lkd, current_nl);
|
|
|
|
/*
|
|
* Check that all the names we need are there
|
|
*/
|
|
for (i = 0; cursyms[i] != -1; i++)
|
|
if (current_nl[cursyms[i]].n_value == 0) {
|
|
(void) fprintf(stderr, "%s: %s not in namelist",
|
|
sysname, current_nl[cursyms[i]].n_name);
|
|
syslog(LOG_ERR, "%s: %s not in namelist",
|
|
sysname, current_nl[cursyms[i]].n_name);
|
|
exit(1);
|
|
}
|
|
if (sysname == NULL) {
|
|
Kvm_read(lkd, current_nl[X_VERSION].n_value,
|
|
(char *)&utsname, sizeof (utsname));
|
|
nullterm(utsname.version, sizeof (utsname.version));
|
|
}
|
|
(void) kvm_close(lkd);
|
|
}
|
|
|
|
void
|
|
nullterm(char *s, int len)
|
|
{
|
|
s[len - 1] = '\0';
|
|
while (*s++ != '\n') {
|
|
if (*s == '\0')
|
|
return;
|
|
}
|
|
*s = '\0';
|
|
}
|
|
|
|
void
|
|
check_kmem(void)
|
|
{
|
|
nullterm(dump_versionstr(dhp), strlen(dump_versionstr(dhp)) + 1);
|
|
if (!eq(utsname.version, dump_versionstr(dhp)) && sysname == 0) {
|
|
(void) fprintf(stderr,
|
|
"Warning: kernel version mismatch:\n\t%sand\n\t%s",
|
|
utsname.version, dump_versionstr(dhp));
|
|
syslog(LOG_WARNING,
|
|
"Warning: kernel version mismatch: %s and %s",
|
|
utsname.version, dump_versionstr(dhp));
|
|
}
|
|
}
|
|
|
|
int
|
|
get_crashtime(void)
|
|
{
|
|
if (dhp->dump_crashtime.tv_sec == 0) {
|
|
if (Verbose)
|
|
(void) fprintf(stderr, "Dump time not found.\n");
|
|
return (0);
|
|
}
|
|
(void) fprintf(stdout, "System went down at %s",
|
|
ctime(&dhp->dump_crashtime.tv_sec));
|
|
|
|
if (!damnit && (dhp->dump_crashtime.tv_sec < now - LEEWAY ||
|
|
dhp->dump_crashtime.tv_sec > now + LEEWAY)) {
|
|
|
|
(void) fprintf(stderr, "dump time is unreasonable\n");
|
|
return (0);
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
char *
|
|
path(char *file)
|
|
{
|
|
register char *cp;
|
|
|
|
cp = Malloc((u_int)(strlen(file) + strlen(dirname) + 2));
|
|
(void) strcpy(cp, dirname);
|
|
(void) strcat(cp, "/");
|
|
(void) strcat(cp, file);
|
|
return (cp);
|
|
}
|
|
|
|
int
|
|
check_space(void)
|
|
{
|
|
struct statvfs fsb;
|
|
longlong_t spacefree;
|
|
longlong_t dumpsize;
|
|
longlong_t minfree;
|
|
|
|
if (statvfs(dirname, &fsb) < 0) {
|
|
int oerrno = errno;
|
|
|
|
perror(dirname);
|
|
errno = oerrno;
|
|
syslog(LOG_ERR, "%s: %m", dirname);
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* From savecore(1M):
|
|
*
|
|
* "Before savecore writes out a core image, it reads a number
|
|
* from the file directory/minfree. This is the minimum number
|
|
* of kilobytes that must remain free on the file system con-
|
|
* taining directory. If there is less free space on the file
|
|
* system containing directory than the number of kilobytes
|
|
* specified in minfree, the core dump is not saved. If the
|
|
* minfree file does not exist, savecore always writes out the
|
|
* core file (assuming that a core dump was taken)."
|
|
*
|
|
* Historical note:
|
|
*
|
|
* All 4.x and early 5.x releases ignored the second sentence
|
|
* above, and implemented only the third sentence i.e. 'minfree'
|
|
* was treated as the number of Kbytes that should be available
|
|
* on the filesystem without including the size of the dump,
|
|
* rather than the minimum left -after- taking the dump.
|
|
*
|
|
* See 2001305, 1099788 and 1094160.
|
|
*/
|
|
|
|
spacefree = fsb.f_bavail * fsb.f_frsize;
|
|
minfree = 1024LL * read_number("minfree");
|
|
dumpsize = dhp->dump_nchunks * dhp->dump_chunksize
|
|
* dhp->dump_pagesize;
|
|
|
|
if (minfree > 0 && (spacefree - dumpsize) < minfree) {
|
|
(void) fprintf(stderr,
|
|
"%s: Dump omitted, not enough space on device\n",
|
|
progname);
|
|
syslog(LOG_WARNING,
|
|
"Dump omitted, not enough space on device");
|
|
return (0);
|
|
}
|
|
|
|
if (spacefree < dumpsize) {
|
|
(void) fprintf(stderr,
|
|
"%s: Dump will be saved, but free space threshold "
|
|
"will be crossed\n", progname);
|
|
syslog(LOG_WARNING,
|
|
"Dump will be saved, but free space threshold will "
|
|
"be crossed");
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
read_number(char *fn)
|
|
{
|
|
char lin[80];
|
|
register FILE *fp;
|
|
|
|
fp = fopen(path(fn), "r");
|
|
if (fp == NULL)
|
|
return (0);
|
|
if (fgets(lin, 80, fp) == NULL) {
|
|
(void) fclose(fp);
|
|
return (0);
|
|
}
|
|
(void) fclose(fp);
|
|
return (atoi(lin));
|
|
}
|
|
|
|
/*
|
|
* First, save the core image. Then build the namelist file, complete
|
|
* with module symbol tables.
|
|
*/
|
|
int
|
|
save_core(void)
|
|
{
|
|
char corefile[COREFSIZE];
|
|
register char *cp;
|
|
register int ifd, ofd, bounds;
|
|
register FILE *fp;
|
|
int saved = 0;
|
|
int nchunks;
|
|
int bitmapsize;
|
|
|
|
cp = Malloc((u_int)pagesize);
|
|
if (cp == 0) {
|
|
(void) fprintf(stderr,
|
|
"%s: Can't allocate i/o buffer.\n", progname);
|
|
return (0);
|
|
}
|
|
bounds = read_number("bounds");
|
|
/*
|
|
* Calculate some constants
|
|
*/
|
|
nchunks = dhp->dump_nchunks;
|
|
dumpsize = nchunks * dhp->dump_chunksize;
|
|
bitmapsize = howmany(dhp->dump_bitmapsize, pagesize);
|
|
|
|
/*
|
|
* We'll save everything, including the bit map
|
|
*/
|
|
ifd = dfd;
|
|
(void) Lseek(ifd, -(dhp->dump_dumpsize), SEEK_END);
|
|
|
|
(void) sprintf(cp, "vmcore.%d", bounds);
|
|
(void) strcpy(corefile, path(cp));
|
|
ofd = Create(corefile, 0644);
|
|
(void) fprintf(stdout, "Saving %d pages of image in vmcore.%d\n",
|
|
dumpsize, bounds);
|
|
syslog(LOG_NOTICE, "Saving %d pages of image in vmcore.%d\n",
|
|
dumpsize, bounds);
|
|
|
|
/* Save the first header */
|
|
if (!save_a_page(ifd, ofd, cp))
|
|
goto error;
|
|
|
|
/* Save the bitmap */
|
|
for (; bitmapsize > 0; bitmapsize--) {
|
|
if (!save_a_page(ifd, ofd, cp))
|
|
break;
|
|
}
|
|
|
|
/* Save the chunks */
|
|
for (; dumpsize > 0; dumpsize--) {
|
|
if (!save_a_page(ifd, ofd, cp))
|
|
break;
|
|
if ((++saved & 07) == 0) { /* every 8 pages */
|
|
(void) fprintf(stdout, "%6d pages saved\r",
|
|
saved);
|
|
(void) fflush(stdout);
|
|
}
|
|
}
|
|
|
|
/* Save the last header */
|
|
(void) save_a_page(ifd, ofd, cp);
|
|
|
|
error:
|
|
(void) fprintf(stdout, "%6d pages saved.\n", saved);
|
|
|
|
Fsync(ofd);
|
|
(void) close(ofd);
|
|
free(cp);
|
|
|
|
/*
|
|
* We have the core file. Build a namelist file w/ all the modules
|
|
* symbol tables.
|
|
*/
|
|
if (build_namelist(bounds, corefile) != 0) {
|
|
(void) fprintf(stderr,
|
|
"%s: error building namelist file.\n", progname);
|
|
return (0);
|
|
}
|
|
|
|
#ifdef SAVESWAP
|
|
/*
|
|
* Now save the swapped out uareas into vmswap.N
|
|
*/
|
|
save_swap(bounds);
|
|
#endif SAVESWAP
|
|
|
|
fp = fopen(path("bounds"), "w");
|
|
if (fp == NULL) {
|
|
(void) fprintf(stderr, "%s: ", progname);
|
|
perror("cannot update \"bounds\" file");
|
|
} else {
|
|
(void) fprintf(fp, "%d\n", bounds+1);
|
|
(void) fclose(fp);
|
|
}
|
|
|
|
/* success! */
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
save_a_page(int ifd, int ofd, char *cp)
|
|
{
|
|
register int n;
|
|
|
|
n = Read(ifd, cp, pagesize);
|
|
if (n == 0) {
|
|
syslog(LOG_WARNING, "WARNING: vmcore may be incomplete\n");
|
|
(void) fprintf(stderr,
|
|
"WARNING: vmcore may be incomplete\n");
|
|
return (0);
|
|
}
|
|
if (n <= 0) {
|
|
syslog(LOG_WARNING,
|
|
"WARNING: Dump area was too small - %d pages not saved",
|
|
dumpsize);
|
|
(void) fprintf(stderr,
|
|
"WARNING: Dump area was too small - %d pages not saved\n",
|
|
dumpsize);
|
|
return (0);
|
|
}
|
|
Write(ofd, cp, n);
|
|
return (1);
|
|
}
|
|
|
|
#ifdef SAVESWAP
|
|
#include <sys/proc.h>
|
|
#include <sys/user.h>
|
|
#include <vm/anon.h>
|
|
#include <machine/seg_u.h>
|
|
|
|
void
|
|
save_swap(int bounds)
|
|
{
|
|
int ifd, ofd;
|
|
char *cp, *Malloc(u_int);
|
|
char *vmunix, *vmcore, *vmswap;
|
|
kvm_t *kd;
|
|
struct proc *proc;
|
|
int nuptes; /* number of ptes to map uarea */
|
|
|
|
nuptes = roundup(sizeof (struct user), pagesize) / pagesize;
|
|
|
|
cp = Malloc(pagesize); /* get a buffer to work with */
|
|
(void) sprintf(cp, "unix.%d", bounds);
|
|
vmunix = path(cp);
|
|
(void) sprintf(cp, "vmcore.%d", bounds);
|
|
vmcore = path(cp);
|
|
(void) sprintf(cp, "vmswap.%d", bounds);
|
|
vmswap = path(cp);
|
|
|
|
ifd = Open("/dev/drum", O_RDONLY);
|
|
ofd = Create(vmswap, 0644);
|
|
kd = Kvm_open(vmunix, vmcore, NULL, O_RDONLY);
|
|
|
|
/*
|
|
* For each proc that is swapped out,
|
|
* copy its user pages from /dev/drum to vmswap.N.
|
|
* Note that this code is liberally borrowed from kvmgetu.c in
|
|
* libkvm, and even uses an internal libkvm function!
|
|
*/
|
|
/* XXX This is all wrong for 4.1; needs to be changed XXX */
|
|
for (kvm_setproc(kd); proc = kvm_nextproc(kd); /* void */) {
|
|
struct segu_data sud;
|
|
struct anon **app = &sud.su_swaddr[0];
|
|
int cc;
|
|
|
|
/* Skip loaded processes */
|
|
if ((proc->p_flag & SLOAD) != 0)
|
|
continue;
|
|
|
|
/*
|
|
* u-area information lives in the segu data structure
|
|
* pointed to by proc->p_useg. Obtain the contents of
|
|
* this structure before worrying about whether or not
|
|
* the u-area is swapped out.
|
|
*/
|
|
if (kvm_read(kd, (u_long)proc->p_useg, (char *)&sud,
|
|
sizeof (sud))
|
|
!= sizeof (sud)) {
|
|
(void) fprintf(stderr,
|
|
"%s: couldn't read seg_u for pid %d\n",
|
|
progname, proc->p_pid);
|
|
syslog(LOG_WARNING,
|
|
"couldn't read seg_u for pid %d\n",
|
|
proc->p_pid);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* u-area is swapped out. proc->p_useg->su_swaddr[i]
|
|
* contains an anon pointer for the swap space holding
|
|
* the corresponding u page. Note that swap space for
|
|
* a given u-area is not guaranteed to be contiguous.
|
|
*/
|
|
|
|
for (cc = 0; cc < nuptes; cc++) {
|
|
long swapoffset;
|
|
addr_t /* really a (struct vnode *) */ vp;
|
|
u_int off;
|
|
|
|
/* XXX _anonoffset is an internal libkvm function! */
|
|
swapoffset = _anonoffset(kd, app[cc], &vp, &off);
|
|
if (swapoffset == -1) {
|
|
(void) fprintf(stderr,
|
|
"%s: couldn't find uarea for pid %d\n",
|
|
progname, proc->p_pid);
|
|
syslog(LOG_WARNING,
|
|
"couldn't find uarea for pid %d\n",
|
|
proc->p_pid);
|
|
continue;
|
|
}
|
|
lseek(ifd, swapoffset, 0);
|
|
lseek(ofd, swapoffset, 0);
|
|
read(ifd, cp, pagesize);
|
|
write(ofd, cp, pagesize);
|
|
}
|
|
}
|
|
/* End of stolen code */
|
|
|
|
(void) close(ifd);
|
|
Fsync(ofd);
|
|
(void) close(ofd);
|
|
free(cp);
|
|
free(vmunix);
|
|
free(vmcore);
|
|
free(vmswap);
|
|
kvm_close(kd);
|
|
}
|
|
#endif SAVESWAP
|
|
|
|
void
|
|
clear_dump(void)
|
|
{
|
|
if (fflag) /* Don't clear if overridden */
|
|
return;
|
|
(void) Lseek(dfd, -pagesize, SEEK_END); /* Seek to last header */
|
|
dhp->dump_flags &= ~DF_VALID; /* turn off valid bit */
|
|
Write(dfd, (char *)dhp, sizeof (*dhp)); /* re-write the header */
|
|
}
|
|
|
|
void
|
|
usage(int c)
|
|
{
|
|
if (c == 'f')
|
|
(void) fprintf(stderr, "-f requires dumpfile name");
|
|
(void) fprintf(stderr, "usage: %s [-v] dirname [ system ]\n",
|
|
progname);
|
|
}
|
|
|
|
/*
|
|
* Prints the module names at the time of crash in an orderly way.
|
|
* Called only in the verbose mode.
|
|
*/
|
|
static void
|
|
print_modname(const char *const modname)
|
|
{
|
|
static int mod_count = 0;
|
|
|
|
(void) fprintf(stdout, "\t%-20s%c", modname,
|
|
(++mod_count % 3) ? NULL : '\n');
|
|
fflush(stdout);
|
|
}
|
|
|
|
/*
|
|
* Using information from our core file, create a namelist file (with the
|
|
* help of /dev/ksyms) that contains all the symbol information for all
|
|
* modules loaded in the system at the time of the crash.
|
|
*/
|
|
int
|
|
build_namelist(int bounds, char *corefile)
|
|
{
|
|
/* The two symbols we're interested in */
|
|
static struct nlist nd[] = {
|
|
{ "default_path", 0, 0, 0, 0, 0 },
|
|
{ "modules", 0, 0, 0, 0, 0 },
|
|
{ "", 0, 0, 0, 0, 0 },
|
|
};
|
|
Elf32_Sym *symbols; /* temporary symbol table */
|
|
Elf32_Sym *locals; /* start of locals in temporary */
|
|
int nlocals; /* number of local symbols */
|
|
Elf32_Sym *globals; /* start of globals in temporary */
|
|
int nglobals; /* number of nonlocal symbols */
|
|
int loc_size; /* total size of combined locals */
|
|
int glb_size; /* total size of combined globals */
|
|
int str_size; /* total size of string table */
|
|
int i_nld, o_nld; /* i/o namelist descriptors */
|
|
int mod_fd; /* module file descriptor */
|
|
Elf32_Shdr *shp; /* used for relocation */
|
|
Elf32_Shdr *shdr; /* used for libelf routines */
|
|
register int cnt; /* temp counter */
|
|
Elf *elf; /* output file's elf desc */
|
|
Elf *mod_ed; /* current module's elf desc */
|
|
Elf32_Ehdr *hdr; /* temp elf hdr */
|
|
Elf_Scn *sec, *sym_sec = 0, *str_sec = 0;
|
|
Elf_Scn *mod_symsec, *mod_strsec;
|
|
Elf_Scn *dyn_symsec, *dyn_strsec;
|
|
Elf_Data *mod_symdata, *mod_strdata, *sym_data, *str_data;
|
|
Elf32_Sym *loc_p, *glb_p, *tp; /* symbol processing */
|
|
Elf32_Sym *wlk_sym; /* ditto */
|
|
char *strings; /* temporary string table */
|
|
char *wlk_str; /* string table processing */
|
|
Elf32_Sym *wlk_symbols; /* coalesced symtab processing */
|
|
char *symname; /* symbol name */
|
|
char *str_p; /* working strings table ptr */
|
|
u_int str_ndx = 1; /* index into string table */
|
|
register char *sect_name; /* Elf section name */
|
|
kvm_t *kd; /* kvm_t descriptor for namelist */
|
|
char *ptr; /* buffer for lvalue */
|
|
char *tbuf; /* scratch */
|
|
char namelist_buf[10]; /* namelist name buffer */
|
|
char *namelist = &namelist_buf[0];
|
|
char default_path[DEFPATHSIZE];
|
|
register struct loaded *wlk; /* list walking ptr for loaded */
|
|
struct loaded *head = (struct loaded *)0;
|
|
u_int bss;
|
|
|
|
kd = Kvm_open(sysname, corefile, (char *)0, O_RDONLY);
|
|
Kvm_nlist(kd, nd);
|
|
if (nd[0].n_type == 0 || nd[1].n_type == 0) {
|
|
(void) fprintf(stderr, "%s: nlist of symbols: "
|
|
"'default_path' and 'modules' in namelist "
|
|
"file '%s' failed.\n", progname, sysname);
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* get default_path.
|
|
*/
|
|
Kvm_read(kd, nd[0].n_value, (char *)&ptr, sizeof (char *));
|
|
if (kvm_read(kd, (u_long)ptr, &default_path[0], DEFPATHSIZE) < 0) {
|
|
(void) fprintf(stderr, "%s: kernel read error\n",
|
|
progname);
|
|
syslog(LOG_ERR, "kernel read error");
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* get list of accessable modules.
|
|
*/
|
|
get_mod_list(kd, nd[1].n_value, default_path, &head);
|
|
kvm_close(kd);
|
|
|
|
/* get size totals for non-kernel modules */
|
|
loc_size = glb_size = str_size = nlocals = nglobals = 0;
|
|
for (wlk = head; wlk != (struct loaded *)0; wlk = wlk->next) {
|
|
if (wlk->mp == (struct module *)0)
|
|
continue; /* failed to read module */
|
|
nlocals += wlk->cd.nlcls;
|
|
nglobals += wlk->cd.nglbs;
|
|
loc_size += (wlk->cd.sym_size -
|
|
(wlk->cd.nglbs * sizeof (Elf32_Sym)));
|
|
glb_size += (wlk->cd.sym_size -
|
|
(wlk->cd.nlcls * sizeof (Elf32_Sym)));
|
|
str_size += wlk->cd.str_size;
|
|
}
|
|
if (((loc_size + glb_size) % sizeof (Elf32_Sym)) != 0) {
|
|
(void) fprintf(stderr,
|
|
"%s: symbol table size is not a multiple of entry size.\n",
|
|
progname);
|
|
return (-1);
|
|
}
|
|
|
|
locals = Malloc(loc_size);
|
|
loc_p = locals;
|
|
globals = Malloc(glb_size);
|
|
glb_p = globals;
|
|
strings = Malloc(str_size);
|
|
str_p = strings;
|
|
|
|
/* set up for undefined symbols */
|
|
*str_p++ = '\0';
|
|
|
|
#ifdef DEBUG
|
|
(void) fprintf(stdout, "locals: 0x%x, globals: 0x%x, strings: 0x%x\n",
|
|
locals, globals, strings);
|
|
(void) fprintf(stdout, "size of locals in symbol table: 0x%x\n",
|
|
loc_size);
|
|
(void) fprintf(stdout, "size of globals in symbol table: 0x%x\n",
|
|
glb_size);
|
|
(void) fprintf(stdout, "size of string table: 0x%x\n", str_size);
|
|
#endif /* DEBUG */
|
|
|
|
/*
|
|
* for each module in the list, open the module file, access
|
|
* it's symbol/string setions, transfer locals/globals to the
|
|
* image. relocate symbol values to load address. Toss any
|
|
* undefined symbols (We hope they're defined in the kernel
|
|
* anyway)
|
|
*/
|
|
if (Verbose)
|
|
(void) fprintf(stdout,
|
|
"Modules loaded at the time of crash:\n");
|
|
for (wlk = head; wlk != (struct loaded *)0; wlk = wlk->next) {
|
|
if (wlk->mp == (struct module *)0)
|
|
continue; /* failed to find module */
|
|
if (Verbose)
|
|
print_modname(wlk->modname);
|
|
if ((mod_fd = open(wlk->path, O_RDONLY)) < 0) {
|
|
(void) fprintf(stderr,
|
|
"%s: Warning: cannot open module '%s': ",
|
|
progname, wlk->path);
|
|
perror(wlk->path);
|
|
syslog(LOG_WARNING, "cannot open module '%s': %m",
|
|
wlk->path);
|
|
continue; /* skip it */
|
|
}
|
|
elf_version(EV_CURRENT);
|
|
if ((mod_ed = elf_begin(mod_fd, ELF_C_READ, (Elf *)0)) ==
|
|
0) {
|
|
(void) fprintf(stderr,
|
|
"%s: Warning: can't get ELF descriptor for %s\n",
|
|
progname, wlk->path);
|
|
syslog(LOG_WARNING,
|
|
"can't get ELF descriptor for %s", wlk->path);
|
|
(void) close(mod_fd);
|
|
continue;
|
|
}
|
|
if (elf_kind(mod_ed) != ELF_K_ELF) {
|
|
(void) fprintf(stderr,
|
|
"%s: Warning: '%s'is not an ELF file.\n",
|
|
progname, wlk->path);
|
|
syslog(LOG_WARNING, "'%s'is not an ELF file.",
|
|
wlk->path);
|
|
elf_end(mod_ed);
|
|
(void) close(mod_fd);
|
|
continue;
|
|
}
|
|
hdr = elf32_getehdr(mod_ed);
|
|
mod_symsec = mod_strsec = (Elf_Scn *)0;
|
|
dyn_symsec = dyn_strsec = (Elf_Scn *)0;
|
|
for (cnt = 1, sec = elf_getscn(mod_ed, cnt);
|
|
(shdr = elf32_getshdr(sec)) != 0;
|
|
sec = elf_nextscn(mod_ed, sec), cnt++) {
|
|
if (shdr->sh_type != SHT_SYMTAB &&
|
|
shdr->sh_type != SHT_STRTAB &&
|
|
shdr->sh_type != SHT_DYNSYM)
|
|
continue;
|
|
sect_name = elf_strptr(mod_ed, hdr->e_shstrndx,
|
|
shdr->sh_name);
|
|
|
|
if (eq(sect_name, ".dynsym"))
|
|
dyn_symsec = sec;
|
|
if (eq(sect_name, ".dynstr"))
|
|
dyn_strsec = sec;
|
|
if (eq(sect_name, ".symtab"))
|
|
mod_symsec = sec;
|
|
if (eq(sect_name, ".strtab"))
|
|
mod_strsec = sec;
|
|
}
|
|
/*
|
|
* Use dynamic sections.
|
|
* XXX We make sure that shdrs is NULL.
|
|
* If the kernel linker used the dynamic symbol table
|
|
* that was mapped into the image, then it won't
|
|
* have section header information.
|
|
*/
|
|
if (wlk == head && wlk->mp->shdrs == NULL &&
|
|
dyn_symsec && dyn_strsec) {
|
|
mod_symsec = dyn_symsec;
|
|
mod_strsec = dyn_strsec;
|
|
}
|
|
if (mod_symsec == (Elf_Scn *)0 ||
|
|
mod_strsec == (Elf_Scn *)0) {
|
|
(void) fprintf(stderr,
|
|
"%s: Warning: '%s' is missing symbol "
|
|
"or string table(s).\n", progname, wlk->path);
|
|
syslog(LOG_WARNING,
|
|
"'%s' is missing symbol or string table(s).",
|
|
wlk->path);
|
|
elf_end(mod_ed);
|
|
(void) close(mod_fd);
|
|
continue;
|
|
}
|
|
mod_symdata = elf_getdata(mod_symsec, (Elf_Data *)0);
|
|
if (mod_symdata == 0 || mod_symdata->d_size == 0) {
|
|
(void) fprintf(stderr, "%s: Warning: '%s' has an "
|
|
"empty symbol table section.\n",
|
|
progname, wlk->path);
|
|
syslog(LOG_WARNING,
|
|
"'%s' has an empty symbol table section.",
|
|
wlk->path);
|
|
elf_end(mod_ed);
|
|
(void) close(mod_fd);
|
|
continue;
|
|
}
|
|
mod_strdata = elf_getdata(mod_strsec, (Elf_Data *)0);
|
|
if (mod_strdata == 0 || mod_strdata->d_size == 0) {
|
|
(void) fprintf(stderr, "%s: Warning: '%s' has an "
|
|
"empty string table section.\n",
|
|
progname, wlk->path);
|
|
syslog(LOG_WARNING,
|
|
"'%s' has an empty string table section.",
|
|
wlk->path);
|
|
elf_end(mod_ed);
|
|
(void) close(mod_fd);
|
|
continue;
|
|
}
|
|
wlk_str = mod_strdata->d_buf;
|
|
if (mod_strdata->d_size != wlk->cd.str_size) {
|
|
(void) fprintf(stderr,
|
|
"%s: Warning: mismatched core/file string "
|
|
"table: %s\n", progname, wlk->modname);
|
|
syslog(LOG_WARNING,
|
|
"mismatched core/file string table: '%s'",
|
|
wlk->modname);
|
|
elf_end(mod_ed);
|
|
(void) close(mod_fd);
|
|
continue;
|
|
}
|
|
wlk_sym = mod_symdata->d_buf;
|
|
if (mod_symdata->d_size != wlk->cd.sym_size) {
|
|
(void) fprintf(stderr,
|
|
"%s: Warning: mismatched core/file symbol "
|
|
"table: %s\n", progname, wlk->modname);
|
|
syslog(LOG_WARNING,
|
|
"mismatched core/file symbol table: '%s'",
|
|
wlk->modname);
|
|
elf_end(mod_ed);
|
|
(void) close(mod_fd);
|
|
continue;
|
|
}
|
|
bss = ALIGN(wlk->mp->bss, wlk->mp->bss_align);
|
|
/*
|
|
* transfer the symbols and strings...
|
|
*/
|
|
for (cnt = 0; cnt < wlk->mp->nsyms; ++cnt) {
|
|
symname = (char *)((u_int)wlk_str +
|
|
(u_int)wlk_sym->st_name);
|
|
if (ELF32_ST_BIND(wlk_sym->st_info) == STB_LOCAL) {
|
|
/*
|
|
* locals first...
|
|
*
|
|
* We need the first "UNDEF" in the
|
|
* kernel file to satisfy the elf format.
|
|
*/
|
|
if ((wlk_sym->st_shndx != SHN_UNDEF) ||
|
|
(wlk == head && cnt == 0)) {
|
|
/* struct copy */
|
|
*loc_p = *wlk_sym++;
|
|
tp = loc_p;
|
|
loc_p++;
|
|
} else {
|
|
/* undefined. toss it */
|
|
wlk_sym++;
|
|
continue;
|
|
}
|
|
} else {
|
|
/* then the globals.... */
|
|
if (wlk_sym->st_shndx != SHN_UNDEF) {
|
|
/* struct copy */
|
|
*glb_p = *wlk_sym++;
|
|
tp = glb_p;
|
|
glb_p++;
|
|
} else {
|
|
/* undefined. toss it */
|
|
wlk_sym++;
|
|
continue;
|
|
}
|
|
}
|
|
/*
|
|
* Relocate symbol value to previous load addr.
|
|
* But don't adjust values in kernel module.
|
|
*/
|
|
if (wlk != head) {
|
|
if (tp->st_shndx < SHN_LORESERVE) {
|
|
shp = (Elf32_Shdr *)
|
|
((u_long)wlk->mp->shdrs +
|
|
(tp->st_shndx *
|
|
wlk->mp->hdr.e_shentsize));
|
|
tp->st_value += shp->sh_addr;
|
|
}
|
|
if (tp->st_shndx == SHN_COMMON) {
|
|
Elf32_Sym *ep, *sp;
|
|
|
|
/*
|
|
* Scan for an allocated copy of
|
|
* this COMMON symbol.
|
|
* XXX This is *horribly* slow,
|
|
* should use a hash table.
|
|
*/
|
|
ep = glb_p - 1;
|
|
for (sp = globals; sp < ep; sp++) {
|
|
if (sp->st_name && symname &&
|
|
eq(strings + sp->st_name,
|
|
symname)) {
|
|
tp->st_shndx = SHN_ABS;
|
|
tp->st_value =
|
|
sp->st_value;
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
* Didn't find it, calculate it.
|
|
*/
|
|
if (sp >= ep) {
|
|
bss = ALIGN(bss, tp->st_value);
|
|
tp->st_value = bss;
|
|
bss += tp->st_size;
|
|
tp->st_shndx = SHN_ABS;
|
|
}
|
|
}
|
|
}
|
|
/* update string table */
|
|
if (tp->st_name != 0) {
|
|
tp->st_name = str_ndx;
|
|
while (*symname != '\0') {
|
|
*str_p = *symname++;
|
|
str_p++;
|
|
str_ndx++;
|
|
}
|
|
/* terminate the string with a null. */
|
|
*str_p = '\0';
|
|
str_p++; /* step over the null char */
|
|
str_ndx++;
|
|
}
|
|
}
|
|
/* we're done with this module. Free it's data. */
|
|
(void) elf_end(mod_ed);
|
|
if (wlk != head) {
|
|
/* then these are dynamically allocated. */
|
|
free(wlk->path);
|
|
if (*wlk->modname != '/')
|
|
free(wlk->modname);
|
|
}
|
|
if (wlk->mp->shdrs)
|
|
free(wlk->mp->shdrs);
|
|
free(wlk->mp);
|
|
(void) close(mod_fd);
|
|
}
|
|
|
|
/* free list */
|
|
for (wlk = head; wlk != (struct loaded *)0; wlk = wlk->next)
|
|
free(wlk);
|
|
|
|
(void) fprintf(stdout, "\nProcessing modules: Done.\n");
|
|
|
|
/*
|
|
* We probably threw away some symbols and their associated strings.
|
|
* Coalesce the remaining locals and globals into one symbol
|
|
* table.
|
|
*/
|
|
|
|
/* Reset symbol / string counts to reflect syms/strs remaining */
|
|
loc_size = ((u_long)loc_p - (u_long)locals);
|
|
glb_size = ((u_long)glb_p - (u_long)globals);
|
|
str_size = (u_long)str_p - (u_long)strings;
|
|
|
|
symbols = Malloc(loc_size + glb_size);
|
|
wlk_symbols = symbols;
|
|
|
|
(void) memcpy(wlk_symbols, locals, loc_size);
|
|
wlk_symbols = (Elf32_Sym *)((u_long)wlk_symbols + (u_long)loc_size);
|
|
(void) memcpy(wlk_symbols, globals, glb_size);
|
|
|
|
/* free up symbol buffers. We don't need them now */
|
|
free(locals);
|
|
free(globals);
|
|
|
|
/*
|
|
* We have what we need. Create the namelist file, substitute our
|
|
* symbol and string tables for the kernel's, and update our
|
|
* namelist file.
|
|
*/
|
|
i_nld = Open(sysname, O_RDONLY);
|
|
(void) sprintf(namelist, "unix.%d", bounds);
|
|
namelist = path(namelist);
|
|
(void) fprintf(stdout, "Constructing Namelist file: %s\n", namelist);
|
|
syslog(LOG_NOTICE, "Constructing Namelist file: %s", namelist);
|
|
o_nld = Create(namelist, 0644);
|
|
tbuf = Malloc((unsigned)pagesize);
|
|
while ((cnt = Read(i_nld, tbuf, pagesize)) > 0)
|
|
Write(o_nld, tbuf, cnt);
|
|
(void) close(i_nld);
|
|
Fsync(o_nld);
|
|
(void) close(o_nld);
|
|
free(tbuf);
|
|
|
|
/* reopen namelist file - RDWR */
|
|
o_nld = Open(namelist, O_RDWR);
|
|
|
|
elf_version(EV_CURRENT);
|
|
(void) elf_errno();
|
|
if ((elf = elf_begin(o_nld, ELF_C_RDWR, (Elf *)0)) == 0) {
|
|
(void) fprintf(stderr,
|
|
"%s: cannot get ELF descriptor for %s (%s)\n",
|
|
progname, namelist, elf_errmsg(elf_errno()));
|
|
return (-1);
|
|
}
|
|
if (elf_kind(elf) != ELF_K_ELF) {
|
|
(void) fprintf(stderr, "%s: '%s' is not an ELF file.\n",
|
|
progname, sysname);
|
|
return (-1);
|
|
}
|
|
hdr = elf32_getehdr(elf);
|
|
|
|
for (cnt = 1, sec = elf_getscn(elf, cnt); /* continued line */
|
|
(shdr = elf32_getshdr(sec)) != 0; sec = elf_nextscn(elf, sec),
|
|
cnt++) {
|
|
if (shdr->sh_type != SHT_SYMTAB &&
|
|
shdr->sh_type != SHT_STRTAB)
|
|
continue;
|
|
sect_name = elf_strptr(elf, hdr->e_shstrndx, shdr->sh_name);
|
|
if (eq(sect_name, ".symtab"))
|
|
sym_sec = sec;
|
|
if (eq(sect_name, ".strtab"))
|
|
str_sec = sec;
|
|
}
|
|
if (sym_sec == (Elf_Scn *)0 || str_sec == (Elf_Scn *)0) {
|
|
(void) fprintf(stderr,
|
|
"%s: '%s' is missing symbol or string table(s).\n",
|
|
progname, sysname);
|
|
return (-1);
|
|
}
|
|
sym_data = elf_getdata(sym_sec, (Elf_Data *)0);
|
|
if (sym_data == 0 || sym_data->d_size == 0) {
|
|
(void) fprintf(stderr,
|
|
"%s: '%s' has an empty symbol table section.\n",
|
|
progname, sysname);
|
|
return (-1);
|
|
}
|
|
str_data = elf_getdata(str_sec, (Elf_Data *)0);
|
|
if (str_data == 0 || str_data->d_size == 0) {
|
|
(void) fprintf(stderr,
|
|
"%s: '%s' has an empty string table section.\n",
|
|
progname, sysname);
|
|
return (-1);
|
|
}
|
|
sym_data->d_buf = symbols;
|
|
sym_data->d_size = loc_size + glb_size;
|
|
str_data->d_buf = strings;
|
|
str_data->d_size = str_size;
|
|
(void) elf_flagdata(sym_data, ELF_C_SET, ELF_F_DIRTY);
|
|
(void) elf_flagdata(str_data, ELF_C_SET, ELF_F_DIRTY);
|
|
(void) elf_update(elf, ELF_C_WRITE);
|
|
(void) elf_end(elf);
|
|
(void) close(o_nld);
|
|
(void) fprintf(stdout, "Namelist file complete.\n");
|
|
syslog(LOG_NOTICE, "Namelist file complete.");
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Get the modctl list from the core file. We need some information from
|
|
* the core image about the module to handle relocation. We need the
|
|
* virtual addresses/contents of each module's section headers.
|
|
*
|
|
* This information is contained in the module's struct module. We'll
|
|
* need to get the "name" of the module - but the pointers contained in
|
|
* the module struct will give us the values we need. Use the loaded
|
|
* struct to store our list.
|
|
*
|
|
* We also need to locate our modules.
|
|
*/
|
|
void
|
|
get_mod_list(kvm_t *kd, u_long value, char *search, struct loaded **hd)
|
|
{
|
|
struct modctl tmp, *modctl_head, *modctl_walk;
|
|
struct loaded *wlk;
|
|
Elf32_Shdr tmp_shdr;
|
|
register char *cp;
|
|
register char *module_name;
|
|
register int shdrs_size;
|
|
|
|
/*
|
|
* get the head of the list.
|
|
*/
|
|
Kvm_read(kd, value, (char *)&tmp, sizeof (tmp));
|
|
|
|
if (tmp.mod_id != 0) { /* sanity check */
|
|
(void) fprintf(stderr,
|
|
"%s: FATAL: struct modctl list format not recognized. "
|
|
"Mismatched %s/%s?\n", progname, progname, sysname);
|
|
syslog(LOG_CRIT, "struct modctl list format not "
|
|
"recognized. Mismatched %s/%s?", progname, sysname);
|
|
exit(1);
|
|
}
|
|
|
|
modctl_head = &tmp;
|
|
modctl_walk = modctl_head;
|
|
|
|
*hd = Malloc(sizeof (struct loaded));
|
|
(void) memset(*hd, 0, sizeof (struct loaded));
|
|
wlk = *hd;
|
|
for (;;) {
|
|
if (modctl_walk->mod_mp == 0)
|
|
goto skip;
|
|
wlk->mp = Malloc(sizeof (struct module));
|
|
|
|
/* get module struct */
|
|
Kvm_read(kd, (u_long)modctl_walk->mod_mp, (char *)wlk->mp,
|
|
sizeof (struct module));
|
|
|
|
module_name = Malloc(MOD_MAXPATH);
|
|
if (kvm_read(kd, (u_long)modctl_walk->mod_filename,
|
|
module_name, MOD_MAXPATH) < 0) {
|
|
(void) fprintf(stderr, "%s: kvm_read error\n",
|
|
progname);
|
|
syslog(LOG_ERR, "kernel read error");
|
|
exit(1);
|
|
}
|
|
if (modctl_walk->mod_id == 0) {
|
|
/*
|
|
* We will use the info in core kernel module to
|
|
* obtain our path to "sysname".
|
|
* We have defaulted to running kernel for info til now.
|
|
*/
|
|
wlk->modname = (char *)0; /* kernel mod */
|
|
sysname = module_name;
|
|
} else {
|
|
wlk->modname = module_name;
|
|
}
|
|
|
|
/* locate a module matching this name */
|
|
if (find_module(wlk, search) != 0) {
|
|
/*
|
|
* Non-fatal error occurred. (has been reported).
|
|
* We simply won't process this module.
|
|
*/
|
|
free(wlk->mp);
|
|
wlk->path = (char *)0;
|
|
wlk->mp = (struct module *)0;
|
|
} else {
|
|
/* update sym/str table size totals */
|
|
Kvm_read(kd, (u_long)wlk->mp->symhdr,
|
|
(char *)&tmp_shdr, sizeof (tmp_shdr));
|
|
if (tmp_shdr.sh_size == 0) {
|
|
(void) fprintf(stderr, "%s: WARNING: '%s' "
|
|
"has a zero size symbol table.\n",
|
|
progname, module_name);
|
|
syslog(LOG_WARNING,
|
|
"'%s' has a zero size symbol table.",
|
|
module_name);
|
|
free(wlk->mp);
|
|
wlk->path = (char *)0;
|
|
wlk->mp = (struct module *)0;
|
|
} else {
|
|
wlk->cd.sym_size = tmp_shdr.sh_size;
|
|
/*
|
|
* Index of first global == number of locals.
|
|
*/
|
|
wlk->cd.nlcls = tmp_shdr.sh_info;
|
|
wlk->cd.nglbs = (tmp_shdr.sh_size /
|
|
tmp_shdr.sh_entsize) - tmp_shdr.sh_info;
|
|
|
|
/* strings */
|
|
Kvm_read(kd, (u_long)wlk->mp->strhdr,
|
|
(char *)&tmp_shdr, sizeof (tmp_shdr));
|
|
wlk->cd.str_size += tmp_shdr.sh_size;
|
|
|
|
/* get section hdrs */
|
|
shdrs_size = wlk->mp->hdr.e_shentsize *
|
|
wlk->mp->hdr.e_shnum;
|
|
cp = Malloc(shdrs_size);
|
|
if (wlk->mp->shdrs) {
|
|
Kvm_read(kd, (u_long)wlk->mp->shdrs, cp,
|
|
shdrs_size);
|
|
wlk->mp->shdrs = cp;
|
|
}
|
|
}
|
|
}
|
|
skip:
|
|
/* next modctl list element */
|
|
if (modctl_walk->mod_next == (struct modctl *)value) {
|
|
/* circled back to the kernel module */
|
|
wlk->next = (struct loaded *)0;
|
|
break;
|
|
} else {
|
|
Kvm_read(kd, (u_long)modctl_walk->mod_next,
|
|
(char *)&tmp, sizeof (tmp));
|
|
modctl_walk = &tmp;
|
|
wlk->next = Malloc(sizeof (struct loaded));
|
|
wlk = wlk->next;
|
|
(void) memset(wlk, 0, sizeof (struct loaded));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Find the module in module path. Returns 0 on success,
|
|
* -1 on failure.
|
|
*
|
|
* If a module has a null name, it is assumed to be the kernel module. If
|
|
* a module's name begins with "/", it is assumed to have been loaded
|
|
* via a manual modload (no path search is done).
|
|
*/
|
|
int
|
|
find_module(struct loaded *mod, char *path)
|
|
{
|
|
register char *el, *cp;
|
|
char *p_buf, *tmp_buf;
|
|
register int len;
|
|
register char c;
|
|
|
|
if (mod->modname == (char *)0) {
|
|
/* kernel module */
|
|
mod->modname = sysname;
|
|
mod->path = sysname;
|
|
goto verify_mod;
|
|
}
|
|
if (*mod->modname == '/') {
|
|
/* manually loaded module. */
|
|
mod->path = mod->modname;
|
|
goto verify_mod;
|
|
}
|
|
|
|
/*
|
|
* Look in the module path for the module..
|
|
*/
|
|
p_buf = Malloc(MOD_MAXPATH);
|
|
len = strlen(path) + 1;
|
|
tmp_buf = Malloc(len);
|
|
(void) strcpy(tmp_buf, path);
|
|
mod->path = (char *)0;
|
|
for (el = tmp_buf, cp = tmp_buf; cp < (tmp_buf + len); cp++) {
|
|
if ((c = *cp) == '\t' || c == ' ' || c == ':' || c == '\0') {
|
|
*cp = '\0';
|
|
/* element of path. */
|
|
(void) sprintf(p_buf, "%s/%s", el,
|
|
mod->modname);
|
|
if (access(p_buf, R_OK | F_OK) < 0) {
|
|
el = (char *)((u_long)cp + 1);
|
|
continue;
|
|
}
|
|
mod->path = p_buf;
|
|
break;
|
|
}
|
|
}
|
|
free(tmp_buf);
|
|
if (mod->path == (char *)0) {
|
|
(void) fprintf(stderr,
|
|
"%s: Warning: can't find '%s' in module path: %s\n",
|
|
progname, mod->modname, path);
|
|
syslog(LOG_WARNING, "can't find '%s' in module path: %s",
|
|
mod->modname, path);
|
|
free(p_buf);
|
|
return (-1);
|
|
} else
|
|
return (0);
|
|
|
|
verify_mod:
|
|
if (access(mod->path, R_OK | F_OK) < 0) {
|
|
(void) fprintf(stderr,
|
|
"%s: Warning: can't access '%s'.\n", progname,
|
|
mod->modname);
|
|
syslog(LOG_WARNING, "can't access '%s'.",
|
|
mod->modname);
|
|
return (-1);
|
|
} else
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Versions of kvm routines that exit on error.
|
|
*/
|
|
kvm_t *
|
|
Kvm_open(char *namelist, char *corefile, char *swapfile, int flag)
|
|
{
|
|
kvm_t *kd;
|
|
|
|
kd = kvm_open(namelist, corefile, swapfile, flag,
|
|
Verbose ? progname : NULL);
|
|
if (kd == NULL) {
|
|
if (Verbose)
|
|
(void) fprintf(stderr, "%s: kvm_open failed\n",
|
|
progname);
|
|
exit(1);
|
|
}
|
|
return (kd);
|
|
}
|
|
|
|
void
|
|
Kvm_nlist(kvm_t *kd, struct nlist nl[])
|
|
{
|
|
if (kvm_nlist(kd, nl) < 0) {
|
|
(void) fprintf(stderr, "%s: no namelist\n", progname);
|
|
syslog(LOG_ERR, "no namelist");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void
|
|
Kvm_read(kvm_t *kd, u_long addr, char *buf, u_int nbytes)
|
|
{
|
|
if (kvm_read(kd, addr, buf, nbytes) != nbytes) {
|
|
(void) fprintf(stderr, "%s: kernel read error\n",
|
|
progname);
|
|
syslog(LOG_ERR, "kernel read error");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Versions of std routines that exit on error.
|
|
*/
|
|
int
|
|
Open(char *name, int rw)
|
|
{
|
|
int fd;
|
|
|
|
fd = open(name, rw);
|
|
if (fd < 0) {
|
|
int oerrno = errno;
|
|
|
|
(void) fprintf(stderr, "%s: ", progname);
|
|
errno = oerrno;
|
|
perror(name);
|
|
errno = oerrno;
|
|
syslog(LOG_ERR, "%s: %m", name);
|
|
exit(1);
|
|
}
|
|
return (fd);
|
|
}
|
|
|
|
int
|
|
Read(int fd, char *buff, int size)
|
|
{
|
|
int ret;
|
|
|
|
ret = read(fd, buff, size);
|
|
if (ret < 0) {
|
|
int oerrno = errno;
|
|
|
|
(void) fprintf(stderr, "%s: ", progname);
|
|
perror("read");
|
|
errno = oerrno;
|
|
syslog(LOG_ERR, "read: %m");
|
|
exit(1);
|
|
}
|
|
return (ret);
|
|
}
|
|
|
|
int
|
|
Create(char *file, int mode)
|
|
{
|
|
register int fd;
|
|
|
|
fd = creat(file, mode);
|
|
if (fd < 0) {
|
|
int oerrno = errno;
|
|
|
|
(void) fprintf(stderr, "%s: ", progname);
|
|
errno = oerrno;
|
|
perror(file);
|
|
errno = oerrno;
|
|
syslog(LOG_ERR, "%s: %m", file);
|
|
exit(1);
|
|
}
|
|
return (fd);
|
|
}
|
|
|
|
void
|
|
Write(int fd, char *buf, int size)
|
|
{
|
|
if (write(fd, buf, size) < size) {
|
|
int oerrno = errno;
|
|
|
|
(void) fprintf(stderr, "%s: ", progname);
|
|
perror("write");
|
|
errno = oerrno;
|
|
syslog(LOG_ERR, "write: %m");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void
|
|
Fsync(int fd)
|
|
{
|
|
if (fsync(fd) < 0) {
|
|
int oerrno = errno;
|
|
|
|
(void) fprintf(stderr, "%s: ", progname);
|
|
perror("fsync");
|
|
errno = oerrno;
|
|
syslog(LOG_ERR, "write: %m");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
off_t
|
|
Lseek(int fd, off_t offset, int whence)
|
|
{
|
|
off_t ret;
|
|
|
|
ret = lseek(fd, offset, whence);
|
|
if (ret == -1) {
|
|
int oerrno = errno;
|
|
|
|
(void) fprintf(stderr, "%s: ", progname);
|
|
perror("lseek");
|
|
errno = oerrno;
|
|
syslog(LOG_ERR, "lseek: %m");
|
|
exit(1);
|
|
}
|
|
return (ret);
|
|
}
|
|
|
|
void *
|
|
Malloc(size_t size)
|
|
{
|
|
register void *tmp;
|
|
|
|
if ((tmp = malloc(size)) == (void *)0) {
|
|
int oerrno = errno;
|
|
(void) fprintf(stderr, "%s: ", progname);
|
|
perror("malloc");
|
|
errno = oerrno;
|
|
syslog(LOG_ERR, "malloc: %m");
|
|
exit(1);
|
|
}
|
|
return (tmp);
|
|
}
|