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