kvm.c revision 130246
155682Smarkm/*- 257416Smarkm * Copyright (c) 1989, 1992, 1993 355682Smarkm * The Regents of the University of California. All rights reserved. 455682Smarkm * 555682Smarkm * This code is derived from software developed by the Computer Systems 655682Smarkm * Engineering group at Lawrence Berkeley Laboratory under DARPA contract 755682Smarkm * BG 91-66 and contributed to Berkeley. 855682Smarkm * 955682Smarkm * Redistribution and use in source and binary forms, with or without 1055682Smarkm * modification, are permitted provided that the following conditions 1155682Smarkm * are met: 1255682Smarkm * 1. Redistributions of source code must retain the above copyright 1355682Smarkm * notice, this list of conditions and the following disclaimer. 1455682Smarkm * 2. Redistributions in binary form must reproduce the above copyright 1555682Smarkm * notice, this list of conditions and the following disclaimer in the 1655682Smarkm * documentation and/or other materials provided with the distribution. 1755682Smarkm * 3. All advertising materials mentioning features or use of this software 1855682Smarkm * must display the following acknowledgement: 1955682Smarkm * This product includes software developed by the University of 2055682Smarkm * California, Berkeley and its contributors. 2155682Smarkm * 4. Neither the name of the University nor the names of its contributors 2255682Smarkm * may be used to endorse or promote products derived from this software 2355682Smarkm * without specific prior written permission. 2455682Smarkm * 2555682Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2655682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2755682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2855682Smarkm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2955682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3055682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3155682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3255682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3355682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3455682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3555682Smarkm * SUCH DAMAGE. 3655682Smarkm */ 3755682Smarkm 3855682Smarkm#include <sys/cdefs.h> 3955682Smarkm__FBSDID("$FreeBSD: head/lib/libkvm/kvm.c 130246 2004-06-08 13:08:19Z stefanf $"); 4057416Smarkm 4155682Smarkm#if defined(LIBC_SCCS) && !defined(lint) 4255682Smarkm#if 0 4355682Smarkmstatic char sccsid[] = "@(#)kvm.c 8.2 (Berkeley) 2/13/94"; 4455682Smarkm#endif 4555682Smarkm#endif /* LIBC_SCCS and not lint */ 4655682Smarkm 4755682Smarkm#include <sys/param.h> 4855682Smarkm#include <sys/user.h> 4955682Smarkm#include <sys/proc.h> 5055682Smarkm#include <sys/ioctl.h> 5155682Smarkm#include <sys/stat.h> 5255682Smarkm#include <sys/sysctl.h> 5355682Smarkm#include <sys/linker.h> 5455682Smarkm 5555682Smarkm#include <vm/vm.h> 5655682Smarkm#include <vm/vm_param.h> 5755682Smarkm 5855682Smarkm#include <machine/vmparam.h> 5955682Smarkm 6055682Smarkm#include <ctype.h> 6155682Smarkm#include <fcntl.h> 6255682Smarkm#include <kvm.h> 6355682Smarkm#include <limits.h> 6455682Smarkm#include <nlist.h> 6555682Smarkm#include <paths.h> 6655682Smarkm#include <stdio.h> 6755682Smarkm#include <stdlib.h> 6855682Smarkm#include <string.h> 6955682Smarkm#include <unistd.h> 7055682Smarkm 7155682Smarkm#include "kvm_private.h" 7255682Smarkm 7355682Smarkm/* from src/lib/libc/gen/nlist.c */ 7455682Smarkmint __fdnlist(int, struct nlist *); 7555682Smarkm 7655682Smarkmchar * 7755682Smarkmkvm_geterr(kd) 7855682Smarkm kvm_t *kd; 7955682Smarkm{ 8055682Smarkm return (kd->errbuf); 8155682Smarkm} 8255682Smarkm 8355682Smarkm#include <stdarg.h> 8455682Smarkm 8555682Smarkm/* 8655682Smarkm * Report an error using printf style arguments. "program" is kd->program 8755682Smarkm * on hard errors, and 0 on soft errors, so that under sun error emulation, 8855682Smarkm * only hard errors are printed out (otherwise, programs like gdb will 8955682Smarkm * generate tons of error messages when trying to access bogus pointers). 9055682Smarkm */ 9155682Smarkmvoid 9255682Smarkm_kvm_err(kvm_t *kd, const char *program, const char *fmt, ...) 9355682Smarkm{ 9455682Smarkm va_list ap; 9555682Smarkm 9655682Smarkm va_start(ap, fmt); 9755682Smarkm if (program != NULL) { 9855682Smarkm (void)fprintf(stderr, "%s: ", program); 9955682Smarkm (void)vfprintf(stderr, fmt, ap); 10055682Smarkm (void)fputc('\n', stderr); 10155682Smarkm } else 10255682Smarkm (void)vsnprintf(kd->errbuf, 10355682Smarkm sizeof(kd->errbuf), (char *)fmt, ap); 10455682Smarkm 10555682Smarkm va_end(ap); 10655682Smarkm} 10755682Smarkm 10855682Smarkmvoid 10955682Smarkm_kvm_syserr(kvm_t *kd, const char *program, const char *fmt, ...) 11055682Smarkm{ 11155682Smarkm va_list ap; 11255682Smarkm int n; 11355682Smarkm 11455682Smarkm va_start(ap, fmt); 11555682Smarkm if (program != NULL) { 11655682Smarkm (void)fprintf(stderr, "%s: ", program); 11755682Smarkm (void)vfprintf(stderr, fmt, ap); 11855682Smarkm (void)fprintf(stderr, ": %s\n", strerror(errno)); 11955682Smarkm } else { 12055682Smarkm char *cp = kd->errbuf; 12155682Smarkm 12255682Smarkm (void)vsnprintf(cp, sizeof(kd->errbuf), (char *)fmt, ap); 12355682Smarkm n = strlen(cp); 12455682Smarkm (void)snprintf(&cp[n], sizeof(kd->errbuf) - n, ": %s", 12555682Smarkm strerror(errno)); 12655682Smarkm } 12755682Smarkm va_end(ap); 12855682Smarkm} 12955682Smarkm 13055682Smarkmvoid * 13155682Smarkm_kvm_malloc(kd, n) 13255682Smarkm kvm_t *kd; 13355682Smarkm size_t n; 13455682Smarkm{ 13555682Smarkm void *p; 13655682Smarkm 13755682Smarkm if ((p = calloc(n, sizeof(char))) == NULL) 13855682Smarkm _kvm_err(kd, kd->program, "can't allocate %u bytes: %s", 13955682Smarkm n, strerror(errno)); 14055682Smarkm return (p); 14155682Smarkm} 14255682Smarkm 14355682Smarkmstatic kvm_t * 14455682Smarkm_kvm_open(kd, uf, mf, flag, errout) 14555682Smarkm kvm_t *kd; 14655682Smarkm const char *uf; 14755682Smarkm const char *mf; 14857416Smarkm int flag; 14957416Smarkm char *errout; 15057416Smarkm{ 15155682Smarkm struct stat st; 15255682Smarkm 15355682Smarkm kd->vmfd = -1; 15455682Smarkm kd->pmfd = -1; 15555682Smarkm kd->nlfd = -1; 15655682Smarkm kd->vmst = 0; 15755682Smarkm kd->procbase = 0; 15855682Smarkm kd->argspc = 0; 15955682Smarkm kd->argv = 0; 16055682Smarkm 16155682Smarkm if (uf == 0) 16255682Smarkm uf = getbootfile(); 16355682Smarkm else if (strlen(uf) >= MAXPATHLEN) { 16455682Smarkm _kvm_err(kd, kd->program, "exec file name too long"); 16555682Smarkm goto failed; 16655682Smarkm } 16755682Smarkm if (flag & ~O_RDWR) { 16855682Smarkm _kvm_err(kd, kd->program, "bad flags arg"); 16955682Smarkm goto failed; 17055682Smarkm } 17155682Smarkm if (mf == 0) 17255682Smarkm mf = _PATH_MEM; 17355682Smarkm 17455682Smarkm if ((kd->pmfd = open(mf, flag, 0)) < 0) { 17555682Smarkm _kvm_syserr(kd, kd->program, "%s", mf); 17655682Smarkm goto failed; 17755682Smarkm } 17855682Smarkm if (fstat(kd->pmfd, &st) < 0) { 17955682Smarkm _kvm_syserr(kd, kd->program, "%s", mf); 18055682Smarkm goto failed; 18155682Smarkm } 18255682Smarkm if (fcntl(kd->pmfd, F_SETFD, FD_CLOEXEC) < 0) { 18355682Smarkm _kvm_syserr(kd, kd->program, "%s", mf); 18455682Smarkm goto failed; 18555682Smarkm } 18655682Smarkm if (S_ISCHR(st.st_mode)) { 18755682Smarkm /* 18855682Smarkm * If this is a character special device, then check that 18955682Smarkm * it's /dev/mem. If so, open kmem too. (Maybe we should 19055682Smarkm * make it work for either /dev/mem or /dev/kmem -- in either 19155682Smarkm * case you're working with a live kernel.) 19255682Smarkm */ 19355682Smarkm if (strcmp(mf, _PATH_DEVNULL) == 0) { 19455682Smarkm kd->vmfd = open(_PATH_DEVNULL, O_RDONLY); 19555682Smarkm return (kd); 19655682Smarkm } else if (strcmp(mf, _PATH_MEM) == 0) { 19755682Smarkm if ((kd->vmfd = open(_PATH_KMEM, flag)) < 0) { 19855682Smarkm _kvm_syserr(kd, kd->program, "%s", _PATH_KMEM); 19955682Smarkm goto failed; 20055682Smarkm } 20155682Smarkm if (fcntl(kd->vmfd, F_SETFD, FD_CLOEXEC) < 0) { 20255682Smarkm _kvm_syserr(kd, kd->program, "%s", _PATH_KMEM); 20355682Smarkm goto failed; 20455682Smarkm } 20555682Smarkm return (kd); 20655682Smarkm } 20755682Smarkm } 20855682Smarkm /* 20955682Smarkm * This is a crash dump. 21055682Smarkm * Initialize the virtual address translation machinery, 21155682Smarkm * but first setup the namelist fd. 21255682Smarkm */ 21355682Smarkm if ((kd->nlfd = open(uf, O_RDONLY, 0)) < 0) { 21455682Smarkm _kvm_syserr(kd, kd->program, "%s", uf); 21555682Smarkm goto failed; 21655682Smarkm } 21755682Smarkm if (fcntl(kd->nlfd, F_SETFD, FD_CLOEXEC) < 0) { 21855682Smarkm _kvm_syserr(kd, kd->program, "%s", uf); 21955682Smarkm goto failed; 22055682Smarkm } 22155682Smarkm if (_kvm_initvtop(kd) < 0) 22255682Smarkm goto failed; 22355682Smarkm return (kd); 22455682Smarkmfailed: 22555682Smarkm /* 22655682Smarkm * Copy out the error if doing sane error semantics. 22755682Smarkm */ 22855682Smarkm if (errout != 0) 22955682Smarkm strlcpy(errout, kd->errbuf, _POSIX2_LINE_MAX); 23055682Smarkm (void)kvm_close(kd); 23155682Smarkm return (0); 23255682Smarkm} 23355682Smarkm 23455682Smarkmkvm_t * 23555682Smarkmkvm_openfiles(uf, mf, sf, flag, errout) 23655682Smarkm const char *uf; 23755682Smarkm const char *mf; 23855682Smarkm const char *sf __unused; 23955682Smarkm int flag; 24055682Smarkm char *errout; 24155682Smarkm{ 24255682Smarkm kvm_t *kd; 24355682Smarkm 24455682Smarkm if ((kd = malloc(sizeof(*kd))) == NULL) { 24555682Smarkm (void)strlcpy(errout, strerror(errno), _POSIX2_LINE_MAX); 24655682Smarkm return (0); 24755682Smarkm } 24855682Smarkm memset(kd, 0, sizeof(*kd)); 24955682Smarkm kd->program = 0; 25055682Smarkm return (_kvm_open(kd, uf, mf, flag, errout)); 25155682Smarkm} 25255682Smarkm 25355682Smarkmkvm_t * 25455682Smarkmkvm_open(uf, mf, sf, flag, errstr) 25555682Smarkm const char *uf; 25655682Smarkm const char *mf; 25755682Smarkm const char *sf __unused; 25855682Smarkm int flag; 25955682Smarkm const char *errstr; 26055682Smarkm{ 26155682Smarkm kvm_t *kd; 26255682Smarkm 26355682Smarkm if ((kd = malloc(sizeof(*kd))) == NULL) { 26455682Smarkm if (errstr != NULL) 26555682Smarkm (void)fprintf(stderr, "%s: %s\n", 26655682Smarkm errstr, strerror(errno)); 26755682Smarkm return (0); 26855682Smarkm } 26955682Smarkm memset(kd, 0, sizeof(*kd)); 27055682Smarkm kd->program = errstr; 27155682Smarkm return (_kvm_open(kd, uf, mf, flag, NULL)); 27255682Smarkm} 27355682Smarkm 27455682Smarkmint 27555682Smarkmkvm_close(kd) 27655682Smarkm kvm_t *kd; 27755682Smarkm{ 27855682Smarkm int error = 0; 27955682Smarkm 28055682Smarkm if (kd->pmfd >= 0) 28155682Smarkm error |= close(kd->pmfd); 28255682Smarkm if (kd->vmfd >= 0) 28355682Smarkm error |= close(kd->vmfd); 28455682Smarkm if (kd->nlfd >= 0) 28555682Smarkm error |= close(kd->nlfd); 28655682Smarkm if (kd->vmst) 28755682Smarkm _kvm_freevtop(kd); 28855682Smarkm if (kd->procbase != 0) 28955682Smarkm free((void *)kd->procbase); 29055682Smarkm if (kd->argv != 0) 29155682Smarkm free((void *)kd->argv); 29255682Smarkm free((void *)kd); 29355682Smarkm 29455682Smarkm return (0); 29555682Smarkm} 29655682Smarkm 29755682Smarkmint 29855682Smarkmkvm_nlist(kd, nl) 29955682Smarkm kvm_t *kd; 30055682Smarkm struct nlist *nl; 30155682Smarkm{ 30255682Smarkm struct nlist *p; 30355682Smarkm int nvalid; 30455682Smarkm struct kld_sym_lookup lookup; 30555682Smarkm 30655682Smarkm /* 30755682Smarkm * If we can't use the kld symbol lookup, revert to the 30855682Smarkm * slow library call. 30955682Smarkm */ 31055682Smarkm if (!ISALIVE(kd)) 31155682Smarkm return (__fdnlist(kd->nlfd, nl)); 31255682Smarkm 31355682Smarkm /* 31455682Smarkm * We can use the kld lookup syscall. Go through each nlist entry 31555682Smarkm * and look it up with a kldsym(2) syscall. 31655682Smarkm */ 31755682Smarkm nvalid = 0; 31855682Smarkm for (p = nl; p->n_name && p->n_name[0]; ++p) { 31955682Smarkm lookup.version = sizeof(lookup); 32055682Smarkm lookup.symname = p->n_name; 32155682Smarkm lookup.symvalue = 0; 32255682Smarkm lookup.symsize = 0; 32355682Smarkm 32455682Smarkm if (lookup.symname[0] == '_') 32555682Smarkm lookup.symname++; 32655682Smarkm 32755682Smarkm if (kldsym(0, KLDSYM_LOOKUP, &lookup) != -1) { 32855682Smarkm p->n_type = N_TEXT; 32955682Smarkm p->n_other = 0; 33055682Smarkm p->n_desc = 0; 33155682Smarkm p->n_value = lookup.symvalue; 33255682Smarkm ++nvalid; 33355682Smarkm /* lookup.symsize */ 33455682Smarkm } 33555682Smarkm } 33655682Smarkm /* 33755682Smarkm * Return the number of entries that weren't found. 33855682Smarkm */ 33955682Smarkm return ((p - nl) - nvalid); 34055682Smarkm} 34155682Smarkm 34255682Smarkmssize_t 34355682Smarkmkvm_read(kd, kva, buf, len) 34455682Smarkm kvm_t *kd; 34555682Smarkm u_long kva; 34655682Smarkm void *buf; 34755682Smarkm size_t len; 34855682Smarkm{ 34955682Smarkm int cc; 35055682Smarkm char *cp; 35155682Smarkm 35255682Smarkm if (ISALIVE(kd)) { 35355682Smarkm /* 35455682Smarkm * We're using /dev/kmem. Just read straight from the 35555682Smarkm * device and let the active kernel do the address translation. 35655682Smarkm */ 35755682Smarkm errno = 0; 35855682Smarkm if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) { 35955682Smarkm _kvm_err(kd, 0, "invalid address (%x)", kva); 36055682Smarkm return (-1); 36155682Smarkm } 36255682Smarkm cc = read(kd->vmfd, buf, len); 36355682Smarkm if (cc < 0) { 36455682Smarkm _kvm_syserr(kd, 0, "kvm_read"); 36555682Smarkm return (-1); 36655682Smarkm } else if (cc < len) 36755682Smarkm _kvm_err(kd, kd->program, "short read"); 36855682Smarkm return (cc); 36955682Smarkm } else { 37055682Smarkm cp = buf; 37155682Smarkm while (len > 0) { 37255682Smarkm u_long pa; 37355682Smarkm 37455682Smarkm cc = _kvm_kvatop(kd, kva, &pa); 37555682Smarkm if (cc == 0) 37655682Smarkm return (-1); 37755682Smarkm if (cc > len) 37855682Smarkm cc = len; 37955682Smarkm errno = 0; 38055682Smarkm if (lseek(kd->pmfd, (off_t)pa, 0) == -1 && errno != 0) { 38155682Smarkm _kvm_syserr(kd, 0, _PATH_MEM); 38255682Smarkm break; 38355682Smarkm } 38455682Smarkm cc = read(kd->pmfd, cp, cc); 38555682Smarkm if (cc < 0) { 38655682Smarkm _kvm_syserr(kd, kd->program, "kvm_read"); 38755682Smarkm break; 38855682Smarkm } 38955682Smarkm /* 39055682Smarkm * If kvm_kvatop returns a bogus value or our core 39155682Smarkm * file is truncated, we might wind up seeking beyond 39255682Smarkm * the end of the core file in which case the read will 39355682Smarkm * return 0 (EOF). 39455682Smarkm */ 39555682Smarkm if (cc == 0) 39655682Smarkm break; 39755682Smarkm cp += cc; 39855682Smarkm kva += cc; 39955682Smarkm len -= cc; 40055682Smarkm } 40155682Smarkm return (cp - (char *)buf); 40255682Smarkm } 40355682Smarkm /* NOTREACHED */ 40455682Smarkm} 40555682Smarkm 40655682Smarkmssize_t 40755682Smarkmkvm_write(kd, kva, buf, len) 40855682Smarkm kvm_t *kd; 40955682Smarkm u_long kva; 41055682Smarkm const void *buf; 41155682Smarkm size_t len; 41255682Smarkm{ 41355682Smarkm int cc; 41455682Smarkm 41555682Smarkm if (ISALIVE(kd)) { 41655682Smarkm /* 41755682Smarkm * Just like kvm_read, only we write. 41855682Smarkm */ 41955682Smarkm errno = 0; 42055682Smarkm if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) { 42155682Smarkm _kvm_err(kd, 0, "invalid address (%x)", kva); 42255682Smarkm return (-1); 42355682Smarkm } 42455682Smarkm cc = write(kd->vmfd, buf, len); 42555682Smarkm if (cc < 0) { 42655682Smarkm _kvm_syserr(kd, 0, "kvm_write"); 42755682Smarkm return (-1); 42855682Smarkm } else if (cc < len) 42955682Smarkm _kvm_err(kd, kd->program, "short write"); 43055682Smarkm return (cc); 43155682Smarkm } else { 43255682Smarkm _kvm_err(kd, kd->program, 43355682Smarkm "kvm_write not implemented for dead kernels"); 43455682Smarkm return (-1); 43555682Smarkm } 43655682Smarkm /* NOTREACHED */ 43755682Smarkm} 43855682Smarkm