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