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