1 /* $NetBSD: main.c,v 1.3 2003/01/16 15:34:18 atatat 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 __RCSID("$NetBSD: main.c,v 1.3 2003/01/16 15:34:18 atatat Exp $"); 42 #endif 43 44 #include <sys/param.h> 45 46 #ifndef __NetBSD_Version__ 47 #error go away, you fool 48 #elif (__NetBSD_Version__ < 105000000) 49 #error only works with uvm 50 #endif 51 52 #include <fcntl.h> 53 #include <errno.h> 54 #include <unistd.h> 55 #include <limits.h> 56 #include <string.h> 57 58 #include "pmap.h" 59 #include "main.h" 60 61 /* 62 * strange gyrations to get the prototype for the lockdebug version of 63 * the process_map function 64 */ 65 #undef VERSION 66 #define VERSION lockdebug 67 #include "pmap.h" 68 #undef VERSION 69 #define VERSION regular 70 71 struct cache_head lcache; 72 struct nchashhead *nchashtbl; 73 void *uvm_vnodeops, *uvm_deviceops, *aobj_pager, *ubc_pager; 74 void *kernel_floor; 75 struct vm_map *kmem_map, *mb_map, *phys_map, *exec_map, *pager_map; 76 u_long nchash_addr, nchashtbl_addr, kernel_map_addr; 77 int debug, verbose, recurse; 78 int print_all, print_map, print_maps, print_solaris, print_ddb; 79 int rwx = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE, heapfound; 80 rlim_t maxssiz; 81 82 struct nlist ksyms[] = { 83 { "_maxsmap" }, 84 #define NL_MAXSSIZ 0 85 { "_uvm_vnodeops" }, 86 #define NL_UVM_VNODEOPS 1 87 { "_uvm_deviceops" }, 88 #define NL_UVM_DEVICEOPS 2 89 { "_aobj_pager" }, 90 #define NL_AOBJ_PAGER 3 91 { "_ubc_pager" }, 92 #define NL_UBC_PAGER 4 93 { "_kernel_map" }, 94 #define NL_KERNEL_MAP 5 95 { "_nchashtbl" }, 96 #define NL_NCHASHTBL 6 97 { "_nchash" }, 98 #define NL_NCHASH 7 99 { "_kernel_text" }, 100 #define NL_KENTER 8 101 { NULL } 102 }; 103 104 struct nlist kmaps[] = { 105 { "_kmem_map" }, 106 #define NL_KMEM_MAP 0 107 { "_mb_map" }, 108 #define NL_MB_MAP 1 109 { "_phys_map" }, 110 #define NL_PHYS_MAP 2 111 { "_exec_map" }, 112 #define NL_EXEC_MAP 3 113 { "_pager_map" }, 114 #define NL_PAGER_MAP 4 115 { NULL } 116 }; 117 118 void check_fd(int); 119 int using_lockdebug(kvm_t *); 120 void load_symbols(kvm_t *); 121 void cache_enter(int, struct namecache *); 122 123 int 124 main(int argc, char *argv[]) 125 { 126 kvm_t *kd; 127 pid_t pid; 128 int many, ch, rc; 129 char errbuf[_POSIX2_LINE_MAX + 1]; 130 struct kinfo_proc2 *kproc; 131 char *kmem, *kernel; 132 gid_t egid; 133 134 egid = getegid(); 135 if (setegid(getgid()) == -1) 136 err(1, "failed to reset privileges"); 137 138 check_fd(STDIN_FILENO); 139 check_fd(STDOUT_FILENO); 140 check_fd(STDERR_FILENO); 141 142 pid = -1; 143 verbose = debug = 0; 144 print_all = print_map = print_maps = print_solaris = print_ddb = 0; 145 recurse = 0; 146 kmem = kernel = NULL; 147 148 while ((ch = getopt(argc, argv, "aD:dlmM:N:p:PRrsvx")) != -1) { 149 switch (ch) { 150 case 'a': 151 print_all = 1; 152 break; 153 case 'd': 154 print_ddb = 1; 155 break; 156 case 'D': 157 debug = atoi(optarg); 158 break; 159 case 'l': 160 print_maps = 1; 161 break; 162 case 'm': 163 print_map = 1; 164 break; 165 case 'M': 166 kmem = optarg; 167 break; 168 case 'N': 169 kernel = optarg; 170 break; 171 case 'p': 172 pid = atoi(optarg); 173 break; 174 case 'P': 175 pid = getpid(); 176 break; 177 case 'R': 178 recurse = 1; 179 break; 180 case 's': 181 print_solaris = 1; 182 break; 183 case 'v': 184 verbose++; 185 break; 186 case 'r': 187 case 'x': 188 errx(1, "-%c option not implemented, sorry", optopt); 189 /*NOTREACHED*/ 190 case '?': 191 default: 192 fprintf(stderr, "usage: %s [-adlmPsv] [-D number] " 193 "[-M core] [-N system] [-p pid] [pid ...]\n", 194 getprogname()); 195 exit(1); 196 } 197 } 198 argc -= optind; 199 argv += optind; 200 201 /* more than one "process" to dump? */ 202 many = (argc > 1 - (pid == -1 ? 0 : 1)) ? 1 : 0; 203 204 /* apply default */ 205 if (print_all + print_map + print_maps + print_solaris + 206 print_ddb == 0) 207 print_solaris = 1; 208 209 /* get privs back if it appears to be safe, otherwise toss them */ 210 if (kernel == NULL && kmem == NULL) 211 rc = setegid(egid); 212 else 213 rc = setgid(getgid()); 214 if (rc == -1) 215 err(1, "failed to reset privileges"); 216 217 /* start by opening libkvm */ 218 kd = kvm_openfiles(kernel, kmem, NULL, O_RDONLY, errbuf); 219 errbuf[_POSIX2_LINE_MAX] = '\0'; 220 if (kd == NULL) 221 errx(1, "%s", errbuf); 222 223 /* get "bootstrap" addresses from kernel */ 224 load_symbols(kd); 225 226 if (! using_lockdebug(kd)) 227 process_map = PMAPFUNC(process_map,regular); 228 else 229 process_map = PMAPFUNC(process_map,lockdebug); 230 231 do { 232 if (pid == -1) { 233 if (argc == 0) 234 pid = getppid(); 235 else { 236 pid = atoi(argv[0]); 237 argv++; 238 argc--; 239 } 240 } 241 242 /* find the process id */ 243 if (pid == 0) 244 kproc = NULL; 245 else { 246 kproc = kvm_getproc2(kd, KERN_PROC_PID, pid, 247 sizeof(struct kinfo_proc2), &rc); 248 if (kproc == NULL || rc == 0) { 249 errno = ESRCH; 250 warn("%d", pid); 251 pid = -1; 252 continue; 253 } 254 } 255 256 /* dump it */ 257 if (many) { 258 if (kproc) 259 printf("process %d:\n", kproc->p_pid); 260 else 261 printf("kernel:\n"); 262 } 263 264 (*process_map)(kd, pid, kproc); 265 pid = -1; 266 } while (argc > 0); 267 268 /* done. go away. */ 269 rc = kvm_close(kd); 270 if (rc == -1) 271 err(1, "kvm_close"); 272 273 return (0); 274 } 275 276 void 277 check_fd(int fd) 278 { 279 struct stat st; 280 int n; 281 282 if (fstat(fd, &st) == -1) { 283 (void)close(fd); 284 n = open("/dev/null", O_RDWR); 285 if (n == fd || n == -1) 286 /* we're either done or we can do no more */ 287 return; 288 /* if either of these fail, there's not much we can do */ 289 (void)dup2(n, fd); 290 (void)close(n); 291 /* XXX should we exit if it fails? */ 292 } 293 } 294 295 int 296 using_lockdebug(kvm_t *kd) 297 { 298 struct kbit kbit[3]; 299 struct kbit *vm_map, *header, *vm_map_entry; 300 301 vm_map = &kbit[0]; 302 header = &kbit[1]; 303 vm_map_entry = &kbit[2]; 304 305 A(vm_map) = kernel_map_addr; 306 S(vm_map) = sizeof(struct vm_map); 307 KDEREF(kd, vm_map); 308 309 A(header) = A(vm_map) + offsetof(struct vm_map, header); 310 S(header) = sizeof(struct vm_map_entry); 311 memcpy(D(header, vm_map_entry), &D(vm_map, vm_map)->header, S(header)); 312 313 /* 314 * the kernel *always* has map entries, but we might see a 315 * zero if we're using a lockdebug kernel and haven't noticed 316 * yet. 317 */ 318 if (D(vm_map, vm_map)->nentries == 0) { 319 320 /* no entries -> all pointers must point to the header */ 321 if (P(header) == D(header, vm_map_entry)->next && 322 P(header) == D(header, vm_map_entry)->prev && 323 P(header) == D(vm_map, vm_map)->hint && 324 P(header) == D(vm_map, vm_map)->first_free) 325 return (0); 326 } 327 else { 328 329 P(vm_map_entry) = D(header, vm_map_entry)->next; 330 S(vm_map_entry) = sizeof(struct vm_map_entry); 331 KDEREF(kd, vm_map_entry); 332 333 /* we have entries, so there must be referential integrity */ 334 if (D(vm_map_entry, vm_map_entry)->prev == P(header) && 335 D(header, vm_map_entry)->start <= 336 D(vm_map_entry, vm_map_entry)->start && 337 D(vm_map_entry, vm_map_entry)->end <= 338 D(header, vm_map_entry)->end) 339 return (0); 340 } 341 342 return (1); 343 } 344 345 void 346 load_symbols(kvm_t *kd) 347 { 348 int rc, i; 349 350 rc = kvm_nlist(kd, &ksyms[0]); 351 if (rc != 0) { 352 for (i = 0; ksyms[i].n_name != NULL; i++) 353 if (ksyms[i].n_value == 0) 354 warnx("symbol %s: not found", ksyms[i].n_name); 355 exit(1); 356 } 357 358 uvm_vnodeops = (void*)ksyms[NL_UVM_VNODEOPS].n_value; 359 uvm_deviceops = (void*)ksyms[NL_UVM_DEVICEOPS].n_value; 360 aobj_pager = (void*)ksyms[NL_AOBJ_PAGER].n_value; 361 ubc_pager = (void*)ksyms[NL_UBC_PAGER].n_value; 362 363 kernel_floor = (void*)ksyms[NL_KENTER].n_value; 364 nchash_addr = ksyms[NL_NCHASH].n_value; 365 366 _KDEREF(kd, ksyms[NL_MAXSSIZ].n_value, &maxssiz, 367 sizeof(maxssiz)); 368 _KDEREF(kd, ksyms[NL_NCHASHTBL].n_value, &nchashtbl_addr, 369 sizeof(nchashtbl_addr)); 370 _KDEREF(kd, ksyms[NL_KERNEL_MAP].n_value, &kernel_map_addr, 371 sizeof(kernel_map_addr)); 372 373 /* 374 * Some of these may be missing from some platforms, for 375 * example sparc, sh3, and most powerpc platforms don't 376 * have a "phys_map". 377 */ 378 (void)kvm_nlist(kd, &kmaps[0]); 379 if (kmaps[NL_KMEM_MAP].n_value != 0) 380 _KDEREF(kd, kmaps[NL_KMEM_MAP].n_value, &kmem_map, 381 sizeof(kmem_map)); 382 if (kmaps[NL_MB_MAP].n_value != 0) 383 _KDEREF(kd, kmaps[NL_MB_MAP].n_value, &mb_map, 384 sizeof(mb_map)); 385 if (kmaps[NL_PHYS_MAP].n_value != 0) 386 _KDEREF(kd, kmaps[NL_PHYS_MAP].n_value, &phys_map, 387 sizeof(phys_map)); 388 if (kmaps[NL_EXEC_MAP].n_value != 0) 389 _KDEREF(kd, kmaps[NL_EXEC_MAP].n_value, &exec_map, 390 sizeof(exec_map)); 391 if (kmaps[NL_PAGER_MAP].n_value != 0) 392 _KDEREF(kd, kmaps[NL_PAGER_MAP].n_value, &pager_map, 393 sizeof(pager_map)); 394 } 395 396 void 397 load_name_cache(kvm_t *kd) 398 { 399 struct namecache _ncp, *ncp, *oncp; 400 struct nchashhead _ncpp, *ncpp; 401 u_long nchash; 402 int i; 403 404 LIST_INIT(&lcache); 405 406 _KDEREF(kd, nchash_addr, &nchash, sizeof(nchash)); 407 nchashtbl = malloc(sizeof(nchashtbl) * (int)nchash); 408 _KDEREF(kd, nchashtbl_addr, nchashtbl, 409 sizeof(nchashtbl) * (int)nchash); 410 411 ncpp = &_ncpp; 412 413 for (i = 0; i <= nchash; i++) { 414 ncpp = &nchashtbl[i]; 415 oncp = NULL; 416 LIST_FOREACH(ncp, ncpp, nc_hash) { 417 if (ncp == oncp || 418 (void*)ncp < kernel_floor || 419 ncp == (void*)0xdeadbeef) 420 break; 421 oncp = ncp; 422 _KDEREF(kd, (u_long)ncp, &_ncp, sizeof(*ncp)); 423 ncp = &_ncp; 424 if ((void*)ncp->nc_vp > kernel_floor && 425 ncp->nc_nlen > 0) { 426 if (ncp->nc_nlen > 2 || 427 ncp->nc_name[0] != '.' || 428 (ncp->nc_name[1] != '.' && 429 ncp->nc_nlen != 1)) 430 cache_enter(i, ncp); 431 } 432 } 433 } 434 } 435 436 void 437 cache_enter(int i, struct namecache *ncp) 438 { 439 struct cache_entry *ce; 440 441 if (debug & DUMP_NAMEI_CACHE) 442 printf("[%d] ncp->nc_vp %10p, ncp->nc_dvp %10p, " 443 "ncp->nc_nlen %3d [%.*s] (nc_dvpid=%lu, nc_vpid=%lu)\n", 444 i, ncp->nc_vp, ncp->nc_dvp, 445 ncp->nc_nlen, ncp->nc_nlen, ncp->nc_name, 446 ncp->nc_dvpid, ncp->nc_vpid); 447 448 ce = malloc(sizeof(struct cache_entry)); 449 450 ce->ce_vp = ncp->nc_vp; 451 ce->ce_pvp = ncp->nc_dvp; 452 ce->ce_cid = ncp->nc_vpid; 453 ce->ce_pcid = ncp->nc_dvpid; 454 ce->ce_nlen = ncp->nc_nlen; 455 strncpy(ce->ce_name, ncp->nc_name, sizeof(ce->ce_name)); 456 ce->ce_name[MIN(ce->ce_nlen, sizeof(ce->ce_name) - 1)] = '\0'; 457 458 LIST_INSERT_HEAD(&lcache, ce, ce_next); 459 } 460