elfxx.c revision 96c1bca2b08f147011814b1b7be6863151ef0be9
1/* libunwind - a platform-independent unwind library 2 Copyright (C) 2003-2005 Hewlett-Packard Co 3 Copyright (C) 2007 David Mosberger-Tang 4 Contributed by David Mosberger-Tang <dmosberger@gmail.com> 5 6This file is part of libunwind. 7 8Permission is hereby granted, free of charge, to any person obtaining 9a copy of this software and associated documentation files (the 10"Software"), to deal in the Software without restriction, including 11without limitation the rights to use, copy, modify, merge, publish, 12distribute, sublicense, and/or sell copies of the Software, and to 13permit persons to whom the Software is furnished to do so, subject to 14the following conditions: 15 16The above copyright notice and this permission notice shall be 17included in all copies or substantial portions of the Software. 18 19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 26 27#include "libunwind_i.h" 28 29#include <stdio.h> 30#include <sys/param.h> 31 32#ifdef HAVE_LZMA 33#include <lzma.h> 34#endif /* HAVE_LZMA */ 35 36static Elf_W (Shdr)* 37elf_w (section_table) (struct elf_image *ei) 38{ 39 Elf_W (Ehdr) *ehdr = ei->image; 40 Elf_W (Off) soff; 41 42 soff = ehdr->e_shoff; 43 if (soff + ehdr->e_shnum * ehdr->e_shentsize > ei->size) 44 { 45 Debug (1, "section table outside of image? (%lu > %lu)\n", 46 (unsigned long) (soff + ehdr->e_shnum * ehdr->e_shentsize), 47 (unsigned long) ei->size); 48 return NULL; 49 } 50 51 return (Elf_W (Shdr) *) ((char *) ei->image + soff); 52} 53 54static char* 55elf_w (string_table) (struct elf_image *ei, int section) 56{ 57 Elf_W (Ehdr) *ehdr = ei->image; 58 Elf_W (Off) soff, str_soff; 59 Elf_W (Shdr) *str_shdr; 60 61 /* this offset is assumed to be OK */ 62 soff = ehdr->e_shoff; 63 64 str_soff = soff + (section * ehdr->e_shentsize); 65 if (str_soff + ehdr->e_shentsize > ei->size) 66 { 67 Debug (1, "string shdr table outside of image? (%lu > %lu)\n", 68 (unsigned long) (str_soff + ehdr->e_shentsize), 69 (unsigned long) ei->size); 70 return NULL; 71 } 72 str_shdr = (Elf_W (Shdr) *) ((char *) ei->image + str_soff); 73 74 if (str_shdr->sh_offset + str_shdr->sh_size > ei->size) 75 { 76 Debug (1, "string table outside of image? (%lu > %lu)\n", 77 (unsigned long) (str_shdr->sh_offset + str_shdr->sh_size), 78 (unsigned long) ei->size); 79 return NULL; 80 } 81 82 Debug (16, "strtab=0x%lx\n", (long) str_shdr->sh_offset); 83 return ei->image + str_shdr->sh_offset; 84} 85 86static int 87elf_w (lookup_symbol) (unw_addr_space_t as, 88 unw_word_t ip, struct elf_image *ei, 89 Elf_W (Addr) load_offset, 90 char *buf, size_t buf_len, Elf_W (Addr) *min_dist) 91{ 92 size_t syment_size; 93 Elf_W (Ehdr) *ehdr = ei->image; 94 Elf_W (Sym) *sym, *symtab, *symtab_end; 95 Elf_W (Shdr) *shdr; 96 Elf_W (Addr) val; 97 int i, ret = -UNW_ENOINFO; 98 char *strtab; 99 100 if (!elf_w (valid_object) (ei)) 101 return -UNW_ENOINFO; 102 103 shdr = elf_w (section_table) (ei); 104 if (!shdr) 105 return -UNW_ENOINFO; 106 107 for (i = 0; i < ehdr->e_shnum; ++i) 108 { 109 switch (shdr->sh_type) 110 { 111 case SHT_SYMTAB: 112 case SHT_DYNSYM: 113 symtab = (Elf_W (Sym) *) ((char *) ei->image + shdr->sh_offset); 114 symtab_end = (Elf_W (Sym) *) ((char *) symtab + shdr->sh_size); 115 syment_size = shdr->sh_entsize; 116 117 strtab = elf_w (string_table) (ei, shdr->sh_link); 118 if (!strtab) 119 break; 120 121 Debug (16, "symtab=0x%lx[%d]\n", 122 (long) shdr->sh_offset, shdr->sh_type); 123 124 for (sym = symtab; 125 sym < symtab_end; 126 sym = (Elf_W (Sym) *) ((char *) sym + syment_size)) 127 { 128 if (ELF_W (ST_TYPE) (sym->st_info) == STT_FUNC 129 && sym->st_shndx != SHN_UNDEF) 130 { 131 if (tdep_get_func_addr (as, sym->st_value, &val) < 0) 132 continue; 133 if (sym->st_shndx != SHN_ABS) 134 val += load_offset; 135 Debug (16, "0x%016lx info=0x%02x %s\n", 136 (long) val, sym->st_info, strtab + sym->st_name); 137 138 if ((Elf_W (Addr)) (ip - val) < *min_dist) 139 { 140 *min_dist = (Elf_W (Addr)) (ip - val); 141 strncpy (buf, strtab + sym->st_name, buf_len); 142 buf[buf_len - 1] = '\0'; 143 ret = (strlen (strtab + sym->st_name) >= buf_len 144 ? -UNW_ENOMEM : 0); 145 } 146 } 147 } 148 break; 149 150 default: 151 break; 152 } 153 shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize); 154 } 155 return ret; 156} 157 158static Elf_W (Addr) 159elf_w (get_load_offset) (struct elf_image *ei, unsigned long segbase, 160 unsigned long mapoff) 161{ 162 Elf_W (Addr) offset = 0; 163 Elf_W (Ehdr) *ehdr; 164 Elf_W (Phdr) *phdr; 165 int i; 166 167 ehdr = ei->image; 168 phdr = (Elf_W (Phdr) *) ((char *) ei->image + ehdr->e_phoff); 169 170 for (i = 0; i < ehdr->e_phnum; ++i) 171 if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff) 172 { 173 offset = segbase - phdr[i].p_vaddr; 174 break; 175 } 176 177 return offset; 178} 179 180#if HAVE_LZMA 181static size_t 182xz_uncompressed_size (uint8_t *compressed, size_t length) 183{ 184 uint64_t memlimit = UINT64_MAX; 185 size_t ret = 0, pos = 0; 186 lzma_stream_flags options; 187 lzma_index *index; 188 189 if (length < LZMA_STREAM_HEADER_SIZE) 190 return 0; 191 192 uint8_t *footer = compressed + length - LZMA_STREAM_HEADER_SIZE; 193 if (lzma_stream_footer_decode (&options, footer) != LZMA_OK) 194 return 0; 195 196 if (length < LZMA_STREAM_HEADER_SIZE + options.backward_size) 197 return 0; 198 199 uint8_t *indexdata = footer - options.backward_size; 200 if (lzma_index_buffer_decode (&index, &memlimit, NULL, indexdata, 201 &pos, options.backward_size) != LZMA_OK) 202 return 0; 203 204 if (lzma_index_size (index) == options.backward_size) 205 { 206 ret = lzma_index_uncompressed_size (index); 207 } 208 209 lzma_index_end (index, NULL); 210 return ret; 211} 212 213static int 214elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi) 215{ 216 Elf_W (Ehdr) *ehdr = ei->image; 217 Elf_W (Shdr) *shdr; 218 char *strtab; 219 int i; 220 uint8_t *compressed = NULL; 221 uint64_t memlimit = UINT64_MAX; /* no memory limit */ 222 size_t compressed_len, uncompressed_len; 223 224 if (!elf_w (valid_object) (ei)) 225 return 0; 226 227 shdr = elf_w (section_table) (ei); 228 if (!shdr) 229 return 0; 230 231 strtab = elf_w (string_table) (ei, ehdr->e_shstrndx); 232 if (!strtab) 233 return 0; 234 235 for (i = 0; i < ehdr->e_shnum; ++i) 236 { 237 if (strcmp (strtab + shdr->sh_name, ".gnu_debugdata") == 0) 238 { 239 if (shdr->sh_offset + shdr->sh_size > ei->size) 240 { 241 Debug (1, ".gnu_debugdata outside image? (0x%lu > 0x%lu)\n", 242 (unsigned long) shdr->sh_offset + shdr->sh_size, 243 (unsigned long) ei->size); 244 return 0; 245 } 246 247 Debug (16, "found .gnu_debugdata at 0x%lx\n", 248 (unsigned long) shdr->sh_offset); 249 compressed = ((uint8_t *) ei->image) + shdr->sh_offset; 250 compressed_len = shdr->sh_size; 251 break; 252 } 253 254 shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize); 255 } 256 257 /* not found */ 258 if (!compressed) 259 return 0; 260 261 uncompressed_len = xz_uncompressed_size (compressed, compressed_len); 262 if (uncompressed_len == 0) 263 { 264 Debug (1, "invalid .gnu_debugdata contents\n"); 265 return 0; 266 } 267 268 mdi->size = uncompressed_len; 269 mdi->image = mmap (NULL, uncompressed_len, PROT_READ|PROT_WRITE, 270 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 271 272 if (mdi->image == MAP_FAILED) 273 return 0; 274 275 size_t in_pos = 0, out_pos = 0; 276 lzma_ret lret; 277 lret = lzma_stream_buffer_decode (&memlimit, 0, NULL, 278 compressed, &in_pos, compressed_len, 279 mdi->image, &out_pos, mdi->size); 280 if (lret != LZMA_OK) 281 { 282 Debug (1, "LZMA decompression failed: %d\n", lret); 283 munmap (mdi->image, mdi->size); 284 return 0; 285 } 286 287 return 1; 288} 289#else 290static int 291elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi) 292{ 293 return 0; 294} 295#endif /* !HAVE_LZMA */ 296 297/* Find the ELF image that contains IP and return the "closest" 298 procedure name, if there is one. With some caching, this could be 299 sped up greatly, but until an application materializes that's 300 sensitive to the performance of this routine, why bother... */ 301 302HIDDEN int 303elf_w (get_proc_name_in_image) (unw_addr_space_t as, struct elf_image *ei, 304 unsigned long segbase, 305 unsigned long mapoff, 306 unw_word_t ip, 307 char *buf, size_t buf_len, unw_word_t *offp) 308{ 309 Elf_W (Addr) load_offset; 310 Elf_W (Addr) min_dist = ~(Elf_W (Addr))0; 311 int ret; 312 313 load_offset = elf_w (get_load_offset) (ei, segbase, mapoff); 314 ret = elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, &min_dist); 315 316 /* If the ELF image has MiniDebugInfo embedded in it, look up the symbol in 317 there as well and replace the previously found if it is closer. */ 318 struct elf_image mdi; 319 if (elf_w (extract_minidebuginfo) (ei, &mdi)) 320 { 321 int ret_mdi; 322 323 load_offset = elf_w (get_load_offset) (&mdi, segbase, mapoff); 324 ret_mdi = elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf, 325 buf_len, &min_dist); 326 327 /* Closer symbol was found (possibly truncated). */ 328 if (ret_mdi == 0 || ret_mdi == -UNW_ENOMEM) 329 { 330 ret = ret_mdi; 331 } 332 333 munmap (mdi.image, mdi.size); 334 } 335 336 if (min_dist >= ei->size) 337 return -UNW_ENOINFO; /* not found */ 338 if (offp) 339 *offp = min_dist; 340 return ret; 341} 342 343HIDDEN int 344elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip, 345 char *buf, size_t buf_len, unw_word_t *offp) 346{ 347 unsigned long segbase, mapoff; 348 struct elf_image ei; 349 int ret; 350 351 ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff, NULL, 0); 352 if (ret < 0) 353 return ret; 354 355 ret = elf_w (get_proc_name_in_image) (as, &ei, segbase, mapoff, ip, buf, buf_len, offp); 356 357 munmap (ei.image, ei.size); 358 ei.image = NULL; 359 360 return ret; 361} 362