]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libkvm/kvm.c
This commit was generated by cvs2svn to compensate for changes in r69830,
[FreeBSD/FreeBSD.git] / lib / libkvm / kvm.c
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)
39 #if 0
40 static char sccsid[] = "@(#)kvm.c       8.2 (Berkeley) 2/13/94";
41 #else
42 static const char rcsid[] =
43  "$FreeBSD$";
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 */
75 int __fdnlist           __P((int, struct nlist *));
76
77 char *
78 kvm_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  */
96 void
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
124 void
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         register 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                 register 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
157 void *
158 _kvm_malloc(kd, n)
159         register kvm_t *kd;
160         register 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
170 static kvm_t *
171 _kvm_open(kd, uf, mf, flag, errout)
172         register 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);
242 failed:
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
252 kvm_t *
253 kvm_openfiles(uf, mf, sf, flag, errout)
254         const char *uf;
255         const char *mf;
256         const char *sf;
257         int flag;
258         char *errout;
259 {
260         register 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
271 kvm_t *
272 kvm_open(uf, mf, sf, flag, errstr)
273         const char *uf;
274         const char *mf;
275         const char *sf;
276         int flag;
277         const char *errstr;
278 {
279         register 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
292 int
293 kvm_close(kd)
294         kvm_t *kd;
295 {
296         register 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
315 int
316 kvm_nlist(kd, nl)
317         kvm_t *kd;
318         struct nlist *nl;
319 {
320         register struct nlist *p;
321         register 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
360 ssize_t
361 kvm_read(kd, kva, buf, len)
362         kvm_t *kd;
363         register u_long kva;
364         register void *buf;
365         register size_t len;
366 {
367         register int cc;
368         register 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
424 ssize_t
425 kvm_write(kd, kva, buf, len)
426         kvm_t *kd;
427         register u_long kva;
428         register const void *buf;
429         register size_t len;
430 {
431         register 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 }