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