kvm.c revision 1856
1/*-
2 * Copyright (c) 1989, 1992, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software developed by the Computer Systems
6 * Engineering group at Lawrence Berkeley Laboratory under DARPA contract
7 * BG 91-66 and contributed to Berkeley.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *	This product includes software developed by the University of
20 *	California, Berkeley and its contributors.
21 * 4. Neither the name of the University nor the names of its contributors
22 *    may be used to endorse or promote products derived from this software
23 *    without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38#if defined(LIBC_SCCS) && !defined(lint)
39static char sccsid[] = "@(#)kvm.c	8.2 (Berkeley) 2/13/94";
40#endif /* LIBC_SCCS and not lint */
41
42#include <sys/param.h>
43#include <sys/user.h>
44#include <sys/proc.h>
45#include <sys/ioctl.h>
46#include <sys/stat.h>
47#include <sys/sysctl.h>
48
49#include <vm/vm.h>
50#include <vm/vm_param.h>
51#include <vm/swap_pager.h>
52
53#include <machine/vmparam.h>
54
55#include <ctype.h>
56#include <db.h>
57#include <fcntl.h>
58#include <kvm.h>
59#include <limits.h>
60#include <nlist.h>
61#include <paths.h>
62#include <stdio.h>
63#include <stdlib.h>
64#include <string.h>
65#include <unistd.h>
66
67#include "kvm_private.h"
68
69static int kvm_dbopen __P((kvm_t *, const char *));
70
71char *
72kvm_geterr(kd)
73	kvm_t *kd;
74{
75	return (kd->errbuf);
76}
77
78#if __STDC__
79#include <stdarg.h>
80#else
81#include <varargs.h>
82#endif
83
84/*
85 * Report an error using printf style arguments.  "program" is kd->program
86 * on hard errors, and 0 on soft errors, so that under sun error emulation,
87 * only hard errors are printed out (otherwise, programs like gdb will
88 * generate tons of error messages when trying to access bogus pointers).
89 */
90void
91#if __STDC__
92_kvm_err(kvm_t *kd, const char *program, const char *fmt, ...)
93#else
94_kvm_err(kd, program, fmt, va_alist)
95	kvm_t *kd;
96	char *program, *fmt;
97	va_dcl
98#endif
99{
100	va_list ap;
101
102#ifdef __STDC__
103	va_start(ap, fmt);
104#else
105	va_start(ap);
106#endif
107	if (program != NULL) {
108		(void)fprintf(stderr, "%s: ", program);
109		(void)vfprintf(stderr, fmt, ap);
110		(void)fputc('\n', stderr);
111	} else
112		(void)vsnprintf(kd->errbuf,
113		    sizeof(kd->errbuf), (char *)fmt, ap);
114
115	va_end(ap);
116}
117
118void
119#if __STDC__
120_kvm_syserr(kvm_t *kd, const char *program, const char *fmt, ...)
121#else
122_kvm_syserr(kd, program, fmt, va_alist)
123	kvm_t *kd;
124	char *program, *fmt;
125	va_dcl
126#endif
127{
128	va_list ap;
129	register int n;
130
131#if __STDC__
132	va_start(ap, fmt);
133#else
134	va_start(ap);
135#endif
136	if (program != NULL) {
137		(void)fprintf(stderr, "%s: ", program);
138		(void)vfprintf(stderr, fmt, ap);
139		(void)fprintf(stderr, ": %s\n", strerror(errno));
140	} else {
141		register char *cp = kd->errbuf;
142
143		(void)vsnprintf(cp, sizeof(kd->errbuf), (char *)fmt, ap);
144		n = strlen(cp);
145		(void)snprintf(&cp[n], sizeof(kd->errbuf) - n, ": %s",
146		    strerror(errno));
147	}
148	va_end(ap);
149}
150
151void *
152_kvm_malloc(kd, n)
153	register kvm_t *kd;
154	register size_t n;
155{
156	void *p;
157
158	if ((p = malloc(n)) == NULL)
159		_kvm_err(kd, kd->program, strerror(errno));
160	return (p);
161}
162
163static kvm_t *
164_kvm_open(kd, uf, mf, sf, flag, errout)
165	register kvm_t *kd;
166	const char *uf;
167	const char *mf;
168	const char *sf;
169	int flag;
170	char *errout;
171{
172	struct stat st;
173
174	kd->vmfd = -1;
175	kd->pmfd = -1;
176	kd->swfd = -1;
177	kd->nlfd = -1;
178	kd->vmst = 0;
179	kd->db = 0;
180	kd->procbase = 0;
181	kd->argspc = 0;
182	kd->argv = 0;
183
184	if (uf == 0)
185		uf = _PATH_UNIX;
186	else if (strlen(uf) >= MAXPATHLEN) {
187		_kvm_err(kd, kd->program, "exec file name too long");
188		goto failed;
189	}
190	if (flag & ~O_RDWR) {
191		_kvm_err(kd, kd->program, "bad flags arg");
192		goto failed;
193	}
194	if (mf == 0)
195		mf = _PATH_MEM;
196	if (sf == 0)
197		sf = _PATH_DRUM;
198
199	if ((kd->pmfd = open(mf, flag, 0)) < 0) {
200		_kvm_syserr(kd, kd->program, "%s", mf);
201		goto failed;
202	}
203	if (fstat(kd->pmfd, &st) < 0) {
204		_kvm_syserr(kd, kd->program, "%s", mf);
205		goto failed;
206	}
207	if (S_ISCHR(st.st_mode)) {
208		/*
209		 * If this is a character special device, then check that
210		 * it's /dev/mem.  If so, open kmem too.  (Maybe we should
211		 * make it work for either /dev/mem or /dev/kmem -- in either
212		 * case you're working with a live kernel.)
213		 */
214		if (strcmp(mf, _PATH_MEM) != 0) {	/* XXX */
215			_kvm_err(kd, kd->program,
216				 "%s: not physical memory device", mf);
217			goto failed;
218		}
219		if ((kd->vmfd = open(_PATH_KMEM, flag)) < 0) {
220			_kvm_syserr(kd, kd->program, "%s", _PATH_KMEM);
221			goto failed;
222		}
223		if ((kd->swfd = open(sf, flag, 0)) < 0) {
224			_kvm_syserr(kd, kd->program, "%s", sf);
225			goto failed;
226		}
227		/*
228		 * Open kvm nlist database.  We go ahead and do this
229		 * here so that we don't have to hold on to the kernel
230		 * path name.  Since a kvm application will surely do
231		 * a kvm_nlist(), this probably won't be a wasted effort.
232		 * If the database cannot be opened, open the namelist
233		 * argument so we revert to slow nlist() calls.
234		 */
235		if (kvm_dbopen(kd, uf) < 0 &&
236		    (kd->nlfd = open(uf, O_RDONLY, 0)) < 0) {
237			_kvm_syserr(kd, kd->program, "%s", uf);
238			goto failed;
239		}
240	} else {
241		/*
242		 * This is a crash dump.
243		 * Initalize the virtual address translation machinery,
244		 * but first setup the namelist fd.
245		 */
246		if ((kd->nlfd = open(uf, O_RDONLY, 0)) < 0) {
247			_kvm_syserr(kd, kd->program, "%s", uf);
248			goto failed;
249		}
250		if (_kvm_initvtop(kd) < 0)
251			goto failed;
252	}
253	return (kd);
254failed:
255	/*
256	 * Copy out the error if doing sane error semantics.
257	 */
258	if (errout != 0)
259		strcpy(errout, kd->errbuf);
260	(void)kvm_close(kd);
261	return (0);
262}
263
264kvm_t *
265kvm_openfiles(uf, mf, sf, flag, errout)
266	const char *uf;
267	const char *mf;
268	const char *sf;
269	int flag;
270	char *errout;
271{
272	register kvm_t *kd;
273
274	if ((kd = malloc(sizeof(*kd))) == NULL) {
275		(void)strcpy(errout, strerror(errno));
276		return (0);
277	}
278	kd->program = 0;
279	return (_kvm_open(kd, uf, mf, sf, flag, errout));
280}
281
282kvm_t *
283kvm_open(uf, mf, sf, flag, program)
284	const char *uf;
285	const char *mf;
286	const char *sf;
287	int flag;
288	const char *program;
289{
290	register kvm_t *kd;
291
292	if ((kd = malloc(sizeof(*kd))) == NULL && program != NULL) {
293		(void)fprintf(stderr, "%s: %s\n", strerror(errno));
294		return (0);
295	}
296	kd->program = program;
297	return (_kvm_open(kd, uf, mf, sf, flag, NULL));
298}
299
300int
301kvm_close(kd)
302	kvm_t *kd;
303{
304	register int error = 0;
305
306	if (kd->pmfd >= 0)
307		error |= close(kd->pmfd);
308	if (kd->vmfd >= 0)
309		error |= close(kd->vmfd);
310	if (kd->nlfd >= 0)
311		error |= close(kd->nlfd);
312	if (kd->swfd >= 0)
313		error |= close(kd->swfd);
314	if (kd->db != 0)
315		error |= (kd->db->close)(kd->db);
316	if (kd->vmst)
317		_kvm_freevtop(kd);
318	if (kd->procbase != 0)
319		free((void *)kd->procbase);
320	if (kd->argv != 0)
321		free((void *)kd->argv);
322	free((void *)kd);
323
324	return (0);
325}
326
327/*
328 * Set up state necessary to do queries on the kernel namelist
329 * data base.  If the data base is out-of-data/incompatible with
330 * given executable, set up things so we revert to standard nlist call.
331 * Only called for live kernels.  Return 0 on success, -1 on failure.
332 */
333static int
334kvm_dbopen(kd, uf)
335	kvm_t *kd;
336	const char *uf;
337{
338	char *cp;
339	DBT rec;
340	int dbversionlen;
341	struct nlist nitem;
342	char dbversion[_POSIX2_LINE_MAX];
343	char kversion[_POSIX2_LINE_MAX];
344	char dbname[MAXPATHLEN];
345
346	if ((cp = rindex(uf, '/')) != 0)
347		uf = cp + 1;
348
349	(void)snprintf(dbname, sizeof(dbname), "%skvm_%s.db", _PATH_VARDB, uf);
350	kd->db = dbopen(dbname, O_RDONLY, 0, DB_HASH, NULL);
351	if (kd->db == 0)
352		return (-1);
353	/*
354	 * read version out of database
355	 */
356	rec.data = VRS_KEY;
357	rec.size = sizeof(VRS_KEY) - 1;
358	if ((kd->db->get)(kd->db, (DBT *)&rec, (DBT *)&rec, 0))
359		goto close;
360	if (rec.data == 0 || rec.size > sizeof(dbversion))
361		goto close;
362
363	bcopy(rec.data, dbversion, rec.size);
364	dbversionlen = rec.size;
365	/*
366	 * Read version string from kernel memory.
367	 * Since we are dealing with a live kernel, we can call kvm_read()
368	 * at this point.
369	 */
370	rec.data = VRS_SYM;
371	rec.size = sizeof(VRS_SYM) - 1;
372	if ((kd->db->get)(kd->db, (DBT *)&rec, (DBT *)&rec, 0))
373		goto close;
374	if (rec.data == 0 || rec.size != sizeof(struct nlist))
375		goto close;
376	bcopy((char *)rec.data, (char *)&nitem, sizeof(nitem));
377	if (kvm_read(kd, (u_long)nitem.n_value, kversion, dbversionlen) !=
378	    dbversionlen)
379		goto close;
380	/*
381	 * If they match, we win - otherwise clear out kd->db so
382	 * we revert to slow nlist().
383	 */
384	if (bcmp(dbversion, kversion, dbversionlen) == 0)
385		return (0);
386close:
387	(void)(kd->db->close)(kd->db);
388	kd->db = 0;
389
390	return (-1);
391}
392
393int
394kvm_nlist(kd, nl)
395	kvm_t *kd;
396	struct nlist *nl;
397{
398	register struct nlist *p;
399	register int nvalid;
400
401	/*
402	 * If we can't use the data base, revert to the
403	 * slow library call.
404	 */
405	if (kd->db == 0)
406		return (__fdnlist(kd->nlfd, nl));
407
408	/*
409	 * We can use the kvm data base.  Go through each nlist entry
410	 * and look it up with a db query.
411	 */
412	nvalid = 0;
413	for (p = nl; p->n_name && p->n_name[0]; ++p) {
414		register int len;
415		DBT rec;
416
417		if ((len = strlen(p->n_name)) > 4096) {
418			/* sanity */
419			_kvm_err(kd, kd->program, "symbol too large");
420			return (-1);
421		}
422		rec.data = p->n_name;
423		rec.size = len;
424		if ((kd->db->get)(kd->db, (DBT *)&rec, (DBT *)&rec, 0))
425			continue;
426		if (rec.data == 0 || rec.size != sizeof(struct nlist))
427			continue;
428		++nvalid;
429		/*
430		 * Avoid alignment issues.
431		 */
432		bcopy((char *)&((struct nlist *)rec.data)->n_type,
433		      (char *)&p->n_type,
434		      sizeof(p->n_type));
435		bcopy((char *)&((struct nlist *)rec.data)->n_value,
436		      (char *)&p->n_value,
437		      sizeof(p->n_value));
438	}
439	/*
440	 * Return the number of entries that weren't found.
441	 */
442	return ((p - nl) - nvalid);
443}
444
445ssize_t
446kvm_read(kd, kva, buf, len)
447	kvm_t *kd;
448	register u_long kva;
449	register void *buf;
450	register size_t len;
451{
452	register int cc;
453	register void *cp;
454
455	if (ISALIVE(kd)) {
456		/*
457		 * We're using /dev/kmem.  Just read straight from the
458		 * device and let the active kernel do the address translation.
459		 */
460		errno = 0;
461		if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) {
462			_kvm_err(kd, 0, "invalid address (%x)", kva);
463			return (0);
464		}
465		cc = read(kd->vmfd, buf, len);
466		if (cc < 0) {
467			_kvm_syserr(kd, 0, "kvm_read");
468			return (0);
469		} else if (cc < len)
470			_kvm_err(kd, kd->program, "short read");
471		return (cc);
472	} else {
473		cp = buf;
474		while (len > 0) {
475			u_long pa;
476
477			cc = _kvm_kvatop(kd, kva, &pa);
478			if (cc == 0)
479				return (0);
480			if (cc > len)
481				cc = len;
482			errno = 0;
483			if (lseek(kd->pmfd, (off_t)pa, 0) == -1 && errno != 0) {
484				_kvm_syserr(kd, 0, _PATH_MEM);
485				break;
486			}
487			cc = read(kd->pmfd, cp, cc);
488			if (cc < 0) {
489				_kvm_syserr(kd, kd->program, "kvm_read");
490				break;
491			}
492			/*
493			 * If kvm_kvatop returns a bogus value or our core
494			 * file is truncated, we might wind up seeking beyond
495			 * the end of the core file in which case the read will
496			 * return 0 (EOF).
497			 */
498			if (cc == 0)
499				break;
500			(char *)cp += cc;
501			kva += cc;
502			len -= cc;
503		}
504		return ((char *)cp - (char *)buf);
505	}
506	/* NOTREACHED */
507}
508
509ssize_t
510kvm_write(kd, kva, buf, len)
511	kvm_t *kd;
512	register u_long kva;
513	register const void *buf;
514	register size_t len;
515{
516	register int cc;
517
518	if (ISALIVE(kd)) {
519		/*
520		 * Just like kvm_read, only we write.
521		 */
522		errno = 0;
523		if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) {
524			_kvm_err(kd, 0, "invalid address (%x)", kva);
525			return (0);
526		}
527		cc = write(kd->vmfd, buf, len);
528		if (cc < 0) {
529			_kvm_syserr(kd, 0, "kvm_write");
530			return (0);
531		} else if (cc < len)
532			_kvm_err(kd, kd->program, "short write");
533		return (cc);
534	} else {
535		_kvm_err(kd, kd->program,
536		    "kvm_write not implemented for dead kernels");
537		return (0);
538	}
539	/* NOTREACHED */
540}
541