kvm.c revision 92917
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#include <sys/cdefs.h>
39__FBSDID("$FreeBSD: head/lib/libkvm/kvm.c 92917 2002-03-21 23:54:04Z obrien $");
40
41#if defined(LIBC_SCCS) && !defined(lint)
42#if 0
43static char sccsid[] = "@(#)kvm.c	8.2 (Berkeley) 2/13/94";
44#endif
45#endif /* LIBC_SCCS and not lint */
46
47#include <sys/param.h>
48#include <sys/user.h>
49#include <sys/proc.h>
50#include <sys/ioctl.h>
51#include <sys/stat.h>
52#include <sys/sysctl.h>
53#include <sys/linker.h>
54
55#include <vm/vm.h>
56#include <vm/vm_param.h>
57#include <vm/swap_pager.h>
58
59#include <machine/vmparam.h>
60
61#include <ctype.h>
62#include <fcntl.h>
63#include <kvm.h>
64#include <limits.h>
65#include <nlist.h>
66#include <paths.h>
67#include <stdio.h>
68#include <stdlib.h>
69#include <string.h>
70#include <unistd.h>
71
72#include "kvm_private.h"
73
74/* from src/lib/libc/gen/nlist.c */
75int __fdnlist(int, struct nlist *);
76
77char *
78kvm_geterr(kd)
79	kvm_t *kd;
80{
81	return (kd->errbuf);
82}
83
84#if __STDC__
85#include <stdarg.h>
86#else
87#include <varargs.h>
88#endif
89
90/*
91 * Report an error using printf style arguments.  "program" is kd->program
92 * on hard errors, and 0 on soft errors, so that under sun error emulation,
93 * only hard errors are printed out (otherwise, programs like gdb will
94 * generate tons of error messages when trying to access bogus pointers).
95 */
96void
97#if __STDC__
98_kvm_err(kvm_t *kd, const char *program, const char *fmt, ...)
99#else
100_kvm_err(kd, program, fmt, va_alist)
101	kvm_t *kd;
102	char *program, *fmt;
103	va_dcl
104#endif
105{
106	va_list ap;
107
108#ifdef __STDC__
109	va_start(ap, fmt);
110#else
111	va_start(ap);
112#endif
113	if (program != NULL) {
114		(void)fprintf(stderr, "%s: ", program);
115		(void)vfprintf(stderr, fmt, ap);
116		(void)fputc('\n', stderr);
117	} else
118		(void)vsnprintf(kd->errbuf,
119		    sizeof(kd->errbuf), (char *)fmt, ap);
120
121	va_end(ap);
122}
123
124void
125#if __STDC__
126_kvm_syserr(kvm_t *kd, const char *program, const char *fmt, ...)
127#else
128_kvm_syserr(kd, program, fmt, va_alist)
129	kvm_t *kd;
130	char *program, *fmt;
131	va_dcl
132#endif
133{
134	va_list ap;
135	int n;
136
137#if __STDC__
138	va_start(ap, fmt);
139#else
140	va_start(ap);
141#endif
142	if (program != NULL) {
143		(void)fprintf(stderr, "%s: ", program);
144		(void)vfprintf(stderr, fmt, ap);
145		(void)fprintf(stderr, ": %s\n", strerror(errno));
146	} else {
147		char *cp = kd->errbuf;
148
149		(void)vsnprintf(cp, sizeof(kd->errbuf), (char *)fmt, ap);
150		n = strlen(cp);
151		(void)snprintf(&cp[n], sizeof(kd->errbuf) - n, ": %s",
152		    strerror(errno));
153	}
154	va_end(ap);
155}
156
157void *
158_kvm_malloc(kd, n)
159	kvm_t *kd;
160	size_t n;
161{
162	void *p;
163
164	if ((p = calloc(n, sizeof(char))) == NULL)
165		_kvm_err(kd, kd->program, "can't allocate %u bytes: %s",
166			 n, strerror(errno));
167	return (p);
168}
169
170static kvm_t *
171_kvm_open(kd, uf, mf, flag, errout)
172	kvm_t *kd;
173	const char *uf;
174	const char *mf;
175	int flag;
176	char *errout;
177{
178	struct stat st;
179
180	kd->vmfd = -1;
181	kd->pmfd = -1;
182	kd->nlfd = -1;
183	kd->vmst = 0;
184	kd->procbase = 0;
185	kd->argspc = 0;
186	kd->argv = 0;
187
188	if (uf == 0)
189		uf = getbootfile();
190	else if (strlen(uf) >= MAXPATHLEN) {
191		_kvm_err(kd, kd->program, "exec file name too long");
192		goto failed;
193	}
194	if (flag & ~O_RDWR) {
195		_kvm_err(kd, kd->program, "bad flags arg");
196		goto failed;
197	}
198	if (mf == 0)
199		mf = _PATH_MEM;
200
201	if ((kd->pmfd = open(mf, flag, 0)) < 0) {
202		_kvm_syserr(kd, kd->program, "%s", mf);
203		goto failed;
204	}
205	if (fstat(kd->pmfd, &st) < 0) {
206		_kvm_syserr(kd, kd->program, "%s", mf);
207		goto failed;
208	}
209	if (S_ISCHR(st.st_mode)) {
210		/*
211		 * If this is a character special device, then check that
212		 * it's /dev/mem.  If so, open kmem too.  (Maybe we should
213		 * make it work for either /dev/mem or /dev/kmem -- in either
214		 * case you're working with a live kernel.)
215		 */
216		if (strcmp(mf, _PATH_DEVNULL) == 0) {
217			kd->vmfd = open(_PATH_DEVNULL, O_RDONLY);
218		} else if (strcmp(mf, _PATH_MEM) != 0) {
219			_kvm_err(kd, kd->program,
220				 "%s: not physical memory device", mf);
221			goto failed;
222		} else {
223			if ((kd->vmfd = open(_PATH_KMEM, flag)) < 0) {
224				_kvm_syserr(kd, kd->program, "%s", _PATH_KMEM);
225				goto failed;
226			}
227		}
228	} else {
229		/*
230		 * This is a crash dump.
231		 * Initialize the virtual address translation machinery,
232		 * but first setup the namelist fd.
233		 */
234		if ((kd->nlfd = open(uf, O_RDONLY, 0)) < 0) {
235			_kvm_syserr(kd, kd->program, "%s", uf);
236			goto failed;
237		}
238		if (_kvm_initvtop(kd) < 0)
239			goto failed;
240	}
241	return (kd);
242failed:
243	/*
244	 * Copy out the error if doing sane error semantics.
245	 */
246	if (errout != 0)
247		strlcpy(errout, kd->errbuf, _POSIX2_LINE_MAX);
248	(void)kvm_close(kd);
249	return (0);
250}
251
252kvm_t *
253kvm_openfiles(uf, mf, sf, flag, errout)
254	const char *uf;
255	const char *mf;
256	const char *sf __unused;
257	int flag;
258	char *errout;
259{
260	kvm_t *kd;
261
262	if ((kd = malloc(sizeof(*kd))) == NULL) {
263		(void)strlcpy(errout, strerror(errno), _POSIX2_LINE_MAX);
264		return (0);
265	}
266	memset(kd, 0, sizeof(*kd));
267	kd->program = 0;
268	return (_kvm_open(kd, uf, mf, flag, errout));
269}
270
271kvm_t *
272kvm_open(uf, mf, sf, flag, errstr)
273	const char *uf;
274	const char *mf;
275	const char *sf __unused;
276	int flag;
277	const char *errstr;
278{
279	kvm_t *kd;
280
281	if ((kd = malloc(sizeof(*kd))) == NULL) {
282		if (errstr != NULL)
283			(void)fprintf(stderr, "%s: %s\n",
284				      errstr, strerror(errno));
285		return (0);
286	}
287	memset(kd, 0, sizeof(*kd));
288	kd->program = errstr;
289	return (_kvm_open(kd, uf, mf, flag, NULL));
290}
291
292int
293kvm_close(kd)
294	kvm_t *kd;
295{
296	int error = 0;
297
298	if (kd->pmfd >= 0)
299		error |= close(kd->pmfd);
300	if (kd->vmfd >= 0)
301		error |= close(kd->vmfd);
302	if (kd->nlfd >= 0)
303		error |= close(kd->nlfd);
304	if (kd->vmst)
305		_kvm_freevtop(kd);
306	if (kd->procbase != 0)
307		free((void *)kd->procbase);
308	if (kd->argv != 0)
309		free((void *)kd->argv);
310	free((void *)kd);
311
312	return (0);
313}
314
315int
316kvm_nlist(kd, nl)
317	kvm_t *kd;
318	struct nlist *nl;
319{
320	struct nlist *p;
321	int nvalid;
322	struct kld_sym_lookup lookup;
323
324	/*
325	 * If we can't use the kld symbol lookup, revert to the
326	 * slow library call.
327	 */
328	if (!ISALIVE(kd))
329		return (__fdnlist(kd->nlfd, nl));
330
331	/*
332	 * We can use the kld lookup syscall.  Go through each nlist entry
333	 * and look it up with a kldsym(2) syscall.
334	 */
335	nvalid = 0;
336	for (p = nl; p->n_name && p->n_name[0]; ++p) {
337		lookup.version = sizeof(lookup);
338		lookup.symname = p->n_name;
339		lookup.symvalue = 0;
340		lookup.symsize = 0;
341
342		if (lookup.symname[0] == '_')
343			lookup.symname++;
344
345		if (kldsym(0, KLDSYM_LOOKUP, &lookup) != -1) {
346			p->n_type = N_TEXT;
347			p->n_other = 0;
348			p->n_desc = 0;
349			p->n_value = lookup.symvalue;
350			++nvalid;
351			/* lookup.symsize */
352		}
353	}
354	/*
355	 * Return the number of entries that weren't found.
356	 */
357	return ((p - nl) - nvalid);
358}
359
360ssize_t
361kvm_read(kd, kva, buf, len)
362	kvm_t *kd;
363	u_long kva;
364	void *buf;
365	size_t len;
366{
367	int cc;
368	void *cp;
369
370	if (ISALIVE(kd)) {
371		/*
372		 * We're using /dev/kmem.  Just read straight from the
373		 * device and let the active kernel do the address translation.
374		 */
375		errno = 0;
376		if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) {
377			_kvm_err(kd, 0, "invalid address (%x)", kva);
378			return (-1);
379		}
380		cc = read(kd->vmfd, buf, len);
381		if (cc < 0) {
382			_kvm_syserr(kd, 0, "kvm_read");
383			return (-1);
384		} else if (cc < len)
385			_kvm_err(kd, kd->program, "short read");
386		return (cc);
387	} else {
388		cp = buf;
389		while (len > 0) {
390			u_long pa;
391
392			cc = _kvm_kvatop(kd, kva, &pa);
393			if (cc == 0)
394				return (-1);
395			if (cc > len)
396				cc = len;
397			errno = 0;
398			if (lseek(kd->pmfd, (off_t)pa, 0) == -1 && errno != 0) {
399				_kvm_syserr(kd, 0, _PATH_MEM);
400				break;
401			}
402			cc = read(kd->pmfd, cp, cc);
403			if (cc < 0) {
404				_kvm_syserr(kd, kd->program, "kvm_read");
405				break;
406			}
407			/*
408			 * If kvm_kvatop returns a bogus value or our core
409			 * file is truncated, we might wind up seeking beyond
410			 * the end of the core file in which case the read will
411			 * return 0 (EOF).
412			 */
413			if (cc == 0)
414				break;
415			(char *)cp += cc;
416			kva += cc;
417			len -= cc;
418		}
419		return ((char *)cp - (char *)buf);
420	}
421	/* NOTREACHED */
422}
423
424ssize_t
425kvm_write(kd, kva, buf, len)
426	kvm_t *kd;
427	u_long kva;
428	const void *buf;
429	size_t len;
430{
431	int cc;
432
433	if (ISALIVE(kd)) {
434		/*
435		 * Just like kvm_read, only we write.
436		 */
437		errno = 0;
438		if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) {
439			_kvm_err(kd, 0, "invalid address (%x)", kva);
440			return (-1);
441		}
442		cc = write(kd->vmfd, buf, len);
443		if (cc < 0) {
444			_kvm_syserr(kd, 0, "kvm_write");
445			return (-1);
446		} else if (cc < len)
447			_kvm_err(kd, kd->program, "short write");
448		return (cc);
449	} else {
450		_kvm_err(kd, kd->program,
451		    "kvm_write not implemented for dead kernels");
452		return (-1);
453	}
454	/* NOTREACHED */
455}
456