Home | History | Annotate | Line # | Download | only in pmap
      1 /*	$NetBSD: main.c,v 1.30 2022/08/21 07:46:52 mlelstv Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2002, 2003, 2020 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.30 2022/08/21 07:46:52 mlelstv 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 void *uvm_vnodeops, *uvm_deviceops, *aobj_pager, *ubc_pager;
     58 struct vm_map *kmem_map, *phys_map, *exec_map, *pager_map;
     59 struct vm_map *st_map, *pt_map, *module_map, *buf_map;
     60 u_long kernel_map_addr;
     61 int debug, verbose, recurse, page_size;
     62 int print_all, print_map, print_maps, print_solaris, print_ddb;
     63 int tree;
     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 	{ NULL, 0, 0, 0, 0 }
     80 };
     81 
     82 struct nlist kmaps[] = {
     83 	{ "_kmem_map", 0, 0, 0, 0 },
     84 #define NL_kmem_map		0
     85 	{ "_phys_map", 0, 0, 0, 0 },
     86 #define NL_phys_map		1
     87 	{ "_exec_map", 0, 0, 0, 0 },
     88 #define NL_exec_map		2
     89 	{ "_pager_map", 0, 0, 0, 0 },
     90 #define NL_pager_map		3
     91 	{ "_st_map", 0, 0, 0, 0 },
     92 #define NL_st_map		4
     93 	{ "_pt_map", 0, 0, 0, 0 },
     94 #define NL_pt_map		5
     95 	{ "_module_map", 0, 0, 0, 0 },
     96 #define NL_module_map		6
     97 	{ "_buf_map", 0, 0, 0, 0 },
     98 #define NL_buf_map		7
     99 	{ NULL, 0, 0, 0, 0 },
    100 };
    101 
    102 #define VMSPACE_ADDRESS		1
    103 #define VM_MAP_ADDRESS		2
    104 #define VM_MAP_ENTRY_ADDRESS	3
    105 #define AMAP_ADDRESS		4
    106 
    107 void check_fd(int);
    108 void load_symbols(kvm_t *);
    109 void cache_enter(u_long, struct namecache *);
    110 
    111 int
    112 main(int argc, char *argv[])
    113 {
    114 	kvm_t *kd;
    115 	pid_t pid;
    116 	uid_t uid;
    117 	int which, many, ch, rc;
    118 	char errbuf[_POSIX2_LINE_MAX + 1];
    119 	struct kinfo_proc2 *kproc;
    120 	char *kmem, *kernel, *t;
    121 	gid_t egid;
    122 	struct kbit kbit, *vmspace;
    123 	u_long address;
    124 
    125 	uid = getuid();
    126 	egid = getegid();
    127 	if (setegid(getgid()) == -1)
    128 		err(1, "failed to reset privileges");
    129 
    130 	check_fd(STDIN_FILENO);
    131 	check_fd(STDOUT_FILENO);
    132 	check_fd(STDERR_FILENO);
    133 
    134 	pid = -1;
    135 	which = verbose = debug = 0;
    136 	print_all = print_map = print_maps = print_solaris = print_ddb = 0;
    137 	tree = 0;
    138 	recurse = 0;
    139 	kmem = kernel = NULL;
    140 	address = 0;
    141 	vmspace = &kbit;
    142 
    143 	while ((ch = getopt(argc, argv, "A:aD:dE:lM:mN:Pp:RrS:stV:vx")) != -1) {
    144 		switch (ch) {
    145 		case 'A':
    146 		case 'E':
    147 		case 'S':
    148 		case 'V':
    149 			if (which != 0)
    150 				errx(1, "use only one of -A, -E, -S, or -V");
    151 			errno = 0;
    152 			address = strtoul(optarg, &t, 0);
    153 			if (*t != '\0')
    154 				errx(1, "%s is not a valid address", optarg);
    155 			if (errno != 0)
    156 				err(1, "%s is not a valid address", optarg);
    157 			switch (ch) {
    158 			case 'A':	which = AMAP_ADDRESS;		break;
    159 			case 'E':	which = VM_MAP_ENTRY_ADDRESS;	break;
    160 			case 'S':	which = VMSPACE_ADDRESS;	break;
    161 			case 'V':	which = VM_MAP_ADDRESS;		break;
    162 			}
    163 			break;
    164 		case 'a':
    165 			print_all = 1;
    166 			break;
    167 		case 'd':
    168 			print_ddb = 1;
    169 			break;
    170 		case 'D':
    171 			errno = 0;
    172 			debug = strtoul(optarg, &t, 0);
    173 			if (*t != '\0')
    174 				errx(1, "%s is not a valid number", optarg);
    175 			if (errno != 0)
    176 				err(1, "%s is not a valid number", optarg);
    177 			break;
    178 		case 'l':
    179 			print_maps = 1;
    180 			break;
    181 		case 'm':
    182 			print_map = 1;
    183 			break;
    184 		case 'M':
    185 			kmem = optarg;
    186 			break;
    187 		case 'N':
    188 			kernel = optarg;
    189 			break;
    190 		case 'p':
    191 			errno = 0;
    192 			pid = strtol(optarg, &t, 0);
    193 			if (pid < 0)
    194 				errno = EINVAL;
    195 			if (*t != '\0')
    196 				errx(1, "%s is not a valid pid", optarg);
    197 			if (errno != 0)
    198 				err(1, "%s is not a valid pid", optarg);
    199 			break;
    200 		case 'P':
    201 			pid = getpid();
    202 			break;
    203 		case 'R':
    204 			recurse = 1;
    205 			break;
    206 		case 's':
    207 			print_solaris = 1;
    208 			break;
    209 		case 't':
    210 			tree = 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 [-adlmPRstv] [-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 	if ((kernel != NULL || kmem != NULL || address != 0 ||
    241 	     print_ddb || debug) && uid != 0)
    242 		errx(1, "one or more options specified is restricted to root");
    243 
    244 	/* get privs back since it appears to be safe. */
    245 	rc = setegid(egid);
    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 	do {
    294 		if (pid == -1) {
    295 			if (argc == 0)
    296 				pid = getppid();
    297 			else {
    298 				errno = 0;
    299 				pid = strtol(argv[0], &t, 0);
    300 				if (pid < 0)
    301 					errno = EINVAL;
    302 				if (*t != '\0')
    303 					errx(1, "%s is not a valid pid",
    304 					    argv[0]);
    305 				if (errno != 0)
    306 					err(1, "%s is not a valid pid",
    307 					    argv[0]);
    308 				argv++;
    309 				argc--;
    310 			}
    311 		}
    312 
    313 		errno = 0;
    314 		/* find the process id */
    315 		if (pid == 0) {
    316 			kproc = NULL;
    317 			if (uid != 0) {
    318 				/* only root can print kernel mappings */
    319 				errno = EPERM;
    320 			}
    321 		} else {
    322 			kproc = kvm_getproc2(kd, KERN_PROC_PID, pid,
    323 			    sizeof(struct kinfo_proc2), &rc);
    324 			if (kproc == NULL || rc == 0) {
    325 				errno = ESRCH;
    326 			} else if (uid != 0 && uid != kproc->p_uid) {
    327 				/*
    328 				 * only the real owner of the process and
    329 				 * root can print process mappings
    330 				 */
    331 				errno = EPERM;
    332 			}
    333 		}
    334 
    335 		if (errno != 0) {
    336 			warn("%d", pid);
    337 			pid = -1;
    338 			continue;
    339 		}
    340 
    341 		/* dump it */
    342 		if (many) {
    343 			if (kproc != NULL)
    344 				printf("process %d:\n", kproc->p_pid);
    345 			else
    346 				printf("kernel:\n");
    347 		}
    348 
    349 		(*process_map)(kd, kproc, vmspace, NULL);
    350 		pid = -1;
    351 	} while (argc > 0);
    352 
    353 	/* done.  go away. */
    354 	rc = kvm_close(kd);
    355 	if (rc == -1)
    356 		err(1, "kvm_close");
    357 
    358 	return (0);
    359 }
    360 
    361 void
    362 check_fd(int fd)
    363 {
    364 	struct stat st;
    365 	int n;
    366 
    367 	if (fstat(fd, &st) == -1) {
    368 		(void)close(fd);
    369 		n = open("/dev/null", O_RDWR);
    370 		if (n == fd || n == -1)
    371 			/* we're either done or we can do no more */
    372 			return;
    373 		/* if either of these fail, there's not much we can do */
    374 		(void)dup2(n, fd);
    375 		(void)close(n);
    376 		/* XXX should we exit if it fails? */
    377 	}
    378 }
    379 
    380 void
    381 load_symbols(kvm_t *kd)
    382 {
    383 	int rc, i, mib[2];
    384 	size_t sz;
    385 
    386 	rc = kvm_nlist(kd, &ksyms[0]);
    387 	if (rc != 0) {
    388 		for (i = 0; ksyms[i].n_name != NULL; i++)
    389 			if (ksyms[i].n_value == 0)
    390 				warnx("symbol %s: not found", ksyms[i].n_name);
    391 		exit(1);
    392 	}
    393 
    394 	uvm_vnodeops =	(void*)ksyms[NL_UVM_VNODEOPS].n_value;
    395 	uvm_deviceops =	(void*)ksyms[NL_UVM_DEVICEOPS].n_value;
    396 	aobj_pager =	(void*)ksyms[NL_AOBJ_PAGER].n_value;
    397 	ubc_pager =	(void*)ksyms[NL_UBC_PAGER].n_value;
    398 
    399 	_KDEREF(kd, ksyms[NL_MAXSSIZ].n_value, &maxssiz,
    400 		sizeof(maxssiz));
    401 	_KDEREF(kd, ksyms[NL_KERNEL_MAP].n_value, &kernel_map_addr,
    402 		sizeof(kernel_map_addr));
    403 
    404 	/*
    405 	 * Some of these may be missing from some platforms, for
    406 	 * example sparc, sh3, and most powerpc platforms don't
    407 	 * have a "phys_map", etc.
    408 	 */
    409 	(void)kvm_nlist(kd, &kmaps[0]);
    410 
    411 #define get_map_address(m) do {\
    412 	if (kmaps[__CONCAT(NL_,m)].n_value != 0) \
    413 		_KDEREF(kd, kmaps[__CONCAT(NL_,m)].n_value, &m, sizeof(m)); \
    414 	} while (0/*CONSTCOND*/)
    415 
    416 	get_map_address(kmem_map);
    417 	get_map_address(phys_map);
    418 	get_map_address(exec_map);
    419 	get_map_address(pager_map);
    420 	get_map_address(st_map);
    421 	get_map_address(pt_map);
    422 	get_map_address(module_map);
    423 	get_map_address(buf_map);
    424 
    425 	mib[0] = CTL_HW;
    426 	mib[1] = HW_PAGESIZE;
    427 	sz = sizeof(page_size);
    428 	if (sysctl(&mib[0], 2, &page_size, &sz, NULL, 0) == -1)
    429 		err(1, "sysctl: hw.pagesize");
    430 }
    431 
    432 const char *
    433 mapname(void *addr)
    434 {
    435 
    436 	if (addr == (void*)kernel_map_addr)
    437 		return ("kernel_map");
    438 	else if (addr == kmem_map)
    439 		return ("kmem_map");
    440 	else if (addr == phys_map)
    441 		return ("phys_map");
    442 	else if (addr == exec_map)
    443 		return ("exec_map");
    444 	else if (addr == pager_map)
    445 		return ("pager_map");
    446 	else if (addr == st_map)
    447 		return ("st_map");
    448 	else if (addr == pt_map)
    449 		return ("pt_map");
    450 	else if (addr == module_map)
    451 		return ("module_map");
    452 	else if (addr == buf_map)
    453 		return ("buf_map");
    454 	else
    455 		return (NULL);
    456 }
    457