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