Commit | Line | Data |
---|---|---|
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 | 13 | static 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 | 41 | static int kvm_dbopen(kvm_t *, const char *); |
41a4fbbe | 42 | |
967726ab KM |
43 | char * |
44 | kvm_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 | 61 | void |
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 | ||
89 | void | |
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 |
121 | void * |
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 |
133 | static 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); |
224 | failed: | |
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 |
234 | kvm_t * |
235 | kvm_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 |
252 | kvm_t * |
253 | kvm_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 |
270 | int |
271 | kvm_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 | */ | |
303 | static int | |
304 | kvm_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); | |
356 | close: | |
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 |
363 | int |
364 | kvm_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 |
415 | ssize_t |
416 | kvm_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 | |
471 | ssize_t | |
472 | kvm_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 | } |