• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  
6  This file is part of libunwind.
7  
8  Permission is hereby granted, free of charge, to any person obtaining
9  a copy of this software and associated documentation files (the
10  "Software"), to deal in the Software without restriction, including
11  without limitation the rights to use, copy, modify, merge, publish,
12  distribute, sublicense, and/or sell copies of the Software, and to
13  permit persons to whom the Software is furnished to do so, subject to
14  the following conditions:
15  
16  The above copyright notice and this permission notice shall be
17  included in all copies or substantial portions of the Software.
18  
19  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25  WITH 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  
36  // --------------------------------------------------------------------------
37  // Functions to read elf data from memory.
38  // --------------------------------------------------------------------------
elf_w(memory_read)39  extern size_t elf_w (memory_read) (
40      struct elf_image* ei, unw_word_t addr, uint8_t* buffer, size_t bytes, bool string_read) {
41    struct map_info* map = ei->u.memory.map;
42    unw_accessors_t* a = unw_get_accessors (ei->u.memory.as);
43    if (map->end - addr < bytes) {
44      bytes = map->end - addr;
45    }
46    size_t bytes_read = 0;
47    unw_word_t data_word;
48    size_t align_bytes = addr & (sizeof(unw_word_t) - 1);
49    if (align_bytes != 0) {
50      if ((*a->access_mem) (ei->u.memory.as, addr & ~(sizeof(unw_word_t) - 1), &data_word,
51                            0, ei->u.memory.as_arg) != 0) {
52        return 0;
53      }
54      size_t copy_bytes = MIN(sizeof(unw_word_t) - align_bytes, bytes);
55      memcpy (buffer, (uint8_t*) (&data_word) + align_bytes, copy_bytes);
56      if (string_read) {
57        // Check for nul terminator.
58        uint8_t* nul_terminator = memchr (buffer, '\0', copy_bytes);
59        if (nul_terminator != NULL) {
60          return nul_terminator - buffer;
61        }
62      }
63  
64      addr += copy_bytes;
65      bytes_read += copy_bytes;
66      bytes -= copy_bytes;
67      buffer += copy_bytes;
68    }
69  
70    size_t num_words = bytes / sizeof(unw_word_t);
71    size_t i;
72    for (i = 0; i < num_words; i++) {
73      if ((*a->access_mem) (ei->u.memory.as, addr, &data_word, 0, ei->u.memory.as_arg) != 0) {
74        return bytes_read;
75      }
76  
77      memcpy (buffer, &data_word, sizeof(unw_word_t));
78      if (string_read) {
79        // Check for nul terminator.
80        uint8_t* nul_terminator = memchr (buffer, '\0', sizeof(unw_word_t));
81        if (nul_terminator != NULL) {
82          return nul_terminator - buffer + bytes_read;
83        }
84      }
85  
86      addr += sizeof(unw_word_t);
87      bytes_read += sizeof(unw_word_t);
88      buffer += sizeof(unw_word_t);
89    }
90  
91    size_t left_over = bytes & (sizeof(unw_word_t) - 1);
92    if (left_over) {
93      if ((*a->access_mem) (ei->u.memory.as, addr, &data_word, 0, ei->u.memory.as_arg) != 0) {
94        return bytes_read;
95      }
96  
97      memcpy (buffer, &data_word, left_over);
98      if (string_read) {
99        // Check for nul terminator.
100        uint8_t* nul_terminator = memchr (buffer, '\0', sizeof(unw_word_t));
101        if (nul_terminator != NULL) {
102          return nul_terminator - buffer + bytes_read;
103        }
104      }
105  
106      bytes_read += left_over;
107    }
108    return bytes_read;
109  }
110  
elf_w(section_table_offset)111  static bool elf_w (section_table_offset) (struct elf_image* ei, Elf_W(Ehdr)* ehdr, Elf_W(Off)* offset) {
112    GET_EHDR_FIELD(ei, ehdr, e_shoff, true);
113    GET_EHDR_FIELD(ei, ehdr, e_shentsize, true);
114    GET_EHDR_FIELD(ei, ehdr, e_shnum, true);
115  
116    uintptr_t size = ei->u.memory.map->end - ei->u.memory.map->start;
117    if (ehdr->e_shoff + ehdr->e_shnum * ehdr->e_shentsize > size) {
118      Debug (1, "section table outside of image? (%lu > %lu)\n",
119             (unsigned long) (ehdr->e_shoff + ehdr->e_shnum * ehdr->e_shentsize),
120             (unsigned long) size);
121      return false;
122    }
123  
124    *offset = ehdr->e_shoff;
125    return true;
126  }
127  
elf_w(string_table_offset)128  static bool elf_w (string_table_offset) (
129      struct elf_image* ei, int section, Elf_W(Ehdr)* ehdr, Elf_W(Off)* offset) {
130    GET_EHDR_FIELD(ei, ehdr, e_shoff, true);
131    GET_EHDR_FIELD(ei, ehdr, e_shentsize, true);
132    unw_word_t str_soff = ehdr->e_shoff + (section * ehdr->e_shentsize);
133    uintptr_t size = ei->u.memory.map->end - ei->u.memory.map->start;
134    if (str_soff + ehdr->e_shentsize > size) {
135      Debug (1, "string shdr table outside of image? (%lu > %lu)\n",
136             (unsigned long) (str_soff + ehdr->e_shentsize),
137             (unsigned long) size);
138      return false;
139    }
140  
141    Elf_W(Shdr) shdr;
142    GET_SHDR_FIELD(ei, str_soff, &shdr, sh_offset);
143    GET_SHDR_FIELD(ei, str_soff, &shdr, sh_size);
144    if (shdr.sh_offset + shdr.sh_size > size) {
145      Debug (1, "string table outside of image? (%lu > %lu)\n",
146             (unsigned long) (shdr.sh_offset + shdr.sh_size),
147             (unsigned long) size);
148      return false;
149    }
150  
151    Debug (16, "strtab=0x%lx\n", (long) shdr.sh_offset);
152    *offset = shdr.sh_offset;
153    return true;
154  }
155  
elf_w(lookup_symbol_memory)156  static bool elf_w (lookup_symbol_memory) (
157      unw_addr_space_t as, unw_word_t ip, struct elf_image* ei, Elf_W(Addr) load_offset,
158      char* buf, size_t buf_len, unw_word_t* offp, Elf_W(Ehdr)* ehdr) {
159    Elf_W(Off) shdr_offset;
160    if (!elf_w (section_table_offset) (ei, ehdr, &shdr_offset)) {
161      return false;
162    }
163  
164    GET_EHDR_FIELD(ei, ehdr, e_shnum, true);
165    GET_EHDR_FIELD(ei, ehdr, e_shentsize, true);
166    int i;
167    for (i = 0; i < ehdr->e_shnum; ++i) {
168      Elf_W(Shdr) shdr;
169      GET_SHDR_FIELD(ei, shdr_offset, &shdr, sh_type);
170      switch (shdr.sh_type) {
171        case SHT_SYMTAB:
172        case SHT_DYNSYM:
173        {
174          GET_SHDR_FIELD(ei, shdr_offset, &shdr, sh_link);
175  
176          Elf_W(Off) strtab_offset;
177          if (!elf_w (string_table_offset) (ei, shdr.sh_link, ehdr, &strtab_offset)) {
178            continue;
179          }
180  
181          GET_SHDR_FIELD(ei, shdr_offset, &shdr, sh_offset);
182          GET_SHDR_FIELD(ei, shdr_offset, &shdr, sh_size);
183          GET_SHDR_FIELD(ei, shdr_offset, &shdr, sh_entsize);
184  
185          Debug (16, "symtab=0x%lx[%d]\n", (long) shdr.sh_offset, shdr.sh_type);
186  
187          unw_word_t sym_offset;
188          unw_word_t symtab_end = shdr.sh_offset + shdr.sh_size;
189          for (sym_offset = shdr.sh_offset;
190               sym_offset < symtab_end;
191               sym_offset += shdr.sh_entsize) {
192            Elf_W(Sym) sym;
193            GET_SYM_FIELD(ei, sym_offset, &sym, st_info);
194            GET_SYM_FIELD(ei, sym_offset, &sym, st_shndx);
195  
196            if (ELF_W (ST_TYPE) (sym.st_info) == STT_FUNC && sym.st_shndx != SHN_UNDEF) {
197              GET_SYM_FIELD(ei, sym_offset, &sym, st_value);
198              Elf_W(Addr) val;
199              if (tdep_get_func_addr (as, sym.st_value, &val) < 0) {
200                continue;
201              }
202              if (sym.st_shndx != SHN_ABS) {
203                val += load_offset;
204              }
205              Debug (16, "0x%016lx info=0x%02x\n", (long) val, sym.st_info);
206  
207              GET_SYM_FIELD(ei, sym_offset, &sym, st_size);
208              if (ip >= val && (Elf_W(Addr)) (ip - val) < sym.st_size) {
209                GET_SYM_FIELD(ei, sym_offset, &sym, st_name);
210                uintptr_t size = ei->u.memory.map->end - ei->u.memory.map->start;
211                Elf_W(Off) strname_offset = strtab_offset + sym.st_name;
212                if (strname_offset > size || strname_offset < strtab_offset) {
213                  // Malformed elf symbol table.
214                  break;
215                }
216  
217                size_t bytes_read = elf_w (memory_read) (
218                    ei, ei->u.memory.map->start + strname_offset,
219                    (uint8_t*) buf, buf_len, true);
220                if (bytes_read == 0) {
221                  // Empty name, so keep checking the other symbol tables
222                  // for a possible match.
223                  break;
224                }
225                // Ensure the string is nul terminated, it is assumed that
226                // sizeof(buf) >= buf_len + 1.
227                buf[buf_len] = '\0';
228  
229                if (offp != NULL) {
230                  *offp = ip - val;
231                }
232                return true;
233              }
234            }
235          }
236          break;
237        }
238  
239        default:
240          break;
241      }
242      shdr_offset += ehdr->e_shentsize;
243    }
244    return false;
245  }
246  
elf_w(get_load_offset_memory)247  static bool elf_w (get_load_offset_memory) (
248      struct elf_image* ei, unsigned long segbase, unsigned long mapoff,
249      Elf_W(Ehdr)* ehdr, Elf_W(Addr)* load_offset) {
250    GET_EHDR_FIELD(ei, ehdr, e_phoff, true);
251    GET_EHDR_FIELD(ei, ehdr, e_phnum, true);
252  
253    unw_word_t offset = ehdr->e_phoff;
254    int i;
255    for (i = 0; i < ehdr->e_phnum; ++i) {
256      Elf_W(Phdr) phdr;
257      GET_PHDR_FIELD(ei, offset, &phdr, p_type);
258      if (phdr.p_type == PT_LOAD) {
259        GET_PHDR_FIELD(ei, offset, &phdr, p_offset);
260        if (phdr.p_offset == mapoff) {
261          GET_PHDR_FIELD(ei, offset, &phdr, p_vaddr);
262          *load_offset = segbase - phdr.p_vaddr;
263          return true;
264        }
265      }
266      offset += sizeof(Elf_W(Phdr));
267    }
268    return false;
269  }
270  
271  // --------------------------------------------------------------------------
272  // Functions to read elf data from the mapped elf image.
273  // --------------------------------------------------------------------------
Elf_W(Shdr)274  static Elf_W(Shdr)* elf_w (section_table) (struct elf_image* ei) {
275    Elf_W(Ehdr)* ehdr = ei->u.mapped.image;
276    Elf_W(Off) soff = ehdr->e_shoff;
277    if (soff + ehdr->e_shnum * ehdr->e_shentsize > ei->u.mapped.size) {
278      Debug (1, "section table outside of image? (%lu > %lu)\n",
279             (unsigned long) (soff + ehdr->e_shnum * ehdr->e_shentsize),
280             (unsigned long) ei->u.mapped.size);
281      return NULL;
282    }
283  
284    return (Elf_W(Shdr) *) ((char *) ei->u.mapped.image + soff);
285  }
286  
elf_w(string_table)287  static char* elf_w (string_table) (struct elf_image* ei, int section) {
288    Elf_W(Ehdr)* ehdr = ei->u.mapped.image;
289    Elf_W(Off) str_soff = ehdr->e_shoff + (section * ehdr->e_shentsize);
290    if (str_soff + ehdr->e_shentsize > ei->u.mapped.size) {
291      Debug (1, "string shdr table outside of image? (%lu > %lu)\n",
292             (unsigned long) (str_soff + ehdr->e_shentsize),
293             (unsigned long) ei->u.mapped.size);
294      return NULL;
295    }
296    Elf_W(Shdr)* str_shdr = (Elf_W(Shdr) *) ((char *) ei->u.mapped.image + str_soff);
297  
298    if (str_shdr->sh_offset + str_shdr->sh_size > ei->u.mapped.size) {
299      Debug (1, "string table outside of image? (%lu > %lu)\n",
300             (unsigned long) (str_shdr->sh_offset + str_shdr->sh_size),
301             (unsigned long) ei->u.mapped.size);
302      return NULL;
303    }
304  
305    Debug (16, "strtab=0x%lx\n", (long) str_shdr->sh_offset);
306    return (char*) ((uintptr_t) ei->u.mapped.image + str_shdr->sh_offset);
307  }
308  
elf_w(lookup_symbol_mapped)309  static bool elf_w (lookup_symbol_mapped) (
310      unw_addr_space_t as, unw_word_t ip, struct elf_image* ei, Elf_W(Addr) load_offset,
311      char* buf, size_t buf_len, unw_word_t* offp) {
312  
313    Elf_W(Shdr)* shdr = elf_w (section_table) (ei);
314    if (!shdr) {
315      return false;
316    }
317  
318    Elf_W(Ehdr)* ehdr = ei->u.mapped.image;
319    int i;
320    for (i = 0; i < ehdr->e_shnum; ++i) {
321      switch (shdr->sh_type) {
322        case SHT_SYMTAB:
323        case SHT_DYNSYM:
324        {
325          Elf_W(Sym)* symtab = (Elf_W(Sym) *) ((char *) ei->u.mapped.image + shdr->sh_offset);
326          Elf_W(Sym)* symtab_end = (Elf_W(Sym) *) ((char *) symtab + shdr->sh_size);
327  
328          char* strtab = elf_w (string_table) (ei, shdr->sh_link);
329          if (!strtab) {
330            continue;
331          }
332  
333          Debug (16, "symtab=0x%lx[%d]\n", (long) shdr->sh_offset, shdr->sh_type);
334  
335          Elf_W(Sym)* sym;
336          for (sym = symtab;
337               sym < symtab_end;
338               sym = (Elf_W(Sym) *) ((char *) sym + shdr->sh_entsize)) {
339            if (ELF_W (ST_TYPE) (sym->st_info) == STT_FUNC && sym->st_shndx != SHN_UNDEF) {
340              Elf_W(Addr) val;
341              if (tdep_get_func_addr (as, sym->st_value, &val) < 0) {
342                continue;
343              }
344              if (sym->st_shndx != SHN_ABS) {
345                val += load_offset;
346              }
347              Debug (16, "0x%016lx info=0x%02x\n", (long) val, sym->st_info);
348              if (ip >= val && (Elf_W(Addr)) (ip - val) < sym->st_size) {
349                char* str_name = strtab + sym->st_name;
350                if (str_name > (char*) ei->u.mapped.image + ei->u.mapped.size ||
351                    str_name < strtab) {
352                  // Malformed elf symbol table.
353                  break;
354                }
355  
356                // Make sure we don't try and read past the end of the image.
357                uintptr_t max_size = (uintptr_t) str_name - (uintptr_t) ei->u.mapped.image;
358                if (buf_len > max_size) {
359                  buf_len = max_size;
360                }
361  
362                strncpy (buf, str_name, buf_len);
363                // Ensure the string is nul terminated, it is assumed that
364                // sizeof(buf) >= buf_len + 1.
365                buf[buf_len] = '\0';
366                if (buf[0] == '\0') {
367                  // Empty name, so keep checking the other symbol tables
368                  // for a possible match.
369                  break;
370                }
371                if (offp != NULL) {
372                  *offp = ip - val;
373                }
374                return true;
375              }
376            }
377          }
378          break;
379        }
380  
381        default:
382          break;
383      }
384      shdr = (Elf_W(Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
385    }
386    return false;
387  }
388  
elf_w(get_load_offset_mapped)389  static bool elf_w (get_load_offset_mapped) (
390      struct elf_image *ei, unsigned long segbase, unsigned long mapoff, Elf_W(Addr)* load_offset) {
391    Elf_W(Ehdr) *ehdr = ei->u.mapped.image;
392    Elf_W(Phdr) *phdr = (Elf_W(Phdr) *) ((char *) ei->u.mapped.image + ehdr->e_phoff);
393  
394    int i;
395    for (i = 0; i < ehdr->e_phnum; ++i) {
396      if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff) {
397        *load_offset = segbase - phdr[i].p_vaddr;
398        return true;
399      }
400    }
401    return false;
402  }
403  
404  // --------------------------------------------------------------------------
405  
elf_w(lookup_symbol)406  static inline bool elf_w (lookup_symbol) (
407      unw_addr_space_t as, unw_word_t ip, struct elf_image *ei, Elf_W(Addr) load_offset,
408      char *buf, size_t buf_len, unw_word_t* offp, Elf_W(Ehdr)* ehdr) {
409    if (!ei->valid)
410      return false;
411  
412    if (buf_len <= 1) {
413      Debug (1, "lookup_symbol called with a buffer too small to hold a name %zu\n", buf_len);
414      return false;
415    }
416  
417    // Leave enough space for the nul terminator.
418    buf_len--;
419  
420    if (ei->mapped) {
421      return elf_w (lookup_symbol_mapped) (as, ip, ei, load_offset, buf, buf_len, offp);
422    } else {
423      return elf_w (lookup_symbol_memory) (as, ip, ei, load_offset, buf, buf_len, offp, ehdr);
424    }
425  }
426  
elf_w(get_load_offset)427  static bool elf_w (get_load_offset) (
428      struct elf_image* ei, unsigned long segbase, unsigned long mapoff,
429      Elf_W(Ehdr)* ehdr, Elf_W(Addr)* load_offset) {
430    if (ei->mapped) {
431      return elf_w (get_load_offset_mapped) (ei, segbase, mapoff, load_offset);
432    } else {
433      return elf_w (get_load_offset_memory) (ei, segbase, mapoff, ehdr, load_offset);
434    }
435  }
436  
437  #if HAVE_LZMA
xz_uncompressed_size(uint8_t * compressed,size_t length)438  static size_t xz_uncompressed_size (uint8_t* compressed, size_t length) {
439    uint64_t memlimit = UINT64_MAX;
440    size_t ret = 0, pos = 0;
441    lzma_stream_flags options;
442    lzma_index *index;
443  
444    if (length < LZMA_STREAM_HEADER_SIZE) {
445      return 0;
446    }
447  
448    uint8_t *footer = compressed + length - LZMA_STREAM_HEADER_SIZE;
449    if (lzma_stream_footer_decode (&options, footer) != LZMA_OK) {
450      return 0;
451    }
452  
453    if (length < LZMA_STREAM_HEADER_SIZE + options.backward_size) {
454      return 0;
455    }
456  
457    uint8_t* indexdata = footer - options.backward_size;
458    if (lzma_index_buffer_decode (&index, &memlimit, NULL, indexdata,
459                                  &pos, options.backward_size) != LZMA_OK) {
460      return 0;
461    }
462  
463    if (lzma_index_size (index) == options.backward_size) {
464      ret = lzma_index_uncompressed_size (index);
465    }
466  
467    lzma_index_end (index, NULL);
468    return ret;
469  }
470  
elf_w(extract_minidebuginfo)471  static bool elf_w (extract_minidebuginfo) (struct elf_image* ei, struct elf_image* mdi, Elf_W(Ehdr)* ehdr) {
472    Elf_W(Ehdr)* ehdr = ei->image;
473    Elf_W(Shdr)* shdr;
474    char *strtab;
475    int i;
476    uint8_t *compressed = NULL;
477    uint64_t memlimit = UINT64_MAX; /* no memory limit */
478    size_t compressed_len, uncompressed_len;
479  
480    if (!ei->valid) {
481      return false;
482    }
483  
484    shdr = elf_w (section_table) (ei);
485    if (!shdr) {
486      return false;
487    }
488  
489    strtab = elf_w (string_table) (ei, ehdr->e_shstrndx);
490    if (!strtab) {
491      return false;
492    }
493  
494    for (i = 0; i < ehdr->e_shnum; ++i) {
495      if (strcmp (strtab + shdr->sh_name, ".gnu_debugdata") == 0) {
496        if (shdr->sh_offset + shdr->sh_size > ei->size) {
497          Debug (1, ".gnu_debugdata outside image? (0x%lu > 0x%lu)\n",
498                 (unsigned long) shdr->sh_offset + shdr->sh_size,
499                 (unsigned long) ei->size);
500          return false;
501        }
502  
503        Debug (16, "found .gnu_debugdata at 0x%lx\n",
504               (unsigned long) shdr->sh_offset);
505               compressed = ((uint8_t *) ei->image) + shdr->sh_offset;
506               compressed_len = shdr->sh_size;
507        break;
508      }
509  
510      shdr = (Elf_W(Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
511    }
512  
513    /* not found */
514    if (!compressed) {
515      return false;
516    }
517  
518    uncompressed_len = xz_uncompressed_size (compressed, compressed_len);
519    if (uncompressed_len == 0) {
520      Debug (1, "invalid .gnu_debugdata contents\n");
521      return false;
522    }
523  
524    mdi->size = uncompressed_len;
525    mdi->image = mmap (NULL, uncompressed_len, PROT_READ|PROT_WRITE,
526                       MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
527  
528    if (mdi->image == MAP_FAILED) {
529      return false;
530    }
531  
532    size_t in_pos = 0, out_pos = 0;
533    lzma_ret lret;
534    lret = lzma_stream_buffer_decode (&memlimit, 0, NULL,
535                                      compressed, &in_pos, compressed_len,
536                                      mdi->image, &out_pos, mdi->size);
537    if (lret != LZMA_OK) {
538      Debug (1, "LZMA decompression failed: %d\n", lret);
539      munmap (mdi->image, mdi->size);
540      return false;
541    }
542  
543    return true;
544  }
545  #else
elf_w(extract_minidebuginfo)546  static bool elf_w (extract_minidebuginfo) (struct elf_image* ei, struct elf_image* mdi, Elf_W(Ehdr)* ehdr) {
547    return false;
548  }
549  #endif /* !HAVE_LZMA */
550  
551  // Find the ELF image that contains IP and return the procedure name from
552  // the symbol table that matches the IP.
elf_w(get_proc_name_in_image)553  HIDDEN bool elf_w (get_proc_name_in_image) (
554      unw_addr_space_t as, struct elf_image* ei, unsigned long segbase, unsigned long mapoff,
555      unw_word_t ip, char* buf, size_t buf_len, unw_word_t* offp) {
556    Elf_W(Ehdr) ehdr;
557    memset(&ehdr, 0, sizeof(ehdr));
558    Elf_W(Addr) load_offset;
559    if (!elf_w (get_load_offset) (ei, segbase, mapoff, &ehdr, &load_offset)) {
560      return false;
561    }
562  
563    if (elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, offp, &ehdr) != 0) {
564      return true;
565    }
566  
567    // If the ELF image doesn't contain a match, look up the symbol in
568    // the MiniDebugInfo.
569    struct elf_image mdi;
570    if (elf_w (extract_minidebuginfo) (ei, &mdi, &ehdr)) {
571      if (!elf_w (get_load_offset) (&mdi, segbase, mapoff, &ehdr, &load_offset)) {
572        return false;
573      }
574      if (elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf, buf_len, offp, &ehdr)) {
575        munmap (mdi.u.mapped.image, mdi.u.mapped.size);
576        return true;
577      }
578      return false;
579    }
580    return false;
581  }
582  
elf_w(get_proc_name)583  HIDDEN bool elf_w (get_proc_name) (
584      unw_addr_space_t as, pid_t pid, unw_word_t ip, char* buf, size_t buf_len,
585      unw_word_t* offp, void* as_arg) {
586    unsigned long segbase, mapoff;
587    struct elf_image ei;
588  
589    if (tdep_get_elf_image(as, &ei, pid, ip, &segbase, &mapoff, NULL, as_arg) < 0) {
590      return false;
591    }
592  
593    return elf_w (get_proc_name_in_image) (as, &ei, segbase, mapoff, ip, buf, buf_len, offp);
594  }
595  
elf_w(get_load_base)596  HIDDEN bool elf_w (get_load_base) (struct elf_image* ei, unw_word_t mapoff, unw_word_t* load_base) {
597    if (!ei->valid) {
598      return false;
599    }
600  
601    if (ei->mapped) {
602      Elf_W(Ehdr)* ehdr = ei->u.mapped.image;
603      Elf_W(Phdr)* phdr = (Elf_W(Phdr)*) ((char*) ei->u.mapped.image + ehdr->e_phoff);
604      int i;
605      for (i = 0; i < ehdr->e_phnum; ++i) {
606        if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff) {
607          *load_base = phdr[i].p_vaddr;
608          return true;
609        }
610      }
611      return false;
612    } else {
613      Elf_W(Ehdr) ehdr;
614      GET_EHDR_FIELD(ei, &ehdr, e_phnum, false);
615      GET_EHDR_FIELD(ei, &ehdr, e_phoff, false);
616      int i;
617      unw_word_t offset = ehdr.e_phoff;
618      for (i = 0; i < ehdr.e_phnum; ++i) {
619        Elf_W(Phdr) phdr;
620        GET_PHDR_FIELD(ei, offset, &phdr, p_type);
621        GET_PHDR_FIELD(ei, offset, &phdr, p_offset);
622        if (phdr.p_type == PT_LOAD && phdr.p_offset == mapoff) {
623          GET_PHDR_FIELD(ei, offset, &phdr, p_vaddr);
624          *load_base = phdr.p_vaddr;
625          return true;
626        }
627        offset += sizeof(phdr);
628      }
629      return false;
630    }
631    return false;
632  }
633