1/*	$NetBSD: main.c,v 1.23 2011/10/25 22:13:22 jym 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 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#ifndef lint
34__RCSID("$NetBSD: main.c,v 1.23 2011/10/25 22:13:22 jym Exp $");
35#endif
36
37#include <sys/param.h>
38
39#ifndef __NetBSD_Version__
40#error go away, you fool
41#elif (__NetBSD_Version__ < 105000000)
42#error only works with uvm
43#endif
44
45#include <fcntl.h>
46#include <errno.h>
47#include <unistd.h>
48#include <limits.h>
49#include <string.h>
50#include <signal.h>
51
52#include "pmap.h"
53#include "main.h"
54
55struct cache_head lcache;
56struct nchashhead *nchashtbl;
57void *uvm_vnodeops, *uvm_deviceops, *aobj_pager, *ubc_pager;
58struct vm_map *kmem_map, *mb_map, *phys_map, *exec_map, *pager_map;
59struct vm_map *st_map, *pt_map, *lkm_map, *buf_map;
60u_long nchash_addr, nchashtbl_addr, kernel_map_addr;
61int debug, verbose, recurse, page_size;
62int print_all, print_map, print_maps, print_solaris, print_ddb;
63rlim_t maxssiz;
64
65struct nlist ksyms[] = {
66	{ "_maxsmap", 0, 0, 0, 0 },
67#define NL_MAXSSIZ		0
68	{ "_uvm_vnodeops", 0, 0, 0, 0 },
69#define NL_UVM_VNODEOPS		1
70	{ "_uvm_deviceops", 0, 0, 0, 0 },
71#define NL_UVM_DEVICEOPS	2
72	{ "_aobj_pager", 0, 0, 0, 0 },
73#define NL_AOBJ_PAGER		3
74	{ "_ubc_pager", 0, 0, 0, 0 },
75#define NL_UBC_PAGER		4
76	{ "_kernel_map", 0, 0, 0, 0 },
77#define NL_KERNEL_MAP		5
78	{ "_nchashtbl", 0, 0, 0, 0 },
79#define NL_NCHASHTBL		6
80	{ "_nchash", 0, 0, 0, 0 },
81#define NL_NCHASH		7
82	{ NULL, 0, 0, 0, 0 }
83};
84
85struct nlist kmaps[] = {
86	{ "_kmem_map", 0, 0, 0, 0 },
87#define NL_kmem_map		0
88	{ "_mb_map", 0, 0, 0, 0 },
89#define NL_mb_map		1
90	{ "_phys_map", 0, 0, 0, 0 },
91#define NL_phys_map		2
92	{ "_exec_map", 0, 0, 0, 0 },
93#define NL_exec_map		3
94	{ "_pager_map", 0, 0, 0, 0 },
95#define NL_pager_map		4
96	{ "_st_map", 0, 0, 0, 0 },
97#define NL_st_map		5
98	{ "_pt_map", 0, 0, 0, 0 },
99#define NL_pt_map		6
100	{ "_lkm_map", 0, 0, 0, 0 },
101#define NL_lkm_map		7
102	{ "_buf_map", 0, 0, 0, 0 },
103#define NL_buf_map		8
104	{ NULL, 0, 0, 0, 0 },
105};
106
107#define VMSPACE_ADDRESS		1
108#define VM_MAP_ADDRESS		2
109#define VM_MAP_ENTRY_ADDRESS	3
110#define AMAP_ADDRESS		4
111
112void check_fd(int);
113void load_symbols(kvm_t *);
114void cache_enter(u_long, struct namecache *);
115
116int
117main(int argc, char *argv[])
118{
119	kvm_t *kd;
120	pid_t pid;
121	uid_t uid;
122	int which, many, ch, rc;
123	char errbuf[_POSIX2_LINE_MAX + 1];
124	struct kinfo_proc2 *kproc;
125	char *kmem, *kernel, *t;
126	gid_t egid;
127	struct kbit kbit, *vmspace;
128	u_long address;
129
130	egid = getegid();
131	if (setegid(getgid()) == -1)
132		err(1, "failed to reset privileges");
133
134	check_fd(STDIN_FILENO);
135	check_fd(STDOUT_FILENO);
136	check_fd(STDERR_FILENO);
137
138	pid = -1;
139	which = verbose = debug = 0;
140	print_all = print_map = print_maps = print_solaris = print_ddb = 0;
141	recurse = 0;
142	kmem = kernel = NULL;
143	address = 0;
144	vmspace = &kbit;
145
146	while ((ch = getopt(argc, argv, "A:aD:dE:lM:mN:Pp:RrS:sV:vx")) != -1) {
147		switch (ch) {
148		case 'A':
149		case 'E':
150		case 'S':
151		case 'V':
152			if (which != 0)
153				errx(1, "use only one of -A, -E, -S, or -V");
154			errno = 0;
155			address = strtoul(optarg, &t, 0);
156			if (*t != '\0')
157				errx(1, "%s is not a valid address", optarg);
158			if (errno != 0)
159				err(1, "%s is not a valid address", optarg);
160			switch (ch) {
161			case 'A':	which = AMAP_ADDRESS;		break;
162			case 'E':	which = VM_MAP_ENTRY_ADDRESS;	break;
163			case 'S':	which = VMSPACE_ADDRESS;	break;
164			case 'V':	which = VM_MAP_ADDRESS;		break;
165			}
166			break;
167		case 'a':
168			print_all = 1;
169			break;
170		case 'd':
171			print_ddb = 1;
172			break;
173		case 'D':
174			errno = 0;
175			debug = strtoul(optarg, &t, 0);
176			if (*t != '\0')
177				errx(1, "%s is not a valid number", optarg);
178			if (errno != 0)
179				err(1, "%s is not a valid number", optarg);
180			break;
181		case 'l':
182			print_maps = 1;
183			break;
184		case 'm':
185			print_map = 1;
186			break;
187		case 'M':
188			kmem = optarg;
189			break;
190		case 'N':
191			kernel = optarg;
192			break;
193		case 'p':
194			errno = 0;
195			pid = strtol(optarg, &t, 0);
196			if (pid < 0)
197				errno = EINVAL;
198			if (*t != '\0')
199				errx(1, "%s is not a valid pid", optarg);
200			if (errno != 0)
201				err(1, "%s is not a valid pid", optarg);
202			break;
203		case 'P':
204			pid = getpid();
205			break;
206		case 'R':
207			recurse = 1;
208			break;
209		case 's':
210			print_solaris = 1;
211			break;
212		case 'v':
213			verbose++;
214			break;
215		case 'r':
216		case 'x':
217			errx(1, "-%c option not implemented, sorry", optopt);
218			/*NOTREACHED*/
219		case '?':
220		default:
221			fprintf(stderr, "usage: %s [-adlmPRsv] [-A address] "
222				"[-D number] [-E address] [-M core]\n"
223				"\t[-N system] [-p pid] [-S address] "
224				"[-V address] [pid ...]\n",
225				getprogname());
226			exit(1);
227		}
228	}
229	argc -= optind;
230	argv += optind;
231
232	/* more than one "process" to dump? */
233	many = (argc > 1 - (pid == -1 ? 0 : 1)) ? 1 : 0;
234
235	/* apply default */
236	if (print_all + print_map + print_maps + print_solaris +
237	    print_ddb == 0)
238		print_solaris = 1;
239
240	/* get privs back if it appears to be safe, otherwise toss them */
241	if (kernel == NULL && kmem == NULL && address == 0)
242		rc = setegid(egid);
243	else
244		rc = setgid(getgid());
245	if (rc == -1)
246		err(1, "failed to reset privileges");
247
248	/* start by opening libkvm */
249	kd = kvm_openfiles(kernel, kmem, NULL, O_RDONLY, errbuf);
250
251	/* we're completely done with privileges now */
252	rc = setgid(getgid());
253	if (rc == -1)
254		err(1, "failed to reset privileges");
255
256	/* print the kvm_open error, if any */
257	errbuf[_POSIX2_LINE_MAX] = '\0';
258	if (kd == NULL)
259		errx(1, "%s", errbuf);
260
261	/* get "bootstrap" addresses from kernel */
262	load_symbols(kd);
263
264	if (address) {
265		struct kbit kbit2, *at = &kbit2;
266
267		memset(vmspace, 0, sizeof(*vmspace));
268		A(at) = address;
269		S(at) = (size_t)-1;
270
271		switch (which) {
272		    case VMSPACE_ADDRESS:
273			/* (kd, kproc, vmspace, thing) */
274			(*process_map)(kd, NULL, at, "vm_map");
275			break;
276		    case VM_MAP_ADDRESS:
277			/* (kd, proc, vmspace, vm_map, thing) */
278			(*dump_vm_map)(kd, NULL, vmspace, at, "vm_map");
279			break;
280		    case VM_MAP_ENTRY_ADDRESS:
281			/* (kd, proc, vmspace, vm_map_entry, 0) */
282			(*dump_vm_map_entry)(kd, NULL, vmspace, at, 0);
283			break;
284		    case AMAP_ADDRESS:
285			/* (kd, amap) */
286			(*dump_amap)(kd, at);
287			break;
288		}
289		exit(0);
290	}
291
292	uid = getuid();
293
294	do {
295		if (pid == -1) {
296			if (argc == 0)
297				pid = getppid();
298			else {
299				errno = 0;
300				pid = strtol(argv[0], &t, 0);
301				if (pid < 0)
302					errno = EINVAL;
303				if (*t != '\0')
304					errx(1, "%s is not a valid pid",
305					    argv[0]);
306				if (errno != 0)
307					err(1, "%s is not a valid pid",
308					    argv[0]);
309				argv++;
310				argc--;
311			}
312		}
313
314		errno = 0;
315		/* find the process id */
316		if (pid == 0) {
317			kproc = NULL;
318			if (uid != 0) {
319				/* only root can print kernel mappings */
320				errno = EPERM;
321			}
322		} else {
323			kproc = kvm_getproc2(kd, KERN_PROC_PID, pid,
324			    sizeof(struct kinfo_proc2), &rc);
325			if (kproc == NULL || rc == 0) {
326				errno = ESRCH;
327			} else if (uid != 0 && uid != kproc->p_uid) {
328				/*
329				 * only the real owner of the process and
330				 * root can print process mappings
331				 */
332				errno = EPERM;
333			}
334		}
335
336		if (errno != 0) {
337			warn("%d", pid);
338			pid = -1;
339			continue;
340		}
341
342		/* dump it */
343		if (many) {
344			if (kproc != NULL)
345				printf("process %d:\n", kproc->p_pid);
346			else
347				printf("kernel:\n");
348		}
349
350		(*process_map)(kd, kproc, vmspace, NULL);
351		pid = -1;
352	} while (argc > 0);
353
354	/* done.  go away. */
355	rc = kvm_close(kd);
356	if (rc == -1)
357		err(1, "kvm_close");
358
359	return (0);
360}
361
362void
363check_fd(int fd)
364{
365	struct stat st;
366	int n;
367
368	if (fstat(fd, &st) == -1) {
369		(void)close(fd);
370		n = open("/dev/null", O_RDWR);
371		if (n == fd || n == -1)
372			/* we're either done or we can do no more */
373			return;
374		/* if either of these fail, there's not much we can do */
375		(void)dup2(n, fd);
376		(void)close(n);
377		/* XXX should we exit if it fails? */
378	}
379}
380
381void
382load_symbols(kvm_t *kd)
383{
384	int rc, i, mib[2];
385	size_t sz;
386
387	rc = kvm_nlist(kd, &ksyms[0]);
388	if (rc != 0) {
389		for (i = 0; ksyms[i].n_name != NULL; i++)
390			if (ksyms[i].n_value == 0)
391				warnx("symbol %s: not found", ksyms[i].n_name);
392		exit(1);
393	}
394
395	uvm_vnodeops =	(void*)ksyms[NL_UVM_VNODEOPS].n_value;
396	uvm_deviceops =	(void*)ksyms[NL_UVM_DEVICEOPS].n_value;
397	aobj_pager =	(void*)ksyms[NL_AOBJ_PAGER].n_value;
398	ubc_pager =	(void*)ksyms[NL_UBC_PAGER].n_value;
399
400	nchash_addr =	ksyms[NL_NCHASH].n_value;
401
402	_KDEREF(kd, ksyms[NL_MAXSSIZ].n_value, &maxssiz,
403		sizeof(maxssiz));
404	_KDEREF(kd, ksyms[NL_NCHASHTBL].n_value, &nchashtbl_addr,
405	       sizeof(nchashtbl_addr));
406	_KDEREF(kd, ksyms[NL_KERNEL_MAP].n_value, &kernel_map_addr,
407		sizeof(kernel_map_addr));
408
409	/*
410	 * Some of these may be missing from some platforms, for
411	 * example sparc, sh3, and most powerpc platforms don't
412	 * have a "phys_map", etc.
413	 */
414	(void)kvm_nlist(kd, &kmaps[0]);
415
416#define get_map_address(m) do {\
417	if (kmaps[__CONCAT(NL_,m)].n_value != 0) \
418		_KDEREF(kd, kmaps[__CONCAT(NL_,m)].n_value, &m, sizeof(m)); \
419	} while (0/*CONSTCOND*/)
420
421	get_map_address(kmem_map);
422	get_map_address(mb_map);
423	get_map_address(phys_map);
424	get_map_address(exec_map);
425	get_map_address(pager_map);
426	get_map_address(st_map);
427	get_map_address(pt_map);
428	get_map_address(lkm_map);
429	get_map_address(buf_map);
430
431	mib[0] = CTL_HW;
432	mib[1] = HW_PAGESIZE;
433	sz = sizeof(page_size);
434	if (sysctl(&mib[0], 2, &page_size, &sz, NULL, 0) == -1)
435		err(1, "sysctl: hw.pagesize");
436}
437
438const char *
439mapname(void *addr)
440{
441
442	if (addr == (void*)kernel_map_addr)
443		return ("kernel_map");
444	else if (addr == kmem_map)
445		return ("kmem_map");
446	else if (addr == mb_map)
447		return ("mb_map");
448	else if (addr == phys_map)
449		return ("phys_map");
450	else if (addr == exec_map)
451		return ("exec_map");
452	else if (addr == pager_map)
453		return ("pager_map");
454	else if (addr == st_map)
455		return ("st_map");
456	else if (addr == pt_map)
457		return ("pt_map");
458	else if (addr == lkm_map)
459		return ("lkm_map");
460	else if (addr == buf_map)
461		return ("buf_map");
462	else
463		return (NULL);
464}
465
466void
467load_name_cache(kvm_t *kd)
468{
469	struct namecache _ncp, *ncp, *oncp;
470	struct nchashhead _ncpp, *ncpp;
471	u_long nchash, i;
472
473	LIST_INIT(&lcache);
474
475	_KDEREF(kd, nchash_addr, &nchash, sizeof(nchash));
476	nchashtbl = malloc(sizeof(nchashtbl) * (int)(nchash + 1));
477	_KDEREF(kd, nchashtbl_addr, nchashtbl,
478		sizeof(nchashtbl) * (int)(nchash + 1));
479
480	ncpp = &_ncpp;
481
482	for (i = 0; i <= nchash; i++) {
483		ncpp = &nchashtbl[i];
484		oncp = NULL;
485		LIST_FOREACH(ncp, ncpp, nc_hash) {
486			if (ncp == oncp ||
487			    ncp == (void*)0xdeadbeef)
488				break;
489			oncp = ncp;
490			_KDEREF(kd, (u_long)ncp, &_ncp, sizeof(*ncp));
491			ncp = &_ncp;
492			if (ncp->nc_nlen > 0) {
493				if (ncp->nc_nlen > 2 ||
494				    ncp->nc_name[0] != '.' ||
495				    (ncp->nc_name[1] != '.' &&
496				     ncp->nc_nlen != 1))
497					cache_enter(i, ncp);
498			}
499		}
500	}
501}
502
503void
504cache_enter(u_long i, struct namecache *ncp)
505{
506	struct cache_entry *ce;
507
508	if (debug & DUMP_NAMEI_CACHE)
509		printf("[%lu] ncp->nc_vp %10p, ncp->nc_dvp %10p, "
510		       "ncp->nc_nlen %3d [%.*s]\n",
511		       i, ncp->nc_vp, ncp->nc_dvp,
512		       ncp->nc_nlen, ncp->nc_nlen, ncp->nc_name);
513
514	ce = malloc(sizeof(struct cache_entry));
515
516	ce->ce_vp = ncp->nc_vp;
517	ce->ce_pvp = ncp->nc_dvp;
518	ce->ce_nlen = ncp->nc_nlen;
519	strncpy(ce->ce_name, ncp->nc_name, sizeof(ce->ce_name));
520	ce->ce_name[MIN(ce->ce_nlen, (int)(sizeof(ce->ce_name) - 1))] = '\0';
521
522	LIST_INSERT_HEAD(&lcache, ce, ce_next);
523}
524