1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//
8// Abstracts accessing local vs remote address spaces.
9//
10//===----------------------------------------------------------------------===//
11
12#ifndef __ADDRESSSPACE_HPP__
13#define __ADDRESSSPACE_HPP__
14
15#include <stdint.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <sys/tree.h>
20
21#include "libunwind.h"
22#include "config.h"
23#include "dwarf2.h"
24#include "EHHeaderParser.hpp"
25#include "Registers.hpp"
26
27#ifndef _LIBUNWIND_USE_DLADDR
28  #if !(defined(_LIBUNWIND_IS_BAREMETAL) || defined(_WIN32) || defined(_AIX))
29    #define _LIBUNWIND_USE_DLADDR 1
30  #else
31    #define _LIBUNWIND_USE_DLADDR 0
32  #endif
33#endif
34
35#if _LIBUNWIND_USE_DLADDR
36#include <dlfcn.h>
37#if defined(__ELF__) && defined(_LIBUNWIND_LINK_DL_LIB)
38#pragma comment(lib, "dl")
39#endif
40#endif
41
42#if defined(_LIBUNWIND_ARM_EHABI)
43struct EHABIIndexEntry {
44  uint32_t functionOffset;
45  uint32_t data;
46};
47#endif
48
49#if defined(_AIX)
50namespace libunwind {
51char *getFuncNameFromTBTable(uintptr_t pc, uint16_t &NameLen,
52                             unw_word_t *offset);
53}
54#endif
55
56#ifdef __APPLE__
57
58  struct dyld_unwind_sections
59  {
60    const struct mach_header*   mh;
61    const void*                 dwarf_section;
62    uintptr_t                   dwarf_section_length;
63    const void*                 compact_unwind_section;
64    uintptr_t                   compact_unwind_section_length;
65  };
66
67  // In 10.7.0 or later, libSystem.dylib implements this function.
68  extern "C" bool _dyld_find_unwind_sections(void *, dyld_unwind_sections *);
69
70#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL)
71
72// When statically linked on bare-metal, the symbols for the EH table are looked
73// up without going through the dynamic loader.
74
75// The following linker script may be used to produce the necessary sections and symbols.
76// Unless the --eh-frame-hdr linker option is provided, the section is not generated
77// and does not take space in the output file.
78//
79//   .eh_frame :
80//   {
81//       __eh_frame_start = .;
82//       KEEP(*(.eh_frame))
83//       __eh_frame_end = .;
84//   }
85//
86//   .eh_frame_hdr :
87//   {
88//       KEEP(*(.eh_frame_hdr))
89//   }
90//
91//   __eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0;
92//   __eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0;
93
94extern char __eh_frame_start;
95extern char __eh_frame_end;
96
97#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
98extern char __eh_frame_hdr_start;
99extern char __eh_frame_hdr_end;
100#endif
101
102#elif defined(_LIBUNWIND_ARM_EHABI) && defined(_LIBUNWIND_IS_BAREMETAL)
103
104// When statically linked on bare-metal, the symbols for the EH table are looked
105// up without going through the dynamic loader.
106extern char __exidx_start;
107extern char __exidx_end;
108
109#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_WIN32)
110
111#include <windows.h>
112#include <psapi.h>
113
114#elif defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) ||                               \
115      defined(_LIBUNWIND_USE_DL_UNWIND_FIND_EXIDX)
116
117#include <link.h>
118
119#endif
120
121namespace libunwind {
122
123/// Used by findUnwindSections() to return info about needed sections.
124struct UnwindInfoSections {
125#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) ||                                \
126    defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) ||                              \
127    defined(_LIBUNWIND_USE_DL_ITERATE_PHDR)
128  // No dso_base for SEH.
129  uintptr_t       dso_base;
130#endif
131#if defined(_LIBUNWIND_USE_DL_ITERATE_PHDR)
132  size_t          text_segment_length;
133#endif
134#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
135  uintptr_t       dwarf_section;
136  size_t          dwarf_section_length;
137#endif
138#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
139  uintptr_t       dwarf_index_section;
140  size_t          dwarf_index_section_length;
141#endif
142#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND)
143  uintptr_t       compact_unwind_section;
144  size_t          compact_unwind_section_length;
145#endif
146#if defined(_LIBUNWIND_ARM_EHABI)
147  uintptr_t       arm_section;
148  size_t          arm_section_length;
149#endif
150};
151
152class UnwindInfoSectionsCache {
153public:
154
155  struct CacheItem {
156    CacheItem(UnwindInfoSections &uis, uintptr_t pc)
157      : m_uis(uis), m_pc(pc) {
158    }
159    CacheItem(uintptr_t pc)
160      : m_pc(pc) {
161    }
162
163    UnwindInfoSections m_uis;
164    uintptr_t m_pc;
165
166    RB_ENTRY(CacheItem) entry;
167  };
168
169  typedef uintptr_t CacheItemKey;
170
171  int CacheCmp(struct CacheItem *c1, struct CacheItem *c2) {
172    return (c1->m_pc < c2->m_pc ? -1 : c1->m_pc > c2->m_pc);
173  }
174
175  UnwindInfoSectionsCache() {
176    m_head = RB_INITIALIZER(&head);
177  }
178
179  bool getUnwindInfoSectionsForPC(CacheItemKey key, UnwindInfoSections &uis) {
180    UnwindInfoSections *result = nullptr;
181    if (m_prev_req_item && m_prev_req_item->m_pc == key)
182      result = &m_prev_req_item->m_uis;
183    else {
184      struct CacheItem find(key), *res;
185      res = RB_FIND(CacheTree, &m_head, &find);
186      if (res) {
187        m_prev_req_item = res;
188        result = &res->m_uis;
189      }
190    }
191    if (result) {
192      uis = *result;
193      return true;
194    }
195    return false;
196  }
197
198  void setUnwindInfoSectionsForPC(CacheItemKey key, UnwindInfoSections &uis) {
199    CacheItem *p_item(new CacheItem(uis, key));
200    RB_INSERT(CacheTree, &m_head, p_item);
201  }
202
203private:
204  CacheItem *m_prev_req_item = nullptr;
205  RB_HEAD(CacheTree, CacheItem) m_head;
206  RB_GENERATE(CacheTree, CacheItem, entry, CacheCmp);
207};
208
209/// LocalAddressSpace is used as a template parameter to UnwindCursor when
210/// unwinding a thread in the same process.  The wrappers compile away,
211/// making local unwinds fast.
212class _LIBUNWIND_HIDDEN LocalAddressSpace {
213public:
214  typedef uintptr_t pint_t;
215  typedef intptr_t  sint_t;
216  uint8_t         get8(pint_t addr) {
217    uint8_t val;
218    memcpy(&val, (void *)addr, sizeof(val));
219    return val;
220  }
221  uint16_t         get16(pint_t addr) {
222    uint16_t val;
223    memcpy(&val, (void *)addr, sizeof(val));
224    return val;
225  }
226  uint32_t         get32(pint_t addr) {
227    uint32_t val;
228    memcpy(&val, (void *)addr, sizeof(val));
229    return val;
230  }
231  uint64_t         get64(pint_t addr) {
232    uint64_t val;
233    memcpy(&val, (void *)addr, sizeof(val));
234    return val;
235  }
236  double           getDouble(pint_t addr) {
237    double val;
238    memcpy(&val, (void *)addr, sizeof(val));
239    return val;
240  }
241  v128             getVector(pint_t addr) {
242    v128 val;
243    memcpy(&val, (void *)addr, sizeof(val));
244    return val;
245  }
246  uintptr_t       getP(pint_t addr);
247  uint64_t        getRegister(pint_t addr);
248  static uint64_t getULEB128(pint_t &addr, pint_t end);
249  static int64_t  getSLEB128(pint_t &addr, pint_t end);
250
251  pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
252                     pint_t datarelBase = 0);
253  bool findFunctionName(pint_t addr, char *buf, size_t bufLen,
254                        unw_word_t *offset);
255  bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info);
256  bool findOtherFDE(pint_t targetAddr, pint_t &fde);
257
258  static LocalAddressSpace sThisAddressSpace;
259};
260
261inline uintptr_t LocalAddressSpace::getP(pint_t addr) {
262#if __SIZEOF_POINTER__ == 8
263  return get64(addr);
264#else
265  return get32(addr);
266#endif
267}
268
269inline uint64_t LocalAddressSpace::getRegister(pint_t addr) {
270#if __SIZEOF_POINTER__ == 8 || defined(__mips64)
271  return get64(addr);
272#else
273  return get32(addr);
274#endif
275}
276
277/// Read a ULEB128 into a 64-bit word.
278inline uint64_t LocalAddressSpace::getULEB128(pint_t &addr, pint_t end) {
279  const uint8_t *p = (uint8_t *)addr;
280  const uint8_t *pend = (uint8_t *)end;
281  uint64_t result = 0;
282  int bit = 0;
283  do {
284    uint64_t b;
285
286    if (p == pend)
287      _LIBUNWIND_ABORT("truncated uleb128 expression");
288
289    b = *p & 0x7f;
290
291    if (bit >= 64 || b << bit >> bit != b) {
292      _LIBUNWIND_ABORT("malformed uleb128 expression");
293    } else {
294      result |= b << bit;
295      bit += 7;
296    }
297  } while (*p++ >= 0x80);
298  addr = (pint_t) p;
299  return result;
300}
301
302/// Read a SLEB128 into a 64-bit word.
303inline int64_t LocalAddressSpace::getSLEB128(pint_t &addr, pint_t end) {
304  const uint8_t *p = (uint8_t *)addr;
305  const uint8_t *pend = (uint8_t *)end;
306  uint64_t result = 0;
307  int bit = 0;
308  uint8_t byte;
309  do {
310    if (p == pend)
311      _LIBUNWIND_ABORT("truncated sleb128 expression");
312    byte = *p++;
313    result |= (uint64_t)(byte & 0x7f) << bit;
314    bit += 7;
315  } while (byte & 0x80);
316  // sign extend negative numbers
317  if ((byte & 0x40) != 0 && bit < 64)
318    result |= (-1ULL) << bit;
319  addr = (pint_t) p;
320  return (int64_t)result;
321}
322
323inline LocalAddressSpace::pint_t
324LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
325                               pint_t datarelBase) {
326  pint_t startAddr = addr;
327  const uint8_t *p = (uint8_t *)addr;
328  pint_t result;
329
330  if (encoding == DW_EH_PE_omit) {
331    return (pint_t)NULL;
332  }
333
334  // first get value
335  switch (encoding & 0x0F) {
336  case DW_EH_PE_ptr:
337    result = getP(addr);
338    p += sizeof(pint_t);
339    addr = (pint_t) p;
340    break;
341  case DW_EH_PE_uleb128:
342    result = (pint_t)getULEB128(addr, end);
343    break;
344  case DW_EH_PE_udata2:
345    result = get16(addr);
346    p += 2;
347    addr = (pint_t) p;
348    break;
349  case DW_EH_PE_udata4:
350    result = get32(addr);
351    p += 4;
352    addr = (pint_t) p;
353    break;
354  case DW_EH_PE_udata8:
355    result = (pint_t)get64(addr);
356    p += 8;
357    addr = (pint_t) p;
358    break;
359  case DW_EH_PE_sleb128:
360    result = (pint_t)getSLEB128(addr, end);
361    break;
362  case DW_EH_PE_sdata2:
363    // Sign extend from signed 16-bit value.
364    result = (pint_t)(int16_t)get16(addr);
365    p += 2;
366    addr = (pint_t) p;
367    break;
368  case DW_EH_PE_sdata4:
369    // Sign extend from signed 32-bit value.
370    result = (pint_t)(int32_t)get32(addr);
371    p += 4;
372    addr = (pint_t) p;
373    break;
374  case DW_EH_PE_sdata8:
375    result = (pint_t)get64(addr);
376    p += 8;
377    addr = (pint_t) p;
378    break;
379  default:
380    _LIBUNWIND_ABORT("unknown pointer encoding");
381  }
382
383  // then add relative offset
384  switch (encoding & 0x70) {
385  case DW_EH_PE_absptr:
386    // do nothing
387    break;
388  case DW_EH_PE_pcrel:
389    result += startAddr;
390    break;
391  case DW_EH_PE_textrel:
392    _LIBUNWIND_ABORT("DW_EH_PE_textrel pointer encoding not supported");
393    break;
394  case DW_EH_PE_datarel:
395    // DW_EH_PE_datarel is only valid in a few places, so the parameter has a
396    // default value of 0, and we abort in the event that someone calls this
397    // function with a datarelBase of 0 and DW_EH_PE_datarel encoding.
398    if (datarelBase == 0)
399      _LIBUNWIND_ABORT("DW_EH_PE_datarel is invalid with a datarelBase of 0");
400    result += datarelBase;
401    break;
402  case DW_EH_PE_funcrel:
403    _LIBUNWIND_ABORT("DW_EH_PE_funcrel pointer encoding not supported");
404    break;
405  case DW_EH_PE_aligned:
406    _LIBUNWIND_ABORT("DW_EH_PE_aligned pointer encoding not supported");
407    break;
408  default:
409    _LIBUNWIND_ABORT("unknown pointer encoding");
410    break;
411  }
412
413  if (encoding & DW_EH_PE_indirect)
414    result = getP(result);
415
416  return result;
417}
418
419#if defined(_LIBUNWIND_USE_DL_ITERATE_PHDR)
420
421// The ElfW() macro for pointer-size independent ELF header traversal is not
422// provided by <link.h> on some systems (e.g., FreeBSD). On these systems the
423// data structures are just called Elf_XXX. Define ElfW() locally.
424#if !defined(ElfW)
425  #define ElfW(type) Elf_##type
426#endif
427#if !defined(Elf_Half)
428  typedef ElfW(Half) Elf_Half;
429#endif
430#if !defined(Elf_Phdr)
431  typedef ElfW(Phdr) Elf_Phdr;
432#endif
433#if !defined(Elf_Addr)
434  typedef ElfW(Addr) Elf_Addr;
435#endif
436
437struct _LIBUNWIND_HIDDEN dl_iterate_cb_data {
438  LocalAddressSpace *addressSpace;
439  UnwindInfoSections *sects;
440  uintptr_t targetAddr;
441};
442
443#if defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE)
444#include "FrameHeaderCache.hpp"
445
446// Typically there is one cache per process, but when libunwind is built as a
447// hermetic static library, then each shared object may have its own cache.
448static FrameHeaderCache TheFrameHeaderCache;
449#endif
450
451static bool checkAddrInSegment(const Elf_Phdr *phdr, size_t image_base,
452                               dl_iterate_cb_data *cbdata) {
453  if (phdr->p_type == PT_LOAD) {
454    uintptr_t begin = image_base + phdr->p_vaddr;
455    uintptr_t end = begin + phdr->p_memsz;
456    if (cbdata->targetAddr >= begin && cbdata->targetAddr < end) {
457      cbdata->sects->dso_base = begin;
458      cbdata->sects->text_segment_length = phdr->p_memsz;
459      return true;
460    }
461  }
462  return false;
463}
464
465static bool checkForUnwindInfoSegment(const Elf_Phdr *phdr, size_t image_base,
466                                      dl_iterate_cb_data *cbdata) {
467#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
468  if (phdr->p_type == PT_GNU_EH_FRAME) {
469    EHHeaderParser<LocalAddressSpace>::EHHeaderInfo hdrInfo;
470    uintptr_t eh_frame_hdr_start = image_base + phdr->p_vaddr;
471    cbdata->sects->dwarf_index_section = eh_frame_hdr_start;
472    cbdata->sects->dwarf_index_section_length = phdr->p_memsz;
473    if (EHHeaderParser<LocalAddressSpace>::decodeEHHdr(
474            *cbdata->addressSpace, eh_frame_hdr_start, phdr->p_memsz,
475            hdrInfo)) {
476      // .eh_frame_hdr records the start of .eh_frame, but not its size.
477      // Rely on a zero terminator to find the end of the section.
478      cbdata->sects->dwarf_section = hdrInfo.eh_frame_ptr;
479      cbdata->sects->dwarf_section_length = SIZE_MAX;
480      return true;
481    }
482  }
483  return false;
484#elif defined(_LIBUNWIND_ARM_EHABI)
485  if (phdr->p_type == PT_ARM_EXIDX) {
486    uintptr_t exidx_start = image_base + phdr->p_vaddr;
487    cbdata->sects->arm_section = exidx_start;
488    cbdata->sects->arm_section_length = phdr->p_memsz;
489    return true;
490  }
491  return false;
492#else
493#error Need one of _LIBUNWIND_SUPPORT_DWARF_INDEX or _LIBUNWIND_ARM_EHABI
494#endif
495}
496
497static int findUnwindSectionsByPhdr(struct dl_phdr_info *pinfo,
498                                    size_t pinfo_size, void *data) {
499  auto cbdata = static_cast<dl_iterate_cb_data *>(data);
500  if (pinfo->dlpi_phnum == 0 || cbdata->targetAddr < pinfo->dlpi_addr)
501    return 0;
502#if defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE)
503  if (TheFrameHeaderCache.find(pinfo, pinfo_size, data))
504    return 1;
505#else
506  // Avoid warning about unused variable.
507  (void)pinfo_size;
508#endif
509
510  Elf_Addr image_base = pinfo->dlpi_addr;
511
512  // Most shared objects seen in this callback function likely don't contain the
513  // target address, so optimize for that. Scan for a matching PT_LOAD segment
514  // first and bail when it isn't found.
515  bool found_text = false;
516  for (Elf_Half i = 0; i < pinfo->dlpi_phnum; ++i) {
517    if (checkAddrInSegment(&pinfo->dlpi_phdr[i], image_base, cbdata)) {
518      found_text = true;
519      break;
520    }
521  }
522  if (!found_text)
523    return 0;
524
525  // PT_GNU_EH_FRAME and PT_ARM_EXIDX are usually near the end. Iterate
526  // backward.
527  bool found_unwind = false;
528  for (Elf_Half i = pinfo->dlpi_phnum; i > 0; i--) {
529    const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i - 1];
530    if (checkForUnwindInfoSegment(phdr, image_base, cbdata)) {
531      found_unwind = true;
532      break;
533    }
534  }
535  if (!found_unwind)
536    return 0;
537
538#if defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE)
539  TheFrameHeaderCache.add(cbdata->sects);
540#endif
541  return 1;
542}
543
544#endif  // defined(_LIBUNWIND_USE_DL_ITERATE_PHDR)
545
546
547inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr,
548                                                  UnwindInfoSections &info) {
549#ifdef __APPLE__
550  dyld_unwind_sections dyldInfo;
551  if (_dyld_find_unwind_sections((void *)targetAddr, &dyldInfo)) {
552    info.dso_base                      = (uintptr_t)dyldInfo.mh;
553 #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
554    info.dwarf_section                 = (uintptr_t)dyldInfo.dwarf_section;
555    info.dwarf_section_length          = (size_t)dyldInfo.dwarf_section_length;
556 #endif
557    info.compact_unwind_section        = (uintptr_t)dyldInfo.compact_unwind_section;
558    info.compact_unwind_section_length = (size_t)dyldInfo.compact_unwind_section_length;
559    return true;
560  }
561#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL)
562  info.dso_base = 0;
563  // Bare metal is statically linked, so no need to ask the dynamic loader
564  info.dwarf_section_length = (size_t)(&__eh_frame_end - &__eh_frame_start);
565  info.dwarf_section =        (uintptr_t)(&__eh_frame_start);
566  _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: section %p length %p",
567                             (void *)info.dwarf_section, (void *)info.dwarf_section_length);
568#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
569  info.dwarf_index_section =        (uintptr_t)(&__eh_frame_hdr_start);
570  info.dwarf_index_section_length = (size_t)(&__eh_frame_hdr_end - &__eh_frame_hdr_start);
571  _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: index section %p length %p",
572                             (void *)info.dwarf_index_section, (void *)info.dwarf_index_section_length);
573#endif
574  if (info.dwarf_section_length)
575    return true;
576#elif defined(_LIBUNWIND_ARM_EHABI) && defined(_LIBUNWIND_IS_BAREMETAL)
577  // Bare metal is statically linked, so no need to ask the dynamic loader
578  info.arm_section =        (uintptr_t)(&__exidx_start);
579  info.arm_section_length = (size_t)(&__exidx_end - &__exidx_start);
580  _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: section %p length %p",
581                             (void *)info.arm_section, (void *)info.arm_section_length);
582  if (info.arm_section && info.arm_section_length)
583    return true;
584#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_WIN32)
585  HMODULE mods[1024];
586  HANDLE process = GetCurrentProcess();
587  DWORD needed;
588
589  if (!EnumProcessModules(process, mods, sizeof(mods), &needed)) {
590    DWORD err = GetLastError();
591    _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: EnumProcessModules failed, "
592                               "returned error %d", (int)err);
593    (void)err;
594    return false;
595  }
596
597  for (unsigned i = 0; i < (needed / sizeof(HMODULE)); i++) {
598    PIMAGE_DOS_HEADER pidh = (PIMAGE_DOS_HEADER)mods[i];
599    PIMAGE_NT_HEADERS pinh = (PIMAGE_NT_HEADERS)((BYTE *)pidh + pidh->e_lfanew);
600    PIMAGE_FILE_HEADER pifh = (PIMAGE_FILE_HEADER)&pinh->FileHeader;
601    PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION(pinh);
602    bool found_obj = false;
603    bool found_hdr = false;
604
605    info.dso_base = (uintptr_t)mods[i];
606    for (unsigned j = 0; j < pifh->NumberOfSections; j++, pish++) {
607      uintptr_t begin = pish->VirtualAddress + (uintptr_t)mods[i];
608      uintptr_t end = begin + pish->Misc.VirtualSize;
609      if (!strncmp((const char *)pish->Name, ".text",
610                   IMAGE_SIZEOF_SHORT_NAME)) {
611        if (targetAddr >= begin && targetAddr < end)
612          found_obj = true;
613      } else if (!strncmp((const char *)pish->Name, ".eh_frame",
614                          IMAGE_SIZEOF_SHORT_NAME)) {
615        info.dwarf_section = begin;
616        info.dwarf_section_length = pish->Misc.VirtualSize;
617        found_hdr = true;
618      }
619      if (found_obj && found_hdr)
620        return true;
621    }
622  }
623  return false;
624#elif defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32)
625  // Don't even bother, since Windows has functions that do all this stuff
626  // for us.
627  (void)targetAddr;
628  (void)info;
629  return true;
630#elif defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
631  // The traceback table is used for unwinding.
632  (void)targetAddr;
633  (void)info;
634  return true;
635#elif defined(_LIBUNWIND_USE_DL_UNWIND_FIND_EXIDX)
636  int length = 0;
637  info.arm_section =
638      (uintptr_t)dl_unwind_find_exidx((_Unwind_Ptr)targetAddr, &length);
639  info.arm_section_length = (size_t)length * sizeof(EHABIIndexEntry);
640  if (info.arm_section && info.arm_section_length)
641    return true;
642#elif defined(_LIBUNWIND_USE_DL_ITERATE_PHDR)
643  // Use DLFO_STRUCT_HAS_EH_DBASE to determine the existence of
644  // `_dl_find_object`. Use _LIBUNWIND_SUPPORT_DWARF_INDEX, because libunwind
645  // support for _dl_find_object on other unwind formats is not implemented,
646  // yet.
647#if defined(DLFO_STRUCT_HAS_EH_DBASE) & defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
648  // We expect `_dl_find_object` to return PT_GNU_EH_FRAME.
649#if DLFO_EH_SEGMENT_TYPE != PT_GNU_EH_FRAME
650#error _dl_find_object retrieves an unexpected section type
651#endif
652  // We look-up `dl_find_object` dynamically at runtime to ensure backwards
653  // compatibility with earlier version of glibc not yet providing it. On older
654  // systems, we gracefully fallback to `dl_iterate_phdr`. Cache the pointer
655  // so we only look it up once. Do manual lock to avoid _cxa_guard_acquire.
656  static decltype(_dl_find_object) *dlFindObject;
657  static bool dlFindObjectChecked = false;
658  if (!dlFindObjectChecked) {
659    dlFindObject = reinterpret_cast<decltype(_dl_find_object) *>(
660        dlsym(RTLD_DEFAULT, "_dl_find_object"));
661    dlFindObjectChecked = true;
662  }
663  // Try to find the unwind info using `dl_find_object`
664  dl_find_object findResult;
665  if (dlFindObject && dlFindObject((void *)targetAddr, &findResult) == 0) {
666    if (findResult.dlfo_eh_frame == nullptr) {
667      // Found an entry for `targetAddr`, but there is no unwind info.
668      return false;
669    }
670    info.dso_base = reinterpret_cast<uintptr_t>(findResult.dlfo_map_start);
671    info.text_segment_length = static_cast<size_t>(
672        (char *)findResult.dlfo_map_end - (char *)findResult.dlfo_map_start);
673
674    // Record the start of PT_GNU_EH_FRAME.
675    info.dwarf_index_section =
676        reinterpret_cast<uintptr_t>(findResult.dlfo_eh_frame);
677    // `_dl_find_object` does not give us the size of PT_GNU_EH_FRAME.
678    // Setting length to `SIZE_MAX` effectively disables all range checks.
679    info.dwarf_index_section_length = SIZE_MAX;
680    EHHeaderParser<LocalAddressSpace>::EHHeaderInfo hdrInfo;
681    if (!EHHeaderParser<LocalAddressSpace>::decodeEHHdr(
682            *this, info.dwarf_index_section, info.dwarf_index_section_length,
683            hdrInfo)) {
684      return false;
685    }
686    // Record the start of the FDE and use SIZE_MAX to indicate that we do
687    // not know the end address.
688    info.dwarf_section = hdrInfo.eh_frame_ptr;
689    info.dwarf_section_length = SIZE_MAX;
690    return true;
691  }
692#endif
693  dl_iterate_cb_data cb_data = {this, &info, targetAddr};
694  int found = dl_iterate_phdr(findUnwindSectionsByPhdr, &cb_data);
695  return static_cast<bool>(found);
696#endif
697
698  return false;
699}
700
701inline bool LocalAddressSpace::findOtherFDE(pint_t targetAddr, pint_t &fde) {
702  // TO DO: if OS has way to dynamically register FDEs, check that.
703  (void)targetAddr;
704  (void)fde;
705  return false;
706}
707
708inline bool LocalAddressSpace::findFunctionName(pint_t addr, char *buf,
709                                                size_t bufLen,
710                                                unw_word_t *offset) {
711#if _LIBUNWIND_USE_DLADDR
712  Dl_info dyldInfo;
713  if (dladdr((void *)addr, &dyldInfo)) {
714    if (dyldInfo.dli_sname != NULL) {
715      snprintf(buf, bufLen, "%s", dyldInfo.dli_sname);
716      *offset = (addr - (pint_t) dyldInfo.dli_saddr);
717      return true;
718    }
719  }
720#elif defined(_AIX)
721  uint16_t nameLen;
722  char *funcName = getFuncNameFromTBTable(addr, nameLen, offset);
723  if (funcName != NULL) {
724    snprintf(buf, bufLen, "%.*s", nameLen, funcName);
725    return true;
726  }
727#else
728  (void)addr;
729  (void)buf;
730  (void)bufLen;
731  (void)offset;
732#endif
733  return false;
734}
735
736} // namespace libunwind
737
738#endif // __ADDRESSSPACE_HPP__
739