xref: /netbsd-src/usr.bin/pmap/main.c (revision b8c616269f5ebf18ab2e35cb8099d683130a177c)
1 /*	$NetBSD: main.c,v 1.3 2003/01/16 15:34:18 atatat Exp $ */
2 
3 /*
4  * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Andrew Brown.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed by the NetBSD
21  *      Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __RCSID("$NetBSD: main.c,v 1.3 2003/01/16 15:34:18 atatat Exp $");
42 #endif
43 
44 #include <sys/param.h>
45 
46 #ifndef __NetBSD_Version__
47 #error go away, you fool
48 #elif (__NetBSD_Version__ < 105000000)
49 #error only works with uvm
50 #endif
51 
52 #include <fcntl.h>
53 #include <errno.h>
54 #include <unistd.h>
55 #include <limits.h>
56 #include <string.h>
57 
58 #include "pmap.h"
59 #include "main.h"
60 
61 /*
62  * strange gyrations to get the prototype for the lockdebug version of
63  * the process_map function
64  */
65 #undef VERSION
66 #define VERSION lockdebug
67 #include "pmap.h"
68 #undef VERSION
69 #define VERSION regular
70 
71 struct cache_head lcache;
72 struct nchashhead *nchashtbl;
73 void *uvm_vnodeops, *uvm_deviceops, *aobj_pager, *ubc_pager;
74 void *kernel_floor;
75 struct vm_map *kmem_map, *mb_map, *phys_map, *exec_map, *pager_map;
76 u_long nchash_addr, nchashtbl_addr, kernel_map_addr;
77 int debug, verbose, recurse;
78 int print_all, print_map, print_maps, print_solaris, print_ddb;
79 int rwx = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE, heapfound;
80 rlim_t maxssiz;
81 
82 struct nlist ksyms[] = {
83 	{ "_maxsmap" },
84 #define NL_MAXSSIZ		0
85 	{ "_uvm_vnodeops" },
86 #define NL_UVM_VNODEOPS		1
87 	{ "_uvm_deviceops" },
88 #define NL_UVM_DEVICEOPS	2
89 	{ "_aobj_pager" },
90 #define NL_AOBJ_PAGER		3
91 	{ "_ubc_pager" },
92 #define NL_UBC_PAGER		4
93 	{ "_kernel_map" },
94 #define NL_KERNEL_MAP		5
95 	{ "_nchashtbl" },
96 #define NL_NCHASHTBL		6
97 	{ "_nchash" },
98 #define NL_NCHASH		7
99 	{ "_kernel_text" },
100 #define NL_KENTER		8
101 	{ NULL }
102 };
103 
104 struct nlist kmaps[] = {
105 	{ "_kmem_map" },
106 #define NL_KMEM_MAP		0
107 	{ "_mb_map" },
108 #define NL_MB_MAP		1
109 	{ "_phys_map" },
110 #define NL_PHYS_MAP		2
111 	{ "_exec_map" },
112 #define NL_EXEC_MAP		3
113 	{ "_pager_map" },
114 #define NL_PAGER_MAP		4
115 	{ NULL }
116 };
117 
118 void check_fd(int);
119 int using_lockdebug(kvm_t *);
120 void load_symbols(kvm_t *);
121 void cache_enter(int, struct namecache *);
122 
123 int
124 main(int argc, char *argv[])
125 {
126 	kvm_t *kd;
127 	pid_t pid;
128 	int many, ch, rc;
129 	char errbuf[_POSIX2_LINE_MAX + 1];
130 	struct kinfo_proc2 *kproc;
131 	char *kmem, *kernel;
132 	gid_t egid;
133 
134 	egid = getegid();
135 	if (setegid(getgid()) == -1)
136 		err(1, "failed to reset privileges");
137 
138 	check_fd(STDIN_FILENO);
139 	check_fd(STDOUT_FILENO);
140 	check_fd(STDERR_FILENO);
141 
142 	pid = -1;
143 	verbose = debug = 0;
144 	print_all = print_map = print_maps = print_solaris = print_ddb = 0;
145 	recurse = 0;
146 	kmem = kernel = NULL;
147 
148 	while ((ch = getopt(argc, argv, "aD:dlmM:N:p:PRrsvx")) != -1) {
149 		switch (ch) {
150 		case 'a':
151 			print_all = 1;
152 			break;
153 		case 'd':
154 			print_ddb = 1;
155 			break;
156 		case 'D':
157 			debug = atoi(optarg);
158 			break;
159 		case 'l':
160 			print_maps = 1;
161 			break;
162 		case 'm':
163 			print_map = 1;
164 			break;
165 		case 'M':
166 			kmem = optarg;
167 			break;
168 		case 'N':
169 			kernel = optarg;
170 			break;
171 		case 'p':
172 			pid = atoi(optarg);
173 			break;
174 		case 'P':
175 			pid = getpid();
176 			break;
177 		case 'R':
178 			recurse = 1;
179 			break;
180 		case 's':
181 			print_solaris = 1;
182 			break;
183 		case 'v':
184 			verbose++;
185 			break;
186 		case 'r':
187 		case 'x':
188 			errx(1, "-%c option not implemented, sorry", optopt);
189 			/*NOTREACHED*/
190 		case '?':
191 		default:
192 			fprintf(stderr, "usage: %s [-adlmPsv] [-D number] "
193 				"[-M core] [-N system] [-p pid] [pid ...]\n",
194 				getprogname());
195 			exit(1);
196 		}
197 	}
198 	argc -= optind;
199 	argv += optind;
200 
201 	/* more than one "process" to dump? */
202 	many = (argc > 1 - (pid == -1 ? 0 : 1)) ? 1 : 0;
203 
204 	/* apply default */
205 	if (print_all + print_map + print_maps + print_solaris +
206 	    print_ddb == 0)
207 		print_solaris = 1;
208 
209 	/* get privs back if it appears to be safe, otherwise toss them */
210 	if (kernel == NULL && kmem == NULL)
211 		rc = setegid(egid);
212 	else
213 		rc = setgid(getgid());
214 	if (rc == -1)
215 		err(1, "failed to reset privileges");
216 
217 	/* start by opening libkvm */
218 	kd = kvm_openfiles(kernel, kmem, NULL, O_RDONLY, errbuf);
219 	errbuf[_POSIX2_LINE_MAX] = '\0';
220 	if (kd == NULL)
221 		errx(1, "%s", errbuf);
222 
223 	/* get "bootstrap" addresses from kernel */
224 	load_symbols(kd);
225 
226 	if (! using_lockdebug(kd))
227 		process_map = PMAPFUNC(process_map,regular);
228 	else
229 		process_map = PMAPFUNC(process_map,lockdebug);
230 
231 	do {
232 		if (pid == -1) {
233 			if (argc == 0)
234 				pid = getppid();
235 			else {
236 				pid = atoi(argv[0]);
237 				argv++;
238 				argc--;
239 			}
240 		}
241 
242 		/* find the process id */
243 		if (pid == 0)
244 			kproc = NULL;
245 		else {
246 			kproc = kvm_getproc2(kd, KERN_PROC_PID, pid,
247 					     sizeof(struct kinfo_proc2), &rc);
248 			if (kproc == NULL || rc == 0) {
249 				errno = ESRCH;
250 				warn("%d", pid);
251 				pid = -1;
252 				continue;
253 			}
254 		}
255 
256 		/* dump it */
257 		if (many) {
258 			if (kproc)
259 				printf("process %d:\n", kproc->p_pid);
260 			else
261 				printf("kernel:\n");
262 		}
263 
264 		(*process_map)(kd, pid, kproc);
265 		pid = -1;
266 	} while (argc > 0);
267 
268 	/* done.  go away. */
269 	rc = kvm_close(kd);
270 	if (rc == -1)
271 		err(1, "kvm_close");
272 
273 	return (0);
274 }
275 
276 void
277 check_fd(int fd)
278 {
279 	struct stat st;
280 	int n;
281 
282 	if (fstat(fd, &st) == -1) {
283 		(void)close(fd);
284 		n = open("/dev/null", O_RDWR);
285 		if (n == fd || n == -1)
286 			/* we're either done or we can do no more */
287 			return;
288 		/* if either of these fail, there's not much we can do */
289 		(void)dup2(n, fd);
290 		(void)close(n);
291 		/* XXX should we exit if it fails? */
292 	}
293 }
294 
295 int
296 using_lockdebug(kvm_t *kd)
297 {
298 	struct kbit kbit[3];
299 	struct kbit *vm_map, *header, *vm_map_entry;
300 
301 	vm_map = &kbit[0];
302 	header = &kbit[1];
303 	vm_map_entry = &kbit[2];
304 
305 	A(vm_map) = kernel_map_addr;
306 	S(vm_map) = sizeof(struct vm_map);
307 	KDEREF(kd, vm_map);
308 
309 	A(header) = A(vm_map) + offsetof(struct vm_map, header);
310 	S(header) = sizeof(struct vm_map_entry);
311 	memcpy(D(header, vm_map_entry), &D(vm_map, vm_map)->header, S(header));
312 
313 	/*
314 	 * the kernel *always* has map entries, but we might see a
315 	 * zero if we're using a lockdebug kernel and haven't noticed
316 	 * yet.
317 	 */
318 	if (D(vm_map, vm_map)->nentries == 0) {
319 
320 		/* no entries -> all pointers must point to the header */
321 		if (P(header) == D(header, vm_map_entry)->next &&
322 		    P(header) == D(header, vm_map_entry)->prev &&
323 		    P(header) == D(vm_map, vm_map)->hint &&
324 		    P(header) == D(vm_map, vm_map)->first_free)
325 			return (0);
326 	}
327 	else {
328 
329 		P(vm_map_entry) = D(header, vm_map_entry)->next;
330 		S(vm_map_entry) = sizeof(struct vm_map_entry);
331 		KDEREF(kd, vm_map_entry);
332 
333 		/* we have entries, so there must be referential integrity */
334 		if (D(vm_map_entry, vm_map_entry)->prev == P(header) &&
335 		    D(header, vm_map_entry)->start <=
336 		    D(vm_map_entry, vm_map_entry)->start &&
337 		    D(vm_map_entry, vm_map_entry)->end <=
338 		    D(header, vm_map_entry)->end)
339 			return (0);
340 	}
341 
342 	return (1);
343 }
344 
345 void
346 load_symbols(kvm_t *kd)
347 {
348 	int rc, i;
349 
350 	rc = kvm_nlist(kd, &ksyms[0]);
351 	if (rc != 0) {
352 		for (i = 0; ksyms[i].n_name != NULL; i++)
353 			if (ksyms[i].n_value == 0)
354 				warnx("symbol %s: not found", ksyms[i].n_name);
355 		exit(1);
356 	}
357 
358 	uvm_vnodeops =	(void*)ksyms[NL_UVM_VNODEOPS].n_value;
359 	uvm_deviceops =	(void*)ksyms[NL_UVM_DEVICEOPS].n_value;
360 	aobj_pager =	(void*)ksyms[NL_AOBJ_PAGER].n_value;
361 	ubc_pager =	(void*)ksyms[NL_UBC_PAGER].n_value;
362 
363 	kernel_floor =	(void*)ksyms[NL_KENTER].n_value;
364 	nchash_addr =	ksyms[NL_NCHASH].n_value;
365 
366 	_KDEREF(kd, ksyms[NL_MAXSSIZ].n_value, &maxssiz,
367 		sizeof(maxssiz));
368 	_KDEREF(kd, ksyms[NL_NCHASHTBL].n_value, &nchashtbl_addr,
369 	       sizeof(nchashtbl_addr));
370 	_KDEREF(kd, ksyms[NL_KERNEL_MAP].n_value, &kernel_map_addr,
371 		sizeof(kernel_map_addr));
372 
373 	/*
374 	 * Some of these may be missing from some platforms, for
375 	 * example sparc, sh3, and most powerpc platforms don't
376 	 * have a "phys_map".
377 	 */
378 	(void)kvm_nlist(kd, &kmaps[0]);
379 	if (kmaps[NL_KMEM_MAP].n_value != 0)
380 		_KDEREF(kd, kmaps[NL_KMEM_MAP].n_value, &kmem_map,
381 			sizeof(kmem_map));
382 	if (kmaps[NL_MB_MAP].n_value != 0)
383 		_KDEREF(kd, kmaps[NL_MB_MAP].n_value, &mb_map,
384 			sizeof(mb_map));
385 	if (kmaps[NL_PHYS_MAP].n_value != 0)
386 		_KDEREF(kd, kmaps[NL_PHYS_MAP].n_value, &phys_map,
387 			sizeof(phys_map));
388 	if (kmaps[NL_EXEC_MAP].n_value != 0)
389 		_KDEREF(kd, kmaps[NL_EXEC_MAP].n_value, &exec_map,
390 			sizeof(exec_map));
391 	if (kmaps[NL_PAGER_MAP].n_value != 0)
392 		_KDEREF(kd, kmaps[NL_PAGER_MAP].n_value, &pager_map,
393 			sizeof(pager_map));
394 }
395 
396 void
397 load_name_cache(kvm_t *kd)
398 {
399 	struct namecache _ncp, *ncp, *oncp;
400 	struct nchashhead _ncpp, *ncpp;
401 	u_long nchash;
402 	int i;
403 
404 	LIST_INIT(&lcache);
405 
406 	_KDEREF(kd, nchash_addr, &nchash, sizeof(nchash));
407 	nchashtbl = malloc(sizeof(nchashtbl) * (int)nchash);
408 	_KDEREF(kd, nchashtbl_addr, nchashtbl,
409 		sizeof(nchashtbl) * (int)nchash);
410 
411 	ncpp = &_ncpp;
412 
413 	for (i = 0; i <= nchash; i++) {
414 		ncpp = &nchashtbl[i];
415 		oncp = NULL;
416 		LIST_FOREACH(ncp, ncpp, nc_hash) {
417 			if (ncp == oncp ||
418 			    (void*)ncp < kernel_floor ||
419 			    ncp == (void*)0xdeadbeef)
420 				break;
421 			oncp = ncp;
422 			_KDEREF(kd, (u_long)ncp, &_ncp, sizeof(*ncp));
423 			ncp = &_ncp;
424 			if ((void*)ncp->nc_vp > kernel_floor &&
425 			    ncp->nc_nlen > 0) {
426 				if (ncp->nc_nlen > 2 ||
427 				    ncp->nc_name[0] != '.' ||
428 				    (ncp->nc_name[1] != '.' &&
429 				     ncp->nc_nlen != 1))
430 					cache_enter(i, ncp);
431 			}
432 		}
433 	}
434 }
435 
436 void
437 cache_enter(int i, struct namecache *ncp)
438 {
439 	struct cache_entry *ce;
440 
441 	if (debug & DUMP_NAMEI_CACHE)
442 		printf("[%d] ncp->nc_vp %10p, ncp->nc_dvp %10p, "
443 		       "ncp->nc_nlen %3d [%.*s] (nc_dvpid=%lu, nc_vpid=%lu)\n",
444 		       i, ncp->nc_vp, ncp->nc_dvp,
445 		       ncp->nc_nlen, ncp->nc_nlen, ncp->nc_name,
446 		       ncp->nc_dvpid, ncp->nc_vpid);
447 
448 	ce = malloc(sizeof(struct cache_entry));
449 
450 	ce->ce_vp = ncp->nc_vp;
451 	ce->ce_pvp = ncp->nc_dvp;
452 	ce->ce_cid = ncp->nc_vpid;
453 	ce->ce_pcid = ncp->nc_dvpid;
454 	ce->ce_nlen = ncp->nc_nlen;
455 	strncpy(ce->ce_name, ncp->nc_name, sizeof(ce->ce_name));
456 	ce->ce_name[MIN(ce->ce_nlen, sizeof(ce->ce_name) - 1)] = '\0';
457 
458 	LIST_INSERT_HEAD(&lcache, ce, ce_next);
459 }
460