1/* libunwind - a platform-independent unwind library 2 Copyright (C) 2003-2004 Hewlett-Packard Co 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#include <fcntl.h> 27#include <inttypes.h> 28#include <string.h> 29#include <unistd.h> 30 31#include <sys/mman.h> 32 33#include "libunwind_i.h" 34#include "dwarf-eh.h" 35#include "dwarf_i.h" 36 37HIDDEN int 38dwarf_find_unwind_table (struct elf_dyn_info *edi, unw_addr_space_t as, 39 char *path, unw_word_t segbase, unw_word_t mapoff, 40 unw_word_t ip) 41{ 42 Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL; 43 unw_word_t addr, eh_frame_start, fde_count, load_base; 44 unw_word_t max_load_addr = 0; 45 unw_word_t start_ip = (unw_word_t) -1; 46 unw_word_t end_ip = 0; 47 struct dwarf_eh_frame_hdr *hdr; 48 unw_proc_info_t pi; 49 unw_accessors_t *a; 50 Elf_W(Ehdr) *ehdr; 51#if UNW_TARGET_ARM 52 const Elf_W(Phdr) *parm_exidx = NULL; 53#endif 54 int i, ret, found = 0; 55 56 Debug (3, "(edi %p, %p, \"%s\", 0x%lx, 0x%lx, 0x%lx)\n", 57 edi, as, path, (long) segbase, (long) mapoff, (long) ip); 58 59 /* XXX: Much of this code is Linux/LSB-specific. */ 60 61 if (!elf_w(valid_object) (&edi->ei)) 62 { 63 Debug(3, "returning, invalid elf object\n"); 64 return -UNW_ENOINFO; 65 } 66 67 ehdr = edi->ei.image; 68 phdr = (Elf_W(Phdr) *) ((char *) edi->ei.image + ehdr->e_phoff); 69 70 for (i = 0; i < ehdr->e_phnum; ++i) 71 { 72 switch (phdr[i].p_type) 73 { 74 case PT_LOAD: 75 if (phdr[i].p_vaddr < start_ip) 76 start_ip = phdr[i].p_vaddr; 77 78 if (phdr[i].p_vaddr + phdr[i].p_memsz > end_ip) 79 end_ip = phdr[i].p_vaddr + phdr[i].p_memsz; 80 81 if (phdr[i].p_offset == mapoff) 82 ptxt = phdr + i; 83 if ((uintptr_t) edi->ei.image + phdr->p_filesz > max_load_addr) 84 max_load_addr = (uintptr_t) edi->ei.image + phdr->p_filesz; 85 break; 86 87 case PT_GNU_EH_FRAME: 88 peh_hdr = phdr + i; 89 break; 90 91 case PT_DYNAMIC: 92 pdyn = phdr + i; 93 break; 94 95#if UNW_TARGET_ARM 96 case PT_ARM_EXIDX: 97 parm_exidx = phdr + i; 98 break; 99#endif 100 101 default: 102 break; 103 } 104 } 105 106 if (!ptxt) 107 { 108 Debug(3, "returning 0, no text\n"); 109 return 0; 110 } 111 112 load_base = segbase - ptxt->p_vaddr; 113 start_ip += load_base; 114 end_ip += load_base; 115 116 if (peh_hdr) 117 { 118 if (pdyn) 119 { 120 /* For dynamicly linked executables and shared libraries, 121 DT_PLTGOT is the value that data-relative addresses are 122 relative to for that object. We call this the "gp". */ 123 Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(pdyn->p_offset 124 + (char *) edi->ei.image); 125 for (; dyn->d_tag != DT_NULL; ++dyn) 126 if (dyn->d_tag == DT_PLTGOT) 127 { 128 /* Assume that _DYNAMIC is writable and GLIBC has 129 relocated it (true for x86 at least). */ 130 edi->di_cache.gp = dyn->d_un.d_ptr; 131 break; 132 } 133 } 134 else 135 /* Otherwise this is a static executable with no _DYNAMIC. Assume 136 that data-relative addresses are relative to 0, i.e., 137 absolute. */ 138 edi->di_cache.gp = 0; 139 140 hdr = (struct dwarf_eh_frame_hdr *) (peh_hdr->p_offset 141 + (char *) edi->ei.image); 142 if (hdr->version != DW_EH_VERSION) 143 { 144 Debug (1, "returning, table `%s' has unexpected version %d\n", 145 path, hdr->version); 146 return -UNW_ENOINFO; 147 } 148 149 a = unw_get_accessors (unw_local_addr_space); 150 addr = (unw_word_t) (hdr + 1); 151 152 /* Fill in a dummy proc_info structure. We just need to fill in 153 enough to ensure that dwarf_read_encoded_pointer() can do it's 154 job. Since we don't have a procedure-context at this point, all 155 we have to do is fill in the global-pointer. */ 156 memset (&pi, 0, sizeof (pi)); 157 pi.gp = edi->di_cache.gp; 158 159 /* (Optionally) read eh_frame_ptr: */ 160 if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a, 161 &addr, hdr->eh_frame_ptr_enc, &pi, 162 &eh_frame_start, NULL)) < 0) 163 { 164 Debug(3, "returning, dwarf_read_encoded_pointer failed: %d\n", ret); 165 return -UNW_ENOINFO; 166 } 167 168 /* (Optionally) read fde_count: */ 169 if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a, 170 &addr, hdr->fde_count_enc, &pi, 171 &fde_count, NULL)) < 0) 172 { 173 Debug(3, "returning, dwarf_read_encoded_pointer failed: %d\n", ret); 174 return -UNW_ENOINFO; 175 } 176 177 if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) 178 { 179 #if 1 180 assert (0); 181 #else 182 unw_word_t eh_frame_end; 183 184 /* If there is no search table or it has an unsupported 185 encoding, fall back on linear search. */ 186 if (hdr->table_enc == DW_EH_PE_omit) 187 Debug (4, "EH lacks search table; doing linear search\n"); 188 else 189 Debug (4, "EH table has encoding 0x%x; doing linear search\n", 190 hdr->table_enc); 191 192 eh_frame_end = max_load_addr; /* XXX can we do better? */ 193 194 if (hdr->fde_count_enc == DW_EH_PE_omit) 195 fde_count = ~0UL; 196 if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit) 197 assert (0); 198 199 return linear_search (unw_local_addr_space, ip, 200 eh_frame_start, eh_frame_end, fde_count, 201 pi, need_unwind_info, NULL); 202 #endif 203 } 204 205 edi->di_cache.start_ip = start_ip; 206 edi->di_cache.end_ip = end_ip; 207 edi->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE; 208 edi->di_cache.u.rti.name_ptr = 0; 209 /* two 32-bit values (ip_offset/fde_offset) per table-entry: */ 210 edi->di_cache.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t); 211 edi->di_cache.u.rti.table_data = ((load_base + peh_hdr->p_vaddr) 212 + sizeof(*hdr)); 213 214 /* For the binary-search table in the eh_frame_hdr, data-relative 215 means relative to the start of that section... */ 216 edi->di_cache.u.rti.segbase = (load_base + peh_hdr->p_vaddr); 217 found = 1; 218 } 219 220#if UNW_TARGET_ARM 221 if (parm_exidx) 222 { 223 edi->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX; 224 edi->di_arm.start_ip = start_ip; 225 edi->di_arm.end_ip = end_ip; 226 edi->di_arm.u.rti.name_ptr = (unw_word_t) path; 227 edi->di_arm.u.rti.table_data = load_base + parm_exidx->p_vaddr; 228 edi->di_arm.u.rti.table_len = parm_exidx->p_memsz; 229 found = 1; 230 } 231#endif 232 233#ifdef CONFIG_DEBUG_FRAME 234 /* Try .debug_frame. */ 235 found = dwarf_find_debug_frame (found, &edi->di_debug, ip, load_base, path, 236 start_ip, end_ip); 237#endif 238 239 Debug(3, "returning, found %d\n", found); 240 return found; 241} 242 243/* Exported version that uses the address space of the potentially 244 remote process. 245 We only need to read memory, but pass |as| to keep a consistent API. */ 246 247HIDDEN int 248dwarf_as_find_unwind_table (struct as_elf_dyn_info *edi, unw_addr_space_t as, 249 const char *path, unw_word_t segbase, unw_word_t mapoff, 250 unw_word_t ip) 251{ 252 Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL; 253 unw_word_t addr, eh_frame_start, fde_count, load_base; 254 unw_word_t start_ip = (unw_word_t) -1; 255 unw_word_t end_ip = 0; 256 struct dwarf_eh_frame_hdr *hdr; 257 unw_proc_info_t pi; 258 Elf_W(Ehdr) *ehdr; 259#if 0 260#if UNW_TARGET_ARM 261 const Elf_W(Phdr) *parm_exidx = NULL; 262#endif 263#endif 264 int i, ret, found = 0; 265 266 Debug (3, "(edi %p, %p, \"%s\", 0x%lx, 0x%lx, 0x%lx)\n", 267 edi, as, path, (long) segbase, (long) mapoff, (long) ip); 268 269 // TODO(dje): byteswapping of ehdr values 270 ret = unwi_load_as_contents(as, &edi->ehdr, segbase, sizeof(*ehdr), edi->arg); 271 if (ret < 0) 272 { 273 Debug(3, "returning, unwi_load_as_contents failed: %d\n", ret); 274 return ret; 275 } 276 ehdr = edi->ehdr.data; 277 278 { 279 /* Construct a fake elf_image sufficient for valid_object. */ 280 struct elf_image ei; 281 ei.image = ehdr; 282 ei.size = sizeof (*ehdr); 283 if (!elf_w(valid_object) (&ei)) 284 { 285 Debug(3, "returning, invalid elf object\n"); 286 return -UNW_ENOINFO; 287 } 288 } 289 290 // TODO(dje): byteswapping of ehdr values 291 { 292 size_t phdr_size = ehdr->e_phnum * ehdr->e_phentsize; 293 ret = unwi_load_as_contents(as, &edi->phdr, segbase + ehdr->e_phoff, phdr_size, edi->arg); 294 if (ret < 0) 295 { 296 Debug(3, "returning, unwi_load_as_contents failed: %d\n", ret); 297 return ret; 298 } 299 phdr = edi->phdr.data; 300 } 301 302 Debug (3, "scanning phdrs\n"); 303 304 for (i = 0; i < ehdr->e_phnum; ++i) 305 { 306 Debug (5, "phdr[%d]: type 0x%x, vaddr 0x%lx, memsz 0x%lx\n", 307 i, phdr[i].p_type, (long) phdr[i].p_vaddr, (long) phdr[i].p_memsz); 308 309 switch (phdr[i].p_type) 310 { 311 case PT_LOAD: 312 if (phdr[i].p_vaddr < start_ip) 313 start_ip = phdr[i].p_vaddr; 314 315 if (phdr[i].p_vaddr + phdr[i].p_memsz > end_ip) 316 end_ip = phdr[i].p_vaddr + phdr[i].p_memsz; 317 318 if (phdr[i].p_offset == mapoff) 319 ptxt = phdr + i; 320 321 break; 322 323 case PT_GNU_EH_FRAME: 324 peh_hdr = phdr + i; 325 ret = unwi_load_as_contents(as, &edi->eh, segbase + peh_hdr->p_vaddr, peh_hdr->p_memsz, edi->arg); 326 if (ret < 0) 327 { 328 Debug(3, "returning, unwi_load_as_contents failed: %d\n", ret); 329 return ret; 330 } 331 break; 332 333 case PT_DYNAMIC: 334 pdyn = phdr + i; 335 ret = unwi_load_as_contents(as, &edi->dyn, segbase + pdyn->p_vaddr, pdyn->p_memsz, edi->arg); 336 if (ret < 0) 337 { 338 Debug(3, "returning, unwi_load_as_contents failed: %d\n", ret); 339 return ret; 340 } 341 break; 342 343#if 0 344#if UNW_TARGET_ARM 345 case PT_ARM_EXIDX: 346 parm_exidx = phdr + i; 347 break; 348#endif 349#endif 350 351 default: 352 break; 353 } 354 } 355 356 Debug (3, "scanning phdrs, ptxt %p\n", ptxt); 357 358 if (!ptxt) 359 { 360 Debug(3, "returning 0, no text\n"); 361 return 0; 362 } 363 364 load_base = segbase - ptxt->p_vaddr; // ??? 365 start_ip += load_base; 366 end_ip += load_base; 367 368 Debug (3, "load_base 0x%lx, start_ip 0x%lx, end_ip 0x%lx\n", 369 (long) load_base, (long) start_ip, (long) end_ip); 370 371 if (peh_hdr) 372 { 373 if (pdyn) 374 { 375 /* For dynamicly linked executables and shared libraries, 376 DT_PLTGOT is the value that data-relative addresses are 377 relative to for that object. We call this the "gp". */ 378 Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(edi->dyn.data); 379 for (; dyn->d_tag != DT_NULL; ++dyn) 380 if (dyn->d_tag == DT_PLTGOT) 381 { 382 /* Assume that _DYNAMIC is writable and GLIBC has 383 relocated it (true for x86 at least). */ 384 edi->di_cache.gp = dyn->d_un.d_ptr; 385 break; 386 } 387 } 388 else 389 /* Otherwise this is a static executable with no _DYNAMIC. Assume 390 that data-relative addresses are relative to 0, i.e., 391 absolute. */ 392 edi->di_cache.gp = 0; 393 394 hdr = (struct dwarf_eh_frame_hdr *) (edi->eh.data); 395 if (hdr->version != DW_EH_VERSION) 396 { 397 Debug (1, "returning, table `%s' has unexpected version %d\n", 398 path, hdr->version); 399 return -UNW_ENOINFO; 400 } 401 402 addr = (unw_word_t) (hdr + 1); 403 404 /* Fill in a dummy proc_info structure. We just need to fill in 405 enough to ensure that dwarf_read_encoded_pointer() can do it's 406 job. Since we don't have a procedure-context at this point, all 407 we have to do is fill in the global-pointer. */ 408 memset (&pi, 0, sizeof (pi)); 409 pi.gp = edi->di_cache.gp; 410 411 // We're reading from the .eh_frame we just read in, so it's our local addr space. 412 unw_accessors_t *la = unw_get_accessors (unw_local_addr_space); 413 414 Debug(5, "unw_local_addr_space %p, la %p\n", unw_local_addr_space, la); 415 416 /* (Optionally) read eh_frame_ptr: */ 417 if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, la, 418 &addr, hdr->eh_frame_ptr_enc, &pi, 419 &eh_frame_start, NULL)) < 0) 420 { 421 Debug(3, "returning, dwarf_read_encoded_pointer failed: %d\n", ret); 422 return -UNW_ENOINFO; 423 } 424 425 /* (Optionally) read fde_count: */ 426 if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, la, 427 &addr, hdr->fde_count_enc, &pi, 428 &fde_count, NULL)) < 0) 429 { 430 Debug(3, "returning, dwarf_read_encoded_pointer failed: %d\n", ret); 431 return -UNW_ENOINFO; 432 } 433 434 if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) 435 { 436 /* If there is no search table or it has an unsupported 437 encoding, fail. For now. */ 438 Debug(3, "returning, weird table encoding\n"); 439 return -UNW_ENOINFO; 440 } 441 442 //Debug(3, "fde_count %lu \n", (long) fde_count); 443 444 edi->di_cache.start_ip = start_ip; 445 edi->di_cache.end_ip = end_ip; 446 edi->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE; 447 edi->di_cache.u.rti.name_ptr = 0; 448 /* two 32-bit values (ip_offset/fde_offset) per table-entry: */ 449 edi->di_cache.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t); 450 edi->di_cache.u.rti.table_data = ((load_base + peh_hdr->p_vaddr) 451 + sizeof(*hdr)); 452 453 /* For the binary-search table in the eh_frame_hdr, data-relative 454 means relative to the start of that section... */ 455 edi->di_cache.u.rti.segbase = (load_base + peh_hdr->p_vaddr); 456 found = 1; 457 } 458 459#if 0 460#if UNW_TARGET_ARM 461 if (parm_exidx) 462 { 463 edi->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX; 464 edi->di_arm.start_ip = start_ip; 465 edi->di_arm.end_ip = end_ip; 466 edi->di_arm.u.rti.name_ptr = (unw_word_t) path; 467 edi->di_arm.u.rti.table_data = load_base + parm_exidx->p_vaddr; 468 edi->di_arm.u.rti.table_len = parm_exidx->p_memsz; 469 found = 1; 470 } 471#endif 472#endif 473 474#ifdef CONFIG_DEBUG_FRAME 475 /* Try .debug_frame. */ 476 found = dwarf_find_debug_frame (found, &edi->di_debug, ip, load_base, path, 477 start_ip, end_ip); 478#endif 479 480 Debug(3, "returning, found %d\n", found); 481 return found; 482} 483