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