Commit | Line | Data |
---|---|---|
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 | 9 | static 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 | 37 | static int kvm_dbopen(kvm_t *, const char *); |
41a4fbbe | 38 | |
967726ab KM |
39 | char * |
40 | kvm_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 | 57 | void |
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 | ||
85 | void | |
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 |
117 | void * |
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 |
129 | static 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); |
220 | failed: | |
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 |
230 | kvm_t * |
231 | kvm_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 |
248 | kvm_t * |
249 | kvm_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 |
266 | int |
267 | kvm_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 | */ | |
299 | static int | |
300 | kvm_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); | |
352 | close: | |
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 |
359 | int |
360 | kvm_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 |
411 | ssize_t |
412 | kvm_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 | |
467 | ssize_t | |
468 | kvm_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 | } |