diff options
Diffstat (limited to 'lib/libkvm/kvm.c')
-rw-r--r-- | lib/libkvm/kvm.c | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/lib/libkvm/kvm.c b/lib/libkvm/kvm.c new file mode 100644 index 0000000..dfc21e4 --- /dev/null +++ b/lib/libkvm/kvm.c @@ -0,0 +1,455 @@ +/*- + * Copyright (c) 1989, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software developed by the Computer Systems + * Engineering group at Lawrence Berkeley Laboratory under DARPA contract + * BG 91-66 and contributed to Berkeley. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)kvm.c 8.2 (Berkeley) 2/13/94"; +#endif +#endif /* LIBC_SCCS and not lint */ + +#include <sys/param.h> +#include <sys/user.h> +#include <sys/proc.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/linker.h> + +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/swap_pager.h> + +#include <machine/vmparam.h> + +#include <ctype.h> +#include <fcntl.h> +#include <kvm.h> +#include <limits.h> +#include <nlist.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "kvm_private.h" + +/* from src/lib/libc/gen/nlist.c */ +int __fdnlist(int, struct nlist *); + +char * +kvm_geterr(kd) + kvm_t *kd; +{ + return (kd->errbuf); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +/* + * Report an error using printf style arguments. "program" is kd->program + * on hard errors, and 0 on soft errors, so that under sun error emulation, + * only hard errors are printed out (otherwise, programs like gdb will + * generate tons of error messages when trying to access bogus pointers). + */ +void +#if __STDC__ +_kvm_err(kvm_t *kd, const char *program, const char *fmt, ...) +#else +_kvm_err(kd, program, fmt, va_alist) + kvm_t *kd; + char *program, *fmt; + va_dcl +#endif +{ + va_list ap; + +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + if (program != NULL) { + (void)fprintf(stderr, "%s: ", program); + (void)vfprintf(stderr, fmt, ap); + (void)fputc('\n', stderr); + } else + (void)vsnprintf(kd->errbuf, + sizeof(kd->errbuf), (char *)fmt, ap); + + va_end(ap); +} + +void +#if __STDC__ +_kvm_syserr(kvm_t *kd, const char *program, const char *fmt, ...) +#else +_kvm_syserr(kd, program, fmt, va_alist) + kvm_t *kd; + char *program, *fmt; + va_dcl +#endif +{ + va_list ap; + int n; + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + if (program != NULL) { + (void)fprintf(stderr, "%s: ", program); + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ": %s\n", strerror(errno)); + } else { + char *cp = kd->errbuf; + + (void)vsnprintf(cp, sizeof(kd->errbuf), (char *)fmt, ap); + n = strlen(cp); + (void)snprintf(&cp[n], sizeof(kd->errbuf) - n, ": %s", + strerror(errno)); + } + va_end(ap); +} + +void * +_kvm_malloc(kd, n) + kvm_t *kd; + size_t n; +{ + void *p; + + if ((p = calloc(n, sizeof(char))) == NULL) + _kvm_err(kd, kd->program, "can't allocate %u bytes: %s", + n, strerror(errno)); + return (p); +} + +static kvm_t * +_kvm_open(kd, uf, mf, flag, errout) + kvm_t *kd; + const char *uf; + const char *mf; + int flag; + char *errout; +{ + struct stat st; + + kd->vmfd = -1; + kd->pmfd = -1; + kd->nlfd = -1; + kd->vmst = 0; + kd->procbase = 0; + kd->argspc = 0; + kd->argv = 0; + + if (uf == 0) + uf = getbootfile(); + else if (strlen(uf) >= MAXPATHLEN) { + _kvm_err(kd, kd->program, "exec file name too long"); + goto failed; + } + if (flag & ~O_RDWR) { + _kvm_err(kd, kd->program, "bad flags arg"); + goto failed; + } + if (mf == 0) + mf = _PATH_MEM; + + if ((kd->pmfd = open(mf, flag, 0)) < 0) { + _kvm_syserr(kd, kd->program, "%s", mf); + goto failed; + } + if (fstat(kd->pmfd, &st) < 0) { + _kvm_syserr(kd, kd->program, "%s", mf); + goto failed; + } + if (S_ISCHR(st.st_mode)) { + /* + * If this is a character special device, then check that + * it's /dev/mem. If so, open kmem too. (Maybe we should + * make it work for either /dev/mem or /dev/kmem -- in either + * case you're working with a live kernel.) + */ + if (strcmp(mf, _PATH_DEVNULL) == 0) { + kd->vmfd = open(_PATH_DEVNULL, O_RDONLY); + } else if (strcmp(mf, _PATH_MEM) != 0) { + _kvm_err(kd, kd->program, + "%s: not physical memory device", mf); + goto failed; + } else { + if ((kd->vmfd = open(_PATH_KMEM, flag)) < 0) { + _kvm_syserr(kd, kd->program, "%s", _PATH_KMEM); + goto failed; + } + } + } else { + /* + * This is a crash dump. + * Initialize the virtual address translation machinery, + * but first setup the namelist fd. + */ + if ((kd->nlfd = open(uf, O_RDONLY, 0)) < 0) { + _kvm_syserr(kd, kd->program, "%s", uf); + goto failed; + } + if (_kvm_initvtop(kd) < 0) + goto failed; + } + return (kd); +failed: + /* + * Copy out the error if doing sane error semantics. + */ + if (errout != 0) + strlcpy(errout, kd->errbuf, _POSIX2_LINE_MAX); + (void)kvm_close(kd); + return (0); +} + +kvm_t * +kvm_openfiles(uf, mf, sf, flag, errout) + const char *uf; + const char *mf; + const char *sf __unused; + int flag; + char *errout; +{ + kvm_t *kd; + + if ((kd = malloc(sizeof(*kd))) == NULL) { + (void)strlcpy(errout, strerror(errno), _POSIX2_LINE_MAX); + return (0); + } + memset(kd, 0, sizeof(*kd)); + kd->program = 0; + return (_kvm_open(kd, uf, mf, flag, errout)); +} + +kvm_t * +kvm_open(uf, mf, sf, flag, errstr) + const char *uf; + const char *mf; + const char *sf __unused; + int flag; + const char *errstr; +{ + kvm_t *kd; + + if ((kd = malloc(sizeof(*kd))) == NULL) { + if (errstr != NULL) + (void)fprintf(stderr, "%s: %s\n", + errstr, strerror(errno)); + return (0); + } + memset(kd, 0, sizeof(*kd)); + kd->program = errstr; + return (_kvm_open(kd, uf, mf, flag, NULL)); +} + +int +kvm_close(kd) + kvm_t *kd; +{ + int error = 0; + + if (kd->pmfd >= 0) + error |= close(kd->pmfd); + if (kd->vmfd >= 0) + error |= close(kd->vmfd); + if (kd->nlfd >= 0) + error |= close(kd->nlfd); + if (kd->vmst) + _kvm_freevtop(kd); + if (kd->procbase != 0) + free((void *)kd->procbase); + if (kd->argv != 0) + free((void *)kd->argv); + free((void *)kd); + + return (0); +} + +int +kvm_nlist(kd, nl) + kvm_t *kd; + struct nlist *nl; +{ + struct nlist *p; + int nvalid; + struct kld_sym_lookup lookup; + + /* + * If we can't use the kld symbol lookup, revert to the + * slow library call. + */ + if (!ISALIVE(kd)) + return (__fdnlist(kd->nlfd, nl)); + + /* + * We can use the kld lookup syscall. Go through each nlist entry + * and look it up with a kldsym(2) syscall. + */ + nvalid = 0; + for (p = nl; p->n_name && p->n_name[0]; ++p) { + lookup.version = sizeof(lookup); + lookup.symname = p->n_name; + lookup.symvalue = 0; + lookup.symsize = 0; + + if (lookup.symname[0] == '_') + lookup.symname++; + + if (kldsym(0, KLDSYM_LOOKUP, &lookup) != -1) { + p->n_type = N_TEXT; + p->n_other = 0; + p->n_desc = 0; + p->n_value = lookup.symvalue; + ++nvalid; + /* lookup.symsize */ + } + } + /* + * Return the number of entries that weren't found. + */ + return ((p - nl) - nvalid); +} + +ssize_t +kvm_read(kd, kva, buf, len) + kvm_t *kd; + u_long kva; + void *buf; + size_t len; +{ + int cc; + void *cp; + + if (ISALIVE(kd)) { + /* + * We're using /dev/kmem. Just read straight from the + * device and let the active kernel do the address translation. + */ + errno = 0; + if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) { + _kvm_err(kd, 0, "invalid address (%x)", kva); + return (-1); + } + cc = read(kd->vmfd, buf, len); + if (cc < 0) { + _kvm_syserr(kd, 0, "kvm_read"); + return (-1); + } else if (cc < len) + _kvm_err(kd, kd->program, "short read"); + return (cc); + } else { + cp = buf; + while (len > 0) { + u_long pa; + + cc = _kvm_kvatop(kd, kva, &pa); + if (cc == 0) + return (-1); + if (cc > len) + cc = len; + errno = 0; + if (lseek(kd->pmfd, (off_t)pa, 0) == -1 && errno != 0) { + _kvm_syserr(kd, 0, _PATH_MEM); + break; + } + cc = read(kd->pmfd, cp, cc); + if (cc < 0) { + _kvm_syserr(kd, kd->program, "kvm_read"); + break; + } + /* + * If kvm_kvatop returns a bogus value or our core + * file is truncated, we might wind up seeking beyond + * the end of the core file in which case the read will + * return 0 (EOF). + */ + if (cc == 0) + break; + (char *)cp += cc; + kva += cc; + len -= cc; + } + return ((char *)cp - (char *)buf); + } + /* NOTREACHED */ +} + +ssize_t +kvm_write(kd, kva, buf, len) + kvm_t *kd; + u_long kva; + const void *buf; + size_t len; +{ + int cc; + + if (ISALIVE(kd)) { + /* + * Just like kvm_read, only we write. + */ + errno = 0; + if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) { + _kvm_err(kd, 0, "invalid address (%x)", kva); + return (-1); + } + cc = write(kd->vmfd, buf, len); + if (cc < 0) { + _kvm_syserr(kd, 0, "kvm_write"); + return (-1); + } else if (cc < len) + _kvm_err(kd, kd->program, "short write"); + return (cc); + } else { + _kvm_err(kd, kd->program, + "kvm_write not implemented for dead kernels"); + return (-1); + } + /* NOTREACHED */ +} |