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
36// --------------------------------------------------------------------------
37// Functions to read elf data from memory.
38// --------------------------------------------------------------------------
39extern 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
111static 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
128static 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
156static 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
247static 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// --------------------------------------------------------------------------
274static 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
287static 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
309static 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
389static 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
406static 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
427static 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
438static 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
471static 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
546static 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.
553HIDDEN 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
583HIDDEN 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
596HIDDEN 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