* 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
* 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
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid
[] = "@(#)kvm.c 8.2 (Berkeley) 2/13/94";
#endif /* LIBC_SCCS and not lint */
#include <vm/swap_pager.h>
#include <machine/vmparam.h>
static int kvm_dbopen
__P((kvm_t
*, const char *));
* 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).
_kvm_err(kvm_t
*kd
, const char *program
, const char *fmt
, ...)
_kvm_err(kd
, program
, fmt
, va_alist
)
(void)fprintf(stderr
, "%s: ", program
);
(void)vfprintf(stderr
, fmt
, ap
);
(void)fputc('\n', stderr
);
(void)vsnprintf(kd
->errbuf
,
sizeof(kd
->errbuf
), (char *)fmt
, ap
);
_kvm_syserr(kvm_t
*kd
, const char *program
, const char *fmt
, ...)
_kvm_syserr(kd
, program
, fmt
, va_alist
)
(void)fprintf(stderr
, "%s: ", program
);
(void)vfprintf(stderr
, fmt
, ap
);
(void)fprintf(stderr
, ": %s\n", strerror(errno
));
register char *cp
= kd
->errbuf
;
(void)vsnprintf(cp
, sizeof(kd
->errbuf
), (char *)fmt
, ap
);
(void)snprintf(&cp
[n
], sizeof(kd
->errbuf
) - n
, ": %s",
if ((p
= malloc(n
)) == NULL
)
_kvm_err(kd
, kd
->program
, strerror(errno
));
_kvm_open(kd
, uf
, mf
, sf
, flag
, errout
)
else if (strlen(uf
) >= MAXPATHLEN
) {
_kvm_err(kd
, kd
->program
, "exec file name too long");
_kvm_err(kd
, kd
->program
, "bad flags arg");
if ((kd
->pmfd
= open(mf
, flag
, 0)) < 0) {
_kvm_syserr(kd
, kd
->program
, "%s", mf
);
if (fstat(kd
->pmfd
, &st
) < 0) {
_kvm_syserr(kd
, kd
->program
, "%s", mf
);
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_MEM
) != 0) { /* XXX */
_kvm_err(kd
, kd
->program
,
"%s: not physical memory device", mf
);
if ((kd
->vmfd
= open(_PATH_KMEM
, flag
)) < 0) {
_kvm_syserr(kd
, kd
->program
, "%s", _PATH_KMEM
);
if ((kd
->swfd
= open(sf
, flag
, 0)) < 0) {
_kvm_syserr(kd
, kd
->program
, "%s", sf
);
* Open kvm nlist database. We go ahead and do this
* here so that we don't have to hold on to the vmunix
* path name. Since a kvm application will surely do
* a kvm_nlist(), this probably won't be a wasted effort.
* If the database cannot be opened, open the namelist
* argument so we revert to slow nlist() calls.
if (kvm_dbopen(kd
, uf
) < 0 &&
(kd
->nlfd
= open(uf
, O_RDONLY
, 0)) < 0) {
_kvm_syserr(kd
, kd
->program
, "%s", uf
);
* Initalize 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
);
if (_kvm_initvtop(kd
) < 0)
* Copy out the error if doing sane error semantics.
strcpy(errout
, kd
->errbuf
);
kvm_openfiles(uf
, mf
, sf
, flag
, errout
)
if ((kd
= malloc(sizeof(*kd
))) == NULL
) {
(void)strcpy(errout
, strerror(errno
));
return (_kvm_open(kd
, uf
, mf
, sf
, flag
, errout
));
kvm_open(uf
, mf
, sf
, flag
, program
)
if ((kd
= malloc(sizeof(*kd
))) == NULL
&& program
!= NULL
) {
(void)fprintf(stderr
, "%s: %s\n", strerror(errno
));
return (_kvm_open(kd
, uf
, mf
, sf
, flag
, NULL
));
error
|= close(kd
->pmfd
);
error
|= close(kd
->vmfd
);
error
|= close(kd
->nlfd
);
error
|= close(kd
->swfd
);
error
|= (kd
->db
->close
)(kd
->db
);
free((void *)kd
->procbase
);
* Set up state necessary to do queries on the kernel namelist
* data base. If the data base is out-of-data/incompatible with
* given executable, set up things so we revert to standard nlist call.
* Only called for live kernels. Return 0 on success, -1 on failure.
char dbversion
[_POSIX2_LINE_MAX
];
char kversion
[_POSIX2_LINE_MAX
];
if ((cp
= rindex(uf
, '/')) != 0)
(void)snprintf(dbname
, sizeof(dbname
), "%skvm_%s.db", _PATH_VARDB
, uf
);
kd
->db
= dbopen(dbname
, O_RDONLY
, 0, DB_HASH
, NULL
);
* read version out of database
rec
.size
= sizeof(VRS_KEY
) - 1;
if ((kd
->db
->get
)(kd
->db
, (DBT
*)&rec
, (DBT
*)&rec
, 0))
if (rec
.data
== 0 || rec
.size
> sizeof(dbversion
))
bcopy(rec
.data
, dbversion
, rec
.size
);
* Read version string from kernel memory.
* Since we are dealing with a live kernel, we can call kvm_read()
rec
.size
= sizeof(VRS_SYM
) - 1;
if ((kd
->db
->get
)(kd
->db
, (DBT
*)&rec
, (DBT
*)&rec
, 0))
if (rec
.data
== 0 || rec
.size
!= sizeof(struct nlist
))
bcopy((char *)rec
.data
, (char *)&nitem
, sizeof(nitem
));
if (kvm_read(kd
, (u_long
)nitem
.n_value
, kversion
, dbversionlen
) !=
* If they match, we win - otherwise clear out kd->db so
* we revert to slow nlist().
if (bcmp(dbversion
, kversion
, dbversionlen
) == 0)
(void)(kd
->db
->close
)(kd
->db
);
register struct nlist
*p
;
* If we can't use the data base, revert to the
return (__fdnlist(kd
->nlfd
, nl
));
* We can use the kvm data base. Go through each nlist entry
* and look it up with a db query.
for (p
= nl
; p
->n_name
&& p
->n_name
[0]; ++p
) {
if ((len
= strlen(p
->n_name
)) > 4096) {
_kvm_err(kd
, kd
->program
, "symbol too large");
if ((kd
->db
->get
)(kd
->db
, (DBT
*)&rec
, (DBT
*)&rec
, 0))
if (rec
.data
== 0 || rec
.size
!= sizeof(struct nlist
))
* Avoid alignment issues.
bcopy((char *)&((struct nlist
*)rec
.data
)->n_type
,
bcopy((char *)&((struct nlist
*)rec
.data
)->n_value
,
* Return the number of entries that weren't found.
return ((p
- nl
) - nvalid
);
kvm_read(kd
, kva
, buf
, len
)
* We're using /dev/kmem. Just read straight from the
* device and let the active kernel do the address translation.
if (lseek(kd
->vmfd
, (off_t
)kva
, 0) == -1 && errno
!= 0) {
_kvm_err(kd
, 0, "invalid address (%x)", kva
);
cc
= read(kd
->vmfd
, buf
, len
);
_kvm_syserr(kd
, 0, "kvm_read");
_kvm_err(kd
, kd
->program
, "short read");
cc
= _kvm_kvatop(kd
, kva
, &pa
);
if (lseek(kd
->pmfd
, (off_t
)pa
, 0) == -1 && errno
!= 0) {
_kvm_syserr(kd
, 0, _PATH_MEM
);
cc
= read(kd
->pmfd
, cp
, cc
);
_kvm_syserr(kd
, kd
->program
, "kvm_read");
* 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 ((char *)cp
- (char *)buf
);
kvm_write(kd
, kva
, buf
, len
)
register const void *buf
;
* Just like kvm_read, only we write.
if (lseek(kd
->vmfd
, (off_t
)kva
, 0) == -1 && errno
!= 0) {
_kvm_err(kd
, 0, "invalid address (%x)", kva
);
cc
= write(kd
->vmfd
, buf
, len
);
_kvm_syserr(kd
, 0, "kvm_write");
_kvm_err(kd
, kd
->program
, "short write");
_kvm_err(kd
, kd
->program
,
"kvm_write not implemented for dead kernels");