add LBL credit line
[unix-history] / usr / src / lib / libkvm / kvm.c
CommitLineData
0c230560 1/*-
41a4fbbe
MT
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
1741f35d
KB
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 *
0c230560 9 * %sccs.include.redist.c%
41a4fbbe
MT
10 */
11
0c230560 12#if defined(LIBC_SCCS) && !defined(lint)
1741f35d 13static char sccsid[] = "@(#)kvm.c 5.28 (Berkeley) %G%";
0c230560 14#endif /* LIBC_SCCS and not lint */
41a4fbbe 15
41a4fbbe 16#include <sys/param.h>
469444a6 17#include <sys/user.h>
41a4fbbe 18#include <sys/proc.h>
41a4fbbe 19#include <sys/ioctl.h>
967726ab 20#include <sys/stat.h>
837ee677 21#include <machine/vmparam.h>
c45ee70b 22#include <fcntl.h>
41a4fbbe 23#include <nlist.h>
469444a6 24#include <kvm.h>
c9c64c8b 25#include <db.h>
41a4fbbe 26#include <paths.h>
0e005c87 27#include <stdio.h>
c45ee70b 28#include <string.h>
533830f8 29#include <stdlib.h>
967726ab 30#include <ctype.h>
41a4fbbe 31
837ee677 32#include <vm/vm.h> /* ??? kinfo_proc currently includes this*/
967726ab
KM
33#include <vm/vm_param.h>
34#include <vm/swap_pager.h>
837ee677 35#include <sys/kinfo_proc.h>
837ee677 36
967726ab 37#include <limits.h>
41a4fbbe 38
967726ab 39#include "kvm_private.h"
bd6478f9 40
967726ab 41static int kvm_dbopen(kvm_t *, const char *);
41a4fbbe 42
967726ab
KM
43char *
44kvm_geterr(kvm_t *kd)
45{
46 return (kd->errbuf);
47}
cc66874e 48
533830f8
KB
49#if __STDC__
50#include <stdarg.h>
51#else
52#include <varargs.h>
53#endif
54
0e005c87 55/*
967726ab
KM
56 * Report an error using printf style arguments. "program" is kd->program
57 * on hard errors, and 0 on soft errors, so that under sun error emulation,
58 * only hard errors are printed out (otherwise, programs like gdb will
59 * generate tons of error messages when trying to access bogus pointers).
0e005c87 60 */
967726ab 61void
533830f8 62#if __STDC__
967726ab 63_kvm_err(kvm_t *kd, const char *program, const char *fmt, ...)
533830f8
KB
64#else
65_kvm_err(kd, program, fmt, va_alist)
66 kvm_t *kd;
67 char *program, *fmt;
68 va_dcl
69#endif
0e005c87 70{
967726ab 71 va_list ap;
41a4fbbe 72
533830f8 73#ifdef __STDC__
967726ab 74 va_start(ap, fmt);
533830f8
KB
75#else
76 va_start(ap);
77#endif
78 if (program != NULL) {
79 (void)fprintf(stderr, "%s: ", program);
80 (void)vfprintf(stderr, fmt, ap);
81 (void)fputc('\n', stderr);
967726ab 82 } else
533830f8
KB
83 (void)vsnprintf(kd->errbuf,
84 sizeof(kd->errbuf), (char *)fmt, ap);
967726ab
KM
85
86 va_end(ap);
87}
88
89void
533830f8 90#if __STDC__
967726ab 91_kvm_syserr(kvm_t *kd, const char *program, const char *fmt, ...)
533830f8
KB
92#else
93_kvm_syserr(kd, program, fmt, va_alist)
94 kvm_t *kd;
95 char *program, *fmt;
96 va_dcl
97#endif
967726ab
KM
98{
99 va_list ap;
100 register int n;
101
533830f8 102#if __STDC__
967726ab 103 va_start(ap, fmt);
533830f8
KB
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)fprintf(stderr, ": %s\n", strerror(errno));
41a4fbbe 111 } else {
967726ab
KM
112 register char *cp = kd->errbuf;
113
533830f8
KB
114 n = vsnprintf(cp, sizeof(kd->errbuf), (char *)fmt, ap);
115 (void)snprintf(&cp[n], sizeof(kd->errbuf) - n, ": %s",
967726ab 116 strerror(errno));
41a4fbbe 117 }
967726ab 118 va_end(ap);
41a4fbbe
MT
119}
120
967726ab
KM
121void *
122_kvm_malloc(kd, n)
123 register kvm_t *kd;
124 register size_t n;
0e005c87 125{
533830f8 126 void *p;
0e005c87 127
533830f8
KB
128 if ((p = malloc(n)) == NULL)
129 _kvm_err(kd, kd->program, strerror(errno));
967726ab 130 return (p);
0e005c87
MT
131}
132
967726ab
KM
133static kvm_t *
134_kvm_open(kd, uf, mf, sf, flag, errout)
135 register kvm_t *kd;
136 const char *uf;
137 const char *mf;
138 const char *sf;
139 int flag;
140 char *errout;
41a4fbbe 141{
967726ab
KM
142 struct stat st;
143
144 kd->vmfd = -1;
145 kd->pmfd = -1;
146 kd->swfd = -1;
147 kd->nlfd = -1;
148 kd->vmst = 0;
149 kd->db = 0;
150 kd->procbase = 0;
151 kd->argspc = 0;
152 kd->argv = 0;
153
154 if (uf == 0)
155 uf = _PATH_UNIX;
156 else if (strlen(uf) >= MAXPATHLEN) {
157 _kvm_err(kd, kd->program, "exec file name too long");
158 goto failed;
0e005c87 159 }
967726ab
KM
160 if (flag & ~O_RDWR) {
161 _kvm_err(kd, kd->program, "bad flags arg");
162 goto failed;
0e005c87 163 }
967726ab
KM
164 if (mf == 0)
165 mf = _PATH_MEM;
166 if (sf == 0)
167 sf = _PATH_DRUM;
168
169 if ((kd->pmfd = open(mf, flag, 0)) < 0) {
170 _kvm_syserr(kd, kd->program, "%s", mf);
171 goto failed;
0e005c87 172 }
967726ab
KM
173 if (fstat(kd->pmfd, &st) < 0) {
174 _kvm_syserr(kd, kd->program, "%s", mf);
175 goto failed;
0e005c87 176 }
967726ab 177 if (S_ISCHR(st.st_mode)) {
41a4fbbe 178 /*
967726ab
KM
179 * If this is a character special device, then check that
180 * it's /dev/mem. If so, open kmem too. (Maybe we should
181 * make it work for either /dev/mem or /dev/kmem -- in either
182 * case you're working with a live kernel.)
41a4fbbe 183 */
967726ab
KM
184 if (strcmp(mf, _PATH_MEM) != 0) { /* XXX */
185 _kvm_err(kd, kd->program,
186 "%s: not physical memory device", mf);
187 goto failed;
41a4fbbe 188 }
967726ab
KM
189 if ((kd->vmfd = open(_PATH_KMEM, flag)) < 0) {
190 _kvm_syserr(kd, kd->program, "%s", _PATH_KMEM);
191 goto failed;
41a4fbbe 192 }
967726ab
KM
193 if ((kd->swfd = open(sf, flag, 0)) < 0) {
194 _kvm_syserr(kd, kd->program, "%s", sf);
195 goto failed;
41a4fbbe 196 }
967726ab
KM
197 /*
198 * Open kvm nlist database. We go ahead and do this
199 * here so that we don't have to hold on to the vmunix
200 * path name. Since a kvm application will surely do
201 * a kvm_nlist(), this probably won't be a wasted effort.
202 * If the database cannot be opened, open the namelist
203 * argument so we revert to slow nlist() calls.
204 */
205 if (kvm_dbopen(kd, uf) < 0 &&
206 (kd->nlfd = open(uf, O_RDONLY, 0)) < 0) {
207 _kvm_syserr(kd, kd->program, "%s", uf);
208 goto failed;
41a4fbbe 209 }
41a4fbbe 210 } else {
41a4fbbe 211 /*
967726ab
KM
212 * This is a crash dump.
213 * Initalize the virtual address translation machinery,
214 * but first setup the namelist fd.
41a4fbbe 215 */
967726ab
KM
216 if ((kd->nlfd = open(uf, O_RDONLY, 0)) < 0) {
217 _kvm_syserr(kd, kd->program, "%s", uf);
218 goto failed;
41a4fbbe 219 }
967726ab
KM
220 if (_kvm_initvtop(kd) < 0)
221 goto failed;
41a4fbbe 222 }
967726ab
KM
223 return (kd);
224failed:
225 /*
226 * Copy out the error if doing sane error semantics.
227 */
228 if (errout != 0)
229 strcpy(errout, kd->errbuf);
230 (void)kvm_close(kd);
231 return (0);
41a4fbbe 232}
41a4fbbe 233
967726ab
KM
234kvm_t *
235kvm_openfiles(uf, mf, sf, flag, errout)
236 const char *uf;
237 const char *mf;
238 const char *sf;
239 int flag;
240 char *errout;
41a4fbbe 241{
533830f8 242 register kvm_t *kd;
41a4fbbe 243
533830f8
KB
244 if ((kd = malloc(sizeof(*kd))) == NULL) {
245 (void)strcpy(errout, strerror(errno));
967726ab 246 return (0);
41a4fbbe 247 }
967726ab 248 kd->program = 0;
533830f8 249 return (_kvm_open(kd, uf, mf, sf, flag, errout));
41a4fbbe
MT
250}
251
967726ab
KM
252kvm_t *
253kvm_open(uf, mf, sf, flag, program)
254 const char *uf;
255 const char *mf;
256 const char *sf;
257 int flag;
258 const char *program;
41a4fbbe 259{
533830f8 260 register kvm_t *kd;
41a4fbbe 261
533830f8
KB
262 if ((kd = malloc(sizeof(*kd))) == NULL && program != NULL) {
263 (void)fprintf(stderr, "%s: %s\n", strerror(errno));
967726ab
KM
264 return (0);
265 }
266 kd->program = program;
533830f8 267 return (_kvm_open(kd, uf, mf, sf, flag, NULL));
41a4fbbe
MT
268}
269
967726ab
KM
270int
271kvm_close(kd)
272 kvm_t *kd;
41a4fbbe 273{
967726ab
KM
274 register int error = 0;
275
276 if (kd->pmfd >= 0)
277 error |= close(kd->pmfd);
278 if (kd->vmfd >= 0)
279 error |= close(kd->vmfd);
280 if (kd->nlfd >= 0)
281 error |= close(kd->nlfd);
282 if (kd->swfd >= 0)
283 error |= close(kd->swfd);
284 if (kd->db != 0)
c9c64c8b 285 error |= (kd->db->close)(kd->db);
967726ab
KM
286 if (kd->vmst)
287 _kvm_freevtop(kd);
288 if (kd->procbase != 0)
289 free((void *)kd->procbase);
290 if (kd->argv != 0)
291 free((void *)kd->argv);
292 free((void *)kd);
41a4fbbe 293
967726ab 294 return (0);
41a4fbbe
MT
295}
296
967726ab
KM
297/*
298 * Set up state necessary to do queries on the kernel namelist
299 * data base. If the data base is out-of-data/incompatible with
300 * given executable, set up things so we revert to standard nlist call.
301 * Only called for live kernels. Return 0 on success, -1 on failure.
302 */
303static int
304kvm_dbopen(kd, uf)
305 kvm_t *kd;
306 const char *uf;
837ee677 307{
967726ab 308 char *cp;
c9c64c8b 309 DBT rec;
967726ab
KM
310 int dbversionlen;
311 struct nlist nitem;
312 char dbversion[_POSIX2_LINE_MAX];
313 char kversion[_POSIX2_LINE_MAX];
314 char dbname[MAXPATHLEN];
837ee677 315
967726ab
KM
316 if ((cp = rindex(uf, '/')) != 0)
317 uf = cp + 1;
318
c9c64c8b
KB
319 (void)snprintf(dbname, sizeof(dbname), "%skvm_%s.db", _PATH_VARDB, uf);
320 kd->db = dbopen(dbname, O_RDONLY, 0, DB_HASH, NULL);
af0a634c
CT
321 if (kd->db == 0)
322 return (-1);
837ee677 323 /*
967726ab 324 * read version out of database
837ee677 325 */
c9c64c8b
KB
326 rec.data = VRS_KEY;
327 rec.size = sizeof(VRS_KEY) - 1;
328 if ((kd->db->get)(kd->db, (DBT *)&rec, (DBT *)&rec, 0))
329 goto close;
330 if (rec.data == 0 || rec.size > sizeof(dbversion))
967726ab
KM
331 goto close;
332
c9c64c8b
KB
333 bcopy(rec.data, dbversion, rec.size);
334 dbversionlen = rec.size;
837ee677 335 /*
967726ab
KM
336 * Read version string from kernel memory.
337 * Since we are dealing with a live kernel, we can call kvm_read()
338 * at this point.
837ee677 339 */
c9c64c8b
KB
340 rec.data = VRS_SYM;
341 rec.size = sizeof(VRS_SYM) - 1;
342 if ((kd->db->get)(kd->db, (DBT *)&rec, (DBT *)&rec, 0))
343 goto close;
344 if (rec.data == 0 || rec.size != sizeof(struct nlist))
967726ab 345 goto close;
c9c64c8b 346 bcopy((char *)rec.data, (char *)&nitem, sizeof(nitem));
967726ab
KM
347 if (kvm_read(kd, (u_long)nitem.n_value, kversion, dbversionlen) !=
348 dbversionlen)
349 goto close;
350 /*
351 * If they match, we win - otherwise clear out kd->db so
352 * we revert to slow nlist().
353 */
354 if (bcmp(dbversion, kversion, dbversionlen) == 0)
355 return (0);
356close:
c9c64c8b 357 (void)(kd->db->close)(kd->db);
967726ab 358 kd->db = 0;
41a4fbbe 359
967726ab 360 return (-1);
41a4fbbe
MT
361}
362
967726ab
KM
363int
364kvm_nlist(kd, nl)
365 kvm_t *kd;
366 struct nlist *nl;
41a4fbbe 367{
967726ab
KM
368 register struct nlist *p;
369 register int nvalid;
41a4fbbe 370
967726ab
KM
371 /*
372 * If we can't use the data base, revert to the
373 * slow library call.
374 */
375 if (kd->db == 0)
376 return (__fdnlist(kd->nlfd, nl));
3c4639f9 377
967726ab
KM
378 /*
379 * We can use the kvm data base. Go through each nlist entry
c9c64c8b 380 * and look it up with a db query.
967726ab
KM
381 */
382 nvalid = 0;
383 for (p = nl; p->n_name && p->n_name[0]; ++p) {
384 register int len;
c9c64c8b 385 DBT rec;
967726ab
KM
386
387 if ((len = strlen(p->n_name)) > 4096) {
388 /* sanity */
389 _kvm_err(kd, kd->program, "symbol too large");
837ee677
MK
390 return (-1);
391 }
c9c64c8b
KB
392 rec.data = p->n_name;
393 rec.size = len;
394 if ((kd->db->get)(kd->db, (DBT *)&rec, (DBT *)&rec, 0))
395 continue;
396 if (rec.data == 0 || rec.size != sizeof(struct nlist))
967726ab
KM
397 continue;
398 ++nvalid;
399 /*
400 * Avoid alignment issues.
401 */
c9c64c8b 402 bcopy((char *)&((struct nlist *)rec.data)->n_type,
967726ab
KM
403 (char *)&p->n_type,
404 sizeof(p->n_type));
c9c64c8b 405 bcopy((char *)&((struct nlist *)rec.data)->n_value,
967726ab
KM
406 (char *)&p->n_value,
407 sizeof(p->n_value));
41a4fbbe 408 }
967726ab
KM
409 /*
410 * Return the number of entries that weren't found.
411 */
412 return ((p - nl) - nvalid);
41a4fbbe
MT
413}
414
967726ab
KM
415ssize_t
416kvm_read(kd, kva, buf, len)
417 kvm_t *kd;
418 register u_long kva;
419 register char *buf;
420 register size_t len;
41a4fbbe 421{
967726ab
KM
422 register int cc;
423 register char *cp;
424
425 if (ISALIVE(kd)) {
426 /*
427 * We're using /dev/kmem. Just read straight from the
428 * device and let the active kernel do the address translation.
429 */
430 errno = 0;
431 if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) {
432 _kvm_err(kd, 0, "invalid address (%x)", kva);
433 return (0);
41a4fbbe 434 }
967726ab
KM
435 cc = read(kd->vmfd, buf, len);
436 if (cc < 0) {
437 _kvm_syserr(kd, 0, "kvm_read");
438 return (0);
439 } else if (cc < len)
440 _kvm_err(kd, kd->program, "short read");
9eeec344 441 return (cc);
41a4fbbe 442 } else {
967726ab
KM
443 cp = buf;
444 while (len > 0) {
445 u_long pa;
446
447 cc = _kvm_kvatop(kd, kva, &pa);
448 if (cc == 0)
449 return (0);
450 if (cc > len)
451 cc = len;
452 errno = 0;
453 if (lseek(kd->pmfd, (off_t)pa, 0) == -1 && errno != 0) {
454 _kvm_syserr(kd, 0, _PATH_MEM);
455 break;
456 }
457 cc = read(kd->pmfd, cp, cc);
458 if (cc < 0) {
459 _kvm_syserr(kd, kd->program, "kvm_read");
460 break;
461 }
462 cp += cc;
463 kva += cc;
464 len -= cc;
41a4fbbe 465 }
967726ab 466 return (cp - buf);
41a4fbbe 467 }
967726ab 468 /* NOTREACHED */
41a4fbbe 469}
9eeec344
CT
470
471ssize_t
472kvm_write(kd, kva, buf, len)
473 kvm_t *kd;
474 register u_long kva;
475 register const char *buf;
476 register size_t len;
477{
478 register int cc;
479
480 if (ISALIVE(kd)) {
481 /*
482 * Just like kvm_read, only we write.
483 */
484 errno = 0;
485 if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) {
486 _kvm_err(kd, 0, "invalid address (%x)", kva);
487 return (0);
488 }
489 cc = write(kd->vmfd, buf, len);
490 if (cc < 0) {
491 _kvm_syserr(kd, 0, "kvm_write");
492 return (0);
493 } else if (cc < len)
494 _kvm_err(kd, kd->program, "short write");
495 return (cc);
496 } else {
497 _kvm_err(kd, kd->program,
498 "kvm_write not implemented for dead kernels");
499 return (0);
500 }
501 /* NOTREACHED */
502}