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_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 
40 void 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 
87 UnwindTable::~UnwindTable() = default;
88 
89 std::optional<AddressRange> UnwindTable::GetAddressRange(const Address &addr,
90                                                          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 
115 FuncUnwindersSP
116 UnwindTable::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.
153 FuncUnwindersSP
154 UnwindTable::GetUncachedFuncUnwindersContainingAddress(const Address &addr,
155                                                        SymbolContext &sc) {
156   Initialize();
157 
158   auto range_or = GetAddressRange(addr, sc);
159   if (!range_or)
160     return nullptr;
161 
162   return std::make_shared<FuncUnwinders>(*this, *range_or);
163 }
164 
165 void UnwindTable::Dump(Stream &s) {
166   std::lock_guard<std::mutex> guard(m_mutex);
167   s.Format("UnwindTable for '{0}':\n", m_module.GetFileSpec());
168   const_iterator begin = m_unwinds.begin();
169   const_iterator end = m_unwinds.end();
170   for (const_iterator pos = begin; pos != end; ++pos) {
171     s.Printf("[%u] 0x%16.16" PRIx64 "\n", (unsigned)std::distance(begin, pos),
172              pos->first);
173   }
174   s.EOL();
175 }
176 
177 lldb_private::CallFrameInfo *UnwindTable::GetObjectFileUnwindInfo() {
178   Initialize();
179   return m_object_file_unwind_up.get();
180 }
181 
182 DWARFCallFrameInfo *UnwindTable::GetEHFrameInfo() {
183   Initialize();
184   return m_eh_frame_up.get();
185 }
186 
187 DWARFCallFrameInfo *UnwindTable::GetDebugFrameInfo() {
188   Initialize();
189   return m_debug_frame_up.get();
190 }
191 
192 CompactUnwindInfo *UnwindTable::GetCompactUnwindInfo() {
193   Initialize();
194   return m_compact_unwind_up.get();
195 }
196 
197 ArmUnwindInfo *UnwindTable::GetArmUnwindInfo() {
198   Initialize();
199   return m_arm_unwind_up.get();
200 }
201 
202 SymbolFile *UnwindTable::GetSymbolFile() { return m_module.GetSymbolFile(); }
203 
204 ArchSpec UnwindTable::GetArchitecture() { return m_module.GetArchitecture(); }
205 
206 bool UnwindTable::GetAllowAssemblyEmulationUnwindPlans() {
207   if (ObjectFile *object_file = m_module.GetObjectFile())
208     return object_file->AllowAssemblyEmulationUnwindPlans();
209   return false;
210 }
211