1//===-- UnwindTable.cpp ---------------------------------------------------===//
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
9#include "lldb/Symbol/UnwindTable.h"
10
11#include <cstdio>
12#include <optional>
13
14#include "lldb/Core/Module.h"
15#include "lldb/Core/Section.h"
16#include "lldb/Symbol/ArmUnwindInfo.h"
17#include "lldb/Symbol/CallFrameInfo.h"
18#include "lldb/Symbol/CompactUnwindInfo.h"
19#include "lldb/Symbol/DWARFCallFrameInfo.h"
20#include "lldb/Symbol/FuncUnwinders.h"
21#include "lldb/Symbol/ObjectFile.h"
22#include "lldb/Symbol/SymbolContext.h"
23#include "lldb/Symbol/SymbolVendor.h"
24
25// There is one UnwindTable object per ObjectFile. It contains a list of Unwind
26// objects -- one per function, populated lazily -- for the ObjectFile. Each
27// Unwind object has multiple UnwindPlans for different scenarios.
28
29using namespace lldb;
30using namespace lldb_private;
31
32UnwindTable::UnwindTable(Module &module)
33    : m_module(module), m_unwinds(), m_initialized(false), m_mutex(),
34      m_object_file_unwind_up(), m_eh_frame_up(), m_compact_unwind_up(),
35      m_arm_unwind_up() {}
36
37// We can't do some of this initialization when the ObjectFile is running its
38// ctor; delay doing it until needed for something.
39
40void UnwindTable::Initialize() {
41  if (m_initialized)
42    return;
43
44  std::lock_guard<std::mutex> guard(m_mutex);
45
46  if (m_initialized) // check again once we've acquired the lock
47    return;
48  m_initialized = true;
49  ObjectFile *object_file = m_module.GetObjectFile();
50  if (!object_file)
51    return;
52
53  m_object_file_unwind_up = object_file->CreateCallFrameInfo();
54
55  SectionList *sl = m_module.GetSectionList();
56  if (!sl)
57    return;
58
59  SectionSP sect = sl->FindSectionByType(eSectionTypeEHFrame, true);
60  if (sect.get()) {
61    m_eh_frame_up = std::make_unique<DWARFCallFrameInfo>(
62        *object_file, sect, DWARFCallFrameInfo::EH);
63  }
64
65  sect = sl->FindSectionByType(eSectionTypeDWARFDebugFrame, true);
66  if (sect) {
67    m_debug_frame_up = std::make_unique<DWARFCallFrameInfo>(
68        *object_file, sect, DWARFCallFrameInfo::DWARF);
69  }
70
71  sect = sl->FindSectionByType(eSectionTypeCompactUnwind, true);
72  if (sect) {
73    m_compact_unwind_up =
74        std::make_unique<CompactUnwindInfo>(*object_file, sect);
75  }
76
77  sect = sl->FindSectionByType(eSectionTypeARMexidx, true);
78  if (sect) {
79    SectionSP sect_extab = sl->FindSectionByType(eSectionTypeARMextab, true);
80    if (sect_extab.get()) {
81      m_arm_unwind_up =
82          std::make_unique<ArmUnwindInfo>(*object_file, sect, sect_extab);
83    }
84  }
85}
86
87UnwindTable::~UnwindTable() = default;
88
89std::optional<AddressRange>
90UnwindTable::GetAddressRange(const Address &addr, const SymbolContext &sc) {
91  AddressRange range;
92
93  // First check the unwind info from the object file plugin
94  if (m_object_file_unwind_up &&
95      m_object_file_unwind_up->GetAddressRange(addr, range))
96    return range;
97
98  // Check the symbol context
99  if (sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
100                         false, range) &&
101      range.GetBaseAddress().IsValid())
102    return range;
103
104  // Does the eh_frame unwind info has a function bounds for this addr?
105  if (m_eh_frame_up && m_eh_frame_up->GetAddressRange(addr, range))
106    return range;
107
108  // Try debug_frame as well
109  if (m_debug_frame_up && m_debug_frame_up->GetAddressRange(addr, range))
110    return range;
111
112  return std::nullopt;
113}
114
115FuncUnwindersSP
116UnwindTable::GetFuncUnwindersContainingAddress(const Address &addr,
117                                               SymbolContext &sc) {
118  Initialize();
119
120  std::lock_guard<std::mutex> guard(m_mutex);
121
122  // There is an UnwindTable per object file, so we can safely use file handles
123  addr_t file_addr = addr.GetFileAddress();
124  iterator end = m_unwinds.end();
125  iterator insert_pos = end;
126  if (!m_unwinds.empty()) {
127    insert_pos = m_unwinds.lower_bound(file_addr);
128    iterator pos = insert_pos;
129    if ((pos == m_unwinds.end()) ||
130        (pos != m_unwinds.begin() &&
131         pos->second->GetFunctionStartAddress() != addr))
132      --pos;
133
134    if (pos->second->ContainsAddress(addr))
135      return pos->second;
136  }
137
138  auto range_or = GetAddressRange(addr, sc);
139  if (!range_or)
140    return nullptr;
141
142  FuncUnwindersSP func_unwinder_sp(new FuncUnwinders(*this, *range_or));
143  m_unwinds.insert(insert_pos,
144                   std::make_pair(range_or->GetBaseAddress().GetFileAddress(),
145                                  func_unwinder_sp));
146  return func_unwinder_sp;
147}
148
149// Ignore any existing FuncUnwinders for this function, create a new one and
150// don't add it to the UnwindTable.  This is intended for use by target modules
151// show-unwind where we want to create new UnwindPlans, not re-use existing
152// ones.
153FuncUnwindersSP UnwindTable::GetUncachedFuncUnwindersContainingAddress(
154    const Address &addr, const SymbolContext &sc) {
155  Initialize();
156
157  auto range_or = GetAddressRange(addr, sc);
158  if (!range_or)
159    return nullptr;
160
161  return std::make_shared<FuncUnwinders>(*this, *range_or);
162}
163
164void UnwindTable::Dump(Stream &s) {
165  std::lock_guard<std::mutex> guard(m_mutex);
166  s.Format("UnwindTable for '{0}':\n", m_module.GetFileSpec());
167  const_iterator begin = m_unwinds.begin();
168  const_iterator end = m_unwinds.end();
169  for (const_iterator pos = begin; pos != end; ++pos) {
170    s.Printf("[%u] 0x%16.16" PRIx64 "\n", (unsigned)std::distance(begin, pos),
171             pos->first);
172  }
173  s.EOL();
174}
175
176lldb_private::CallFrameInfo *UnwindTable::GetObjectFileUnwindInfo() {
177  Initialize();
178  return m_object_file_unwind_up.get();
179}
180
181DWARFCallFrameInfo *UnwindTable::GetEHFrameInfo() {
182  Initialize();
183  return m_eh_frame_up.get();
184}
185
186DWARFCallFrameInfo *UnwindTable::GetDebugFrameInfo() {
187  Initialize();
188  return m_debug_frame_up.get();
189}
190
191CompactUnwindInfo *UnwindTable::GetCompactUnwindInfo() {
192  Initialize();
193  return m_compact_unwind_up.get();
194}
195
196ArmUnwindInfo *UnwindTable::GetArmUnwindInfo() {
197  Initialize();
198  return m_arm_unwind_up.get();
199}
200
201SymbolFile *UnwindTable::GetSymbolFile() { return m_module.GetSymbolFile(); }
202
203ArchSpec UnwindTable::GetArchitecture() { return m_module.GetArchitecture(); }
204
205bool UnwindTable::GetAllowAssemblyEmulationUnwindPlans() {
206  if (ObjectFile *object_file = m_module.GetObjectFile())
207    return object_file->AllowAssemblyEmulationUnwindPlans();
208  return false;
209}
210