1/* libunwind - a platform-independent unwind library 2 Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P. 3 Contributed by David Mosberger-Tang <davidm@hpl.hp.com> 4 5This file is part of libunwind. 6 7Permission is hereby granted, free of charge, to any person obtaining 8a copy of this software and associated documentation files (the 9"Software"), to deal in the Software without restriction, including 10without limitation the rights to use, copy, modify, merge, publish, 11distribute, sublicense, and/or sell copies of the Software, and to 12permit persons to whom the Software is furnished to do so, subject to 13the following conditions: 14 15The above copyright notice and this permission notice shall be 16included in all copies or substantial portions of the Software. 17 18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 25 26/* Locate an FDE via the ELF data-structures defined by LSB v1.3 27 (http://www.linuxbase.org/spec/). */ 28 29#include <stddef.h> 30#include <stdio.h> 31#include <limits.h> 32 33#include "dwarf_i.h" 34#include "dwarf-eh.h" 35#include "libunwind_i.h" 36 37struct table_entry 38 { 39 int32_t start_ip_offset; 40 int32_t fde_offset; 41 }; 42 43#ifdef __linux 44#include "os-linux.h" 45#endif 46 47static int 48linear_search (unw_addr_space_t as, unw_word_t ip, 49 unw_word_t eh_frame_start, unw_word_t eh_frame_end, 50 unw_word_t fde_count, 51 unw_proc_info_t *pi, int need_unwind_info, void *arg) 52{ 53 unw_accessors_t *a = unw_get_accessors (unw_local_addr_space); 54 unw_word_t i = 0, fde_addr, addr = eh_frame_start; 55 int ret; 56 57 while (i++ < fde_count && addr < eh_frame_end) 58 { 59 fde_addr = addr; 60 if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi, 61 eh_frame_start, 62 0, 0, arg)) < 0) 63 return ret; 64 65 if (ip >= pi->start_ip && ip < pi->end_ip) 66 { 67 if (!need_unwind_info) 68 return 1; 69 addr = fde_addr; 70 if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi, 71 eh_frame_start, 72 need_unwind_info, 0, 73 arg)) 74 < 0) 75 return ret; 76 return 1; 77 } 78 } 79 return -UNW_ENOINFO; 80} 81 82#ifdef CONFIG_DEBUG_FRAME 83/* Load .debug_frame section from FILE. Allocates and returns space 84 in *BUF, and sets *BUFSIZE to its size. IS_LOCAL is 1 if using the 85 local process, in which case we can search the system debug file 86 directory; 0 for other address spaces, in which case we do not; or 87 -1 for recursive calls following .gnu_debuglink. Returns 0 on 88 success, 1 on error. Succeeds even if the file contains no 89 .debug_frame. */ 90/* XXX: Could use mmap; but elf_map_image keeps tons mapped in. */ 91 92static int 93load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) 94{ 95 FILE *f; 96 Elf_W (Ehdr) ehdr; 97 Elf_W (Half) shstrndx; 98 Elf_W (Shdr) *sec_hdrs = NULL; 99 char *stringtab = NULL; 100 unsigned int i; 101 size_t linksize = 0; 102 char *linkbuf = NULL; 103 104 *buf = NULL; 105 *bufsize = 0; 106 107 f = fopen (file, "r"); 108 109 if (!f) 110 return 1; 111 112 if (fread (&ehdr, sizeof (Elf_W (Ehdr)), 1, f) != 1) 113 goto file_error; 114 115 shstrndx = ehdr.e_shstrndx; 116 117 Debug (4, "opened file '%s'. Section header at offset %d\n", 118 file, (int) ehdr.e_shoff); 119 120 fseek (f, ehdr.e_shoff, SEEK_SET); 121 sec_hdrs = calloc (ehdr.e_shnum, sizeof (Elf_W (Shdr))); 122 if (fread (sec_hdrs, sizeof (Elf_W (Shdr)), ehdr.e_shnum, f) != ehdr.e_shnum) 123 goto file_error; 124 125 Debug (4, "loading string table of size %zd\n", 126 sec_hdrs[shstrndx].sh_size); 127 stringtab = malloc (sec_hdrs[shstrndx].sh_size); 128 fseek (f, sec_hdrs[shstrndx].sh_offset, SEEK_SET); 129 if (fread (stringtab, 1, sec_hdrs[shstrndx].sh_size, f) != sec_hdrs[shstrndx].sh_size) 130 goto file_error; 131 132 for (i = 1; i < ehdr.e_shnum && *buf == NULL; i++) 133 { 134 char *secname = &stringtab[sec_hdrs[i].sh_name]; 135 136 if (strcmp (secname, ".debug_frame") == 0) 137 { 138 *bufsize = sec_hdrs[i].sh_size; 139 *buf = malloc (*bufsize); 140 141 fseek (f, sec_hdrs[i].sh_offset, SEEK_SET); 142 if (fread (*buf, 1, *bufsize, f) != *bufsize) 143 goto file_error; 144 145 Debug (4, "read %zd bytes of .debug_frame from offset %zd\n", 146 *bufsize, sec_hdrs[i].sh_offset); 147 } 148 else if (strcmp (secname, ".gnu_debuglink") == 0) 149 { 150 linksize = sec_hdrs[i].sh_size; 151 linkbuf = malloc (linksize); 152 153 fseek (f, sec_hdrs[i].sh_offset, SEEK_SET); 154 if (fread (linkbuf, 1, linksize, f) != linksize) 155 goto file_error; 156 157 Debug (4, "read %zd bytes of .gnu_debuglink from offset %zd\n", 158 linksize, sec_hdrs[i].sh_offset); 159 } 160 } 161 162 free (stringtab); 163 free (sec_hdrs); 164 165 fclose (f); 166 167 /* Ignore separate debug files which contain a .gnu_debuglink section. */ 168 if (linkbuf && is_local == -1) 169 { 170 free (linkbuf); 171 return 1; 172 } 173 174 if (*buf == NULL && linkbuf != NULL && memchr (linkbuf, 0, linksize) != NULL) 175 { 176 char *newname, *basedir, *p; 177 static const char *debugdir = "/usr/lib/debug"; 178 int ret; 179 180 /* XXX: Don't bother with the checksum; just search for the file. */ 181 basedir = malloc (strlen (file) + 1); 182 newname = malloc (strlen (linkbuf) + strlen (debugdir) 183 + strlen (file) + 9); 184 185 p = strrchr (file, '/'); 186 if (p != NULL) 187 { 188 memcpy (basedir, file, p - file); 189 basedir[p - file] = '\0'; 190 } 191 else 192 basedir[0] = 0; 193 194 strcpy (newname, basedir); 195 strcat (newname, "/"); 196 strcat (newname, linkbuf); 197 ret = load_debug_frame (newname, buf, bufsize, -1); 198 199 if (ret == 1) 200 { 201 strcpy (newname, basedir); 202 strcat (newname, "/.debug/"); 203 strcat (newname, linkbuf); 204 ret = load_debug_frame (newname, buf, bufsize, -1); 205 } 206 207 if (ret == 1 && is_local == 1) 208 { 209 strcpy (newname, debugdir); 210 strcat (newname, basedir); 211 strcat (newname, "/"); 212 strcat (newname, linkbuf); 213 ret = load_debug_frame (newname, buf, bufsize, -1); 214 } 215 216 free (basedir); 217 free (newname); 218 } 219 free (linkbuf); 220 221 return 0; 222 223/* An error reading image file. Release resources and return error code */ 224file_error: 225 free(stringtab); 226 free(sec_hdrs); 227 free(linkbuf); 228 fclose(f); 229 230 return 1; 231} 232 233/* Locate the binary which originated the contents of address ADDR. Return 234 the name of the binary in *name (space is allocated by the caller) 235 Returns 0 if a binary is successfully found, or 1 if an error occurs. */ 236 237static int 238find_binary_for_address (unw_word_t ip, char *name, size_t name_size) 239{ 240#if defined(__linux) 241 struct map_iterator mi; 242 int found = 0; 243 int pid = getpid (); 244 unsigned long segbase, mapoff, hi; 245 246 maps_init (&mi, pid); 247 while (maps_next (&mi, &segbase, &hi, &mapoff)) 248 if (ip >= segbase && ip < hi) 249 { 250 size_t len = strlen (mi.path); 251 252 if (len + 1 <= name_size) 253 { 254 memcpy (name, mi.path, len + 1); 255 found = 1; 256 } 257 break; 258 } 259 maps_close (&mi); 260 return !found; 261#endif 262 263 return 1; 264} 265 266/* Locate and/or try to load a debug_frame section for address ADDR. Return 267 pointer to debug frame descriptor, or zero if not found. */ 268 269static struct unw_debug_frame_list * 270locate_debug_info (unw_addr_space_t as, unw_word_t addr, const char *dlname, 271 unw_word_t start, unw_word_t end) 272{ 273 struct unw_debug_frame_list *w, *fdesc = 0; 274 char path[PATH_MAX]; 275 char *name = path; 276 int err; 277 char *buf; 278 size_t bufsize; 279 280 /* First, see if we loaded this frame already. */ 281 282 for (w = as->debug_frames; w; w = w->next) 283 { 284 Debug (4, "checking %p: %lx-%lx\n", w, (long)w->start, (long)w->end); 285 if (addr >= w->start && addr < w->end) 286 return w; 287 } 288 289 /* If the object name we receive is blank, there's still a chance of locating 290 the file by parsing /proc/self/maps. */ 291 292 if (strcmp (dlname, "") == 0) 293 { 294 err = find_binary_for_address (addr, name, sizeof(path)); 295 if (err) 296 { 297 Debug (15, "tried to locate binary for 0x%" PRIx64 ", but no luck\n", 298 (uint64_t) addr); 299 return 0; 300 } 301 } 302 else 303 name = (char*) dlname; 304 305 err = load_debug_frame (name, &buf, &bufsize, as == unw_local_addr_space); 306 307 if (!err) 308 { 309 fdesc = malloc (sizeof (struct unw_debug_frame_list)); 310 311 fdesc->start = start; 312 fdesc->end = end; 313 fdesc->debug_frame = buf; 314 fdesc->debug_frame_size = bufsize; 315 fdesc->index = NULL; 316 fdesc->next = as->debug_frames; 317 318 as->debug_frames = fdesc; 319 } 320 321 return fdesc; 322} 323 324struct debug_frame_tab 325 { 326 struct table_entry *tab; 327 uint32_t length; 328 uint32_t size; 329 }; 330 331static void 332debug_frame_tab_append (struct debug_frame_tab *tab, 333 unw_word_t fde_offset, unw_word_t start_ip) 334{ 335 unsigned int length = tab->length; 336 337 if (length == tab->size) 338 { 339 tab->size *= 2; 340 tab->tab = realloc (tab->tab, sizeof (struct table_entry) * tab->size); 341 } 342 343 tab->tab[length].fde_offset = fde_offset; 344 tab->tab[length].start_ip_offset = start_ip; 345 346 tab->length = length + 1; 347} 348 349static void 350debug_frame_tab_shrink (struct debug_frame_tab *tab) 351{ 352 if (tab->size > tab->length) 353 { 354 tab->tab = realloc (tab->tab, sizeof (struct table_entry) * tab->length); 355 tab->size = tab->length; 356 } 357} 358 359static int 360debug_frame_tab_compare (const void *a, const void *b) 361{ 362 const struct table_entry *fa = a, *fb = b; 363 364 if (fa->start_ip_offset > fb->start_ip_offset) 365 return 1; 366 else if (fa->start_ip_offset < fb->start_ip_offset) 367 return -1; 368 else 369 return 0; 370} 371 372HIDDEN int 373dwarf_find_debug_frame (int found, unw_dyn_info_t *di_debug, unw_word_t ip, 374 unw_word_t segbase, const char* obj_name, 375 unw_word_t start, unw_word_t end) 376{ 377 unw_dyn_info_t *di; 378 struct unw_debug_frame_list *fdesc = 0; 379 unw_accessors_t *a; 380 unw_word_t addr; 381 382 Debug (15, "Trying to find .debug_frame for %s\n", obj_name); 383 di = di_debug; 384 385 fdesc = locate_debug_info (unw_local_addr_space, ip, obj_name, start, end); 386 387 if (!fdesc) 388 { 389 Debug (15, "couldn't load .debug_frame\n"); 390 return found; 391 } 392 else 393 { 394 char *buf; 395 size_t bufsize; 396 unw_word_t item_start, item_end = 0; 397 uint32_t u32val = 0; 398 uint64_t cie_id = 0; 399 struct debug_frame_tab tab; 400 401 Debug (15, "loaded .debug_frame\n"); 402 403 buf = fdesc->debug_frame; 404 bufsize = fdesc->debug_frame_size; 405 406 if (bufsize == 0) 407 { 408 Debug (15, "zero-length .debug_frame\n"); 409 return found; 410 } 411 412 /* Now create a binary-search table, if it does not already exist. */ 413 if (!fdesc->index) 414 { 415 addr = (unw_word_t) (uintptr_t) buf; 416 417 a = unw_get_accessors (unw_local_addr_space); 418 419 /* Find all FDE entries in debug_frame, and make into a sorted 420 index. */ 421 422 tab.length = 0; 423 tab.size = 16; 424 tab.tab = calloc (tab.size, sizeof (struct table_entry)); 425 426 while (addr < (unw_word_t) (uintptr_t) (buf + bufsize)) 427 { 428 uint64_t id_for_cie; 429 item_start = addr; 430 431 dwarf_readu32 (unw_local_addr_space, a, &addr, &u32val, NULL); 432 433 if (u32val == 0) 434 break; 435 else if (u32val != 0xffffffff) 436 { 437 uint32_t cie_id32 = 0; 438 item_end = addr + u32val; 439 dwarf_readu32 (unw_local_addr_space, a, &addr, &cie_id32, 440 NULL); 441 cie_id = cie_id32; 442 id_for_cie = 0xffffffff; 443 } 444 else 445 { 446 uint64_t u64val = 0; 447 /* Extended length. */ 448 dwarf_readu64 (unw_local_addr_space, a, &addr, &u64val, NULL); 449 item_end = addr + u64val; 450 451 dwarf_readu64 (unw_local_addr_space, a, &addr, &cie_id, NULL); 452 id_for_cie = 0xffffffffffffffffull; 453 } 454 455 /*Debug (1, "CIE/FDE id = %.8x\n", (int) cie_id);*/ 456 457 if (cie_id == id_for_cie) 458 ; 459 /*Debug (1, "Found CIE at %.8x.\n", item_start);*/ 460 else 461 { 462 unw_word_t fde_addr = item_start; 463 unw_proc_info_t this_pi; 464 int err; 465 466 /*Debug (1, "Found FDE at %.8x\n", item_start);*/ 467 468 err = dwarf_extract_proc_info_from_fde (unw_local_addr_space, 469 a, &fde_addr, 470 &this_pi, 471 (uintptr_t) buf, 0, 1, 472 NULL); 473 if (err == 0) 474 { 475 Debug (15, "start_ip = %lx, end_ip = %lx\n", 476 (long) this_pi.start_ip, (long) this_pi.end_ip); 477 debug_frame_tab_append (&tab, 478 item_start - (unw_word_t) (uintptr_t) buf, 479 this_pi.start_ip); 480 } 481 /*else 482 Debug (1, "FDE parse failed\n");*/ 483 } 484 485 addr = item_end; 486 } 487 488 debug_frame_tab_shrink (&tab); 489 qsort (tab.tab, tab.length, sizeof (struct table_entry), 490 debug_frame_tab_compare); 491 /* for (i = 0; i < tab.length; i++) 492 { 493 fprintf (stderr, "ip %x, fde offset %x\n", 494 (int) tab.tab[i].start_ip_offset, 495 (int) tab.tab[i].fde_offset); 496 }*/ 497 fdesc->index = tab.tab; 498 fdesc->index_size = tab.length; 499 } 500 501 di->format = UNW_INFO_FORMAT_TABLE; 502 di->start_ip = fdesc->start; 503 di->end_ip = fdesc->end; 504 di->u.ti.name_ptr = (unw_word_t) (uintptr_t) obj_name; 505 di->u.ti.table_data = (unw_word_t *) fdesc; 506 di->u.ti.table_len = sizeof (*fdesc) / sizeof (unw_word_t); 507 di->u.ti.segbase = segbase; 508 509 found = 1; 510 Debug (15, "found debug_frame table `%s': segbase=0x%lx, len=%lu, " 511 "gp=0x%lx, table_data=0x%lx\n", 512 (char *) (uintptr_t) di->u.ti.name_ptr, 513 (long) di->u.ti.segbase, (long) di->u.ti.table_len, 514 (long) di->gp, (long) di->u.ti.table_data); 515 } 516 return found; 517} 518 519#endif /* CONFIG_DEBUG_FRAME */ 520 521/* ptr is a pointer to a dwarf_callback_data structure and, on entry, 522 member ip contains the instruction-pointer we're looking 523 for. */ 524HIDDEN int 525dwarf_callback (struct dl_phdr_info *info, size_t size, void *ptr) 526{ 527 struct dwarf_callback_data *cb_data = ptr; 528 unw_dyn_info_t *di = &cb_data->di; 529 const Elf_W(Phdr) *phdr, *p_eh_hdr, *p_dynamic, *p_text; 530 unw_word_t addr, eh_frame_start, eh_frame_end, fde_count, ip; 531 Elf_W(Addr) load_base, max_load_addr = 0; 532 int ret, need_unwind_info = cb_data->need_unwind_info; 533 unw_proc_info_t *pi = cb_data->pi; 534 struct dwarf_eh_frame_hdr *hdr; 535 unw_accessors_t *a; 536 long n; 537 int found = 0; 538#ifdef CONFIG_DEBUG_FRAME 539 unw_word_t start, end; 540#endif /* CONFIG_DEBUG_FRAME*/ 541 542 ip = cb_data->ip; 543 544 /* Make sure struct dl_phdr_info is at least as big as we need. */ 545 if (size < offsetof (struct dl_phdr_info, dlpi_phnum) 546 + sizeof (info->dlpi_phnum)) 547 return -1; 548 549 Debug (15, "checking %s, base=0x%lx)\n", 550 info->dlpi_name, (long) info->dlpi_addr); 551 552 phdr = info->dlpi_phdr; 553 load_base = info->dlpi_addr; 554 p_text = NULL; 555 p_eh_hdr = NULL; 556 p_dynamic = NULL; 557 558 /* See if PC falls into one of the loaded segments. Find the 559 eh-header segment at the same time. */ 560 for (n = info->dlpi_phnum; --n >= 0; phdr++) 561 { 562 if (phdr->p_type == PT_LOAD) 563 { 564 Elf_W(Addr) vaddr = phdr->p_vaddr + load_base; 565 566 if (ip >= vaddr && ip < vaddr + phdr->p_memsz) 567 p_text = phdr; 568 569 if (vaddr + phdr->p_filesz > max_load_addr) 570 max_load_addr = vaddr + phdr->p_filesz; 571 } 572 else if (phdr->p_type == PT_GNU_EH_FRAME) 573 p_eh_hdr = phdr; 574 else if (phdr->p_type == PT_DYNAMIC) 575 p_dynamic = phdr; 576 } 577 578 if (!p_text) 579 return 0; 580 581 if (p_eh_hdr) 582 { 583 if (p_dynamic) 584 { 585 /* For dynamicly linked executables and shared libraries, 586 DT_PLTGOT is the value that data-relative addresses are 587 relative to for that object. We call this the "gp". */ 588 Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(p_dynamic->p_vaddr + load_base); 589 for (; dyn->d_tag != DT_NULL; ++dyn) 590 if (dyn->d_tag == DT_PLTGOT) 591 { 592 /* Assume that _DYNAMIC is writable and GLIBC has 593 relocated it (true for x86 at least). */ 594 di->gp = dyn->d_un.d_ptr; 595 break; 596 } 597 } 598 else 599 /* Otherwise this is a static executable with no _DYNAMIC. Assume 600 that data-relative addresses are relative to 0, i.e., 601 absolute. */ 602 di->gp = 0; 603 pi->gp = di->gp; 604 605 hdr = (struct dwarf_eh_frame_hdr *) (p_eh_hdr->p_vaddr + load_base); 606 if (hdr->version != DW_EH_VERSION) 607 { 608 Debug (1, "table `%s' has unexpected version %d\n", 609 info->dlpi_name, hdr->version); 610 return 0; 611 } 612 613 a = unw_get_accessors (unw_local_addr_space); 614 addr = (unw_word_t) (uintptr_t) (hdr + 1); 615 616 /* (Optionally) read eh_frame_ptr: */ 617 if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a, 618 &addr, hdr->eh_frame_ptr_enc, pi, 619 &eh_frame_start, NULL)) < 0) 620 return ret; 621 622 /* (Optionally) read fde_count: */ 623 if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a, 624 &addr, hdr->fde_count_enc, pi, 625 &fde_count, NULL)) < 0) 626 return ret; 627 628 if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) 629 { 630 /* If there is no search table or it has an unsupported 631 encoding, fall back on linear search. */ 632 if (hdr->table_enc == DW_EH_PE_omit) 633 Debug (4, "table `%s' lacks search table; doing linear search\n", 634 info->dlpi_name); 635 else 636 Debug (4, "table `%s' has encoding 0x%x; doing linear search\n", 637 info->dlpi_name, hdr->table_enc); 638 639 eh_frame_end = max_load_addr; /* XXX can we do better? */ 640 641 if (hdr->fde_count_enc == DW_EH_PE_omit) 642 fde_count = ~0UL; 643 if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit) 644 assert (0); 645 646 /* XXX we know how to build a local binary search table for 647 .debug_frame, so we could do that here too. */ 648 cb_data->single_fde = 1; 649 found = linear_search (unw_local_addr_space, ip, 650 eh_frame_start, eh_frame_end, fde_count, 651 pi, need_unwind_info, NULL); 652 if (found != 1) 653 found = 0; 654 } 655 else 656 { 657 di->format = UNW_INFO_FORMAT_REMOTE_TABLE; 658 di->start_ip = p_text->p_vaddr + load_base; 659 di->end_ip = p_text->p_vaddr + load_base + p_text->p_memsz; 660 di->u.rti.name_ptr = (unw_word_t) (uintptr_t) info->dlpi_name; 661 di->u.rti.table_data = addr; 662 assert (sizeof (struct table_entry) % sizeof (unw_word_t) == 0); 663 di->u.rti.table_len = (fde_count * sizeof (struct table_entry) 664 / sizeof (unw_word_t)); 665 /* For the binary-search table in the eh_frame_hdr, data-relative 666 means relative to the start of that section... */ 667 di->u.rti.segbase = (unw_word_t) (uintptr_t) hdr; 668 669 found = 1; 670 Debug (15, "found table `%s': segbase=0x%lx, len=%lu, gp=0x%lx, " 671 "table_data=0x%lx\n", (char *) (uintptr_t) di->u.rti.name_ptr, 672 (long) di->u.rti.segbase, (long) di->u.rti.table_len, 673 (long) di->gp, (long) di->u.rti.table_data); 674 } 675 } 676 677#ifdef CONFIG_DEBUG_FRAME 678 /* Find the start/end of the described region by parsing the phdr_info 679 structure. */ 680 start = (unw_word_t) -1; 681 end = 0; 682 683 for (n = 0; n < info->dlpi_phnum; n++) 684 { 685 if (info->dlpi_phdr[n].p_type == PT_LOAD) 686 { 687 unw_word_t seg_start = info->dlpi_addr + info->dlpi_phdr[n].p_vaddr; 688 unw_word_t seg_end = seg_start + info->dlpi_phdr[n].p_memsz; 689 690 if (seg_start < start) 691 start = seg_start; 692 693 if (seg_end > end) 694 end = seg_end; 695 } 696 } 697 698 found = dwarf_find_debug_frame (found, &cb_data->di_debug, ip, 699 info->dlpi_addr, info->dlpi_name, start, 700 end); 701#endif /* CONFIG_DEBUG_FRAME */ 702 703 return found; 704} 705 706HIDDEN int 707dwarf_find_proc_info (unw_addr_space_t as, unw_word_t ip, 708 unw_proc_info_t *pi, int need_unwind_info, void *arg) 709{ 710 struct dwarf_callback_data cb_data; 711 intrmask_t saved_mask; 712 int ret; 713 714 Debug (14, "looking for IP=0x%lx\n", (long) ip); 715 716 memset (&cb_data, 0, sizeof (cb_data)); 717 cb_data.ip = ip; 718 cb_data.pi = pi; 719 cb_data.need_unwind_info = need_unwind_info; 720 cb_data.di.format = -1; 721 cb_data.di_debug.format = -1; 722 723 SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask); 724 ret = dl_iterate_phdr (dwarf_callback, &cb_data); 725 SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL); 726 727 if (ret <= 0) 728 { 729 Debug (14, "IP=0x%lx not found\n", (long) ip); 730 return -UNW_ENOINFO; 731 } 732 733 if (cb_data.single_fde) 734 /* already got the result in *pi */ 735 return 0; 736 737 /* search the table: */ 738 if (cb_data.di.format != -1) 739 ret = dwarf_search_unwind_table (as, ip, &cb_data.di, 740 pi, need_unwind_info, arg); 741 else 742 ret = -UNW_ENOINFO; 743 744 if (ret == -UNW_ENOINFO && cb_data.di_debug.format != -1) 745 ret = dwarf_search_unwind_table (as, ip, &cb_data.di_debug, pi, 746 need_unwind_info, arg); 747 return ret; 748} 749 750static inline const struct table_entry * 751lookup (const struct table_entry *table, size_t table_size, int32_t rel_ip) 752{ 753 unsigned long table_len = table_size / sizeof (struct table_entry); 754 const struct table_entry *e = NULL; 755 unsigned long lo, hi, mid; 756 757 /* do a binary search for right entry: */ 758 for (lo = 0, hi = table_len; lo < hi;) 759 { 760 mid = (lo + hi) / 2; 761 e = table + mid; 762 Debug (15, "e->start_ip_offset = %lx\n", (long) e->start_ip_offset); 763 if (rel_ip < e->start_ip_offset) 764 hi = mid; 765 else 766 lo = mid + 1; 767 } 768 if (hi <= 0) 769 return NULL; 770 e = table + hi - 1; 771 return e; 772} 773 774/* Lookup an unwind-table entry in remote memory. Returns 1 if an 775 entry is found, 0 if no entry is found, negative if an error 776 occurred reading remote memory. */ 777static int 778remote_lookup (unw_addr_space_t as, 779 unw_word_t table, size_t table_size, int32_t rel_ip, 780 struct table_entry *e, void *arg) 781{ 782 unsigned long table_len = table_size / sizeof (struct table_entry); 783 unw_accessors_t *a = unw_get_accessors (as); 784 unsigned long lo, hi, mid; 785 unw_word_t e_addr = 0; 786 int32_t start; 787 int ret; 788 789 /* do a binary search for right entry: */ 790 for (lo = 0, hi = table_len; lo < hi;) 791 { 792 mid = (lo + hi) / 2; 793 e_addr = table + mid * sizeof (struct table_entry); 794 if ((ret = dwarf_reads32 (as, a, &e_addr, &start, arg)) < 0) 795 return ret; 796 797 if (rel_ip < start) 798 hi = mid; 799 else 800 lo = mid + 1; 801 } 802 if (hi <= 0) 803 return 0; 804 e_addr = table + (hi - 1) * sizeof (struct table_entry); 805 if ((ret = dwarf_reads32 (as, a, &e_addr, &e->start_ip_offset, arg)) < 0 806 || (ret = dwarf_reads32 (as, a, &e_addr, &e->fde_offset, arg)) < 0) 807 return ret; 808 return 1; 809} 810 811static int is_remote_table(int format) 812{ 813 return (format == UNW_INFO_FORMAT_REMOTE_TABLE || 814 format == UNW_INFO_FORMAT_IP_OFFSET); 815} 816 817HIDDEN int 818dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, 819 unw_dyn_info_t *di, unw_proc_info_t *pi, 820 int need_unwind_info, void *arg) 821{ 822 const struct table_entry *e = NULL, *table = NULL; 823 unw_word_t ip_base = 0, segbase = 0, fde_addr; 824 unw_accessors_t *a; 825 struct table_entry ent; 826 int ret; 827 unw_word_t debug_frame_base; 828 size_t table_len = 0; 829 830 Debug (10, "ip 0x%lx, di->format %u, di->start_ip 0x%lx, di->end_ip 0x%lx\n", 831 (long) ip, di->format, (long) di->start_ip, (long) di->end_ip); 832 833 assert (is_remote_table(di->format) 834 || di->format == UNW_INFO_FORMAT_TABLE); 835 assert (ip >= di->start_ip && ip < di->end_ip); 836 837 if (is_remote_table(di->format)) 838 { 839 table = (const struct table_entry *) (uintptr_t) di->u.rti.table_data; 840 table_len = di->u.rti.table_len * sizeof (unw_word_t); 841 debug_frame_base = 0; 842 } 843 else 844 { 845 assert(di->format == UNW_INFO_FORMAT_TABLE); 846 struct unw_debug_frame_list *fdesc = (void *) di->u.ti.table_data; 847 848 /* UNW_INFO_FORMAT_TABLE (i.e. .debug_frame) is read from local address 849 space. Both the index and the unwind tables live in local memory, but 850 the address space to check for properties like the address size and 851 endianness is the target one. */ 852 as = unw_local_addr_space; 853 table = fdesc->index; 854 table_len = fdesc->index_size * sizeof (struct table_entry); 855 debug_frame_base = (uintptr_t) fdesc->debug_frame; 856 } 857 858 a = unw_get_accessors (as); 859 860 segbase = di->u.rti.segbase; 861 if (di->format == UNW_INFO_FORMAT_IP_OFFSET) { 862 ip_base = di->start_ip; 863 } else { 864 ip_base = segbase; 865 } 866 867 if (as == unw_local_addr_space) 868 { 869 e = lookup (table, table_len, ip - ip_base); 870 } 871 else 872 { 873 segbase = di->u.rti.segbase; 874 ret = remote_lookup (as, (uintptr_t) table, table_len, 875 ip - ip_base, &ent, arg); 876 if (ret < 0) 877 return ret; 878 if (ret) 879 e = &ent; 880 else 881 e = NULL; /* no info found */ 882 } 883 884 if (!e) 885 { 886 Debug (1, "IP %lx inside range %lx-%lx, but no explicit unwind info found\n", 887 (long) ip, (long) di->start_ip, (long) di->end_ip); 888 /* IP is inside this table's range, but there is no explicit 889 unwind info. */ 890 return -UNW_ENOINFO; 891 } 892 Debug (15, "ip=0x%lx, start_ip_offset=0x%lx\n", 893 (long) ip, (long) (e->start_ip_offset)); 894 895 if (debug_frame_base) 896 fde_addr = e->fde_offset + debug_frame_base; 897 else 898 fde_addr = e->fde_offset + segbase; 899 Debug (1, "e->fde_offset = %lx, segbase = %lx, debug_frame_base = %lx, " 900 "fde_addr = %lx\n", (long) e->fde_offset, (long) segbase, 901 (long) debug_frame_base, (long) fde_addr); 902 903 if ((ret = dwarf_extract_proc_info_from_fde (as, a, &fde_addr, pi, 904 debug_frame_base ? 905 debug_frame_base : segbase, 906 need_unwind_info, 907 debug_frame_base != 0, arg)) < 0) 908 return ret; 909 910 /* .debug_frame uses an absolute encoding that does not know about any 911 shared library relocation. */ 912 if (di->format == UNW_INFO_FORMAT_TABLE) 913 { 914 pi->start_ip += segbase; 915 pi->end_ip += segbase; 916 pi->flags = UNW_PI_FLAG_DEBUG_FRAME; 917 } 918 919 if (ip < pi->start_ip || ip >= pi->end_ip) 920 return -UNW_ENOINFO; 921 922 return 0; 923} 924 925HIDDEN void 926dwarf_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *pi, void *arg) 927{ 928 return; /* always a nop */ 929} 930