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