proc_sym.c revision 279082
1/*- 2 * Copyright (c) 2010 The FreeBSD Foundation 3 * Copyright (c) 2008 John Birrell (jb@freebsd.org) 4 * All rights reserved. 5 * 6 * Portions of this software were developed by Rui Paulo under sponsorship 7 * from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $FreeBSD: stable/10/lib/libproc/proc_sym.c 279082 2015-02-20 20:02:47Z rpaulo $ 31 */ 32 33#include <sys/types.h> 34#include <sys/user.h> 35 36#include <assert.h> 37#include <err.h> 38#include <stdio.h> 39#include <libgen.h> 40#include <string.h> 41#include <stdlib.h> 42#include <fcntl.h> 43#include <string.h> 44#include <unistd.h> 45#include <libutil.h> 46 47#include "_libproc.h" 48 49#ifndef NO_CXA_DEMANGLE 50extern char *__cxa_demangle(const char *, char *, size_t *, int *); 51#endif /* NO_CXA_DEMANGLE */ 52 53static void proc_rdl2prmap(rd_loadobj_t *, prmap_t *); 54 55static void 56demangle(const char *symbol, char *buf, size_t len) 57{ 58#ifndef NO_CXA_DEMANGLE 59 char *dembuf; 60 61 if (symbol[0] == '_' && symbol[1] == 'Z' && symbol[2]) { 62 dembuf = __cxa_demangle(symbol, NULL, NULL, NULL); 63 if (!dembuf) 64 goto fail; 65 strlcpy(buf, dembuf, len); 66 free(dembuf); 67 return; 68 } 69fail: 70#endif /* NO_CXA_DEMANGLE */ 71 strlcpy(buf, symbol, len); 72} 73 74static int 75find_dbg_obj(const char *path) 76{ 77 int fd; 78 char dbg_path[PATH_MAX]; 79 80 snprintf(dbg_path, sizeof(dbg_path), 81 "/usr/lib/debug/%s.debug", path); 82 fd = open(dbg_path, O_RDONLY); 83 if (fd > 0) 84 return (fd); 85 else 86 return (open(path, O_RDONLY)); 87} 88 89static void 90proc_rdl2prmap(rd_loadobj_t *rdl, prmap_t *map) 91{ 92 map->pr_vaddr = rdl->rdl_saddr; 93 map->pr_size = rdl->rdl_eaddr - rdl->rdl_saddr; 94 map->pr_offset = rdl->rdl_offset; 95 map->pr_mflags = 0; 96 if (rdl->rdl_prot & RD_RDL_R) 97 map->pr_mflags |= MA_READ; 98 if (rdl->rdl_prot & RD_RDL_W) 99 map->pr_mflags |= MA_WRITE; 100 if (rdl->rdl_prot & RD_RDL_X) 101 map->pr_mflags |= MA_EXEC; 102 strlcpy(map->pr_mapname, rdl->rdl_path, 103 sizeof(map->pr_mapname)); 104} 105 106char * 107proc_objname(struct proc_handle *p, uintptr_t addr, char *objname, 108 size_t objnamesz) 109{ 110 size_t i; 111 rd_loadobj_t *rdl; 112 113 for (i = 0; i < p->nobjs; i++) { 114 rdl = &p->rdobjs[i]; 115 if (addr >= rdl->rdl_saddr && addr < rdl->rdl_eaddr) { 116 strlcpy(objname, rdl->rdl_path, objnamesz); 117 return (objname); 118 } 119 } 120 return (NULL); 121} 122 123prmap_t * 124proc_obj2map(struct proc_handle *p, const char *objname) 125{ 126 size_t i; 127 prmap_t *map; 128 rd_loadobj_t *rdl; 129 char path[MAXPATHLEN]; 130 131 rdl = NULL; 132 for (i = 0; i < p->nobjs; i++) { 133 basename_r(p->rdobjs[i].rdl_path, path); 134 if (strcmp(path, objname) == 0) { 135 rdl = &p->rdobjs[i]; 136 break; 137 } 138 } 139 if (rdl == NULL) { 140 if (strcmp(objname, "a.out") == 0 && p->rdexec != NULL) 141 rdl = p->rdexec; 142 else 143 return (NULL); 144 } 145 146 if ((map = malloc(sizeof(*map))) == NULL) 147 return (NULL); 148 proc_rdl2prmap(rdl, map); 149 return (map); 150} 151 152int 153proc_iter_objs(struct proc_handle *p, proc_map_f *func, void *cd) 154{ 155 size_t i; 156 rd_loadobj_t *rdl; 157 prmap_t map; 158 char path[MAXPATHLEN]; 159 char last[MAXPATHLEN]; 160 161 if (p->nobjs == 0) 162 return (-1); 163 memset(last, 0, sizeof(last)); 164 for (i = 0; i < p->nobjs; i++) { 165 rdl = &p->rdobjs[i]; 166 proc_rdl2prmap(rdl, &map); 167 basename_r(rdl->rdl_path, path); 168 /* 169 * We shouldn't call the callback twice with the same object. 170 * To do that we are assuming the fact that if there are 171 * repeated object names (i.e. different mappings for the 172 * same object) they occur next to each other. 173 */ 174 if (strcmp(path, last) == 0) 175 continue; 176 (*func)(cd, &map, path); 177 strlcpy(last, path, sizeof(last)); 178 } 179 180 return (0); 181} 182 183prmap_t * 184proc_addr2map(struct proc_handle *p, uintptr_t addr) 185{ 186 size_t i; 187 int cnt, lastvn = 0; 188 prmap_t *map; 189 rd_loadobj_t *rdl; 190 struct kinfo_vmentry *kves, *kve; 191 192 /* 193 * If we don't have a cache of listed objects, we need to query 194 * it ourselves. 195 */ 196 if (p->nobjs == 0) { 197 if ((kves = kinfo_getvmmap(p->pid, &cnt)) == NULL) 198 return (NULL); 199 for (i = 0; i < (size_t)cnt; i++) { 200 kve = kves + i; 201 if (kve->kve_type == KVME_TYPE_VNODE) 202 lastvn = i; 203 if (addr >= kve->kve_start && addr < kve->kve_end) { 204 if ((map = malloc(sizeof(*map))) == NULL) { 205 free(kves); 206 return (NULL); 207 } 208 map->pr_vaddr = kve->kve_start; 209 map->pr_size = kve->kve_end - kve->kve_start; 210 map->pr_offset = kve->kve_offset; 211 map->pr_mflags = 0; 212 if (kve->kve_protection & KVME_PROT_READ) 213 map->pr_mflags |= MA_READ; 214 if (kve->kve_protection & KVME_PROT_WRITE) 215 map->pr_mflags |= MA_WRITE; 216 if (kve->kve_protection & KVME_PROT_EXEC) 217 map->pr_mflags |= MA_EXEC; 218 if (kve->kve_flags & KVME_FLAG_COW) 219 map->pr_mflags |= MA_COW; 220 if (kve->kve_flags & KVME_FLAG_NEEDS_COPY) 221 map->pr_mflags |= MA_NEEDS_COPY; 222 if (kve->kve_flags & KVME_FLAG_NOCOREDUMP) 223 map->pr_mflags |= MA_NOCOREDUMP; 224 strlcpy(map->pr_mapname, kves[lastvn].kve_path, 225 sizeof(map->pr_mapname)); 226 free(kves); 227 return (map); 228 } 229 } 230 free(kves); 231 return (NULL); 232 } 233 234 for (i = 0; i < p->nobjs; i++) { 235 rdl = &p->rdobjs[i]; 236 if (addr >= rdl->rdl_saddr && addr < rdl->rdl_eaddr) { 237 if ((map = malloc(sizeof(*map))) == NULL) 238 return (NULL); 239 proc_rdl2prmap(rdl, map); 240 return (map); 241 } 242 } 243 return (NULL); 244} 245 246int 247proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name, 248 size_t namesz, GElf_Sym *symcopy) 249{ 250 Elf *e; 251 Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL; 252 Elf_Data *data; 253 GElf_Shdr shdr; 254 GElf_Sym sym; 255 GElf_Ehdr ehdr; 256 int fd, error = -1; 257 size_t i; 258 uint64_t rsym; 259 prmap_t *map; 260 char *s; 261 unsigned long symtabstridx = 0, dynsymstridx = 0; 262 263 if ((map = proc_addr2map(p, addr)) == NULL) 264 return (-1); 265 if ((fd = find_dbg_obj(map->pr_mapname)) < 0) { 266 DPRINTF("ERROR: open %s failed", map->pr_mapname); 267 goto err0; 268 } 269 if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 270 DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1)); 271 goto err1; 272 } 273 if (gelf_getehdr(e, &ehdr) == NULL) { 274 DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1)); 275 goto err2; 276 } 277 /* 278 * Find the index of the STRTAB and SYMTAB sections to locate 279 * symbol names. 280 */ 281 scn = NULL; 282 while ((scn = elf_nextscn(e, scn)) != NULL) { 283 gelf_getshdr(scn, &shdr); 284 switch (shdr.sh_type) { 285 case SHT_SYMTAB: 286 symtabscn = scn; 287 symtabstridx = shdr.sh_link; 288 break; 289 case SHT_DYNSYM: 290 dynsymscn = scn; 291 dynsymstridx = shdr.sh_link; 292 break; 293 default: 294 break; 295 } 296 } 297 /* 298 * Iterate over the Dynamic Symbols table to find the symbol. 299 * Then look up the string name in STRTAB (.dynstr) 300 */ 301 if ((data = elf_getdata(dynsymscn, NULL)) == NULL) { 302 DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1)); 303 goto symtab; 304 } 305 i = 0; 306 while (gelf_getsym(data, i++, &sym) != NULL) { 307 /* 308 * Calculate the address mapped to the virtual memory 309 * by rtld. 310 */ 311 if (ehdr.e_type != ET_EXEC) 312 rsym = map->pr_vaddr + sym.st_value; 313 else 314 rsym = sym.st_value; 315 if (addr >= rsym && addr < rsym + sym.st_size) { 316 s = elf_strptr(e, dynsymstridx, sym.st_name); 317 if (s) { 318 demangle(s, name, namesz); 319 memcpy(symcopy, &sym, sizeof(sym)); 320 /* 321 * DTrace expects the st_value to contain 322 * only the address relative to the start of 323 * the function. 324 */ 325 symcopy->st_value = rsym; 326 error = 0; 327 goto out; 328 } 329 } 330 } 331symtab: 332 /* 333 * Iterate over the Symbols Table to find the symbol. 334 * Then look up the string name in STRTAB (.dynstr) 335 */ 336 if ((data = elf_getdata(symtabscn, NULL)) == NULL) { 337 DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1)); 338 goto err2; 339 } 340 i = 0; 341 while (gelf_getsym(data, i++, &sym) != NULL) { 342 /* 343 * Calculate the address mapped to the virtual memory 344 * by rtld. 345 */ 346 if (ehdr.e_type != ET_EXEC) 347 rsym = map->pr_vaddr + sym.st_value; 348 else 349 rsym = sym.st_value; 350 if (addr >= rsym && addr < rsym + sym.st_size) { 351 s = elf_strptr(e, symtabstridx, sym.st_name); 352 if (s) { 353 demangle(s, name, namesz); 354 memcpy(symcopy, &sym, sizeof(sym)); 355 /* 356 * DTrace expects the st_value to contain 357 * only the address relative to the start of 358 * the function. 359 */ 360 symcopy->st_value = rsym; 361 error = 0; 362 goto out; 363 } 364 } 365 } 366out: 367err2: 368 elf_end(e); 369err1: 370 close(fd); 371err0: 372 free(map); 373 return (error); 374} 375 376prmap_t * 377proc_name2map(struct proc_handle *p, const char *name) 378{ 379 size_t i; 380 int cnt; 381 prmap_t *map; 382 char tmppath[MAXPATHLEN]; 383 struct kinfo_vmentry *kves, *kve; 384 rd_loadobj_t *rdl; 385 386 /* 387 * If we haven't iterated over the list of loaded objects, 388 * librtld_db isn't yet initialized and it's very likely 389 * that librtld_db called us. We need to do the heavy 390 * lifting here to find the symbol librtld_db is looking for. 391 */ 392 if (p->nobjs == 0) { 393 if ((kves = kinfo_getvmmap(proc_getpid(p), &cnt)) == NULL) 394 return (NULL); 395 for (i = 0; i < (size_t)cnt; i++) { 396 kve = kves + i; 397 basename_r(kve->kve_path, tmppath); 398 if (strcmp(tmppath, name) == 0) { 399 map = proc_addr2map(p, kve->kve_start); 400 free(kves); 401 return (map); 402 } 403 } 404 free(kves); 405 return (NULL); 406 } 407 if ((name == NULL || strcmp(name, "a.out") == 0) && 408 p->rdexec != NULL) { 409 map = proc_addr2map(p, p->rdexec->rdl_saddr); 410 return (map); 411 } 412 for (i = 0; i < p->nobjs; i++) { 413 rdl = &p->rdobjs[i]; 414 basename_r(rdl->rdl_path, tmppath); 415 if (strcmp(tmppath, name) == 0) { 416 if ((map = malloc(sizeof(*map))) == NULL) 417 return (NULL); 418 proc_rdl2prmap(rdl, map); 419 return (map); 420 } 421 } 422 423 return (NULL); 424} 425 426int 427proc_name2sym(struct proc_handle *p, const char *object, const char *symbol, 428 GElf_Sym *symcopy) 429{ 430 Elf *e; 431 Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL; 432 Elf_Data *data; 433 GElf_Shdr shdr; 434 GElf_Sym sym; 435 GElf_Ehdr ehdr; 436 int fd, error = -1; 437 size_t i; 438 prmap_t *map; 439 char *s; 440 unsigned long symtabstridx = 0, dynsymstridx = 0; 441 442 if ((map = proc_name2map(p, object)) == NULL) { 443 DPRINTFX("ERROR: couldn't find object %s", object); 444 goto err0; 445 } 446 if ((fd = find_dbg_obj(map->pr_mapname)) < 0) { 447 DPRINTF("ERROR: open %s failed", map->pr_mapname); 448 goto err0; 449 } 450 if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 451 DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1)); 452 goto err1; 453 } 454 if (gelf_getehdr(e, &ehdr) == NULL) { 455 DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1)); 456 goto err2; 457 } 458 /* 459 * Find the index of the STRTAB and SYMTAB sections to locate 460 * symbol names. 461 */ 462 scn = NULL; 463 while ((scn = elf_nextscn(e, scn)) != NULL) { 464 gelf_getshdr(scn, &shdr); 465 switch (shdr.sh_type) { 466 case SHT_SYMTAB: 467 symtabscn = scn; 468 symtabstridx = shdr.sh_link; 469 break; 470 case SHT_DYNSYM: 471 dynsymscn = scn; 472 dynsymstridx = shdr.sh_link; 473 break; 474 default: 475 break; 476 } 477 } 478 /* 479 * Iterate over the Dynamic Symbols table to find the symbol. 480 * Then look up the string name in STRTAB (.dynstr) 481 */ 482 if ((data = elf_getdata(dynsymscn, NULL))) { 483 i = 0; 484 while (gelf_getsym(data, i++, &sym) != NULL) { 485 s = elf_strptr(e, dynsymstridx, sym.st_name); 486 if (s && strcmp(s, symbol) == 0) { 487 memcpy(symcopy, &sym, sizeof(sym)); 488 if (ehdr.e_type != ET_EXEC) 489 symcopy->st_value += map->pr_vaddr; 490 error = 0; 491 goto out; 492 } 493 } 494 } 495 /* 496 * Iterate over the Symbols Table to find the symbol. 497 * Then look up the string name in STRTAB (.dynstr) 498 */ 499 if ((data = elf_getdata(symtabscn, NULL))) { 500 i = 0; 501 while (gelf_getsym(data, i++, &sym) != NULL) { 502 s = elf_strptr(e, symtabstridx, sym.st_name); 503 if (s && strcmp(s, symbol) == 0) { 504 memcpy(symcopy, &sym, sizeof(sym)); 505 if (ehdr.e_type != ET_EXEC) 506 symcopy->st_value += map->pr_vaddr; 507 error = 0; 508 goto out; 509 } 510 } 511 } 512out: 513 DPRINTFX("found addr 0x%lx for %s", symcopy->st_value, symbol); 514err2: 515 elf_end(e); 516err1: 517 close(fd); 518err0: 519 free(map); 520 521 return (error); 522} 523 524 525int 526proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which, 527 int mask, proc_sym_f *func, void *cd) 528{ 529 Elf *e; 530 int i, fd; 531 prmap_t *map; 532 Elf_Scn *scn, *foundscn = NULL; 533 Elf_Data *data; 534 GElf_Ehdr ehdr; 535 GElf_Shdr shdr; 536 GElf_Sym sym; 537 unsigned long stridx = -1; 538 char *s; 539 int error = -1; 540 541 if ((map = proc_name2map(p, object)) == NULL) 542 return (-1); 543 if ((fd = find_dbg_obj(map->pr_mapname)) < 0) { 544 DPRINTF("ERROR: open %s failed", map->pr_mapname); 545 goto err0; 546 } 547 if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 548 DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1)); 549 goto err1; 550 } 551 if (gelf_getehdr(e, &ehdr) == NULL) { 552 DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1)); 553 goto err2; 554 } 555 /* 556 * Find the section we are looking for. 557 */ 558 scn = NULL; 559 while ((scn = elf_nextscn(e, scn)) != NULL) { 560 gelf_getshdr(scn, &shdr); 561 if (which == PR_SYMTAB && 562 shdr.sh_type == SHT_SYMTAB) { 563 foundscn = scn; 564 break; 565 } else if (which == PR_DYNSYM && 566 shdr.sh_type == SHT_DYNSYM) { 567 foundscn = scn; 568 break; 569 } 570 } 571 if (!foundscn) 572 return (-1); 573 stridx = shdr.sh_link; 574 if ((data = elf_getdata(foundscn, NULL)) == NULL) { 575 DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1)); 576 goto err2; 577 } 578 i = 0; 579 while (gelf_getsym(data, i++, &sym) != NULL) { 580 if (GELF_ST_BIND(sym.st_info) == STB_LOCAL && 581 (mask & BIND_LOCAL) == 0) 582 continue; 583 if (GELF_ST_BIND(sym.st_info) == STB_GLOBAL && 584 (mask & BIND_GLOBAL) == 0) 585 continue; 586 if (GELF_ST_BIND(sym.st_info) == STB_WEAK && 587 (mask & BIND_WEAK) == 0) 588 continue; 589 if (GELF_ST_TYPE(sym.st_info) == STT_NOTYPE && 590 (mask & TYPE_NOTYPE) == 0) 591 continue; 592 if (GELF_ST_TYPE(sym.st_info) == STT_OBJECT && 593 (mask & TYPE_OBJECT) == 0) 594 continue; 595 if (GELF_ST_TYPE(sym.st_info) == STT_FUNC && 596 (mask & TYPE_FUNC) == 0) 597 continue; 598 if (GELF_ST_TYPE(sym.st_info) == STT_SECTION && 599 (mask & TYPE_SECTION) == 0) 600 continue; 601 if (GELF_ST_TYPE(sym.st_info) == STT_FILE && 602 (mask & TYPE_FILE) == 0) 603 continue; 604 s = elf_strptr(e, stridx, sym.st_name); 605 if (ehdr.e_type != ET_EXEC) 606 sym.st_value += map->pr_vaddr; 607 (*func)(cd, &sym, s); 608 } 609 error = 0; 610err2: 611 elf_end(e); 612err1: 613 close(fd); 614err0: 615 free(map); 616 return (error); 617} 618